Mercurial > ~darius > hgwebdir.cgi > mikmod
view playercode/load_stm.c @ 11:d5cb2cfc8eca
Initial revision
author | darius |
---|---|
date | Fri, 23 Jan 1998 16:05:11 +0000 |
parents | 5d614bcc4287 |
children |
line wrap: on
line source
/* 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 };