diff playercode/load_stm.c @ 4:5d614bcc4287

Initial entry of mikmod into the CVS tree.
author darius
date Fri, 23 Jan 1998 16:05:08 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/load_stm.c	Fri Jan 23 16:05:08 1998 +0000
@@ -0,0 +1,374 @@
+/*
+
+ Name: LOAD_STM.C
+
+ Description:
+ ScreamTracker 2 (STM) module Loader - Version 1.oOo Release 2 
+ A Coding Nightmare by Rao and Air Richter of HaRDCoDE
+ You can now play all of those wonderful old C.C. Catch STM's!
+
+ Portability:
+ All systems - all compilers (hopefully)
+
+ If this module is found to not be portable to any particular platform,
+ please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
+ more information on contacting the author). 
+
+*/
+
+#include <string.h>
+#include <ctype.h>
+#include "mikmod.h"
+
+
+typedef struct STMNOTE
+{   UBYTE note,insvol,volcmd,cmdinf;
+} STMNOTE;
+
+
+// Raw STM sampleinfo struct:
+
+typedef struct STMSAMPLE
+{  CHAR  filename[12];   // Can't have long comments - just filename comments :)
+   UBYTE unused;         // 0x00
+   UBYTE instdisk;       // Instrument disk
+   UWORD reserved;       // ISA in memory when in ST 2
+   UWORD length;         // Sample length
+   UWORD loopbeg;        // Loop start point
+   UWORD loopend;        // Loop end point
+   UBYTE volume;         // Volume
+   UBYTE reserved2;      // More reserved crap
+   UWORD c2spd;          // Good old c2spd
+   UBYTE reserved3[4];   // Yet more of PSi's reserved crap
+   UWORD isa;            // Internal Segment Address ->
+                         //    contrary to the tech specs, this is NOT actually
+                         //    written to the stm file.
+} STMSAMPLE;
+
+// Raw STM header struct:
+
+typedef struct STMHEADER
+{  CHAR  songname[20];
+   CHAR  trackername[8];   // !SCREAM! for ST 2.xx 
+   UBYTE unused;           // 0x1A 
+   UBYTE filetype;         // 1=song, 2=module (only 2 is supported, of course) :) 
+   UBYTE ver_major;        // Like 2 
+   UBYTE ver_minor;        // "ditto" 
+   UBYTE inittempo;        // initspeed= stm inittempo>>4 
+   UBYTE  numpat;          // number of patterns 
+   UBYTE   globalvol;      // <- WoW! a RiGHT TRiANGLE =8*) 
+   UBYTE    reserved[13];  // More of PSi's internal crap 
+   STMSAMPLE sample[31];   // STM sample data
+   UBYTE patorder[128];    // Docs say 64 - actually 128
+} STMHEADER;
+
+
+static STMNOTE *stmbuf = NULL;
+static STMHEADER *mh = NULL;
+
+static CHAR  STM_Version[] = "Screamtracker 2";
+
+
+BOOL STM_Test(void)
+{
+   UBYTE str[9],filetype;
+
+   _mm_fseek(modfp,21,SEEK_SET);
+   _mm_read_UBYTES(str,9,modfp);
+   filetype = _mm_read_UBYTE(modfp);
+   if(!memcmp(str,"!SCREAM!",8) || (filetype!=2)) // STM Module = filetype 2
+      return 0;
+   return 1;
+}
+
+
+
+BOOL STM_Init(void)
+{
+    if(!(mh=(STMHEADER *)_mm_calloc(1,sizeof(STMHEADER)))) return 0;
+    if(!(stmbuf=(STMNOTE *)_mm_calloc(64U*of.numchn,sizeof(STMNOTE)))) return 0;
+
+    return 1;
+}
+
+void STM_Cleanup(void)
+{
+    if(mh!=NULL) free(mh);
+    if(stmbuf!=NULL) free(stmbuf);
+
+    stmbuf = NULL;
+    mh = NULL;
+}
+
+
+
+void STM_ConvertNote(STMNOTE *n)
+{
+    UBYTE note,ins,vol,cmd,inf;
+
+    // extract the various information from the 4 bytes that
+    //  make up a single note
+
+    note = n->note;
+    ins  = n->insvol>>3;
+    vol  = (n->insvol&7)+(n->volcmd>>1);
+    cmd  = n->volcmd&15;
+    inf  = n->cmdinf;
+
+    if(ins!=0 && ins<32)  UniInstrument(ins-1);
+
+  // special values of [SBYTE0] are handled here ->
+  // we have no idea if these strange values will ever be encountered.
+  // but it appears as though stms sound correct.
+  if(note==254 || note==252) UniPTEffect(0xc,0); // <- note off command (???)
+     else
+  // if note < 251, then all three bytes are stored in the file
+  if(note<251) UniNote((((note>>4)+2)*12)+(note&0xf));      // <- normal note and up the octave by two
+
+    if(vol<65)
+        UniPTEffect(0xc,vol);
+
+    if(cmd!=255){
+        switch(cmd){
+
+            case 1:                 // Axx set speed to xx and add 0x1c to fix StoOoPiD STM 2.x
+                UniPTEffect(0xf,inf>>4);
+                break;
+
+            case 2:                 // Bxx position jump
+                UniPTEffect(0xb,inf);
+                break;
+
+            case 3:                 // Cxx patternbreak to row xx
+                UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf));
+                break;
+
+            case 4:                 // Dxy volumeslide
+                UniWrite(UNI_S3MEFFECTD);
+                UniWrite(inf);
+                break;
+
+            case 5:                 // Exy toneslide down
+                UniWrite(UNI_S3MEFFECTE);
+                UniWrite(inf);
+                break;
+
+            case 6:                 // Fxy toneslide up
+                UniWrite(UNI_S3MEFFECTF);
+                UniWrite(inf);
+                break;
+
+            case 7:                 // Gxx Tone portamento,speed xx
+                UniPTEffect(0x3,inf);
+                break;
+
+            case 8:                 // Hxy vibrato
+                UniPTEffect(0x4,inf);
+                break;
+
+            case 9:                 // Ixy tremor, ontime x, offtime y
+                UniWrite(UNI_S3MEFFECTI);
+                UniWrite(inf);
+                break;
+
+            case 0xa:               // Jxy arpeggio
+                UniPTEffect(0x0,inf);
+                break;
+
+            case 0xb:               // Kxy Dual command H00 & Dxy
+                UniPTEffect(0x4,0);
+                UniWrite(UNI_S3MEFFECTD);
+                UniWrite(inf);
+                break;
+
+            case 0xc:               // Lxy Dual command G00 & Dxy
+                UniPTEffect(0x3,0);
+                UniWrite(UNI_S3MEFFECTD);
+                UniWrite(inf);
+                break;
+
+         // Support all these above, since ST2 can LOAD these values
+         // but can actually only play up to J - and J is only
+         // half-way implemented in ST2
+
+            case 0x18:      // Xxx amiga command 8xx - What the hell, support panning. :)
+                UniPTEffect(0x8,inf);
+                break;
+        }
+    }
+}
+
+
+UBYTE *STM_ConvertTrack(STMNOTE *n)
+{
+    int t;
+
+    UniReset();
+    for(t=0; t<64; t++)
+    {   STM_ConvertNote(n);
+        UniNewline();
+        n+=of.numchn;
+    }
+    return UniDup();
+}
+
+
+BOOL STM_LoadPatterns(void)
+{
+    int t,s,tracks=0;
+
+    if(!AllocPatterns()) return 0;
+    if(!AllocTracks()) return 0;
+
+    // Allocate temporary buffer for loading
+    // and converting the patterns
+
+    for(t=0; t<of.numpat; t++)
+    {   for(s=0; s<(64U*of.numchn); s++)
+        {   stmbuf[s].note   = _mm_read_UBYTE(modfp);
+            stmbuf[s].insvol = _mm_read_UBYTE(modfp);
+            stmbuf[s].volcmd = _mm_read_UBYTE(modfp);
+            stmbuf[s].cmdinf = _mm_read_UBYTE(modfp);
+        }
+
+        if(feof(modfp))
+        {   _mm_errno = MMERR_LOADING_PATTERN;
+            return 0;
+        }
+
+        for(s=0;s<of.numchn;s++)
+        {   if(!(of.tracks[tracks++]=STM_ConvertTrack(stmbuf+s))) return 0;
+        }
+    }
+
+    return 1;
+}
+
+
+
+BOOL STM_Load(void)
+{
+    int        t; 
+    ULONG      MikMod_ISA; // We MUST generate our own ISA - NOT stored in the stm
+    SAMPLE     *q;
+
+    // try to read stm header
+
+    _mm_read_string(mh->songname,20,modfp);
+    _mm_read_string(mh->trackername,8,modfp);
+    mh->unused      =_mm_read_UBYTE(modfp);
+    mh->filetype    =_mm_read_UBYTE(modfp);
+    mh->ver_major   =_mm_read_UBYTE(modfp);
+    mh->ver_minor   =_mm_read_UBYTE(modfp);
+    mh->inittempo   =_mm_read_UBYTE(modfp);
+    mh->numpat      =_mm_read_UBYTE(modfp);
+    mh->globalvol   =_mm_read_UBYTE(modfp);
+    _mm_read_UBYTES(mh->reserved,13,modfp);
+
+    for(t=0;t<31;t++)
+    {   STMSAMPLE *s = &mh->sample[t];  // STM sample data
+
+        _mm_read_string(s->filename,12,modfp);
+        s->unused   =_mm_read_UBYTE(modfp);
+        s->instdisk =_mm_read_UBYTE(modfp);
+        s->reserved =_mm_read_I_UWORD(modfp);
+        s->length   =_mm_read_I_UWORD(modfp);
+        s->loopbeg  =_mm_read_I_UWORD(modfp);
+        s->loopend  =_mm_read_I_UWORD(modfp);
+        s->volume   =_mm_read_UBYTE(modfp);
+        s->reserved2=_mm_read_UBYTE(modfp);
+        s->c2spd    =_mm_read_I_UWORD(modfp);
+        _mm_read_UBYTES(s->reserved3,4,modfp);
+        s->isa      =_mm_read_I_UWORD(modfp);
+    }
+    _mm_read_UBYTES(mh->patorder,128,modfp);
+
+    if(feof(modfp))
+    {   _mm_errno = MMERR_LOADING_HEADER;
+        return 0;
+    }
+
+    // set module variables
+
+    of.modtype   = strdup(STM_Version);
+    of.songname  = DupStr(mh->songname,20); // make a cstr of songname
+    of.numpat    = mh->numpat;
+    of.inittempo = 125;                     // mh->inittempo+0x1c;
+    of.initspeed = mh->inittempo>>4;
+    of.numchn    = 4;                       // get number of channels
+
+    t=0;
+    if(!AllocPositions(0x80)) return 0;
+    while(mh->patorder[t]!=99)    // 99 terminates the patorder list
+    {   of.positions[t] = mh->patorder[t];
+        t++;
+    }
+    of.numpos = --t;
+    of.numtrk = of.numpat*of.numchn;
+
+    // Finally, init the sampleinfo structures
+    of.numins = of.numsmp = 31;    // always this
+
+    if(!AllocSamples()) return 0;
+    if(!STM_LoadPatterns()) return 0;
+
+    q = of.samples;
+
+    MikMod_ISA = _mm_ftell(modfp);
+    MikMod_ISA = (MikMod_ISA+15)&0xfffffff0;
+
+    for(t=0; t<of.numins; t++)
+    {   // load sample info
+
+        q->samplename = DupStr(mh->sample[t].filename,12);
+        q->speed      = mh->sample[t].c2spd;
+        q->volume     = mh->sample[t].volume;
+        q->length     = mh->sample[t].length;
+        if (!mh->sample[t].volume || q->length==1 ) q->length = 0; // if vol = 0 or length = 1, then no sample
+        q->loopstart  = mh->sample[t].loopbeg;
+        q->loopend    = mh->sample[t].loopend;
+        q->seekpos    = MikMod_ISA;
+
+        MikMod_ISA+=q->length;
+        MikMod_ISA = (MikMod_ISA+15)&0xfffffff0;
+
+        // Once again, contrary to the STM specs, all the sample data is
+        // actually SIGNED! Sheesh
+
+        q->flags = SF_SIGNED;
+
+        if(mh->sample[t].loopend>0 && mh->sample[t].loopend!=0xffff) q->flags|=SF_LOOP;
+
+        // fix replen if repend>length
+
+        if(q->loopend > q->length) q->loopend = q->length;
+
+        q++;
+    }
+
+    return 1;
+}
+
+
+CHAR *STM_LoadTitle(void)
+{
+    CHAR s[20];
+
+    _mm_fseek(modfp,0,SEEK_SET);
+    if(!fread(s,20,1,modfp)) return NULL;
+
+    return(DupStr(s,20));
+}
+
+
+MLOADER load_stm =
+{   NULL,
+    "STM",
+    "Portable STM Loader - V 1.2",
+    STM_Init,
+    STM_Test,
+    STM_Load,
+    STM_Cleanup,
+    STM_LoadTitle
+};
+