Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_s3m.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_s3m.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
24 \r
25   Screamtracker (S3M) 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 /* header */\r
52 typedef struct S3MHEADER {\r
53         CHAR  songname[28];\r
54         UBYTE t1a;\r
55         UBYTE type;\r
56         UBYTE unused1[2];\r
57         UWORD ordnum;\r
58         UWORD insnum;\r
59         UWORD patnum;\r
60         UWORD flags;\r
61         UWORD tracker;\r
62         UWORD fileformat;\r
63         CHAR  scrm[4];\r
64         UBYTE mastervol;\r
65         UBYTE initspeed;\r
66         UBYTE inittempo;\r
67         UBYTE mastermult;\r
68         UBYTE ultraclick;\r
69         UBYTE pantable;\r
70         UBYTE unused2[8];\r
71         UWORD special;\r
72         UBYTE channels[32];\r
73 } S3MHEADER;\r
74 \r
75 /* sample information */\r
76 typedef struct S3MSAMPLE {\r
77         UBYTE type;\r
78         CHAR  filename[12];\r
79         UBYTE memsegh;\r
80         UWORD memsegl;\r
81         ULONG length;\r
82         ULONG loopbeg;\r
83         ULONG loopend;\r
84         UBYTE volume;\r
85         UBYTE dsk;\r
86         UBYTE pack;\r
87         UBYTE flags;\r
88         ULONG c2spd;\r
89         UBYTE unused[12];\r
90         CHAR  sampname[28];\r
91         CHAR  scrs[4];\r
92 } S3MSAMPLE;\r
93 \r
94 typedef struct S3MNOTE {\r
95         UBYTE note,ins,vol,cmd,inf;\r
96 } S3MNOTE;\r
97 \r
98 /*========== Loader variables */\r
99 \r
100 static S3MNOTE   *s3mbuf  = NULL; /* pointer to a complete S3M pattern */\r
101 static S3MHEADER *mh      = NULL;\r
102 static UWORD     *paraptr = NULL; /* parapointer array (see S3M docs) */\r
103 static unsigned int tracker;    /* tracker id */\r
104 \r
105 /* tracker identifiers */\r
106 #define NUMTRACKERS 4\r
107 static CHAR* S3M_Version[] = {\r
108         "Screamtracker x.xx",\r
109         "Imago Orpheus x.xx (S3M format)",\r
110         "Impulse Tracker x.xx (S3M format)",\r
111         "Unknown tracker x.xx (S3M format)",\r
112         "Impulse Tracker 2.14p3 (S3M format)",\r
113         "Impulse Tracker 2.14p4 (S3M format)"\r
114 };\r
115 /* version number position in above array */\r
116 static int numeric[NUMTRACKERS]={14,14,16,16};\r
117 \r
118 /*========== Loader code */\r
119 \r
120 BOOL S3M_Test(void)\r
121 {\r
122         UBYTE id[4];\r
123 \r
124         _mm_fseek(modreader,0x2c,SEEK_SET);\r
125         if(!_mm_read_UBYTES(id,4,modreader)) return 0;\r
126         if(!memcmp(id,"SCRM",4)) return 1;\r
127         return 0;\r
128 }\r
129 \r
130 BOOL S3M_Init(void)\r
131 {\r
132         if(!(s3mbuf=(S3MNOTE*)_mm_malloc(32*64*sizeof(S3MNOTE)))) return 0;\r
133         if(!(mh=(S3MHEADER*)_mm_malloc(sizeof(S3MHEADER)))) return 0;\r
134         if(!(poslookup=(UBYTE*)_mm_malloc(sizeof(UBYTE)*256))) return 0;\r
135         memset(poslookup,-1,256);\r
136 \r
137         return 1;\r
138 }\r
139 \r
140 void S3M_Cleanup(void)\r
141 {\r
142         _mm_free(s3mbuf);\r
143         _mm_free(paraptr);\r
144         _mm_free(poslookup);\r
145         _mm_free(mh);\r
146         _mm_free(origpositions);\r
147 }\r
148 \r
149 /* Because so many s3m files have 16 channels as the set number used, but really\r
150    only use far less (usually 8 to 12 still), I had to make this function, which\r
151    determines the number of channels that are actually USED by a pattern.\r
152 \r
153    For every channel that's used, it sets the appropriate array entry of the\r
154    global variable 'remap'\r
155 \r
156    NOTE: You must first seek to the file location of the pattern before calling\r
157          this procedure.\r
158 \r
159    Returns 1 on fail.                                                         */\r
160 static BOOL S3M_GetNumChannels(void)\r
161 {\r
162         int row=0,flag,ch;\r
163 \r
164         while(row<64) {\r
165                 flag=_mm_read_UBYTE(modreader);\r
166 \r
167                 if(_mm_eof(modreader)) {\r
168                         _mm_errno = MMERR_LOADING_PATTERN;\r
169                         return 1;\r
170                 }\r
171 \r
172                 if(flag) {\r
173                         ch=flag&31;\r
174                         if(mh->channels[ch]<32) remap[ch] = 0;\r
175                         if(flag&32) {_mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader);}\r
176                         if(flag&64) _mm_read_UBYTE(modreader);\r
177                         if(flag&128){_mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader);}\r
178                 } else row++;\r
179         }\r
180         return 0;\r
181 }    \r
182 \r
183 static BOOL S3M_ReadPattern(void)\r
184 {\r
185         int row=0,flag,ch;\r
186         S3MNOTE *n,dummy;\r
187 \r
188         /* clear pattern data */\r
189         memset(s3mbuf,255,32*64*sizeof(S3MNOTE));\r
190 \r
191         while(row<64) {\r
192                 flag=_mm_read_UBYTE(modreader);\r
193 \r
194                 if(_mm_eof(modreader)) {\r
195                         _mm_errno = MMERR_LOADING_PATTERN;\r
196                         return 0;\r
197                 }\r
198 \r
199                 if(flag) {\r
200                         ch=remap[flag&31];\r
201 \r
202                         if(ch!=-1)\r
203                                 n=&s3mbuf[(64U*ch)+row];\r
204                         else\r
205                                 n=&dummy;\r
206 \r
207                         if(flag&32) {\r
208                                 n->note=_mm_read_UBYTE(modreader);\r
209                                 n->ins=_mm_read_UBYTE(modreader);\r
210                         }\r
211                         if(flag&64) {\r
212                                 n->vol=_mm_read_UBYTE(modreader);\r
213                                 if (n->vol>64) n->vol=64;\r
214                         }\r
215                         if(flag&128) {\r
216                                 n->cmd=_mm_read_UBYTE(modreader);\r
217                                 n->inf=_mm_read_UBYTE(modreader);\r
218                         }\r
219                 } else row++;\r
220         }\r
221         return 1;\r
222 }\r
223 \r
224 static UBYTE* S3M_ConvertTrack(S3MNOTE* tr)\r
225 {\r
226         int t;\r
227 \r
228         UniReset();\r
229         for(t=0;t<64;t++) {\r
230                 UBYTE note,ins,vol;\r
231 \r
232                 note=tr[t].note;\r
233                 ins=tr[t].ins;\r
234                 vol=tr[t].vol;\r
235 \r
236                 if((ins)&&(ins!=255)) UniInstrument(ins-1);\r
237                 if(note!=255) {\r
238                         if(note==254) {\r
239                                 UniPTEffect(0xc,0);     /* note cut command */\r
240                                 vol=255;\r
241                         } else\r
242                                 UniNote(((note>>4)*OCTAVE)+(note&0xf)); /* normal note */\r
243                 }\r
244                 if(vol<255) UniPTEffect(0xc,vol);\r
245 \r
246                 S3MIT_ProcessCmd(tr[t].cmd,tr[t].inf,\r
247                         tracker == 1 ? S3MIT_OLDSTYLE | S3MIT_SCREAM : S3MIT_OLDSTYLE);\r
248                 UniNewline();\r
249         }\r
250         return UniDup();\r
251 }\r
252 \r
253 BOOL S3M_Load(BOOL curious)\r
254 {\r
255         int t,u,track = 0;\r
256         SAMPLE *q;\r
257         UBYTE pan[32];\r
258 \r
259         /* try to read module header */\r
260         _mm_read_string(mh->songname,28,modreader);\r
261         mh->t1a         =_mm_read_UBYTE(modreader);\r
262         mh->type        =_mm_read_UBYTE(modreader);\r
263         _mm_read_UBYTES(mh->unused1,2,modreader);\r
264         mh->ordnum      =_mm_read_I_UWORD(modreader);\r
265         mh->insnum      =_mm_read_I_UWORD(modreader);\r
266         mh->patnum      =_mm_read_I_UWORD(modreader);\r
267         mh->flags       =_mm_read_I_UWORD(modreader);\r
268         mh->tracker     =_mm_read_I_UWORD(modreader);\r
269         mh->fileformat  =_mm_read_I_UWORD(modreader);\r
270         _mm_read_string(mh->scrm,4,modreader);\r
271         mh->mastervol   =_mm_read_UBYTE(modreader);\r
272         mh->initspeed   =_mm_read_UBYTE(modreader);\r
273         mh->inittempo   =_mm_read_UBYTE(modreader);\r
274         mh->mastermult  =_mm_read_UBYTE(modreader);\r
275         mh->ultraclick  =_mm_read_UBYTE(modreader);\r
276         mh->pantable    =_mm_read_UBYTE(modreader);\r
277         _mm_read_UBYTES(mh->unused2,8,modreader);\r
278         mh->special     =_mm_read_I_UWORD(modreader);\r
279         _mm_read_UBYTES(mh->channels,32,modreader);\r
280 \r
281         if(_mm_eof(modreader)) {\r
282                 _mm_errno = MMERR_LOADING_HEADER;\r
283                 return 0;\r
284         }\r
285 \r
286         /* then we can decide the module type */\r
287         tracker=mh->tracker>>12;\r
288         if((!tracker)||(tracker>=NUMTRACKERS))\r
289                 tracker=NUMTRACKERS-1; /* unknown tracker */\r
290         else {\r
291                 if(mh->tracker>=0x3217)\r
292                         tracker=NUMTRACKERS+1; /* IT 2.14p4 */\r
293                 else if(mh->tracker>=0x3216)\r
294                         tracker=NUMTRACKERS; /* IT 2.14p3 */\r
295                 else tracker--;\r
296         }\r
297         of.modtype = strdup(S3M_Version[tracker]);\r
298         if(tracker<NUMTRACKERS) {\r
299                 of.modtype[numeric[tracker]] = ((mh->tracker>>8) &0xf)+'0';\r
300                 of.modtype[numeric[tracker]+2] = ((mh->tracker>>4)&0xf)+'0';\r
301                 of.modtype[numeric[tracker]+3] = ((mh->tracker)&0xf)+'0';\r
302         }\r
303         /* set module variables */\r
304         of.songname    = DupStr(mh->songname,28,0);\r
305         of.numpat      = mh->patnum;\r
306         of.reppos      = 0;\r
307         of.numins      = of.numsmp = mh->insnum;\r
308         of.initspeed   = mh->initspeed;\r
309         of.inittempo   = mh->inittempo;\r
310         of.initvolume  = mh->mastervol<<1;\r
311         of.flags      |= UF_ARPMEM | UF_PANNING;\r
312         if((mh->tracker==0x1300)||(mh->flags&64))\r
313                 of.flags|=UF_S3MSLIDES;\r
314         of.bpmlimit    = 32;\r
315 \r
316         /* read the order data */\r
317         if(!AllocPositions(mh->ordnum)) return 0;\r
318         if(!(origpositions=_mm_calloc(mh->ordnum,sizeof(UWORD)))) return 0;\r
319 \r
320         for(t=0;t<mh->ordnum;t++) {\r
321                 origpositions[t]=_mm_read_UBYTE(modreader);\r
322                 if((origpositions[t]>=mh->patnum)&&(origpositions[t]<254))\r
323                         origpositions[t]=255/*mh->patnum-1*/;\r
324         }\r
325 \r
326         if(_mm_eof(modreader)) {\r
327                 _mm_errno = MMERR_LOADING_HEADER;\r
328                 return 0;\r
329         }\r
330 \r
331         poslookupcnt=mh->ordnum;\r
332         S3MIT_CreateOrders(curious);\r
333 \r
334         if(!(paraptr=(UWORD*)_mm_malloc((of.numins+of.numpat)*sizeof(UWORD))))\r
335                 return 0;\r
336 \r
337         /* read the instrument+pattern parapointers */\r
338         _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modreader);\r
339 \r
340         if(mh->pantable==252) {\r
341                 /* read the panning table (ST 3.2 addition.  See below for further\r
342                    portions of channel panning [past reampper]). */\r
343                 _mm_read_UBYTES(pan,32,modreader);\r
344         }\r
345 \r
346         if(_mm_eof(modreader)) {\r
347                 _mm_errno = MMERR_LOADING_HEADER;\r
348                 return 0;\r
349         }\r
350 \r
351         /* load samples */\r
352         if(!AllocSamples()) return 0;\r
353         q = of.samples;\r
354         for(t=0;t<of.numins;t++) {\r
355                 S3MSAMPLE s;\r
356 \r
357                 /* seek to instrument position */\r
358                 _mm_fseek(modreader,((long)paraptr[t])<<4,SEEK_SET);\r
359                 /* and load sample info */\r
360                 s.type      =_mm_read_UBYTE(modreader);\r
361                 _mm_read_string(s.filename,12,modreader);\r
362                 s.memsegh   =_mm_read_UBYTE(modreader);\r
363                 s.memsegl   =_mm_read_I_UWORD(modreader);\r
364                 s.length    =_mm_read_I_ULONG(modreader);\r
365                 s.loopbeg   =_mm_read_I_ULONG(modreader);\r
366                 s.loopend   =_mm_read_I_ULONG(modreader);\r
367                 s.volume    =_mm_read_UBYTE(modreader);\r
368                 s.dsk       =_mm_read_UBYTE(modreader);\r
369                 s.pack      =_mm_read_UBYTE(modreader);\r
370                 s.flags     =_mm_read_UBYTE(modreader);\r
371                 s.c2spd     =_mm_read_I_ULONG(modreader);\r
372                 _mm_read_UBYTES(s.unused,12,modreader);\r
373                 _mm_read_string(s.sampname,28,modreader);\r
374                 _mm_read_string(s.scrs,4,modreader);\r
375 \r
376                 /* ScreamTracker imposes a 64000 bytes (not 64k !) limit */\r
377                 if (s.length > 64000)\r
378                         s.length = 64000;\r
379 \r
380                 if(_mm_eof(modreader)) {\r
381                         _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
382                         return 0;\r
383                 }\r
384 \r
385                 q->samplename = DupStr(s.sampname,28,0);\r
386                 q->speed      = s.c2spd;\r
387                 q->length     = s.length;\r
388                 q->loopstart  = s.loopbeg;\r
389                 q->loopend    = s.loopend;\r
390                 q->volume     = s.volume;\r
391                 q->seekpos    = (((long)s.memsegh)<<16|s.memsegl)<<4;\r
392 \r
393                 if(s.flags&1) q->flags |= SF_LOOP;\r
394                 if(s.flags&4) q->flags |= SF_16BITS;\r
395                 if(mh->fileformat==1) q->flags |= SF_SIGNED;\r
396 \r
397                 /* don't load sample if it doesn't have the SCRS tag */\r
398                 if(memcmp(s.scrs,"SCRS",4)) q->length = 0;\r
399 \r
400                 q++;\r
401         }\r
402 \r
403         /* determine the number of channels actually used. */\r
404         of.numchn = 0;\r
405         memset(remap,-1,32*sizeof(UBYTE));\r
406         for(t=0;t<of.numpat;t++) {\r
407                 /* seek to pattern position (+2 skip pattern length) */\r
408                 _mm_fseek(modreader,(long)((paraptr[of.numins+t])<<4)+2,SEEK_SET);\r
409                 if(S3M_GetNumChannels()) return 0;\r
410         }\r
411 \r
412         /* build the remap array  */\r
413         for(t=0;t<32;t++)\r
414                 if(!remap[t]) \r
415                         remap[t]=of.numchn++;\r
416 \r
417         /* set panning positions after building remap chart! */\r
418         for(t=0;t<32;t++) \r
419                 if((mh->channels[t]<32)&&(remap[t]!=-1)) {\r
420                         if(mh->channels[t]<8)\r
421                                 of.panning[remap[t]]=0x30;\r
422                         else\r
423                                 of.panning[remap[t]]=0xc0;\r
424                 }\r
425         if(mh->pantable==252)\r
426                 /* set panning positions according to panning table (new for st3.2) */\r
427                 for(t=0;t<32;t++)\r
428                         if((pan[t]&0x20)&&(mh->channels[t]<32)&&(remap[t]!=-1))\r
429                                 of.panning[remap[t]]=(pan[t]&0xf)<<4;\r
430 \r
431         /* load pattern info */\r
432         of.numtrk=of.numpat*of.numchn;\r
433         if(!AllocTracks()) return 0;\r
434         if(!AllocPatterns()) return 0;\r
435 \r
436         for(t=0;t<of.numpat;t++) {\r
437                 /* seek to pattern position (+2 skip pattern length) */\r
438                 _mm_fseek(modreader,(((long)paraptr[of.numins+t])<<4)+2,SEEK_SET);\r
439                 if(!S3M_ReadPattern()) return 0;\r
440                 for(u=0;u<of.numchn;u++)\r
441                         if(!(of.tracks[track++]=S3M_ConvertTrack(&s3mbuf[u*64]))) return 0;\r
442         }\r
443 \r
444         return 1;\r
445 }\r
446 \r
447 CHAR *S3M_LoadTitle(void)\r
448 {\r
449         CHAR s[28];\r
450 \r
451         _mm_fseek(modreader,0,SEEK_SET);\r
452         if(!_mm_read_UBYTES(s,28,modreader)) return NULL;\r
453 \r
454         return(DupStr(s,28,0));\r
455 }\r
456 \r
457 /*========== Loader information */\r
458 \r
459 MIKMODAPI MLOADER load_s3m={\r
460         NULL,\r
461         "S3M",\r
462         "S3M (Scream Tracker 3)",\r
463         S3M_Init,\r
464         S3M_Test,\r
465         S3M_Load,\r
466         S3M_Cleanup,\r
467         S3M_LoadTitle\r
468 };\r
469 \r
470 /* ex:set ts=4: */\r