Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_mod.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_mod.c,v 1.2 2004/01/21 13:33:11 raph Exp $\r
24 \r
25   Generic MOD loader (Protracker, StarTracker, FastTracker, etc)\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 <ctype.h>\r
38 #include <stdio.h>\r
39 #ifdef HAVE_MEMORY_H\r
40 #include <memory.h>\r
41 #endif\r
42 #include <string.h>\r
43 \r
44 #include "mikmod_internals.h"\r
45 \r
46 #ifdef SUNOS\r
47 extern int fprintf(FILE *, const char *, ...);\r
48 #endif\r
49 \r
50 /*========== Module structure */\r
51 \r
52 typedef struct MSAMPINFO {\r
53         CHAR samplename[23];            /* 22 in module, 23 in memory */\r
54         UWORD length;\r
55         UBYTE finetune;\r
56         UBYTE volume;\r
57         UWORD reppos;\r
58         UWORD replen;\r
59 } MSAMPINFO;\r
60 \r
61 typedef struct MODULEHEADER {\r
62         CHAR songname[21];                      /* the songname.. 20 in module, 21 in memory */\r
63         MSAMPINFO samples[31];          /* all sampleinfo */\r
64         UBYTE songlength;                       /* number of patterns used */\r
65         UBYTE magic1;                           /* should be 127 */\r
66         UBYTE positions[128];           /* which pattern to play at pos */\r
67         UBYTE magic2[4];                        /* string "M.K." or "FLT4" or "FLT8" */\r
68 } MODULEHEADER;\r
69 \r
70 typedef struct MODTYPE {\r
71         CHAR id[5];\r
72         UBYTE channels;\r
73         CHAR *name;\r
74 } MODTYPE;\r
75 \r
76 typedef struct MODNOTE {\r
77         UBYTE a, b, c, d;\r
78 } MODNOTE;\r
79 \r
80 /*========== Loader variables */\r
81 \r
82 #define MODULEHEADERSIZE 0x438\r
83 \r
84 static CHAR protracker[] = "Protracker";\r
85 static CHAR startrekker[] = "Startrekker";\r
86 static CHAR fasttracker[] = "Fasttracker";\r
87 static CHAR oktalyser[] = "Oktalyser";\r
88 static CHAR oktalyzer[] = "Oktalyzer";\r
89 static CHAR taketracker[] = "TakeTracker";\r
90 static CHAR orpheus[] = "Imago Orpheus (MOD format)";\r
91 \r
92 static MODULEHEADER *mh = NULL;\r
93 static MODNOTE *patbuf = NULL;\r
94 static int modtype, trekker;\r
95 \r
96 /*========== Loader code */\r
97 \r
98 /* given the module ID, determine the number of channels and the tracker\r
99    description ; also alters modtype */\r
100 static BOOL MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)\r
101 {\r
102         modtype = trekker = 0;\r
103 \r
104         /* Protracker and variants */\r
105         if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) {\r
106                 *descr = protracker;\r
107                 modtype = 0;\r
108                 *numchn = 4;\r
109                 return 1;\r
110         }\r
111         \r
112         /* Star Tracker */\r
113         if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) &&\r
114                 (isdigit(id[3]))) {\r
115                 *descr = startrekker;\r
116                 modtype = trekker = 1;\r
117                 *numchn = id[3] - '0';\r
118                 if (*numchn == 4 || *numchn == 8)\r
119                         return 1;\r
120 #ifdef MIKMOD_DEBUG\r
121                 else\r
122                         fprintf(stderr, "\rUnknown FLT%d module type\n", *numchn);\r
123 #endif\r
124                 return 0;\r
125         }\r
126 \r
127         /* Oktalyzer (Amiga) */\r
128         if (!memcmp(id, "OKTA", 4)) {\r
129                 *descr = oktalyzer;\r
130                 modtype = 1;\r
131                 *numchn = 8;\r
132                 return 1;\r
133         }\r
134 \r
135         /* Oktalyser (Atari) */\r
136         if (!memcmp(id, "CD81", 4)) {\r
137                 *descr = oktalyser;\r
138                 modtype = 1;\r
139                 *numchn = 8;\r
140                 return 1;\r
141         }\r
142 \r
143         /* Fasttracker */\r
144         if ((!memcmp(id + 1, "CHN", 3)) && (isdigit(id[0]))) {\r
145                 *descr = fasttracker;\r
146                 modtype = 1;\r
147                 *numchn = id[0] - '0';\r
148                 return 1;\r
149         }\r
150         /* Fasttracker or Taketracker */\r
151         if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2)))\r
152                 && (isdigit(id[0])) && (isdigit(id[1]))) {\r
153                 if (id[3] == 'H') {\r
154                         *descr = fasttracker;\r
155                         modtype = 2;            /* this can also be Imago Orpheus */\r
156                 } else {\r
157                         *descr = taketracker;\r
158                         modtype = 1;\r
159                 }\r
160                 *numchn = (id[0] - '0') * 10 + (id[1] - '0');\r
161                 return 1;\r
162         }\r
163 \r
164         return 0;\r
165 }\r
166 \r
167 static BOOL MOD_Test(void)\r
168 {\r
169         UBYTE id[4], numchn;\r
170         CHAR *descr;\r
171 \r
172         _mm_fseek(modreader, MODULEHEADERSIZE, SEEK_SET);\r
173         if (!_mm_read_UBYTES(id, 4, modreader))\r
174                 return 0;\r
175 \r
176         if (MOD_CheckType(id, &numchn, &descr))\r
177                 return 1;\r
178 \r
179         return 0;\r
180 }\r
181 \r
182 static BOOL MOD_Init(void)\r
183 {\r
184         if (!(mh = (MODULEHEADER *)_mm_malloc(sizeof(MODULEHEADER))))\r
185                 return 0;\r
186         return 1;\r
187 }\r
188 \r
189 static void MOD_Cleanup(void)\r
190 {\r
191         _mm_free(mh);\r
192         _mm_free(patbuf);\r
193 }\r
194 \r
195 /*\r
196 Old (amiga) noteinfo:\r
197 \r
198 _____byte 1_____   byte2_    _____byte 3_____   byte4_\r
199 /                \ /      \  /                \ /      \\r
200 0000          0000-00000000  0000          0000-00000000\r
201 \r
202 Upper four    12 bits for    Lower four    Effect command.\r
203 bits of sam-  note period.   bits of sam-\r
204 ple number.                  ple number.\r
205 \r
206 */\r
207 \r
208 static UBYTE ConvertNote(MODNOTE *n, UBYTE lasteffect)\r
209 {\r
210         UBYTE instrument, effect, effdat, note;\r
211         UWORD period;\r
212         UBYTE lastnote = 0;\r
213 \r
214         /* extract the various information from the 4 bytes that make up a note */\r
215         instrument = (n->a & 0x10) | (n->c >> 4);\r
216         period = (((UWORD)n->a & 0xf) << 8) + n->b;\r
217         effect = n->c & 0xf;\r
218         effdat = n->d;\r
219 \r
220         /* Convert the period to a note number */\r
221         note = 0;\r
222         if (period) {\r
223                 for (note = 0; note < 7 * OCTAVE; note++)\r
224                         if (period >= npertab[note])\r
225                                 break;\r
226                 if (note == 7 * OCTAVE)\r
227                         note = 0;\r
228                 else\r
229                         note++;\r
230         }\r
231 \r
232         if (instrument) {\r
233                 /* if instrument does not exist, note cut */\r
234                 if ((instrument > 31) || (!mh->samples[instrument - 1].length)) {\r
235                         UniPTEffect(0xc, 0);\r
236                         if (effect == 0xc)\r
237                                 effect = effdat = 0;\r
238                 } else {\r
239                         /* Protracker handling */\r
240                         if (!modtype) {\r
241                                 /* if we had a note, then change instrument... */\r
242                                 if (note)\r
243                                         UniInstrument(instrument - 1);\r
244                                 /* ...otherwise, only adjust volume... */\r
245                                 else {\r
246                                         /* ...unless an effect was specified, which forces a new\r
247                                            note to be played */\r
248                                         if (effect || effdat) {\r
249                                                 UniInstrument(instrument - 1);\r
250                                                 note = lastnote;\r
251                                         } else\r
252                                                 UniPTEffect(0xc,\r
253                                                                         mh->samples[instrument -\r
254                                                                                                 1].volume & 0x7f);\r
255                                 }\r
256                         } else {\r
257                                 /* Fasttracker handling */\r
258                                 UniInstrument(instrument - 1);\r
259                                 if (!note)\r
260                                         note = lastnote;\r
261                         }\r
262                 }\r
263         }\r
264         if (note) {\r
265                 UniNote(note + 2 * OCTAVE - 1);\r
266                 lastnote = note;\r
267         }\r
268 \r
269         /* Convert pattern jump from Dec to Hex */\r
270         if (effect == 0xd)\r
271                 effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf);\r
272 \r
273         /* Volume slide, up has priority */\r
274         if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0))\r
275                 effdat &= 0xf0;\r
276 \r
277         /* Handle ``heavy'' volumes correctly */\r
278         if ((effect == 0xc) && (effdat > 0x40))\r
279                 effdat = 0x40;\r
280         \r
281         /* An isolated 100, 200 or 300 effect should be ignored (no\r
282            "standalone" porta memory in mod files). However, a sequence such\r
283            as 1XX, 100, 100, 100 is fine. */\r
284         if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)) &&\r
285                 (lasteffect < 0x10) && (effect != lasteffect))\r
286                 effect = 0;\r
287 \r
288         UniPTEffect(effect, effdat);\r
289         if (effect == 8)\r
290                 of.flags |= UF_PANNING;\r
291         \r
292         return effect;\r
293 }\r
294 \r
295 static UBYTE *ConvertTrack(MODNOTE *n, int numchn)\r
296 {\r
297         int t;\r
298         UBYTE lasteffect = 0x10;        /* non existant effect */\r
299 \r
300         UniReset();\r
301         for (t = 0; t < 64; t++) {\r
302                 lasteffect = ConvertNote(n,lasteffect);\r
303                 UniNewline();\r
304                 n += numchn;\r
305         }\r
306         return UniDup();\r
307 }\r
308 \r
309 /* Loads all patterns of a modfile and converts them into the 3 byte format. */\r
310 static BOOL ML_LoadPatterns(void)\r
311 {\r
312         int t, s, tracks = 0;\r
313 \r
314         if (!AllocPatterns())\r
315                 return 0;\r
316         if (!AllocTracks())\r
317                 return 0;\r
318         \r
319         /* Allocate temporary buffer for loading and converting the patterns */\r
320         if (!(patbuf = (MODNOTE *)_mm_calloc(64U * of.numchn, sizeof(MODNOTE))))\r
321                 return 0;\r
322 \r
323         if (trekker && of.numchn == 8) {\r
324                 /* Startrekker module dual pattern */\r
325                 for (t = 0; t < of.numpat; t++) {\r
326                         for (s = 0; s < (64U * 4); s++) {\r
327                                 patbuf[s].a = _mm_read_UBYTE(modreader);\r
328                                 patbuf[s].b = _mm_read_UBYTE(modreader);\r
329                                 patbuf[s].c = _mm_read_UBYTE(modreader);\r
330                                 patbuf[s].d = _mm_read_UBYTE(modreader);\r
331                         }\r
332                         for (s = 0; s < 4; s++)\r
333                                 if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))\r
334                                         return 0;\r
335                         for (s = 0; s < (64U * 4); s++) {\r
336                                 patbuf[s].a = _mm_read_UBYTE(modreader);\r
337                                 patbuf[s].b = _mm_read_UBYTE(modreader);\r
338                                 patbuf[s].c = _mm_read_UBYTE(modreader);\r
339                                 patbuf[s].d = _mm_read_UBYTE(modreader);\r
340                         }\r
341                         for (s = 0; s < 4; s++)\r
342                                 if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))\r
343                                         return 0;\r
344                 }\r
345         } else {\r
346                 /* Generic module pattern */\r
347                 for (t = 0; t < of.numpat; t++) {\r
348                         /* Load the pattern into the temp buffer and convert it */\r
349                         for (s = 0; s < (64U * of.numchn); s++) {\r
350                                 patbuf[s].a = _mm_read_UBYTE(modreader);\r
351                                 patbuf[s].b = _mm_read_UBYTE(modreader);\r
352                                 patbuf[s].c = _mm_read_UBYTE(modreader);\r
353                                 patbuf[s].d = _mm_read_UBYTE(modreader);\r
354                         }\r
355                         for (s = 0; s < of.numchn; s++)\r
356                                 if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, of.numchn)))\r
357                                         return 0;\r
358                 }\r
359         }\r
360         return 1;\r
361 }\r
362 \r
363 static BOOL MOD_Load(BOOL curious)\r
364 {\r
365         int t, scan;\r
366         SAMPLE *q;\r
367         MSAMPINFO *s;\r
368         CHAR *descr;\r
369 \r
370         /* try to read module header */\r
371         _mm_read_string((CHAR *)mh->songname, 20, modreader);\r
372         mh->songname[20] = 0;           /* just in case */\r
373 \r
374         for (t = 0; t < 31; t++) {\r
375                 s = &mh->samples[t];\r
376                 _mm_read_string(s->samplename, 22, modreader);\r
377                 s->samplename[22] = 0;  /* just in case */\r
378                 s->length = _mm_read_M_UWORD(modreader);\r
379                 s->finetune = _mm_read_UBYTE(modreader);\r
380                 s->volume = _mm_read_UBYTE(modreader);\r
381                 s->reppos = _mm_read_M_UWORD(modreader);\r
382                 s->replen = _mm_read_M_UWORD(modreader);\r
383         }\r
384 \r
385         mh->songlength = _mm_read_UBYTE(modreader);\r
386 \r
387         /* this fixes mods which declare more than 128 positions. \r
388          * eg: beatwave.mod */\r
389         if (mh->songlength > 128) { mh->songlength = 128; }\r
390         \r
391         mh->magic1 = _mm_read_UBYTE(modreader);\r
392         _mm_read_UBYTES(mh->positions, 128, modreader);\r
393         _mm_read_UBYTES(mh->magic2, 4, modreader);\r
394 \r
395         if (_mm_eof(modreader)) {\r
396                 _mm_errno = MMERR_LOADING_HEADER;\r
397                 return 0;\r
398         }\r
399 \r
400         /* set module variables */\r
401         of.initspeed = 6;\r
402         of.inittempo = 125;\r
403         if (!(MOD_CheckType(mh->magic2, &of.numchn, &descr))) {\r
404                 _mm_errno = MMERR_NOT_A_MODULE;\r
405                 return 0;\r
406         }\r
407         if (trekker && of.numchn == 8)\r
408                 for (t = 0; t < 128; t++)\r
409                         /* if module pretends to be FLT8, yet the order table\r
410                            contains odd numbers, chances are it's a lying FLT4... */\r
411                         if (mh->positions[t] & 1) {\r
412                                 of.numchn = 4;\r
413                                 break;\r
414                         }\r
415         if (trekker && of.numchn == 8)\r
416                 for (t = 0; t < 128; t++)\r
417                         mh->positions[t] >>= 1;\r
418 \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 \r
429         /* since some old modules embed extra patterns, we have to check the\r
430            whole list to get the samples' file offsets right - however we can find\r
431            garbage here, so check carefully */\r
432         scan = 1;\r
433         for (t = of.numpos; t < 128; t++)\r
434                 if (mh->positions[t] >= 0x80)\r
435                         scan = 0;\r
436         if (scan)\r
437                 for (t = of.numpos; t < 128; t++) {\r
438                         if (mh->positions[t] > of.numpat)\r
439                                 of.numpat = mh->positions[t];\r
440                         if ((curious) && (mh->positions[t]))\r
441                                 of.numpos = t + 1;\r
442                 }\r
443         of.numpat++;\r
444         of.numtrk = of.numpat * of.numchn;\r
445 \r
446         if (!AllocPositions(of.numpos))\r
447                 return 0;\r
448         for (t = 0; t < of.numpos; t++)\r
449                 of.positions[t] = mh->positions[t];\r
450 \r
451         /* Finally, init the sampleinfo structures  */\r
452         of.numins = of.numsmp = 31;\r
453         if (!AllocSamples())\r
454                 return 0;\r
455         s = mh->samples;\r
456         q = of.samples;\r
457         for (t = 0; t < of.numins; t++) {\r
458                 /* convert the samplename */\r
459                 q->samplename = DupStr(s->samplename, 23, 1);\r
460                 /* init the sampleinfo variables and convert the size pointers */\r
461                 q->speed = finetune[s->finetune & 0xf];\r
462                 q->volume = s->volume & 0x7f;\r
463                 q->loopstart = (ULONG)s->reppos << 1;\r
464                 q->loopend = q->loopstart + ((ULONG)s->replen << 1);\r
465                 q->length = (ULONG)s->length << 1;\r
466                 q->flags = SF_SIGNED;\r
467                 /* Imago Orpheus creates MODs with 16 bit samples, check */\r
468                 if ((modtype == 2) && (s->volume & 0x80)) {\r
469                         q->flags |= SF_16BITS;\r
470                         descr = orpheus;\r
471                 }\r
472                 if (s->replen > 2)\r
473                         q->flags |= SF_LOOP;\r
474 \r
475                 s++;\r
476                 q++;\r
477         }\r
478 \r
479         of.modtype = strdup(descr);\r
480 \r
481         if (!ML_LoadPatterns())\r
482                 return 0;\r
483         \r
484         return 1;\r
485 }\r
486 \r
487 static CHAR *MOD_LoadTitle(void)\r
488 {\r
489         CHAR s[21];\r
490 \r
491         _mm_fseek(modreader, 0, SEEK_SET);\r
492         if (!_mm_read_UBYTES(s, 20, modreader))\r
493                 return NULL;\r
494         s[20] = 0;                                      /* just in case */\r
495 \r
496         return (DupStr(s, 21, 1));\r
497 }\r
498 \r
499 /*========== Loader information */\r
500 \r
501 MIKMODAPI MLOADER load_mod = {\r
502         NULL,\r
503         "Standard module",\r
504         "MOD (31 instruments)",\r
505         MOD_Init,\r
506         MOD_Test,\r
507         MOD_Load,\r
508         MOD_Cleanup,\r
509         MOD_LoadTitle\r
510 };\r
511 \r
512 /* ex:set ts=4: */\r