Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / playercode / mloader.c
diff --git a/apps/plugins/mikmod/playercode/mloader.c b/apps/plugins/mikmod/playercode/mloader.c
new file mode 100644 (file)
index 0000000..75504eb
--- /dev/null
@@ -0,0 +1,564 @@
+/*     MikMod sound library\r
+       (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file\r
+       AUTHORS for complete list.\r
+\r
+       This library is free software; you can redistribute it and/or modify\r
+       it under the terms of the GNU Library General Public License as\r
+       published by the Free Software Foundation; either version 2 of\r
+       the License, or (at your option) any later version.\r
\r
+       This program is distributed in the hope that it will be useful,\r
+       but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+       GNU Library General Public License for more details.\r
\r
+       You should have received a copy of the GNU Library General Public\r
+       License along with this library; if not, write to the Free Software\r
+       Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\r
+       02111-1307, USA.\r
+*/\r
+\r
+/*==============================================================================\r
+\r
+  $Id: mloader.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
+\r
+  These routines are used to access the available module loaders\r
+\r
+==============================================================================*/\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#ifdef HAVE_UNISTD_H\r
+#include <unistd.h>\r
+#endif\r
+\r
+#ifdef HAVE_MEMORY_H\r
+#include <memory.h>\r
+#endif\r
+#include <string.h>\r
+\r
+#include "mikmod_internals.h"\r
+\r
+#ifdef SUNOS\r
+extern int fprintf(FILE *, const char *, ...);\r
+#endif\r
+\r
+               MREADER *modreader;\r
+               MODULE of;\r
+\r
+static MLOADER *firstloader=NULL;\r
+\r
+UWORD finetune[16]={\r
+       8363,8413,8463,8529,8581,8651,8723,8757,\r
+       7895,7941,7985,8046,8107,8169,8232,8280\r
+};\r
+/*\r
+MIKMODAPI CHAR* MikMod_InfoLoader(void)\r
+{\r
+       int len=0;\r
+       MLOADER *l;\r
+       CHAR *list=NULL;\r
+\r
+       MUTEX_LOCK(lists);\r
+       /* compute size of buffer *\r
+       for(l=firstloader;l;l=l->next) len+=1+(l->next?1:0)+strlen(l->version);\r
+\r
+       if(len)\r
+               if((list=_mm_malloc(len*sizeof(CHAR)))) {\r
+                       list[0]=0;\r
+                       /* list all registered module loders *\r
+                       for(l=firstloader;l;l=l->next)\r
+                               sprintf(list,(l->next)?"%s%s\n":"%s%s",list,l->version);\r
+               }*\r
+       MUTEX_UNLOCK(lists);\r
+       return list;\r
+}\r
+*/\r
+void _mm_registerloader(MLOADER* ldr)\r
+{\r
+       MLOADER *cruise=firstloader;\r
+\r
+       if(cruise) {\r
+               while(cruise->next) cruise = cruise->next;\r
+               cruise->next=ldr;\r
+       } else\r
+               firstloader=ldr; \r
+}\r
+\r
+MIKMODAPI void MikMod_RegisterLoader(struct MLOADER* ldr)\r
+{\r
+       /* if we try to register an invalid loader, or an already registered loader,\r
+          ignore this attempt */\r
+       if ((!ldr)||(ldr->next))\r
+               return;\r
+\r
+       MUTEX_LOCK(lists);\r
+       _mm_registerloader(ldr);\r
+       MUTEX_UNLOCK(lists);\r
+}\r
+\r
+BOOL ReadComment(UWORD len)\r
+{\r
+       if(len) {\r
+               int i;\r
+\r
+               if(!(of.comment=(CHAR*)_mm_malloc(len+1))) return 0;\r
+               _mm_read_UBYTES(of.comment,len,modreader);\r
+               \r
+               /* translate IT linefeeds */\r
+               for(i=0;i<len;i++)\r
+                       if(of.comment[i]=='\r') of.comment[i]='\n';\r
+\r
+               of.comment[len]=0;      /* just in case */\r
+       }\r
+       if(!of.comment[0]) {\r
+               free(of.comment);\r
+               of.comment=NULL;\r
+       }\r
+       return 1;\r
+}\r
+\r
+BOOL ReadLinedComment(UWORD len,UWORD linelen)\r
+{\r
+       CHAR *tempcomment,*line,*storage;\r
+       UWORD total=0,t,lines;\r
+       int i;\r
+\r
+       lines = (len + linelen - 1) / linelen;\r
+       if (len) {\r
+               if(!(tempcomment=(CHAR*)_mm_malloc(len+1))) return 0;\r
+               if(!(storage=(CHAR*)_mm_malloc(linelen+1))) {\r
+                       free(tempcomment);\r
+                       return 0;\r
+               }\r
+               memset(tempcomment, ' ', len);\r
+               _mm_read_UBYTES(tempcomment,len,modreader);\r
+\r
+               /* compute message length */\r
+               for(line=tempcomment,total=t=0;t<lines;t++,line+=linelen) {\r
+                       for(i=linelen;(i>=0)&&(line[i]==' ');i--) line[i]=0;\r
+                       for(i=0;i<linelen;i++) if (!line[i]) break;\r
+                       total+=1+i;\r
+               }\r
+\r
+               if(total>lines) {\r
+                       if(!(of.comment=(CHAR*)_mm_malloc(total+1))) {\r
+                               free(storage);\r
+                               free(tempcomment);\r
+                               return 0;\r
+                       }\r
+\r
+                       /* convert message */\r
+                       for(line=tempcomment,t=0;t<lines;t++,line+=linelen) {\r
+                               for(i=0;i<linelen;i++) if(!(storage[i]=line[i])) break;\r
+                               storage[i]=0; /* if (i==linelen) */\r
+                               strcat(of.comment,storage);strcat(of.comment,"\r");\r
+                       }\r
+                       free(storage);\r
+                       free(tempcomment);\r
+               }\r
+       }\r
+       return 1;\r
+}\r
+\r
+BOOL AllocPositions(int total)\r
+{\r
+       if(!total) {\r
+               _mm_errno=MMERR_NOT_A_MODULE;\r
+               return 0;\r
+       }\r
+       if(!(of.positions=_mm_calloc(total,sizeof(UWORD)))) return 0;\r
+       return 1;\r
+}\r
+\r
+BOOL AllocPatterns(void)\r
+{\r
+       int s,t,tracks = 0;\r
+\r
+       if((!of.numpat)||(!of.numchn)) {\r
+               _mm_errno=MMERR_NOT_A_MODULE;\r
+               return 0;\r
+       }\r
+       /* Allocate track sequencing array */\r
+       if(!(of.patterns=(UWORD*)_mm_calloc((ULONG)(of.numpat+1)*of.numchn,sizeof(UWORD)))) return 0;\r
+       if(!(of.pattrows=(UWORD*)_mm_calloc(of.numpat+1,sizeof(UWORD)))) return 0;\r
+\r
+       for(t=0;t<=of.numpat;t++) {\r
+               of.pattrows[t]=64;\r
+               for(s=0;s<of.numchn;s++)\r
+               of.patterns[(t*of.numchn)+s]=tracks++;\r
+       }\r
+\r
+       return 1;\r
+}\r
+\r
+BOOL AllocTracks(void)\r
+{\r
+       if(!of.numtrk) {\r
+               _mm_errno=MMERR_NOT_A_MODULE;\r
+               return 0;\r
+       }\r
+       if(!(of.tracks=(UBYTE **)_mm_calloc(of.numtrk,sizeof(UBYTE *)))) return 0;\r
+       return 1;\r
+}\r
+\r
+BOOL AllocInstruments(void)\r
+{\r
+       int t,n;\r
+       \r
+       if(!of.numins) {\r
+               _mm_errno=MMERR_NOT_A_MODULE;\r
+               return 0;\r
+       }\r
+       if(!(of.instruments=(INSTRUMENT*)_mm_calloc(of.numins,sizeof(INSTRUMENT))))\r
+               return 0;\r
+\r
+       for(t=0;t<of.numins;t++) {\r
+               for(n=0;n<INSTNOTES;n++) { \r
+                       /* Init note / sample lookup table */\r
+                       of.instruments[t].samplenote[n]   = n;\r
+                       of.instruments[t].samplenumber[n] = t;\r
+               }   \r
+               of.instruments[t].globvol = 64;\r
+       }\r
+       return 1;\r
+}\r
+\r
+BOOL AllocSamples(void)\r
+{\r
+       UWORD u;\r
+\r
+       if(!of.numsmp) {\r
+               _mm_errno=MMERR_NOT_A_MODULE;\r
+               return 0;\r
+       }\r
+       if(!(of.samples=(SAMPLE*)_mm_calloc(of.numsmp,sizeof(SAMPLE)))) return 0;\r
+\r
+       for(u=0;u<of.numsmp;u++) {\r
+               of.samples[u].panning = 128; /* center */\r
+               of.samples[u].handle  = -1;\r
+               of.samples[u].globvol = 64;\r
+               of.samples[u].volume  = 64;\r
+       }\r
+       return 1;\r
+}\r
+\r
+static BOOL ML_LoadSamples(void)\r
+{\r
+       SAMPLE *s;\r
+       int u;\r
+\r
+       for(u=of.numsmp,s=of.samples;u;u--,s++)\r
+               if(s->length) SL_RegisterSample(s,MD_MUSIC,modreader);\r
+\r
+       return 1;\r
+}\r
+\r
+/* Creates a CSTR out of a character buffer of 'len' bytes, but strips any\r
+   terminating non-printing characters like 0, spaces etc.                    */\r
+CHAR *DupStr(CHAR* s,UWORD len,BOOL strict)\r
+{\r
+       UWORD t;\r
+       CHAR *d=NULL;\r
+\r
+       /* Scan for last printing char in buffer [includes high ascii up to 254] */\r
+       while(len) {\r
+               if(s[len-1]>0x20) break;\r
+               len--;\r
+       }\r
+\r
+       /* Scan forward for possible NULL character */\r
+       if(strict) {\r
+               for(t=0;t<len;t++) if (!s[t]) break;\r
+               if (t<len) len=t;\r
+       }\r
+\r
+       /* When the buffer wasn't completely empty, allocate a cstring and copy the\r
+          buffer into that string, except for any control-chars */\r
+       if((d=(CHAR*)_mm_malloc(sizeof(CHAR)*(len+1)))) {\r
+               for(t=0;t<len;t++) d[t]=(s[t]<32)?'.':s[t];\r
+               d[len]=0;\r
+       }\r
+       return d;\r
+}\r
+\r
+static void ML_XFreeSample(SAMPLE *s)\r
+{\r
+       if(s->handle>=0)\r
+               MD_SampleUnload(s->handle);\r
+       if(s->samplename) free(s->samplename);\r
+}\r
+\r
+static void ML_XFreeInstrument(INSTRUMENT *i)\r
+{\r
+       if(i->insname) free(i->insname);\r
+}\r
+\r
+static void ML_FreeEx(MODULE *mf)\r
+{\r
+       UWORD t;\r
+\r
+       if(mf->songname) free(mf->songname);\r
+       if(mf->comment)  free(mf->comment);\r
+\r
+       if(mf->modtype)   free(mf->modtype);\r
+       if(mf->positions) free(mf->positions);\r
+       if(mf->patterns)  free(mf->patterns);\r
+       if(mf->pattrows)  free(mf->pattrows);\r
+\r
+       if(mf->tracks) {\r
+               for(t=0;t<mf->numtrk;t++)\r
+                       if(mf->tracks[t]) free(mf->tracks[t]);\r
+               free(mf->tracks);\r
+       }\r
+       if(mf->instruments) {\r
+               for(t=0;t<mf->numins;t++)\r
+                       ML_XFreeInstrument(&mf->instruments[t]);\r
+               free(mf->instruments);\r
+       }\r
+       if(mf->samples) {\r
+               for(t=0;t<mf->numsmp;t++)\r
+                       if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]);\r
+               free(mf->samples);\r
+       }\r
+       memset(mf,0,sizeof(MODULE));\r
+       if(mf!=&of) free(mf);\r
+}\r
+\r
+static MODULE *ML_AllocUniMod(void)\r
+{\r
+       MODULE *mf;\r
+\r
+       return (mf=_mm_malloc(sizeof(MODULE)));\r
+}\r
+\r
+void Player_Free_internal(MODULE *mf)\r
+{\r
+       if(mf) {\r
+               Player_Exit_internal(mf);\r
+               ML_FreeEx(mf);\r
+       }\r
+}\r
+\r
+MIKMODAPI void Player_Free(MODULE *mf)\r
+{\r
+       MUTEX_LOCK(vars);\r
+       Player_Free_internal(mf);\r
+       MUTEX_UNLOCK(vars);\r
+}\r
+\r
+static CHAR* Player_LoadTitle_internal(MREADER *reader)\r
+{\r
+       MLOADER *l;\r
+\r
+       modreader=reader;\r
+       _mm_errno = 0;\r
+       _mm_critical = 0;\r
+       _mm_iobase_setcur(modreader);\r
+\r
+       /* Try to find a loader that recognizes the module */\r
+       for(l=firstloader;l;l=l->next) {\r
+               _mm_rewind(modreader);\r
+               if(l->Test()) break;\r
+       }\r
+\r
+       if(!l) {\r
+               _mm_errno = MMERR_NOT_A_MODULE;\r
+               if(_mm_errorhandler) _mm_errorhandler();\r
+               return NULL;\r
+       }\r
+\r
+       return l->LoadTitle();\r
+}\r
+\r
+MIKMODAPI CHAR* Player_LoadTitleFP(int fp)\r
+{\r
+       CHAR* result=NULL;\r
+       MREADER* reader;\r
+\r
+       if(fp && (reader=_mm_new_file_reader(fp))) {\r
+               MUTEX_LOCK(lists);\r
+               result=Player_LoadTitle_internal(reader);\r
+               MUTEX_UNLOCK(lists);\r
+               _mm_delete_file_reader(reader);\r
+       }\r
+       return result;\r
+}\r
+\r
+MIKMODAPI CHAR* Player_LoadTitle(CHAR* filename)\r
+{\r
+       CHAR* result=NULL;\r
+       int fp;\r
+       MREADER* reader;\r
+\r
+       if((fp=_mm_fopen(filename,O_RDONLY))) {\r
+               if((reader=_mm_new_file_reader(fp))) {\r
+                       MUTEX_LOCK(lists);\r
+                       result=Player_LoadTitle_internal(reader);\r
+                       MUTEX_UNLOCK(lists);\r
+                       _mm_delete_file_reader(reader);\r
+               }\r
+               _mm_fclose(fp);\r
+       }\r
+       return result;\r
+}\r
+\r
+/* Loads a module given an reader */\r
+MODULE* Player_LoadGeneric_internal(MREADER *reader,int maxchan,BOOL curious)\r
+{\r
+       int t;\r
+       MLOADER *l;\r
+       BOOL ok;\r
+       MODULE *mf;\r
+\r
+       modreader = reader;\r
+       _mm_errno = 0;\r
+       _mm_critical = 0;\r
+       _mm_iobase_setcur(modreader);\r
+\r
+       /* Try to find a loader that recognizes the module */\r
+       for(l=firstloader;l;l=l->next) {\r
+               _mm_rewind(modreader);\r
+               if(l->Test()) break;\r
+       }\r
+\r
+       if(!l) {\r
+               _mm_errno = MMERR_NOT_A_MODULE;\r
+               if(_mm_errorhandler) _mm_errorhandler();\r
+               _mm_rewind(modreader);_mm_iobase_revert();\r
+               return NULL;\r
+       }\r
+\r
+       /* init unitrk routines */\r
+       if(!UniInit()) {\r
+               if(_mm_errorhandler) _mm_errorhandler();\r
+               _mm_rewind(modreader);_mm_iobase_revert();\r
+               return NULL;\r
+       }\r
+\r
+       /* init the module structure with vanilla settings */\r
+       memset(&of,0,sizeof(MODULE));\r
+       of.bpmlimit = 33;\r
+       of.initvolume = 128;\r
+       for (t = 0; t < UF_MAXCHAN; t++) of.chanvol[t] = 64;\r
+       for (t = 0; t < UF_MAXCHAN; t++)\r
+               of.panning[t] = ((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT;\r
+\r
+       /* init module loader and load the header / patterns */\r
+       if (!l->Init || l->Init()) {\r
+               _mm_rewind(modreader);\r
+               ok = l->Load(curious);\r
+               /* propagate inflags=flags for in-module samples */\r
+               for (t = 0; t < of.numsmp; t++)\r
+                       if (of.samples[t].inflags == 0)\r
+                               of.samples[t].inflags = of.samples[t].flags;\r
+       } else\r
+               ok = 0;\r
+\r
+       /* free loader and unitrk allocations */\r
+       if (l->Cleanup) l->Cleanup();\r
+       UniCleanup();\r
+\r
+       if(!ok) {\r
+               ML_FreeEx(&of);\r
+               if(_mm_errorhandler) _mm_errorhandler();\r
+               _mm_rewind(modreader);_mm_iobase_revert();\r
+               return NULL;\r
+       }\r
+\r
+       if(!ML_LoadSamples()) {\r
+               ML_FreeEx(&of);\r
+               if(_mm_errorhandler) _mm_errorhandler();\r
+               _mm_rewind(modreader);_mm_iobase_revert();\r
+               return NULL;\r
+       }\r
+\r
+       if(!(mf=ML_AllocUniMod())) {\r
+               ML_FreeEx(&of);\r
+               _mm_rewind(modreader);_mm_iobase_revert();\r
+               if(_mm_errorhandler) _mm_errorhandler();\r
+               return NULL;\r
+       }\r
+       \r
+       /* If the module doesn't have any specific panning, create a\r
+          MOD-like panning, with the channels half-separated. */\r
+       if (!(of.flags & UF_PANNING))\r
+               for (t = 0; t < of.numchn; t++)\r
+                       of.panning[t] = ((t + 1) & 2) ? PAN_HALFRIGHT : PAN_HALFLEFT;\r
+\r
+       /* Copy the static MODULE contents into the dynamic MODULE struct. */\r
+       memcpy(mf,&of,sizeof(MODULE));\r
+\r
+       if(maxchan>0) {\r
+               if(!(mf->flags&UF_NNA)&&(mf->numchn<maxchan))\r
+                       maxchan = mf->numchn;\r
+               else\r
+                 if((mf->numvoices)&&(mf->numvoices<maxchan))\r
+                       maxchan = mf->numvoices;\r
+\r
+               if(maxchan<mf->numchn) mf->flags |= UF_NNA;\r
+\r
+               if(MikMod_SetNumVoices_internal(maxchan,-1)) {\r
+                       _mm_iobase_revert();\r
+                       Player_Free(mf);\r
+                       return NULL;\r
+               }\r
+       }\r
+       if(SL_LoadSamples()) {\r
+               _mm_iobase_revert();\r
+               Player_Free_internal(mf);\r
+               return NULL;\r
+       }\r
+       if(Player_Init(mf)) {\r
+               _mm_iobase_revert();\r
+               Player_Free_internal(mf);\r
+               mf=NULL;\r
+       }\r
+       _mm_iobase_revert();\r
+       return mf;\r
+}\r
+\r
+MIKMODAPI MODULE* Player_LoadGeneric(MREADER *reader,int maxchan,BOOL curious)\r
+{\r
+       MODULE* result;\r
+\r
+       MUTEX_LOCK(vars);\r
+       MUTEX_LOCK(lists);\r
+               result=Player_LoadGeneric_internal(reader,maxchan,curious);\r
+       MUTEX_UNLOCK(lists);\r
+       MUTEX_UNLOCK(vars);\r
+\r
+       return result;\r
+}\r
+\r
+/* Loads a module given a file pointer.\r
+   File is loaded from the current file seek position. */\r
+MIKMODAPI MODULE* Player_LoadFP(int fp,int maxchan,BOOL curious)\r
+{\r
+       MODULE* result=NULL;\r
+       struct MREADER* reader=_mm_new_file_reader (fp);\r
+\r
+       if (reader) {\r
+               result=Player_LoadGeneric(reader,maxchan,curious);\r
+               _mm_delete_file_reader(reader);\r
+       }\r
+       return result;\r
+}\r
+\r
+/* Open a module via its filename.  The loader will initialize the specified\r
+   song-player 'player'. */\r
+MIKMODAPI MODULE* Player_Load(CHAR* filename,int maxchan,BOOL curious)\r
+{\r
+       int fp;\r
+       MODULE *mf=NULL;\r
+\r
+       if((fp=_mm_fopen(filename,O_RDONLY))) {\r
+               mf=Player_LoadFP(fp,maxchan,curious);\r
+               _mm_fclose(fp);\r
+       }\r
+       return mf;\r
+}\r
+\r
+/* ex:set ts=4: */\r