Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_dsm.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_dsm.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
24 \r
25   DSIK internal format (DSM) 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 #define DSM_MAXCHAN (16)\r
52 #define DSM_MAXORDERS (128)\r
53 \r
54 typedef struct DSMSONG {\r
55         CHAR    songname[28];\r
56         UWORD   version;\r
57         UWORD   flags;\r
58         ULONG   reserved2;\r
59         UWORD   numord;\r
60         UWORD   numsmp;\r
61         UWORD   numpat;\r
62         UWORD   numtrk;\r
63         UBYTE   globalvol;\r
64         UBYTE   mastervol;\r
65         UBYTE   speed;\r
66         UBYTE   bpm;\r
67         UBYTE   panpos[DSM_MAXCHAN];\r
68         UBYTE   orders[DSM_MAXORDERS];\r
69 } DSMSONG;\r
70 \r
71 typedef struct DSMINST {\r
72         CHAR    filename[13];\r
73         UWORD   flags;\r
74         UBYTE   volume;\r
75         ULONG   length;\r
76         ULONG   loopstart;\r
77         ULONG   loopend;\r
78         ULONG   reserved1;\r
79         UWORD   c2spd;\r
80         UWORD   period;\r
81         CHAR    samplename[28];\r
82 } DSMINST;\r
83 \r
84 typedef struct DSMNOTE {\r
85         UBYTE   note,ins,vol,cmd,inf;\r
86 } DSMNOTE;\r
87 \r
88 #define DSM_SURROUND (0xa4)\r
89 \r
90 /*========== Loader variables */\r
91 \r
92 static  CHAR* SONGID="SONG";\r
93 static  CHAR* INSTID="INST";\r
94 static  CHAR* PATTID="PATT";\r
95 \r
96 static  UBYTE blockid[4];\r
97 static  ULONG blockln;\r
98 static  ULONG blocklp;\r
99 static  DSMSONG* mh=NULL;\r
100 static  DSMNOTE* dsmbuf=NULL;\r
101 \r
102 static  CHAR DSM_Version[]="DSIK DSM-format";\r
103 \r
104 static  unsigned char DSMSIG[4+4]={'R','I','F','F','D','S','M','F'};\r
105 \r
106 /*========== Loader code */\r
107 \r
108 BOOL DSM_Test(void)\r
109 {\r
110         UBYTE id[12];\r
111 \r
112         if(!_mm_read_UBYTES(id,12,modreader)) return 0;\r
113         if(!memcmp(id,DSMSIG,4) && !memcmp(id+8,DSMSIG+4,4)) return 1;\r
114 \r
115         return 0;\r
116 }\r
117 \r
118 BOOL DSM_Init(void)\r
119 {\r
120         if(!(dsmbuf=(DSMNOTE *)_mm_malloc(DSM_MAXCHAN*64*sizeof(DSMNOTE)))) return 0;\r
121         if(!(mh=(DSMSONG *)_mm_calloc(1,sizeof(DSMSONG)))) return 0;\r
122         return 1;\r
123 }\r
124 \r
125 void DSM_Cleanup(void)\r
126 {\r
127         _mm_free(dsmbuf);\r
128         _mm_free(mh);\r
129 }\r
130 \r
131 static BOOL GetBlockHeader(void)\r
132 {\r
133         /* make sure we're at the right position for reading the\r
134            next riff block, no matter how many bytes read */\r
135         _mm_fseek(modreader, blocklp+blockln, SEEK_SET);\r
136    \r
137         while(1) {\r
138                 _mm_read_UBYTES(blockid,4,modreader);\r
139                 blockln=_mm_read_I_ULONG(modreader);\r
140                 if(_mm_eof(modreader)) {\r
141                         _mm_errno = MMERR_LOADING_HEADER;\r
142                         return 0;\r
143                 }\r
144 \r
145                 if(memcmp(blockid,SONGID,4) && memcmp(blockid,INSTID,4) &&\r
146                    memcmp(blockid,PATTID,4)) {\r
147 #ifdef MIKMOD_DEBUG\r
148                                 fprintf(stderr,"\rDSM: Skipping unknown block type %4.4s\n",blockid);\r
149 #endif\r
150                                 _mm_fseek(modreader, blockln, SEEK_CUR);\r
151                 } else\r
152                         break;\r
153         }\r
154 \r
155         blocklp = _mm_ftell(modreader);\r
156 \r
157         return 1;\r
158 }\r
159 \r
160 static BOOL DSM_ReadPattern(void)\r
161 {\r
162         int flag,row=0;\r
163         SWORD length;\r
164         DSMNOTE *n;\r
165 \r
166         /* clear pattern data */\r
167         memset(dsmbuf,255,DSM_MAXCHAN*64*sizeof(DSMNOTE));\r
168         length=_mm_read_I_SWORD(modreader);\r
169 \r
170         while(row<64) {\r
171                 flag=_mm_read_UBYTE(modreader);\r
172                 if((_mm_eof(modreader))||(--length<0)) {\r
173                         _mm_errno = MMERR_LOADING_PATTERN;\r
174                         return 0;\r
175                 }\r
176 \r
177                 if(flag) {\r
178                         n=&dsmbuf[((flag&0xf)*64)+row];\r
179                         if(flag&0x80) n->note=_mm_read_UBYTE(modreader);\r
180                         if(flag&0x40) n->ins=_mm_read_UBYTE(modreader);\r
181                         if(flag&0x20) n->vol=_mm_read_UBYTE(modreader);\r
182                         if(flag&0x10) {\r
183                                 n->cmd=_mm_read_UBYTE(modreader);\r
184                                 n->inf=_mm_read_UBYTE(modreader);\r
185                         }\r
186                 } else\r
187                         row++;\r
188         }\r
189 \r
190         return 1;\r
191 }\r
192 \r
193 static UBYTE *DSM_ConvertTrack(DSMNOTE *tr)\r
194 {\r
195         int t;\r
196         UBYTE note,ins,vol,cmd,inf;\r
197 \r
198         UniReset();\r
199         for(t=0;t<64;t++) {\r
200                 note=tr[t].note;\r
201                 ins=tr[t].ins;\r
202                 vol=tr[t].vol;\r
203                 cmd=tr[t].cmd;\r
204                 inf=tr[t].inf;\r
205 \r
206                 if(ins!=0 && ins!=255) UniInstrument(ins-1);\r
207                 if(note!=255) UniNote(note-1); /* normal note */\r
208                 if(vol<65) UniPTEffect(0xc,vol);\r
209 \r
210                 if(cmd!=255) {\r
211                         if(cmd==0x8) {\r
212                                 if(inf==DSM_SURROUND)\r
213                                         UniEffect(UNI_ITEFFECTS0,0x91);\r
214                                 else\r
215                                   if(inf<=0x80) {\r
216                                         inf=(inf<0x80)?inf<<1:255;\r
217                                         UniPTEffect(cmd,inf);\r
218                                 }\r
219                         } else\r
220                           if(cmd==0xb) {\r
221                                 if(inf<=0x7f) UniPTEffect(cmd,inf);\r
222                         } else {\r
223                                 /* Convert pattern jump from Dec to Hex */\r
224                                 if(cmd == 0xd)\r
225                                         inf = (((inf&0xf0)>>4)*10)+(inf&0xf);\r
226                                 UniPTEffect(cmd,inf);\r
227                         }\r
228                 }\r
229                 UniNewline();\r
230         }\r
231         return UniDup();\r
232 }\r
233 \r
234 BOOL DSM_Load(BOOL curious)\r
235 {\r
236         int t;\r
237         DSMINST s;\r
238         SAMPLE *q;\r
239         int cursmp=0,curpat=0,track=0;\r
240 \r
241         blocklp=0;\r
242         blockln=12;\r
243         (void)curious;\r
244 \r
245         if(!GetBlockHeader()) return 0;\r
246         if(memcmp(blockid,SONGID,4)) {\r
247                 _mm_errno = MMERR_LOADING_HEADER;\r
248                 return 0;\r
249         }\r
250 \r
251         _mm_read_UBYTES(mh->songname,28,modreader);\r
252         mh->version=_mm_read_I_UWORD(modreader);\r
253         mh->flags=_mm_read_I_UWORD(modreader);\r
254         mh->reserved2=_mm_read_I_ULONG(modreader);\r
255         mh->numord=_mm_read_I_UWORD(modreader);\r
256         mh->numsmp=_mm_read_I_UWORD(modreader);\r
257         mh->numpat=_mm_read_I_UWORD(modreader);\r
258         mh->numtrk=_mm_read_I_UWORD(modreader);\r
259         mh->globalvol=_mm_read_UBYTE(modreader);\r
260         mh->mastervol=_mm_read_UBYTE(modreader);\r
261         mh->speed=_mm_read_UBYTE(modreader);\r
262         mh->bpm=_mm_read_UBYTE(modreader);\r
263         _mm_read_UBYTES(mh->panpos,DSM_MAXCHAN,modreader);\r
264         _mm_read_UBYTES(mh->orders,DSM_MAXORDERS,modreader);\r
265 \r
266         /* set module variables */\r
267         of.initspeed=mh->speed;\r
268         of.inittempo=mh->bpm;\r
269         of.modtype=strdup(DSM_Version);\r
270         of.numchn=mh->numtrk;\r
271         of.numpat=mh->numpat;\r
272         of.numtrk=of.numchn*of.numpat;\r
273         of.songname=DupStr(mh->songname,28,1); /* make a cstr of songname */\r
274         of.reppos=0;\r
275         of.flags |= UF_PANNING;\r
276         /* XXX whenever possible, we should try to determine the original format.\r
277            Here we assume it was S3M-style wrt bpmlimit... */\r
278         of.bpmlimit = 32;\r
279 \r
280         for(t=0;t<DSM_MAXCHAN;t++)\r
281                 of.panning[t]=mh->panpos[t]==DSM_SURROUND?PAN_SURROUND:\r
282                               mh->panpos[t]<0x80?(mh->panpos[t]<<1):255;\r
283 \r
284         if(!AllocPositions(mh->numord)) return 0;\r
285         of.numpos=0;\r
286         for(t=0;t<mh->numord;t++) {\r
287                 int order=mh->orders[t];\r
288                 if(order==255) order=LAST_PATTERN;\r
289                 of.positions[of.numpos]=order;\r
290                 if(mh->orders[t]<254) of.numpos++;\r
291         }\r
292 \r
293         of.numins=of.numsmp=mh->numsmp;\r
294 \r
295         if(!AllocSamples()) return 0;\r
296         if(!AllocTracks()) return 0;\r
297         if(!AllocPatterns()) return 0;\r
298 \r
299         while(cursmp<of.numins||curpat<of.numpat) {\r
300                 if(!GetBlockHeader()) return 0;\r
301                 if(!memcmp(blockid,INSTID,4) && cursmp<of.numins) {\r
302                         q=&of.samples[cursmp];\r
303 \r
304                         /* try to read sample info */\r
305                         _mm_read_UBYTES(s.filename,13,modreader);\r
306                         s.flags=_mm_read_I_UWORD(modreader);\r
307                         s.volume=_mm_read_UBYTE(modreader);\r
308                         s.length=_mm_read_I_ULONG(modreader);\r
309                         s.loopstart=_mm_read_I_ULONG(modreader);\r
310                         s.loopend=_mm_read_I_ULONG(modreader);\r
311                         s.reserved1=_mm_read_I_ULONG(modreader);\r
312                         s.c2spd=_mm_read_I_UWORD(modreader);\r
313                         s.period=_mm_read_I_UWORD(modreader);\r
314                         _mm_read_UBYTES(s.samplename,28,modreader);\r
315 \r
316                         q->samplename=DupStr(s.samplename,28,1);\r
317                         q->seekpos=_mm_ftell(modreader);\r
318                         q->speed=s.c2spd;\r
319                         q->length=s.length;\r
320                         q->loopstart=s.loopstart;\r
321                         q->loopend=s.loopend;\r
322                         q->volume=s.volume;\r
323 \r
324                         if(s.flags&1) q->flags|=SF_LOOP;\r
325                         if(s.flags&2) q->flags|=SF_SIGNED;\r
326                         /* (s.flags&4) means packed sample,\r
327                            but did they really exist in dsm ?*/\r
328                         cursmp++;\r
329                 } else\r
330                   if(!memcmp(blockid,PATTID,4) && curpat<of.numpat) {\r
331                         DSM_ReadPattern();\r
332                         for(t=0;t<of.numchn;t++)\r
333                                 if(!(of.tracks[track++]=DSM_ConvertTrack(&dsmbuf[t*64]))) return 0;\r
334                         curpat++;\r
335                 }\r
336         }\r
337 \r
338         return 1;\r
339 }\r
340 \r
341 CHAR *DSM_LoadTitle(void)\r
342 {\r
343         CHAR s[28];\r
344 \r
345         _mm_fseek(modreader,12,SEEK_SET);\r
346         if(!_mm_read_UBYTES(s,28,modreader)) return NULL;\r
347    \r
348         return(DupStr(s,28,1));\r
349 }\r
350 \r
351 /*========== Loader information */\r
352 \r
353 MIKMODAPI MLOADER load_dsm={\r
354         NULL,\r
355         "DSM",\r
356         "DSM (DSIK internal format)",\r
357         DSM_Init,\r
358         DSM_Test,\r
359         DSM_Load,\r
360         DSM_Cleanup,\r
361         DSM_LoadTitle\r
362 };\r
363 \r
364 \r
365 /* ex:set ts=4: */\r