Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_xm.c
1 /*      MikMod sound library\r
2         (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file\r
3         AUTHORS for complete list.\r
4 \r
5         This library is free software; you can redistribute it and/or modify\r
6         it under the terms of the GNU Library General Public License as\r
7         published by the Free Software Foundation; either version 2 of\r
8         the License, or (at your option) any later version.\r
9  \r
10         This program is distributed in the hope that it will be useful,\r
11         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13         GNU Library General Public License for more details.\r
14  \r
15         You should have received a copy of the GNU Library General Public\r
16         License along with this library; if not, write to the Free Software\r
17         Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\r
18         02111-1307, USA.\r
19 */\r
20 \r
21 /*==============================================================================\r
22 \r
23   $Id: load_xm.c,v 1.2 2004/02/06 19:29:03 raph Exp $\r
24 \r
25   Fasttracker (XM) module loader\r
26 \r
27 ==============================================================================*/\r
28 \r
29 #ifdef HAVE_CONFIG_H\r
30 #include "config.h"\r
31 #endif\r
32 \r
33 #ifdef HAVE_UNISTD_H\r
34 #include <unistd.h>\r
35 #endif\r
36 \r
37 #include <stdio.h>\r
38 #ifdef HAVE_MEMORY_H\r
39 #include <memory.h>\r
40 #endif\r
41 #include <string.h>\r
42 \r
43 #include "mikmod_internals.h"\r
44 \r
45 #ifdef SUNOS\r
46 extern int fprintf(FILE *, const char *, ...);\r
47 #endif\r
48 \r
49 /*========== Module structure */\r
50 \r
51 typedef struct XMHEADER {\r
52         CHAR  id[17];          /* ID text: 'Extended module: ' */\r
53         CHAR  songname[21];    /* Module name */\r
54         CHAR  trackername[20]; /* Tracker name */\r
55         UWORD version;         /* Version number */\r
56         ULONG headersize;      /* Header size */\r
57         UWORD songlength;      /* Song length (in patten order table) */\r
58         UWORD restart;         /* Restart position */\r
59         UWORD numchn;          /* Number of channels (2,4,6,8,10,...,32) */\r
60         UWORD numpat;          /* Number of patterns (max 256) */\r
61         UWORD numins;          /* Number of instruments (max 128) */\r
62         UWORD flags;       \r
63         UWORD tempo;           /* Default tempo */\r
64         UWORD bpm;             /* Default BPM */\r
65         UBYTE orders[256];     /* Pattern order table  */\r
66 } XMHEADER;\r
67 \r
68 typedef struct XMINSTHEADER {\r
69         ULONG size;     /* Instrument size */\r
70         CHAR  name[22]; /* Instrument name */\r
71         UBYTE type;     /* Instrument type (always 0) */\r
72         UWORD numsmp;   /* Number of samples in instrument */\r
73         ULONG ssize;\r
74 } XMINSTHEADER;\r
75 \r
76 #define XMENVCNT (12*2)\r
77 #define XMNOTECNT (8*OCTAVE)\r
78 typedef struct XMPATCHHEADER {\r
79         UBYTE what[XMNOTECNT];  /*  Sample number for all notes */\r
80         UWORD volenv[XMENVCNT]; /*  Points for volume envelope */\r
81         UWORD panenv[XMENVCNT]; /*  Points for panning envelope */\r
82         UBYTE volpts;      /*  Number of volume points */\r
83         UBYTE panpts;      /*  Number of panning points */\r
84         UBYTE volsus;      /*  Volume sustain point */\r
85         UBYTE volbeg;      /*  Volume loop start point */\r
86         UBYTE volend;      /*  Volume loop end point */\r
87         UBYTE pansus;      /*  Panning sustain point */\r
88         UBYTE panbeg;      /*  Panning loop start point */\r
89         UBYTE panend;      /*  Panning loop end point */\r
90         UBYTE volflg;      /*  Volume type: bit 0: On; 1: Sustain; 2: Loop */\r
91         UBYTE panflg;      /*  Panning type: bit 0: On; 1: Sustain; 2: Loop */\r
92         UBYTE vibflg;      /*  Vibrato type */\r
93         UBYTE vibsweep;    /*  Vibrato sweep */\r
94         UBYTE vibdepth;    /*  Vibrato depth */\r
95         UBYTE vibrate;     /*  Vibrato rate */\r
96         UWORD volfade;     /*  Volume fadeout */\r
97 } XMPATCHHEADER;\r
98 \r
99 typedef struct XMWAVHEADER {\r
100         ULONG length;         /* Sample length */\r
101         ULONG loopstart;      /* Sample loop start */\r
102         ULONG looplength;     /* Sample loop length */\r
103         UBYTE volume;         /* Volume  */\r
104         SBYTE finetune;       /* Finetune (signed byte -128..+127) */\r
105         UBYTE type;           /* Loop type */\r
106         UBYTE panning;        /* Panning (0-255) */\r
107         SBYTE relnote;        /* Relative note number (signed byte) */\r
108         UBYTE reserved;\r
109         CHAR  samplename[22]; /* Sample name */\r
110         UBYTE vibtype;        /* Vibrato type */\r
111         UBYTE vibsweep;       /* Vibrato sweep */\r
112         UBYTE vibdepth;       /* Vibrato depth */\r
113         UBYTE vibrate;        /* Vibrato rate */\r
114 } XMWAVHEADER;\r
115 \r
116 typedef struct XMPATHEADER {\r
117         ULONG size;     /* Pattern header length  */\r
118         UBYTE packing;  /* Packing type (always 0) */\r
119         UWORD numrows;  /* Number of rows in pattern (1..256) */\r
120         SWORD packsize; /* Packed patterndata size */\r
121 } XMPATHEADER;\r
122 \r
123 typedef struct XMNOTE {\r
124         UBYTE note,ins,vol,eff,dat;\r
125 } XMNOTE;\r
126 \r
127 /*========== Loader variables */\r
128 \r
129 static  XMNOTE *xmpat=NULL;\r
130 static  XMHEADER *mh=NULL;\r
131 \r
132 /* increment unit for sample array reallocation */\r
133 #define XM_SMPINCR 64\r
134 static  ULONG *nextwav=NULL;\r
135 static  XMWAVHEADER *wh=NULL,*s=NULL;\r
136 \r
137 /*========== Loader code */\r
138 \r
139 BOOL XM_Test(void)\r
140 {\r
141         UBYTE id[38];\r
142 \r
143         if(!_mm_read_UBYTES(id,38,modreader)) return 0;\r
144         if(memcmp(id,"Extended Module: ",17)) return 0;\r
145         if(id[37]==0x1a) return 1;\r
146         return 0;\r
147 }\r
148 \r
149 BOOL XM_Init(void)\r
150 {\r
151         if(!(mh=(XMHEADER *)_mm_malloc(sizeof(XMHEADER)))) return 0;\r
152         return 1;\r
153 }\r
154 \r
155 void XM_Cleanup(void)\r
156 {\r
157         _mm_free(mh);\r
158 }\r
159 \r
160 static int XM_ReadNote(XMNOTE* n)\r
161 {\r
162         UBYTE cmp,result=1;\r
163 \r
164         memset(n,0,sizeof(XMNOTE));\r
165         cmp=_mm_read_UBYTE(modreader);\r
166 \r
167         if(cmp&0x80) {\r
168                 if(cmp&1)  { result++;n->note = _mm_read_UBYTE(modreader); }\r
169                 if(cmp&2)  { result++;n->ins  = _mm_read_UBYTE(modreader); }\r
170                 if(cmp&4)  { result++;n->vol  = _mm_read_UBYTE(modreader); }\r
171                 if(cmp&8)  { result++;n->eff  = _mm_read_UBYTE(modreader); }\r
172                 if(cmp&16) { result++;n->dat  = _mm_read_UBYTE(modreader); }\r
173         } else {\r
174                 n->note = cmp;\r
175                 n->ins  = _mm_read_UBYTE(modreader);\r
176                 n->vol  = _mm_read_UBYTE(modreader);\r
177                 n->eff  = _mm_read_UBYTE(modreader);\r
178                 n->dat  = _mm_read_UBYTE(modreader);\r
179                 result += 4;\r
180         }\r
181         return result;\r
182 }\r
183 \r
184 static UBYTE* XM_Convert(XMNOTE* xmtrack,UWORD rows)\r
185 {\r
186         int t;\r
187         UBYTE note,ins,vol,eff,dat;\r
188 \r
189         UniReset();\r
190         for(t=0;t<rows;t++) {\r
191                 note = xmtrack->note;\r
192                 ins  = xmtrack->ins;\r
193                 vol  = xmtrack->vol;\r
194                 eff  = xmtrack->eff;\r
195                 dat  = xmtrack->dat;\r
196 \r
197                 if(note) {\r
198                         if(note>XMNOTECNT)\r
199                                 UniEffect(UNI_KEYFADE,0);\r
200                         else\r
201                                 UniNote(note-1);\r
202                 }\r
203                 if(ins) UniInstrument(ins-1);\r
204 \r
205                 switch(vol>>4) {\r
206                         case 0x6: /* volslide down */\r
207                                 if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol&0xf);\r
208                                 break;\r
209                         case 0x7: /* volslide up */\r
210                                 if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol<<4);\r
211                                 break;\r
212 \r
213                                 /* volume-row fine volume slide is compatible with protracker\r
214                                    EBx and EAx effects i.e. a zero nibble means DO NOT SLIDE, as\r
215                                    opposed to 'take the last sliding value'. */\r
216                         case 0x8: /* finevol down */\r
217                                 UniPTEffect(0xe,0xb0|(vol&0xf));\r
218                                 break;\r
219                         case 0x9: /* finevol up */\r
220                                 UniPTEffect(0xe,0xa0|(vol&0xf));\r
221                                 break;\r
222                         case 0xa: /* set vibrato speed */\r
223                                 UniEffect(UNI_XMEFFECT4,vol<<4);\r
224                                 break;\r
225                         case 0xb: /* vibrato */\r
226                                 UniEffect(UNI_XMEFFECT4,vol&0xf);\r
227                                 break;\r
228                         case 0xc: /* set panning */\r
229                                 UniPTEffect(0x8,vol<<4);\r
230                                 break;\r
231                         case 0xd: /* panning slide left (only slide when data not zero) */\r
232                                 if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol&0xf);\r
233                                 break;\r
234                         case 0xe: /* panning slide right (only slide when data not zero) */\r
235                                 if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol<<4);\r
236                                 break;\r
237                         case 0xf: /* tone porta */\r
238                                 UniPTEffect(0x3,vol<<4);\r
239                                 break;\r
240                         default:\r
241                                 if((vol>=0x10)&&(vol<=0x50))\r
242                                         UniPTEffect(0xc,vol-0x10);\r
243                 }\r
244 \r
245                 switch(eff) {\r
246                         case 0x4:\r
247                                 UniEffect(UNI_XMEFFECT4,dat);\r
248                                 break;\r
249                         case 0x6:\r
250                                 UniEffect(UNI_XMEFFECT6,dat);\r
251                                 break;\r
252                         case 0xa:\r
253                                 UniEffect(UNI_XMEFFECTA,dat);\r
254                                 break;\r
255                         case 0xe: /* Extended effects */\r
256                                 switch(dat>>4) {\r
257                                         case 0x1: /* XM fine porta up */\r
258                                                 UniEffect(UNI_XMEFFECTE1,dat&0xf);\r
259                                                 break;\r
260                                         case 0x2: /* XM fine porta down */\r
261                                                 UniEffect(UNI_XMEFFECTE2,dat&0xf);\r
262                                                 break;\r
263                                         case 0xa: /* XM fine volume up */\r
264                                                 UniEffect(UNI_XMEFFECTEA,dat&0xf);\r
265                                                 break;\r
266                                         case 0xb: /* XM fine volume down */\r
267                                                 UniEffect(UNI_XMEFFECTEB,dat&0xf);\r
268                                                 break;\r
269                                         default:\r
270                                                 UniPTEffect(eff,dat);\r
271                                 }\r
272                                 break;\r
273                         case 'G'-55: /* G - set global volume */\r
274                                 UniEffect(UNI_XMEFFECTG,dat>64?128:dat<<1);\r
275                                 break;\r
276                         case 'H'-55: /* H - global volume slide */\r
277                                 UniEffect(UNI_XMEFFECTH,dat);\r
278                                 break;\r
279                         case 'K'-55: /* K - keyOff and KeyFade */\r
280                                 UniEffect(UNI_KEYFADE,dat);\r
281                                 break;\r
282                         case 'L'-55: /* L - set envelope position */\r
283                                 UniEffect(UNI_XMEFFECTL,dat);\r
284                                 break;\r
285                         case 'P'-55: /* P - panning slide */\r
286                                 UniEffect(UNI_XMEFFECTP,dat);\r
287                                 break;\r
288                         case 'R'-55: /* R - multi retrig note */\r
289                                 UniEffect(UNI_S3MEFFECTQ,dat);\r
290                                 break;\r
291                         case 'T'-55: /* T - Tremor */\r
292                                 UniEffect(UNI_S3MEFFECTI,dat);\r
293                                 break;\r
294                         case 'X'-55:\r
295                                 switch(dat>>4) {\r
296                                         case 1: /* X1 - Extra Fine Porta up */\r
297                                                 UniEffect(UNI_XMEFFECTX1,dat&0xf);\r
298                                                 break;\r
299                                         case 2: /* X2 - Extra Fine Porta down */\r
300                                                 UniEffect(UNI_XMEFFECTX2,dat&0xf);\r
301                                                 break;\r
302                                 }\r
303                                 break;\r
304                         default:\r
305                                 if(eff<=0xf) {\r
306                                         /* the pattern jump destination is written in decimal,\r
307                                            but it seems some poor tracker software writes them\r
308                                            in hexadecimal... (sigh) */\r
309                                         if (eff==0xd)\r
310                                                 /* don't change anything if we're sure it's in hexa */\r
311                                                 if ((((dat&0xf0)>>4)<=9)&&((dat&0xf)<=9))\r
312                                                         /* otherwise, convert from dec to hex */\r
313                                                         dat=(((dat&0xf0)>>4)*10)+(dat&0xf);\r
314                                         UniPTEffect(eff,dat);\r
315                                 }\r
316                                 break;\r
317                 }\r
318                 UniNewline();\r
319                 xmtrack++;\r
320         }\r
321         return UniDup();\r
322 }\r
323 \r
324 static BOOL LoadPatterns(BOOL dummypat)\r
325 {\r
326         int t,u,v,numtrk;\r
327 \r
328         if(!AllocTracks()) return 0;\r
329         if(!AllocPatterns()) return 0;\r
330 \r
331         numtrk=0;\r
332         for(t=0;t<mh->numpat;t++) {\r
333                 XMPATHEADER ph;\r
334 \r
335                 ph.size     =_mm_read_I_ULONG(modreader);\r
336                 if (ph.size<(mh->version==0x0102?8:9)) {\r
337                         _mm_errno=MMERR_LOADING_PATTERN;\r
338                         return 0;\r
339                 }\r
340                 ph.packing  =_mm_read_UBYTE(modreader);\r
341                 if(ph.packing) {\r
342                         _mm_errno=MMERR_LOADING_PATTERN;\r
343                         return 0;\r
344                 }\r
345                 if(mh->version==0x0102)\r
346                         ph.numrows  =_mm_read_UBYTE(modreader)+1;\r
347                 else\r
348                         ph.numrows  =_mm_read_I_UWORD(modreader);\r
349                 ph.packsize =_mm_read_I_UWORD(modreader);\r
350 \r
351                 ph.size-=(mh->version==0x0102?8:9);\r
352                 if(ph.size)\r
353                         _mm_fseek(modreader,ph.size,SEEK_CUR);\r
354 \r
355                 of.pattrows[t]=ph.numrows;\r
356 \r
357                 if(ph.numrows) {\r
358                         if(!(xmpat=(XMNOTE*)_mm_calloc(ph.numrows*of.numchn,sizeof(XMNOTE))))\r
359                                 return 0;\r
360 \r
361                         /* when packsize is 0, don't try to load a pattern.. it's empty. */\r
362                         if(ph.packsize) \r
363                                 for(u=0;u<ph.numrows;u++) \r
364                                         for(v=0;v<of.numchn;v++) {\r
365                                                 if(!ph.packsize) break;\r
366 \r
367                                                 ph.packsize-=XM_ReadNote(&xmpat[(v*ph.numrows)+u]);\r
368                                                 if(ph.packsize<0) {\r
369                                                         free(xmpat);xmpat=NULL;\r
370                                                         _mm_errno=MMERR_LOADING_PATTERN;\r
371                                                         return 0;\r
372                                                 }\r
373                                         }\r
374 \r
375                         if(ph.packsize) {\r
376                                 _mm_fseek(modreader,ph.packsize,SEEK_CUR);\r
377                         }\r
378 \r
379                         if(_mm_eof(modreader)) {\r
380                                 free(xmpat);xmpat=NULL;\r
381                                 _mm_errno=MMERR_LOADING_PATTERN;\r
382                                 return 0;\r
383                         }\r
384 \r
385                         for(v=0;v<of.numchn;v++)\r
386                                 of.tracks[numtrk++]=XM_Convert(&xmpat[v*ph.numrows],ph.numrows);\r
387 \r
388                         free(xmpat);xmpat=NULL;\r
389                 } else {\r
390                         for(v=0;v<of.numchn;v++)\r
391                                 of.tracks[numtrk++]=XM_Convert(NULL,ph.numrows);\r
392                 }\r
393         }\r
394 \r
395         if(dummypat) {\r
396                 of.pattrows[t]=64;\r
397                 if(!(xmpat=(XMNOTE*)_mm_calloc(64*of.numchn,sizeof(XMNOTE)))) return 0;\r
398                 for(v=0;v<of.numchn;v++)\r
399                         of.tracks[numtrk++]=XM_Convert(&xmpat[v*64],64);\r
400                 free(xmpat);xmpat=NULL;\r
401         }\r
402 \r
403         return 1;\r
404 }\r
405 \r
406 static void FixEnvelope(ENVPT *cur, int pts)\r
407 {\r
408                 int u, old, tmp;\r
409                 ENVPT *prev;\r
410 \r
411                 /* Some broken XM editing program will only save the low byte\r
412                    of the position value. Try to compensate by adding the\r
413                    missing high byte. */\r
414 \r
415                 prev = cur++;\r
416                 old = prev->pos;\r
417 \r
418                 for (u = 1; u < pts; u++, prev++, cur++) {\r
419                         if (cur->pos < prev->pos) {\r
420                                 if (cur->pos < 0x100) {\r
421                                         if (cur->pos > old)     /* same hex century */\r
422                                                         tmp = cur->pos + (prev->pos - old);\r
423                                         else\r
424                                                         tmp = cur->pos | ((prev->pos + 0x100) & 0xff00);\r
425                                         old = cur->pos;\r
426                                         cur->pos = tmp;\r
427 #ifdef MIKMOD_DEBUG\r
428                                         fprintf(stderr, "\rbroken envelope position(%d/%d), %d %d -> %d\n",\r
429                                             u, pts, prev->pos, old, cur->pos);\r
430 #endif\r
431                                 } else {\r
432 #ifdef MIKMOD_DEBUG\r
433                                         /* different brokenness style... fix unknown */\r
434                                         fprintf(stderr, "\rbroken envelope position(%d/%d), %d %d\n",\r
435                                             u, pts, old, cur->pos);\r
436 #endif\r
437                                         old = cur->pos;\r
438                                 }\r
439                         } else\r
440                                 old = cur->pos;\r
441                 }\r
442 }\r
443 \r
444 static BOOL LoadInstruments(void)\r
445 {\r
446         int t,u;\r
447         INSTRUMENT *d;\r
448         ULONG next=0;\r
449         UWORD wavcnt=0;\r
450 \r
451         if(!AllocInstruments()) return 0;\r
452         d=of.instruments;\r
453         for(t=0;t<of.numins;t++,d++) {\r
454                 XMINSTHEADER ih;\r
455                 long headend;\r
456 \r
457                 memset(d->samplenumber,0xff,INSTNOTES*sizeof(UWORD));\r
458 \r
459                 /* read instrument header */\r
460                 headend     = _mm_ftell(modreader);\r
461                 ih.size     = _mm_read_I_ULONG(modreader);\r
462                 headend    += ih.size;\r
463                 _mm_read_string(ih.name, 22, modreader);\r
464                 ih.type     = _mm_read_UBYTE(modreader);\r
465                 ih.numsmp   = _mm_read_I_UWORD(modreader);\r
466 \r
467                 d->insname  = DupStr(ih.name,22,1);\r
468 \r
469                 if((SWORD)ih.size>29) {\r
470                         ih.ssize    = _mm_read_I_ULONG(modreader);\r
471                         if(((SWORD)ih.numsmp>0)&&(ih.numsmp<=XMNOTECNT)) {\r
472                                 XMPATCHHEADER pth;\r
473                                 int p;\r
474 \r
475                                 _mm_read_UBYTES (pth.what,XMNOTECNT,modreader);\r
476                                 _mm_read_I_UWORDS (pth.volenv, XMENVCNT, modreader);\r
477                                 _mm_read_I_UWORDS (pth.panenv, XMENVCNT, modreader);\r
478                                 pth.volpts      =  _mm_read_UBYTE(modreader);\r
479                                 pth.panpts      =  _mm_read_UBYTE(modreader);\r
480                                 pth.volsus      =  _mm_read_UBYTE(modreader);\r
481                                 pth.volbeg      =  _mm_read_UBYTE(modreader);\r
482                                 pth.volend      =  _mm_read_UBYTE(modreader);\r
483                                 pth.pansus      =  _mm_read_UBYTE(modreader);\r
484                                 pth.panbeg      =  _mm_read_UBYTE(modreader);\r
485                                 pth.panend      =  _mm_read_UBYTE(modreader);\r
486                                 pth.volflg      =  _mm_read_UBYTE(modreader);\r
487                                 pth.panflg      =  _mm_read_UBYTE(modreader);\r
488                                 pth.vibflg      =  _mm_read_UBYTE(modreader);\r
489                                 pth.vibsweep    =  _mm_read_UBYTE(modreader);\r
490                                 pth.vibdepth    =  _mm_read_UBYTE(modreader);\r
491                                 pth.vibrate     =  _mm_read_UBYTE(modreader);\r
492                                 pth.volfade     =  _mm_read_I_UWORD(modreader);\r
493 \r
494                                 /* read the remainder of the header\r
495                                    (2 bytes for 1.03, 22 for 1.04) */\r
496                                 for(u=headend-_mm_ftell(modreader);u;u--) _mm_read_UBYTE(modreader);\r
497 \r
498                                 /* we can't trust the envelope point count here, as some\r
499                                    modules have incorrect values (K_OSPACE.XM reports 32 volume\r
500                                    points, for example). */\r
501                                 if(pth.volpts>XMENVCNT/2) pth.volpts=XMENVCNT/2;\r
502                                 if(pth.panpts>XMENVCNT/2) pth.panpts=XMENVCNT/2;\r
503 \r
504                                 if((_mm_eof(modreader))||(pth.volpts>XMENVCNT/2)||(pth.panpts>XMENVCNT/2)) {\r
505                                         if(nextwav) { free(nextwav);nextwav=NULL; }\r
506                                         if(wh) { free(wh);wh=NULL; }\r
507                                         _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
508                                         return 0;\r
509                                 }\r
510 \r
511                                 for(u=0;u<XMNOTECNT;u++)\r
512                                         d->samplenumber[u]=pth.what[u]+of.numsmp;\r
513                                 d->volfade = pth.volfade;\r
514 \r
515 #if defined __STDC__ || defined _MSC_VER || defined MPW_C\r
516 #define XM_ProcessEnvelope(name)                                                                                \\r
517                                 for (u = 0; u < (XMENVCNT >> 1); u++) {                                 \\r
518                                         d-> name##env[u].pos = pth. name##env[u << 1];          \\r
519                                         d-> name##env[u].val = pth. name##env[(u << 1)+ 1];     \\r
520                                 }                                                                                                               \\r
521                                 if (pth. name##flg&1) d-> name##flg|=EF_ON;                             \\r
522                                 if (pth. name##flg&2) d-> name##flg|=EF_SUSTAIN;                \\r
523                                 if (pth. name##flg&4) d-> name##flg|=EF_LOOP;                   \\r
524                                 d-> name##susbeg=d-> name##susend=pth. name##sus;               \\r
525                                 d-> name##beg=pth. name##beg;                                                   \\r
526                                 d-> name##end=pth. name##end;                                                   \\r
527                                 d-> name##pts=pth. name##pts;                                                   \\r
528                                                                                                                                                 \\r
529                                 /* scale envelope */                                                                    \\r
530                                 for (p=0;p<XMENVCNT/2;p++)                                                              \\r
531                                         d-> name##env[p].val<<=2;                                                       \\r
532                                                                                                                                                 \\r
533                                 if ((d-> name##flg&EF_ON)&&(d-> name##pts<2))                   \\r
534                                         d-> name##flg&=~EF_ON\r
535 #else\r
536 #define XM_ProcessEnvelope(name)                                                                                        \\r
537                                 for (u = 0; u < (XMENVCNT >> 1); u++) {                                         \\r
538                                         d-> name/**/env[u].pos = pth. name/**/env[u << 1];              \\r
539                                         d-> name/**/env[u].val = pth. name/**/env[(u << 1)+ 1]; \\r
540                                 }                                                                                                                       \\r
541                                 if (pth. name/**/flg&1) d-> name/**/flg|=EF_ON;                         \\r
542                                 if (pth. name/**/flg&2) d-> name/**/flg|=EF_SUSTAIN;            \\r
543                                 if (pth. name/**/flg&4) d-> name/**/flg|=EF_LOOP;                       \\r
544                                 d-> name/**/susbeg=d-> name/**/susend=                                          \\r
545                                                       pth. name/**/sus;                                         \\r
546                                 d-> name/**/beg=pth. name/**/beg;                                                       \\r
547                                 d-> name/**/end=pth. name/**/end;                                                       \\r
548                                 d-> name/**/pts=pth. name/**/pts;                                                       \\r
549                                                                                                                                                         \\r
550                                 /* scale envelope */                                                                            \\r
551                                 for (p=0;p<XMENVCNT/2;p++)                                                                      \\r
552                                         d-> name/**/env[p].val<<=2;                                                             \\r
553                                                                                                                                                         \\r
554                                 if ((d-> name/**/flg&EF_ON)&&(d-> name/**/pts<2))                       \\r
555                                         d-> name/**/flg&=~EF_ON\r
556 #endif                  \r
557 \r
558                                 XM_ProcessEnvelope(vol);\r
559                                 XM_ProcessEnvelope(pan);\r
560 #undef XM_ProcessEnvelope\r
561 \r
562                                 if (d->volflg & EF_ON)\r
563                                         FixEnvelope(d->volenv, d->volpts);\r
564                                 if (d->panflg & EF_ON)\r
565                                         FixEnvelope(d->panenv, d->panpts);\r
566 \r
567                                 /* Samples are stored outside the instrument struct now, so we\r
568                                    have to load them all into a temp area, count the of.numsmp\r
569                                    along the way and then do an AllocSamples() and move\r
570                                    everything over */\r
571                                 if(mh->version>0x0103) next = 0;\r
572                                 for(u=0;u<ih.numsmp;u++,s++) {\r
573                                         /* Allocate more room for sample information if necessary */\r
574                                         if(of.numsmp+u==wavcnt) {\r
575                                                 wavcnt+=XM_SMPINCR;\r
576                                                 if(!(nextwav=realloc(nextwav,wavcnt*sizeof(ULONG)))){\r
577                                                         if(wh) { free(wh);wh=NULL; }\r
578                                                         _mm_errno = MMERR_OUT_OF_MEMORY;\r
579                                                         return 0;\r
580                                                 }\r
581                                                 if(!(wh=realloc(wh,wavcnt*sizeof(XMWAVHEADER)))) {\r
582                                                         free(nextwav);nextwav=NULL;\r
583                                                         _mm_errno = MMERR_OUT_OF_MEMORY;\r
584                                                         return 0;\r
585                                                 }\r
586                                                 s=wh+(wavcnt-XM_SMPINCR);\r
587                                         }\r
588 \r
589                                         s->length       =_mm_read_I_ULONG (modreader);\r
590                                         s->loopstart    =_mm_read_I_ULONG (modreader);\r
591                                         s->looplength   =_mm_read_I_ULONG (modreader);\r
592                                         s->volume       =_mm_read_UBYTE (modreader);\r
593                                         s->finetune     =_mm_read_SBYTE (modreader);\r
594                                         s->type         =_mm_read_UBYTE (modreader);\r
595                                         s->panning      =_mm_read_UBYTE (modreader);\r
596                                         s->relnote      =_mm_read_SBYTE (modreader);\r
597                                         s->vibtype      = pth.vibflg;\r
598                                         s->vibsweep     = pth.vibsweep;\r
599                                         s->vibdepth     = pth.vibdepth*4;\r
600                                         s->vibrate      = pth.vibrate;\r
601                                         s->reserved     =_mm_read_UBYTE (modreader);\r
602                                         _mm_read_string(s->samplename, 22, modreader);\r
603 \r
604                                         nextwav[of.numsmp+u]=next;\r
605                                         next+=s->length;\r
606 \r
607                                         if(_mm_eof(modreader)) {\r
608                                                 free(nextwav);free(wh);\r
609                                                 nextwav=NULL;wh=NULL;\r
610                                                 _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
611                                                 return 0;\r
612                                         }\r
613                                 }\r
614 \r
615                                 if(mh->version>0x0103) {\r
616                                         for(u=0;u<ih.numsmp;u++)\r
617                                                 nextwav[of.numsmp++]+=_mm_ftell(modreader);\r
618                                         _mm_fseek(modreader,next,SEEK_CUR);\r
619                                 } else\r
620                                         of.numsmp+=ih.numsmp;\r
621                         } else {\r
622                                 /* read the remainder of the header */\r
623                                 for(u=headend-_mm_ftell(modreader);u;u--) _mm_read_UBYTE(modreader);\r
624 \r
625                                 if(_mm_eof(modreader)) {\r
626                                         free(nextwav);free(wh);\r
627                                         nextwav=NULL;wh=NULL;\r
628                                         _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
629                                         return 0;\r
630                                 }\r
631                         }\r
632                 }\r
633         }\r
634 \r
635         /* sanity check */\r
636         if(!of.numsmp) {\r
637                 if(nextwav) { free(nextwav);nextwav=NULL; }\r
638                 if(wh) { free(wh);wh=NULL; }\r
639                 _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
640                 return 0;\r
641         }\r
642 \r
643         return 1;\r
644 }\r
645 \r
646 BOOL XM_Load(BOOL curious)\r
647 {\r
648         INSTRUMENT *d;\r
649         SAMPLE *q;\r
650         int t,u;\r
651         BOOL dummypat=0;\r
652         char tracker[21],modtype[60];\r
653         (void)curious;\r
654 \r
655         /* try to read module header */\r
656         _mm_read_string(mh->id,17,modreader);\r
657         _mm_read_string(mh->songname,21,modreader);\r
658         _mm_read_string(mh->trackername,20,modreader);\r
659         mh->version     =_mm_read_I_UWORD(modreader);\r
660         if((mh->version<0x102)||(mh->version>0x104)) {\r
661                 _mm_errno=MMERR_NOT_A_MODULE;\r
662                 return 0;\r
663         }\r
664         mh->headersize  =_mm_read_I_ULONG(modreader);\r
665         mh->songlength  =_mm_read_I_UWORD(modreader);\r
666         mh->restart     =_mm_read_I_UWORD(modreader);\r
667         mh->numchn      =_mm_read_I_UWORD(modreader);\r
668         mh->numpat      =_mm_read_I_UWORD(modreader);\r
669         mh->numins      =_mm_read_I_UWORD(modreader);\r
670         mh->flags       =_mm_read_I_UWORD(modreader);\r
671         mh->tempo       =_mm_read_I_UWORD(modreader);\r
672         mh->bpm         =_mm_read_I_UWORD(modreader);\r
673         if(!mh->bpm) {\r
674                 _mm_errno=MMERR_NOT_A_MODULE;\r
675                 return 0;\r
676         }\r
677         _mm_read_UBYTES(mh->orders,256,modreader);\r
678 \r
679         if(_mm_eof(modreader)) {\r
680                 _mm_errno = MMERR_LOADING_HEADER;\r
681                 return 0;\r
682         }\r
683 \r
684         /* set module variables */\r
685         of.initspeed = mh->tempo;         \r
686         of.inittempo = mh->bpm;\r
687         strncpy(tracker,mh->trackername,20);tracker[20]=0;\r
688         for(t=20;(tracker[t]<=' ')&&(t>=0);t--) tracker[t]=0;\r
689         \r
690         /* some modules have the tracker name empty */\r
691         if (!tracker[0])\r
692                 strcpy(tracker,"Unknown tracker");\r
693 \r
694 #ifdef HAVE_SNPRINTF\r
695         snprintf(modtype,60,"%s (XM format %d.%02d)",\r
696                             tracker,mh->version>>8,mh->version&0xff);\r
697 #else\r
698         sprintf(modtype,"%s (XM format %d.%02d)",\r
699                         tracker,mh->version>>8,mh->version&0xff);\r
700 #endif\r
701         of.modtype   = strdup(modtype);\r
702         of.numchn    = mh->numchn;\r
703         of.numpat    = mh->numpat;\r
704         of.numtrk    = (UWORD)of.numpat*of.numchn;   /* get number of channels */\r
705         of.songname  = DupStr(mh->songname,20,1);\r
706         of.numpos    = mh->songlength;               /* copy the songlength */\r
707         of.reppos    = mh->restart<mh->songlength?mh->restart:0;\r
708         of.numins    = mh->numins;\r
709         of.flags    |= UF_XMPERIODS | UF_INST | UF_NOWRAP | UF_FT2QUIRKS |\r
710                                    UF_PANNING;\r
711         if(mh->flags&1) of.flags |= UF_LINEAR;\r
712         of.bpmlimit  = 32;\r
713 \r
714         memset(of.chanvol,64,of.numchn);             /* store channel volumes */\r
715 \r
716         if(!AllocPositions(of.numpos+1)) return 0;\r
717         for(t=0;t<of.numpos;t++)\r
718                 of.positions[t]=mh->orders[t];\r
719 \r
720         /* We have to check for any pattern numbers in the order list greater than\r
721            the number of patterns total. If one or more is found, we set it equal to\r
722            the pattern total and make a dummy pattern to workaround the problem */\r
723         for(t=0;t<of.numpos;t++) {\r
724                 if(of.positions[t]>=of.numpat) {\r
725                         of.positions[t]=of.numpat;\r
726                         dummypat=1;\r
727                 }\r
728         }\r
729         if(dummypat) {\r
730                 of.numpat++;of.numtrk+=of.numchn;\r
731         }\r
732 \r
733         if(mh->version<0x0104) {\r
734                 if(!LoadInstruments()) return 0;\r
735                 if(!LoadPatterns(dummypat)) return 0;\r
736                 for(t=0;t<of.numsmp;t++)\r
737                         nextwav[t]+=_mm_ftell(modreader);\r
738         } else {\r
739                 if(!LoadPatterns(dummypat)) return 0;\r
740                 if(!LoadInstruments()) return 0;\r
741         }\r
742 \r
743         if(!AllocSamples()) {\r
744                 free(nextwav);free(wh);\r
745                 nextwav=NULL;wh=NULL;\r
746                 return 0;\r
747         }\r
748         q = of.samples;\r
749         s = wh;\r
750         for(u=0;u<of.numsmp;u++,q++,s++) {\r
751                 q->samplename   = DupStr(s->samplename,22,1);\r
752                 q->length       = s->length;\r
753                 q->loopstart    = s->loopstart;\r
754                 q->loopend      = s->loopstart+s->looplength;\r
755                 q->volume       = s->volume;\r
756                 q->speed        = s->finetune+128;\r
757                 q->panning      = s->panning;\r
758                 q->seekpos      = nextwav[u];\r
759                 q->vibtype      = s->vibtype;\r
760                 q->vibsweep     = s->vibsweep;\r
761                 q->vibdepth     = s->vibdepth;\r
762                 q->vibrate      = s->vibrate;\r
763 \r
764                 if(s->type & 0x10) {\r
765                         q->length    >>= 1;\r
766                         q->loopstart >>= 1;\r
767                         q->loopend   >>= 1;\r
768                 }\r
769 \r
770                 q->flags|=SF_OWNPAN|SF_DELTA|SF_SIGNED;\r
771                 if(s->type&0x3) q->flags|=SF_LOOP;\r
772                 if(s->type&0x2) q->flags|=SF_BIDI;\r
773                 if(s->type&0x10) q->flags|=SF_16BITS;\r
774         }\r
775 \r
776         d=of.instruments;\r
777         s=wh;\r
778         for(u=0;u<of.numins;u++,d++)\r
779                 for(t=0;t<XMNOTECNT;t++) {\r
780                         if (d->samplenumber[t]>=of.numsmp)\r
781                                 d->samplenote[t]=255;\r
782                         else {\r
783                                 int note=t+s[d->samplenumber[t]].relnote;\r
784                                 d->samplenote[t]=(note<0)?0:note;\r
785                         }\r
786                 }\r
787 \r
788         free(wh);free(nextwav);\r
789         wh=NULL;nextwav=NULL;\r
790         return 1;\r
791 }\r
792 \r
793 CHAR *XM_LoadTitle(void)\r
794 {\r
795         CHAR s[21];\r
796 \r
797         _mm_fseek(modreader,17,SEEK_SET);\r
798         if(!_mm_read_UBYTES(s,21,modreader)) return NULL;\r
799 \r
800         return(DupStr(s,21,1));\r
801 }\r
802 \r
803 /*========== Loader information */\r
804 \r
805 MIKMODAPI MLOADER load_xm={\r
806         NULL,\r
807         "XM",\r
808         "XM (FastTracker 2)",\r
809         XM_Init,\r
810         XM_Test,\r
811         XM_Load,\r
812         XM_Cleanup,\r
813         XM_LoadTitle\r
814 };\r
815 \r
816 /* ex:set ts=4: */\r