4
|
1 /*
|
|
2
|
|
3 Name: LOAD_M15.C
|
|
4
|
|
5 Description:
|
|
6 15 instrument MOD loader
|
|
7 Also supports Ultimate Sound Tracker (old M15 format)
|
|
8
|
|
9 Portability:
|
|
10 All systems - all compilers (hopefully)
|
|
11
|
|
12 If this module is found to not be portable to any particular platform,
|
|
13 please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
|
|
14 more information on contacting the author).
|
|
15
|
|
16 */
|
|
17
|
|
18 #include <string.h>
|
|
19 #include "mikmod.h"
|
|
20
|
|
21 /*************************************************************************
|
|
22 *************************************************************************/
|
|
23
|
|
24
|
|
25 typedef struct MSAMPINFO // sample header as it appears in a module
|
|
26 { CHAR samplename[22];
|
|
27 UWORD length;
|
|
28 UBYTE finetune;
|
|
29 UBYTE volume;
|
|
30 UWORD reppos;
|
|
31 UWORD replen;
|
|
32 } MSAMPINFO;
|
|
33
|
|
34
|
|
35 typedef struct MODULEHEADER // verbatim module header
|
|
36 { CHAR songname[20]; // the songname..
|
|
37 MSAMPINFO samples[15]; // all sampleinfo
|
|
38 UBYTE songlength; // number of patterns used
|
|
39 UBYTE magic1; // should be 127
|
|
40 UBYTE positions[128]; // which pattern to play at pos
|
|
41 } MODULEHEADER;
|
|
42
|
|
43
|
|
44 typedef struct MODNOTE
|
|
45 { UBYTE a,b,c,d;
|
|
46 } MODNOTE;
|
|
47
|
|
48
|
|
49 /*************************************************************************
|
|
50 *************************************************************************/
|
|
51
|
|
52 static MODULEHEADER *mh = NULL; // raw as-is module header
|
|
53 static MODNOTE *patbuf = NULL;
|
|
54 static BOOL ust_loader = 0; // if TRUE, load as a ust module.
|
|
55 static CHAR nulls[3] = {0,0,0};
|
|
56
|
|
57 static BOOL LoadModuleHeader(MODULEHEADER *mh)
|
|
58 {
|
|
59 int t;
|
|
60
|
|
61 _mm_read_string(mh->songname,20,modfp);
|
|
62
|
|
63 for(t=0; t<15; t++)
|
|
64 { MSAMPINFO *s = &mh->samples[t];
|
|
65 _mm_read_string(s->samplename,22,modfp);
|
|
66 s->length =_mm_read_M_UWORD(modfp);
|
|
67 s->finetune =_mm_read_UBYTE(modfp);
|
|
68 s->volume =_mm_read_UBYTE(modfp);
|
|
69 s->reppos =_mm_read_M_UWORD(modfp);
|
|
70 s->replen =_mm_read_M_UWORD(modfp);
|
|
71 }
|
|
72
|
|
73 mh->songlength =_mm_read_UBYTE(modfp);
|
|
74 mh->magic1 =_mm_read_UBYTE(modfp); // should be 127
|
|
75 _mm_read_UBYTES(mh->positions,128,modfp);
|
|
76
|
|
77 return(!feof(modfp));
|
|
78 }
|
|
79
|
|
80
|
|
81 static int CheckPatternType(int numpat)
|
|
82
|
|
83 // Checks the patterns in the modfile for UST / 15-inst indications.
|
|
84 // For example, if an effect 3xx is found, it is assumed that the song
|
|
85 // is 15-inst. If a 1xx effect has dat greater than 0x20, it is UST.
|
|
86 // Returns: 0 indecisive; 1 = UST; 2 = 15-inst
|
|
87
|
|
88 {
|
|
89 int t;
|
|
90 UBYTE eff, dat;
|
|
91
|
|
92 ust_loader = 1;
|
|
93
|
|
94 for(t=0; t<numpat*(64U*4); t++)
|
|
95 { // Load the pattern into the temp buffer
|
|
96 // and convert it
|
|
97
|
|
98 _mm_read_UBYTE(modfp); // read note
|
|
99 _mm_read_UBYTE(modfp); // read inst
|
|
100 eff = _mm_read_UBYTE(modfp);
|
|
101 dat = _mm_read_UBYTE(modfp);
|
|
102
|
|
103 if((eff==3) && (dat!=0) || (eff >= 2)) return 2;
|
|
104 if(eff==1)
|
|
105 { if(dat > 0x1f) return 1;
|
|
106 if(dat < 0x3) return 2;
|
|
107 }
|
|
108 if((eff==2) && (dat > 0x1f)) return 1;
|
|
109 }
|
|
110
|
|
111 return 0;
|
|
112 }
|
|
113
|
|
114
|
|
115 BOOL M15_Test(void)
|
|
116 {
|
|
117 int t, numpat;
|
|
118 MODULEHEADER mh;
|
|
119
|
|
120 ust_loader = 0;
|
|
121
|
|
122 if(!LoadModuleHeader(&mh)) return 0;
|
|
123 if(mh.magic1>127) return 0;
|
|
124
|
|
125 for(t=0; t<15; t++)
|
|
126 { // all finetunes should be zero
|
|
127 if(mh.samples[t].finetune != 0) return 0;
|
|
128
|
|
129 // all volumes should be <= 64
|
|
130 if(mh.samples[t].volume > 64) return 0;
|
|
131
|
|
132 // all instrument names should begin with s, st-, or a number
|
|
133 if(mh.samples[t].samplename[0] == 's')
|
|
134 { if((memcmp(mh.samples[t].samplename,"st-",3) != 0) &&
|
|
135 (memcmp(mh.samples[t].samplename,"ST-",3) != 0) &&
|
|
136 (memcmp(mh.samples[t].samplename,nulls,3) != 0))
|
|
137 ust_loader = 1;
|
|
138 } else if((mh.samples[t].samplename[0] < '0') || (mh.samples[t].samplename[0] > '9'))
|
|
139 ust_loader = 1;
|
|
140
|
|
141 if(mh.samples[t].length > 4999)
|
|
142 { ust_loader = 0;
|
|
143 if(mh.samples[t].length > 32768) return 0;
|
|
144 }
|
|
145
|
|
146 if(!ust_loader) return 1;
|
|
147
|
|
148 if(((mh.samples[t].reppos) + mh.samples[t].replen) > (mh.samples[t].length + 10))
|
|
149 { ust_loader = 1;
|
|
150 return 1;
|
|
151 }
|
|
152
|
|
153 }
|
|
154
|
|
155 for(numpat=0, t=0; t<mh.songlength; t++)
|
|
156 { if(mh.positions[t] > numpat)
|
|
157 numpat = mh.positions[t];
|
|
158 }
|
|
159
|
|
160 numpat++;
|
|
161 switch(CheckPatternType(numpat))
|
|
162 { case 0: // indecisive, so check more clues...
|
|
163
|
|
164 break;
|
|
165
|
|
166 case 1: ust_loader = 1; break;
|
|
167 case 2: ust_loader = 0; break;
|
|
168 }
|
|
169
|
|
170 return 1;
|
|
171 }
|
|
172
|
|
173
|
|
174 BOOL M15_Init(void)
|
|
175 {
|
|
176 if(!(mh=(MODULEHEADER *)_mm_calloc(1,sizeof(MODULEHEADER)))) return 0;
|
|
177 return 1;
|
|
178 }
|
|
179
|
|
180
|
|
181 void M15_Cleanup(void)
|
|
182 {
|
|
183 if(mh!=NULL) free(mh);
|
|
184 if(patbuf!=NULL) free(patbuf);
|
|
185
|
|
186 mh = NULL;
|
|
187 patbuf = NULL;
|
|
188 }
|
|
189
|
|
190
|
|
191 /*
|
|
192 Old (amiga) noteinfo:
|
|
193
|
|
194 _____byte 1_____ byte2_ _____byte 3_____ byte4_
|
|
195 / \ / \ / \ / \
|
|
196 0000 0000-00000000 0000 0000-00000000
|
|
197
|
|
198 Upper four 12 bits for Lower four Effect command.
|
|
199 bits of sam- note period. bits of sam-
|
|
200 ple number. ple number.
|
|
201
|
|
202 */
|
|
203
|
|
204
|
|
205 static void M15_ConvertNote(MODNOTE *n)
|
|
206 {
|
|
207 UBYTE instrument,effect,effdat,note;
|
|
208 UWORD period;
|
|
209
|
|
210 // extract the various information from the 4 bytes that
|
|
211 // make up a single note
|
|
212
|
|
213 instrument = (n->a&0x10)|(n->c>>4);
|
|
214 period = (((UWORD)n->a&0xf)<<8)+n->b;
|
|
215 effect = n->c&0xf;
|
|
216 effdat = n->d;
|
|
217
|
|
218 // Convert the period to a note number
|
|
219
|
|
220 note=0;
|
|
221 if(period != 0)
|
|
222 { for(note=0; note<60; note++)
|
|
223 if(period >= npertab[note]) break;
|
|
224 note++;
|
|
225 if(note==61) note = 0;
|
|
226 }
|
|
227
|
|
228 if(instrument!=0) UniInstrument(instrument-1);
|
|
229 if(note!=0) UniNote(note+23);
|
|
230
|
|
231 // Convert pattern jump from Dec to Hex
|
|
232 if(effect == 0xd)
|
|
233 effdat = (((effdat&0xf0)>>4)*10)+(effdat&0xf);
|
|
234
|
|
235 if(ust_loader)
|
|
236 { switch(effect)
|
|
237 { case 0: break;
|
|
238 case 1:
|
|
239 UniPTEffect(0,effdat);
|
|
240 break;
|
|
241
|
|
242 case 2:
|
|
243 if(effdat&0xf) UniPTEffect(1,effdat&0xf);
|
|
244 if(effdat>>2) UniPTEffect(2,effdat>>2);
|
|
245 break;
|
|
246
|
|
247 case 3: break;
|
|
248
|
|
249 default:
|
|
250 UniPTEffect(effect,effdat);
|
|
251 break;
|
|
252 }
|
|
253 } else UniPTEffect(effect,effdat);
|
|
254 }
|
|
255
|
|
256
|
|
257 static UBYTE *M15_ConvertTrack(MODNOTE *n)
|
|
258 {
|
|
259 int t;
|
|
260
|
|
261 UniReset();
|
|
262 for(t=0; t<64; t++)
|
|
263 { M15_ConvertNote(n);
|
|
264 UniNewline();
|
|
265 n += 4;
|
|
266 }
|
|
267 return UniDup();
|
|
268 }
|
|
269
|
|
270
|
|
271
|
|
272 static BOOL M15_LoadPatterns(void)
|
|
273 // Loads all patterns of a modfile and converts them into the
|
|
274 // 3 byte format.
|
|
275 {
|
|
276 int t,s,tracks=0;
|
|
277
|
|
278 if(!AllocPatterns()) return 0;
|
|
279 if(!AllocTracks()) return 0;
|
|
280
|
|
281 // Allocate temporary buffer for loading
|
|
282 // and converting the patterns
|
|
283
|
|
284 if(!(patbuf=(MODNOTE *)_mm_calloc(64U*4,sizeof(MODNOTE)))) return 0;
|
|
285
|
|
286 for(t=0; t<of.numpat; t++)
|
|
287 { // Load the pattern into the temp buffer
|
|
288 // and convert it
|
|
289
|
|
290 for(s=0; s<(64U*4); s++)
|
|
291 { patbuf[s].a=_mm_read_UBYTE(modfp);
|
|
292 patbuf[s].b=_mm_read_UBYTE(modfp);
|
|
293 patbuf[s].c=_mm_read_UBYTE(modfp);
|
|
294 patbuf[s].d=_mm_read_UBYTE(modfp);
|
|
295 }
|
|
296
|
|
297 for(s=0; s<4; s++)
|
|
298 if(!(of.tracks[tracks++]=M15_ConvertTrack(patbuf+s))) return 0;
|
|
299 }
|
|
300
|
|
301 return 1;
|
|
302 }
|
|
303
|
|
304
|
|
305 BOOL M15_Load(void)
|
|
306 {
|
|
307 int t;
|
|
308 SAMPLE *q;
|
|
309 MSAMPINFO *s; // old module sampleinfo
|
|
310
|
|
311 // try to read module header
|
|
312
|
|
313 if(!LoadModuleHeader(mh))
|
|
314 { _mm_errno = MMERR_LOADING_HEADER;
|
|
315 return 0;
|
|
316 }
|
|
317
|
|
318
|
|
319 if(ust_loader)
|
|
320 of.modtype = strdup("Ultimate Soundtracker");
|
|
321 else
|
|
322 of.modtype = strdup("Soundtracker");
|
|
323
|
|
324 // set module variables
|
|
325
|
|
326 of.initspeed = 6;
|
|
327 of.inittempo = 125;
|
|
328 of.numchn = 4; // get number of channels
|
|
329 of.songname = DupStr(mh->songname,20); // make a cstr of songname
|
|
330 of.numpos = mh->songlength; // copy the songlength
|
|
331
|
|
332 if(!AllocPositions(of.numpos)) return 0;
|
|
333 for(t=0; t<of.numpos; t++)
|
|
334 of.positions[t] = mh->positions[t];
|
|
335
|
|
336
|
|
337 // Count the number of patterns
|
|
338
|
|
339 of.numpat = 0;
|
|
340
|
|
341 for(t=0; t<of.numpos; t++)
|
|
342 { if(of.positions[t] > of.numpat)
|
|
343 of.numpat = of.positions[t];
|
|
344 }
|
|
345 of.numpat++;
|
|
346 of.numtrk = of.numpat*4;
|
|
347
|
|
348 // Finally, init the sampleinfo structures
|
|
349
|
|
350 of.numins = of.numsmp = 15;
|
|
351 if(!AllocSamples()) return 0;
|
|
352
|
|
353 s = mh->samples; // init source pointer
|
|
354 q = of.samples;
|
|
355
|
|
356 for(t=0; t<of.numins; t++)
|
|
357 { // convert the samplename
|
|
358 q->samplename = DupStr(s->samplename,22);
|
|
359
|
|
360 // init the sampleinfo variables and
|
|
361 // convert the size pointers to longword format
|
|
362
|
|
363 q->speed = finetune[s->finetune&0xf];
|
|
364 q->volume = s->volume;
|
|
365 if(ust_loader)
|
|
366 q->loopstart = s->reppos;
|
|
367 else
|
|
368 q->loopstart = s->reppos<<1;
|
|
369 q->loopend = q->loopstart+(s->replen<<1);
|
|
370 q->length = s->length<<1;
|
|
371
|
|
372 q->flags = SF_SIGNED | SF_UST_LOOP;
|
|
373 if(s->replen>1) q->flags |= SF_LOOP;
|
|
374
|
|
375 // fix replen if repend>length
|
|
376
|
|
377 if(q->loopend>q->length) q->loopend = q->length;
|
|
378
|
|
379 s++; // point to next source sampleinfo
|
|
380 q++;
|
|
381 }
|
|
382
|
|
383 if(!M15_LoadPatterns()) return 0;
|
|
384
|
|
385 ust_loader = 0;
|
|
386 return 1;
|
|
387 }
|
|
388
|
|
389
|
|
390 CHAR *M15_LoadTitle(void)
|
|
391 {
|
|
392 CHAR s[20];
|
|
393
|
|
394 fseek(modfp,0,SEEK_SET);
|
|
395 if(!fread(s,22,1,modfp)) return NULL;
|
|
396
|
|
397 return(DupStr(s,20));
|
|
398 }
|
|
399
|
|
400
|
|
401 MLOADER load_m15 =
|
|
402 { NULL,
|
|
403 "15-instrument module",
|
|
404 "Portable MOD-15 loader v0.1",
|
|
405 M15_Init,
|
|
406 M15_Test,
|
|
407 M15_Load,
|
|
408 M15_Cleanup,
|
|
409 M15_LoadTitle
|
|
410 };
|