/* * This source code is public domain. * * Authors: Olivier Lapicque */ #include "libmodplug.h" #pragma pack(1) typedef struct tagSTMNOTE { BYTE note; BYTE insvol; BYTE volcmd; BYTE cmdinf; } STMNOTE; // Raw STM sampleinfo struct: typedef struct tagSTMSAMPLE { CHAR filename[14]; // Can't have long comments - just filename comments :) WORD reserved; // ISA in memory when in ST 2 WORD length; // Sample length WORD loopbeg; // Loop start point WORD loopend; // Loop end point BYTE volume; // Volume BYTE reserved2; // More reserved crap WORD c2spd; // Good old c2spd BYTE reserved3[6]; // Yet more of PSi's reserved crap } STMSAMPLE; // Raw STM header struct: typedef struct tagSTMHEADER { char songname[20]; // changed from CHAR char trackername[8]; // !SCREAM! for ST 2.xx // changed from CHAR CHAR unused; // 0x1A CHAR filetype; // 1=song, 2=module (only 2 is supported, of course) :) CHAR ver_major; // Like 2 CHAR ver_minor; // "ditto" BYTE inittempo; // initspeed= stm inittempo>>4 BYTE numpat; // number of patterns BYTE globalvol; // <- WoW! a RiGHT TRiANGLE =8*) BYTE reserved[13]; // More of PSi's internal crap STMSAMPLE sample[31]; // STM sample data BYTE patorder[128]; // Docs say 64 - actually 128 } STMHEADER; #pragma pack() BOOL CSoundFile_ReadSTM(CSoundFile *_this, const BYTE *lpStream, DWORD dwMemLength) //--------------------------------------------------------------- { const STMHEADER *phdr = (STMHEADER *)lpStream; DWORD dwMemPos = 0; UINT nPatterns; UINT j; if ((!lpStream) || (dwMemLength < sizeof(STMHEADER))) return FALSE; if ((phdr->filetype != 2) || (phdr->unused != 0x1A) || ((SDL_strncmp(phdr->trackername, "!Scream!", 8)) && (SDL_strncmp(phdr->trackername, "BMOD2STM", 8)))) return FALSE; // Read STM header _this->m_nType = MOD_TYPE_STM; _this->m_nSamples = 31; _this->m_nChannels = 4; _this->m_nInstruments = 0; _this->m_nMinPeriod = 64; _this->m_nMaxPeriod = 0x7FFF; _this->m_nDefaultSpeed = phdr->inittempo >> 4; if (_this->m_nDefaultSpeed < 1) _this->m_nDefaultSpeed = 1; _this->m_nDefaultTempo = 125; _this->m_nDefaultGlobalVolume = phdr->globalvol << 2; if (_this->m_nDefaultGlobalVolume > 256) _this->m_nDefaultGlobalVolume = 256; SDL_memcpy(_this->Order, phdr->patorder, 128); // Setting up channels for (j=0; j<4; j++) { _this->ChnSettings[j].dwFlags = 0; _this->ChnSettings[j].nVolume = 64; _this->ChnSettings[j].nPan = (j & 1) ? 0x40 : 0xC0; } // Reading samples for (j=0; j<31; j++) { MODINSTRUMENT *pIns = &_this->Ins[j+1]; const STMSAMPLE *pStm = &phdr->sample[j]; // STM sample data pIns->nC4Speed = bswapLE16(pStm->c2spd); pIns->nGlobalVol = 64; pIns->nVolume = pStm->volume << 2; if (pIns->nVolume > 256) pIns->nVolume = 256; pIns->nLength = bswapLE16(pStm->length); if ((pIns->nLength < 4) || (!pIns->nVolume)) pIns->nLength = 0; pIns->nLoopStart = bswapLE16(pStm->loopbeg); pIns->nLoopEnd = bswapLE16(pStm->loopend); if ((pIns->nLoopEnd > pIns->nLoopStart) && (pIns->nLoopEnd != 0xFFFF)) pIns->uFlags |= CHN_LOOP; } dwMemPos = sizeof(STMHEADER); for (j=0; jOrder[j] >= 99) _this->Order[j] = 0xFF; nPatterns = phdr->numpat; if (nPatterns > MAX_PATTERNS) nPatterns = MAX_PATTERNS; for (j=0; j dwMemLength) return TRUE; _this->PatternSize[j] = 64; if ((_this->Patterns[j] = CSoundFile_AllocatePattern(64, _this->m_nChannels)) == NULL) return TRUE; m = _this->Patterns[j]; p = (const STMNOTE *)(lpStream + dwMemPos); for (n=0; n<64*4; n++, p++, m++) { UINT note,ins,vol,cmd; // extract the various information from the 4 bytes that // make up a single note note = p->note; ins = p->insvol >> 3; vol = (p->insvol & 0x07) + (p->volcmd >> 1); cmd = p->volcmd & 0x0F; if ((ins) && (ins < 32)) m->instr = ins; // 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 == 0xFE) || (note == 0xFC)) m->note = 0xFE; else // if note < 251, then all three bytes are stored in the file if (note < 0xFC) m->note = (note >> 4)*12 + (note&0xf) + 37; if (vol <= 64) { m->volcmd = VOLCMD_VOLUME; m->vol = vol; } m->param = p->cmdinf; switch(cmd) { // Axx set speed to xx case 1: m->command = CMD_SPEED; m->param >>= 4; break; // Bxx position jump case 2: m->command = CMD_POSITIONJUMP; break; // Cxx patternbreak to row xx case 3: m->command = CMD_PATTERNBREAK; m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F); break; // Dxy volumeslide case 4: m->command = CMD_VOLUMESLIDE; break; // Exy toneslide down case 5: m->command = CMD_PORTAMENTODOWN; break; // Fxy toneslide up case 6: m->command = CMD_PORTAMENTOUP; break; // Gxx Tone portamento,speed xx case 7: m->command = CMD_TONEPORTAMENTO; break; // Hxy vibrato case 8: m->command = CMD_VIBRATO; break; // Ixy tremor, ontime x, offtime y case 9: m->command = CMD_TREMOR; break; // Jxy arpeggio case 10: m->command = CMD_ARPEGGIO; break; // Kxy Dual command H00 & Dxy case 11: m->command = CMD_VIBRATOVOL; break; // Lxy Dual command G00 & Dxy case 12: m->command = CMD_TONEPORTAVOL; break; // Xxx amiga command 8xx case 0x18: m->command = CMD_PANNING8; break; default: m->command = m->param = 0; } } dwMemPos += 64*4*4; } // Reading Samples for (j=1; j<=31; j++) { MODINSTRUMENT *pIns = &_this->Ins[j]; dwMemPos = (dwMemPos + 15) & (~15); if (pIns->nLength) { UINT nPos = ((UINT)phdr->sample[j-1].reserved) << 4; if ((nPos >= sizeof(STMHEADER)) && (nPos+pIns->nLength <= dwMemLength)) dwMemPos = nPos; if (dwMemPos < dwMemLength) { dwMemPos += CSoundFile_ReadSample(_this, pIns, RS_PCM8S, (LPSTR)(lpStream+dwMemPos),dwMemLength-dwMemPos); } } } return TRUE; }