Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_asy.c
1 /*      MikMod sound library\r
2         (c) 2004, Raphael Assenat and others - see file AUTHORS for\r
3         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_asy.c,v 1.3 2004/01/28 01:18:22 raph Exp $\r
24 \r
25   ASYLUM Music Format v1.0 (.amf) loader\r
26   adapted from load_mod.c by Raphael Assenat <raph@raphnet.net>,\r
27   with the help of the AMF2MOD utility sourcecode,\r
28   written to convert crusader's amf files into 8\r
29   channels mod file in 1995 by Mr. P / Powersource\r
30   mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca \r
31   \r
32 \r
33 ==============================================================================*/\r
34 \r
35 #ifdef HAVE_CONFIG_H\r
36 #include "config.h"\r
37 #endif\r
38 \r
39 #ifdef HAVE_UNISTD_H\r
40 #include <unistd.h>\r
41 #endif\r
42 \r
43 //#include <ctype.h>\r
44 #include <string.h>\r
45 \r
46 #include "mikmod_internals.h"\r
47 \r
48 /*========== Module structure */\r
49 \r
50 typedef struct MSAMPINFO {\r
51         CHAR samplename[24];\r
52         UBYTE finetune;\r
53         UBYTE volume;\r
54         ULONG length;\r
55         ULONG reppos;\r
56         ULONG replen;\r
57 } MSAMPINFO;\r
58 \r
59 typedef struct MODULEHEADER {\r
60         CHAR songname[21];\r
61         UBYTE num_patterns;     /* number of patterns used */\r
62         UBYTE num_orders;\r
63         UBYTE positions[256];   /* which pattern to play at pos */\r
64         MSAMPINFO samples[64];  /* all sampleinfo */\r
65 } MODULEHEADER;\r
66 \r
67 typedef struct MODTYPE {\r
68         CHAR id[5];\r
69         UBYTE channels;\r
70         CHAR *name;\r
71 } MODTYPE;\r
72 \r
73 typedef struct MODNOTE {\r
74         UBYTE a, b, c, d;\r
75 } MODNOTE;\r
76 \r
77 /* This table is taken from AMF2MOD.C\r
78  * written in 1995 by Mr. P / Powersource\r
79  * mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca */ \r
80 UWORD periodtable[]={6848,6464,6096,5760,5424,5120,4832,4560,4304,\r
81         4064,3840,3628,3424,3232,3048,2880,2712,2560,\r
82         2416,2280,2152,2032,1920,1814,1712,1616,1524,\r
83         1440,1356,1280,1208,1140,1076,1016, 960, 907,\r
84         856, 808, 762, 720, 678, 640, 604, 570, 538,\r
85         508, 480, 453, 428, 404, 381, 360, 339, 320,\r
86         302, 285, 269, 254, 240, 226, 214, 202, 190,\r
87         180, 170, 160, 151, 143, 135, 127, 120, 113,\r
88         107, 101,  95,  90,  85,  80,  75,  71,  67,\r
89         63,  60,  56,  53,  50,  47,  45,  42,  40,\r
90         37,  35,  33,  31,  30,  28};\r
91 \r
92 /*========== Loader variables */\r
93 \r
94 static CHAR asylum[] = "Asylum 1.0";\r
95 \r
96 static MODULEHEADER *mh = NULL;\r
97 static MODNOTE *patbuf = NULL;\r
98 static int modtype = 0;\r
99 \r
100 /*========== Loader code */\r
101 \r
102 static BOOL ASY_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)\r
103 {\r
104         if (!memcmp(id, "ASYLUM Music Format V1.0", 24))\r
105         {\r
106                 *descr = asylum;\r
107                 *numchn = 8;\r
108                 modtype = 1;\r
109                 return 1;\r
110         }\r
111         \r
112         return 0;\r
113 }\r
114 \r
115 static BOOL ASY_Test(void)\r
116 {\r
117         UBYTE namestring[24], numchn;\r
118         CHAR *descr;\r
119 \r
120         /* Read the magic string */\r
121         _mm_fseek(modreader, 0, SEEK_SET);\r
122         if (!_mm_read_UBYTES(namestring, 24, modreader))\r
123                 return 0;\r
124 \r
125         /* Test if the string is what we expect */\r
126         if (ASY_CheckType(namestring, &numchn, &descr))\r
127                 return 1;\r
128 \r
129         return 0;\r
130 }\r
131 \r
132 static BOOL ASY_Init(void)\r
133 {\r
134         if (!(mh = (MODULEHEADER *)_mm_malloc(sizeof(MODULEHEADER))))\r
135                 return 0;\r
136         return 1;\r
137 }\r
138 \r
139 static void ASY_Cleanup(void)\r
140 {\r
141         _mm_free(mh);\r
142         _mm_free(patbuf);\r
143 }\r
144 \r
145 static void ConvertNote(MODNOTE *n)\r
146 {\r
147         UBYTE instrument, effect, effdat, note;\r
148         UWORD period;\r
149         UBYTE lastnote = 0;\r
150         \r
151         instrument = n->b&0x1f;\r
152         effect = n->c;\r
153         effdat = n->d;\r
154 \r
155         /* convert amf note to mod period */\r
156         if (n->a) {\r
157                 period = periodtable[n->a];\r
158         } else {\r
159                 period = 0;\r
160         }\r
161         \r
162         /* Convert the period to a note number */\r
163         note = 0;\r
164         if (period) \r
165         {\r
166                 for (note = 0; note < 7 * OCTAVE; note++)\r
167                         if (period >= npertab[note])\r
168                                 break;\r
169                 if (note == 7 * OCTAVE)\r
170                         note = 0;\r
171                 else\r
172                         note++;\r
173         }\r
174 \r
175         if (instrument) {\r
176                 /* if instrument does not exist, note cut */\r
177                 if ((instrument > 31) || (!mh->samples[instrument - 1].length)) {\r
178                         UniPTEffect(0xc, 0);\r
179                         if (effect == 0xc)\r
180                                 effect = effdat = 0;\r
181                 } else {\r
182                         /* Protracker handling */\r
183                         if (!modtype) {\r
184                                 /* if we had a note, then change instrument...*/\r
185                                 if (note)\r
186                                         UniInstrument(instrument - 1);\r
187                                 /* ...otherwise, only adjust volume... */\r
188                                 else {\r
189                                         /* ...unless an effect was specified, \r
190                                          * which forces a new note to be \r
191                                          * played */\r
192                                         if (effect || effdat) {\r
193                                                 UniInstrument(instrument - 1);\r
194                                                 note = lastnote;\r
195                                         } else\r
196                                                 UniPTEffect(0xc,\r
197                                                         mh->samples[instrument -\r
198                                                         1].volume & 0x7f);\r
199                                 }\r
200                         } else {\r
201                                 /* Fasttracker handling */\r
202                                 UniInstrument(instrument - 1);\r
203                                 if (!note)\r
204                                         note = lastnote;\r
205                         }\r
206                 }\r
207         }\r
208         if (note) {\r
209                 UniNote(note + 2 * OCTAVE - 1);\r
210                 lastnote = note;\r
211         }\r
212 \r
213         /* Convert pattern jump from Dec to Hex */\r
214         if (effect == 0xd)\r
215                 effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf);\r
216 \r
217         /* Volume slide, up has priority */\r
218         if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0))\r
219                 effdat &= 0xf0;\r
220 \r
221         UniPTEffect(effect, effdat);\r
222 }\r
223 \r
224 static UBYTE *ConvertTrack(MODNOTE *n)\r
225 {\r
226         int t;\r
227 \r
228         UniReset();\r
229         for (t = 0; t < 64; t++) {\r
230                 ConvertNote(n);\r
231                 UniNewline();\r
232                 n += of.numchn;\r
233         }\r
234         return UniDup();\r
235 }\r
236 \r
237 /* Loads all patterns of a modfile and converts them into the 3 byte format. */\r
238 static BOOL ML_LoadPatterns(void)\r
239 {\r
240         int t, s, tracks = 0;\r
241 \r
242         if (!AllocPatterns()) {\r
243                 return 0;\r
244         }\r
245         if (!AllocTracks()) {\r
246                 return 0;\r
247         }\r
248         \r
249         /* Allocate temporary buffer for loading and converting the patterns */\r
250         if (!(patbuf = (MODNOTE *)_mm_calloc(64U * of.numchn, sizeof(MODNOTE))))\r
251                 return 0;\r
252 \r
253 \r
254         /* patterns start here */\r
255         _mm_fseek(modreader, 0xA66, SEEK_SET);\r
256         for (t = 0; t < of.numpat; t++) {\r
257                 /* Load the pattern into the temp buffer and convert it */\r
258                 for (s = 0; s < (64U * of.numchn); s++) {\r
259                         patbuf[s].a = _mm_read_UBYTE(modreader);\r
260                         patbuf[s].b = _mm_read_UBYTE(modreader);\r
261                         patbuf[s].c = _mm_read_UBYTE(modreader);\r
262                         patbuf[s].d = _mm_read_UBYTE(modreader);\r
263                 }\r
264                 for (s = 0; s < of.numchn; s++) {\r
265                         if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s))) {\r
266                                 return 0;\r
267                         }\r
268                 }\r
269         }\r
270         return 1;\r
271 }\r
272 \r
273 static BOOL ASY_Load(BOOL curious)\r
274 {\r
275         int t;\r
276         SAMPLE *q;\r
277         MSAMPINFO *s;\r
278         CHAR *descr=asylum;\r
279         ULONG seekpos;\r
280         (void)curious;\r
281 \r
282         // no title in asylum amf files :(\r
283         strcpy(mh->songname, "");\r
284 \r
285         _mm_fseek(modreader, 0x23, SEEK_SET);\r
286         mh->num_patterns = _mm_read_UBYTE(modreader);\r
287         mh->num_orders = _mm_read_UBYTE(modreader);\r
288         \r
289         // skip unknown byte\r
290         _mm_read_UBYTE(modreader);\r
291         _mm_read_UBYTES(mh->positions, 256, modreader);\r
292         \r
293         /* read samples headers*/\r
294         for (t = 0; t < 64; t++) {\r
295                 s = &mh->samples[t];\r
296                 \r
297                 _mm_fseek(modreader, 0x126 + (t*37), SEEK_SET);\r
298                 \r
299                 _mm_read_string(s->samplename, 22, modreader);\r
300                 s->samplename[21] = 0;  /* just in case */\r
301                 \r
302                 s->finetune = _mm_read_UBYTE(modreader);\r
303                 s->volume = _mm_read_UBYTE(modreader);\r
304                 _mm_read_UBYTE(modreader); // skip unknown byte\r
305                 s->length = _mm_read_I_ULONG(modreader);\r
306                 s->reppos = _mm_read_I_ULONG(modreader);\r
307                 s->replen = _mm_read_I_ULONG(modreader);\r
308         }\r
309 \r
310         if (_mm_eof(modreader)) {\r
311                 _mm_errno = MMERR_LOADING_HEADER;\r
312                 return 0;\r
313         }\r
314 \r
315         /* set module variables */\r
316         of.initspeed = 6;\r
317         of.inittempo = 125;\r
318         of.numchn = 8;\r
319         modtype = 0;\r
320         of.songname = DupStr(mh->songname, 21, 1);\r
321         of.numpos = mh->num_orders;\r
322         of.reppos = 0;\r
323         of.numpat = mh->num_patterns;\r
324         of.numtrk = of.numpat * of.numchn;\r
325 \r
326         \r
327         /* Copy positions (orders) */\r
328         if (!AllocPositions(of.numpos))\r
329                 return 0;\r
330         for (t = 0; t < of.numpos; t++) {\r
331                 of.positions[t] = mh->positions[t];\r
332         }\r
333 \r
334         /* Finally, init the sampleinfo structures  */\r
335         of.numins = 31;\r
336         of.numsmp = 31;\r
337         if (!AllocSamples())\r
338                 return 0;\r
339         s = mh->samples;\r
340         q = of.samples;\r
341         seekpos = 2662+(2048*(of.numpat));\r
342         for (t = 0; t < of.numins; t++) {\r
343                 /* convert the samplename */\r
344                 q->samplename = DupStr(s->samplename, 23, 1);\r
345                 \r
346                 /* init the sampleinfo variables */\r
347                 q->speed = finetune[s->finetune & 0xf];\r
348                 q->volume = s->volume & 0x7f;\r
349 \r
350                 q->loopstart = (ULONG)s->reppos;\r
351                 q->loopend = (ULONG)q->loopstart + (s->replen);\r
352                 q->length = (ULONG)s->length;\r
353                 \r
354                 q->flags = SF_SIGNED;\r
355         \r
356                 q->seekpos = seekpos;\r
357                 seekpos += q->length;\r
358                 \r
359                 if ((s->replen) > 2) {\r
360                         q->flags |= SF_LOOP;\r
361                 }\r
362                 \r
363                 /* fix replen if repend > length */\r
364                 if (q->loopend > q->length)\r
365                         q->loopend = q->length;\r
366 \r
367                 s++;\r
368                 q++;\r
369         }\r
370 \r
371         of.modtype = strdup(descr);\r
372 \r
373         if (!ML_LoadPatterns())\r
374                 return 0;\r
375 \r
376         return 1;\r
377 }\r
378 \r
379 static CHAR *ASY_LoadTitle(void)\r
380 {\r
381         CHAR *s = ""; // no titles\r
382 \r
383         return (DupStr(s, 21, 1));\r
384 }\r
385 \r
386 /*========== Loader information */\r
387 \r
388 MLOADER load_asy = {\r
389         NULL,\r
390         "AMF",\r
391         "AMF (ASYLUM Music Format V1.0)",\r
392         ASY_Init,\r
393         ASY_Test,\r
394         ASY_Load,\r
395         ASY_Cleanup,\r
396         ASY_LoadTitle\r
397 };\r
398 \r
399 /* ex:set ts=4: */\r