--- /dev/null
+/* 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