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