--- /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_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