Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_gdm.c
diff --git a/apps/plugins/mikmod/loaders/load_gdm.c b/apps/plugins/mikmod/loaders/load_gdm.c
new file mode 100644 (file)
index 0000000..d65c2f8
--- /dev/null
@@ -0,0 +1,559 @@
+/*     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: load_gdm.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
+\r
+  General DigiMusic (GDM) module loader\r
+\r
+==============================================================================*/\r
+\r
+/*\r
+\r
+       Written by Kev Vance<kvance@zeux.org>\r
+       based on the file format description written by 'MenTaLguY'\r
+                                                               <mental@kludge.org>\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
+#include <stdio.h>\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
+typedef struct GDMNOTE {\r
+       UBYTE note;\r
+       UBYTE samp;\r
+       struct {\r
+               UBYTE effect;\r
+               UBYTE param;\r
+       } effect[4];\r
+} GDMNOTE;\r
+\r
+typedef GDMNOTE GDMTRACK[64];\r
+\r
+typedef struct GDMHEADER {\r
+       CHAR  id1[4];\r
+       CHAR  songname[32];\r
+       CHAR  author[32];\r
+       CHAR  eofmarker[3];\r
+       CHAR  id2[4];\r
+\r
+       UBYTE majorver;\r
+       UBYTE minorver;\r
+       UWORD trackerid;\r
+       UBYTE t_majorver;\r
+       UBYTE t_minorver;\r
+       UBYTE pantable[32];\r
+       UBYTE mastervol;\r
+       UBYTE mastertempo;\r
+       UBYTE masterbpm;\r
+       UWORD flags;\r
+\r
+       ULONG orderloc;\r
+       UBYTE ordernum;\r
+       ULONG patternloc;\r
+       UBYTE patternnum;\r
+       ULONG samhead;\r
+       ULONG samdata;\r
+       UBYTE samnum;\r
+       ULONG messageloc;\r
+       ULONG messagelen;\r
+       ULONG scrollyloc;\r
+       UWORD scrollylen;\r
+       ULONG graphicloc;\r
+       UWORD graphiclen;\r
+} GDMHEADER;\r
+\r
+typedef struct GDMSAMPLE {\r
+       CHAR  sampname[32];\r
+       CHAR  filename[13];\r
+       UBYTE ems;\r
+       ULONG length;\r
+       ULONG loopbeg;\r
+       ULONG loopend;\r
+       UBYTE flags;\r
+       UWORD c4spd;\r
+       UBYTE vol;\r
+       UBYTE pan;\r
+} GDMSAMPLE;\r
+\r
+static GDMHEADER *mh=NULL;     /* pointer to GDM header */\r
+static GDMNOTE *gdmbuf=NULL;   /* pointer to a complete GDM pattern */\r
+\r
+CHAR GDM_Version[]="General DigiMusic 1.xx";\r
+\r
+BOOL GDM_Test(void)\r
+{\r
+       /* test for gdm magic numbers */\r
+       UBYTE id[4];\r
+\r
+       _mm_fseek(modreader,0x00,SEEK_SET);\r
+       if (!_mm_read_UBYTES(id,4,modreader))\r
+               return 0;\r
+       if (!memcmp(id,"GDM\xfe",4)) {\r
+               _mm_fseek(modreader,71,SEEK_SET);\r
+               if (!_mm_read_UBYTES(id,4,modreader))\r
+                       return 0;\r
+               if (!memcmp(id,"GMFS",4))\r
+                       return 1;\r
+       }\r
+       return 0;\r
+}\r
+\r
+BOOL GDM_Init(void)\r
+{\r
+       if (!(gdmbuf=(GDMNOTE*)_mm_malloc(32*64*sizeof(GDMNOTE)))) return 0;\r
+       if (!(mh=(GDMHEADER*)_mm_malloc(sizeof(GDMHEADER)))) return 0;\r
+\r
+       return 1;\r
+}\r
+\r
+void GDM_Cleanup(void)\r
+{\r
+       _mm_free(mh);\r
+       _mm_free(gdmbuf);\r
+}\r
+\r
+BOOL GDM_ReadPattern(void)\r
+{\r
+       int pos,flag,ch,i,maxch;\r
+       GDMNOTE n;\r
+       UWORD length,x=0;\r
+\r
+       /* get pattern length */\r
+       length=_mm_read_I_UWORD(modreader)-2;\r
+\r
+       /* clear pattern data */\r
+       memset(gdmbuf,255,32*64*sizeof(GDMNOTE));\r
+       pos=0;\r
+       maxch=0;\r
+\r
+       while (x<length) {\r
+               memset(&n,255,sizeof(GDMNOTE));\r
+               flag=_mm_read_UBYTE(modreader);\r
+               x++;\r
+\r
+               if (_mm_eof(modreader)) {\r
+                       _mm_errno=MMERR_LOADING_PATTERN;\r
+                       return 0;\r
+               }\r
+\r
+               ch=flag&31;\r
+               if (ch>maxch) maxch=ch;\r
+               if (!flag) {\r
+                       pos++;\r
+                       continue;\r
+               }\r
+               if (flag&0x60) {\r
+                       if (flag&0x20) {\r
+                               /* new note */\r
+                               n.note=_mm_read_UBYTE(modreader)&127;\r
+                               n.samp=_mm_read_UBYTE(modreader);\r
+                               x +=2;\r
+                       }\r
+                       if (flag&0x40) {\r
+                               do {\r
+                                       /* effect channel set */\r
+                                       i=_mm_read_UBYTE(modreader);\r
+                                       n.effect[i>>6].effect=i&31;\r
+                                       n.effect[i>>6].param=_mm_read_UBYTE(modreader);\r
+                                       x +=2;\r
+                               } while (i&32);\r
+                       }\r
+                       memcpy(gdmbuf+(64U*ch)+pos,&n,sizeof(GDMNOTE));\r
+               }\r
+       }\r
+       return 1;\r
+}\r
+\r
+UBYTE *GDM_ConvertTrack(GDMNOTE*tr)\r
+{\r
+       int t,i=0;\r
+       UBYTE note,ins,inf;\r
+\r
+       UniReset();\r
+       for (t=0;t<64;t++) {\r
+               note=tr[t].note;\r
+               ins=tr[t].samp;\r
+\r
+               if ((ins)&&(ins!=255))\r
+                       UniInstrument(ins-1);\r
+               if (note!=255) {\r
+                       UniNote(((note>>4)*OCTAVE)+(note&0xf)-1);\r
+               }\r
+               for (i=0;i<4;i++) {\r
+                       inf = tr[t].effect[i].param;\r
+                       switch (tr[t].effect[i].effect) {\r
+                               case 1: /* toneslide up */\r
+                                       UniEffect(UNI_S3MEFFECTF,inf);\r
+                                       break;\r
+                               case 2: /* toneslide down */\r
+                                       UniEffect(UNI_S3MEFFECTE,inf);\r
+                                       break;\r
+                               case 3: /* glissando to note */\r
+                                       UniEffect(UNI_ITEFFECTG,inf);\r
+                                       break;\r
+                               case 4: /* vibrato */\r
+                                       UniEffect(UNI_ITEFFECTH,inf);\r
+                                       break;\r
+                               case 5: /* portamento+volslide */\r
+                                       UniEffect(UNI_ITEFFECTG,0);\r
+                                       UniEffect(UNI_S3MEFFECTD,inf);\r
+                                       break;\r
+                               case 6: /* vibrato+volslide */\r
+                                       UniEffect(UNI_ITEFFECTH,0);\r
+                                       UniEffect(UNI_S3MEFFECTD,inf);\r
+                                       break;\r
+                               case 7: /* tremolo */\r
+                                       UniEffect(UNI_S3MEFFECTR,inf);\r
+                                       break;\r
+                               case 8: /* tremor */\r
+                                       UniEffect(UNI_S3MEFFECTI,inf);\r
+                                       break;\r
+                               case 9: /* offset */\r
+                                       UniPTEffect(0x09,inf);\r
+                                       break;\r
+                               case 0x0a:      /* volslide */\r
+                                       UniEffect(UNI_S3MEFFECTD,inf);\r
+                                       break;\r
+                               case 0x0b:      /* jump to order */\r
+                                       UniPTEffect(0x0b,inf);\r
+                                       break;\r
+                               case 0x0c:      /* volume set */\r
+                                       UniPTEffect(0x0c,inf);\r
+                                       break;\r
+                               case 0x0d:      /* pattern break */\r
+                                       UniPTEffect(0x0d,inf);\r
+                                       break;\r
+                               case 0x0e:      /* extended */\r
+                                       switch (inf&0xf0) {\r
+                                               case 0x10:      /* fine portamento up */\r
+                                                       UniEffect(UNI_S3MEFFECTF, 0x0f|((inf<<4)&0x0f));\r
+                                                       break;\r
+                                               case 0x20:      /* fine portamento down */\r
+                                                       UniEffect(UNI_S3MEFFECTE, 0xf0|(inf&0x0f));\r
+                                                       break;\r
+                                               case 0x30:      /* glissando control */\r
+                                                       UniEffect(SS_GLISSANDO, inf&0x0f);\r
+                                                       break;\r
+                                               case 0x40:      /* vibrato waveform */\r
+                                                       UniEffect(SS_VIBWAVE, inf&0x0f);\r
+                                                       break;\r
+                                               case 0x50:      /* set c4spd */\r
+                                                       UniEffect(SS_FINETUNE, inf&0x0f);\r
+                                                       break;\r
+                                               case 0x60:      /* loop fun */\r
+                                                       UniEffect(UNI_ITEFFECTS0, (inf&0x0f)|0xb0);\r
+                                                       break;\r
+                                               case 0x70:      /* tremolo waveform */\r
+                                                       UniEffect(SS_TREMWAVE, inf&0x0f);\r
+                                                       break;\r
+                                               case 0x80:      /* extra fine porta up */\r
+                                                       UniEffect(UNI_S3MEFFECTF, 0x0e|((inf<<4)&0x0f));\r
+                                                       break;\r
+                                               case 0x90:      /* extra fine porta down */\r
+                                                       UniEffect(UNI_S3MEFFECTE, 0xe0|(inf&0x0f));\r
+                                                       break;\r
+                                               case 0xa0:      /* fine volslide up */\r
+                                                       UniEffect(UNI_S3MEFFECTD, 0x0f|((inf<<4)&0x0f));\r
+                                                       break;\r
+                                               case 0xb0:      /* fine volslide down */\r
+                                                       UniEffect(UNI_S3MEFFECTE, 0xf0|(inf&0x0f));\r
+                                                       break;\r
+                                               case 0xc0:      /* note cut */\r
+                                               case 0xd0:      /* note delay */\r
+                                               case 0xe0:      /* extend row */\r
+                                                       UniPTEffect(0xe,inf);\r
+                                                       break;\r
+                                       }\r
+                                       break;\r
+                               case 0x0f:      /* set tempo */\r
+                                       UniEffect(UNI_S3MEFFECTA,inf);\r
+                                       break;\r
+                               case 0x10:      /* arpeggio */\r
+                                       UniPTEffect(0x0,inf);\r
+                                       break;\r
+                               case 0x12:      /* retrigger */\r
+                                       UniEffect(UNI_S3MEFFECTQ,inf);\r
+                                       break;\r
+                               case 0x13:      /* set global volume */\r
+                                       UniEffect(UNI_XMEFFECTG,inf<<1);\r
+                                       break;\r
+                               case 0x14:      /* fine vibrato */\r
+                                       UniEffect(UNI_ITEFFECTU,inf);\r
+                                       break;\r
+                               case 0x1e:      /* special */\r
+                                       switch (inf&0xf0) {\r
+                                               case 8: /* set pan position */\r
+                                                       if (inf >=128)\r
+                                                               UniPTEffect(0x08,255);\r
+                                                       else\r
+                                                               UniPTEffect(0x08,inf<<1);\r
+                                                       break;\r
+                                       }\r
+                                       break;\r
+                               case 0x1f:      /* set bpm */\r
+                                       if (inf >=0x20)\r
+                                               UniEffect(UNI_S3MEFFECTT,inf);\r
+                                       break;\r
+                       }\r
+               }\r
+               UniNewline();\r
+       }\r
+       return UniDup();\r
+}\r
+\r
+BOOL GDM_Load(BOOL curious)\r
+{\r
+       int i,x,u,track;\r
+       SAMPLE *q;\r
+       GDMSAMPLE s;\r
+       ULONG position;\r
+       (void)curious;\r
+\r
+       /* read header */\r
+       _mm_read_string(mh->id1,4,modreader);\r
+       _mm_read_string(mh->songname,32,modreader);\r
+       _mm_read_string(mh->author,32,modreader);\r
+       _mm_read_string(mh->eofmarker,3,modreader);\r
+       _mm_read_string(mh->id2,4,modreader);\r
+\r
+       mh->majorver=_mm_read_UBYTE(modreader);\r
+       mh->minorver=_mm_read_UBYTE(modreader);\r
+       mh->trackerid=_mm_read_I_UWORD(modreader);\r
+       mh->t_majorver=_mm_read_UBYTE(modreader);\r
+       mh->t_minorver=_mm_read_UBYTE(modreader);\r
+       _mm_read_UBYTES(mh->pantable,32,modreader);\r
+       mh->mastervol=_mm_read_UBYTE(modreader);\r
+       mh->mastertempo=_mm_read_UBYTE(modreader);\r
+       mh->masterbpm=_mm_read_UBYTE(modreader);\r
+       mh->flags=_mm_read_I_UWORD(modreader);\r
+\r
+       mh->orderloc=_mm_read_I_ULONG(modreader);\r
+       mh->ordernum=_mm_read_UBYTE(modreader);\r
+       mh->patternloc=_mm_read_I_ULONG(modreader);\r
+       mh->patternnum=_mm_read_UBYTE(modreader);\r
+       mh->samhead=_mm_read_I_ULONG(modreader);\r
+       mh->samdata=_mm_read_I_ULONG(modreader);\r
+       mh->samnum=_mm_read_UBYTE(modreader);\r
+       mh->messageloc=_mm_read_I_ULONG(modreader);\r
+       mh->messagelen=_mm_read_I_ULONG(modreader);\r
+       mh->scrollyloc=_mm_read_I_ULONG(modreader);\r
+       mh->scrollylen=_mm_read_I_UWORD(modreader);\r
+       mh->graphicloc=_mm_read_I_ULONG(modreader);\r
+       mh->graphiclen=_mm_read_I_UWORD(modreader);\r
+\r
+       /* have we ended abruptly? */\r
+       if (_mm_eof(modreader)) {\r
+               _mm_errno=MMERR_LOADING_HEADER;\r
+               return 0;\r
+       }\r
+\r
+       /* any orders? */\r
+       if(mh->ordernum==255) {\r
+               _mm_errno=MMERR_LOADING_PATTERN;\r
+               return 0;\r
+       }\r
+\r
+       /* now we fill */\r
+       of.modtype=strdup(GDM_Version);\r
+       of.modtype[18]=mh->majorver+'0';\r
+       of.modtype[20]=mh->minorver/10+'0';\r
+       of.modtype[21]=mh->minorver%10+'0';\r
+       of.songname=DupStr(mh->songname,32,0);\r
+       of.numpat=mh->patternnum+1;\r
+       of.reppos=0;\r
+       of.numins=of.numsmp=mh->samnum+1;\r
+       of.initspeed=mh->mastertempo;\r
+       of.inittempo=mh->masterbpm;\r
+       of.initvolume=mh->mastervol<<1;\r
+       of.flags|=UF_S3MSLIDES | UF_PANNING;\r
+       /* XXX whenever possible, we should try to determine the original format.\r
+          Here we assume it was S3M-style wrt bpmlimit... */\r
+       of.bpmlimit = 32;\r
+\r
+       /* read the order data */\r
+       if (!AllocPositions(mh->ordernum+1)) {\r
+               _mm_errno=MMERR_OUT_OF_MEMORY;\r
+               return 0;\r
+       }\r
+\r
+       _mm_fseek(modreader,mh->orderloc,SEEK_SET);\r
+       for (i=0;i<mh->ordernum+1;i++)\r
+               of.positions[i]=_mm_read_UBYTE(modreader);\r
+\r
+       of.numpos=0;\r
+       for (i=0;i<mh->ordernum+1;i++) {\r
+               int order=of.positions[i];\r
+               if(order==255) order=LAST_PATTERN;\r
+               of.positions[of.numpos]=order;\r
+               if (of.positions[i]<254) of.numpos++;\r
+       }\r
+\r
+       /* have we ended abruptly yet? */\r
+       if (_mm_eof(modreader)) {\r
+               _mm_errno=MMERR_LOADING_HEADER;\r
+               return 0;\r
+       }\r
+\r
+       /* time to load the samples */\r
+       if (!AllocSamples()) {\r
+               _mm_errno=MMERR_OUT_OF_MEMORY;\r
+               return 0;\r
+       }\r
+\r
+       q=of.samples;\r
+       position=mh->samdata;\r
+\r
+       /* seek to instrument position */\r
+       _mm_fseek(modreader,mh->samhead,SEEK_SET);\r
+\r
+       for (i=0;i<of.numins;i++) {\r
+               /* load sample info */\r
+               _mm_read_UBYTES(s.sampname,32,modreader);\r
+               _mm_read_UBYTES(s.filename,12,modreader);\r
+               s.ems=_mm_read_UBYTE(modreader);\r
+               s.length=_mm_read_I_ULONG(modreader);\r
+               s.loopbeg=_mm_read_I_ULONG(modreader);\r
+               s.loopend=_mm_read_I_ULONG(modreader);\r
+               s.flags=_mm_read_UBYTE(modreader);\r
+               s.c4spd=_mm_read_I_UWORD(modreader);\r
+               s.vol=_mm_read_UBYTE(modreader);\r
+               s.pan=_mm_read_UBYTE(modreader);\r
+\r
+               if (_mm_eof(modreader)) {\r
+                       _mm_errno=MMERR_LOADING_SAMPLEINFO;\r
+                       return 0;\r
+               }\r
+               q->samplename=DupStr(s.sampname,32,0);\r
+               q->speed=s.c4spd;\r
+               q->length=s.length;\r
+               q->loopstart=s.loopbeg;\r
+               q->loopend=s.loopend;\r
+               q->volume=s.vol;\r
+               q->panning=s.pan;\r
+               q->seekpos=position;\r
+\r
+               position +=s.length;\r
+\r
+               if (s.flags&1)\r
+                       q->flags |=SF_LOOP;\r
+               if (s.flags&2)\r
+                       q->flags |=SF_16BITS;\r
+               if (s.flags&16)\r
+                       q->flags |=SF_STEREO;\r
+               q++;\r
+       }\r
+\r
+       /* set the panning */\r
+       for (i=x=0;i<32;i++) {\r
+               of.panning[i]=mh->pantable[i];\r
+               if (!of.panning[i])\r
+                       of.panning[i]=PAN_LEFT;\r
+               else if (of.panning[i]==8)\r
+                       of.panning[i]=PAN_CENTER;\r
+               else if (of.panning[i]==15)\r
+                       of.panning[i]=PAN_RIGHT;\r
+               else if (of.panning[i]==16)\r
+                       of.panning[i]=PAN_SURROUND;\r
+               else if (of.panning[i]==255)\r
+                       of.panning[i]=128;\r
+               else\r
+                       of.panning[i]<<=3;\r
+               if (mh->pantable[i]!=255)\r
+                       x=i;\r
+       }\r
+\r
+       of.numchn=x+1;\r
+       if (of.numchn<1)\r
+               of.numchn=1;    /* for broken counts */\r
+\r
+       /* load the pattern info */\r
+       of.numtrk=of.numpat*of.numchn;\r
+\r
+       /* jump to patterns */\r
+       _mm_fseek(modreader,mh->patternloc,SEEK_SET);\r
+\r
+       if (!AllocTracks()) {\r
+               _mm_errno=MMERR_OUT_OF_MEMORY;\r
+               return 0;\r
+       }\r
+\r
+       if (!AllocPatterns()) {\r
+               _mm_errno=MMERR_OUT_OF_MEMORY;\r
+               return 0;\r
+       }\r
+\r
+       for (i=track=0;i<of.numpat;i++) {\r
+               if (!GDM_ReadPattern()) {\r
+                       _mm_errno=MMERR_LOADING_PATTERN;\r
+                       return 0;\r
+               }\r
+               for (u=0;u<of.numchn;u++,track++) {\r
+                       of.tracks[track]=GDM_ConvertTrack(&gdmbuf[u<<6]);\r
+                       if (!of.tracks[track]) {\r
+                               _mm_errno=MMERR_LOADING_TRACK;\r
+                               return 0;\r
+                       }\r
+               }\r
+       }\r
+       return 1;\r
+}\r
+\r
+CHAR *GDM_LoadTitle(void)\r
+{\r
+       CHAR s[32];\r
+\r
+       _mm_fseek(modreader,4,SEEK_SET);\r
+       if (!_mm_read_UBYTES(s,32,modreader)) return NULL;\r
+\r
+       return DupStr(s,28,0);\r
+}\r
+\r
+MIKMODAPI MLOADER load_gdm=\r
+{\r
+       NULL,\r
+       "GDM",\r
+       "GDM (General DigiMusic)",\r
+       GDM_Init,\r
+       GDM_Test,\r
+       GDM_Load,\r
+       GDM_Cleanup,\r
+       GDM_LoadTitle\r
+};\r
+\r
+/* ex:set ts=4: */\r