comparison playercode/load_s3m.c @ 4:5d614bcc4287

Initial entry of mikmod into the CVS tree.
author darius
date Fri, 23 Jan 1998 16:05:08 +0000
parents
children
comparison
equal deleted inserted replaced
3:71e20a32bd84 4:5d614bcc4287
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