4
|
1 /*
|
|
2
|
|
3 Name: MLOADER.C
|
|
4
|
|
5 Description:
|
|
6 These routines are used to access the available module loaders
|
|
7
|
|
8 Portability:
|
|
9 All systems - all compilers
|
|
10
|
|
11 */
|
|
12
|
|
13 #include <string.h>
|
|
14 #include "mikmod.h"
|
|
15
|
|
16
|
|
17 FILE *modfp;
|
|
18 UNIMOD of;
|
|
19
|
|
20 static MLOADER *firstloader = NULL;
|
|
21
|
|
22 UWORD finetune[16] =
|
|
23 { 8363, 8413, 8463, 8529, 8581, 8651, 8723, 8757,
|
|
24 7895, 7941, 7985, 8046, 8107, 8169, 8232, 8280
|
|
25 };
|
|
26
|
|
27
|
|
28 void ML_InfoLoader(void)
|
|
29 {
|
|
30 int t;
|
|
31 MLOADER *l;
|
|
32
|
|
33 // list all registered devicedrivers:
|
|
34
|
|
35 for(t=1,l=firstloader; l!=NULL; l=l->next, t++)
|
|
36 printf("%d. %s\n",t,l->version);
|
|
37 }
|
|
38
|
|
39
|
|
40 void ML_RegisterLoader(MLOADER *ldr)
|
|
41 {
|
|
42 MLOADER *cruise = firstloader;
|
|
43
|
|
44 if(cruise!=NULL)
|
|
45 { while(cruise->next!=NULL) cruise = cruise->next;
|
|
46 cruise->next = ldr;
|
|
47 } else
|
|
48 firstloader = ldr;
|
|
49 }
|
|
50
|
|
51
|
|
52 BOOL ReadComment(UWORD len)
|
|
53 {
|
|
54 //int t;
|
|
55
|
|
56 if(len)
|
|
57 { if(!(of.comment=(CHAR *)_mm_malloc(len+1))) return 0;
|
|
58 fread(of.comment,len,1,modfp);
|
|
59 of.comment[len] = 0;
|
|
60 }
|
|
61 return 1;
|
|
62 }
|
|
63
|
|
64
|
|
65 BOOL AllocPositions(int total)
|
|
66 {
|
|
67 if((of.positions = _mm_calloc(total,sizeof(UWORD))) == NULL) return 0;
|
|
68 return 1;
|
|
69 }
|
|
70
|
|
71
|
|
72 BOOL AllocPatterns(void)
|
|
73 {
|
|
74 int s,t,tracks = 0;
|
|
75
|
|
76 // Allocate track sequencing array
|
|
77
|
|
78 if(!(of.patterns = (UWORD *)_mm_calloc((ULONG)(of.numpat+1)*of.numchn,sizeof(UWORD)))) return 0;
|
|
79 if(!(of.pattrows = (UWORD *)_mm_calloc(of.numpat+1,sizeof(UWORD)))) return 0;
|
|
80
|
|
81 for(t=0; t<of.numpat+1; t++)
|
|
82 { of.pattrows[t] = 64;
|
|
83 for(s=0; s<of.numchn; s++)
|
|
84 of.patterns[(t*of.numchn)+s] = tracks++;
|
|
85 }
|
|
86
|
|
87 return 1;
|
|
88 }
|
|
89
|
|
90
|
|
91 BOOL AllocTracks(void)
|
|
92 {
|
|
93 if(!(of.tracks=(UBYTE **)_mm_calloc(of.numtrk,sizeof(UBYTE *)))) return 0;
|
|
94 return 1;
|
|
95 }
|
|
96
|
|
97
|
|
98 BOOL AllocInstruments(void)
|
|
99 {
|
|
100 int t,n;
|
|
101
|
|
102 if((of.instruments=(INSTRUMENT *)_mm_calloc(of.numins,sizeof(INSTRUMENT)))==NULL) return 0;
|
|
103
|
|
104 for(t=0; t<of.numins; t++)
|
|
105 { for(n=0; n<120; n++) // Init note / sample lookup table
|
|
106 { of.instruments[t].samplenote[n] = n;
|
|
107 of.instruments[t].samplenumber[n] = t;
|
|
108 }
|
|
109 of.instruments[t].globvol = 64;
|
|
110 }
|
|
111 return 1;
|
|
112 }
|
|
113
|
|
114
|
|
115 BOOL AllocSamples(void)
|
|
116 {
|
|
117 UWORD u;
|
|
118
|
|
119 if((of.samples = (SAMPLE *)_mm_calloc(of.numsmp,sizeof(SAMPLE)))==NULL) return 0;
|
|
120
|
|
121 for(u=0; u<of.numsmp; u++)
|
|
122 { of.samples[u].panning = 128;
|
|
123 of.samples[u].handle = -1;
|
|
124 of.samples[u].globvol = 64;
|
|
125 of.samples[u].volume = 64;
|
|
126 }
|
|
127 return 1;
|
|
128 }
|
|
129
|
|
130
|
|
131 BOOL ML_LoadSamples(void)
|
|
132 {
|
|
133 SAMPLE *s;
|
|
134 int u;
|
|
135
|
|
136 for(u=of.numsmp, s=of.samples; u; u--, s++)
|
|
137 if(s->length) SL_RegisterSample(s,MD_MUSIC,modfp);
|
|
138
|
|
139 return 1;
|
|
140 }
|
|
141
|
|
142
|
|
143 CHAR *DupStr(CHAR *s, UWORD len)
|
|
144 // Creates a CSTR out of a character buffer of 'len' bytes, but strips
|
|
145 // any terminating non-printing characters like 0, spaces etc.
|
|
146 {
|
|
147 UWORD t;
|
|
148 CHAR *d = NULL;
|
|
149
|
|
150 // Scan for first printing char in buffer [includes high ascii up to 254]
|
|
151 while(len)
|
|
152 { if(s[len-1] > 0x20) break;
|
|
153 len--;
|
|
154 }
|
|
155
|
|
156 // When the buffer wasn't completely empty, allocate
|
|
157 // a cstring and copy the buffer into that string, except
|
|
158 // for any control-chars
|
|
159
|
|
160 #ifdef __GNUC__
|
|
161 if(len<16) len = 16;
|
|
162 #endif
|
|
163
|
|
164 if((d=(CHAR *)_mm_malloc(len+1)) != NULL)
|
|
165 { for(t=0; t<len; t++) d[t] = (s[t]<32) ? ' ' : s[t];
|
|
166 d[t] = 0;
|
|
167 }
|
|
168
|
|
169 return d;
|
|
170 }
|
|
171
|
|
172
|
|
173 static void ML_XFreeSample(SAMPLE *s)
|
|
174 {
|
|
175 if(s->handle>=0)
|
|
176 MD_SampleUnLoad(s->handle);
|
|
177 if(s->samplename!=NULL) free(s->samplename);
|
|
178 }
|
|
179
|
|
180
|
|
181 static void ML_XFreeInstrument(INSTRUMENT *i)
|
|
182 {
|
|
183 if(i->insname!=NULL) free(i->insname);
|
|
184 }
|
|
185
|
|
186
|
|
187 static void ML_FreeEx(UNIMOD *mf)
|
|
188 {
|
|
189 UWORD t;
|
|
190
|
|
191 if(mf->songname!=NULL) free(mf->songname);
|
|
192 if(mf->composer!=NULL) free(mf->composer);
|
|
193 if(mf->comment!=NULL) free(mf->comment);
|
|
194
|
|
195 if(mf->modtype!=NULL) free(mf->modtype);
|
|
196 if(mf->positions!=NULL) free(mf->positions);
|
|
197 if(mf->patterns!=NULL) free(mf->patterns);
|
|
198 if(mf->pattrows!=NULL) free(mf->pattrows);
|
|
199
|
|
200 if(mf->tracks!=NULL)
|
|
201 { for(t=0; t<mf->numtrk; t++)
|
|
202 if(mf->tracks[t]!=NULL) free(mf->tracks[t]);
|
|
203 free(mf->tracks);
|
|
204 }
|
|
205
|
|
206 if(mf->instruments != NULL)
|
|
207 { for(t=0; t<mf->numins; t++)
|
|
208 ML_XFreeInstrument(&mf->instruments[t]);
|
|
209 free(mf->instruments);
|
|
210 }
|
|
211
|
|
212 if(mf->samples != NULL)
|
|
213 { for(t=0; t<mf->numsmp; t++)
|
|
214 if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]);
|
|
215 free(mf->samples);
|
|
216 }
|
|
217
|
|
218 memset(mf,0,sizeof(UNIMOD));
|
|
219 }
|
|
220
|
|
221
|
|
222 static UNIMOD *ML_AllocUniMod(void)
|
|
223 {
|
|
224 UNIMOD *mf;
|
|
225
|
|
226 if((mf=_mm_calloc(1,sizeof(UNIMOD))) == NULL) return NULL;
|
|
227 return mf;
|
|
228 }
|
|
229
|
|
230
|
|
231 /******************************************
|
|
232
|
|
233 Next are the user-callable functions
|
|
234
|
|
235 ******************************************/
|
|
236
|
|
237
|
|
238 void MikMod_FreeSong(UNIMOD *mf)
|
|
239 {
|
|
240 if(mf!=NULL)
|
|
241 { Player_Exit(mf);
|
|
242 ML_FreeEx(mf);
|
|
243 }
|
|
244 }
|
|
245
|
|
246
|
|
247 CHAR *MikMod_LoadSongTitle(CHAR *filename)
|
|
248 {
|
|
249 MLOADER *l;
|
|
250 CHAR *retval;
|
|
251 FILE *fp;
|
|
252
|
|
253 if((fp = _mm_fopen(filename,"rb"))==NULL) return NULL;
|
|
254
|
|
255 _mm_errno = 0;
|
|
256 _mm_critical = 0;
|
|
257 _mm_iobase_setcur(modfp);
|
|
258
|
|
259 // Try to find a loader that recognizes the module
|
|
260
|
|
261 for(l=firstloader; l!=NULL; l=l->next)
|
|
262 { _mm_rewind(modfp);
|
|
263 if(l->Test()) break;
|
|
264 }
|
|
265
|
|
266 if(l==NULL)
|
|
267 { _mm_errno = MMERR_NOT_A_MODULE;
|
|
268 _mm_iobase_revert();
|
|
269 if(_mm_errorhandler!=NULL) _mm_errorhandler();
|
|
270 return NULL;
|
|
271 }
|
|
272
|
|
273 retval = l->LoadTitle();
|
|
274
|
|
275 fclose(fp);
|
|
276 return(retval);
|
|
277 }
|
|
278
|
|
279
|
|
280 UNIMOD *MikMod_LoadSongFP(FILE *fp, int maxchan)
|
|
281
|
|
282 // Loads a module given a file pointer.
|
|
283 // File is loaded from the current file seek position.
|
|
284
|
|
285 {
|
|
286 int t;
|
|
287 MLOADER *l;
|
|
288 BOOL ok;
|
|
289 UNIMOD *mf;
|
|
290
|
|
291 modfp = fp;
|
|
292 _mm_errno = 0;
|
|
293 _mm_critical = 0;
|
|
294
|
|
295 _mm_iobase_setcur(modfp);
|
|
296
|
|
297 // Try to find a loader that recognizes the module
|
|
298
|
|
299 for(l=firstloader; l!=NULL; l=l->next)
|
|
300 { _mm_rewind(modfp);
|
|
301 if(l->Test()) break;
|
|
302 }
|
|
303
|
|
304 if(l==NULL)
|
|
305 { _mm_errno = MMERR_NOT_A_MODULE;
|
|
306 _mm_iobase_revert();
|
|
307 if(_mm_errorhandler!=NULL) _mm_errorhandler();
|
|
308 return NULL;
|
|
309 }
|
|
310
|
|
311 // init unitrk routines
|
|
312 if(!UniInit())
|
|
313 { if(_mm_errorhandler!=NULL) _mm_errorhandler();
|
|
314 return NULL;
|
|
315 }
|
|
316
|
|
317 // load the song using the song's loader variable
|
|
318 memset(&of,0,sizeof(UNIMOD));
|
|
319 of.initvolume = 128;
|
|
320
|
|
321 // init panning array
|
|
322 for(t=0; t<64; t++) of.panning[t] = ((t+1)&2) ? 255 : 0;
|
|
323 for(t=0; t<64; t++) of.chanvol[t] = 64;
|
|
324
|
|
325 // init module loader and load the header / patterns
|
|
326 if(l->Init())
|
|
327 { _mm_rewind(modfp);
|
|
328 ok = l->Load();
|
|
329 } else ok = 0;
|
|
330
|
|
331 // free loader and unitrk allocations
|
|
332 l->Cleanup();
|
|
333 UniCleanup();
|
|
334
|
|
335 if(!ok)
|
|
336 { ML_FreeEx(&of);
|
|
337 _mm_iobase_revert();
|
|
338 if(_mm_errorhandler!=NULL) _mm_errorhandler();
|
|
339 return NULL;
|
|
340 }
|
|
341
|
|
342 if(!ML_LoadSamples())
|
|
343 { ML_FreeEx(&of);
|
|
344 _mm_iobase_revert();
|
|
345 if(_mm_errorhandler!=NULL) _mm_errorhandler();
|
|
346 return NULL;
|
|
347 }
|
|
348
|
|
349 if((mf=ML_AllocUniMod()) == NULL)
|
|
350 { ML_FreeEx(&of);
|
|
351 _mm_iobase_revert();
|
|
352 if(_mm_errorhandler!=NULL) _mm_errorhandler();
|
|
353 return NULL;
|
|
354 }
|
|
355
|
|
356 // Copy the static UNIMOD contents into the dynamic UNIMOD struct.
|
|
357 memcpy(mf,&of,sizeof(UNIMOD));
|
|
358
|
|
359 _mm_iobase_revert();
|
|
360
|
|
361 if(maxchan > 0)
|
|
362 { if(!(mf->flags & UF_NNA) && (mf->numchn < maxchan))
|
|
363 maxchan = mf->numchn;
|
|
364 else if((mf->numvoices!=0) && mf->numvoices < maxchan)
|
|
365 maxchan = mf->numvoices;
|
|
366
|
|
367 if(maxchan < mf->numchn) mf->flags |= UF_NNA;
|
|
368
|
|
369 if(MikMod_SetNumVoices(maxchan,-1))
|
|
370 { MikMod_FreeSong(mf);
|
|
371 return NULL;
|
|
372 }
|
|
373 }
|
|
374
|
|
375 return mf;
|
|
376 }
|
|
377
|
|
378
|
|
379 UNIMOD *MikMod_LoadSong(CHAR *filename, int maxchan)
|
|
380
|
|
381 // Open a module via it's filename. The loader will initialize the specified
|
|
382 // song-player 'player'.
|
|
383
|
|
384 {
|
|
385 FILE *fp;
|
|
386 UNIMOD *mf;
|
|
387
|
|
388 if((fp = _mm_fopen(filename,"rb"))==NULL) return NULL;
|
|
389 if((mf = MikMod_LoadSongFP(fp, maxchan)) != NULL)
|
|
390 { if(SL_LoadSamples() || Player_Init(mf))
|
|
391 { MikMod_FreeSong(mf);
|
|
392 mf = NULL;
|
|
393 }
|
|
394 }
|
|
395
|
|
396 fclose(fp);
|
|
397 return mf;
|
|
398 }
|
|
399
|