Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_gdm.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_gdm.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
24 \r
25   General DigiMusic (GDM) module loader\r
26 \r
27 ==============================================================================*/\r
28 \r
29 /*\r
30 \r
31         Written by Kev Vance<kvance@zeux.org>\r
32         based on the file format description written by 'MenTaLguY'\r
33                                                                 <mental@kludge.org>\r
34 \r
35 */\r
36 \r
37 #ifdef HAVE_CONFIG_H\r
38 #include "config.h"\r
39 #endif\r
40 \r
41 #ifdef HAVE_UNISTD_H\r
42 #include <unistd.h>\r
43 #endif\r
44 \r
45 #include <stdio.h>\r
46 #ifdef HAVE_MEMORY_H\r
47 #include <memory.h>\r
48 #endif\r
49 #include <string.h>\r
50 \r
51 #include "mikmod_internals.h"\r
52 \r
53 #ifdef SUNOS\r
54 extern int fprintf(FILE *, const char *, ...);\r
55 #endif\r
56 \r
57 typedef struct GDMNOTE {\r
58         UBYTE note;\r
59         UBYTE samp;\r
60         struct {\r
61                 UBYTE effect;\r
62                 UBYTE param;\r
63         } effect[4];\r
64 } GDMNOTE;\r
65 \r
66 typedef GDMNOTE GDMTRACK[64];\r
67 \r
68 typedef struct GDMHEADER {\r
69         CHAR  id1[4];\r
70         CHAR  songname[32];\r
71         CHAR  author[32];\r
72         CHAR  eofmarker[3];\r
73         CHAR  id2[4];\r
74 \r
75         UBYTE majorver;\r
76         UBYTE minorver;\r
77         UWORD trackerid;\r
78         UBYTE t_majorver;\r
79         UBYTE t_minorver;\r
80         UBYTE pantable[32];\r
81         UBYTE mastervol;\r
82         UBYTE mastertempo;\r
83         UBYTE masterbpm;\r
84         UWORD flags;\r
85 \r
86         ULONG orderloc;\r
87         UBYTE ordernum;\r
88         ULONG patternloc;\r
89         UBYTE patternnum;\r
90         ULONG samhead;\r
91         ULONG samdata;\r
92         UBYTE samnum;\r
93         ULONG messageloc;\r
94         ULONG messagelen;\r
95         ULONG scrollyloc;\r
96         UWORD scrollylen;\r
97         ULONG graphicloc;\r
98         UWORD graphiclen;\r
99 } GDMHEADER;\r
100 \r
101 typedef struct GDMSAMPLE {\r
102         CHAR  sampname[32];\r
103         CHAR  filename[13];\r
104         UBYTE ems;\r
105         ULONG length;\r
106         ULONG loopbeg;\r
107         ULONG loopend;\r
108         UBYTE flags;\r
109         UWORD c4spd;\r
110         UBYTE vol;\r
111         UBYTE pan;\r
112 } GDMSAMPLE;\r
113 \r
114 static GDMHEADER *mh=NULL;      /* pointer to GDM header */\r
115 static GDMNOTE *gdmbuf=NULL;    /* pointer to a complete GDM pattern */\r
116 \r
117 CHAR GDM_Version[]="General DigiMusic 1.xx";\r
118 \r
119 BOOL GDM_Test(void)\r
120 {\r
121         /* test for gdm magic numbers */\r
122         UBYTE id[4];\r
123 \r
124         _mm_fseek(modreader,0x00,SEEK_SET);\r
125         if (!_mm_read_UBYTES(id,4,modreader))\r
126                 return 0;\r
127         if (!memcmp(id,"GDM\xfe",4)) {\r
128                 _mm_fseek(modreader,71,SEEK_SET);\r
129                 if (!_mm_read_UBYTES(id,4,modreader))\r
130                         return 0;\r
131                 if (!memcmp(id,"GMFS",4))\r
132                         return 1;\r
133         }\r
134         return 0;\r
135 }\r
136 \r
137 BOOL GDM_Init(void)\r
138 {\r
139         if (!(gdmbuf=(GDMNOTE*)_mm_malloc(32*64*sizeof(GDMNOTE)))) return 0;\r
140         if (!(mh=(GDMHEADER*)_mm_malloc(sizeof(GDMHEADER)))) return 0;\r
141 \r
142         return 1;\r
143 }\r
144 \r
145 void GDM_Cleanup(void)\r
146 {\r
147         _mm_free(mh);\r
148         _mm_free(gdmbuf);\r
149 }\r
150 \r
151 BOOL GDM_ReadPattern(void)\r
152 {\r
153         int pos,flag,ch,i,maxch;\r
154         GDMNOTE n;\r
155         UWORD length,x=0;\r
156 \r
157         /* get pattern length */\r
158         length=_mm_read_I_UWORD(modreader)-2;\r
159 \r
160         /* clear pattern data */\r
161         memset(gdmbuf,255,32*64*sizeof(GDMNOTE));\r
162         pos=0;\r
163         maxch=0;\r
164 \r
165         while (x<length) {\r
166                 memset(&n,255,sizeof(GDMNOTE));\r
167                 flag=_mm_read_UBYTE(modreader);\r
168                 x++;\r
169 \r
170                 if (_mm_eof(modreader)) {\r
171                         _mm_errno=MMERR_LOADING_PATTERN;\r
172                         return 0;\r
173                 }\r
174 \r
175                 ch=flag&31;\r
176                 if (ch>maxch) maxch=ch;\r
177                 if (!flag) {\r
178                         pos++;\r
179                         continue;\r
180                 }\r
181                 if (flag&0x60) {\r
182                         if (flag&0x20) {\r
183                                 /* new note */\r
184                                 n.note=_mm_read_UBYTE(modreader)&127;\r
185                                 n.samp=_mm_read_UBYTE(modreader);\r
186                                 x +=2;\r
187                         }\r
188                         if (flag&0x40) {\r
189                                 do {\r
190                                         /* effect channel set */\r
191                                         i=_mm_read_UBYTE(modreader);\r
192                                         n.effect[i>>6].effect=i&31;\r
193                                         n.effect[i>>6].param=_mm_read_UBYTE(modreader);\r
194                                         x +=2;\r
195                                 } while (i&32);\r
196                         }\r
197                         memcpy(gdmbuf+(64U*ch)+pos,&n,sizeof(GDMNOTE));\r
198                 }\r
199         }\r
200         return 1;\r
201 }\r
202 \r
203 UBYTE *GDM_ConvertTrack(GDMNOTE*tr)\r
204 {\r
205         int t,i=0;\r
206         UBYTE note,ins,inf;\r
207 \r
208         UniReset();\r
209         for (t=0;t<64;t++) {\r
210                 note=tr[t].note;\r
211                 ins=tr[t].samp;\r
212 \r
213                 if ((ins)&&(ins!=255))\r
214                         UniInstrument(ins-1);\r
215                 if (note!=255) {\r
216                         UniNote(((note>>4)*OCTAVE)+(note&0xf)-1);\r
217                 }\r
218                 for (i=0;i<4;i++) {\r
219                         inf = tr[t].effect[i].param;\r
220                         switch (tr[t].effect[i].effect) {\r
221                                 case 1: /* toneslide up */\r
222                                         UniEffect(UNI_S3MEFFECTF,inf);\r
223                                         break;\r
224                                 case 2: /* toneslide down */\r
225                                         UniEffect(UNI_S3MEFFECTE,inf);\r
226                                         break;\r
227                                 case 3: /* glissando to note */\r
228                                         UniEffect(UNI_ITEFFECTG,inf);\r
229                                         break;\r
230                                 case 4: /* vibrato */\r
231                                         UniEffect(UNI_ITEFFECTH,inf);\r
232                                         break;\r
233                                 case 5: /* portamento+volslide */\r
234                                         UniEffect(UNI_ITEFFECTG,0);\r
235                                         UniEffect(UNI_S3MEFFECTD,inf);\r
236                                         break;\r
237                                 case 6: /* vibrato+volslide */\r
238                                         UniEffect(UNI_ITEFFECTH,0);\r
239                                         UniEffect(UNI_S3MEFFECTD,inf);\r
240                                         break;\r
241                                 case 7: /* tremolo */\r
242                                         UniEffect(UNI_S3MEFFECTR,inf);\r
243                                         break;\r
244                                 case 8: /* tremor */\r
245                                         UniEffect(UNI_S3MEFFECTI,inf);\r
246                                         break;\r
247                                 case 9: /* offset */\r
248                                         UniPTEffect(0x09,inf);\r
249                                         break;\r
250                                 case 0x0a:      /* volslide */\r
251                                         UniEffect(UNI_S3MEFFECTD,inf);\r
252                                         break;\r
253                                 case 0x0b:      /* jump to order */\r
254                                         UniPTEffect(0x0b,inf);\r
255                                         break;\r
256                                 case 0x0c:      /* volume set */\r
257                                         UniPTEffect(0x0c,inf);\r
258                                         break;\r
259                                 case 0x0d:      /* pattern break */\r
260                                         UniPTEffect(0x0d,inf);\r
261                                         break;\r
262                                 case 0x0e:      /* extended */\r
263                                         switch (inf&0xf0) {\r
264                                                 case 0x10:      /* fine portamento up */\r
265                                                         UniEffect(UNI_S3MEFFECTF, 0x0f|((inf<<4)&0x0f));\r
266                                                         break;\r
267                                                 case 0x20:      /* fine portamento down */\r
268                                                         UniEffect(UNI_S3MEFFECTE, 0xf0|(inf&0x0f));\r
269                                                         break;\r
270                                                 case 0x30:      /* glissando control */\r
271                                                         UniEffect(SS_GLISSANDO, inf&0x0f);\r
272                                                         break;\r
273                                                 case 0x40:      /* vibrato waveform */\r
274                                                         UniEffect(SS_VIBWAVE, inf&0x0f);\r
275                                                         break;\r
276                                                 case 0x50:      /* set c4spd */\r
277                                                         UniEffect(SS_FINETUNE, inf&0x0f);\r
278                                                         break;\r
279                                                 case 0x60:      /* loop fun */\r
280                                                         UniEffect(UNI_ITEFFECTS0, (inf&0x0f)|0xb0);\r
281                                                         break;\r
282                                                 case 0x70:      /* tremolo waveform */\r
283                                                         UniEffect(SS_TREMWAVE, inf&0x0f);\r
284                                                         break;\r
285                                                 case 0x80:      /* extra fine porta up */\r
286                                                         UniEffect(UNI_S3MEFFECTF, 0x0e|((inf<<4)&0x0f));\r
287                                                         break;\r
288                                                 case 0x90:      /* extra fine porta down */\r
289                                                         UniEffect(UNI_S3MEFFECTE, 0xe0|(inf&0x0f));\r
290                                                         break;\r
291                                                 case 0xa0:      /* fine volslide up */\r
292                                                         UniEffect(UNI_S3MEFFECTD, 0x0f|((inf<<4)&0x0f));\r
293                                                         break;\r
294                                                 case 0xb0:      /* fine volslide down */\r
295                                                         UniEffect(UNI_S3MEFFECTE, 0xf0|(inf&0x0f));\r
296                                                         break;\r
297                                                 case 0xc0:      /* note cut */\r
298                                                 case 0xd0:      /* note delay */\r
299                                                 case 0xe0:      /* extend row */\r
300                                                         UniPTEffect(0xe,inf);\r
301                                                         break;\r
302                                         }\r
303                                         break;\r
304                                 case 0x0f:      /* set tempo */\r
305                                         UniEffect(UNI_S3MEFFECTA,inf);\r
306                                         break;\r
307                                 case 0x10:      /* arpeggio */\r
308                                         UniPTEffect(0x0,inf);\r
309                                         break;\r
310                                 case 0x12:      /* retrigger */\r
311                                         UniEffect(UNI_S3MEFFECTQ,inf);\r
312                                         break;\r
313                                 case 0x13:      /* set global volume */\r
314                                         UniEffect(UNI_XMEFFECTG,inf<<1);\r
315                                         break;\r
316                                 case 0x14:      /* fine vibrato */\r
317                                         UniEffect(UNI_ITEFFECTU,inf);\r
318                                         break;\r
319                                 case 0x1e:      /* special */\r
320                                         switch (inf&0xf0) {\r
321                                                 case 8: /* set pan position */\r
322                                                         if (inf >=128)\r
323                                                                 UniPTEffect(0x08,255);\r
324                                                         else\r
325                                                                 UniPTEffect(0x08,inf<<1);\r
326                                                         break;\r
327                                         }\r
328                                         break;\r
329                                 case 0x1f:      /* set bpm */\r
330                                         if (inf >=0x20)\r
331                                                 UniEffect(UNI_S3MEFFECTT,inf);\r
332                                         break;\r
333                         }\r
334                 }\r
335                 UniNewline();\r
336         }\r
337         return UniDup();\r
338 }\r
339 \r
340 BOOL GDM_Load(BOOL curious)\r
341 {\r
342         int i,x,u,track;\r
343         SAMPLE *q;\r
344         GDMSAMPLE s;\r
345         ULONG position;\r
346         (void)curious;\r
347 \r
348         /* read header */\r
349         _mm_read_string(mh->id1,4,modreader);\r
350         _mm_read_string(mh->songname,32,modreader);\r
351         _mm_read_string(mh->author,32,modreader);\r
352         _mm_read_string(mh->eofmarker,3,modreader);\r
353         _mm_read_string(mh->id2,4,modreader);\r
354 \r
355         mh->majorver=_mm_read_UBYTE(modreader);\r
356         mh->minorver=_mm_read_UBYTE(modreader);\r
357         mh->trackerid=_mm_read_I_UWORD(modreader);\r
358         mh->t_majorver=_mm_read_UBYTE(modreader);\r
359         mh->t_minorver=_mm_read_UBYTE(modreader);\r
360         _mm_read_UBYTES(mh->pantable,32,modreader);\r
361         mh->mastervol=_mm_read_UBYTE(modreader);\r
362         mh->mastertempo=_mm_read_UBYTE(modreader);\r
363         mh->masterbpm=_mm_read_UBYTE(modreader);\r
364         mh->flags=_mm_read_I_UWORD(modreader);\r
365 \r
366         mh->orderloc=_mm_read_I_ULONG(modreader);\r
367         mh->ordernum=_mm_read_UBYTE(modreader);\r
368         mh->patternloc=_mm_read_I_ULONG(modreader);\r
369         mh->patternnum=_mm_read_UBYTE(modreader);\r
370         mh->samhead=_mm_read_I_ULONG(modreader);\r
371         mh->samdata=_mm_read_I_ULONG(modreader);\r
372         mh->samnum=_mm_read_UBYTE(modreader);\r
373         mh->messageloc=_mm_read_I_ULONG(modreader);\r
374         mh->messagelen=_mm_read_I_ULONG(modreader);\r
375         mh->scrollyloc=_mm_read_I_ULONG(modreader);\r
376         mh->scrollylen=_mm_read_I_UWORD(modreader);\r
377         mh->graphicloc=_mm_read_I_ULONG(modreader);\r
378         mh->graphiclen=_mm_read_I_UWORD(modreader);\r
379 \r
380         /* have we ended abruptly? */\r
381         if (_mm_eof(modreader)) {\r
382                 _mm_errno=MMERR_LOADING_HEADER;\r
383                 return 0;\r
384         }\r
385 \r
386         /* any orders? */\r
387         if(mh->ordernum==255) {\r
388                 _mm_errno=MMERR_LOADING_PATTERN;\r
389                 return 0;\r
390         }\r
391 \r
392         /* now we fill */\r
393         of.modtype=strdup(GDM_Version);\r
394         of.modtype[18]=mh->majorver+'0';\r
395         of.modtype[20]=mh->minorver/10+'0';\r
396         of.modtype[21]=mh->minorver%10+'0';\r
397         of.songname=DupStr(mh->songname,32,0);\r
398         of.numpat=mh->patternnum+1;\r
399         of.reppos=0;\r
400         of.numins=of.numsmp=mh->samnum+1;\r
401         of.initspeed=mh->mastertempo;\r
402         of.inittempo=mh->masterbpm;\r
403         of.initvolume=mh->mastervol<<1;\r
404         of.flags|=UF_S3MSLIDES | UF_PANNING;\r
405         /* XXX whenever possible, we should try to determine the original format.\r
406            Here we assume it was S3M-style wrt bpmlimit... */\r
407         of.bpmlimit = 32;\r
408 \r
409         /* read the order data */\r
410         if (!AllocPositions(mh->ordernum+1)) {\r
411                 _mm_errno=MMERR_OUT_OF_MEMORY;\r
412                 return 0;\r
413         }\r
414 \r
415         _mm_fseek(modreader,mh->orderloc,SEEK_SET);\r
416         for (i=0;i<mh->ordernum+1;i++)\r
417                 of.positions[i]=_mm_read_UBYTE(modreader);\r
418 \r
419         of.numpos=0;\r
420         for (i=0;i<mh->ordernum+1;i++) {\r
421                 int order=of.positions[i];\r
422                 if(order==255) order=LAST_PATTERN;\r
423                 of.positions[of.numpos]=order;\r
424                 if (of.positions[i]<254) of.numpos++;\r
425         }\r
426 \r
427         /* have we ended abruptly yet? */\r
428         if (_mm_eof(modreader)) {\r
429                 _mm_errno=MMERR_LOADING_HEADER;\r
430                 return 0;\r
431         }\r
432 \r
433         /* time to load the samples */\r
434         if (!AllocSamples()) {\r
435                 _mm_errno=MMERR_OUT_OF_MEMORY;\r
436                 return 0;\r
437         }\r
438 \r
439         q=of.samples;\r
440         position=mh->samdata;\r
441 \r
442         /* seek to instrument position */\r
443         _mm_fseek(modreader,mh->samhead,SEEK_SET);\r
444 \r
445         for (i=0;i<of.numins;i++) {\r
446                 /* load sample info */\r
447                 _mm_read_UBYTES(s.sampname,32,modreader);\r
448                 _mm_read_UBYTES(s.filename,12,modreader);\r
449                 s.ems=_mm_read_UBYTE(modreader);\r
450                 s.length=_mm_read_I_ULONG(modreader);\r
451                 s.loopbeg=_mm_read_I_ULONG(modreader);\r
452                 s.loopend=_mm_read_I_ULONG(modreader);\r
453                 s.flags=_mm_read_UBYTE(modreader);\r
454                 s.c4spd=_mm_read_I_UWORD(modreader);\r
455                 s.vol=_mm_read_UBYTE(modreader);\r
456                 s.pan=_mm_read_UBYTE(modreader);\r
457 \r
458                 if (_mm_eof(modreader)) {\r
459                         _mm_errno=MMERR_LOADING_SAMPLEINFO;\r
460                         return 0;\r
461                 }\r
462                 q->samplename=DupStr(s.sampname,32,0);\r
463                 q->speed=s.c4spd;\r
464                 q->length=s.length;\r
465                 q->loopstart=s.loopbeg;\r
466                 q->loopend=s.loopend;\r
467                 q->volume=s.vol;\r
468                 q->panning=s.pan;\r
469                 q->seekpos=position;\r
470 \r
471                 position +=s.length;\r
472 \r
473                 if (s.flags&1)\r
474                         q->flags |=SF_LOOP;\r
475                 if (s.flags&2)\r
476                         q->flags |=SF_16BITS;\r
477                 if (s.flags&16)\r
478                         q->flags |=SF_STEREO;\r
479                 q++;\r
480         }\r
481 \r
482         /* set the panning */\r
483         for (i=x=0;i<32;i++) {\r
484                 of.panning[i]=mh->pantable[i];\r
485                 if (!of.panning[i])\r
486                         of.panning[i]=PAN_LEFT;\r
487                 else if (of.panning[i]==8)\r
488                         of.panning[i]=PAN_CENTER;\r
489                 else if (of.panning[i]==15)\r
490                         of.panning[i]=PAN_RIGHT;\r
491                 else if (of.panning[i]==16)\r
492                         of.panning[i]=PAN_SURROUND;\r
493                 else if (of.panning[i]==255)\r
494                         of.panning[i]=128;\r
495                 else\r
496                         of.panning[i]<<=3;\r
497                 if (mh->pantable[i]!=255)\r
498                         x=i;\r
499         }\r
500 \r
501         of.numchn=x+1;\r
502         if (of.numchn<1)\r
503                 of.numchn=1;    /* for broken counts */\r
504 \r
505         /* load the pattern info */\r
506         of.numtrk=of.numpat*of.numchn;\r
507 \r
508         /* jump to patterns */\r
509         _mm_fseek(modreader,mh->patternloc,SEEK_SET);\r
510 \r
511         if (!AllocTracks()) {\r
512                 _mm_errno=MMERR_OUT_OF_MEMORY;\r
513                 return 0;\r
514         }\r
515 \r
516         if (!AllocPatterns()) {\r
517                 _mm_errno=MMERR_OUT_OF_MEMORY;\r
518                 return 0;\r
519         }\r
520 \r
521         for (i=track=0;i<of.numpat;i++) {\r
522                 if (!GDM_ReadPattern()) {\r
523                         _mm_errno=MMERR_LOADING_PATTERN;\r
524                         return 0;\r
525                 }\r
526                 for (u=0;u<of.numchn;u++,track++) {\r
527                         of.tracks[track]=GDM_ConvertTrack(&gdmbuf[u<<6]);\r
528                         if (!of.tracks[track]) {\r
529                                 _mm_errno=MMERR_LOADING_TRACK;\r
530                                 return 0;\r
531                         }\r
532                 }\r
533         }\r
534         return 1;\r
535 }\r
536 \r
537 CHAR *GDM_LoadTitle(void)\r
538 {\r
539         CHAR s[32];\r
540 \r
541         _mm_fseek(modreader,4,SEEK_SET);\r
542         if (!_mm_read_UBYTES(s,32,modreader)) return NULL;\r
543 \r
544         return DupStr(s,28,0);\r
545 }\r
546 \r
547 MIKMODAPI MLOADER load_gdm=\r
548 {\r
549         NULL,\r
550         "GDM",\r
551         "GDM (General DigiMusic)",\r
552         GDM_Init,\r
553         GDM_Test,\r
554         GDM_Load,\r
555         GDM_Cleanup,\r
556         GDM_LoadTitle\r
557 };\r
558 \r
559 /* ex:set ts=4: */\r