added an old version of mikmod for dos
[dosdemo] / libs / oldmik / src / load_s3m.c
1 /*\r
2 \r
3 Name:\r
4 LOAD_S3M.C\r
5 \r
6 Description:\r
7 Screamtracker (S3M) module loader\r
8 \r
9 Portability:\r
10 All systems - all compilers (hopefully)\r
11 \r
12 */\r
13 #include <stdio.h>\r
14 #include <stdlib.h>\r
15 #include <string.h>\r
16 #include <ctype.h>\r
17 #include "mikmod.h"\r
18 \r
19 /**************************************************************************\r
20 **************************************************************************/\r
21 \r
22 typedef struct S3MNOTE{\r
23         UBYTE note,ins,vol,cmd,inf;\r
24 } S3MNOTE;\r
25 \r
26 typedef S3MNOTE S3MTRACK[64];\r
27 \r
28 \r
29 /* Raw S3M header struct: */\r
30 \r
31 typedef struct S3MHEADER{\r
32         char  songname[28];\r
33         char  t1a;\r
34         char  type;\r
35         UBYTE unused1[2];\r
36         UWORD ordnum;\r
37         UWORD insnum;\r
38         UWORD patnum;\r
39         UWORD flags;\r
40         UWORD tracker;\r
41         UWORD fileformat;\r
42         char  scrm[4];\r
43         UBYTE mastervol;\r
44         UBYTE initspeed;\r
45         UBYTE inittempo;\r
46         UBYTE mastermult;\r
47         UBYTE ultraclick;\r
48         UBYTE pantable;\r
49         UBYTE unused2[8];\r
50         UWORD special;\r
51         UBYTE channels[32];\r
52 } S3MHEADER;\r
53 \r
54 \r
55 /* Raw S3M sampleinfo struct: */\r
56 \r
57 typedef struct S3MSAMPLE{\r
58         UBYTE type;\r
59         char  filename[12];\r
60         UBYTE memsegh;\r
61         UWORD memsegl;\r
62         ULONG length;\r
63         ULONG loopbeg;\r
64         ULONG loopend;\r
65         UBYTE volume;\r
66         UBYTE dsk;\r
67         UBYTE pack;\r
68         UBYTE flags;\r
69         ULONG c2spd;\r
70         UBYTE unused[12];\r
71         char  sampname[28];\r
72         char  scrs[4];\r
73 } S3MSAMPLE;\r
74 \r
75 \r
76 /**************************************************************************\r
77 **************************************************************************/\r
78 \r
79 \r
80 \r
81 S3MNOTE *s3mbuf;        /* pointer to a complete S3M pattern */\r
82 UWORD *paraptr;         /* parapointer array (see S3M docs) */\r
83 static S3MHEADER *mh;\r
84 UBYTE remap[32];\r
85 \r
86 \r
87 char S3M_Version[]="Screamtracker 3.xx";\r
88 \r
89 \r
90 \r
91 BOOL S3M_Test(void)\r
92 {\r
93         char id[4];\r
94         _mm_fseek(modfp,0x2c,SEEK_SET);\r
95         if(!fread(id,4,1,modfp)) return 0;\r
96         if(!memcmp(id,"SCRM",4)) return 1;\r
97         return 0;\r
98 }\r
99 \r
100 BOOL S3M_Init(void)\r
101 {\r
102         s3mbuf=NULL;\r
103         paraptr=NULL;\r
104 \r
105         if(!(s3mbuf=(S3MNOTE *)MyMalloc(16*64*sizeof(S3MNOTE)))) return 0;\r
106         if(!(mh=(S3MHEADER *)MyCalloc(1,sizeof(S3MHEADER)))) return 0;\r
107 \r
108         return 1;\r
109 }\r
110 \r
111 void S3M_Cleanup(void)\r
112 {\r
113         if(s3mbuf!=NULL) free(s3mbuf);\r
114         if(paraptr!=NULL) free(paraptr);\r
115         if(mh!=NULL) free(mh);\r
116 }\r
117 \r
118 \r
119 \r
120 \r
121 BOOL S3M_ReadPattern(void)\r
122 {\r
123         int row=0,flag,ch;\r
124         S3MNOTE *n;\r
125         S3MNOTE dummy;\r
126 \r
127         /* clear pattern data */\r
128 \r
129         memset(s3mbuf,255,16*64*sizeof(S3MNOTE));\r
130 \r
131         while(row<64){\r
132 \r
133                 flag=fgetc(modfp);\r
134 \r
135                 if(flag==EOF){\r
136                         myerr="Error loading pattern";\r
137                         return 0;\r
138                 }\r
139 \r
140                 if(flag){\r
141 \r
142                         ch=flag&31;\r
143 \r
144                         if(mh->channels[ch]<16){\r
145                                 n=&s3mbuf[(64U*remap[ch])+row];\r
146                         }\r
147                         else{\r
148                                 n=&dummy;\r
149                         }\r
150 \r
151                         if(flag&32){\r
152                                 n->note=fgetc(modfp);\r
153                                 n->ins=fgetc(modfp);\r
154                         }\r
155 \r
156                         if(flag&64){\r
157                                 n->vol=fgetc(modfp);\r
158                         }\r
159 \r
160                         if(flag&128){\r
161                                 n->cmd=fgetc(modfp);\r
162                                 n->inf=fgetc(modfp);\r
163                         }\r
164                 }\r
165                 else row++;\r
166         }\r
167         return 1;\r
168 }\r
169 \r
170 \r
171 \r
172 UBYTE *S3M_ConvertTrack(S3MNOTE *tr)\r
173 {\r
174         int t;\r
175 \r
176         UBYTE note,ins,vol,cmd,inf,lo,hi;\r
177 \r
178         UniReset();\r
179         for(t=0;t<64;t++){\r
180 \r
181                 note=tr[t].note;\r
182                 ins=tr[t].ins;\r
183                 vol=tr[t].vol;\r
184                 cmd=tr[t].cmd;\r
185                 inf=tr[t].inf;\r
186                 lo=inf&0xf;\r
187                 hi=inf>>4;\r
188 \r
189 \r
190                 if(ins!=0 && ins!=255){\r
191                         UniInstrument(ins-1);\r
192                 }\r
193 \r
194                 if(note!=255){\r
195                         if(note==254) UniPTEffect(0xc,0);                       /* <- note off command */\r
196                         else UniNote(((note>>4)*12)+(note&0xf));        /* <- normal note */\r
197                 }\r
198 \r
199                 if(vol<255){\r
200                         UniPTEffect(0xc,vol);\r
201 /*                      UniWrite(UNI_S3MVOLUME); */\r
202 /*                      UniWrite(vol); */\r
203                 }\r
204 \r
205                 if(cmd!=255){\r
206                         switch(cmd){\r
207 \r
208                                 case 1:                 /* Axx set speed to xx */\r
209                                         UniWrite(UNI_S3MEFFECTA);\r
210                                         UniWrite(inf);\r
211                                         break;\r
212 \r
213                                 case 2:                 /* Bxx position jump */\r
214                                         UniPTEffect(0xb,inf);\r
215                                         break;\r
216 \r
217                                 case 3:                 /* Cxx patternbreak to row xx */\r
218                                         UniPTEffect(0xd,inf);\r
219                                         break;\r
220 \r
221                                 case 4:                 /* Dxy volumeslide */\r
222                                         UniWrite(UNI_S3MEFFECTD);\r
223                                         UniWrite(inf);\r
224                                         break;\r
225 \r
226                                 case 5:                 /* Exy toneslide down */\r
227                                         UniWrite(UNI_S3MEFFECTE);\r
228                                         UniWrite(inf);\r
229                                         break;\r
230 \r
231                                 case 6:                 /* Fxy toneslide up */\r
232                                         UniWrite(UNI_S3MEFFECTF);\r
233                                         UniWrite(inf);\r
234                                         break;\r
235 \r
236                                 case 7:                 /* Gxx Tone portamento,speed xx */\r
237                                         UniPTEffect(0x3,inf);\r
238                                         break;\r
239 \r
240                                 case 8:                 /* Hxy vibrato */\r
241                                         UniPTEffect(0x4,inf);\r
242                                         break;\r
243 \r
244                                 case 9:                 /* Ixy tremor, ontime x, offtime y */\r
245                                         UniWrite(UNI_S3MEFFECTI);\r
246                                         UniWrite(inf);\r
247                                         break;\r
248 \r
249                                 case 0xa:               /* Jxy arpeggio */\r
250                                         UniPTEffect(0x0,inf);\r
251                                         break;\r
252 \r
253                                 case 0xb:               /* Kxy Dual command H00 & Dxy */\r
254                                         UniPTEffect(0x4,0);\r
255                                         UniWrite(UNI_S3MEFFECTD);\r
256                                         UniWrite(inf);\r
257                                         break;\r
258 \r
259                                 case 0xc:               /* Lxy Dual command G00 & Dxy */\r
260                                         UniPTEffect(0x3,0);\r
261                                         UniWrite(UNI_S3MEFFECTD);\r
262                                         UniWrite(inf);\r
263                                         break;\r
264 \r
265                                 case 0xf:               /* Oxx set sampleoffset xx00h */\r
266                                         UniPTEffect(0x9,inf);\r
267                                         break;\r
268 \r
269                                 case 0x11:              /* Qxy Retrig (+volumeslide) */\r
270                                         UniWrite(UNI_S3MEFFECTQ);\r
271                                         UniWrite(inf);\r
272                                         break;\r
273 \r
274                                 case 0x12:              /* Rxy tremolo speed x, depth y */\r
275                                         UniPTEffect(0x6,inf);\r
276                                         break;\r
277 \r
278                                 case 0x13:              /* Sxx special commands */\r
279                                         switch(hi){\r
280 \r
281                                                 case 0: /* S0x set filter */\r
282                                                         UniPTEffect(0xe,0x00|lo);\r
283                                                         break;\r
284 \r
285                                                 case 1: /* S1x set glissando control */\r
286                                                         UniPTEffect(0xe,0x30|lo);\r
287                                                         break;\r
288 \r
289                                                 case 2: /* S2x set finetune */\r
290                                                         UniPTEffect(0xe,0x50|lo);\r
291                                                         break;\r
292 \r
293                                                 case 3: /* S3x set vibrato waveform */\r
294                                                         UniPTEffect(0xe,0x40|lo);\r
295                                                         break;\r
296 \r
297                                                 case 4: /* S4x set tremolo waveform */\r
298                                                         UniPTEffect(0xe,0x70|lo);\r
299                                                         break;\r
300 \r
301                                                 case 8: /* S8x set panning position */\r
302                                                         UniPTEffect(0xe,0x80|lo);\r
303                                                         break;\r
304 \r
305                                                 case 0xb:       /* SBx pattern loop */\r
306                                                         UniPTEffect(0xe,0x60|lo);\r
307                                                         break;\r
308 \r
309                                                 case 0xc:       /* SCx notecut */\r
310                                                         UniPTEffect(0xe,0xC0|lo);\r
311                                                         break;\r
312 \r
313                                                 case 0xd:       /* SDx notedelay */\r
314                                                         UniPTEffect(0xe,0xD0|lo);\r
315                                                         break;\r
316 \r
317                                                 case 0xe:       /* SDx patterndelay */\r
318                                                         UniPTEffect(0xe,0xE0|lo);\r
319                                                         break;\r
320                                         }\r
321                                         break;\r
322 \r
323                                 case 0x14:      /* Txx tempo */\r
324                                         if(inf>0x20){\r
325                                                 UniWrite(UNI_S3MEFFECTT);\r
326                                                 UniWrite(inf);\r
327                                         }\r
328                                         break;\r
329 \r
330                                 case 0x18:      /* Xxx amiga command 8xx */\r
331                                         UniPTEffect(0x8,inf);\r
332                                         break;\r
333                         }\r
334                 }\r
335 \r
336                 UniNewline();\r
337         }\r
338         return UniDup();\r
339 }\r
340 \r
341 \r
342 \r
343 \r
344 BOOL S3M_Load(void)\r
345 {\r
346         int t,u,track=0;\r
347         INSTRUMENT *d;\r
348         SAMPLE *q;\r
349         UBYTE isused[16];\r
350         UBYTE pan[32];\r
351 \r
352         /* try to read module header */\r
353 \r
354         _mm_read_str(mh->songname,28,modfp);\r
355         mh->t1a                 =_mm_read_UBYTE(modfp);\r
356         mh->type                =_mm_read_UBYTE(modfp);\r
357         _mm_read_UBYTES(mh->unused1,2,modfp);\r
358         mh->ordnum              =_mm_read_I_UWORD(modfp);\r
359         mh->insnum              =_mm_read_I_UWORD(modfp);\r
360         mh->patnum              =_mm_read_I_UWORD(modfp);\r
361         mh->flags               =_mm_read_I_UWORD(modfp);\r
362         mh->tracker             =_mm_read_I_UWORD(modfp);\r
363         mh->fileformat  =_mm_read_I_UWORD(modfp);\r
364         _mm_read_str(mh->scrm,4,modfp);\r
365 \r
366         mh->mastervol   =_mm_read_UBYTE(modfp);\r
367         mh->initspeed   =_mm_read_UBYTE(modfp);\r
368         mh->inittempo   =_mm_read_UBYTE(modfp);\r
369         mh->mastermult  =_mm_read_UBYTE(modfp);\r
370         mh->ultraclick  =_mm_read_UBYTE(modfp);\r
371         mh->pantable    =_mm_read_UBYTE(modfp);\r
372         _mm_read_UBYTES(mh->unused2,8,modfp);\r
373         mh->special             =_mm_read_I_UWORD(modfp);\r
374         _mm_read_UBYTES(mh->channels,32,modfp);\r
375 \r
376         if(feof(modfp)){\r
377                 myerr="Error loading header";\r
378                 return 0;\r
379         }\r
380 \r
381         /* set module variables */\r
382 \r
383         of.modtype=strdup(S3M_Version);\r
384         of.songname=DupStr(mh->songname,28);    /* make a cstr of songname */\r
385         of.numpat=mh->patnum;\r
386         of.numins=mh->insnum;\r
387         of.initspeed=mh->initspeed;\r
388         of.inittempo=mh->inittempo;\r
389 \r
390         /* count the number of channels used */\r
391 \r
392         of.numchn=0;\r
393 \r
394 /*      for(t=0;t<32;t++) printf("%2.2x ",mh->channels[t]);\r
395 */\r
396         for(t=0;t<32;t++) remap[t]=0;\r
397         for(t=0;t<16;t++) isused[t]=0;\r
398 \r
399         /* set a flag for each channel (1 out of of 16) thats being used: */\r
400 \r
401         for(t=0;t<32;t++){\r
402                 if(mh->channels[t]<16){\r
403                         isused[mh->channels[t]]=1;\r
404                 }\r
405         }\r
406 \r
407         /* give each of them a different number */\r
408 \r
409         for(t=0;t<16;t++){\r
410                 if(isused[t]){\r
411                         isused[t]=of.numchn;\r
412                         of.numchn++;\r
413                 }\r
414         }\r
415 \r
416         /* build the remap array */\r
417 \r
418         for(t=0;t<32;t++){\r
419                 if(mh->channels[t]<16){\r
420                         remap[t]=isused[mh->channels[t]];\r
421                 }\r
422         }\r
423 \r
424         /* set panning positions */\r
425 \r
426         for(t=0;t<32;t++){\r
427                 if(mh->channels[t]<16){\r
428                         if(mh->channels[t]<8){\r
429                                 of.panning[remap[t]]=0x30;\r
430                         }\r
431                         else{\r
432                                 of.panning[remap[t]]=0xc0;\r
433                         }\r
434                 }\r
435         }\r
436 \r
437         of.numtrk=of.numpat*of.numchn;\r
438 \r
439 /*      printf("Uses %d channels\n",of.numchn);\r
440 */\r
441         /* read the order data */\r
442 \r
443         _mm_read_UBYTES(of.positions,mh->ordnum,modfp);\r
444 \r
445         of.numpos=0;\r
446         for(t=0;t<mh->ordnum;t++){\r
447                 of.positions[of.numpos]=of.positions[t];\r
448                 if(of.positions[t]<254) of.numpos++;\r
449         }\r
450 \r
451         if((paraptr=(UWORD *)MyMalloc((of.numins+of.numpat)*sizeof(UWORD)))==NULL) return 0;\r
452 \r
453         /* read the instrument+pattern parapointers */\r
454 \r
455         _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modfp);\r
456 \r
457 /*      printf("pantab %d\n",mh->pantable);\r
458 */\r
459         if(mh->pantable==252){\r
460 \r
461                 /* read the panning table */\r
462 \r
463                 _mm_read_UBYTES(pan,32,modfp);\r
464 \r
465                 /* set panning positions according to panning table (new for st3.2) */\r
466 \r
467                 for(t=0;t<32;t++){\r
468                         if((pan[t]&0x20) && mh->channels[t]<16){\r
469                                 of.panning[remap[t]]=(pan[t]&0xf)<<4;\r
470                         }\r
471                 }\r
472         }\r
473 \r
474         /* now is a good time to check if the header was too short :) */\r
475 \r
476         if(feof(modfp)){\r
477                 myerr="Error loading header";\r
478                 return 0;\r
479         }\r
480 \r
481         if(!AllocInstruments()) return 0;\r
482 \r
483         d=of.instruments;\r
484 \r
485         for(t=0;t<of.numins;t++){\r
486                 S3MSAMPLE s;\r
487 \r
488                 d->numsmp=1;\r
489                 if(!AllocSamples(d)) return 0;\r
490                 q=d->samples;\r
491 \r
492                 /* seek to instrument position */\r
493 \r
494                 _mm_fseek(modfp,((long)paraptr[t])<<4,SEEK_SET);\r
495 \r
496                 /* and load sample info */\r
497 \r
498                 s.type          =_mm_read_UBYTE(modfp);\r
499                 _mm_read_str(s.filename,12,modfp);\r
500                 s.memsegh       =_mm_read_UBYTE(modfp);\r
501                 s.memsegl       =_mm_read_I_UWORD(modfp);\r
502                 s.length        =_mm_read_I_ULONG(modfp);\r
503                 s.loopbeg       =_mm_read_I_ULONG(modfp);\r
504                 s.loopend       =_mm_read_I_ULONG(modfp);\r
505                 s.volume        =_mm_read_UBYTE(modfp);\r
506                 s.dsk           =_mm_read_UBYTE(modfp);\r
507                 s.pack          =_mm_read_UBYTE(modfp);\r
508                 s.flags         =_mm_read_UBYTE(modfp);\r
509                 s.c2spd         =_mm_read_I_ULONG(modfp);\r
510                 _mm_read_UBYTES(s.unused,12,modfp);\r
511                 _mm_read_str(s.sampname,28,modfp);\r
512                 _mm_read_str(s.scrs,4,modfp);\r
513 \r
514                 if(feof(modfp)){\r
515                         myerr=ERROR_LOADING_HEADER;\r
516                         return 0;\r
517                 }\r
518 \r
519                 d->insname=DupStr(s.sampname,28);\r
520                 q->c2spd=s.c2spd;\r
521                 q->length=s.length;\r
522                 q->loopstart=s.loopbeg;\r
523                 q->loopend=s.loopend;\r
524                 q->volume=s.volume;\r
525                 q->seekpos=(((long)s.memsegh)<<16|s.memsegl)<<4;\r
526 \r
527                 q->flags=0;\r
528 \r
529                 if(s.flags&1) q->flags|=SF_LOOP;\r
530                 if(s.flags&4) q->flags|=SF_16BITS;\r
531                 if(mh->fileformat==1) q->flags|=SF_SIGNED;\r
532 \r
533                 /* DON'T load sample if it doesn't have the SCRS tag */\r
534 \r
535                 if(memcmp(s.scrs,"SCRS",4)!=0) q->length=0;\r
536 \r
537 /*              printf("%s\n",d->insname);\r
538 */\r
539                 d++;\r
540         }\r
541 \r
542         if(!AllocTracks()) return 0;\r
543         if(!AllocPatterns()) return 0;\r
544 \r
545         for(t=0;t<of.numpat;t++){\r
546 \r
547                 /* seek to pattern position ( + 2 skip pattern length ) */\r
548 \r
549                 _mm_fseek(modfp,(((long)paraptr[of.numins+t])<<4)+2,SEEK_SET);\r
550 \r
551                 if(!S3M_ReadPattern()) return 0;\r
552 \r
553                 for(u=0;u<of.numchn;u++){\r
554                         if(!(of.tracks[track++]=S3M_ConvertTrack(&s3mbuf[u*64]))) return 0;\r
555                 }\r
556         }\r
557 \r
558         return 1;\r
559 }\r
560 \r
561 \r
562 LOADER load_s3m={\r
563         NULL,\r
564         "S3M",\r
565         "Portable S3M loader v0.2",\r
566         S3M_Init,\r
567         S3M_Test,\r
568         S3M_Load,\r
569         S3M_Cleanup\r
570 };\r