Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_stm.c
diff --git a/apps/plugins/mikmod/loaders/load_stm.c b/apps/plugins/mikmod/loaders/load_stm.c
new file mode 100644 (file)
index 0000000..6a60af0
--- /dev/null
@@ -0,0 +1,375 @@
+/*     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_stm.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
+\r
+  Screamtracker 2 (STM) module loader\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
+/*========== Module structure */\r
+\r
+/* sample information */\r
+typedef struct STMSAMPLE {\r
+       CHAR  filename[12];\r
+       UBYTE unused;       /* 0x00 */\r
+       UBYTE instdisk;     /* Instrument disk */\r
+       UWORD reserved;\r
+       UWORD length;       /* Sample length */\r
+       UWORD loopbeg;      /* Loop start point */\r
+       UWORD loopend;      /* Loop end point */\r
+       UBYTE volume;       /* Volume */\r
+       UBYTE reserved2;\r
+       UWORD c2spd;        /* Good old c2spd */\r
+       ULONG reserved3;\r
+       UWORD isa;\r
+} STMSAMPLE;\r
+\r
+/* header */\r
+typedef struct STMHEADER {\r
+       CHAR  songname[20];\r
+       CHAR  trackername[8]; /* !Scream! for ST 2.xx  */\r
+       UBYTE unused;         /* 0x1A  */\r
+       UBYTE filetype;       /* 1=song, 2=module */\r
+       UBYTE ver_major;\r
+       UBYTE ver_minor;\r
+       UBYTE inittempo;      /* initspeed= stm inittempo>>4  */\r
+       UBYTE numpat;         /* number of patterns  */\r
+       UBYTE globalvol;     \r
+       UBYTE reserved[13];\r
+       STMSAMPLE sample[31]; /* STM sample data */\r
+       UBYTE patorder[128];  /* Docs say 64 - actually 128 */\r
+} STMHEADER;\r
+\r
+typedef struct STMNOTE {\r
+       UBYTE note,insvol,volcmd,cmdinf;\r
+} STMNOTE;\r
+\r
+/*========== Loader variables */\r
+\r
+static STMNOTE *stmbuf = NULL;\r
+static STMHEADER *mh = NULL;\r
+\r
+/* tracker identifiers */\r
+static CHAR* STM_Version[STM_NTRACKERS] = {\r
+       "Screamtracker 2",\r
+       "Converted by MOD2STM (STM format)",\r
+       "Wuzamod (STM format)"\r
+};\r
+\r
+/*========== Loader code */\r
+\r
+BOOL STM_Test(void)\r
+{\r
+       UBYTE str[44];\r
+       int t;\r
+\r
+       _mm_fseek(modreader,20,SEEK_SET);\r
+       _mm_read_UBYTES(str,44,modreader);\r
+       if(str[9]!=2) return 0; /* STM Module = filetype 2 */\r
+\r
+       /* Prevent false positives for S3M files */\r
+       if(!memcmp(str+40,"SCRM",4))\r
+               return 0;\r
+       \r
+       for (t=0;t<STM_NTRACKERS;t++)\r
+               if(!memcmp(str,STM_Signatures[t],8))\r
+                       return 1;\r
+\r
+       return 0;\r
+}\r
+\r
+BOOL STM_Init(void)\r
+{\r
+       if(!(mh=(STMHEADER*)_mm_malloc(sizeof(STMHEADER)))) return 0;\r
+       if(!(stmbuf=(STMNOTE*)_mm_calloc(64U*4,sizeof(STMNOTE)))) return 0;\r
+\r
+       return 1;\r
+}\r
+\r
+static void STM_Cleanup(void)\r
+{\r
+       _mm_free(mh);\r
+       _mm_free(stmbuf);\r
+}\r
+\r
+static void STM_ConvertNote(STMNOTE *n)\r
+{\r
+       UBYTE note,ins,vol,cmd,inf;\r
+\r
+       /* extract the various information from the 4 bytes that make up a note */\r
+       note = n->note;\r
+       ins  = n->insvol>>3;\r
+       vol  = (n->insvol&7)+((n->volcmd&0x70)>>1);\r
+       cmd  = n->volcmd&15;\r
+       inf  = n->cmdinf;\r
+\r
+       if((ins)&&(ins<32)) UniInstrument(ins-1);\r
+\r
+       /* special values of [SBYTE0] are handled here \r
+          we have no idea if these strange values will ever be encountered.\r
+          but it appears as those stms sound correct. */\r
+       if((note==254)||(note==252)) {\r
+               UniPTEffect(0xc,0); /* note cut */\r
+               n->volcmd|=0x80;\r
+       } else\r
+               /* if note < 251, then all three bytes are stored in the file */\r
+         if(note<251) UniNote((((note>>4)+2)*OCTAVE)+(note&0xf));\r
+\r
+       if((!(n->volcmd&0x80))&&(vol<65)) UniPTEffect(0xc,vol);\r
+       if(cmd!=255)\r
+               switch(cmd) {\r
+                       case 1: /* Axx set speed to xx */\r
+                               UniPTEffect(0xf,inf>>4);\r
+                               break;\r
+                       case 2: /* Bxx position jump */\r
+                               UniPTEffect(0xb,inf);\r
+                               break;\r
+                       case 3: /* Cxx patternbreak to row xx */\r
+                               UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf));\r
+                               break;\r
+                       case 4: /* Dxy volumeslide */\r
+                               UniEffect(UNI_S3MEFFECTD,inf);\r
+                               break;\r
+                       case 5: /* Exy toneslide down */\r
+                               UniEffect(UNI_S3MEFFECTE,inf);\r
+                               break;\r
+                       case 6: /* Fxy toneslide up */\r
+                               UniEffect(UNI_S3MEFFECTF,inf);\r
+                               break;\r
+                       case 7: /* Gxx Tone portamento,speed xx */\r
+                               UniPTEffect(0x3,inf);\r
+                               break;\r
+                       case 8: /* Hxy vibrato */\r
+                               UniPTEffect(0x4,inf);\r
+                               break;\r
+                       case 9: /* Ixy tremor, ontime x, offtime y */\r
+                               UniEffect(UNI_S3MEFFECTI,inf);\r
+                       break;\r
+                       case 0:         /* protracker arpeggio */\r
+                               if(!inf) break;\r
+                               /* fall through */\r
+                       case 0xa:       /* Jxy arpeggio */\r
+                               UniPTEffect(0x0,inf);\r
+                               break;\r
+                       case 0xb:       /* Kxy Dual command H00 & Dxy */\r
+                               UniPTEffect(0x4,0);\r
+                               UniEffect(UNI_S3MEFFECTD,inf);\r
+                               break;\r
+                       case 0xc:       /* Lxy Dual command G00 & Dxy */\r
+                               UniPTEffect(0x3,0);\r
+                               UniEffect(UNI_S3MEFFECTD,inf);\r
+                               break;\r
+                       /* Support all these above, since ST2 can LOAD these values but can\r
+                          actually only play up to J - and J is only half-way implemented\r
+                          in ST2 */\r
+                       case 0x18:      /* Xxx amiga panning command 8xx */\r
+                               UniPTEffect(0x8,inf);\r
+                               of.flags |= UF_PANNING;\r
+                               break;\r
+               }\r
+}\r
+\r
+static UBYTE *STM_ConvertTrack(STMNOTE *n)\r
+{\r
+       int t;\r
+\r
+       UniReset();\r
+       for(t=0;t<64;t++) {\r
+               STM_ConvertNote(n);\r
+               UniNewline();\r
+               n+=of.numchn;\r
+       }\r
+       return UniDup();\r
+}\r
+\r
+static BOOL STM_LoadPatterns(void)\r
+{\r
+       int t,s,tracks=0;\r
+\r
+       if(!AllocPatterns()) return 0;\r
+       if(!AllocTracks()) return 0;\r
+\r
+       /* Allocate temporary buffer for loading and converting the patterns */\r
+       for(t=0;t<of.numpat;t++) {\r
+               for(s=0;s<(64U*of.numchn);s++) {\r
+                       stmbuf[s].note   = _mm_read_UBYTE(modreader);\r
+                       stmbuf[s].insvol = _mm_read_UBYTE(modreader);\r
+                       stmbuf[s].volcmd = _mm_read_UBYTE(modreader);\r
+                       stmbuf[s].cmdinf = _mm_read_UBYTE(modreader);\r
+               }\r
+\r
+               if(_mm_eof(modreader)) {\r
+                       _mm_errno = MMERR_LOADING_PATTERN;\r
+                       return 0;\r
+               }\r
+\r
+               for(s=0;s<of.numchn;s++)\r
+                       if(!(of.tracks[tracks++]=STM_ConvertTrack(stmbuf+s))) return 0;\r
+       }\r
+       return 1;\r
+}\r
+\r
+BOOL STM_Load(BOOL curious)\r
+{\r
+       int t; \r
+       ULONG MikMod_ISA; /* We must generate our own ISA, it's not stored in stm */\r
+       SAMPLE *q;\r
+       (void)curious;\r
+\r
+       /* try to read stm header */\r
+       _mm_read_string(mh->songname,20,modreader);\r
+       _mm_read_string(mh->trackername,8,modreader);\r
+       mh->unused      =_mm_read_UBYTE(modreader);\r
+       mh->filetype    =_mm_read_UBYTE(modreader);\r
+       mh->ver_major   =_mm_read_UBYTE(modreader);\r
+       mh->ver_minor   =_mm_read_UBYTE(modreader);\r
+       mh->inittempo   =_mm_read_UBYTE(modreader);\r
+       if(!mh->inittempo) {\r
+               _mm_errno=MMERR_NOT_A_MODULE;\r
+               return 0;\r
+       }\r
+       mh->numpat      =_mm_read_UBYTE(modreader);\r
+       mh->globalvol   =_mm_read_UBYTE(modreader);\r
+       _mm_read_UBYTES(mh->reserved,13,modreader);\r
+\r
+       for(t=0;t<31;t++) {\r
+               STMSAMPLE *s=&mh->sample[t];    /* STM sample data */\r
+\r
+               _mm_read_string(s->filename,12,modreader);\r
+               s->unused   =_mm_read_UBYTE(modreader);\r
+               s->instdisk =_mm_read_UBYTE(modreader);\r
+               s->reserved =_mm_read_I_UWORD(modreader);\r
+               s->length   =_mm_read_I_UWORD(modreader);\r
+               s->loopbeg  =_mm_read_I_UWORD(modreader);\r
+               s->loopend  =_mm_read_I_UWORD(modreader);\r
+               s->volume   =_mm_read_UBYTE(modreader);\r
+               s->reserved2=_mm_read_UBYTE(modreader);\r
+               s->c2spd    =_mm_read_I_UWORD(modreader);\r
+               s->reserved3=_mm_read_I_ULONG(modreader);\r
+               s->isa      =_mm_read_I_UWORD(modreader);\r
+       }\r
+       _mm_read_UBYTES(mh->patorder,128,modreader);\r
+\r
+       if(_mm_eof(modreader)) {\r
+               _mm_errno = MMERR_LOADING_HEADER;\r
+               return 0;\r
+       }\r
+\r
+       /* set module variables */\r
+       for(t=0;t<STM_NTRACKERS;t++)\r
+               if(!memcmp(mh->trackername,STM_Signatures[t],8)) break;\r
+       of.modtype   = strdup(STM_Version[t]);\r
+       of.songname  = DupStr(mh->songname,20,1); /* make a cstr of songname */\r
+       of.numpat    = mh->numpat;\r
+       of.inittempo = 125;                     /* mh->inittempo+0x1c; */\r
+       of.initspeed = mh->inittempo>>4;\r
+       of.numchn    = 4;                       /* get number of channels */\r
+       of.reppos    = 0;\r
+       of.flags    |= UF_S3MSLIDES;\r
+       of.bpmlimit  = 32;\r
+\r
+       t=0;\r
+       if(!AllocPositions(0x80)) return 0;\r
+       /* 99 terminates the patorder list */\r
+       while((mh->patorder[t]<=99)&&(mh->patorder[t]<mh->numpat)) {\r
+               of.positions[t]=mh->patorder[t];\r
+               t++;\r
+       }\r
+       if(mh->patorder[t]<=99) t++;\r
+       of.numpos=t;\r
+       of.numtrk=of.numpat*of.numchn;\r
+       of.numins=of.numsmp=31;\r
+\r
+       if(!AllocSamples()) return 0;\r
+       if(!STM_LoadPatterns()) return 0;\r
+       MikMod_ISA=_mm_ftell(modreader);\r
+       MikMod_ISA=(MikMod_ISA+15)&0xfffffff0;  /* normalize */\r
+\r
+       for(q=of.samples,t=0;t<of.numsmp;t++,q++) {\r
+               /* load sample info */\r
+               q->samplename = DupStr(mh->sample[t].filename,12,1);\r
+               q->speed      = (mh->sample[t].c2spd * 8363) / 8448;\r
+               q->volume     = mh->sample[t].volume;\r
+               q->length     = mh->sample[t].length;\r
+               if (/*(!mh->sample[t].volume)||*/(q->length==1)) q->length=0;\r
+               q->loopstart  = mh->sample[t].loopbeg;\r
+               q->loopend    = mh->sample[t].loopend;\r
+               q->seekpos    = MikMod_ISA;\r
+\r
+               MikMod_ISA+=q->length;\r
+               MikMod_ISA=(MikMod_ISA+15)&0xfffffff0;  /* normalize */\r
+\r
+               /* contrary to the STM specs, sample data is signed */\r
+               q->flags = SF_SIGNED;\r
+\r
+               if(q->loopend && q->loopend != 0xffff)\r
+                               q->flags|=SF_LOOP;\r
+       }\r
+       return 1;\r
+}\r
+\r
+CHAR *STM_LoadTitle(void)\r
+{\r
+       CHAR s[20];\r
+\r
+       _mm_fseek(modreader,0,SEEK_SET);\r
+       if(!_mm_read_UBYTES(s,20,modreader)) return NULL;\r
+\r
+       return(DupStr(s,20,1));\r
+}\r
+\r
+/*========== Loader information */\r
+\r
+MIKMODAPI MLOADER load_stm={\r
+       NULL,\r
+       "STM",\r
+       "STM (Scream Tracker)",\r
+       STM_Init,\r
+       STM_Test,\r
+       STM_Load,\r
+       STM_Cleanup,\r
+       STM_LoadTitle\r
+};\r
+\r
+\r
+/* ex:set ts=4: */\r