4
|
1 /*
|
|
2
|
|
3 Name: LOAD_S3M.C
|
|
4
|
|
5 Description:
|
|
6 Screamtracker (S3M) module loader
|
|
7
|
|
8 Portability:
|
|
9 All systems - all compilers (hopefully)
|
|
10
|
|
11 If this module is found to not be portable to any particular platform,
|
|
12 please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
|
|
13 more information on contacting the author).
|
|
14
|
|
15 */
|
|
16
|
|
17 #include <string.h>
|
|
18 #include "mikmod.h"
|
|
19
|
|
20 /**************************************************************************
|
|
21 **************************************************************************/
|
|
22
|
|
23 typedef struct S3MNOTE
|
|
24 { UBYTE note,ins,vol,cmd,inf;
|
|
25 } S3MNOTE;
|
|
26
|
|
27 typedef S3MNOTE S3MTRACK[64];
|
|
28
|
|
29
|
|
30 // Raw S3M header struct:
|
|
31
|
|
32 typedef struct S3MHEADER
|
|
33 { CHAR songname[28];
|
|
34 UBYTE t1a;
|
|
35 UBYTE type;
|
|
36 UBYTE unused1[2];
|
|
37 UWORD ordnum;
|
|
38 UWORD insnum;
|
|
39 UWORD patnum;
|
|
40 UWORD flags;
|
|
41 UWORD tracker;
|
|
42 UWORD fileformat;
|
|
43 CHAR scrm[4];
|
|
44 UBYTE mastervol;
|
|
45 UBYTE initspeed;
|
|
46 UBYTE inittempo;
|
|
47 UBYTE mastermult;
|
|
48 UBYTE ultraclick;
|
|
49 UBYTE pantable;
|
|
50 UBYTE unused2[8];
|
|
51 UWORD special;
|
|
52 UBYTE channels[32];
|
|
53 } S3MHEADER;
|
|
54
|
|
55
|
|
56 // Raw S3M sampleinfo struct:
|
|
57
|
|
58 typedef struct S3MSAMPLE
|
|
59 { UBYTE type;
|
|
60 CHAR filename[12];
|
|
61 UBYTE memsegh;
|
|
62 UWORD memsegl;
|
|
63 ULONG length;
|
|
64 ULONG loopbeg;
|
|
65 ULONG loopend;
|
|
66 UBYTE volume;
|
|
67 UBYTE dsk;
|
|
68 UBYTE pack;
|
|
69 UBYTE flags;
|
|
70 ULONG c2spd;
|
|
71 UBYTE unused[12];
|
|
72 CHAR sampname[28];
|
|
73 CHAR scrs[4];
|
|
74 } S3MSAMPLE;
|
|
75
|
|
76 /**************************************************************************
|
|
77 **************************************************************************/
|
|
78
|
|
79
|
|
80 extern UBYTE *poslookup; // S3M/IT fix - removing blank patterns needs a
|
|
81 // lookup table to fix position-jump commands
|
|
82 extern SBYTE remap[64]; // for removing empty channels
|
|
83
|
|
84 static S3MNOTE *s3mbuf = NULL; // pointer to a complete S3M pattern
|
|
85 static S3MHEADER *mh = NULL;
|
|
86 static UWORD *paraptr = NULL; // parapointer array (see S3M docs)
|
|
87
|
|
88 CHAR S3M_Version[] = "Screamtracker 3.xx";
|
|
89
|
|
90 BOOL S3M_Test(void)
|
|
91 {
|
|
92 UBYTE id[4];
|
|
93
|
|
94 _mm_fseek(modfp,0x2c,SEEK_SET);
|
|
95 if(!_mm_read_UBYTES(id,4,modfp)) return 0;
|
|
96 if(!memcmp(id,"SCRM",4)) return 1;
|
|
97 return 0;
|
|
98 }
|
|
99
|
|
100 BOOL S3M_Init(void)
|
|
101 {
|
|
102 if(!(s3mbuf = (S3MNOTE *)_mm_malloc(16*64*sizeof(S3MNOTE)))) return 0;
|
|
103 if(!(mh = (S3MHEADER *)_mm_calloc(1,sizeof(S3MHEADER)))) return 0;
|
|
104 if(!(poslookup = (UBYTE *)_mm_malloc(sizeof(UBYTE)*128))) return 0;
|
|
105
|
|
106 return 1;
|
|
107 }
|
|
108
|
|
109 void S3M_Cleanup(void)
|
|
110 {
|
|
111 if(s3mbuf!=NULL) free(s3mbuf);
|
|
112 if(paraptr!=NULL) free(paraptr);
|
|
113 if(poslookup!=NULL) free(poslookup);
|
|
114 if(mh!=NULL) free(mh);
|
|
115
|
|
116 paraptr = NULL;
|
|
117 s3mbuf = NULL;
|
|
118 poslookup = NULL;
|
|
119 mh = NULL;
|
|
120 }
|
|
121
|
|
122
|
|
123 BOOL S3M_GetNumChannels(void)
|
|
124
|
|
125 // Because so many s3m files have 16 channels as the set number used, but really
|
|
126 // only use far less (usually 8 to 12 still), I had to make this function,
|
|
127 // which determines the number of channels that are actually USED by a pattern.
|
|
128 //
|
|
129 // For every channel that's used, it sets the appropriate array entry of the
|
|
130 // global varialbe 'isused'
|
|
131 //
|
|
132 // NOTE: You must first seek to the file location of the pattern before calling
|
|
133 // this procedure.
|
|
134 // Returns 1 on fail.
|
|
135
|
|
136 {
|
|
137 int row=0,flag,ch;
|
|
138
|
|
139 while(row<64)
|
|
140 { flag = _mm_read_UBYTE(modfp);
|
|
141
|
|
142 if(feof(modfp))
|
|
143 { _mm_errno = MMERR_LOADING_PATTERN;
|
|
144 return 1;
|
|
145 }
|
|
146
|
|
147 if(flag)
|
|
148 { ch = flag&31;
|
|
149 if(mh->channels[ch] < 16) remap[ch] = 0;
|
|
150
|
|
151 if(flag&32)
|
|
152 { _mm_read_UBYTE(modfp);
|
|
153 _mm_read_UBYTE(modfp);
|
|
154 }
|
|
155
|
|
156 if(flag&64)
|
|
157 _mm_read_UBYTE(modfp);
|
|
158
|
|
159 if(flag&128)
|
|
160 { _mm_read_UBYTE(modfp);
|
|
161 _mm_read_UBYTE(modfp);
|
|
162 }
|
|
163 } else row++;
|
|
164 }
|
|
165
|
|
166 return 0;
|
|
167 }
|
|
168
|
|
169
|
|
170 BOOL S3M_ReadPattern(void)
|
|
171 {
|
|
172 int row=0,flag,ch;
|
|
173 S3MNOTE *n;
|
|
174 S3MNOTE dummy;
|
|
175
|
|
176 // clear pattern data
|
|
177 memset(s3mbuf,255,16*64*sizeof(S3MNOTE));
|
|
178
|
|
179 while(row<64)
|
|
180 { flag = _mm_read_UBYTE(modfp);
|
|
181
|
|
182 if(flag==EOF)
|
|
183 { _mm_errno = MMERR_LOADING_PATTERN;
|
|
184 return 0;
|
|
185 }
|
|
186
|
|
187 if(flag)
|
|
188 { ch = remap[flag&31];
|
|
189
|
|
190 if(ch != -1)
|
|
191 n = &s3mbuf[(64U*ch)+row];
|
|
192 else
|
|
193 n = &dummy;
|
|
194
|
|
195 if(flag&32)
|
|
196 { n->note = _mm_read_UBYTE(modfp);
|
|
197 n->ins = _mm_read_UBYTE(modfp);
|
|
198 }
|
|
199
|
|
200 if(flag&64)
|
|
201 n->vol = _mm_read_UBYTE(modfp);
|
|
202
|
|
203 if(flag&128)
|
|
204 { n->cmd = _mm_read_UBYTE(modfp);
|
|
205 n->inf = _mm_read_UBYTE(modfp);
|
|
206 }
|
|
207 } else row++;
|
|
208 }
|
|
209 return 1;
|
|
210 }
|
|
211
|
|
212
|
|
213 void S3MIT_ProcessCmd(UBYTE cmd, UBYTE inf, BOOL oldeffect);
|
|
214
|
|
215 UBYTE *S3M_ConvertTrack(S3MNOTE *tr)
|
|
216 {
|
|
217 int t;
|
|
218
|
|
219 UBYTE note,ins,vol;
|
|
220
|
|
221 UniReset();
|
|
222 for(t=0; t<64; t++)
|
|
223 {
|
|
224 note = tr[t].note;
|
|
225 ins = tr[t].ins;
|
|
226 vol = tr[t].vol;
|
|
227
|
|
228
|
|
229 if(ins!=0 && ins!=255) UniInstrument(ins-1);
|
|
230 if(note!=255)
|
|
231 { if(note==254) UniPTEffect(0xc,0); // <- note off command
|
|
232 else UniNote(((note>>4)*12)+(note&0xf)); // <- normal note
|
|
233 }
|
|
234
|
|
235 if(vol<255)
|
|
236 UniPTEffect(0xc,vol);
|
|
237
|
|
238 S3MIT_ProcessCmd(tr[t].cmd,tr[t].inf,1);
|
|
239 UniNewline();
|
|
240 }
|
|
241
|
|
242 return UniDup();
|
|
243 }
|
|
244
|
|
245
|
|
246 BOOL S3M_Load(void)
|
|
247 {
|
|
248 int t,u,track = 0;
|
|
249 SAMPLE *q;
|
|
250 UBYTE pan[32];
|
|
251
|
|
252 // try to read module header
|
|
253
|
|
254 _mm_read_string(mh->songname,28,modfp);
|
|
255 mh->t1a =_mm_read_UBYTE(modfp);
|
|
256 mh->type =_mm_read_UBYTE(modfp);
|
|
257 _mm_read_UBYTES(mh->unused1,2,modfp);
|
|
258 mh->ordnum =_mm_read_I_UWORD(modfp);
|
|
259 mh->insnum =_mm_read_I_UWORD(modfp);
|
|
260 mh->patnum =_mm_read_I_UWORD(modfp);
|
|
261 mh->flags =_mm_read_I_UWORD(modfp);
|
|
262 mh->tracker =_mm_read_I_UWORD(modfp);
|
|
263 mh->fileformat =_mm_read_I_UWORD(modfp);
|
|
264 _mm_read_string(mh->scrm,4,modfp);
|
|
265
|
|
266 mh->mastervol =_mm_read_UBYTE(modfp);
|
|
267 mh->initspeed =_mm_read_UBYTE(modfp);
|
|
268 mh->inittempo =_mm_read_UBYTE(modfp);
|
|
269 mh->mastermult =_mm_read_UBYTE(modfp);
|
|
270 mh->ultraclick =_mm_read_UBYTE(modfp);
|
|
271 mh->pantable =_mm_read_UBYTE(modfp);
|
|
272 _mm_read_UBYTES(mh->unused2,8,modfp);
|
|
273 mh->special =_mm_read_I_UWORD(modfp);
|
|
274 _mm_read_UBYTES(mh->channels,32,modfp);
|
|
275
|
|
276 if(feof(modfp))
|
|
277 { _mm_errno = MMERR_LOADING_HEADER;
|
|
278 return 0;
|
|
279 }
|
|
280
|
|
281 // set module variables
|
|
282
|
|
283 of.modtype = strdup(S3M_Version);
|
|
284 of.modtype[14] = ((mh->tracker >> 8) &0xf) + 0x30;
|
|
285 of.modtype[16] = ((mh->tracker >> 4)&0xf) + 0x30;
|
|
286 of.modtype[17] = ((mh->tracker)&0xf) + 0x30;
|
|
287 of.songname = DupStr(mh->songname,28);
|
|
288 of.numpat = mh->patnum;
|
|
289 of.reppos = 0;
|
|
290 of.numins = of.numsmp = mh->insnum;
|
|
291 of.initspeed = mh->initspeed;
|
|
292 of.inittempo = mh->inittempo;
|
|
293 of.initvolume = mh->mastervol<<1;
|
|
294
|
|
295 // read the order data
|
|
296 if(!AllocPositions(mh->ordnum)) return 0;
|
|
297 for(t=0; t<mh->ordnum; t++)
|
|
298 of.positions[t] = _mm_read_UBYTE(modfp);
|
|
299
|
|
300 of.numpos = 0;
|
|
301 for(t=0; t<mh->ordnum; t++)
|
|
302 { of.positions[of.numpos] = of.positions[t];
|
|
303 poslookup[t] = of.numpos; // bug fix for FREAKY S3Ms
|
|
304 if(of.positions[t]<254) of.numpos++;
|
|
305 }
|
|
306
|
|
307 if((paraptr=(UWORD *)_mm_malloc((of.numins+of.numpat)*sizeof(UWORD)))==NULL) return 0;
|
|
308
|
|
309 // read the instrument+pattern parapointers
|
|
310 _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modfp);
|
|
311
|
|
312
|
|
313 if(mh->pantable==252)
|
|
314 { // read the panning table (ST 3.2 addition. See below for further
|
|
315 // portions of channel panning [past reampper]).
|
|
316 _mm_read_UBYTES(pan,32,modfp);
|
|
317 }
|
|
318
|
|
319
|
|
320 // now is a good time to check if the header was too short :)
|
|
321
|
|
322 if(feof(modfp))
|
|
323 { _mm_errno = MMERR_LOADING_HEADER;
|
|
324 return 0;
|
|
325 }
|
|
326
|
|
327
|
|
328 // ==============================================
|
|
329 // Load those darned Samples! (no insts in ST3)
|
|
330
|
|
331 if(!AllocSamples()) return 0;
|
|
332
|
|
333 q = of.samples;
|
|
334
|
|
335 for(t=0; t<of.numins; t++)
|
|
336 { S3MSAMPLE s;
|
|
337
|
|
338 // seek to instrument position
|
|
339
|
|
340 _mm_fseek(modfp,((long)paraptr[t])<<4,SEEK_SET);
|
|
341
|
|
342 // and load sample info
|
|
343
|
|
344 s.type =_mm_read_UBYTE(modfp);
|
|
345 _mm_read_string(s.filename,12,modfp);
|
|
346 s.memsegh =_mm_read_UBYTE(modfp);
|
|
347 s.memsegl =_mm_read_I_UWORD(modfp);
|
|
348 s.length =_mm_read_I_ULONG(modfp);
|
|
349 s.loopbeg =_mm_read_I_ULONG(modfp);
|
|
350 s.loopend =_mm_read_I_ULONG(modfp);
|
|
351 s.volume =_mm_read_UBYTE(modfp);
|
|
352 s.dsk =_mm_read_UBYTE(modfp);
|
|
353 s.pack =_mm_read_UBYTE(modfp);
|
|
354 s.flags =_mm_read_UBYTE(modfp);
|
|
355 s.c2spd =_mm_read_I_ULONG(modfp);
|
|
356 _mm_read_UBYTES(s.unused,12,modfp);
|
|
357 _mm_read_string(s.sampname,28,modfp);
|
|
358 _mm_read_string(s.scrs,4,modfp);
|
|
359
|
|
360 if(feof(modfp))
|
|
361 { _mm_errno = MMERR_LOADING_SAMPLEINFO;
|
|
362 return 0;
|
|
363 }
|
|
364
|
|
365 q->samplename = DupStr(s.sampname,28);
|
|
366 q->speed = s.c2spd;
|
|
367 q->length = s.length;
|
|
368 q->loopstart = s.loopbeg;
|
|
369 q->loopend = s.loopend;
|
|
370 q->volume = s.volume;
|
|
371 q->seekpos = (((long)s.memsegh)<<16|s.memsegl)<<4;
|
|
372
|
|
373 if(s.flags&1) q->flags |= SF_LOOP;
|
|
374 if(s.flags&4) q->flags |= SF_16BITS;
|
|
375 if(mh->fileformat==1) q->flags |= SF_SIGNED;
|
|
376
|
|
377 // DON'T load sample if it doesn't have the SCRS tag
|
|
378 if(memcmp(s.scrs,"SCRS",4)!=0) q->length = 0;
|
|
379
|
|
380 q++;
|
|
381 }
|
|
382
|
|
383 // ====================================
|
|
384 // Determine the number of channels actually used. (what ever happened
|
|
385 // to the concept of a single "numchn" variable, eh?!
|
|
386
|
|
387 of.numchn = 0;
|
|
388 memset(remap,-1,32*sizeof(UBYTE));
|
|
389
|
|
390 for(t=0; t<of.numpat; t++)
|
|
391 { // seek to pattern position ( + 2 skip pattern length )
|
|
392 _mm_fseek(modfp,(long)((paraptr[of.numins+t])<<4)+2,SEEK_SET);
|
|
393 if(S3M_GetNumChannels()) return 0;
|
|
394 }
|
|
395
|
|
396 // build the remap array
|
|
397 for(t=0; t<32; t++)
|
|
398 { if(remap[t]==0)
|
|
399 { remap[t] = of.numchn;
|
|
400 of.numchn++;
|
|
401 }
|
|
402 }
|
|
403
|
|
404 // ============================================================
|
|
405 // set panning positions AFTER building remap chart!
|
|
406
|
|
407 for(t=0; t<32; t++)
|
|
408 { if((mh->channels[t]<16) && (remap[t]!=-1))
|
|
409 { if(mh->channels[t]<8)
|
|
410 of.panning[remap[t]] = 0x20; // 0x30 = std s3m val
|
|
411 else
|
|
412 of.panning[remap[t]] = 0xd0; // 0xc0 = std s3m val
|
|
413 }
|
|
414 }
|
|
415
|
|
416 if(mh->pantable==252)
|
|
417 { // set panning positions according to panning table (new for st3.2)
|
|
418 for(t=0; t<32; t++)
|
|
419 { if((pan[t]&0x20) && (mh->channels[t]<16) && (remap[t]!=-1))
|
|
420 of.panning[remap[t]] = (pan[t]&0xf)<<4;
|
|
421 }
|
|
422 }
|
|
423
|
|
424
|
|
425 // ==============================
|
|
426 // Load the pattern info now!
|
|
427
|
|
428 of.numtrk = of.numpat*of.numchn;
|
|
429 if(!AllocTracks()) return 0;
|
|
430 if(!AllocPatterns()) return 0;
|
|
431
|
|
432 for(t=0; t<of.numpat; t++)
|
|
433 { // seek to pattern position ( + 2 skip pattern length )
|
|
434 _mm_fseek(modfp,(((long)paraptr[of.numins+t])<<4)+2,SEEK_SET);
|
|
435 if(!S3M_ReadPattern()) return 0;
|
|
436 for(u=0; u<of.numchn; u++)
|
|
437 if(!(of.tracks[track++]=S3M_ConvertTrack(&s3mbuf[u*64]))) return 0;
|
|
438 }
|
|
439
|
|
440 return 1;
|
|
441 }
|
|
442
|
|
443
|
|
444 CHAR *S3M_LoadTitle(void)
|
|
445 {
|
|
446 CHAR s[28];
|
|
447
|
|
448 _mm_fseek(modfp,0,SEEK_SET);
|
|
449 if(!fread(s,28,1,modfp)) return NULL;
|
|
450
|
|
451 return(DupStr(s,28));
|
|
452 }
|
|
453
|
|
454
|
|
455 MLOADER load_s3m =
|
|
456 { NULL,
|
|
457 "S3M",
|
|
458 "S3M loader v0.3",
|
|
459 S3M_Init,
|
|
460 S3M_Test,
|
|
461 S3M_Load,
|
|
462 S3M_Cleanup,
|
|
463
|
|
464 S3M_LoadTitle
|
|
465 };
|
|
466
|