4
|
1 /*
|
|
2
|
|
3 Name: LOAD_MED.C
|
|
4
|
|
5 Description:
|
|
6 Amiga MED 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 #define MMD0_string 0x4D4D4430
|
|
21 #define MMD1_string 0x4D4D4431
|
|
22
|
|
23 typedef struct MMD0
|
|
24 { ULONG id;
|
|
25 ULONG modlen;
|
|
26 ULONG MMD0songP; // struct MMD0song *song;
|
|
27 UWORD psecnum; // for the player routine, MMD2 only
|
|
28 UWORD pseq; // " " " "
|
|
29 ULONG MMD0BlockPP; // struct MMD0Block **blockarr;
|
|
30 ULONG reserved1;
|
|
31 ULONG InstrHdrPP; // struct InstrHdr **smplarr;
|
|
32 ULONG reserved2;
|
|
33 ULONG MMD0expP; // struct MMD0exp *expdata;
|
|
34 ULONG reserved3;
|
|
35 UWORD pstate; // some data for the player routine
|
|
36 UWORD pblock;
|
|
37 UWORD pline;
|
|
38 UWORD pseqnum;
|
|
39 SWORD actplayline;
|
|
40 UBYTE counter;
|
|
41 UBYTE extra_songs; // number of songs - 1
|
|
42 } MMD0;
|
|
43
|
|
44
|
|
45 typedef struct MMD0sample
|
|
46 { UWORD rep,replen; // offs: 0(s), 2(s)
|
|
47 UBYTE midich; // offs: 4(s)
|
|
48 UBYTE midipreset; // offs: 5(s)
|
|
49 UBYTE svol; // offs: 6(s)
|
|
50 SBYTE strans; // offs: 7(s)
|
|
51 } MMD0sample;
|
|
52
|
|
53
|
|
54 typedef struct MMD0song
|
|
55 { MMD0sample sample[63]; // 63 * 8 bytes = 504 bytes
|
|
56 UWORD numblocks; // offs: 504
|
|
57 UWORD songlen; // offs: 506
|
|
58 UBYTE playseq[256]; // offs: 508
|
|
59 UWORD deftempo; // offs: 764
|
|
60 SBYTE playtransp; // offs: 766
|
|
61 UBYTE flags; // offs: 767
|
|
62 UBYTE flags2; // offs: 768
|
|
63 UBYTE tempo2; // offs: 769
|
|
64 UBYTE trkvol[16]; // offs: 770
|
|
65 UBYTE mastervol; // offs: 786
|
|
66 UBYTE numsamples; // offs: 787
|
|
67 } MMD0song;
|
|
68
|
|
69
|
|
70 typedef struct MMD0NOTE
|
|
71 { UBYTE a,b,c;
|
|
72 } MMD0NOTE;
|
|
73
|
|
74
|
|
75 typedef struct MMD1NOTE
|
|
76 { UBYTE a,b,c,d;
|
|
77 } MMD1NOTE;
|
|
78
|
|
79
|
|
80 typedef struct InstrHdr
|
|
81 { ULONG length;
|
|
82 SWORD type;
|
|
83 // Followed by actual data
|
|
84 } InstrHdr;
|
|
85
|
|
86
|
|
87 static MMD0 *mh = NULL;
|
|
88 static MMD0song *ms = NULL;
|
|
89 static ULONG *ba = NULL;
|
|
90 static MMD0NOTE *mmd0pat = NULL;
|
|
91 static MMD1NOTE *mmd1pat = NULL;
|
|
92
|
|
93 #define d0note(row,col) mmd0pat[(row*(UWORD)of.numchn)+col]
|
|
94 #define d1note(row,col) mmd1pat[(row*(UWORD)of.numchn)+col]
|
|
95
|
|
96
|
|
97 static CHAR MED_Version[] = "MED";
|
|
98
|
|
99
|
|
100 BOOL MED_Test(void)
|
|
101 {
|
|
102 UBYTE id[4];
|
|
103
|
|
104 if(!_mm_read_UBYTES(id,4,modfp)) return 0;
|
|
105 if(!memcmp(id,"MMD0",4)) return 1;
|
|
106 if(!memcmp(id,"MMD1",4)) return 1;
|
|
107 return 0;
|
|
108 }
|
|
109
|
|
110
|
|
111 BOOL MED_Init(void)
|
|
112 {
|
|
113 if(!(mh=(MMD0 *)_mm_calloc(1,sizeof(MMD0)))) return 0;
|
|
114 if(!(ms=(MMD0song *)_mm_calloc(1,sizeof(MMD0song)))) return 0;
|
|
115 return 1;
|
|
116 }
|
|
117
|
|
118
|
|
119 void MED_Cleanup(void)
|
|
120 {
|
|
121 if(mh!=NULL) free(mh);
|
|
122 if(ms!=NULL) free(ms);
|
|
123 if(ba!=NULL) free(ba);
|
|
124 if(mmd0pat!=NULL) free(mmd0pat);
|
|
125 if(mmd1pat!=NULL) free(mmd1pat);
|
|
126
|
|
127 mh = NULL;
|
|
128 ms = NULL;
|
|
129 ba = NULL; // blockarr
|
|
130 mmd0pat = NULL;
|
|
131 mmd1pat = NULL;
|
|
132 }
|
|
133
|
|
134
|
|
135 void EffectCvt(UBYTE eff,UBYTE dat)
|
|
136 {
|
|
137 switch(eff)
|
|
138 { // 0x0 0x1 0x2 0x3 0x4 // PT effects
|
|
139 case 0x5: // PT vibrato with speed/depth nibbles swapped
|
|
140 UniPTEffect(0x4,(dat>>4) | ((dat&0xf)<<4) );
|
|
141 break;
|
|
142
|
|
143 case 0x6: // not used
|
|
144 case 0x7: // not used
|
|
145 case 0x8: // midi hold/decay
|
|
146 break;
|
|
147
|
|
148 case 0x9:
|
|
149 if(dat<=0x20) UniPTEffect(0xf,dat);
|
|
150 break;
|
|
151
|
|
152 // 0xa 0xb 0xc all PT effects
|
|
153
|
|
154 case 0xd: // same as PT volslide
|
|
155 UniPTEffect(0xa,dat);
|
|
156 break;
|
|
157
|
|
158 case 0xe: // synth jmp - midi
|
|
159 break;
|
|
160
|
|
161 case 0xf:
|
|
162 // F00 does patternbreak with med
|
|
163 if(dat==0) UniPTEffect(0xd,0);
|
|
164 else if(dat<=0xa) UniPTEffect(0xf,dat);
|
|
165 else if(dat<0xf1) UniPTEffect(0xf,((UWORD)dat*125)/33);
|
|
166 else if(dat==0xff) UniPTEffect(0xc,0); // stop note
|
|
167 break;
|
|
168
|
|
169 default: // all normal PT effects are handled here :)
|
|
170 // Convert pattern jump from Dec to Hex
|
|
171 if(eff == 0xd)
|
|
172 dat = (((dat&0xf0)>>4)*10)+(dat&0xf);
|
|
173 UniPTEffect(eff,dat);
|
|
174 break;
|
|
175 }
|
|
176 }
|
|
177
|
|
178
|
|
179
|
|
180 UBYTE *MED_Convert1(int col)
|
|
181 {
|
|
182 int t;
|
|
183 UBYTE a,b,c,d,inst,note,eff,dat;
|
|
184 MMD1NOTE *n;
|
|
185
|
|
186 UniReset();
|
|
187 for(t=0; t<64; t++)
|
|
188 { n = &d1note(t,col);
|
|
189 a = n->a;
|
|
190 b = n->b;
|
|
191 c = n->c;
|
|
192 d = n->d;
|
|
193
|
|
194 note = a&0x7f;
|
|
195 inst = b&0x3f;
|
|
196 eff = c&0xf;
|
|
197 dat = d;
|
|
198
|
|
199 if(inst!=0) UniInstrument(inst-1);
|
|
200 if(note!=0) UniNote(note+23);
|
|
201
|
|
202 EffectCvt(eff,dat);
|
|
203 UniNewline();
|
|
204 }
|
|
205
|
|
206 return UniDup();
|
|
207 }
|
|
208
|
|
209
|
|
210 UBYTE *MED_Convert0(int col)
|
|
211 {
|
|
212 int t;
|
|
213 UBYTE a,b,c,inst,note,eff,dat;
|
|
214 MMD0NOTE *n;
|
|
215
|
|
216 UniReset();
|
|
217 for(t=0;t<64;t++)
|
|
218 { n = &d0note(t,col);
|
|
219 a = n->a;
|
|
220 b = n->b;
|
|
221 c = n->c;
|
|
222
|
|
223 note = a & 0x3f;
|
|
224 a >>= 6;
|
|
225 a = ((a & 1) << 1) | (a >> 1);
|
|
226
|
|
227 inst = (b >> 4) | (a << 4);
|
|
228 eff = b & 0xf;
|
|
229 dat = c;
|
|
230
|
|
231 if(inst!=0) UniInstrument(inst-1);
|
|
232 if(note!=0) UniNote(note+35);
|
|
233
|
|
234 EffectCvt(eff,dat);
|
|
235 UniNewline();
|
|
236 }
|
|
237 return UniDup();
|
|
238 }
|
|
239
|
|
240
|
|
241 BOOL LoadMMD0Patterns(void)
|
|
242 {
|
|
243 int t,row,col;
|
|
244 UWORD numtracks,numlines,maxlines=0,track=0;
|
|
245 MMD0NOTE *mmdp;
|
|
246
|
|
247 // first, scan patterns to see how many channels are used
|
|
248 for(t=0; t<of.numpat; t++)
|
|
249 { _mm_fseek(modfp,ba[t],SEEK_SET);
|
|
250 numtracks = _mm_read_UBYTE(modfp);
|
|
251 numlines = _mm_read_UBYTE(modfp);
|
|
252
|
|
253 if(numtracks>of.numchn) of.numchn = numtracks;
|
|
254 if(numlines>maxlines) maxlines = numlines;
|
|
255 }
|
|
256
|
|
257 of.numtrk = of.numpat*of.numchn;
|
|
258 if(!AllocTracks()) return 0;
|
|
259 if(!AllocPatterns()) return 0;
|
|
260
|
|
261 if(!(mmd0pat=(MMD0NOTE*)_mm_calloc(of.numchn*(maxlines+1),sizeof(MMD0NOTE)))) return 0;
|
|
262
|
|
263 // second read: no more mr. nice guy,
|
|
264 // really read and convert patterns
|
|
265
|
|
266 for(t=0; t<of.numpat; t++)
|
|
267 { _mm_fseek(modfp,ba[t],SEEK_SET);
|
|
268 numtracks = _mm_read_UBYTE(modfp);
|
|
269 numlines = _mm_read_UBYTE(modfp);
|
|
270
|
|
271 of.pattrows[t] = numlines+1;
|
|
272 memset(mmdp=mmd0pat,0,of.numchn*maxlines*sizeof(MMD0NOTE));
|
|
273 for(row=numlines+1; row; row--)
|
|
274 { for(col=numtracks; col; col--,mmdp++)
|
|
275 { mmdp->a = _mm_read_UBYTE(modfp);
|
|
276 mmdp->b = _mm_read_UBYTE(modfp);
|
|
277 mmdp->c = _mm_read_UBYTE(modfp);
|
|
278 }
|
|
279 }
|
|
280
|
|
281 for(col=0; col<of.numchn; col++)
|
|
282 { of.tracks[track] = MED_Convert0(col);
|
|
283 track++;
|
|
284 }
|
|
285 }
|
|
286 return 1;
|
|
287 }
|
|
288
|
|
289
|
|
290 BOOL LoadMMD1Patterns(void)
|
|
291 {
|
|
292 int t,row,col;
|
|
293 UWORD numtracks,numlines,maxlines=0,track=0;
|
|
294 MMD1NOTE *mmdp;
|
|
295
|
|
296 // first, scan patterns to see how many channels are used
|
|
297 for(t=0; t<of.numpat; t++)
|
|
298 { _mm_fseek(modfp,ba[t],SEEK_SET);
|
|
299 numtracks = _mm_read_M_UWORD(modfp);
|
|
300 numlines = _mm_read_M_UWORD(modfp);
|
|
301
|
|
302 _mm_fseek(modfp,sizeof(ULONG),SEEK_CUR);
|
|
303 if(numtracks>of.numchn) of.numchn = numtracks;
|
|
304 if(numlines>maxlines) maxlines = numlines;
|
|
305 }
|
|
306
|
|
307 of.numtrk = of.numpat*of.numchn;
|
|
308 if(!AllocTracks()) return 0;
|
|
309 if(!AllocPatterns()) return 0;
|
|
310
|
|
311 if(!(mmd1pat=(MMD1NOTE*)_mm_calloc(of.numchn*(maxlines+1),sizeof(MMD1NOTE)))) return 0;
|
|
312
|
|
313 // second read: no more mr. nice guy, really read and convert patterns
|
|
314 for(t=0; t<of.numpat; t++)
|
|
315 { _mm_fseek(modfp,ba[t],SEEK_SET);
|
|
316 numtracks = _mm_read_M_UWORD(modfp);
|
|
317 numlines = _mm_read_M_UWORD(modfp);
|
|
318
|
|
319 _mm_fseek(modfp,sizeof(ULONG),SEEK_CUR);
|
|
320 of.pattrows[t] = numlines;
|
|
321 memset(mmdp=mmd1pat,0,of.numchn*maxlines*sizeof(MMD1NOTE));
|
|
322
|
|
323 for(row=numlines+1; row; row--)
|
|
324 { for(col=numtracks; col; col--,mmdp++)
|
|
325 { mmdp->a = _mm_read_UBYTE(modfp);
|
|
326 mmdp->b = _mm_read_UBYTE(modfp);
|
|
327 mmdp->c = _mm_read_UBYTE(modfp);
|
|
328 mmdp->d = _mm_read_UBYTE(modfp);
|
|
329 }
|
|
330 }
|
|
331
|
|
332 for(col=0;col<of.numchn;col++)
|
|
333 { of.tracks[track]=MED_Convert1(col);
|
|
334 track++;
|
|
335 }
|
|
336 }
|
|
337 return 1;
|
|
338 }
|
|
339
|
|
340
|
|
341
|
|
342 BOOL MED_Load(void)
|
|
343 {
|
|
344 int t;
|
|
345 ULONG sa[64];
|
|
346 InstrHdr s;
|
|
347 SAMPLE *q;
|
|
348 MMD0sample *mss;
|
|
349
|
|
350 // try to read module header
|
|
351
|
|
352 mh->id = _mm_read_M_ULONG(modfp);
|
|
353 mh->modlen = _mm_read_M_ULONG(modfp);
|
|
354 mh->MMD0songP = _mm_read_M_ULONG(modfp);
|
|
355 mh->psecnum = _mm_read_M_UWORD(modfp);
|
|
356 mh->pseq = _mm_read_M_UWORD(modfp);
|
|
357 mh->MMD0BlockPP = _mm_read_M_ULONG(modfp);
|
|
358 mh->reserved1 = _mm_read_M_ULONG(modfp);
|
|
359 mh->InstrHdrPP = _mm_read_M_ULONG(modfp);
|
|
360 mh->reserved2 = _mm_read_M_ULONG(modfp);
|
|
361 mh->MMD0expP = _mm_read_M_ULONG(modfp);
|
|
362 mh->reserved3 = _mm_read_M_ULONG(modfp);
|
|
363 mh->pstate = _mm_read_M_UWORD(modfp);
|
|
364 mh->pblock = _mm_read_M_UWORD(modfp);
|
|
365 mh->pline = _mm_read_M_UWORD(modfp);
|
|
366 mh->pseqnum = _mm_read_M_UWORD(modfp);
|
|
367 mh->actplayline = _mm_read_M_SWORD(modfp);
|
|
368 mh->counter = _mm_read_UBYTE(modfp);
|
|
369 mh->extra_songs = _mm_read_UBYTE(modfp);
|
|
370
|
|
371 // Seek to MMD0song struct
|
|
372 _mm_fseek(modfp,mh->MMD0songP,SEEK_SET);
|
|
373
|
|
374
|
|
375 // Load the MMD0 Song Header
|
|
376
|
|
377 mss = ms->sample; // load the sample data first
|
|
378 for(t=63; t; t--, mss++)
|
|
379 { mss->rep = _mm_read_M_UWORD(modfp);
|
|
380 mss->replen = _mm_read_M_UWORD(modfp);
|
|
381 mss->midich = _mm_read_UBYTE(modfp);
|
|
382 mss->midipreset = _mm_read_UBYTE(modfp);
|
|
383 mss->svol = _mm_read_UBYTE(modfp);
|
|
384 mss->strans = _mm_read_SBYTE(modfp);
|
|
385 }
|
|
386
|
|
387 ms->numblocks = _mm_read_M_UWORD(modfp);
|
|
388 ms->songlen = _mm_read_M_UWORD(modfp);
|
|
389 _mm_read_UBYTES(ms->playseq,256,modfp);
|
|
390 ms->deftempo = _mm_read_M_UWORD(modfp);
|
|
391 ms->playtransp = _mm_read_SBYTE(modfp);
|
|
392 ms->flags = _mm_read_UBYTE(modfp);
|
|
393 ms->flags2 = _mm_read_UBYTE(modfp);
|
|
394 ms->tempo2 = _mm_read_UBYTE(modfp);
|
|
395 _mm_read_UBYTES(ms->trkvol,16,modfp);
|
|
396 ms->mastervol = _mm_read_UBYTE(modfp);
|
|
397 ms->numsamples = _mm_read_UBYTE(modfp);
|
|
398
|
|
399 // check for a bad header
|
|
400 if(feof(modfp))
|
|
401 { _mm_errno = MMERR_LOADING_HEADER;
|
|
402 return 0;
|
|
403 }
|
|
404
|
|
405 // seek to and read the samplepointer array
|
|
406 _mm_fseek(modfp,mh->InstrHdrPP,SEEK_SET);
|
|
407 if(!_mm_read_M_ULONGS(sa,ms->numsamples,modfp))
|
|
408 { _mm_errno = MMERR_LOADING_HEADER;
|
|
409 return 0;
|
|
410 }
|
|
411
|
|
412 // alloc and read the blockpointer array
|
|
413 if(!(ba=(ULONG *)_mm_calloc(ms->numblocks, sizeof(ULONG)))) return 0;
|
|
414 _mm_fseek(modfp,mh->MMD0BlockPP,SEEK_SET);
|
|
415 if(!_mm_read_M_ULONGS(ba,ms->numblocks,modfp))
|
|
416 { _mm_errno = MMERR_LOADING_HEADER;
|
|
417 return 0;
|
|
418 }
|
|
419
|
|
420
|
|
421 // copy song positions
|
|
422 if(!AllocPositions(ms->songlen)) return 0;
|
|
423 for(t=0; t<ms->songlen; t++)
|
|
424 of.positions[t] = ms->playseq[t];
|
|
425
|
|
426 of.initspeed = 6;
|
|
427 of.inittempo = ((UWORD)ms->deftempo*125)/33;
|
|
428 of.modtype = strdup(MED_Version);
|
|
429 of.numchn = 0; // will be counted later
|
|
430 of.numpat = ms->numblocks;
|
|
431 of.numpos = ms->songlen;
|
|
432 of.numins = ms->numsamples;
|
|
433
|
|
434 of.numsmp = of.numins;
|
|
435 if(!AllocSamples()) return 0;
|
|
436 q = of.samples;
|
|
437
|
|
438 for(t=0; t<of.numins; t++)
|
|
439 { _mm_fseek(modfp,sa[t],SEEK_SET);
|
|
440 s.length = _mm_read_M_ULONG(modfp);
|
|
441 s.type = _mm_read_M_SWORD(modfp);
|
|
442
|
|
443 if(feof(modfp))
|
|
444 { _mm_errno = MMERR_LOADING_SAMPLEINFO;
|
|
445 return 0;
|
|
446 }
|
|
447
|
|
448 q->samplename = NULL;
|
|
449 q->length = s.length;
|
|
450 q->seekpos = _mm_ftell(modfp);
|
|
451 q->loopstart = ms->sample[t].rep<<1;
|
|
452 q->loopend = q->loopstart+(ms->sample[t].replen<<1);
|
|
453 q->flags = SF_SIGNED;
|
|
454 q->speed = 8363;
|
|
455 q->volume = 64;
|
|
456
|
|
457 if(ms->sample[t].replen>1) q->flags|=SF_LOOP;
|
|
458
|
|
459 // don't load sample if length>='MMD0' hah.. hah.. very funny.. NOT!
|
|
460 if(q->length >= MMD0_string) q->length = 0;
|
|
461
|
|
462 q++;
|
|
463 }
|
|
464
|
|
465
|
|
466 if(mh->id==MMD0_string)
|
|
467 { if(!LoadMMD0Patterns()) return 0;
|
|
468 } else if(mh->id==MMD1_string)
|
|
469 { if(!LoadMMD1Patterns()) return 0;
|
|
470 } else
|
|
471 { _mm_errno = MMERR_NOT_A_MODULE;
|
|
472 return 0;
|
|
473 }
|
|
474
|
|
475 return 1;
|
|
476 }
|
|
477
|
|
478
|
|
479 MLOADER load_med =
|
|
480 { NULL,
|
|
481 "MED",
|
|
482 "MED loader v0.1",
|
|
483 MED_Init,
|
|
484 MED_Test,
|
|
485 MED_Load,
|
|
486 MED_Cleanup,
|
|
487 NULL
|
|
488 };
|
|
489
|
|
490
|