|
From: <sag...@us...> - 2012-05-30 16:45:31
|
Revision: 1291
http://modplug.svn.sourceforge.net/modplug/?rev=1291&view=rev
Author: saga-games
Date: 2012-05-30 16:45:20 +0000 (Wed, 30 May 2012)
Log Message:
-----------
[Mod] Rewrote STM loader to use FileReader class. Also fixed some little issues here and there in the loader.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_stm.cpp
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
Modified: trunk/OpenMPT/soundlib/Load_stm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_stm.cpp 2012-05-29 22:37:46 UTC (rev 1290)
+++ trunk/OpenMPT/soundlib/Load_stm.cpp 2012-05-30 16:45:20 UTC (rev 1291)
@@ -16,168 +16,253 @@
#pragma pack(push, 1)
-typedef struct tagSTMNOTE
+// STM sample header struct
+struct STMSampleHeader
{
- BYTE note;
- BYTE insvol;
- BYTE volcmd;
- BYTE cmdinf;
-} STMNOTE;
+ char filename[12]; // Can't have long comments - just filename comments :)
+ uint8 zero;
+ uint8 disk; // A blast from the past
+ uint16 offset; // ISA in memory when in ST 2
+ uint16 length; // Sample length
+ uint16 loopStart; // Loop start point
+ uint16 loopEnd; // Loop end point
+ uint8 volume; // Volume
+ uint8 reserved2;
+ uint16 sampleRate;
+ uint8 reserved3[6]; // Yet more of PSi's reserved crap
+ // Convert an STM sample header to OpenMPT's internal sample header.
+ void ConvertToMPT(ModSample &mptSmp) const
+ {
+ mptSmp.Initialize();
+ StringFixer::ReadString<StringFixer::nullTerminated>(mptSmp.filename, filename);
-// Raw STM sampleinfo struct:
-typedef struct tagSTMSAMPLE
+ mptSmp.nC5Speed = sampleRate;
+ mptSmp.nVolume = Util::Min(volume * 4u, 256u);
+ mptSmp.nLength = length;
+ mptSmp.nLoopStart = loopStart;
+ mptSmp.nLoopEnd = loopEnd;
+
+ if(mptSmp.nLength < 2 || volume == 0)
+ {
+ // As WTF as the above condition might sound, it seems to make sense.
+ // The zero-volume samples in acidlamb.stm shouldn't be loaded, but they have an actual sample length.
+ mptSmp.nLength = 0;
+ }
+
+ if(mptSmp.nLoopStart < mptSmp.nLength
+ && mptSmp.nLoopEnd > mptSmp.nLoopStart
+ && mptSmp.nLoopEnd != 0xFFFF)
+ {
+ mptSmp.uFlags = CHN_LOOP;
+ mptSmp.nLoopEnd = Util::Min(mptSmp.nLoopEnd, mptSmp.nLength);
+ }
+ }
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(length);
+ SwapBytesLE(loopStart);
+ SwapBytesLE(loopEnd);
+ SwapBytesLE(sampleRate);
+ }
+};
+
+
+// STM file header
+struct STMFileHeader
{
- 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;
+ char songname[20];
+ char trackername[8]; // !SCREAM! for ST 2.xx
+ uint8 dosEof; // 0x1A
+ uint8 filetype; // 1=song, 2=module (only 2 is supported, of course) :)
+ uint8 verMajor; // Like 2
+ uint8 verMinor; // "ditto"
+ uint8 initTempo; // initspeed= stm inittempo>>4
+ uint8 numPatterns; // number of patterns
+ uint8 globalVolume; // <- WoW! a RiGHT TRiANGLE =8*)
+ uint8 reserved[13]; // More of PSi's internal crap
+ STMSampleHeader samples[31]; // Sample headers
+ uint8 order[128]; // Order list
+};
-// Raw STM header struct:
-typedef struct tagSTMHEADER
+// Pattern note entry
+struct STMPatternEntry
{
- char songname[20];
- char trackername[8]; // !SCREAM! for ST 2.xx
- 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;
+ uint8 note;
+ uint8 insvol;
+ uint8 volcmd;
+ uint8 cmdinf;
+};
+
#pragma pack(pop)
-bool CSoundFile::ReadSTM(const BYTE *lpStream, const DWORD dwMemLength)
-//---------------------------------------------------------------------
+bool CSoundFile::ReadSTM(FileReader &file)
+//----------------------------------------
{
- STMHEADER *phdr = (STMHEADER *)lpStream;
- DWORD dwMemPos = 0;
+ file.Rewind();
- if ((!lpStream) || (dwMemLength < sizeof(STMHEADER))) return false;
- if ((phdr->filetype != 2) || (phdr->unused != 0x1A)
- || ((_strnicmp(phdr->trackername, "!SCREAM!", 8))
- && (_strnicmp(phdr->trackername, "BMOD2STM", 8)))) return false;
+ STMFileHeader fileHeader;
+ if(!file.Read(fileHeader)
+ || fileHeader.filetype != 2
+ || fileHeader.dosEof != 0x1A
+ || (_strnicmp(fileHeader.trackername, "!SCREAM!", 8)
+ && _strnicmp(fileHeader.trackername, "BMOD2STM", 8)))
+ {
+ return false;
+ }
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], phdr->songname);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], fileHeader.songname);
// Read STM header
m_nType = MOD_TYPE_STM;
+ m_dwSongFlags = 0;
m_nSamples = 31;
m_nChannels = 4;
m_nInstruments = 0;
m_nMinPeriod = 64;
m_nMaxPeriod = 0x7FFF;
- m_nDefaultSpeed = phdr->inittempo >> 4;
- if (m_nDefaultSpeed < 1) m_nDefaultSpeed = 1;
+ m_nDefaultSpeed = fileHeader.initTempo >> 4;
+ if(m_nDefaultSpeed < 1) m_nDefaultSpeed = 1;
m_nDefaultTempo = 125;
- m_nDefaultGlobalVolume = phdr->globalvol << 2;
- if (m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
- Order.ReadFromArray(phdr->patorder);
+ m_nDefaultGlobalVolume = fileHeader.globalVolume * 4;
+ LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME);
+
// Setting up channels
- for (UINT nSet=0; nSet<4; nSet++)
+ for(CHANNELINDEX chn = 0; chn < 4; chn++)
{
- ChnSettings[nSet].dwFlags = 0;
- ChnSettings[nSet].nVolume = 64;
- ChnSettings[nSet].nPan = (nSet & 1) ? 0x40 : 0xC0;
+ ChnSettings[chn].dwFlags = 0;
+ ChnSettings[chn].nVolume = 64;
+ ChnSettings[chn].nPan = (chn & 1) ? 0x40 : 0xC0;
}
- // Reading samples
- for (UINT nIns=0; nIns<31; nIns++)
+
+ // Read samples
+ for(SAMPLEINDEX smp = 0; smp < 31; smp++)
{
- ModSample *pIns = &Samples[nIns+1];
- STMSAMPLE *pStm = &phdr->sample[nIns]; // STM sample data
+ fileHeader.samples[smp].ConvertEndianness();
+ fileHeader.samples[smp].ConvertToMPT(Samples[smp + 1]);
+ StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[smp + 1], fileHeader.samples[smp].filename);
+ }
- StringFixer::ReadString<StringFixer::nullTerminated>(pIns->filename, pStm->filename);
- StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[nIns + 1], pStm->filename);
-
- pIns->nC5Speed = LittleEndianW(pStm->c2spd);
- pIns->nGlobalVol = 64;
- pIns->nVolume = pStm->volume << 2;
- if (pIns->nVolume > 256) pIns->nVolume = 256;
- pIns->nLength = LittleEndianW(pStm->length);
- if ((pIns->nLength < 4) || (!pIns->nVolume)) pIns->nLength = 0;
- pIns->nLoopStart = LittleEndianW(pStm->loopbeg);
- pIns->nLoopEnd = LittleEndianW(pStm->loopend);
- if ((pIns->nLoopEnd > pIns->nLoopStart) && (pIns->nLoopEnd != 0xFFFF))
+ // Read order list
+ Order.ReadFromArray(fileHeader.order);
+ for(ORDERINDEX ord = 0; ord < 128; ord++)
+ {
+ if(Order[ord] >= 99)
{
- pIns->uFlags |= CHN_LOOP;
- pIns->nLoopEnd = min(pIns->nLoopEnd, pIns->nLength);
+ Order[ord] = Order.GetInvalidPatIndex();
}
}
- dwMemPos = sizeof(STMHEADER);
- for (UINT nOrd = 0; nOrd < 128; nOrd++) if (Order[nOrd] >= 99) Order[nOrd] = Order.GetInvalidPatIndex();
- UINT nPatterns = phdr->numpat;
- for (UINT nPat=0; nPat<nPatterns; nPat++)
+
+ for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++)
{
- if (dwMemPos + 64*4*4 > dwMemLength) return true;
- if(Patterns.Insert(nPat, 64))
- return true;
- ModCommand *m = Patterns[nPat];
- STMNOTE *p = (STMNOTE *)(lpStream + dwMemPos);
- for (UINT n=0; n<64*4; n++, p++, m++)
+ STMPatternEntry patternData[64 * 4];
+
+ if(Patterns.Insert(pat, 64) || !file.ReadArray(patternData))
{
- 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)
+ continue;
+ }
+
+ ModCommand *m = Patterns[pat];
+ ORDERINDEX breakPos = ORDERINDEX_INVALID;
+ ROWINDEX breakRow = 63; // Candidate row for inserting pattern break
+
+ for(size_t n = 0; n < 64 * 4; n++, m++)
+ {
+ const STMPatternEntry &entry = patternData[n];
+
+ if(entry.note == 0xFE || entry.note == 0xFC)
{
- // 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;
+ m->note = NOTE_NOTECUT;
+ } else if(entry.note < 0xFC)
+ {
+ m->note = (entry.note >> 4) * 12 + (entry.note & 0x0F) + 36 + NOTE_MIN;
+ }
+
+ m->instr = entry.insvol >> 3;
+ if(m->instr > 31)
+ {
+ m->instr = 0;
+ }
+
+ int8 vol = (entry.insvol & 0x07) | (entry.volcmd >> 1);
+ if(vol <= 64)
+ {
+ m->volcmd = VOLCMD_VOLUME;
+ m->vol = vol;
+ }
+
+ static const uint8 stmEffects[] =
+ {
+ CMD_NONE, CMD_SPEED, CMD_POSITIONJUMP, CMD_PATTERNBREAK, // .ABC
+ CMD_VOLUMESLIDE, CMD_PORTAMENTODOWN, CMD_PORTAMENTOUP, CMD_TONEPORTAMENTO, // DEFG
+ CMD_VIBRATO, CMD_TREMOR, CMD_ARPEGGIO, CMD_NONE, // HIJK
+ CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, // LMNO
+ // KLMNO can be entered in the editor but don't do anything
+ };
+
+ m->command = stmEffects[entry.volcmd & 0x0F];
+ m->param = entry.cmdinf;
+
+ switch(m->command)
+ {
+ case CMD_SPEED:
+ // I don't know how Axx really works, but I do know that this
+ // isn't it. It does all sorts of mindbogglingly screwy things:
+ // 01 - very fast,
+ // 0F - very slow.
+ // 10 - fast again!
+ // I don't get it.
+ m->param >>= 4;
+ break;
+
+ case CMD_PATTERNBREAK:
+ m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F);
+ if(breakRow > m->param)
+ {
+ breakRow = m->param;
+ }
+ break;
+
+ case CMD_POSITIONJUMP:
+ // This effect is also very weird.
+ // Bxx doesn't appear to cause an immediate break -- it merely
+ // sets the next order for when the pattern ends (either by
+ // playing it all the way through, or via Cxx effect)
+ breakPos = m->param;
+ breakRow = 63;
+ m->command = CMD_NONE;
+ break;
+
+ case CMD_TREMOR:
+ // this actually does something with zero values, and has no
+ // effect memory. which makes SENSE for old-effects tremor,
+ // but ST3 went and screwed it all up by adding an effect
+ // memory and IT followed that, and those are much more popular
+ // than STM so we kind of have to live with this effect being
+ // broken... oh well. not a big loss.
+ break;
+
default:
- m->command = m->param = 0;
+ // Anything not listed above is a no-op if there's no value.
+ // (ST2 doesn't have effect memory)
+ if(!m->param)
+ {
+ m->command= CMD_NONE;
+ }
+ break;
}
}
- dwMemPos += 64*4*4;
+
+ if(breakPos != ORDERINDEX_INVALID)
+ {
+ TryWriteEffect(pat, breakRow, CMD_POSITIONJUMP, breakPos, false, CHANNELINDEX_INVALID, false, weTryPreviousRow);
+ }
}
// Reading Samples
@@ -187,18 +272,20 @@
SampleIO::littleEndian,
SampleIO::signedPCM);
- for (UINT nSmp=1; nSmp<=31; nSmp++)
+ for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
{
- ModSample &sample = Samples[nSmp];
- dwMemPos = (dwMemPos + 15) & (~15);
+ ModSample &sample = Samples[smp];
if(sample.nLength)
{
- UINT nPos = ((UINT)phdr->sample[nSmp-1].reserved) << 4;
- if ((nPos >= sizeof(STMHEADER)) && (nPos <= dwMemLength) && (sample.nLength <= dwMemLength-nPos)) dwMemPos = nPos;
- if (dwMemPos < dwMemLength)
+ //size_t sampleOffset = fileHeader.samples[smp - 1].offset << 4;
+ //if(sampleOffset > sizeof(STMPatternEntry) && sampleOffset < file.GetLength())
+ //{
+ // file.Seek(sampleOffset);
+ //} else
{
- dwMemPos += sampleIO.ReadSample(sample, (LPSTR)(lpStream + dwMemPos),dwMemLength - dwMemPos);
+ file.Seek((file.GetPosition() + 15) & (~15));
}
+ sampleIO.ReadSample(sample, file);
}
}
return true;
Modified: trunk/OpenMPT/soundlib/Sndfile.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.cpp 2012-05-29 22:37:46 UTC (rev 1290)
+++ trunk/OpenMPT/soundlib/Sndfile.cpp 2012-05-30 16:45:20 UTC (rev 1291)
@@ -613,7 +613,7 @@
&& !ReadS3M(file)
&& !ReadWav(file)
#ifndef MODPLUG_BASIC_SUPPORT
- && !ReadSTM(lpStream, dwMemLength)
+ && !ReadSTM(file)
&& !ReadMed(lpStream, dwMemLength)
#ifndef FASTSOUNDLIB
&& !ReadMTM(lpStream, dwMemLength)
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2012-05-29 22:37:46 UTC (rev 1290)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2012-05-30 16:45:20 UTC (rev 1291)
@@ -382,7 +382,7 @@
bool ReadMod(FileReader &file);
bool ReadMed(const LPCBYTE lpStream, const DWORD dwMemLength);
bool ReadMTM(const LPCBYTE lpStream, const DWORD dwMemLength);
- bool ReadSTM(const LPCBYTE lpStream, const DWORD dwMemLength);
+ bool ReadSTM(FileReader &file);
bool ReadIT(const LPCBYTE lpStream, const DWORD dwMemLength);
//bool ReadMPT(const LPCBYTE lpStream, const DWORD dwMemLength);
bool ReadITProject(const LPCBYTE lpStream, const DWORD dwMemLength); // -> CODE#0023 -> DESC="IT project files (.itp)" -! NEW_FEATURE#0023
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|