Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_m15.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_m15.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
24 \r
25   15 instrument MOD loader\r
26   Also supports Ultimate Sound Tracker (old M15 format)\r
27 \r
28 ==============================================================================*/\r
29 \r
30 #ifdef HAVE_CONFIG_H\r
31 #include "config.h"\r
32 #endif\r
33 \r
34 #ifdef HAVE_UNISTD_H\r
35 #include <unistd.h>\r
36 #endif\r
37 \r
38 //#include <ctype.h>\r
39 #include <stdio.h>\r
40 #ifdef HAVE_MEMORY_H\r
41 #include <memory.h>\r
42 #endif\r
43 #include <string.h>\r
44 \r
45 #include "mikmod_internals.h"\r
46 \r
47 #ifdef SUNOS\r
48 extern int fprintf(FILE *, const char *, ...);\r
49 #endif\r
50 \r
51 /*========== Module Structure */\r
52 \r
53 typedef struct MSAMPINFO {\r
54         CHAR  samplename[23];   /* 22 in module, 23 in memory */\r
55         UWORD length;\r
56         UBYTE finetune;\r
57         UBYTE volume;\r
58         UWORD reppos;\r
59         UWORD replen;\r
60 } MSAMPINFO;\r
61 \r
62 typedef struct MODULEHEADER {\r
63         CHAR      songname[21];         /* the songname.., 20 in module, 21 in memory */\r
64         MSAMPINFO samples[15];          /* all sampleinfo */\r
65         UBYTE     songlength;           /* number of patterns used */\r
66         UBYTE     magic1;               /* should be 127 */\r
67         UBYTE     positions[128];       /* which pattern to play at pos */\r
68 } MODULEHEADER;\r
69 \r
70 typedef struct MODNOTE {\r
71         UBYTE a,b,c,d;\r
72 } MODNOTE;\r
73 \r
74 /*========== Loader variables */\r
75 \r
76 static MODULEHEADER *mh = NULL;\r
77 static MODNOTE *patbuf = NULL;\r
78 static BOOL ust_loader = 0;             /* if TRUE, load as an ust module. */\r
79 \r
80 /* known file formats which can confuse the loader */\r
81 #define REJECT 2\r
82 static char *signatures[REJECT]={\r
83         "CAKEWALK",     /* cakewalk midi files */\r
84         "SZDD"          /* Microsoft compressed files */\r
85 };\r
86 static int siglen[REJECT]={8,4};\r
87 \r
88 /*========== Loader code */\r
89 \r
90 static BOOL LoadModuleHeader(MODULEHEADER *mh)\r
91 {\r
92         int t,u;\r
93 \r
94         _mm_read_string(mh->songname,20,modreader);\r
95         mh->songname[20]=0;     /* just in case */\r
96 \r
97         /* sanity check : title should contain printable characters and a bunch\r
98            of null chars */\r
99         for(t=0;t<20;t++)\r
100                 if((mh->songname[t])&&(mh->songname[t]<32)) return 0;\r
101         for(t=0;(mh->songname[t])&&(t<20);t++);\r
102         if(t<20) for(;t<20;t++) if(mh->songname[t]) return 0;\r
103 \r
104         for(t=0;t<15;t++) {\r
105                 MSAMPINFO *s=&mh->samples[t];\r
106 \r
107                 _mm_read_string(s->samplename,22,modreader);\r
108                 s->samplename[22]=0;    /* just in case */\r
109                 s->length   =_mm_read_M_UWORD(modreader);\r
110                 s->finetune =_mm_read_UBYTE(modreader);\r
111                 s->volume   =_mm_read_UBYTE(modreader);\r
112                 s->reppos   =_mm_read_M_UWORD(modreader);\r
113                 s->replen   =_mm_read_M_UWORD(modreader);\r
114 \r
115                 /* sanity check : sample title should contain printable characters and\r
116                    a bunch of null chars */\r
117                 for(u=0;u<20;u++)\r
118                         if((s->samplename[u])&&(s->samplename[u]</*32*/14)) return 0;\r
119                 for(u=0;(s->samplename[u])&&(u<20);u++);\r
120                 if(u<20) for(;u<20;u++) if(s->samplename[u]) return 0;\r
121 \r
122                 /* sanity check : finetune values */\r
123                 if(s->finetune>>4) return 0;\r
124         }\r
125 \r
126         mh->songlength  =_mm_read_UBYTE(modreader);\r
127         mh->magic1      =_mm_read_UBYTE(modreader);     /* should be 127 */\r
128 \r
129         /* sanity check : no more than 128 positions, restart position in range */\r
130         if((!mh->songlength)||(mh->songlength>128)) return 0;\r
131         /* values encountered so far are 0x6a and 0x78 */\r
132         if(((mh->magic1&0xf8)!=0x78)&&(mh->magic1!=0x6a)&&(mh->magic1>mh->songlength)) return 0;\r
133 \r
134         _mm_read_UBYTES(mh->positions,128,modreader);\r
135 \r
136         /* sanity check : pattern range is 0..63 */\r
137         for(t=0;t<128;t++)\r
138                 if(mh->positions[t]>63) return 0;\r
139 \r
140         return(!_mm_eof(modreader));\r
141 }\r
142 \r
143 /* Checks the patterns in the modfile for UST / 15-inst indications.\r
144    For example, if an effect 3xx is found, it is assumed that the song \r
145    is 15-inst.  If a 1xx effect has dat greater than 0x20, it is UST.   \r
146 \r
147    Returns:  0 indecisive; 1 = UST; 2 = 15-inst                               */\r
148 static int CheckPatternType(int numpat)\r
149 {\r
150         int t;\r
151         UBYTE eff, dat;\r
152 \r
153         for(t=0;t<numpat*(64U*4);t++) {\r
154                 /* Load the pattern into the temp buffer and scan it */\r
155                 _mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader);\r
156                 eff = _mm_read_UBYTE(modreader);\r
157                 dat = _mm_read_UBYTE(modreader);\r
158 \r
159                 switch(eff) {\r
160                         case 1:\r
161                                 if(dat>0x1f) return 1;\r
162                                 if(dat<0x3)  return 2;\r
163                                 break;\r
164                         case 2:\r
165                                 if(dat>0x1f) return 1;\r
166                                 return 2;\r
167                         case 3:\r
168                                 if (dat) return 2;\r
169                                 break;\r
170                         default:\r
171                                 return 2;\r
172                 }\r
173         }\r
174         return 0;\r
175 }\r
176 \r
177 static BOOL M15_Test(void)\r
178 {\r
179         int t, numpat;\r
180         MODULEHEADER mh;\r
181 \r
182         ust_loader = 0;\r
183         if(!LoadModuleHeader(&mh)) return 0;\r
184 \r
185         /* reject other file types */\r
186         for(t=0;t<REJECT;t++)\r
187                 if(!memcmp(mh.songname,signatures[t],siglen[t])) return 0;\r
188 \r
189         if(mh.magic1>127) return 0;\r
190         if((!mh.songlength)||(mh.songlength>mh.magic1)) return 0;\r
191 \r
192         for(t=0;t<15;t++) {\r
193                 /* all finetunes should be zero */\r
194                 if(mh.samples[t].finetune) return 0;\r
195 \r
196                 /* all volumes should be <= 64 */\r
197                 if(mh.samples[t].volume>64) return 0;\r
198 \r
199                 /* all instrument names should begin with s, st-, or a number */\r
200                 if((mh.samples[t].samplename[0]=='s')||\r
201                    (mh.samples[t].samplename[0]=='S')) {\r
202                         if((memcmp(mh.samples[t].samplename,"st-",3)) &&\r
203                            (memcmp(mh.samples[t].samplename,"ST-",3)) &&\r
204                            (*mh.samples[t].samplename))\r
205                                 ust_loader = 1;\r
206                 } else\r
207                   if(!isdigit((int)mh.samples[t].samplename[0]))\r
208                                 ust_loader = 1;\r
209 \r
210                 if(mh.samples[t].length>4999||mh.samples[t].reppos>9999) {\r
211                         ust_loader = 0;\r
212                         if(mh.samples[t].length>32768) return 0;\r
213                 }\r
214 \r
215                 /* if loop information is incorrect as words, but correct as bytes,\r
216                    this is likely to be an ust-style module */\r
217                 if((mh.samples[t].reppos+mh.samples[t].replen>mh.samples[t].length)&&\r
218                    (mh.samples[t].reppos+mh.samples[t].replen<(mh.samples[t].length<<1))){\r
219                         ust_loader = 1;\r
220                         return 1;\r
221                 }\r
222 \r
223                 if(!ust_loader) return 1; \r
224         }\r
225 \r
226         for(numpat=0,t=0;t<mh.songlength;t++) \r
227                 if(mh.positions[t]>numpat)\r
228                         numpat = mh.positions[t];\r
229         numpat++;\r
230         switch(CheckPatternType(numpat)) {\r
231                 case 0:   /* indecisive, so check more clues... */\r
232                         break;\r
233                 case 1:\r
234                         ust_loader = 1;\r
235                         break;\r
236                 case 2:\r
237                         ust_loader = 0;\r
238                         break;\r
239         }\r
240         return 1;\r
241 }\r
242 \r
243 static BOOL M15_Init(void)\r
244 {\r
245         if(!(mh=(MODULEHEADER*)_mm_malloc(sizeof(MODULEHEADER)))) return 0;\r
246         return 1;\r
247 }\r
248 \r
249 static void M15_Cleanup(void)\r
250 {\r
251         _mm_free(mh);\r
252         _mm_free(patbuf);\r
253 }\r
254 \r
255 /*\r
256 Old (amiga) noteinfo:\r
257 \r
258  _____byte 1_____   byte2_    _____byte 3_____   byte4_\r
259 /                \ /      \  /                \ /      \\r
260 0000          0000-00000000  0000          0000-00000000\r
261 \r
262 Upper four    12 bits for    Lower four    Effect command.\r
263 bits of sam-  note period.   bits of sam-\r
264 ple number.                  ple number.\r
265 */\r
266 \r
267 static UBYTE M15_ConvertNote(MODNOTE* n, UBYTE lasteffect)\r
268 {\r
269         UBYTE instrument,effect,effdat,note;\r
270         UWORD period;\r
271         UBYTE lastnote=0;\r
272 \r
273         /* decode the 4 bytes that make up a single note */\r
274         instrument = n->c>>4;\r
275         period     = (((UWORD)n->a&0xf)<<8)+n->b;\r
276         effect     = n->c&0xf;\r
277         effdat     = n->d;\r
278 \r
279         /* Convert the period to a note number */\r
280         note=0;\r
281         if(period) {\r
282                 for(note=0;note<7*OCTAVE;note++)\r
283                         if(period>=npertab[note]) break;\r
284                 if(note==7*OCTAVE) note=0;\r
285                 else note++;\r
286         }\r
287 \r
288         if(instrument) {\r
289                 /* if instrument does not exist, note cut */\r
290                 if((instrument>15)||(!mh->samples[instrument-1].length)) {\r
291                         UniPTEffect(0xc,0);\r
292                         if(effect==0xc) effect=effdat=0;\r
293                 } else {\r
294                         /* if we had a note, then change instrument... */\r
295                         if(note)\r
296                                 UniInstrument(instrument-1);\r
297                         /* ...otherwise, only adjust volume... */\r
298                         else {\r
299                                 /* ...unless an effect was specified, which forces a new note\r
300                                    to be played */\r
301                                 if(effect||effdat) {\r
302                                         UniInstrument(instrument-1);\r
303                                         note=lastnote;\r
304                                 } else\r
305                                         UniPTEffect(0xc,mh->samples[instrument-1].volume&0x7f);\r
306                         }\r
307                 }\r
308         }\r
309         if(note) {\r
310                 UniNote(note+2*OCTAVE-1);\r
311                 lastnote=note;\r
312         }\r
313 \r
314         /* Convert pattern jump from Dec to Hex */\r
315         if(effect == 0xd)\r
316                 effdat=(((effdat&0xf0)>>4)*10)+(effdat&0xf);\r
317 \r
318         /* Volume slide, up has priority */\r
319         if((effect==0xa)&&(effdat&0xf)&&(effdat&0xf0))\r
320                 effdat&=0xf0;\r
321 \r
322         /* Handle ``heavy'' volumes correctly */\r
323         if ((effect == 0xc) && (effdat > 0x40))\r
324                 effdat = 0x40;\r
325 \r
326         if(ust_loader) {\r
327                 switch(effect) {\r
328                         case 0:\r
329                         case 3:\r
330                                 break;\r
331                         case 1:\r
332                                 UniPTEffect(0,effdat);\r
333                                 break;\r
334                         case 2:  \r
335                                 if(effdat&0xf) UniPTEffect(1,effdat&0xf);\r
336                                 else if(effdat>>2)  UniPTEffect(2,effdat>>2);\r
337                                 break;\r
338                         default:\r
339                                 UniPTEffect(effect,effdat);\r
340                                 break;\r
341                 }\r
342         } else {\r
343                 /* An isolated 100, 200 or 300 effect should be ignored (no\r
344                    "standalone" porta memory in mod files). However, a sequence\r
345                    such as 1XX, 100, 100, 100 is fine. */\r
346                 if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)) &&\r
347                         (lasteffect < 0x10) && (effect != lasteffect))\r
348                         effect = 0;\r
349 \r
350                 UniPTEffect(effect,effdat);\r
351         }\r
352         if (effect == 8)\r
353                 of.flags |= UF_PANNING;\r
354         \r
355         return effect;\r
356 }\r
357 \r
358 static UBYTE *M15_ConvertTrack(MODNOTE* n)\r
359 {\r
360         int t;\r
361         UBYTE lasteffect = 0x10;        /* non existant effect */\r
362 \r
363         UniReset();\r
364         for(t=0;t<64;t++) {\r
365                 lasteffect = M15_ConvertNote(n,lasteffect);\r
366                 UniNewline();\r
367                 n+=4;\r
368         }\r
369         return UniDup();\r
370 }\r
371 \r
372 /* Loads all patterns of a modfile and converts them into the 3 byte format. */\r
373 static BOOL M15_LoadPatterns(void)\r
374 {\r
375         int t,s,tracks=0;\r
376 \r
377         if(!AllocPatterns()) return 0;\r
378         if(!AllocTracks()) return 0;\r
379 \r
380         /* Allocate temporary buffer for loading and converting the patterns */\r
381         if(!(patbuf=(MODNOTE*)_mm_calloc(64U*4,sizeof(MODNOTE)))) return 0;\r
382 \r
383         for(t=0;t<of.numpat;t++) {\r
384                 /* Load the pattern into the temp buffer and convert it */\r
385                 for(s=0;s<(64U*4);s++) {\r
386                         patbuf[s].a=_mm_read_UBYTE(modreader);\r
387                         patbuf[s].b=_mm_read_UBYTE(modreader);\r
388                         patbuf[s].c=_mm_read_UBYTE(modreader);\r
389                         patbuf[s].d=_mm_read_UBYTE(modreader);\r
390                 }\r
391 \r
392                 for(s=0;s<4;s++)\r
393                         if(!(of.tracks[tracks++]=M15_ConvertTrack(patbuf+s))) return 0;\r
394         }\r
395         return 1;\r
396 }\r
397 \r
398 static BOOL M15_Load(BOOL curious)\r
399 {\r
400         int t,scan;\r
401         SAMPLE *q;\r
402         MSAMPINFO *s;\r
403 \r
404         /* try to read module header */\r
405         if(!LoadModuleHeader(mh)) {\r
406                 _mm_errno = MMERR_LOADING_HEADER;\r
407                 return 0;\r
408         }\r
409 \r
410         if(ust_loader)\r
411                 of.modtype = strdup("Ultimate Soundtracker");\r
412         else\r
413                 of.modtype = strdup("Soundtracker");\r
414 \r
415         /* set module variables */\r
416         of.initspeed = 6;\r
417         of.inittempo = 125;\r
418         of.numchn    = 4;                               \r
419         of.songname  = DupStr(mh->songname,21,1);\r
420         of.numpos    = mh->songlength;\r
421         of.reppos    = 0;\r
422 \r
423         /* Count the number of patterns */\r
424         of.numpat = 0;\r
425         for(t=0;t<of.numpos;t++)\r
426                 if(mh->positions[t]>of.numpat)\r
427                         of.numpat=mh->positions[t];\r
428         /* since some old modules embed extra patterns, we have to check the\r
429            whole list to get the samples' file offsets right - however we can find\r
430            garbage here, so check carefully */\r
431         scan=1;\r
432         for(t=of.numpos;t<128;t++)\r
433                 if(mh->positions[t]>=0x80) scan=0;\r
434         if (scan)\r
435                 for(t=of.numpos;t<128;t++) {\r
436                         if(mh->positions[t]>of.numpat)\r
437                                 of.numpat=mh->positions[t];\r
438                         if((curious)&&(mh->positions[t])) of.numpos=t+1;\r
439                 }\r
440         of.numpat++;\r
441         of.numtrk = of.numpat*of.numchn;\r
442 \r
443         if(!AllocPositions(of.numpos)) return 0;\r
444         for(t=0;t<of.numpos;t++)\r
445                 of.positions[t]=mh->positions[t];\r
446 \r
447         /* Finally, init the sampleinfo structures */\r
448         of.numins=of.numsmp=15;\r
449         if(!AllocSamples()) return 0;\r
450 \r
451         s = mh->samples;\r
452         q = of.samples;\r
453 \r
454         for(t=0;t<of.numins;t++) {\r
455                 /* convert the samplename */\r
456                 q->samplename = DupStr(s->samplename,23,1);\r
457 \r
458                 /* init the sampleinfo variables and convert the size pointers */\r
459                 q->speed     = finetune[s->finetune&0xf];\r
460                 q->volume    = s->volume;\r
461                 if(ust_loader)\r
462                         q->loopstart = s->reppos;\r
463                 else\r
464                         q->loopstart = s->reppos<<1;\r
465                 q->loopend   = q->loopstart+(s->replen<<1);\r
466                 q->length    = s->length<<1;\r
467 \r
468                 q->flags = SF_SIGNED;\r
469                 if(ust_loader) q->flags |= SF_UST_LOOP;\r
470                 if(s->replen>2) q->flags |= SF_LOOP;\r
471 \r
472                 s++;\r
473                 q++;\r
474         }\r
475 \r
476         if(!M15_LoadPatterns()) return 0;\r
477         ust_loader = 0;\r
478 \r
479         return 1;\r
480 }\r
481 \r
482 static CHAR *M15_LoadTitle(void)\r
483 {\r
484         CHAR s[21];\r
485 \r
486         _mm_fseek(modreader,0,SEEK_SET);\r
487         if(!_mm_read_UBYTES(s,20,modreader)) return NULL;\r
488         s[20]=0;        /* just in case */\r
489         return(DupStr(s,21,1));\r
490 }\r
491 \r
492 /*========== Loader information */\r
493 \r
494 MIKMODAPI MLOADER load_m15={\r
495         NULL,\r
496         "15-instrument module",\r
497         "MOD (15 instrument)",\r
498         M15_Init,\r
499         M15_Test,\r
500         M15_Load,\r
501         M15_Cleanup,\r
502         M15_LoadTitle\r
503 };\r
504 \r
505 /* ex:set ts=4: */\r