Mercurial > ~darius > hgwebdir.cgi > mikmod
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 |