added an old version of mikmod for dos
[dosdemo] / libs / oldmik / src / load_stm.c
1 /*
2
3 Name:
4 LOAD_STM.C
5
6 Description:
7 ScreamTracker 2 (STM) module Loader - Version 1.oOo Release 2 
8 A Coding Nightmare by Rao and Air Richter of HaRDCoDE
9 You can now play all of those wonderful old C.C. Catch STM's!
10
11 Portability:
12 All systems - all compilers (hopefully)
13
14 */
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include "mikmod.h"
20
21 typedef struct STMNOTE{
22    UBYTE note,insvol,volcmd,cmdinf;
23 } STMNOTE;
24
25
26 /* Raw STM sampleinfo struct: */
27
28 typedef struct STMSAMPLE{
29    char  filename[12]; /* Can't have long comments - just filename comments :) */
30    char  unused;       /* 0x00 */
31    UBYTE instdisk;     /* Instrument disk */
32    UWORD reserved;     /* ISA in memory when in ST 2 */
33    UWORD length;       /* Sample length */
34    UWORD loopbeg;      /* Loop start point */
35    UWORD loopend;      /* Loop end point */
36    UBYTE volume;       /* Volume */
37    UBYTE reserved2;    /* More reserved crap */
38    UWORD c2spd;        /* Good old c2spd */
39    UBYTE reserved3[4]; /* Yet more of PSi's reserved crap */
40    UWORD isa;          /* Internal Segment Address -> */
41                                            /*    contrary to the tech specs, this is NOT actually */
42                                            /*    written to the stm file. */
43 } STMSAMPLE;
44
45 /* Raw STM header struct: */
46
47 typedef struct STMHEADER{
48    char songname[20];
49    char trackername[8];   /* !SCREAM! for ST 2.xx */
50    char unused;           /* 0x1A */
51    char filetype;         /* 1=song, 2=module (only 2 is supported, of course) :) */
52    char ver_major;        /* Like 2 */
53    char ver_minor;        /* "ditto" */
54    UBYTE inittempo;       /* initspeed= stm inittempo>>4 */
55    UBYTE  numpat;         /* number of patterns */
56    UBYTE   globalvol;     /* <- WoW! a RiGHT TRiANGLE =8*) */
57    UBYTE    reserved[13]; /* More of PSi's internal crap */
58    STMSAMPLE sample[31];  /* STM sample data */
59    UBYTE patorder[128];   /* Docs say 64 - actually 128 */
60 } STMHEADER;
61
62
63 static STMNOTE *stmbuf;
64 static STMHEADER *mh;
65
66 char STM_Version[]="Screamtracker 2";
67
68
69
70 BOOL STM_Test(void)
71 {
72    char str[9],filetype;
73
74    _mm_fseek(modfp,21,SEEK_SET);
75    fread(str,1,9,modfp);
76    fread(&filetype,1,1,modfp);
77    if(!memcmp(str,"!SCREAM!",8) || (filetype!=2)) /* STM Module = filetype 2 */
78           return 0;
79    return 1;
80 }
81
82
83
84 BOOL STM_Init(void)
85 {
86         stmbuf=NULL;
87         if(!(mh=(STMHEADER *)MyCalloc(1,sizeof(STMHEADER)))) return 0;
88         return 1;
89 }
90
91 void STM_Cleanup(void)
92 {
93         if(mh!=NULL) free(mh);
94         if(stmbuf!=NULL) free(stmbuf);
95 }
96
97
98
99 void STM_ConvertNote(STMNOTE *n)
100 {
101         UBYTE note,ins,vol,cmd,inf;
102
103         /* extract the various information from the 4 bytes that
104            make up a single note */
105
106                 note=n->note;
107                 ins=n->insvol>>3;
108                 vol=(n->insvol&7)+(n->volcmd>>1);
109                 cmd=n->volcmd&15;
110                 inf=n->cmdinf;
111
112                 if(ins!=0 && ins<32){
113                         UniInstrument(ins-1);
114                 }
115
116       /* special values of [SBYTE0] are handled here -> */
117       /* we have no idea if these strange values will ever be encountered */
118           /* but it appears as though stms sound correct. */
119       if(note==254 || note==252) UniPTEffect(0xc,0); /* <- note off command (???) */
120              else
121       /* if note < 251, then all three bytes are stored in the file */
122       if(note<251) UniNote((((note>>4)+2)*12)+(note&0xf));      /* <- normal note and up the octave by two */
123
124                 if(vol<65){
125                         UniPTEffect(0xc,vol);
126                 }
127
128                 if(cmd!=255){
129                         switch(cmd){
130
131                                 case 1:                 /* Axx set speed to xx and add 0x1c to fix StoOoPiD STM 2.x */
132                                         UniPTEffect(0xf,inf>>4);
133                                         break;
134
135                                 case 2:                 /* Bxx position jump */
136                                         UniPTEffect(0xb,inf);
137                                         break;
138
139                                 case 3:                 /* Cxx patternbreak to row xx */
140                                         UniPTEffect(0xd,inf);
141                                         break;
142
143                                 case 4:                 /* Dxy volumeslide */
144                                         UniWrite(UNI_S3MEFFECTD);
145                                         UniWrite(inf);
146                                         break;
147
148                                 case 5:                 /* Exy toneslide down */
149                                         UniWrite(UNI_S3MEFFECTE);
150                                         UniWrite(inf);
151                                         break;
152
153                                 case 6:                 /* Fxy toneslide up */
154                                         UniWrite(UNI_S3MEFFECTF);
155                                         UniWrite(inf);
156                                         break;
157
158                                 case 7:                 /* Gxx Tone portamento,speed xx */
159                                         UniPTEffect(0x3,inf);
160                                         break;
161
162                                 case 8:                 /* Hxy vibrato */
163                                         UniPTEffect(0x4,inf);
164                                         break;
165
166                                 case 9:                 /* Ixy tremor, ontime x, offtime y */
167                                         UniWrite(UNI_S3MEFFECTI);
168                                         UniWrite(inf);
169                                         break;
170
171                                 case 0xa:               /* Jxy arpeggio */
172                                         UniPTEffect(0x0,inf);
173                                         break;
174
175                                 case 0xb:               /* Kxy Dual command H00 & Dxy */
176                                         UniPTEffect(0x4,0);
177                                         UniWrite(UNI_S3MEFFECTD);
178                                         UniWrite(inf);
179                                         break;
180
181                                 case 0xc:               /* Lxy Dual command G00 & Dxy */
182                                         UniPTEffect(0x3,0);
183                                         UniWrite(UNI_S3MEFFECTD);
184                                         UniWrite(inf);
185                                         break;
186
187                 /* Support all these above, since ST2 can LOAD these values */
188                 /* but can actually only play up to J - and J is only */
189                 /* half-way implemented in ST2 */
190
191                                 case 0x18:      /* Xxx amiga command 8xx - What the hell, support panning. :) */
192                                         UniPTEffect(0x8,inf);
193                                         break;
194                         }
195                 }
196
197 }
198
199
200 UBYTE *STM_ConvertTrack(STMNOTE *n)
201 {
202         int t;
203
204         UniReset();
205         for(t=0;t<64;t++)
206         {       STM_ConvertNote(n);
207                 UniNewline();
208                 n+=of.numchn;
209         }
210         return UniDup();
211 }
212
213
214
215
216 BOOL STM_LoadPatterns(void)
217 {
218         int t,s,tracks=0;
219
220         if(!AllocPatterns()) return 0;
221         if(!AllocTracks()) return 0;
222
223         /* Allocate temporary buffer for loading
224            and converting the patterns */
225
226         if(!(stmbuf=(STMNOTE *)MyCalloc(64U*of.numchn,sizeof(STMNOTE)))) return 0;
227
228         for(t=0;t<of.numpat;t++){
229
230                 for(s=0;s<(64U*of.numchn);s++){
231                         stmbuf[s].note=_mm_read_UBYTE(modfp);
232                         stmbuf[s].insvol=_mm_read_UBYTE(modfp);
233                         stmbuf[s].volcmd=_mm_read_UBYTE(modfp);
234                         stmbuf[s].cmdinf=_mm_read_UBYTE(modfp);
235                 }
236
237                 if(feof(modfp)){
238                         myerr=ERROR_LOADING_PATTERN;
239                         return 0;
240                 }
241
242                 for(s=0;s<of.numchn;s++){
243                         if(!(of.tracks[tracks++]=STM_ConvertTrack(stmbuf+s))) return 0;
244                 }
245         }
246
247         return 1;
248 }
249
250
251
252 BOOL STM_Load(void)
253 {
254         int t;
255         ULONG MikMod_ISA; /* We MUST generate our own ISA - NOT stored in the stm */
256         INSTRUMENT *d;
257         SAMPLE *q;
258
259         /* try to read stm header */
260
261         _mm_read_str(mh->songname,20,modfp);
262         _mm_read_str(mh->trackername,8,modfp);
263         mh->unused              =_mm_read_UBYTE(modfp);
264         mh->filetype    =_mm_read_UBYTE(modfp);
265         mh->ver_major   =_mm_read_UBYTE(modfp);
266         mh->ver_minor   =_mm_read_UBYTE(modfp);
267         mh->inittempo   =_mm_read_UBYTE(modfp);
268         mh->numpat              =_mm_read_UBYTE(modfp);
269         mh->globalvol   =_mm_read_UBYTE(modfp);
270         _mm_read_UBYTES(mh->reserved,13,modfp);
271
272         for(t=0;t<31;t++){
273                 STMSAMPLE *s=&mh->sample[t];  /* STM sample data */
274                 _mm_read_str(s->filename,12,modfp);
275                 s->unused       =_mm_read_UBYTE(modfp);
276                 s->instdisk     =_mm_read_UBYTE(modfp);
277                 s->reserved     =_mm_read_I_UWORD(modfp);
278                 s->length       =_mm_read_I_UWORD(modfp);
279                 s->loopbeg      =_mm_read_I_UWORD(modfp);
280                 s->loopend      =_mm_read_I_UWORD(modfp);
281                 s->volume       =_mm_read_UBYTE(modfp);
282                 s->reserved2=_mm_read_UBYTE(modfp);
283                 s->c2spd        =_mm_read_I_UWORD(modfp);
284                 _mm_read_UBYTES(s->reserved3,4,modfp);
285                 s->isa          =_mm_read_I_UWORD(modfp);
286         }
287         _mm_read_UBYTES(mh->patorder,128,modfp);
288
289         if(feof(modfp)){
290                 myerr=ERROR_LOADING_HEADER;
291                 return 0;
292         }
293
294         /* set module variables */
295
296         of.modtype=strdup(STM_Version);
297         of.songname=DupStr(mh->songname,20); /* make a cstr of songname */
298
299         of.numpat=mh->numpat;
300
301         of.initspeed=6; /* Always this */
302
303         /* STM 2.x tempo has always been fucked... The default of 96 */
304         /* is actually 124, so we add 1ch to the initial value of 60h */
305
306         /* MikMak: No it's not.. STM tempo is UNI speed << 4 */
307
308         of.inittempo=125;               /* mh->inittempo+0x1c; */
309         of.initspeed=mh->inittempo>>4;
310         of.numchn=4; /* get number of channels */
311
312         t=0;
313         while(mh->patorder[t]!=99){ /* 99 terminates the patorder list */
314                 of.positions[t]=mh->patorder[t];
315                 t++;
316         }
317         of.numpos=--t;
318         of.numtrk=of.numpat*of.numchn;
319
320         /* Finally, init the sampleinfo structures */
321
322         of.numins=31; /* always this */
323
324         if(!AllocInstruments()) return 0;
325         if(!STM_LoadPatterns()) return 0;
326
327         d=of.instruments;
328
329         MikMod_ISA=ftell(modfp);
330         MikMod_ISA=(MikMod_ISA+15)&0xfffffff0;
331
332         for(t=0;t<of.numins;t++){
333
334                 d->numsmp=1;
335                 if(!AllocSamples(d)) return 0;
336                 q=d->samples;
337
338                 /* load sample info */
339
340                 d->insname=DupStr(mh->sample[t].filename,12);
341                 q->c2spd=mh->sample[t].c2spd;
342                 q->volume=mh->sample[t].volume;
343                 q->length=mh->sample[t].length;
344                 if (!mh->sample[t].volume || q->length==1 ) q->length = 0; /* if vol = 0 or length = 1, then no sample */
345                 q->loopstart=mh->sample[t].loopbeg;
346                 q->loopend=mh->sample[t].loopend;
347                 q->seekpos=MikMod_ISA;
348
349                 MikMod_ISA+=q->length;
350
351                 MikMod_ISA=(MikMod_ISA+15)&0xfffffff0;
352
353           /* Once again, contrary to the STM specs, all the sample data is */
354           /* actually SIGNED! Sheesh */
355
356                 q->flags=SF_SIGNED;
357
358                 if(mh->sample[t].loopend>0 && mh->sample[t].loopend!=0xffff) q->flags|=SF_LOOP;
359
360                 /* fix replen if repend>length */
361
362                 if(q->loopend>q->length) q->loopend=q->length;
363
364                 d++;
365         }
366
367         return 1;
368 }
369
370
371 LOADER load_stm={
372         NULL,
373         "STM",
374         "Portable STM Loader - V 1.2 - A Coding Nightmare by Rao and Air Richter of HaRDCoDE",
375         STM_Init,
376         STM_Test,
377         STM_Load,
378         STM_Cleanup
379 };