Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_stm.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_stm.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
24 \r
25   Screamtracker 2 (STM) 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 /* sample information */\r
52 typedef struct STMSAMPLE {\r
53         CHAR  filename[12];\r
54         UBYTE unused;       /* 0x00 */\r
55         UBYTE instdisk;     /* Instrument disk */\r
56         UWORD reserved;\r
57         UWORD length;       /* Sample length */\r
58         UWORD loopbeg;      /* Loop start point */\r
59         UWORD loopend;      /* Loop end point */\r
60         UBYTE volume;       /* Volume */\r
61         UBYTE reserved2;\r
62         UWORD c2spd;        /* Good old c2spd */\r
63         ULONG reserved3;\r
64         UWORD isa;\r
65 } STMSAMPLE;\r
66 \r
67 /* header */\r
68 typedef struct STMHEADER {\r
69         CHAR  songname[20];\r
70         CHAR  trackername[8]; /* !Scream! for ST 2.xx  */\r
71         UBYTE unused;         /* 0x1A  */\r
72         UBYTE filetype;       /* 1=song, 2=module */\r
73         UBYTE ver_major;\r
74         UBYTE ver_minor;\r
75         UBYTE inittempo;      /* initspeed= stm inittempo>>4  */\r
76         UBYTE numpat;         /* number of patterns  */\r
77         UBYTE globalvol;     \r
78         UBYTE reserved[13];\r
79         STMSAMPLE sample[31]; /* STM sample data */\r
80         UBYTE patorder[128];  /* Docs say 64 - actually 128 */\r
81 } STMHEADER;\r
82 \r
83 typedef struct STMNOTE {\r
84         UBYTE note,insvol,volcmd,cmdinf;\r
85 } STMNOTE;\r
86 \r
87 /*========== Loader variables */\r
88 \r
89 static STMNOTE *stmbuf = NULL;\r
90 static STMHEADER *mh = NULL;\r
91 \r
92 /* tracker identifiers */\r
93 static CHAR* STM_Version[STM_NTRACKERS] = {\r
94         "Screamtracker 2",\r
95         "Converted by MOD2STM (STM format)",\r
96         "Wuzamod (STM format)"\r
97 };\r
98 \r
99 /*========== Loader code */\r
100 \r
101 BOOL STM_Test(void)\r
102 {\r
103         UBYTE str[44];\r
104         int t;\r
105 \r
106         _mm_fseek(modreader,20,SEEK_SET);\r
107         _mm_read_UBYTES(str,44,modreader);\r
108         if(str[9]!=2) return 0; /* STM Module = filetype 2 */\r
109 \r
110         /* Prevent false positives for S3M files */\r
111         if(!memcmp(str+40,"SCRM",4))\r
112                 return 0;\r
113         \r
114         for (t=0;t<STM_NTRACKERS;t++)\r
115                 if(!memcmp(str,STM_Signatures[t],8))\r
116                         return 1;\r
117 \r
118         return 0;\r
119 }\r
120 \r
121 BOOL STM_Init(void)\r
122 {\r
123         if(!(mh=(STMHEADER*)_mm_malloc(sizeof(STMHEADER)))) return 0;\r
124         if(!(stmbuf=(STMNOTE*)_mm_calloc(64U*4,sizeof(STMNOTE)))) return 0;\r
125 \r
126         return 1;\r
127 }\r
128 \r
129 static void STM_Cleanup(void)\r
130 {\r
131         _mm_free(mh);\r
132         _mm_free(stmbuf);\r
133 }\r
134 \r
135 static void STM_ConvertNote(STMNOTE *n)\r
136 {\r
137         UBYTE note,ins,vol,cmd,inf;\r
138 \r
139         /* extract the various information from the 4 bytes that make up a note */\r
140         note = n->note;\r
141         ins  = n->insvol>>3;\r
142         vol  = (n->insvol&7)+((n->volcmd&0x70)>>1);\r
143         cmd  = n->volcmd&15;\r
144         inf  = n->cmdinf;\r
145 \r
146         if((ins)&&(ins<32)) UniInstrument(ins-1);\r
147 \r
148         /* special values of [SBYTE0] are handled here \r
149            we have no idea if these strange values will ever be encountered.\r
150            but it appears as those stms sound correct. */\r
151         if((note==254)||(note==252)) {\r
152                 UniPTEffect(0xc,0); /* note cut */\r
153                 n->volcmd|=0x80;\r
154         } else\r
155                 /* if note < 251, then all three bytes are stored in the file */\r
156           if(note<251) UniNote((((note>>4)+2)*OCTAVE)+(note&0xf));\r
157 \r
158         if((!(n->volcmd&0x80))&&(vol<65)) UniPTEffect(0xc,vol);\r
159         if(cmd!=255)\r
160                 switch(cmd) {\r
161                         case 1: /* Axx set speed to xx */\r
162                                 UniPTEffect(0xf,inf>>4);\r
163                                 break;\r
164                         case 2: /* Bxx position jump */\r
165                                 UniPTEffect(0xb,inf);\r
166                                 break;\r
167                         case 3: /* Cxx patternbreak to row xx */\r
168                                 UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf));\r
169                                 break;\r
170                         case 4: /* Dxy volumeslide */\r
171                                 UniEffect(UNI_S3MEFFECTD,inf);\r
172                                 break;\r
173                         case 5: /* Exy toneslide down */\r
174                                 UniEffect(UNI_S3MEFFECTE,inf);\r
175                                 break;\r
176                         case 6: /* Fxy toneslide up */\r
177                                 UniEffect(UNI_S3MEFFECTF,inf);\r
178                                 break;\r
179                         case 7: /* Gxx Tone portamento,speed xx */\r
180                                 UniPTEffect(0x3,inf);\r
181                                 break;\r
182                         case 8: /* Hxy vibrato */\r
183                                 UniPTEffect(0x4,inf);\r
184                                 break;\r
185                         case 9: /* Ixy tremor, ontime x, offtime y */\r
186                                 UniEffect(UNI_S3MEFFECTI,inf);\r
187                         break;\r
188                         case 0:         /* protracker arpeggio */\r
189                                 if(!inf) break;\r
190                                 /* fall through */\r
191                         case 0xa:       /* Jxy arpeggio */\r
192                                 UniPTEffect(0x0,inf);\r
193                                 break;\r
194                         case 0xb:       /* Kxy Dual command H00 & Dxy */\r
195                                 UniPTEffect(0x4,0);\r
196                                 UniEffect(UNI_S3MEFFECTD,inf);\r
197                                 break;\r
198                         case 0xc:       /* Lxy Dual command G00 & Dxy */\r
199                                 UniPTEffect(0x3,0);\r
200                                 UniEffect(UNI_S3MEFFECTD,inf);\r
201                                 break;\r
202                         /* Support all these above, since ST2 can LOAD these values but can\r
203                            actually only play up to J - and J is only half-way implemented\r
204                            in ST2 */\r
205                         case 0x18:      /* Xxx amiga panning command 8xx */\r
206                                 UniPTEffect(0x8,inf);\r
207                                 of.flags |= UF_PANNING;\r
208                                 break;\r
209                 }\r
210 }\r
211 \r
212 static UBYTE *STM_ConvertTrack(STMNOTE *n)\r
213 {\r
214         int t;\r
215 \r
216         UniReset();\r
217         for(t=0;t<64;t++) {\r
218                 STM_ConvertNote(n);\r
219                 UniNewline();\r
220                 n+=of.numchn;\r
221         }\r
222         return UniDup();\r
223 }\r
224 \r
225 static BOOL STM_LoadPatterns(void)\r
226 {\r
227         int t,s,tracks=0;\r
228 \r
229         if(!AllocPatterns()) return 0;\r
230         if(!AllocTracks()) return 0;\r
231 \r
232         /* Allocate temporary buffer for loading and converting the patterns */\r
233         for(t=0;t<of.numpat;t++) {\r
234                 for(s=0;s<(64U*of.numchn);s++) {\r
235                         stmbuf[s].note   = _mm_read_UBYTE(modreader);\r
236                         stmbuf[s].insvol = _mm_read_UBYTE(modreader);\r
237                         stmbuf[s].volcmd = _mm_read_UBYTE(modreader);\r
238                         stmbuf[s].cmdinf = _mm_read_UBYTE(modreader);\r
239                 }\r
240 \r
241                 if(_mm_eof(modreader)) {\r
242                         _mm_errno = MMERR_LOADING_PATTERN;\r
243                         return 0;\r
244                 }\r
245 \r
246                 for(s=0;s<of.numchn;s++)\r
247                         if(!(of.tracks[tracks++]=STM_ConvertTrack(stmbuf+s))) return 0;\r
248         }\r
249         return 1;\r
250 }\r
251 \r
252 BOOL STM_Load(BOOL curious)\r
253 {\r
254         int t; \r
255         ULONG MikMod_ISA; /* We must generate our own ISA, it's not stored in stm */\r
256         SAMPLE *q;\r
257         (void)curious;\r
258 \r
259         /* try to read stm header */\r
260         _mm_read_string(mh->songname,20,modreader);\r
261         _mm_read_string(mh->trackername,8,modreader);\r
262         mh->unused      =_mm_read_UBYTE(modreader);\r
263         mh->filetype    =_mm_read_UBYTE(modreader);\r
264         mh->ver_major   =_mm_read_UBYTE(modreader);\r
265         mh->ver_minor   =_mm_read_UBYTE(modreader);\r
266         mh->inittempo   =_mm_read_UBYTE(modreader);\r
267         if(!mh->inittempo) {\r
268                 _mm_errno=MMERR_NOT_A_MODULE;\r
269                 return 0;\r
270         }\r
271         mh->numpat      =_mm_read_UBYTE(modreader);\r
272         mh->globalvol   =_mm_read_UBYTE(modreader);\r
273         _mm_read_UBYTES(mh->reserved,13,modreader);\r
274 \r
275         for(t=0;t<31;t++) {\r
276                 STMSAMPLE *s=&mh->sample[t];    /* STM sample data */\r
277 \r
278                 _mm_read_string(s->filename,12,modreader);\r
279                 s->unused   =_mm_read_UBYTE(modreader);\r
280                 s->instdisk =_mm_read_UBYTE(modreader);\r
281                 s->reserved =_mm_read_I_UWORD(modreader);\r
282                 s->length   =_mm_read_I_UWORD(modreader);\r
283                 s->loopbeg  =_mm_read_I_UWORD(modreader);\r
284                 s->loopend  =_mm_read_I_UWORD(modreader);\r
285                 s->volume   =_mm_read_UBYTE(modreader);\r
286                 s->reserved2=_mm_read_UBYTE(modreader);\r
287                 s->c2spd    =_mm_read_I_UWORD(modreader);\r
288                 s->reserved3=_mm_read_I_ULONG(modreader);\r
289                 s->isa      =_mm_read_I_UWORD(modreader);\r
290         }\r
291         _mm_read_UBYTES(mh->patorder,128,modreader);\r
292 \r
293         if(_mm_eof(modreader)) {\r
294                 _mm_errno = MMERR_LOADING_HEADER;\r
295                 return 0;\r
296         }\r
297 \r
298         /* set module variables */\r
299         for(t=0;t<STM_NTRACKERS;t++)\r
300                 if(!memcmp(mh->trackername,STM_Signatures[t],8)) break;\r
301         of.modtype   = strdup(STM_Version[t]);\r
302         of.songname  = DupStr(mh->songname,20,1); /* make a cstr of songname */\r
303         of.numpat    = mh->numpat;\r
304         of.inittempo = 125;                     /* mh->inittempo+0x1c; */\r
305         of.initspeed = mh->inittempo>>4;\r
306         of.numchn    = 4;                       /* get number of channels */\r
307         of.reppos    = 0;\r
308         of.flags    |= UF_S3MSLIDES;\r
309         of.bpmlimit  = 32;\r
310 \r
311         t=0;\r
312         if(!AllocPositions(0x80)) return 0;\r
313         /* 99 terminates the patorder list */\r
314         while((mh->patorder[t]<=99)&&(mh->patorder[t]<mh->numpat)) {\r
315                 of.positions[t]=mh->patorder[t];\r
316                 t++;\r
317         }\r
318         if(mh->patorder[t]<=99) t++;\r
319         of.numpos=t;\r
320         of.numtrk=of.numpat*of.numchn;\r
321         of.numins=of.numsmp=31;\r
322 \r
323         if(!AllocSamples()) return 0;\r
324         if(!STM_LoadPatterns()) return 0;\r
325         MikMod_ISA=_mm_ftell(modreader);\r
326         MikMod_ISA=(MikMod_ISA+15)&0xfffffff0;  /* normalize */\r
327 \r
328         for(q=of.samples,t=0;t<of.numsmp;t++,q++) {\r
329                 /* load sample info */\r
330                 q->samplename = DupStr(mh->sample[t].filename,12,1);\r
331                 q->speed      = (mh->sample[t].c2spd * 8363) / 8448;\r
332                 q->volume     = mh->sample[t].volume;\r
333                 q->length     = mh->sample[t].length;\r
334                 if (/*(!mh->sample[t].volume)||*/(q->length==1)) q->length=0;\r
335                 q->loopstart  = mh->sample[t].loopbeg;\r
336                 q->loopend    = mh->sample[t].loopend;\r
337                 q->seekpos    = MikMod_ISA;\r
338 \r
339                 MikMod_ISA+=q->length;\r
340                 MikMod_ISA=(MikMod_ISA+15)&0xfffffff0;  /* normalize */\r
341 \r
342                 /* contrary to the STM specs, sample data is signed */\r
343                 q->flags = SF_SIGNED;\r
344 \r
345                 if(q->loopend && q->loopend != 0xffff)\r
346                                 q->flags|=SF_LOOP;\r
347         }\r
348         return 1;\r
349 }\r
350 \r
351 CHAR *STM_LoadTitle(void)\r
352 {\r
353         CHAR s[20];\r
354 \r
355         _mm_fseek(modreader,0,SEEK_SET);\r
356         if(!_mm_read_UBYTES(s,20,modreader)) return NULL;\r
357 \r
358         return(DupStr(s,20,1));\r
359 }\r
360 \r
361 /*========== Loader information */\r
362 \r
363 MIKMODAPI MLOADER load_stm={\r
364         NULL,\r
365         "STM",\r
366         "STM (Scream Tracker)",\r
367         STM_Init,\r
368         STM_Test,\r
369         STM_Load,\r
370         STM_Cleanup,\r
371         STM_LoadTitle\r
372 };\r
373 \r
374 \r
375 /* ex:set ts=4: */\r