7 Fasttracker (XM) module loader
\r
10 All systems - all compilers (hopefully)
\r
20 /**************************************************************************
\r
21 **************************************************************************/
\r
24 typedef struct XMHEADER{
\r
25 char id[17]; /* ID text: 'Extended module: ' */
\r
26 char songname[21]; /* Module name, padded with zeroes and 0x1a at the end */
\r
27 char trackername[20]; /* Tracker name */
\r
28 UWORD version; /* (word) Version number, hi-byte major and low-byte minor */
\r
29 ULONG headersize; /* Header size */
\r
30 UWORD songlength; /* (word) Song length (in patten order table) */
\r
31 UWORD restart; /* (word) Restart position */
\r
32 UWORD numchn; /* (word) Number of channels (2,4,6,8,10,...,32) */
\r
33 UWORD numpat; /* (word) Number of patterns (max 256) */
\r
34 UWORD numins; /* (word) Number of instruments (max 128) */
\r
35 UWORD flags; /* (word) Flags: bit 0: 0 = Amiga frequency table (see below) 1 = Linear frequency table */
\r
36 UWORD tempo; /* (word) Default tempo */
\r
37 UWORD bpm; /* (word) Default BPM */
\r
38 UBYTE orders[256]; /* (byte) Pattern order table */
\r
42 typedef struct XMINSTHEADER{
\r
43 ULONG size; /* (dword) Instrument size */
\r
44 char name[22]; /* (char) Instrument name */
\r
45 UBYTE type; /* (byte) Instrument type (always 0) */
\r
46 UWORD numsmp; /* (word) Number of samples in instrument */
\r
51 typedef struct XMPATCHHEADER{
\r
52 UBYTE what[96]; /* (byte) Sample number for all notes */
\r
53 UBYTE volenv[48]; /* (byte) Points for volume envelope */
\r
54 UBYTE panenv[48]; /* (byte) Points for panning envelope */
\r
55 UBYTE volpts; /* (byte) Number of volume points */
\r
56 UBYTE panpts; /* (byte) Number of panning points */
\r
57 UBYTE volsus; /* (byte) Volume sustain point */
\r
58 UBYTE volbeg; /* (byte) Volume loop start point */
\r
59 UBYTE volend; /* (byte) Volume loop end point */
\r
60 UBYTE pansus; /* (byte) Panning sustain point */
\r
61 UBYTE panbeg; /* (byte) Panning loop start point */
\r
62 UBYTE panend; /* (byte) Panning loop end point */
\r
63 UBYTE volflg; /* (byte) Volume type: bit 0: On; 1: Sustain; 2: Loop */
\r
64 UBYTE panflg; /* (byte) Panning type: bit 0: On; 1: Sustain; 2: Loop */
\r
65 UBYTE vibflg; /* (byte) Vibrato type */
\r
66 UBYTE vibsweep; /* (byte) Vibrato sweep */
\r
67 UBYTE vibdepth; /* (byte) Vibrato depth */
\r
68 UBYTE vibrate; /* (byte) Vibrato rate */
\r
69 UWORD volfade; /* (word) Volume fadeout */
\r
70 UWORD reserved[11]; /* (word) Reserved */
\r
74 typedef struct XMWAVHEADER{
\r
75 ULONG length; /* (dword) Sample length */
\r
76 ULONG loopstart; /* (dword) Sample loop start */
\r
77 ULONG looplength; /* (dword) Sample loop length */
\r
78 UBYTE volume; /* (byte) Volume */
\r
79 SBYTE finetune; /* (byte) Finetune (signed byte -128..+127) */
\r
80 UBYTE type; /* (byte) Type: Bit 0-1: 0 = No loop, 1 = Forward loop, */
\r
81 /* 2 = Ping-pong loop; */
\r
82 /* 4: 16-bit sampledata */
\r
83 UBYTE panning; /* (byte) Panning (0-255) */
\r
84 SBYTE relnote; /* (byte) Relative note number (signed byte) */
\r
85 UBYTE reserved; /* (byte) Reserved */
\r
86 char samplename[22]; /* (char) Sample name */
\r
90 typedef struct XMPATHEADER{
\r
91 ULONG size; /* (dword) Pattern header length */
\r
92 UBYTE packing; /* (byte) Packing type (always 0) */
\r
93 UWORD numrows; /* (word) Number of rows in pattern (1..256) */
\r
94 UWORD packsize; /* (word) Packed patterndata size */
\r
97 typedef struct MTMNOTE{
\r
102 typedef struct XMNOTE{
\r
103 UBYTE note,ins,vol,eff,dat;
\r
108 /**************************************************************************
\r
109 **************************************************************************/
\r
113 static XMHEADER *mh;
\r
115 char XM_Version[]="XM";
\r
122 if(!fread(id,17,1,modfp)) return 0;
\r
123 if(!memcmp(id,"Extended Module: ",17)) return 1;
\r
131 if(!(mh=(XMHEADER *)MyCalloc(1,sizeof(XMHEADER)))) return 0;
\r
136 void XM_Cleanup(void)
\r
138 if(mh!=NULL) free(mh);
\r
142 void XM_ReadNote(XMNOTE *n)
\r
145 memset(n,0,sizeof(XMNOTE));
\r
150 if(cmp&1) n->note=fgetc(modfp);
\r
151 if(cmp&2) n->ins=fgetc(modfp);
\r
152 if(cmp&4) n->vol=fgetc(modfp);
\r
153 if(cmp&8) n->eff=fgetc(modfp);
\r
154 if(cmp&16) n->dat=fgetc(modfp);
\r
158 n->ins=fgetc(modfp);
\r
159 n->vol=fgetc(modfp);
\r
160 n->eff=fgetc(modfp);
\r
161 n->dat=fgetc(modfp);
\r
166 UBYTE *XM_Convert(XMNOTE *xmtrack,UWORD rows)
\r
169 UBYTE note,ins,vol,eff,dat;
\r
173 for(t=0;t<rows;t++){
\r
175 note=xmtrack->note;
\r
181 if(note!=0) UniNote(note-1);
\r
183 if(ins!=0) UniInstrument(ins-1);
\r
185 /* printf("Vol:%d\n",vol); */
\r
189 case 0x6: /* volslide down */
\r
191 UniWrite(UNI_XMEFFECTA);
\r
196 case 0x7: /* volslide up */
\r
198 UniWrite(UNI_XMEFFECTA);
\r
203 /* volume-row fine volume slide is compatible with protracker
\r
204 EBx and EAx effects i.e. a zero nibble means DO NOT SLIDE, as
\r
205 opposed to 'take the last sliding value'.
\r
208 case 0x8: /* finevol down */
\r
209 UniPTEffect(0xe,0xb0 | (vol&0xf));
\r
212 case 0x9: /* finevol up */
\r
213 UniPTEffect(0xe,0xa0 | (vol&0xf));
\r
216 case 0xa: /* set vibrato speed */
\r
217 UniPTEffect(0x4,vol<<4);
\r
220 case 0xb: /* vibrato */
\r
221 UniPTEffect(0x4,vol&0xf);
\r
224 case 0xc: /* set panning */
\r
225 UniPTEffect(0x8,vol<<4);
\r
228 case 0xd: /* panning slide left */
\r
229 /* only slide when data nibble not zero: */
\r
232 UniWrite(UNI_XMEFFECTP);
\r
237 case 0xe: /* panning slide right */
\r
238 /* only slide when data nibble not zero: */
\r
241 UniWrite(UNI_XMEFFECTP);
\r
246 case 0xf: /* tone porta */
\r
247 UniPTEffect(0x3,vol<<4);
\r
251 if(vol>=0x10 && vol<=0x50){
\r
252 UniPTEffect(0xc,vol-0x10);
\r
256 /* if(eff>0xf) printf("Effect %d",eff); */
\r
260 case 'G'-55: /* G - set global volume */
\r
262 UniWrite(UNI_XMEFFECTG);
\r
266 case 'H'-55: /* H - global volume slide */
\r
267 UniWrite(UNI_XMEFFECTH);
\r
271 case 'K'-55: /* K - keyoff */
\r
275 case 'L'-55: /* L - set envelope position */
\r
278 case 'P'-55: /* P - panning slide */
\r
279 UniWrite(UNI_XMEFFECTP);
\r
283 case 'R'-55: /* R - multi retrig note */
\r
284 UniWrite(UNI_S3MEFFECTQ);
\r
288 case 'T'-55: /* T - Tremor !! (== S3M effect I) */
\r
289 UniWrite(UNI_S3MEFFECTI);
\r
294 if((dat>>4)==1){ /* X1 extra fine porta up */
\r
298 else{ /* X2 extra fine porta down */
\r
305 UniWrite(UNI_XMEFFECTA);
\r
308 else if(eff<=0xf) UniPTEffect(eff,dat);
\r
324 int t,u,v,p,numtrk;
\r
327 /* try to read module header */
\r
329 _mm_read_str(mh->id,17,modfp);
\r
330 _mm_read_str(mh->songname,21,modfp);
\r
331 _mm_read_str(mh->trackername,20,modfp);
\r
332 mh->version =_mm_read_I_UWORD(modfp);
\r
333 mh->headersize =_mm_read_I_ULONG(modfp);
\r
334 mh->songlength =_mm_read_I_UWORD(modfp);
\r
335 mh->restart =_mm_read_I_UWORD(modfp);
\r
336 mh->numchn =_mm_read_I_UWORD(modfp);
\r
337 mh->numpat =_mm_read_I_UWORD(modfp);
\r
338 mh->numins =_mm_read_I_UWORD(modfp);
\r
339 mh->flags =_mm_read_I_UWORD(modfp);
\r
340 mh->tempo =_mm_read_I_UWORD(modfp);
\r
341 mh->bpm =_mm_read_I_UWORD(modfp);
\r
342 _mm_read_UBYTES(mh->orders,256,modfp);
\r
345 myerr = ERROR_LOADING_HEADER;
\r
349 /* set module variables */
\r
351 of.initspeed=mh->tempo;
\r
352 of.inittempo=mh->bpm;
\r
353 of.modtype=DupStr(mh->trackername,20);
\r
354 of.numchn=mh->numchn;
\r
355 of.numpat=mh->numpat;
\r
356 of.numtrk=(UWORD)of.numpat*of.numchn; /* get number of channels */
\r
357 of.songname=DupStr(mh->songname,20); /* make a cstr of songname */
\r
358 of.numpos=mh->songlength; /* copy the songlength */
\r
359 of.reppos=mh->restart;
\r
360 of.numins=mh->numins;
\r
361 of.flags|=UF_XMPERIODS;
\r
362 if(mh->flags&1) of.flags|=UF_LINEAR;
\r
364 memcpy(of.positions,mh->orders,256);
\r
367 WHY THIS CODE HERE?? I CAN'T REMEMBER!
\r
370 for(t=0;t<of.numpos;t++){
\r
371 if(of.positions[t]>of.numpat) of.numpat=of.positions[t];
\r
376 /* printf("Modtype :%s\n",of.modtype);
\r
377 printf("Version :%x\n",mh->version);
\r
378 printf("Song :%s\n",of.songname);
\r
379 printf("Speed :%d,%d\n",of.initspeed,of.inittempo);
\r
380 printf("Channels:%d\n",of.numchn);
\r
381 printf("Numins :%d\n",mh->numins);
\r
383 if(!AllocTracks()) return 0;
\r
384 if(!AllocPatterns()) return 0;
\r
387 for(t=0;t<mh->numpat;t++){
\r
390 /* printf("Reading pattern %d\n",t); */
\r
392 ph.size =_mm_read_I_ULONG(modfp);
\r
393 ph.packing =_mm_read_UBYTE(modfp);
\r
394 ph.numrows =_mm_read_I_UWORD(modfp);
\r
395 ph.packsize =_mm_read_I_UWORD(modfp);
\r
397 /* printf("headln: %ld\n",ph.size); */
\r
398 /* printf("numrows: %d\n",ph.numrows); */
\r
399 /* printf("packsize:%d\n",ph.packsize); */
\r
401 of.pattrows[t]=ph.numrows;
\r
404 Gr8.. when packsize is 0, don't try to load a pattern.. it's empty.
\r
405 This bug was discovered thanks to Khyron's module..
\r
408 if(!(xmpat=(XMNOTE *)MyCalloc(ph.numrows*of.numchn,sizeof(XMNOTE)))) return 0;
\r
411 for(u=0;u<ph.numrows;u++){
\r
412 for(v=0;v<of.numchn;v++){
\r
413 XM_ReadNote(&xmpat[(v*ph.numrows)+u]);
\r
418 for(v=0;v<of.numchn;v++){
\r
419 of.tracks[numtrk++]=XM_Convert(&xmpat[v*ph.numrows],ph.numrows);
\r
425 if(!AllocInstruments()) return 0;
\r
429 for(t=0;t<of.numins;t++){
\r
432 /* read instrument header */
\r
434 ih.size =_mm_read_I_ULONG(modfp);
\r
435 _mm_read_str (ih.name, 22, modfp);
\r
436 ih.type =_mm_read_UBYTE(modfp);
\r
437 ih.numsmp =_mm_read_I_UWORD(modfp);
\r
438 ih.ssize =_mm_read_I_ULONG(modfp);
\r
440 /* printf("Size: %ld\n",ih.size);
\r
441 printf("Name: %22.22s\n",ih.name);
\r
442 printf("Samples:%d\n",ih.numsmp);
\r
443 printf("sampleheadersize:%ld\n",ih.ssize);
\r
445 d->insname=DupStr(ih.name,22);
\r
446 d->numsmp=ih.numsmp;
\r
448 if(!AllocSamples(d)) return 0;
\r
454 _mm_read_UBYTES (pth.what, 96, modfp);
\r
455 _mm_read_UBYTES (pth.volenv, 48, modfp);
\r
456 _mm_read_UBYTES (pth.panenv, 48, modfp);
\r
457 pth.volpts =_mm_read_UBYTE(modfp);
\r
458 pth.panpts =_mm_read_UBYTE(modfp);
\r
459 pth.volsus =_mm_read_UBYTE(modfp);
\r
460 pth.volbeg =_mm_read_UBYTE(modfp);
\r
461 pth.volend =_mm_read_UBYTE(modfp);
\r
462 pth.pansus =_mm_read_UBYTE(modfp);
\r
463 pth.panbeg =_mm_read_UBYTE(modfp);
\r
464 pth.panend =_mm_read_UBYTE(modfp);
\r
465 pth.volflg =_mm_read_UBYTE(modfp);
\r
466 pth.panflg =_mm_read_UBYTE(modfp);
\r
467 pth.vibflg =_mm_read_UBYTE(modfp);
\r
468 pth.vibsweep =_mm_read_UBYTE(modfp);
\r
469 pth.vibdepth =_mm_read_UBYTE(modfp);
\r
470 pth.vibrate =_mm_read_UBYTE(modfp);
\r
471 pth.volfade =_mm_read_I_UWORD(modfp);
\r
472 _mm_read_I_UWORDS(pth.reserved, 11, modfp);
\r
474 memcpy(d->samplenumber,pth.what,96);
\r
476 d->volfade=pth.volfade;
\r
478 /* printf("Volfade %x\n",d->volfade); */
\r
480 memcpy(d->volenv,pth.volenv,24);
\r
481 d->volflg=pth.volflg;
\r
482 d->volsus=pth.volsus;
\r
483 d->volbeg=pth.volbeg;
\r
484 d->volend=pth.volend;
\r
485 d->volpts=pth.volpts;
\r
487 /* printf("volume points : %d\n"
\r
498 /* scale volume envelope: */
\r
501 d->volenv[p].val<<=2;
\r
502 /* printf("%d,%d,",d->volenv[p].pos,d->volenv[p].val); */
\r
505 memcpy(d->panenv,pth.panenv,24);
\r
506 d->panflg=pth.panflg;
\r
507 d->pansus=pth.pansus;
\r
508 d->panbeg=pth.panbeg;
\r
509 d->panend=pth.panend;
\r
510 d->panpts=pth.panpts;
\r
512 /* printf("Panning points : %d\n"
\r
523 /* scale panning envelope: */
\r
526 d->panenv[p].val<<=2;
\r
527 /* printf("%d,%d,",d->panenv[p].pos,d->panenv[p].val); */
\r
530 /* for(u=0;u<256;u++){ */
\r
531 /* printf("%2.2x ",fgetc(modfp)); */
\r
536 for(u=0;u<ih.numsmp;u++){
\r
539 wh.length =_mm_read_I_ULONG (modfp);
\r
540 wh.loopstart =_mm_read_I_ULONG (modfp);
\r
541 wh.looplength =_mm_read_I_ULONG (modfp);
\r
542 wh.volume =_mm_read_UBYTE (modfp);
\r
543 wh.finetune =_mm_read_SBYTE (modfp);
\r
544 wh.type =_mm_read_UBYTE (modfp);
\r
545 wh.panning =_mm_read_UBYTE (modfp);
\r
546 wh.relnote =_mm_read_SBYTE (modfp);
\r
547 wh.reserved =_mm_read_UBYTE (modfp);
\r
548 _mm_read_str(wh.samplename, 22, modfp);
\r
550 /* printf("wav %d:%22.22s\n",u,wh.samplename); */
\r
552 q->samplename =DupStr(wh.samplename,22);
\r
553 q->length =wh.length;
\r
554 q->loopstart =wh.loopstart;
\r
555 q->loopend =wh.loopstart+wh.looplength;
\r
556 q->volume =wh.volume;
\r
557 q->c2spd =wh.finetune+128;
\r
558 q->transpose =wh.relnote;
\r
559 q->panning =wh.panning;
\r
570 /* printf("Type %u\n",wh.type); */
\r
571 /* printf("Trans %d\n",wh.relnote); */
\r
573 q->flags|=SF_OWNPAN;
\r
574 if(wh.type&0x3) q->flags|=SF_LOOP;
\r
575 if(wh.type&0x2) q->flags|=SF_BIDI;
\r
577 if(wh.type&0x10) q->flags|=SF_16BITS;
\r
578 q->flags|=SF_DELTA;
\r
579 q->flags|=SF_SIGNED;
\r
582 for(u=0;u<ih.numsmp;u++) d->samples[u].seekpos+=_mm_ftell(modfp);
\r
584 _mm_fseek(modfp,next,SEEK_CUR);
\r
598 "Portable XM loader v0.4 - for your ears only / MikMak",
\r