|
From: <sag...@us...> - 2012-03-30 20:13:06
|
Revision: 1232
http://modplug.svn.sourceforge.net/modplug/?rev=1232&view=rev
Author: saga-games
Date: 2012-03-30 20:12:57 +0000 (Fri, 30 Mar 2012)
Log Message:
-----------
[Ref] Introduced a FileReader class which is safer and more intuitive to use that the good old memory pointer + length stuff. Should be used from now on. A few mod loaders have already been converted, more to follow.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Endianness.h
trunk/OpenMPT/soundlib/ITTools.cpp
trunk/OpenMPT/soundlib/Load_669.cpp
trunk/OpenMPT/soundlib/Load_gdm.cpp
trunk/OpenMPT/soundlib/Load_okt.cpp
trunk/OpenMPT/soundlib/Loaders.h
trunk/OpenMPT/soundlib/ModSequence.cpp
trunk/OpenMPT/soundlib/ModSequence.h
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
trunk/OpenMPT/soundlib/XMTools.cpp
trunk/OpenMPT/soundlib/load_j2b.cpp
Modified: trunk/OpenMPT/soundlib/Endianness.h
===================================================================
--- trunk/OpenMPT/soundlib/Endianness.h 2012-03-27 21:11:51 UTC (rev 1231)
+++ trunk/OpenMPT/soundlib/Endianness.h 2012-03-30 20:12:57 UTC (rev 1232)
@@ -19,6 +19,7 @@
// endian architecture or value x in format of current architecture to little endian
// format.
+// Deprecated. Use "SwapBytesXX" versions below.
#ifdef PLATFORM_BIG_ENDIAN
// PPC
inline DWORD LittleEndian(DWORD x) { return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24); }
@@ -32,3 +33,26 @@
#define LittleEndian(x) (x)
#define LittleEndianW(x) (x)
#endif
+
+#ifdef PLATFORM_BIG_ENDIAN
+// PPC
+inline void SwapBytesBE(uint32 &value) { value; }
+inline void SwapBytesBE(uint16 &value) { value; }
+inline void SwapBytesLE(uint32 &value) { value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); }
+inline void SwapBytesLE(uint16 &value) { value = (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00)); }
+inline void SwapBytesBE(int32 &value) { value; }
+inline void SwapBytesBE(int16 &value) { value; }
+inline void SwapBytesLE(int32 &value) { value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); }
+inline void SwapBytesLE(int16 &value) { value = (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00)); }
+#else
+// x86
+inline void SwapBytesBE(uint32 &value) { value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); }
+inline void SwapBytesBE(uint16 &value) { value = (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00)); }
+inline void SwapBytesLE(uint32 &value) { value; }
+inline void SwapBytesLE(uint16 &value) { value; }
+inline void SwapBytesBE(int32 &value) { value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); }
+inline void SwapBytesBE(int16 &value) { value = (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00)); }
+inline void SwapBytesLE(int32 &value) { value; }
+inline void SwapBytesLE(int16 &value) { value; }
+#endif
+
Modified: trunk/OpenMPT/soundlib/ITTools.cpp
===================================================================
--- trunk/OpenMPT/soundlib/ITTools.cpp 2012-03-27 21:11:51 UTC (rev 1231)
+++ trunk/OpenMPT/soundlib/ITTools.cpp 2012-03-30 20:12:57 UTC (rev 1232)
@@ -18,18 +18,18 @@
void ITFileHeader::ConvertEndianness()
//------------------------------------
{
- id = LittleEndian(id);
- ordnum = LittleEndianW(ordnum);
- insnum = LittleEndianW(insnum);
- smpnum = LittleEndianW(smpnum);
- patnum = LittleEndianW(patnum);
- cwtv = LittleEndianW(cwtv);
- cmwt = LittleEndianW(cmwt);
- flags = LittleEndianW(flags);
- special = LittleEndianW(special);
- msglength = LittleEndianW(msglength);
- msgoffset = LittleEndian(msgoffset);
- reserved = LittleEndian(reserved);
+ SwapBytesLE(id);
+ SwapBytesLE(ordnum);
+ SwapBytesLE(insnum);
+ SwapBytesLE(smpnum);
+ SwapBytesLE(patnum);
+ SwapBytesLE(cwtv);
+ SwapBytesLE(cmwt);
+ SwapBytesLE(flags);
+ SwapBytesLE(special);
+ SwapBytesLE(msglength);
+ SwapBytesLE(msgoffset);
+ SwapBytesLE(reserved);
}
@@ -617,9 +617,9 @@
fattime = static_cast<uint16>((mptHistory.loadDate.tm_sec / 2) | (mptHistory.loadDate.tm_min << 5) | (mptHistory.loadDate.tm_hour << 11));
runtime = static_cast<uint32>(mptHistory.openTime * (18.2f / HISTORY_TIMER_PRECISION));
- fatdate = LittleEndianW(fatdate);
- fattime = LittleEndianW(fattime);
- runtime = LittleEndian(runtime);
+ SwapBytesLE(fatdate);
+ SwapBytesLE(fattime);
+ SwapBytesLE(runtime);
}
#endif // MODPLUG_TRACKER
Modified: trunk/OpenMPT/soundlib/Load_669.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_669.cpp 2012-03-27 21:11:51 UTC (rev 1231)
+++ trunk/OpenMPT/soundlib/Load_669.cpp 2012-03-30 20:12:57 UTC (rev 1232)
@@ -13,52 +13,59 @@
#include "stdafx.h"
#include "Loaders.h"
-#pragma warning(disable:4244) //"conversion from 'type1' to 'type2', possible loss of data"
+#pragma pack(push, 1)
-typedef struct tagFILEHEADER669
+struct _669FileHeader
{
- WORD sig; // 'if' or 'JN'
- signed char songmessage[108]; // Song Message
- BYTE samples; // number of samples (1-64)
- BYTE patterns; // number of patterns (1-128)
- BYTE restartpos;
- BYTE orders[128];
- BYTE tempolist[128];
- BYTE breaks[128];
-} FILEHEADER669;
+ uint16 sig; // 'if' or 'JN'
+ char songmessage[108]; // Song Message
+ uint8 samples; // number of samples (1-64)
+ uint8 patterns; // number of patterns (1-128)
+ uint8 restartpos;
+ uint8 orders[128];
+ uint8 tempolist[128];
+ uint8 breaks[128];
+};
+STATIC_ASSERT(sizeof(_669FileHeader) == 497);
-typedef struct tagSAMPLE669
+struct _669Sample
{
- char filename[13];
+ char filename[13];
uint32 length; // when will somebody think about DWORD align ???
uint32 loopStart;
uint32 loopEnd;
-} SAMPLE669;
+};
+STATIC_ASSERT(sizeof(_669Sample) == 25);
-bool CSoundFile::Read669(const BYTE *lpStream, const DWORD dwMemLength)
-//---------------------------------------------------------------------
+#pragma pack(pop)
+
+
+bool CSoundFile::Read669(FileReader &file)
+//----------------------------------------
{
- BOOL b669Ext;
- const FILEHEADER669 *pfh = (const FILEHEADER669 *)lpStream;
- const SAMPLE669 *psmp = (const SAMPLE669 *)(lpStream + 0x1F1);
- DWORD dwMemPos = 0;
+ bool has669Ext;
+ _669FileHeader fileHeader;
+
+ file.Rewind();
+ if(!file.Read(fileHeader))
+ {
+ return false;
+ }
- if ((!lpStream) || (dwMemLength < sizeof(FILEHEADER669))) return false;
- if ((LittleEndianW(pfh->sig) != 0x6669) && (LittleEndianW(pfh->sig) != 0x4E4A)) return false;
- b669Ext = (LittleEndianW(pfh->sig) == 0x4E4A) ? TRUE : FALSE;
- if ((!pfh->samples) || (pfh->samples > 64) || (pfh->restartpos >= 128)
- || (!pfh->patterns) || (pfh->patterns > 128)) return false;
- DWORD dontfuckwithme = 0x1F1 + pfh->samples * sizeof(SAMPLE669) + pfh->patterns * 0x600;
- if (dontfuckwithme > dwMemLength) return false;
- for (UINT ichk=0; ichk<pfh->samples; ichk++)
+ if(fileHeader.sig != LittleEndianW(0x6669) && fileHeader.sig != LittleEndianW(0x4E4A))
{
- DWORD len = LittleEndian(psmp[ichk].length);
- dontfuckwithme += len;
+ return false;
}
- if (dontfuckwithme - 0x1F1 > dwMemLength) return false;
- // That should be enough checking: this must be a 669 module.
+
+ has669Ext = fileHeader.sig == LittleEndianW(0x4E4A);
+ if(fileHeader.samples > 64 || fileHeader.restartpos >= 128
+ || fileHeader.patterns > 128)
+ {
+ return false;
+ }
+
m_nType = MOD_TYPE_669;
m_dwSongFlags |= SONG_LINEARSLIDES;
m_nMinPeriod = 28 << 2;
@@ -68,14 +75,20 @@
m_nChannels = 8;
// Copy first song message line into song title
- StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[0], reinterpret_cast<const char *>(pfh->songmessage), 36);
+ StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[0], fileHeader.songmessage, 36);
- m_nSamples = pfh->samples;
- for (SAMPLEINDEX nSmp = 1; nSmp <= m_nSamples; nSmp++, psmp++)
+ m_nSamples = fileHeader.samples;
+ for(SAMPLEINDEX nSmp = 1; nSmp <= m_nSamples; nSmp++)
{
- DWORD len = LittleEndian(psmp->length);
- DWORD loopstart = LittleEndian(psmp->loopStart);
- DWORD loopend = LittleEndian(psmp->loopEnd);
+ _669Sample sample;
+ if(!file.Read(sample))
+ {
+ return false;
+ }
+
+ DWORD len = LittleEndian(sample.length);
+ DWORD loopstart = LittleEndian(sample.loopStart);
+ DWORD loopend = LittleEndian(sample.loopEnd);
if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH;
if ((loopend > len) && (!loopstart)) loopend = 0;
if (loopend > len) loopend = len;
@@ -84,19 +97,19 @@
Samples[nSmp].nLoopStart = loopstart;
Samples[nSmp].nLoopEnd = loopend;
if (loopend) Samples[nSmp].uFlags |= CHN_LOOP;
- StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[nSmp], psmp->filename);
+ StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[nSmp], sample.filename);
Samples[nSmp].nVolume = 256;
Samples[nSmp].nGlobalVol = 64;
Samples[nSmp].nPan = 128;
}
// Song Message
- ReadFixedLineLengthMessage((BYTE *)(&pfh->songmessage), 108, 36, 0);
+ ReadFixedLineLengthMessage(file, 108, 36, 0);
// Reading Orders
- Order.ReadAsByte(pfh->orders, 128, 128);
- m_nRestartPos = pfh->restartpos;
- if (Order[m_nRestartPos] >= pfh->patterns) m_nRestartPos = 0;
+ Order.ReadAsByte(fileHeader.orders, 128, 128);
+ m_nRestartPos = fileHeader.restartpos;
+ if(Order[m_nRestartPos] >= fileHeader.patterns) m_nRestartPos = 0;
// Reading Pattern Break Locations
for (UINT npan=0; npan<8; npan++)
{
@@ -105,18 +118,16 @@
}
// Reading Patterns
- dwMemPos = 0x1F1 + pfh->samples * 25;
- for (UINT npat=0; npat<pfh->patterns; npat++)
+ for (UINT npat = 0; npat < fileHeader.patterns; npat++)
{
if(Patterns.Insert(npat, 64))
break;
ModCommand *m = Patterns[npat];
- const BYTE *p = lpStream + dwMemPos;
for (UINT row=0; row<64; row++)
{
ModCommand *mspeed = m;
- if ((row == pfh->breaks[npat]) && (row != 63))
+ if ((row == fileHeader.breaks[npat]) && (row != 63))
{
for (UINT i=0; i<8; i++)
{
@@ -124,25 +135,31 @@
m[i].param = 0;
}
}
- for (UINT n=0; n<8; n++, m++, p+=3)
+ for(UINT n = 0; n < 8; n++, m++)
{
- UINT note = p[0] >> 2;
- UINT instr = ((p[0] & 0x03) << 4) | (p[1] >> 4);
- UINT vol = p[1] & 0x0F;
- if (p[0] < 0xFE)
+ char data[3];
+ if(!file.ReadArray(data))
{
+ break;
+ }
+
+ UINT note = data[0] >> 2;
+ UINT instr = ((data[0] & 0x03) << 4) | (data[1] >> 4);
+ UINT vol = data[1] & 0x0F;
+ if (data[0] < 0xFE)
+ {
m->note = note + 37;
m->instr = instr + 1;
}
- if (p[0] <= 0xFE)
+ if (data[0] <= 0xFE)
{
m->volcmd = VOLCMD_VOLUME;
m->vol = (vol << 2) + 2;
}
- if (p[2] != 0xFF)
+ if (data[2] != 0xFF)
{
- UINT command = p[2] >> 4;
- UINT param = p[2] & 0x0F;
+ UINT command = data[2] >> 4;
+ UINT param = data[2] & 0x0F;
switch(command)
{
case 0x00: command = CMD_PORTAMENTOUP; break;
@@ -170,23 +187,17 @@
for (UINT i=0; i<8; i++) if (!mspeed[i].command)
{
mspeed[i].command = CMD_SPEED;
- mspeed[i].param = pfh->tempolist[npat] + 2;
+ mspeed[i].param = fileHeader.tempolist[npat] + 2;
break;
}
}
}
- dwMemPos += 0x600;
}
// Reading Samples
- for (UINT n=1; n<=m_nSamples; n++)
+ for(SAMPLEINDEX n = 1; n <= m_nSamples; n++)
{
- UINT len = Samples[n].nLength;
- if (dwMemPos >= dwMemLength) break;
- if (len > 4) ReadSample(&Samples[n], RS_PCM8U, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos);
- dwMemPos += len;
+ ReadSample(&Samples[n], RS_PCM8U, file);
}
return true;
}
-
-
Modified: trunk/OpenMPT/soundlib/Load_gdm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_gdm.cpp 2012-03-27 21:11:51 UTC (rev 1231)
+++ trunk/OpenMPT/soundlib/Load_gdm.cpp 2012-03-30 20:12:57 UTC (rev 1232)
@@ -15,494 +15,501 @@
#include "stdafx.h"
#include "Loaders.h"
-#ifdef MODPLUG_TRACKER
-#include "../mptrack/moddoc.h"
-#endif // MODPLUG_TRACKER
#pragma pack(push, 1)
-struct GDMHEADER
+// GDM File Header
+struct GDMFileHeader
{
- uint32 ID; // ID: 'GDM\xFE'
- char SongTitle[32]; // Music's title
- char SongMusician[32]; // Name of music's composer
- char DOSEOF[3]; // 13, 10, 26
- uint32 ID2; // ID: 'GMFS'
- uint8 FormMajorVer; // Format major version
- uint8 FormMinorVer; // Format minor version
- uint16 TrackID; // Composing Tracker ID code (00 = 2GDM)
- uint8 TrackMajorVer; // Tracker's major version
- uint8 TrackMinorVer; // Tracker's minor version
- uint8 PanMap[32]; // 0-Left to 15-Right, 255-N/U
- uint8 MastVol; // Range: 0...64
- uint8 Tempo; // Initial music tempo (6)
- uint8 BPM; // Initial music BPM (125)
- uint16 FormOrigin; // Original format ID:
+ // Header magic bytes
+ enum HeaderMagic
+ {
+ magicGDM_ = 0xFE4D4447,
+ magicGMFS = 0x53464D47,
+ };
+
+ uint32 magic; // ID: 'GDM\xFE'
+ char songTitle[32]; // Music's title
+ char songMusician[32]; // Name of music's composer
+ char dosEOF[3]; // 13, 10, 26
+ uint32 magic2; // ID: 'GMFS'
+ uint8 formatMajorVer; // Format major version
+ uint8 formatMinorVer; // Format minor version
+ uint16 trackerID; // Composing Tracker ID code (00 = 2GDM)
+ uint8 trackerMajorVer; // Tracker's major version
+ uint8 trackerMinorVer; // Tracker's minor version
+ uint8 panMap[32]; // 0-Left to 15-Right, 255-N/U
+ uint8 masterVol; // Range: 0...64
+ uint8 tempo; // Initial music tempo (6)
+ uint8 bpm; // Initial music BPM (125)
+ uint16 originalFormat; // Original format ID:
// 1-MOD, 2-MTM, 3-S3M, 4-669, 5-FAR, 6-ULT, 7-STM, 8-MED
// (versions of 2GDM prior to v1.15 won't set this correctly)
- uint32 OrdOffset;
- uint8 NOO; // Number of orders in module - 1
- uint32 PatOffset;
- uint8 NOP; // Number of patterns in module - 1
- uint32 SamHeadOffset;
- uint32 SamOffset;
- uint8 NOS; // Number of samples in module - 1
- uint32 MTOffset; // Offset of song message
- uint32 MTLength;
- uint32 SSOffset; // Offset of scrolly script (huh?)
- uint16 SSLength;
- uint32 TGOffset; // Offset of text graphic (huh?)
- uint16 TGLength;
+ uint32 orderOffset;
+ uint8 lastOrder; // Number of orders in module - 1
+ uint32 patternOffset;
+ uint8 lastPattern; // Number of patterns in module - 1
+ uint32 sampleHeaderOffset;
+ uint32 sampleDataOffset;
+ uint8 lastSample; // Number of samples in module - 1
+ uint32 messageTextOffset; // Offset of song message
+ uint32 messageTextLength;
+ uint32 scrollyScriptOffset; // Offset of scrolly script (huh?)
+ uint16 scrollyScriptLength;
+ uint32 textGraphicOffset; // Offset of text graphic (huh?)
+ uint16 textGraphicLength;
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(magic);
+ SwapBytesLE(magic2);
+ SwapBytesLE(trackerID);
+ SwapBytesLE(originalFormat);
+ SwapBytesLE(orderOffset);
+ SwapBytesLE(patternOffset);
+ SwapBytesLE(sampleHeaderOffset);
+ SwapBytesLE(sampleDataOffset);
+ SwapBytesLE(messageTextOffset);
+ SwapBytesLE(messageTextLength);
+ SwapBytesLE(messageTextOffset);
+ SwapBytesLE(messageTextLength);
+ SwapBytesLE(scrollyScriptOffset);
+ SwapBytesLE(scrollyScriptLength);
+ SwapBytesLE(textGraphicOffset);
+ SwapBytesLE(textGraphicLength);
+ }
};
-struct GDMSAMPLEHEADER
+// GDM Sample Header
+struct GDMSampleHeader
{
- char SamName[32]; // sample's name
- char FileName[12]; // sample's filename
- uint8 EmsHandle; // useless
- uint32 Length; // length in bytes
- uint32 LoopBegin; // loop start in samples
- uint32 LoopEnd; // loop end in samples
- uint8 Flags; // misc. flags
- uint16 C4Hertz; // frequency
- uint8 Volume; // default volume
- uint8 Pan; // default pan
+ enum SampleFlags
+ {
+ smpLoop = 0x01,
+ smp16Bit = 0x02, // 16-Bit samples are not handled correctly by 2GDM (not implemented)
+ smpVolume = 0x04,
+ smpPanning = 0x08,
+ smpLZW = 0x10, // LZW-compressed samples are not implemented in 2GDM
+ smpStereo = 0x20, // Stereo samples are not handled correctly by 2GDM (not implemented)
+ };
+
+ char name[32]; // sample's name
+ char fileName[12]; // sample's filename
+ uint8 emsHandle; // useless
+ uint32 length; // length in bytes
+ uint32 loopBegin; // loop start in samples
+ uint32 loopEnd; // loop end in samples
+ uint8 flags; // misc. flags
+ uint16 c4Hertz; // frequency
+ uint8 volume; // default volume
+ uint8 panning; // default pan
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(length);
+ SwapBytesLE(loopBegin);
+ SwapBytesLE(loopEnd);
+ SwapBytesLE(c4Hertz);
+ }
};
#pragma pack(pop)
-#define GDMHEAD_GDM_ 0xFE4D4447
-#define GDMHEAD_GMFS 0x53464D47
-
-static const MODTYPE GDMHeader_Origin[] =
+bool CSoundFile::ReadGDM(FileReader &file)
+//----------------------------------------
{
- MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED
-};
+ file.Rewind();
+ GDMFileHeader fileHeader;
+ if(!file.ReadConvertEndianness(fileHeader))
+ {
+ return false;
+ }
-bool CSoundFile::ReadGDM(const LPCBYTE lpStream, const DWORD dwMemLength)
-//-----------------------------------------------------------------------
-{
- if ((!lpStream) || (dwMemLength < sizeof(GDMHEADER))) return false;
-
- const GDMHEADER *pHeader = (GDMHEADER *)lpStream;
-
// Is it a valid GDM file?
- if( (pHeader->ID != LittleEndian(GDMHEAD_GDM_)) || //GDM\xFE
- (pHeader->DOSEOF[0] != 13 || pHeader->DOSEOF[1] != 10 || pHeader->DOSEOF[2] != 26) || //CR+LF+EOF
- (pHeader->ID2 != LittleEndian(GDMHEAD_GMFS))) return false; //GMFS
-
- // There are no other format versions...
- if(pHeader->FormMajorVer != 1 || pHeader->FormMinorVer != 0)
+ if(fileHeader.magic != GDMFileHeader::magicGDM_
+ || fileHeader.dosEOF[0] != 13 || fileHeader.dosEOF[1] != 10 || fileHeader.dosEOF[2] != 26
+ || fileHeader.magic2 != GDMFileHeader::magicGMFS
+ || fileHeader.formatMajorVer != 1 || fileHeader.formatMinorVer != 0)
+ {
return false;
+ }
// 1-MOD, 2-MTM, 3-S3M, 4-669, 5-FAR, 6-ULT, 7-STM, 8-MED
- m_nType = GDMHeader_Origin[pHeader->FormOrigin % CountOf(GDMHeader_Origin)];
+ static const MODTYPE gdmFormatOrigin[] =
+ {
+ MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED
+ };
+
+ m_nType = gdmFormatOrigin[fileHeader.originalFormat % CountOf(gdmFormatOrigin)];
if(m_nType == MOD_TYPE_NONE)
+ {
return false;
+ }
- // Interesting question: Is TrackID, TrackMajorVer, TrackMinorVer relevant? The only TrackID should be 0 - 2GDM.exe, so the answer would be no.
-
// Song name
MemsetZero(m_szNames);
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], pHeader->SongTitle);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], fileHeader.songTitle);
// Read channel pan map... 0...15 = channel panning, 16 = surround channel, 255 = channel does not exist
m_nChannels = 32;
for(CHANNELINDEX i = 0; i < 32; i++)
{
- if(pHeader->PanMap[i] < 16)
+ if(fileHeader.panMap[i] < 16)
{
- ChnSettings[i].nPan = min((pHeader->PanMap[i] * 16) + 8, 256);
+ ChnSettings[i].nPan = min((fileHeader.panMap[i] * 16) + 8, 256);
}
- else if(pHeader->PanMap[i] == 16)
+ else if(fileHeader.panMap[i] == 16)
{
ChnSettings[i].nPan = 128;
ChnSettings[i].dwFlags |= CHN_SURROUND;
}
- else if(pHeader->PanMap[i] == 0xFF)
+ else if(fileHeader.panMap[i] == 0xFF)
{
m_nChannels = i;
break;
}
}
- m_nDefaultGlobalVolume = min(pHeader->MastVol * 4, 256);
- m_nDefaultSpeed = pHeader->Tempo;
- m_nDefaultTempo = pHeader->BPM;
+ m_nDefaultGlobalVolume = min(fileHeader.masterVol * 4, 256);
+ m_nDefaultSpeed = fileHeader.tempo;
+ m_nDefaultTempo = fileHeader.bpm;
m_nRestartPos = 0; // Not supported in this format, so use the default value
m_nSamplePreAmp = 48; // Dito
m_nVSTiVolume = 48; // Dito
- uint32 iSampleOffset = LittleEndian(pHeader->SamOffset),
- iPatternsOffset = LittleEndian(pHeader->PatOffset);
+ // Read orders
+ if(file.Seek(fileHeader.orderOffset))
+ {
+ Order.ReadAsByte(file, fileHeader.lastOrder + 1);
+ }
- const uint32 iOrdOffset = LittleEndian(pHeader->OrdOffset), iSamHeadOffset = LittleEndian(pHeader->SamHeadOffset),
- iMTOffset = LittleEndian(pHeader->MTOffset), iMTLength = LittleEndian(pHeader->MTLength),
- iSSOffset = LittleEndian(pHeader->SSOffset), iSSLength = LittleEndianW(pHeader->SSLength),
- iTGOffset = LittleEndian(pHeader->TGOffset), iTGLength = LittleEndianW(pHeader->TGLength);
-
-
- // Check if offsets are valid. we won't read the scrolly text or text graphics, but invalid pointers would probably indicate a broken file...
- if( dwMemLength < iOrdOffset || dwMemLength - iOrdOffset < pHeader->NOO
- || dwMemLength < iPatternsOffset
- || dwMemLength < iSamHeadOffset || dwMemLength - iSamHeadOffset < (pHeader->NOS + 1) * sizeof(GDMSAMPLEHEADER)
- || dwMemLength < iSampleOffset
- || dwMemLength < iMTOffset || dwMemLength - iMTOffset < iMTLength
- || dwMemLength < iSSOffset || dwMemLength - iSSOffset < iSSLength
- || dwMemLength < iTGOffset || dwMemLength - iTGOffset < iTGLength)
+ // Read samples
+ if(!file.Seek(fileHeader.sampleHeaderOffset))
+ {
return false;
+ }
- // Read orders
- Order.ReadAsByte(lpStream + iOrdOffset, pHeader->NOO + 1, dwMemLength - iOrdOffset);
+ m_nSamples = fileHeader.lastSample + 1;
- // Read samples
- m_nSamples = pHeader->NOS + 1;
-
- for(SAMPLEINDEX iSmp = 1; iSmp <= m_nSamples; iSmp++)
+ // Sample headers
+ for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
{
- const GDMSAMPLEHEADER *pSample = (GDMSAMPLEHEADER *)(lpStream + iSamHeadOffset + (iSmp - 1) * sizeof(GDMSAMPLEHEADER));
+ GDMSampleHeader gdmSample;
+ if(!file.ReadConvertEndianness(gdmSample))
+ {
+ break;
+ }
- // Sample header
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[smp], gdmSample.name);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(Samples[smp].filename, gdmSample.fileName);
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[iSmp], pSample->SamName);
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(Samples[iSmp].filename, pSample->FileName);
+ Samples[smp].nC5Speed = gdmSample.c4Hertz;
+ Samples[smp].nGlobalVol = 256; // Not supported in this format
- Samples[iSmp].nC5Speed = LittleEndianW(pSample->C4Hertz);
- Samples[iSmp].nGlobalVol = 256; // not supported in this format
- Samples[iSmp].nLength = min(LittleEndian(pSample->Length), MAX_SAMPLE_LENGTH); // in bytes
- Samples[iSmp].nLoopStart = min(LittleEndian(pSample->LoopBegin), Samples[iSmp].nLength); // in samples
- Samples[iSmp].nLoopEnd = min(LittleEndian(pSample->LoopEnd) - 1, Samples[iSmp].nLength); // dito
- FrequencyToTranspose(&Samples[iSmp]); // set transpose + finetune for mod files
+ Samples[smp].nLength = min(gdmSample.length, MAX_SAMPLE_LENGTH); // in bytes
+ // Sample format
+ if(gdmSample.flags & GDMSampleHeader::smp16Bit)
+ {
+ Samples[smp].uFlags |= CHN_16BIT;
+ Samples[smp].nLength /= 2;
+ }
+
+ Samples[smp].nLoopStart = min(gdmSample.loopBegin, Samples[smp].nLength); // in samples
+ Samples[smp].nLoopEnd = min(gdmSample.loopEnd - 1, Samples[smp].nLength); // dito
+ FrequencyToTranspose(&Samples[smp]); // set transpose + finetune for mod files
+
// Fix transpose + finetune for some rare cases where transpose is not C-5 (e.g. sample 4 in wander2.gdm)
if(m_nType == MOD_TYPE_MOD)
{
- while(Samples[iSmp].RelativeTone != 0)
+ while(Samples[smp].RelativeTone != 0)
{
- if(Samples[iSmp].RelativeTone > 0)
+ if(Samples[smp].RelativeTone > 0)
{
- Samples[iSmp].RelativeTone -= 1;
- Samples[iSmp].nFineTune += 128;
+ Samples[smp].RelativeTone -= 1;
+ Samples[smp].nFineTune += 128;
}
else
{
- Samples[iSmp].RelativeTone += 1;
- Samples[iSmp].nFineTune -= 128;
+ Samples[smp].RelativeTone += 1;
+ Samples[smp].nFineTune -= 128;
}
}
}
- if(pSample->Flags & 0x01) Samples[iSmp].uFlags |= CHN_LOOP; // Loop sample
+ Samples[smp].uFlags = 0;
+ if(gdmSample.flags & GDMSampleHeader::smpLoop) Samples[smp].uFlags |= CHN_LOOP; // Loop sample
- if(pSample->Flags & 0x04)
+ if(gdmSample.flags & GDMSampleHeader::smpVolume)
{
- Samples[iSmp].nVolume = min(pSample->Volume, 64) * 4; // 0...64, 255 = no default volume
- }
- else
+ // Default volume is used... 0...64, 255 = no default volume
+ Samples[smp].nVolume = min(gdmSample.volume, 64) * 4;
+ } else
{
- Samples[iSmp].nVolume = 256; // Default volume
+ Samples[smp].nVolume = 256;
}
- if(pSample->Flags & 0x08) // Default panning is used
+ if(gdmSample.flags & GDMSampleHeader::smpPanning)
{
- Samples[iSmp].uFlags |= CHN_PANNING;
- Samples[iSmp].nPan = (pSample->Pan > 15) ? 128 : min((pSample->Pan * 16) + 8, 256); // 0...15, 16 = surround (not supported), 255 = no default panning
- }
- else
+ // Default panning is used
+ Samples[smp].uFlags |= CHN_PANNING;
+ // 0...15, 16 = surround (not supported), 255 = no default panning
+ Samples[smp].nPan = (gdmSample.panning > 15) ? 128 : min((gdmSample.panning * 16) + 8, 256);
+ } else
{
- Samples[iSmp].nPan = 128;
+ Samples[smp].nPan = 128;
}
+ }
- // Note: apparently (and according to zilym), 2GDM doesn't handle 16 bit or stereo samples properly.
- // so those flags are pretty much meaningless and we will ignore them... in fact, samples won't load as expected if we don't!
-
- UINT iSampleFormat;
- if(pSample->Flags & 0x02) // 16 bit
+ // Read sample data
+ if(file.Seek(fileHeader.sampleDataOffset))
+ {
+ for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
{
- if(pSample->Flags & 0x20) // stereo
- iSampleFormat = RS_PCM16U; // should be RS_STPCM16U but that breaks the sample reader
- else
- iSampleFormat = RS_PCM16U;
+ ReadSample(&Samples[smp], (Samples[smp].uFlags & CHN_16BIT) ? RS_PCM16U : RS_PCM8U, file);
}
- else // 8 bit
- {
- if(pSample->Flags & 0x20) // stereo
- iSampleFormat = RS_PCM8U; // should be RS_STPCM8U - dito
- else
- iSampleFormat = RS_PCM8U;
- }
-
- // According to zilym, LZW support has never been finished, so this is also practically useless. Just ignore the flag.
- // if(pSample->Flags & 0x10) {...}
-
- // Read sample data
- ReadSample(&Samples[iSmp], iSampleFormat, reinterpret_cast<LPCSTR>(lpStream + iSampleOffset), dwMemLength - iSampleOffset);
-
- iSampleOffset += min(LittleEndian(pSample->Length), dwMemLength - iSampleOffset);
-
}
// Read patterns
- Patterns.ResizeArray(max(MAX_PATTERNS, pHeader->NOP + 1));
+ Patterns.ResizeArray(max(MAX_PATTERNS, fileHeader.lastPattern + 1));
- const bool bS3MCommandSet = (GetBestSaveFormat() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)) != 0;
+ const CModSpecifications &modSpecs = GetModSpecifications(GetBestSaveFormat());
- // We'll start at position iPatternsOffset and decode all patterns
- for(PATTERNINDEX iPat = 0; iPat < pHeader->NOP + 1; iPat++)
+ // We'll start at position patternsOffset and decode all patterns
+ file.Seek(fileHeader.patternOffset);
+ for(PATTERNINDEX pat = 0; pat <= fileHeader.lastPattern; pat++)
{
-
- if(iPatternsOffset + 2 > dwMemLength) break;
- uint16 iPatternLength = LittleEndianW(*(uint16 *)(lpStream + iPatternsOffset)); // Pattern length including the two "length" bytes
- if(iPatternLength > dwMemLength || iPatternsOffset > dwMemLength - iPatternLength) break;
-
- if(Patterns.Insert(iPat, 64))
+ if(!file.CanRead(2))
+ {
break;
+ }
- // Position in THIS pattern
- DWORD iPatternPos = iPatternsOffset + 2;
+ // Read pattern length *including* the two "length" bytes
+ uint16 patternLength = file.ReadUint16LE();
- for(ROWINDEX iRow = 0; iRow < 64; iRow++)
+ if(patternLength <= 2)
{
- ModCommand *p = Patterns[iPat].GetRow(iRow);
+ // Huh, no pattern data present?
+ continue;
+ }
+ FileReader chunk = file.GetChunk(patternLength - 2);
- while(true) // Zero byte = next row
- {
- if(iPatternPos + 1 > dwMemLength) break;
+ if(!chunk.IsValid() || Patterns.Insert(pat, 64))
+ {
+ break;
+ }
- BYTE bChannel = lpStream[iPatternPos++];
+ enum
+ {
+ rowDone = 0, // Advance to next row
+ channelMask = 0x1F, // Mask for retrieving channel information
+ noteFlag = 0x20, // Note / instrument information present
+ effectFlag = 0x40, // Effect information present
+ effectMask = 0x1F, // Mask for retrieving effect command
+ effectDone = 0x20, // Last effect in this channel
+ };
- if(bChannel == 0) break; // Next row, please!
+ for(ROWINDEX row = 0; row < 64; row++)
+ {
+ PatternRow rowBase = Patterns[pat].GetRow(row);
- const UINT channel = bChannel & 0x1F;
+ uint8 channelByte;
+ // If channel byte is zero, advance to next row.
+ while((channelByte = chunk.ReadUint8()) != rowDone)
+ {
+ CHANNELINDEX channel = channelByte & channelMask;
if(channel >= m_nChannels) break; // Better safe than sorry!
- ModCommand *m = &p[channel];
+ ModCommand &m = rowBase[channel];
- if(bChannel & 0x20)
+ if(channelByte & noteFlag)
{
// Note and sample follows
- if(iPatternPos + 2 > dwMemLength) break;
- BYTE bNote = lpStream[iPatternPos++];
- BYTE bSample = lpStream[iPatternPos++];
+ uint8 noteByte = chunk.ReadUint8();
+ uint8 noteSample = chunk.ReadUint8();
- bNote = (bNote & 0x7F) - 1; // This format doesn't have note cuts
- if(bNote < 0xF0) bNote = (bNote & 0x0F) + 12 * (bNote >> 4) + 13;
- if(bNote == 0xFF) bNote = NOTE_NONE;
- m->note = bNote;
- m->instr = bSample;
-
+ if(noteByte)
+ {
+ noteByte = (noteByte & 0x7F) - 1; // This format doesn't have note cuts
+ if(noteByte < 0xF0) noteByte = (noteByte & 0x0F) + 12 * (noteByte >> 4) + 13;
+ m.note = noteByte;
+ }
+ m.instr = noteSample;
}
- if(bChannel & 0x40)
+ if(channelByte & effectFlag)
{
// Effect(s) follow(s)
+ m.command = CMD_NONE;
+ m.volcmd = VOLCMD_NONE;
- m->command = CMD_NONE;
- m->volcmd = CMD_NONE;
-
- while(true)
+ while(chunk.BytesLeft())
{
- if(iPatternPos + 2 > dwMemLength) break;
- BYTE bEffect = lpStream[iPatternPos++];
- BYTE bEffectData = lpStream[iPatternPos++];
+ uint8 effByte = chunk.ReadUint8();
+ uint8 paramByte = chunk.ReadUint8();
- BYTE command = bEffect & 0x1F, param = bEffectData;
- BYTE volcommand = CMD_NONE, volparam = param;
+ // We may want to restore the old command in some cases.
+ const ModCommand oldCmd = m;
+ m.command = effByte & effectMask;
+ m.param = paramByte;
- switch(command)
+ // Effect translation LUT
+ static const uint8 gdmEffTrans[] =
{
- case 0x01: command = CMD_PORTAMENTOUP; if(param >= 0xE0) param = 0xDF; break;
- case 0x02: command = CMD_PORTAMENTODOWN; if(param >= 0xE0) param = 0xDF; break;
- case 0x03: command = CMD_TONEPORTAMENTO; break;
- case 0x04: command = CMD_VIBRATO; break;
- case 0x05: command = CMD_TONEPORTAVOL; if (param & 0xF0) param &= 0xF0; break;
- case 0x06: command = CMD_VIBRATOVOL; if (param & 0xF0) param &= 0xF0; break;
- case 0x07: command = CMD_TREMOLO; break;
- case 0x08: command = CMD_TREMOR; break;
- case 0x09: command = CMD_OFFSET; break;
- case 0x0A: command = CMD_VOLUMESLIDE; break;
- case 0x0B: command = CMD_POSITIONJUMP; break;
- case 0x0C:
- if(bS3MCommandSet)
+ CMD_NONE, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO,
+ CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO,
+ CMD_TREMOR, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP,
+ CMD_VOLUME, CMD_PATTERNBREAK, CMD_MODCMDEX, CMD_SPEED,
+ CMD_ARPEGGIO, CMD_NONE /* set internal flag */, CMD_RETRIG, CMD_GLOBALVOLUME,
+ CMD_FINEVIBRATO, CMD_NONE, CMD_NONE, CMD_NONE,
+ CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE,
+ CMD_NONE, CMD_NONE, CMD_S3MCMDEX, CMD_TEMPO,
+ };
+ STATIC_ASSERT(CountOf(gdmEffTrans) == 0x20);
+
+ // Translate effect
+ if(m.command < CountOf(gdmEffTrans))
+ {
+ m.command = gdmEffTrans[m.command];
+ } else
+ {
+ m.command = CMD_NONE;
+ }
+
+ // Fix some effects
+ switch(m.command)
+ {
+ case CMD_PORTAMENTOUP:
+ if(m.param >= 0xE0)
+ m.param = 0xDF;
+ break;
+
+ case CMD_PORTAMENTODOWN:
+ if(m.param >= 0xE0)
+ m.param = 0xDF;
+ break;
+
+ case CMD_TONEPORTAVOL:
+ if(m.param & 0xF0)
+ m.param &= 0xF0;
+ break;
+
+ case CMD_VIBRATOVOL:
+ if(m.param & 0xF0)
+ m.param &= 0xF0;
+ break;
+
+ case CMD_VOLUME:
+ m.param = min(m.param, 64);
+ if(modSpecs.HasVolCommand(VOLCMD_VOLUME))
{
- command = CMD_NONE;
- volcommand = VOLCMD_VOLUME;
- volparam = min(param, 64);
+ m.volcmd = VOLCMD_VOLUME;
+ m.vol = m.param;
+ // Don't destroy old command, if there was one.
+ m.command = oldCmd.command;
+ m.param = oldCmd.param;
}
- else
+ break;
+
+ case CMD_MODCMDEX:
+ if(!modSpecs.HasVolCommand(CMD_MODCMDEX))
{
- command = CMD_VOLUME;
- param = min(param, 64);
+ m.ExtendedMODtoS3MEffect();
}
break;
- case 0x0D: command = CMD_PATTERNBREAK; break;
- case 0x0E:
- if(bS3MCommandSet)
+
+ case CMD_RETRIG:
+ if(!modSpecs.HasCommand(CMD_RETRIG) && modSpecs.HasCommand(CMD_MODCMDEX))
{
- command = CMD_S3MCMDEX;
- // Need to do some remapping
- switch(param >> 4)
+ // Retrig in "MOD style"
+ m.command = CMD_MODCMDEX;
+ m.param = 0x90 | (m.param & 0x0F);
+ }
+ break;
+
+ case CMD_S3MCMDEX:
+ // Some really special commands
+ switch(m.param >> 4)
+ {
+ case 0x0:
+ switch(m.param & 0x0F)
{
- case 0x0:
- // set filter
+ case 0x0: // Surround Off
+ m.command = CMD_S3MCMDEX;
+ m.param = 0x90;
break;
- case 0x1:
- // fine porta up
- command = CMD_PORTAMENTOUP;
- param = 0xF0 | (param & 0x0F);
+ case 0x1: // Surround On
+ m.command = CMD_PANNING8;
+ m.param = 0xA4;
break;
- case 0x2:
- // fine porta down
- command = CMD_PORTAMENTODOWN;
- param = 0xF0 | (param & 0x0F);
+ case 0x2: // Set normal loop - not implemented in BWSB or 2GDM.
+ case 0x3: // Set bidi loop - dito
+ m.command = CMD_NONE;
break;
- case 0x3:
- // glissando control
- param = 0x10 | (param & 0x0F);
+ case 0x4: // Play sample forwards
+ m.command = CMD_S3MCMDEX;
+ m.param = 0x9E;
break;
- case 0x4:
- // vibrato waveform
- param = 0x30 | (param & 0x0F);
+ case 0x5: // Play sample backwards
+ m.command = CMD_S3MCMDEX;
+ m.param = 0x9F;
break;
- case 0x5:
- // set finetune
- param = 0x20 | (param & 0x0F);
+ case 0x6: // Monaural sample - also not implemented.
+ case 0x7: // Stereo sample - dito
+ case 0x8: // Stop sample on end - dito
+ case 0x9: // Loop sample on end - dito
+ default:
+ m.command = CMD_NONE;
break;
- case 0x6:
- // pattern loop
- param = 0xB0 | (param & 0x0F);
- break;
- case 0x7:
- // tremolo waveform
- param = 0x40 | (param & 0x0F);
- break;
- case 0x8:
- // extra fine porta up
- command = CMD_PORTAMENTOUP;
- param = 0xE0 | (param & 0x0F);
- break;
- case 0x9:
- // extra fine porta down
- command = CMD_PORTAMENTODOWN;
- param = 0xE0 | (param & 0x0F);
- break;
- case 0xA:
- // fine volume up
- command = CMD_VOLUMESLIDE;
- param = ((param & 0x0F) << 4) | 0x0F;
- break;
- case 0xB:
- // fine volume down
- command = CMD_VOLUMESLIDE;
- param = 0xF0 | (param & 0x0F);
- break;
- case 0xC:
- // note cut
- break;
- case 0xD:
- // note delay
- break;
- case 0xE:
- // pattern delay
- break;
- case 0xF:
- command = CMD_MODCMDEX;
- // invert loop / funk repeat
- break;
}
- }
- else
- {
- command = CMD_MODCMDEX;
- }
- break;
- case 0x0F: command = CMD_SPEED; break;
- case 0x10: command = CMD_ARPEGGIO; break;
- case 0x11: command = CMD_NONE /* set internal flag */; break;
- case 0x12:
- if((!bS3MCommandSet) && ((param & 0xF0) == 0))
- {
- // retrig in "mod style"
- command = CMD_MODCMDEX;
- param = 0x90 | (param & 0x0F);
- }
- else
- {
- // either "s3m style" is required or this format is like s3m anyway
- command = CMD_RETRIG;
- }
- break;
- case 0x13: command = CMD_GLOBALVOLUME; break;
- case 0x14: command = CMD_FINEVIBRATO; break;
- case 0x1E:
- switch(param >> 4)
- {
- case 0x0:
- switch(param & 0x0F)
+ break;
+
+ case 0x8: // 4-Bit Panning
+ if(!modSpecs.HasCommand(CMD_S3MCMDEX))
{
- case 0x0: command = CMD_S3MCMDEX; param = 0x90; break;
- case 0x1: command = CMD_PANNING8; param = 0xA4; break;
- case 0x2: command = CMD_NONE /* set normal loop - not implemented in 2GDM */; break;
- case 0x3: command = CMD_NONE /* set bidi loop - dito */; break;
- case 0x4: command = CMD_S3MCMDEX; param = 0x9E; break;
- case 0x5: command = CMD_S3MCMDEX; param = 0x9F; break;
- case 0x6: command = CMD_NONE /* monaural sample - dito */; break;
- case 0x7: command = CMD_NONE /* stereo sample - dito */; break;
- case 0x8: command = CMD_NONE /* stop sample on end - dito */; break;
- case 0x9: command = CMD_NONE /* loop sample on end - dito */; break;
- default: command = CMD_NONE; break;
+ m.command = CMD_MODCMDEX;
}
break;
- case 0x8:
- command = (bS3MCommandSet) ? CMD_S3MCMDEX : CMD_MODCMDEX;
+
+ case 0xD: // Adjust frequency (increment in hz) - also not implemented.
+ default:
+ m.command = CMD_NONE;
break;
- case 0xD:
- // adjust frequency (increment in hz) - not implemented in 2GDM
- command = CMD_NONE;
- break;
- default: command = CMD_NONE; break;
}
break;
- case 0x1F: command = CMD_TEMPO; break;
- default: command = CMD_NONE; break;
+
+ case 0x1F:
+ m.command = CMD_TEMPO;
+ break;
}
- if(command != CMD_NONE)
+ // Move pannings to volume column - should never happen
+ if(m.command == CMD_S3MCMDEX && ((m.param >> 4) == 0x8) && m.volcmd == VOLCMD_NONE)
{
- // move pannings to volume column - should never happen
- if(m->command == CMD_S3MCMDEX && ((m->param >> 4) == 0x8) && volcommand == CMD_NONE)
- {
- volcommand = VOLCMD_PANNING;
- volparam = ((param & 0x0F) * 4) + 2;
- }
-
- m->command = command;
- m->param = param;
+ m.volcmd = VOLCMD_PANNING;
+ m.vol = ((m.param & 0x0F) * 64 + 8) / 15;
+ m.command = oldCmd.command;
+ m.param = oldCmd.param;
}
- if(volcommand != CMD_NONE)
- {
- m->volcmd = volcommand;
- m->vol = volparam;
- }
- if(!(bEffect & 0x20)) break; // no other effect follows
+ if(!(effByte & effectDone)) break; // no other effect follows
}
}
}
}
-
- iPatternsOffset += iPatternLength;
}
- // read song comments
- if(iMTLength > 0)
+ // Read song comments
+ if(fileHeader.messageTextLength > 0 && file.Seek(fileHeader.messageTextOffset))
{
- ReadMessage(lpStream + iMTOffset, iMTLength, leAutodetect);
+ ReadMessage(file, fileHeader.messageTextLength, leAutodetect);
}
-
+
return true;
}
Modified: trunk/OpenMPT/soundlib/Load_okt.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_okt.cpp 2012-03-27 21:11:51 UTC (rev 1231)
+++ trunk/OpenMPT/soundlib/Load_okt.cpp 2012-03-30 20:12:57 UTC (rev 1232)
@@ -12,127 +12,137 @@
#include "stdafx.h"
#include "Loaders.h"
-// IFF chunk names
-#define OKTCHUNKID_CMOD 0x434D4F44
-#define OKTCHUNKID_SAMP 0x53414D50
-#define OKTCHUNKID_SPEE 0x53504545
-#define OKTCHUNKID_SLEN 0x534C454E
-#define OKTCHUNKID_PLEN 0x504C454E
-#define OKTCHUNKID_PATT 0x50415454
-#define OKTCHUNKID_PBOD 0x50424F44
-#define OKTCHUNKID_SBOD 0x53424F44
-
#pragma pack(push, 1)
-struct OKT_IFFCHUNK
+struct OktIffChunk
{
+ // IFF chunk names
+ enum ChunkIdentifiers
+ {
+ idCMOD = 0x434D4F44,
+ idSAMP = 0x53414D50,
+ idSPEE = 0x53504545,
+ idSLEN = 0x534C454E,
+ idPLEN = 0x504C454E,
+ idPATT = 0x50415454,
+ idPBOD = 0x50424F44,
+ idSBOD = 0x53424F44,
+ };
+
uint32 signature; // IFF chunk name
uint32 chunksize; // chunk size without header
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesBE(signature);
+ SwapBytesBE(chunksize);
+ }
};
-struct OKT_SAMPLE
+struct OktSample
{
char name[20];
uint32 length; // length in bytes
- uint16 loopstart; // *2 for real value
- uint16 looplen; // dito
+ uint16 loopStart; // *2 for real value
+ uint16 loopLength; // dito
uint16 volume; // default volume
uint16 type; // 7-/8-bit sample
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesBE(length);
+ SwapBytesBE(loopStart);
+ SwapBytesBE(loopLength);
+ SwapBytesBE(volume);
+ SwapBytesBE(type);
+ }
};
-STATIC_ASSERT(sizeof(OKT_SAMPLE) == 32);
+STATIC_ASSERT(sizeof(OktSample) == 32);
#pragma pack(pop)
-
-// just for keeping track of offsets and stuff...
-struct OKT_SAMPLEINFO
-{
- DWORD start; // start position of the IFF block
- DWORD length; // length of the IFF block
-};
-
-
// Parse the sample header block
-void Read_OKT_Samples(const BYTE *lpStream, const DWORD dwMemLength, vector<bool> &sample7bit, CSoundFile *pSndFile)
-//------------------------------------------------------------------------------------------------------------------
+void ReadOKTSamples(FileReader &chunk, vector<bool> &sample7bit, CSoundFile *pSndFile)
+//------------------------------------------------------------------------------------
{
- pSndFile->m_nSamples = min((SAMPLEINDEX)(dwMemLength / 32), MAX_SAMPLES - 1); // typically 36
+ pSndFile->m_nSamples = min((SAMPLEINDEX)(chunk.BytesLeft() / sizeof(OktSample)), MAX_SAMPLES - 1); // typically 36
sample7bit.resize(pSndFile->GetNumSamples());
for(SAMPLEINDEX nSmp = 1; nSmp <= pSndFile->GetNumSamples(); nSmp++)
{
- ModSample &sample = pSndFile->GetSample(nSmp);
- OKT_SAMPLE oktsmp;
- memcpy(&oktsmp, lpStream + (nSmp - 1) * 32, sizeof(OKT_SAMPLE));
+ ModSample &mptSmp = pSndFile->GetSample(nSmp);
+ OktSample oktSmp;
+ chunk.ReadConvertEndianness(oktSmp);
- oktsmp.length = min(BigEndian(oktsmp.length), MAX_SAMPLE_LENGTH);
- oktsmp.loopstart = BigEndianW(oktsmp.loopstart) * 2;
- oktsmp.looplen = BigEndianW(oktsmp.looplen) * 2;
- oktsmp.volume = BigEndianW(oktsmp.volume);
- oktsmp.type = BigEndianW(oktsmp.type);
+ oktSmp.length = min(oktSmp.length, MAX_SAMPLE_LENGTH);
+ oktSmp.loopStart = oktSmp.loopStart * 2;
+ oktSmp.loopLength = oktSmp.loopLength * 2;
+ oktSmp.volume = oktSmp.volume;
+ oktSmp.type = oktSmp.type;
- MemsetZero(sample);
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(pSndFile->m_szNames[nSmp], oktsmp.name);
+ MemsetZero(mptSmp);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(pSndFile->m_szNames[nSmp], oktSmp.name);
- sample.nC5Speed = 8287;
- sample.nGlobalVol = 64;
- sample.nVolume = min(oktsmp.volume, 64) * 4;
- sample.nLength = oktsmp.length & ~1; // round down
+ mptSmp.nC5Speed = 8287;
+ mptSmp.nGlobalVol = 64;
+ mptSmp.nVolume = min(oktSmp.volume, 64) * 4;
+ mptSmp.nLength = oktSmp.length & ~1; // round down
// parse loops
- if (oktsmp.looplen > 2 && ((UINT)oktsmp.loopstart) + ((UINT)oktsmp.looplen) <= sample.nLength)
+ if (oktSmp.loopLength > 2 && ((UINT)oktSmp.loopStart) + ((UINT)oktSmp.loopLength) <= mptSmp.nLength)
{
- sample.nSustainStart = oktsmp.loopstart;
- sample.nSustainEnd = oktsmp.loopstart + oktsmp.looplen;
- if (sample.nSustainStart < sample.nLength && sample.nSustainEnd <= sample.nLength)
- sample.uFlags |= CHN_SUSTAINLOOP;
+ mptSmp.nSustainStart = oktSmp.loopStart;
+ mptSmp.nSustainEnd = oktSmp.loopStart + oktSmp.loopLength;
+ if (mptSmp.nSustainStart < mptSmp.nLength && mptSmp.nSustainEnd <= mptSmp.nLength)
+ mptSmp.uFlags |= CHN_SUSTAINLOOP;
else
- sample.nSustainStart = sample.nSustainEnd = 0;
+ mptSmp.nSustainStart = mptSmp.nSustainEnd = 0;
}
- sample7bit[nSmp - 1] = (oktsmp.type == 0 || oktsmp.type == 2);
+ sample7bit[nSmp - 1] = (oktSmp.type == 0 || oktSmp.type == 2);
}
}
// Parse a pattern block
-void Read_OKT_Pattern(const BYTE *lpStream, const DWORD dwMemLength, const PATTERNINDEX nPat, CSoundFile *pSndFile)
-//-----------------------------------------------------------------------------------------------------------------
+void ReadOKTPattern(FileReader &chunk, PATTERNINDEX nPat, CSoundFile *pSndFile)
+//-----------------------------------------------------------------------------
{
- #define ASSERT_CAN_READ_OKTPAT(x) ASSERT_CAN_READ_PROTOTYPE(dwMemPos, dwMemLength, x, return);
+ if(!chunk.CanRead(2))
+ {
+ return;
+ }
- DWORD dwMemPos = 0;
+ ROWINDEX rows = Clamp(static_cast<ROWINDEX>(chunk.ReadUint16BE()), ROWINDEX(1), MAX_PATTERN_ROWS);
- ASSERT_CAN_READ_OKTPAT(2);
- ROWINDEX nRows = CLAMP(BigEndianW(*(uint16 *)(lpStream + dwMemPos)), 1, MAX_PATTERN_ROWS);
- dwMemPos += 2;
-
- if(pSndFile->Patterns.Insert(nPat, nRows))
+ if(pSndFile->Patterns.Insert(nPat, rows))
+ {
return;
+ }
- const CHANNELINDEX nChns = pSndFile->GetNumChannels();
- ModCommand *mrow = pSndFile->Patterns[nPat], *m;
+ const CHANNELINDEX chns = pSndFile->GetNumChannels();
- for(ROWINDEX nRow = 0; nRow < nRows; nRow++, mrow += nChns)
+ for(ROWINDEX row = 0; row < rows; row++)
{
- m = mrow;
- for(CHANNELINDEX nChn = 0; nChn < nChns; nChn++, m++)
+ ModCommand *m = pSndFile->Patterns[nPat].GetRow(row);
+ for(CHANNELINDEX chn = 0; chn < chns; chn++, m++)
{
- ASSERT_CAN_READ_OKTPAT(4);
- m->note = lpStream[dwMemPos++];
- m->instr = lpStream[dwMemPos++];
- int8 fxcmd = lpStream[dwMemPos++];
- m->param = lpStream[dwMemPos++];
+ uint8 note = chunk.ReadUint8();
+ uint8 instr = chunk.ReadUint8();
+ uint8 effect = chunk.ReadUint8();
+ m->param = chunk.ReadUint8();
- if(m->note > 0 && m->note <= 36)
+ if(note > 0 && note <= 36)
{
- m->note += 48;
- m->instr++;
+ m->note = note + 48;
+ m->instr = instr + 1;
} else
{
m->instr = 0;
}
- switch(fxcmd)
+ switch(effect)
{
case 0: // Nothing
m->param = 0;
@@ -152,9 +162,9 @@
For now I'm going to leave these unimplemented. */
case 10: // A Arpeggio 1 (down, orig, up)
case 11: // B Arpeggio 2 (orig, up, orig, down)
- if (note->param)
- note->command = CMD_ARPEGGIO;
- break;
+ if (m->param)
+ m->command = CMD_ARPEGGIO;
+ break;
#endif
// This one is close enough to "standard" arpeggio -- I think!
case 12: // C Arpeggio 3 (up, up, orig)
@@ -260,27 +270,22 @@
}
}
}
-
- #undef ASSERT_CAN_READ_OKTPAT
}
-bool CSoundFile::ReadOKT(const BYTE *lpStream, const DWORD dwMemLength)
-//---------------------------------------------------------------------
+bool CSoundFile::ReadOKT(FileReader &file)
+//----------------------------------------
{
- DWORD dwMemPos = 0;
-
- ASSERT_CAN_READ(8);
- if (memcmp(lpStream, "OKTASONG", 8) != 0)
+ file.Rewind();
+ if(!file.ReadMagic("OKTASONG"))
+ {
return false;
- dwMemPos += 8;
+ }
- OKT_IFFCHUNK iffHead;
// prepare some arrays to store offsets etc.
- vector<DWORD> patternOffsets;
+ vector<FileReader> patternChunks;
+ vector<FileReader> sampleChunks;
vector<bool> sample7bit; // 7-/8-bit sample
- vector<OKT_SAMPLEINFO> samplePos;
- PATTERNINDEX nPatterns = 0;
ORDERINDEX nOrders = 0;
MemsetZero(m_szNames);
@@ -288,24 +293,33 @@
m_nSamples = 0;
// Go through IFF chunks...
- while(dwMemPos < dwMemLength)
+ while(file.BytesLeft())
{
- ASSERT_CAN_READ(sizeof(OKT_IFFCHUNK));
- memcpy(&iffHead, lpStream + dwMemPos, sizeof(OKT_IFFCHUNK));
- iffHead.signature = BigEndian(iffHead.signature);
- iffHead.chunksize = BigEndian(iffHead.chunksize);
- dwMemPos += sizeof(OKT_IFFCHUNK);
+ OktIffChunk iffHead;
+ if(!file.ReadConvertEndianness(iffHead))
+ {
+ break;
+ }
+ FileReader chunk = file.GetChunk(iffHead.chunksize);
+ if(!chunk.IsValid())
+ {
+ break;
+ }
+
switch(iffHead.signature)
{
- case OKTCHUNKID_CMOD:
+ case OktIffChunk::idCMOD:
// read that weird channel setup table
- if(m_nChannels > 0)
+ if(m_nChannels > 0 || chunk.GetLength() < 8)
+ {
break;
- ASSERT_CAN_READ(8);
+ }
+
for(CHANNELINDEX nChn = 0; nChn < 4; nChn++)
{
- if(lpStream[dwMemPos + nChn * 2] || lpStream[dwMemPos + nChn * 2 + 1])
+ uint8 ch1 = chunk.ReadUint8(), ch2 = chunk.ReadUint8();
+ if(ch1 || ch2)
{
ChnSettings[m_nChannels++].nPan = (((nChn & 3) == 1) || ((nChn & 3) == 2)) ? 0xC0 : 0x40;
}
@@ -313,66 +327,56 @@
}
break;
- case OKTCHUNKID_SAMP:
+ case OktIffChunk::idSAMP:
// convert sample headers
if(m_nSamples > 0)
+ {
break;
- ASSERT_CAN_READ(iffHead.chunksize);
- Read_OKT_Samples(lpStream + dwMemPos, iffHead.chunksize, sample7bit, this);
+ }
+ ReadOKTSamples(chunk, sample7bit, this);
break;
- case OKTCHUNKID_SPEE:
+ case OktIffChunk::idSPEE:
// read default speed
+ if(chunk.GetLength() >= 2)
{
- ASSERT_CAN_READ(2);
- uint16 defspeed = BigEndianW(*((uint16 *)(lpStream + dwMemPos)));
- m_nDefaultSpeed = CLAMP(defspeed, 1, 255);
+ m_nDefaultSpeed = Clamp(chunk.ReadUint16BE(), uint16(1), uint16(255));
}
break;
- case OKTCHUNKID_SLEN:
+ case OktIffChunk::idSLEN:
// number of patterns, we don't need this.
break;
- case OKTCHUNKID_PLEN:
+ case OktIffChunk::idPLEN:
// read number of valid orders
- ASSERT_CAN_READ(2);
- nOrders = BigEndianW(*((uint16 *)(lpStream + dwMemPos)));
+ if(chunk.GetLength() >= 2)
+ {
+ nOrders = chunk.ReadUint16BE();
+ }
break;
- case OKTCHUNKID_PATT:
+ case OktIffChunk::idPATT:
// read the orderlist
- ASSERT_CAN_READ(iffHead.chunksize);
- Order.ReadAsByte(lpStream + dwMemPos, min(iffHead.chunksize, MAX_ORDERS), iffHead.chunksize);
+ Order.ReadAsByte(chunk, chunk.GetLength());
break;
- case OKTCHUNKID_PBOD:
+ case OktIffChunk::idPBOD:
// don't read patterns for now, as the number of channels might be unknown at this point.
- if(nPatterns < MAX_PATTERNS)
+ if(patternChunks.size() < MAX_PATTERNS)
{
- if(iffHead.chunksize > 0)
- patternOffsets.push_back(dwMemPos);
- nPatterns++;
+ patternChunks.push_back(chunk);
}
break;
- case OKTCHUNKID_SBOD:
+ case OktIffChunk::idSBOD:
// sample data - same as with patterns, as we need to know the sample format / length
- if(samplePos.size() < MAX_SAMPLES - 1)
+ if(sampleChunks.size() < MAX_SAMPLES - 1 && chunk.GetLength() > 0)
{
- ASSERT_CAN_READ(iffHead.chunksize);
- if(iffHead.chunksize)
- {
- OKT_SAMPLEINFO sinfo;
- sinfo.start = dwMemPos;
- sinfo.length = iffHead.chunksize;
- samplePos.push_back(sinfo);
- }
+ sampleChunks.push_back(chunk);
}
break;
}
-
- dwMemPos += iffHead.chunksize;
}
// If there wasn't even a CMOD chunk, we can't really load this.
@@ -383,8 +387,8 @@
m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
m_nSamplePreAmp = m_nVSTiVolume = 48;
m_nType = MOD_TYPE_OKT;
- m_nMinPeriod = 0x71 << 2;
- m_nMaxPeriod = 0x358 << 2;
+ m_nMinPeriod = 0x71 * 4;
+ m_nMaxPeriod = 0x358 * 4;
// Fix orderlist
for(ORDERINDEX nOrd = nOrders; nOrd < Order.GetLengthTailTrimmed(); nOrd++)
@@ -393,10 +397,10 @@
}
// Read patterns
- for(PATTERNINDEX nPat = 0; nPat < nPatterns; nPat++)
+ for(PATTERNINDEX nPat = 0; nPat < patternChunks.size(); nPat++)
{
- if(patternOffsets[nPat] > 0)
- Read_OKT_Pattern(lpStream + patternOffsets[nPat], dwMemLength - patternOffsets[nPat], nPat, this);
+ if(patternChunks[nPat].GetLength() > 0)
+ ReadOKTPattern(patternChunks[nPat], nPat, this);
else
Patterns.Insert(nPat, 64); // invent empty pattern
}
@@ -405,26 +409,28 @@
size_t nFileSmp = 0;
for(SAMPLEINDEX nSmp = 1; nSmp < m_nSamples; nSmp++)
{
- if(nFileSmp >= samplePos.size())
+ if(nFileSmp >= sampleChunks.size())
break;
- ModSample *pSmp = &Samples[nSmp];
- if(pSmp->nLength == 0)
+ ModSample &mptSample = Samples[nSmp];
+ if(mptSample.nLength == 0)
continue;
// weird stuff?
- if(pSmp->nLength != samplePos[nFileSmp].length)
- {
- pSmp->nLength = min(pSmp->nLength, samplePos[nFileSmp].length);
- }
+ mptSample.nLength = min(mptSample.nLength, sampleChunks[nFileSmp].GetLength());
- ReadSample(pSmp, RS_PCM8S, (LPCSTR)(lpStream + samplePos[nFileSmp].start), dwMemLength - samplePos[nFileSmp].start);
+ ReadSample(&mptSample, RS_PCM8S, sampleChunks[nFileSmp]);
// 7-bit to 8-bit hack
- if(sample7bit[nSmp - 1] && pSmp->pSample)
+ if(sample7bit[nSmp - 1] && mptSample.pSample)
{
- for(size_t i = 0; i < pSmp->nLength; i++)
- pSmp->pSample[i] = CLAMP(pSmp->pSample[i] * 2, -128, 127);
+ int8 *data = reinterpret_cast<int8 *>(mptSample.pSample);
+ SmpLength i = mptSample.nLength;
+ while(i--)
+ {
+ *data = Clamp(*data * 2, int8(-128), int8(127));
+ *data++;
+ }
}
nFileSmp++;
Modified: trunk/OpenMPT/soundlib/Loaders.h
===================================================================
--- trunk/OpenMPT/soundlib/Loaders.h 2012-03-27 21:11:51 UTC (rev 1231)
+++ trunk/OpenMPT/soundlib/Loaders.h 2012-03-30 20:12:57 UTC (rev 1232)
@@ -9,12 +9,424 @@
#pragma once
-#include "Sndfile.h"
+#include "Endianness.h"
#include "../common/StringFixer.h"
// Execute "action" if "request_bytes" bytes cannot be read from stream at position "position"
+// DEPRECATED. Use FileReader instead.
#define ASSERT_CAN_READ_PROTOTYPE(position, length, request_bytes, action) \
if((position) > (length) || (request_bytes) > (length) - (position)) action;
// "Default" macro for checking if x bytes can be read from stream.
+// DEPRECATED. Use FileReader instead.
#define ASSERT_CAN_READ(x) ASSERT_CAN_READ_PROTOTYPE(dwMemPos, dwMemLength, x, return false);
+
+
+//==============
+class FileReader
+//==============
+{
+private:
+ const char *streamData; // Pointer to memory-mapped file
+ size_t streamLength; // Size of memory-mapped file in bytes
+ size_t streamPos; // Cursor location in the file
+
+public:
+ FileReader(const char *data, size_t length) : streamData(data), streamLength(length), streamPos(0) { }
+
+ // Returns true if the object points to a valid stream.
+ bool IsValid() const
+ {
+ return streamData != nullptr;
+ }
+
+ // Reset cursor to first byte in file.
+ void Rewind()
+ {
+ streamPos = 0;
+ }
+
+ // Seek to a position in the mapped file.
+ // Returns false if position is invalid.
+ bool Seek(size_t position)
+ {
+ if(position < streamLength)
+ {
+ streamPos = position;
+ return true;
+ } else
+ {
+ return false;
+ }
+ }
+
+ // Increases position by skipBytes.
+ // Returns true if skipBytes could be skipped or false if the file end was reached earlier.
+ bool Skip(size_t skipBytes)
+ {
+ if(BytesLeft() >= skipBytes)
+ {
+ streamPos += skipBytes;
+ return true;
+ } else
+ {
+ streamPos = streamLength;
+ return false;
+ }
+ }
+
+ // Returns cursor position in the mapped file.
+ size_t GetPosition() const
+ {
+ return streamPos;
+ }
+
+ // Returns size of the mapped file in bytes.
+ size_t GetLength() const
+ {
+ return streamLength;
+ }
+
+ // Return byte count between cursor position and end of file, i.e. how many bytes can still be read.
+ size_t BytesLeft() const
+ {
+ ASSERT(streamPos <= streamLength);
+ return streamLength - streamPos;
+ }
+
+ // Check if "amount" bytes can be read from the current position in the stream.
+ bool CanRead(size_t amount) const
+ {
+ return (amount <= BytesLeft());
+ }
+
+ // Create a new FileReader object for parsing a sub chunk at a given position with a given length.
+ // The file cursor is not modified.
+ FileReader GetChunk(size_t position, size_t length) const
+ {
+ if(position < streamLength)
+ {
+ return FileReader(streamData + position, min(length, streamLength - position));
+ } else
+ {
+ return FileReader(nullptr, 0);
+ }
+ }
+
+ // Create a new FileReader object for parsing a sub chunk at the current position with a given length.
+ // The file cursor is advanced by "length" bytes.
+ FileReader GetChunk(size_t length)
+ {
+ size_t position = streamPos;
+ Skip(length);
+ return GetChunk(position, length);
+ }
+
+ // Returns raw stream data at cursor position.
+ // Should only be used if absolutely necessary, for example for sample reading.
+ const char *GetRawData() const
+ {
+ return streamData + streamPos;
+ }
+
+ // Read a "T" object from the stream.
+ // If not enough bytes can be read, false is returned.
+ // If successful, the file cursor is advanced by the size of "T".
+ template <typename T>
+ bool Read(T &target)
+ {
+ if(CanRead(sizeof(T)))
+ {
+ target = *reinterpret_cast<const T *>(streamData + streamPos);
+ streamPos += sizeof(T);
+ return true;
+ } else
+ {
+ return false;
+ }
+ }
+
+protected:
+ // Read some kind of integer in little-endian format.
+ // If successful, the file cursor is advanced by the size of the integer.
+ template <typename T>
+ T ReadIntLE()
+ {
+ static_assert(std::numeric_limits<T>::is_integer == true, "Target type is a not an integer");
+ T target;
+ if(Read(target))
+ {
+ SwapBytesLE(target);
+ return target;
+ } else
+ {
+ return 0;
+ }
+ }
+
+ // Read some kind of integer in big-endian format.
+ // If successful, the file cursor is advanced by the size of the integer.
+ template <typename T>
+ T ReadIntBE()
+ {
+ static_assert(std::numeric_limits<T>::is_integer == true, "Target type is a not an integer");
+ T target;
+ if(Read(target))
+ {
+ SwapBytesBE(target);
+ return target;
+ } else
+ {
+ return 0;
+ }
+ }
+
+public:
+ // Read unsigned 32-Bit integer in little-endian format.
+ // If successful, the file cursor is advanced by the size of the integer.
+ uint32 ReadUint32LE()
+ {
+ return ReadIntLE<uint32>();
+ }
+
+ // Read unsigned 32-Bit integer in big-endian format.
+ // If successful, the file cursor is advanced by the size of the integer.
+ uint32 ReadUint32BE()
+ {
+ return ReadIntBE<uint32>();
+ }
+
+ // Read signed 32-Bit integer in little-endian format.
+ // If successful, the file cursor is advanced by the size of the integer.
+ int32 ReadInt32LE()
+ {
+ return ReadIntLE<int32>();
+ }
+
+ // Read signed 32-Bit integer in big-endian format.
+ // If successful, the file cursor is advanced by the size of the integer.
+ int32 ReadInt32BE()
+ {
+ return ReadIntBE<int32>();
+ }
+
+ // Read unsigned 16-Bit integer in little-endian format.
+ // If successful, the file cursor is advanced by the size of the integer.
+ uint16 ReadUint16LE()
+ {
+ return ReadIntLE<uint16>();
+ }
+
+ // Read unsigned 16-Bit integer in big-endian format.
+ // If successful, the file cursor is advanced by the size of the integer.
+ uint16 ReadUint16BE()
...
[truncated message content] |