1 /*	MikMod sound library
2 	(c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
3 	AUTHORS for complete list.
4 
5 	This library is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU Library General Public License as
7 	published by the Free Software Foundation; either version 2 of
8 	the License, or (at your option) any later version.
9 
10 	This program is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 	GNU Library General Public License for more details.
14 
15 	You should have received a copy of the GNU Library General Public
16 	License along with this library; if not, write to the Free Software
17 	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 	02111-1307, USA.
19 */
20 
21 /*==============================================================================
22 
23   $Id$
24 
25   These routines are used to access the available module loaders
26 
27 ==============================================================================*/
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 
37 #ifdef HAVE_MEMORY_H
38 #include <memory.h>
39 #endif
40 #include <string.h>
41 
42 #include "mikmod_internals.h"
43 
44 #ifdef SUNOS
45 extern int fprintf(FILE *, const char *, ...);
46 #endif
47 
48 		MREADER *modreader;
49 		MODULE of;
50 
51 static	MLOADER *firstloader=NULL;
52 
53 UWORD finetune[16]={
54 	8363,8413,8463,8529,8581,8651,8723,8757,
55 	7895,7941,7985,8046,8107,8169,8232,8280
56 };
57 
MikMod_InfoLoader(void)58 MIKMODAPI CHAR* MikMod_InfoLoader(void)
59 {
60 	int len=0;
61 	MLOADER *l;
62 	CHAR *list=NULL;
63 
64 	MUTEX_LOCK(lists);
65 	/* compute size of buffer */
66 	for(l=firstloader;l;l=l->next) len+=1+(l->next?1:0)+strlen(l->version);
67 
68 	if(len)
69 		if((list=MikMod_malloc(len*sizeof(CHAR)))) {
70 			list[0]=0;
71 			/* list all registered module loders */
72 			for(l=firstloader;l;l=l->next)
73 				sprintf(list,(l->next)?"%s%s\n":"%s%s",list,l->version);
74 		}
75 	MUTEX_UNLOCK(lists);
76 	return list;
77 }
78 
_mm_registerloader(MLOADER * ldr)79 void _mm_registerloader(MLOADER* ldr)
80 {
81 	MLOADER *cruise=firstloader;
82 
83 	if(cruise) {
84 		while(cruise->next) cruise = cruise->next;
85 		cruise->next=ldr;
86 	} else
87 		firstloader=ldr;
88 }
89 
MikMod_RegisterLoader(struct MLOADER * ldr)90 MIKMODAPI void MikMod_RegisterLoader(struct MLOADER* ldr)
91 {
92 	/* if we try to register an invalid loader, or an already registered loader,
93 	   ignore this attempt */
94 	if ((!ldr)||(ldr->next))
95 		return;
96 
97 	MUTEX_LOCK(lists);
98 	_mm_registerloader(ldr);
99 	MUTEX_UNLOCK(lists);
100 }
101 
ReadComment(UWORD len)102 BOOL ReadComment(UWORD len)
103 {
104 	if(len) {
105 		int i;
106 
107 		if(!(of.comment=(CHAR*)MikMod_malloc(len+1))) return 0;
108 		_mm_read_UBYTES(of.comment,len,modreader);
109 
110 		/* translate IT linefeeds */
111 		for(i=0;i<len;i++)
112 			if(of.comment[i]=='\r') of.comment[i]='\n';
113 
114 		of.comment[len]=0;	/* just in case */
115 	}
116 	if(!of.comment[0]) {
117 		MikMod_free(of.comment);
118 		of.comment=NULL;
119 	}
120 	return 1;
121 }
122 
ReadLinedComment(UWORD len,UWORD linelen)123 BOOL ReadLinedComment(UWORD len,UWORD linelen)
124 {
125 	CHAR *tempcomment,*line,*storage;
126 	UWORD total=0,t,lines;
127 	int i;
128 
129 	lines = (len + linelen - 1) / linelen;
130 	if (len) {
131 		if(!(tempcomment=(CHAR*)MikMod_malloc(len+1))) return 0;
132 		if(!(storage=(CHAR*)MikMod_malloc(linelen+1))) {
133 			MikMod_free(tempcomment);
134 			return 0;
135 		}
136 		memset(tempcomment, ' ', len);
137 		_mm_read_UBYTES(tempcomment,len,modreader);
138 
139 		/* compute message length */
140 		for(line=tempcomment,total=t=0;t<lines;t++,line+=linelen) {
141 			for(i=linelen;(i>=0)&&(line[i]==' ');i--) line[i]=0;
142 			for(i=0;i<linelen;i++) if (!line[i]) break;
143 			total+=1+i;
144 		}
145 
146 		if(total>lines) {
147 			if(!(of.comment=(CHAR*)MikMod_malloc(total+1))) {
148 				MikMod_free(storage);
149 				MikMod_free(tempcomment);
150 				return 0;
151 			}
152 
153 			/* convert message */
154 			for(line=tempcomment,t=0;t<lines;t++,line+=linelen) {
155 				for(i=0;i<linelen;i++) if(!(storage[i]=line[i])) break;
156 				storage[i]=0; /* if (i==linelen) */
157 				strcat(of.comment,storage);strcat(of.comment,"\r");
158 			}
159 			MikMod_free(storage);
160 			MikMod_free(tempcomment);
161 		}
162 	}
163 	return 1;
164 }
165 
AllocPositions(int total)166 BOOL AllocPositions(int total)
167 {
168 	if(!total) {
169 		_mm_errno=MMERR_NOT_A_MODULE;
170 		return 0;
171 	}
172 	if(!(of.positions=MikMod_calloc(total,sizeof(UWORD)))) return 0;
173 	return 1;
174 }
175 
AllocPatterns(void)176 BOOL AllocPatterns(void)
177 {
178 	int s,t,tracks = 0;
179 
180 	if((!of.numpat)||(!of.numchn)) {
181 		_mm_errno=MMERR_NOT_A_MODULE;
182 		return 0;
183 	}
184 	/* Allocate track sequencing array */
185 	if(!(of.patterns=(UWORD*)MikMod_calloc((ULONG)(of.numpat+1)*of.numchn,sizeof(UWORD)))) return 0;
186 	if(!(of.pattrows=(UWORD*)MikMod_calloc(of.numpat+1,sizeof(UWORD)))) return 0;
187 
188 	for(t=0;t<=of.numpat;t++) {
189 		of.pattrows[t]=64;
190 		for(s=0;s<of.numchn;s++)
191 		of.patterns[(t*of.numchn)+s]=tracks++;
192 	}
193 
194 	return 1;
195 }
196 
AllocTracks(void)197 BOOL AllocTracks(void)
198 {
199 	if(!of.numtrk) {
200 		_mm_errno=MMERR_NOT_A_MODULE;
201 		return 0;
202 	}
203 	if(!(of.tracks=(UBYTE **)MikMod_calloc(of.numtrk,sizeof(UBYTE *)))) return 0;
204 	return 1;
205 }
206 
AllocInstruments(void)207 BOOL AllocInstruments(void)
208 {
209 	int t,n;
210 
211 	if(!of.numins) {
212 		_mm_errno=MMERR_NOT_A_MODULE;
213 		return 0;
214 	}
215 	if(!(of.instruments=(INSTRUMENT*)MikMod_calloc(of.numins,sizeof(INSTRUMENT))))
216 		return 0;
217 
218 	for(t=0;t<of.numins;t++) {
219 		for(n=0;n<INSTNOTES;n++) {
220 			/* Init note / sample lookup table */
221 			of.instruments[t].samplenote[n]   = n;
222 			of.instruments[t].samplenumber[n] = t;
223 		}
224 		of.instruments[t].globvol = 64;
225 	}
226 	return 1;
227 }
228 
AllocSamples(void)229 BOOL AllocSamples(void)
230 {
231 	UWORD u;
232 
233 	if(!of.numsmp) {
234 		_mm_errno=MMERR_NOT_A_MODULE;
235 		return 0;
236 	}
237 	if(!(of.samples=(SAMPLE*)MikMod_calloc(of.numsmp,sizeof(SAMPLE)))) return 0;
238 
239 	for(u=0;u<of.numsmp;u++) {
240 		of.samples[u].panning = 128; /* center */
241 		of.samples[u].handle  = -1;
242 		of.samples[u].globvol = 64;
243 		of.samples[u].volume  = 64;
244 	}
245 	return 1;
246 }
247 
ML_LoadSamples(void)248 static BOOL ML_LoadSamples(void)
249 {
250 	SAMPLE *s;
251 	int u;
252 
253 	for(u=of.numsmp,s=of.samples;u;u--,s++)
254 		if(s->length) SL_RegisterSample(s,MD_MUSIC,modreader);
255 
256 	return 1;
257 }
258 
259 /* Creates a CSTR out of a character buffer of 'len' bytes, but strips any
260    terminating non-printing characters like 0, spaces etc.                    */
DupStr(CHAR * s,UWORD len,BOOL strict)261 CHAR *DupStr(CHAR* s,UWORD len,BOOL strict)
262 {
263 	UWORD t;
264 	CHAR *d=NULL;
265 
266 	/* Scan for last printing char in buffer [includes high ascii up to 254] */
267 	while(len) {
268 		if(s[len-1]>0x20) break;
269 		len--;
270 	}
271 
272 	/* Scan forward for possible NULL character */
273 	if(strict) {
274 		for(t=0;t<len;t++) if (!s[t]) break;
275 		if (t<len) len=t;
276 	}
277 
278 	/* When the buffer wasn't completely empty, allocate a cstring and copy the
279 	   buffer into that string, except for any control-chars */
280 	if((d=(CHAR*)MikMod_malloc(sizeof(CHAR)*(len+1)))) {
281 		for(t=0;t<len;t++) d[t]=(s[t]<32)?'.':s[t];
282 		d[len]=0;
283 	}
284 	return d;
285 }
286 
ML_XFreeSample(SAMPLE * s)287 static void ML_XFreeSample(SAMPLE *s)
288 {
289 	if(s->handle>=0)
290 		MD_SampleUnload(s->handle);
291 	if(s->samplename) MikMod_free(s->samplename);
292 }
293 
ML_XFreeInstrument(INSTRUMENT * i)294 static void ML_XFreeInstrument(INSTRUMENT *i)
295 {
296 	if(i->insname) MikMod_free(i->insname);
297 }
298 
ML_FreeEx(MODULE * mf)299 static void ML_FreeEx(MODULE *mf)
300 {
301 	UWORD t;
302 
303 	if(mf->songname) MikMod_free(mf->songname);
304 	if(mf->comment)  MikMod_free(mf->comment);
305 
306 	if(mf->modtype)   MikMod_free(mf->modtype);
307 	if(mf->positions) MikMod_free(mf->positions);
308 	if(mf->patterns)  MikMod_free(mf->patterns);
309 	if(mf->pattrows)  MikMod_free(mf->pattrows);
310 
311 	if(mf->tracks) {
312 		for(t=0;t<mf->numtrk;t++)
313 			if(mf->tracks[t]) MikMod_free(mf->tracks[t]);
314 		MikMod_free(mf->tracks);
315 	}
316 	if(mf->instruments) {
317 		for(t=0;t<mf->numins;t++)
318 			ML_XFreeInstrument(&mf->instruments[t]);
319 		MikMod_free(mf->instruments);
320 	}
321 	if(mf->samples) {
322 		for(t=0;t<mf->numsmp;t++)
323 			if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]);
324 		MikMod_free(mf->samples);
325 	}
326 	memset(mf,0,sizeof(MODULE));
327 	if(mf!=&of) MikMod_free(mf);
328 }
329 
ML_AllocUniMod(void)330 static MODULE *ML_AllocUniMod(void)
331 {
332 	MODULE *mf;
333 
334 	return (mf=MikMod_malloc(sizeof(MODULE)));
335 }
336 
Player_Free_internal(MODULE * mf)337 void Player_Free_internal(MODULE *mf)
338 {
339 	if(mf) {
340 		Player_Exit_internal(mf);
341 		ML_FreeEx(mf);
342 	}
343 }
344 
Player_Free(MODULE * mf)345 MIKMODAPI void Player_Free(MODULE *mf)
346 {
347 	MUTEX_LOCK(vars);
348 	Player_Free_internal(mf);
349 	MUTEX_UNLOCK(vars);
350 }
351 
Player_LoadTitle_internal(MREADER * reader)352 static CHAR* Player_LoadTitle_internal(MREADER *reader)
353 {
354 	MLOADER *l;
355 
356 	modreader=reader;
357 	_mm_errno = 0;
358 	_mm_critical = 0;
359 	_mm_iobase_setcur(modreader);
360 
361 	/* Try to find a loader that recognizes the module */
362 	for(l=firstloader;l;l=l->next) {
363 		_mm_rewind(modreader);
364 		if(l->Test()) break;
365 	}
366 
367 	if(!l) {
368 		_mm_errno = MMERR_NOT_A_MODULE;
369 		if(_mm_errorhandler) _mm_errorhandler();
370 		return NULL;
371 	}
372 
373 	return l->LoadTitle();
374 }
375 
Player_LoadTitleFP(FILE * fp)376 MIKMODAPI CHAR* Player_LoadTitleFP(FILE *fp)
377 {
378 	CHAR* result=NULL;
379 	MREADER* reader;
380 
381 	if(fp && (reader=_mm_new_file_reader(fp))) {
382 		MUTEX_LOCK(lists);
383 		result=Player_LoadTitle_internal(reader);
384 		MUTEX_UNLOCK(lists);
385 		_mm_delete_file_reader(reader);
386 	}
387 	return result;
388 }
389 
Player_LoadTitleMem(const char * buffer,int len)390 MIKMODAPI CHAR* Player_LoadTitleMem(const char *buffer,int len)
391 {
392 	CHAR *result=NULL;
393 	MREADER* reader;
394 
395 	if ((reader=_mm_new_mem_reader(buffer,len)))
396 	{
397 		MUTEX_LOCK(lists);
398 		result=Player_LoadTitle_internal(reader);
399 		MUTEX_UNLOCK(lists);
400 		_mm_delete_mem_reader(reader);
401 	}
402 
403 
404 	return result;
405 }
406 
Player_LoadTitleGeneric(MREADER * reader)407 MIKMODAPI CHAR* Player_LoadTitleGeneric(MREADER *reader)
408 {
409 	CHAR *result=NULL;
410 
411 	if (reader) {
412 		MUTEX_LOCK(lists);
413 		result=Player_LoadTitle_internal(reader);
414 		MUTEX_UNLOCK(lists);
415 	}
416 	return result;
417 }
418 
Player_LoadTitle(CHAR * filename)419 MIKMODAPI CHAR* Player_LoadTitle(CHAR* filename)
420 {
421 	CHAR* result=NULL;
422 	FILE* fp;
423 	MREADER* reader;
424 
425 	if((fp=_mm_fopen(filename,"rb"))) {
426 		if((reader=_mm_new_file_reader(fp))) {
427 			MUTEX_LOCK(lists);
428 			result=Player_LoadTitle_internal(reader);
429 			MUTEX_UNLOCK(lists);
430 			_mm_delete_file_reader(reader);
431 		}
432 		_mm_fclose(fp);
433 	}
434 	return result;
435 }
436 
437 /* Loads a module given an reader */
Player_LoadGeneric_internal(MREADER * reader,int maxchan,BOOL curious)438 MODULE* Player_LoadGeneric_internal(MREADER *reader,int maxchan,BOOL curious)
439 {
440 	int t;
441 	MLOADER *l;
442 	BOOL ok;
443 	MODULE *mf;
444 
445 	modreader = reader;
446 	_mm_errno = 0;
447 	_mm_critical = 0;
448 	_mm_iobase_setcur(modreader);
449 
450 	/* Try to find a loader that recognizes the module */
451 	for(l=firstloader;l;l=l->next) {
452 		_mm_rewind(modreader);
453 		if(l->Test()) break;
454 	}
455 
456 	if(!l) {
457 		_mm_errno = MMERR_NOT_A_MODULE;
458 		if(_mm_errorhandler) _mm_errorhandler();
459 		_mm_rewind(modreader);_mm_iobase_revert(modreader);
460 		return NULL;
461 	}
462 
463 	/* init unitrk routines */
464 	if(!UniInit()) {
465 		if(_mm_errorhandler) _mm_errorhandler();
466 		_mm_rewind(modreader);_mm_iobase_revert(modreader);
467 		return NULL;
468 	}
469 
470 	/* init the module structure with vanilla settings */
471 	memset(&of,0,sizeof(MODULE));
472 	of.bpmlimit = 33;
473 	of.initvolume = 128;
474 	for (t = 0; t < UF_MAXCHAN; t++) of.chanvol[t] = 64;
475 	for (t = 0; t < UF_MAXCHAN; t++)
476 		of.panning[t] = ((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT;
477 
478 	/* init module loader and load the header / patterns */
479 	if (!l->Init || l->Init()) {
480 		_mm_rewind(modreader);
481 		ok = l->Load(curious);
482 		if (ok) {
483 			/* propagate inflags=flags for in-module samples */
484 			for (t = 0; t < of.numsmp; t++)
485 				if (of.samples[t].inflags == 0)
486 					of.samples[t].inflags = of.samples[t].flags;
487 		}
488 	} else
489 		ok = 0;
490 
491 	/* free loader and unitrk allocations */
492 	if (l->Cleanup) l->Cleanup();
493 	UniCleanup();
494 
495 	if(!ok) {
496 		ML_FreeEx(&of);
497 		if(_mm_errorhandler) _mm_errorhandler();
498 		_mm_rewind(modreader);_mm_iobase_revert(modreader);
499 		return NULL;
500 	}
501 
502 	if(!ML_LoadSamples()) {
503 		ML_FreeEx(&of);
504 		if(_mm_errorhandler) _mm_errorhandler();
505 		_mm_rewind(modreader);_mm_iobase_revert(modreader);
506 		return NULL;
507 	}
508 
509 	if(!(mf=ML_AllocUniMod())) {
510 		ML_FreeEx(&of);
511 		_mm_rewind(modreader);_mm_iobase_revert(modreader);
512 		if(_mm_errorhandler) _mm_errorhandler();
513 		return NULL;
514 	}
515 
516 	/* If the module doesn't have any specific panning, create a
517 	   MOD-like panning, with the channels half-separated. */
518 	if (!(of.flags & UF_PANNING))
519 		for (t = 0; t < of.numchn; t++)
520 			of.panning[t] = ((t + 1) & 2) ? PAN_HALFRIGHT : PAN_HALFLEFT;
521 
522 	/* Copy the static MODULE contents into the dynamic MODULE struct. */
523 	memcpy(mf,&of,sizeof(MODULE));
524 
525 	if(maxchan>0) {
526 		if(!(mf->flags&UF_NNA)&&(mf->numchn<maxchan))
527 			maxchan = mf->numchn;
528 		else
529 		  if((mf->numvoices)&&(mf->numvoices<maxchan))
530 			maxchan = mf->numvoices;
531 
532 		if(maxchan<mf->numchn) mf->flags |= UF_NNA;
533 
534 		if(MikMod_SetNumVoices_internal(maxchan,-1)) {
535 			_mm_iobase_revert(modreader);
536 			Player_Free(mf);
537 			return NULL;
538 		}
539 	}
540 	if(SL_LoadSamples()) {
541 		_mm_iobase_revert(modreader);
542 		Player_Free_internal(mf);
543 		return NULL;
544 	}
545 	if(Player_Init(mf)) {
546 		_mm_iobase_revert(modreader);
547 		Player_Free_internal(mf);
548 		mf=NULL;
549 	}
550 	_mm_iobase_revert(modreader);
551 	return mf;
552 }
553 
Player_LoadGeneric(MREADER * reader,int maxchan,BOOL curious)554 MIKMODAPI MODULE* Player_LoadGeneric(MREADER *reader,int maxchan,BOOL curious)
555 {
556 	MODULE* result;
557 
558 	MUTEX_LOCK(vars);
559 	MUTEX_LOCK(lists);
560 		result=Player_LoadGeneric_internal(reader,maxchan,curious);
561 	MUTEX_UNLOCK(lists);
562 	MUTEX_UNLOCK(vars);
563 
564 	return result;
565 }
566 
Player_LoadMem(const char * buffer,int len,int maxchan,BOOL curious)567 MIKMODAPI MODULE* Player_LoadMem(const char *buffer,int len,int maxchan,BOOL curious)
568 {
569 	MODULE* result=NULL;
570 	MREADER* reader;
571 
572 	if ((reader=_mm_new_mem_reader(buffer, len))) {
573 		result=Player_LoadGeneric(reader,maxchan,curious);
574 		_mm_delete_mem_reader(reader);
575 	}
576 	return result;
577 }
578 
579 /* Loads a module given a file pointer.
580    File is loaded from the current file seek position. */
Player_LoadFP(FILE * fp,int maxchan,BOOL curious)581 MIKMODAPI MODULE* Player_LoadFP(FILE* fp,int maxchan,BOOL curious)
582 {
583 	MODULE* result=NULL;
584 	struct MREADER* reader=_mm_new_file_reader (fp);
585 
586 	if (reader) {
587 		result=Player_LoadGeneric(reader,maxchan,curious);
588 		_mm_delete_file_reader(reader);
589 	}
590 	return result;
591 }
592 
593 /* Open a module via its filename.  The loader will initialize the specified
594    song-player 'player'. */
Player_Load(CHAR * filename,int maxchan,BOOL curious)595 MIKMODAPI MODULE* Player_Load(CHAR* filename,int maxchan,BOOL curious)
596 {
597 	FILE *fp;
598 	MODULE *mf=NULL;
599 
600 	if((fp=_mm_fopen(filename,"rb"))) {
601 		mf=Player_LoadFP(fp,maxchan,curious);
602 		_mm_fclose(fp);
603 	}
604 	return mf;
605 }
606 
607 /* ex:set ts=4: */
608