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