|
From: <sag...@us...> - 2012-06-15 22:01:30
|
Revision: 1301
http://modplug.svn.sourceforge.net/modplug/?rev=1301&view=rev
Author: saga-games
Date: 2012-06-15 22:01:22 +0000 (Fri, 15 Jun 2012)
Log Message:
-----------
[Ref] Rewrote J2B / PSM loaders to use chunk-based file reader.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_psm.cpp
trunk/OpenMPT/soundlib/load_j2b.cpp
Modified: trunk/OpenMPT/soundlib/Load_psm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_psm.cpp 2012-06-15 21:48:47 UTC (rev 1300)
+++ trunk/OpenMPT/soundlib/Load_psm.cpp 2012-06-15 22:01:22 UTC (rev 1301)
@@ -22,6 +22,7 @@
#include "stdafx.h"
#include "Loaders.h"
+#include "ChunkReader.h"
#ifdef MODPLUG_TRACKER
#include "../mptrack/moddoc.h"
#endif // MODPLUG_TRACKER
@@ -74,15 +75,22 @@
idDSMP = 0x504D5344,
};
+ typedef ChunkIdentifiers id_type;
+
uint32 id;
uint32 length;
- // Convert all multi-byte numeric values to current platform's endianness or vice versa.
- void ConvertEndianness()
+ size_t GetLength() const
{
- SwapBytesLE(id);
- SwapBytesLE(length);
+ uint32 l = length;
+ return SwapBytesLE(l);
}
+
+ id_type GetID() const
+ {
+ uint32 i = id;
+ return static_cast<id_type>(SwapBytesLE(i));
+ }
};
// Song Information
@@ -284,350 +292,337 @@
vector<PSMSubSong> subsongs;
bool subsongPanningDiffers = false; // do we have subsongs with different panning positions?
- while(file.BytesLeft())
+ ChunkReader chunkFile(file);
+ ChunkReader::ChunkList<PSMChunk> chunks = chunkFile.ReadChunks<PSMChunk>(1);
+
+ // "TITL" - Song Title
+ FileReader titleChunk = chunks.GetChunk(PSMChunk::idTITL);
+ titleChunk.ReadString<StringFixer::spacePadded>(m_szNames[0], titleChunk.GetLength());
+
+ // "SDFT" - Format info (song data starts here)
+ if(!chunks.GetChunk(PSMChunk::idSDFT).ReadMagic("MAINSONG"))
{
- // Go through the chunks
- PSMChunk chunkHead;
- if(!file.ReadConvertEndianness(chunkHead))
+ return false;
+ }
+
+ // "PBOD" - Pattern data of a single pattern
+ vector<FileReader> pattChunks = chunks.GetAllChunks(PSMChunk::idPBOD);
+ for(vector<FileReader>::iterator patternIter = pattChunks.begin(); patternIter != pattChunks.end(); patternIter++)
+ {
+ ChunkReader chunk(*patternIter);
+ if(chunk.GetLength() != chunk.ReadUint32LE() // Same value twice
+ || chunk.GetLength() < 8)
{
- break;
+ continue;
}
- FileReader chunk = file.GetChunk(chunkHead.length);
-
- switch(chunkHead.id)
+ // Pattern ID (something like "P0 " or "P13 ", or "PATT0 " in Sinaria) follows
+ char patternID[5];
+ if(!chunk.ReadString<StringFixer::spacePadded>(patternID, 4) || patternID[0] != 'P')
{
- case PSMChunk::idTITL: // "TITL" - Song Title
- chunk.ReadString<StringFixer::spacePadded>(m_szNames[0], chunk.GetLength());
- break;
+ continue;
+ }
+ if(!memcmp(patternID, "PATT", 4))
+ {
+ // New format has four additional bytes - read patternID again.
+ newFormat = true;
+ chunk.ReadString<StringFixer::spacePadded>(patternID, 4);
+ }
+ patternIDs.push_back(atoi(&patternID[newFormat ? 0 : 1]));
+ // We're going to read the rest of the pattern data later.
+ patternChunks.push_back(chunk.GetChunk(chunk.BytesLeft()));
- case PSMChunk::idSDFT: // "SDFT" - Format info (song data starts here)
- if(chunk.GetLength() != 8 || !chunk.ReadMagic("MAINSONG"))
- {
- return false;
- }
- break;
+ // Convert later as we have to know how many channels there are.
+ }
- case PSMChunk::idPBOD: // "PBOD" - Pattern data of a single pattern
- if(chunk.GetLength() != chunk.ReadUint32LE() // Same value twice
- || chunk.GetLength() < 8)
- {
- break;
- }
+ // "SONG" - Subsong information (channel count etc)
+ vector<FileReader> songChunks = chunks.GetAllChunks(PSMChunk::idSONG);
+ if(songChunks.empty())
+ {
+ return false;
+ }
- // Pattern ID (something like "P0 " or "P13 ", or "PATT0 " in Sinaria) follows
- char patternID[5];
- if(!chunk.ReadString<StringFixer::spacePadded>(patternID, 4) || patternID[0] != 'P')
- {
- break;
- }
- if(!memcmp(patternID, "PATT", 4))
- {
- // New format has four additional bytes - read patternID again.
- newFormat = true;
- chunk.ReadString<StringFixer::spacePadded>(patternID, 4);
- }
- patternIDs.push_back(atoi(&patternID[newFormat ? 0 : 1]));
- // We're going to read the rest of the pattern data later.
- patternChunks.push_back(chunk.GetChunk(chunk.BytesLeft()));
+ for(vector<FileReader>::iterator subsongIter = songChunks.begin(); subsongIter != songChunks.end(); subsongIter++)
+ {
+ ChunkReader chunk(*subsongIter);
+ PSMSongHeader songHeader;
+ if(!chunk.Read(songHeader))
+ {
+ return false;
+ }
+ if(songHeader.compression != 0x01)
+ {
+ // No compression for PSM files
+ return false;
+ }
+ // Subsongs *might* have different channel count
+ m_nChannels = Clamp(static_cast<CHANNELINDEX>(songHeader.numChannels), m_nChannels, MAX_BASECHANNELS);
- // Convert later as we have to know how many channels there are.
- break;
+ PSMSubSong subsong;
+ subsong.restartPos = (ORDERINDEX)Order.size(); // restart order "offset": current orderlist length
+ StringFixer::ReadString<StringFixer::nullTerminated>(subsong.songName, songHeader.songType); // subsong name
- case PSMChunk::idSONG: // "SONG" - Subsong information (channel count etc)
+ // Read "Sub sub chunks"
+ ChunkReader::ChunkList<PSMChunk> subChunks = chunk.ReadChunks<PSMChunk>(1);
+
+ for(ChunkReader::ChunkList<PSMChunk>::iterator subChunkIter = subChunks.begin(); subChunkIter != subChunks.end(); subChunkIter++)
+ {
+ FileReader subChunk(subChunkIter->GetData());
+ PSMChunk subChunkHead = subChunkIter->GetHeader();
+
+ switch(subChunkHead.id)
{
- PSMSongHeader songHeader;
- if(!chunk.Read(songHeader))
+ case PSMChunk::idDATE: // "DATE" - Conversion date (YYMMDD)
+ if(subChunkHead.length == 6)
{
- break;
+ char cversion[7];
+ subChunk.ReadString<StringFixer::maybeNullTerminated>(cversion, 6);
+ int version = atoi(cversion);
+ // Sinaria song dates (just to go sure...)
+ if(version == 800211 || version == 940902 || version == 940903 ||
+ version == 940906 || version == 940914 || version == 941213)
+ newFormat = true;
}
- if(songHeader.compression != 0x01)
+ break;
+
+ case PSMChunk::idOPLH: // "OPLH" - Order list, channel + module settings
+ if(subChunkHead.length >= 9)
{
- // No compression for PSM files
- return false;
- }
- // Subsongs *might* have different channel count
- m_nChannels = Clamp(static_cast<CHANNELINDEX>(songHeader.numChannels), m_nChannels, MAX_BASECHANNELS);
+ // First two bytes = Number of chunks that follow
+ //uint16 totalChunks = subChunk.ReadUint16LE();
+ subChunk.Skip(2);
- PSMSubSong subsong;
- subsong.restartPos = (ORDERINDEX)Order.size(); // restart order "offset": current orderlist length
- StringFixer::ReadString<StringFixer::nullTerminated>(subsong.songName, songHeader.songType); // subsong name
+ // Now, the interesting part begins!
+ uint16 chunkCount = 0, firstOrderChunk = uint16_max;
- // Read "Sub sub chunks"
- FileReader subChunk = chunk.GetChunk(chunk.BytesLeft());
- while(subChunk.BytesLeft())
- {
- PSMChunk subChunkHead;
- if(!subChunk.ReadConvertEndianness(subChunkHead))
+ // "Sub sub sub chunks" (grrrr, silly format)
+ while(subChunk.BytesLeft())
{
- break;
- }
-
- switch(subChunkHead.id)
- {
- case PSMChunk::idDATE: // "DATE" - Conversion date (YYMMDD)
- if(subChunkHead.length == 6)
+ uint8 subChunkID = subChunk.ReadUint8();
+ if(!subChunkID)
{
- char cversion[7];
- subChunk.ReadString<StringFixer::maybeNullTerminated>(cversion, 6);
- int version = atoi(cversion);
- // Sinaria song dates (just to go sure...)
- if(version == 800211 || version == 940902 || version == 940903 ||
- version == 940906 || version == 940914 || version == 941213)
- newFormat = true;
+ // Last chunk was reached.
+ break;
}
- break;
- case PSMChunk::idOPLH: // "OPLH" - Order list, channel + module settings
- if(subChunkHead.length >= 9)
+ switch(subChunkID)
{
- // First two bytes = Number of chunks that follow
- //uint16 totalChunks = subChunk.ReadUint16LE();
- subChunk.Skip(2);
-
- // Now, the interesting part begins!
- uint16 chunkCount = 0, firstOrderChunk = uint16_max;
-
- // "Sub sub sub chunks" (grrrr, silly format)
- while(subChunk.BytesLeft())
+ case 0x01: // Order list item
+ // Pattern name follows - find pattern (this is the orderlist)
{
- uint8 subChunkID = subChunk.ReadUint8();
- if(!subChunkID)
+ char patternID[5]; // temporary
+ if(newFormat)
{
- // Last chunk was reached.
- break;
+ subChunk.Skip(4);
+ subChunk.ReadString<StringFixer::spacePadded>(patternID, 4);
+ } else
+ {
+ subChunk.Skip(1);
+ subChunk.ReadString<StringFixer::spacePadded>(patternID, 3);
}
+ uint32 pat = atoi(patternID);
- switch(subChunkID)
+ // seek which pattern has this ID
+ for(size_t i = 0; i < patternIDs.size(); i++)
{
- case 0x01: // Order list item
- // Pattern name follows - find pattern (this is the orderlist)
+ if(patternIDs[i] == pat)
{
- char patternID[5]; // temporary
- if(newFormat)
- {
- subChunk.Skip(4);
- subChunk.ReadString<StringFixer::spacePadded>(patternID, 4);
- } else
- {
- subChunk.Skip(1);
- subChunk.ReadString<StringFixer::spacePadded>(patternID, 3);
- }
- uint32 pat = atoi(patternID);
+ // found the right pattern, copy offset + start / end positions.
+ if(subsong.startOrder == ORDERINDEX_INVALID)
+ subsong.startOrder = static_cast<ORDERINDEX>(orderOffsets.size());
+ subsong.endOrder = static_cast<ORDERINDEX>(orderOffsets.size());
- // seek which pattern has this ID
- for(size_t i = 0; i < patternIDs.size(); i++)
- {
- if(patternIDs[i] == pat)
- {
- // found the right pattern, copy offset + start / end positions.
- if(subsong.startOrder == ORDERINDEX_INVALID)
- subsong.startOrder = static_cast<ORDERINDEX>(orderOffsets.size());
- subsong.endOrder = static_cast<ORDERINDEX>(orderOffsets.size());
-
- // every pattern in the order will be unique, so store the pointer + pattern ID
- orderOffsets.push_back(&patternChunks[i]);
- Order.Append(numPatterns);
- numPatterns++;
- break;
- }
- }
+ // every pattern in the order will be unique, so store the pointer + pattern ID
+ orderOffsets.push_back(&patternChunks[i]);
+ Order.Append(numPatterns);
+ numPatterns++;
+ break;
}
- // Decide whether this is the first order chunk or not (for finding out the correct restart position)
- if(firstOrderChunk == uint16_max) firstOrderChunk = chunkCount;
- break;
+ }
+ }
+ // Decide whether this is the first order chunk or not (for finding out the correct restart position)
+ if(firstOrderChunk == uint16_max) firstOrderChunk = chunkCount;
+ break;
- case 0x04: // Restart position
- {
- uint16 restartChunk = subChunk.ReadUint16LE();
- ORDERINDEX restartPosition = 0;
- if(restartChunk >= firstOrderChunk) restartPosition = static_cast<ORDERINDEX>(restartChunk - firstOrderChunk);
- subsong.restartPos += restartPosition;
- }
- break;
+ case 0x04: // Restart position
+ {
+ uint16 restartChunk = subChunk.ReadUint16LE();
+ ORDERINDEX restartPosition = 0;
+ if(restartChunk >= firstOrderChunk) restartPosition = static_cast<ORDERINDEX>(restartChunk - firstOrderChunk);
+ subsong.restartPos += restartPosition;
+ }
+ break;
- case 0x07: // Default Speed
- subsong.defaultSpeed = subChunk.ReadUint8();
- break;
+ case 0x07: // Default Speed
+ subsong.defaultSpeed = subChunk.ReadUint8();
+ break;
- case 0x08: // Default Tempo
- subsong.defaultTempo = subChunk.ReadUint8();
- break;
+ case 0x08: // Default Tempo
+ subsong.defaultTempo = subChunk.ReadUint8();
+ break;
- case 0x0C: // Sample map table (???)
- // Never seems to be different, so...
- if (subChunk.ReadUint8() != 0x00 || subChunk.ReadUint8() != 0xFF ||
- subChunk.ReadUint8() != 0x00 || subChunk.ReadUint8() != 0x00 ||
- subChunk.ReadUint8() != 0x01 || subChunk.ReadUint8() != 0x00)
- {
- return false;
- }
- break;
+ case 0x0C: // Sample map table (???)
+ // Never seems to be different, so...
+ if (subChunk.ReadUint8() != 0x00 || subChunk.ReadUint8() != 0xFF ||
+ subChunk.ReadUint8() != 0x00 || subChunk.ReadUint8() != 0x00 ||
+ subChunk.ReadUint8() != 0x01 || subChunk.ReadUint8() != 0x00)
+ {
+ return false;
+ }
+ break;
- case 0x0D: // Channel panning table
+ case 0x0D: // Channel panning table
+ {
+ uint8 chn = subChunk.ReadUint8();
+ uint8 pan = subChunk.ReadUint8();
+ uint8 type = subChunk.ReadUint8();
+ if(chn < subsong.channelPanning.size())
+ {
+ switch(type)
{
- uint8 chn = subChunk.ReadUint8();
- uint8 pan = subChunk.ReadUint8();
- uint8 type = subChunk.ReadUint8();
- if(chn < subsong.channelPanning.size())
- {
- switch(type)
- {
- case 0: // use panning
- subsong.channelPanning[chn] = pan ^ 128;
- subsong.channelSurround[chn] = false;
- break;
+ case 0: // use panning
+ subsong.channelPanning[chn] = pan ^ 128;
+ subsong.channelSurround[chn] = false;
+ break;
- case 2: // surround
- subsong.channelPanning[chn] = 128;
- subsong.channelSurround[chn] = true;
- break;
+ case 2: // surround
+ subsong.channelPanning[chn] = 128;
+ subsong.channelSurround[chn] = true;
+ break;
- case 4: // center
- subsong.channelPanning[chn] = 128;
- subsong.channelSurround[chn] = false;
- break;
+ case 4: // center
+ subsong.channelPanning[chn] = 128;
+ subsong.channelSurround[chn] = false;
+ break;
- }
- if(subsongPanningDiffers == false && subsongs.size() > 0)
- {
- if(subsongs.back().channelPanning[chn] != subsong.channelPanning[chn]
- || subsongs.back().channelSurround[chn] != subsong.channelSurround[chn])
- subsongPanningDiffers = true;
- }
- }
}
- break;
-
- case 0x0E: // Channel volume table (0...255) - apparently always 255
+ if(subsongPanningDiffers == false && subsongs.size() > 0)
{
- uint8 chn = subChunk.ReadUint8();
- uint8 vol = subChunk.ReadUint8();
- if(chn < subsong.channelVolume.size())
- {
- subsong.channelVolume[chn] = (vol / 4) + 1;
- }
+ if(subsongs.back().channelPanning[chn] != subsong.channelPanning[chn]
+ || subsongs.back().channelSurround[chn] != subsong.channelSurround[chn])
+ subsongPanningDiffers = true;
}
- break;
-
- default: // How the hell should this happen? I've listened through almost all existing (original) PSM files. :)
- // anyway, in such cases, we have to quit as we don't know how big the chunk really is.
- return false;
-
}
- chunkCount++;
}
- // separate subsongs by "---" patterns
- orderOffsets.push_back(nullptr);
- Order.Append();
- }
- break;
+ break;
- case PSMChunk::idPPAN: // PPAN - Channel panning table (used in Sinaria)
- // In some Sinaria tunes, this is actually longer than 2 * channels...
- ASSERT(subChunkHead.length >= m_nChannels * 2u);
- for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
- {
- if(subChunk.BytesLeft() < 2)
+ case 0x0E: // Channel volume table (0...255) - apparently always 255
{
- break;
+ uint8 chn = subChunk.ReadUint8();
+ uint8 vol = subChunk.ReadUint8();
+ if(chn < subsong.channelVolume.size())
+ {
+ subsong.channelVolume[chn] = (vol / 4) + 1;
+ }
}
+ break;
- uint8 type = subChunk.ReadUint8();
- uint8 pan = subChunk.ReadUint8();
+ default: // How the hell should this happen? I've listened through almost all existing (original) PSM files. :)
+ // anyway, in such cases, we have to quit as we don't know how big the chunk really is.
+ return false;
- switch(type)
- {
- case 0: // use panning
- subsong.channelPanning[chn] = pan ^ 128;
- subsong.channelSurround[chn] = false;
- break;
-
- case 2: // surround
- subsong.channelPanning[chn] = 128;
- subsong.channelSurround[chn] = true;
- break;
-
- case 4: // center
- subsong.channelPanning[chn] = 128;
- subsong.channelSurround[chn] = false;
- break;
- }
}
+ chunkCount++;
+ }
+ // separate subsongs by "---" patterns
+ orderOffsets.push_back(nullptr);
+ Order.Append();
+ }
+ case PSMChunk::idPPAN: // PPAN - Channel panning table (used in Sinaria)
+ // In some Sinaria tunes, this is actually longer than 2 * channels...
+ ASSERT(subChunkHead.length >= m_nChannels * 2u);
+ for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
+ {
+ if(subChunk.BytesLeft() < 2)
+ {
break;
+ }
- case PSMChunk::idPATT: // PATT - Pattern list
- // We don't really need this.
- break;
+ uint8 type = subChunk.ReadUint8();
+ uint8 pan = subChunk.ReadUint8();
- case PSMChunk::idDSAM: // DSAM - Sample list
- // We don't need this either.
+ switch(type)
+ {
+ case 0: // use panning
+ subsong.channelPanning[chn] = pan ^ 128;
+ subsong.channelSurround[chn] = false;
break;
- default:
+ case 2: // surround
+ subsong.channelPanning[chn] = 128;
+ subsong.channelSurround[chn] = true;
break;
+ case 4: // center
+ subsong.channelPanning[chn] = 128;
+ subsong.channelSurround[chn] = false;
+ break;
}
}
+ break;
- // attach this subsong to the subsong list - finally, all "sub sub sub ..." chunks are parsed.
- subsongs.push_back(subsong);
+ case PSMChunk::idPATT: // PATT - Pattern list
+ // We don't really need this.
+ break;
+
+ case PSMChunk::idDSAM: // DSAM - Sample list
+ // We don't need this either.
+ break;
+
+ default:
+ break;
+
}
+ }
- break;
+ // attach this subsong to the subsong list - finally, all "sub sub sub ..." chunks are parsed.
+ subsongs.push_back(subsong);
+ }
- case PSMChunk::idDSMP: // DSMP - Samples
- if(!newFormat)
+ // // DSMP - Samples
+ vector<FileReader> sampleChunks = chunks.GetAllChunks(PSMChunk::idDSMP);
+ for(vector<FileReader>::iterator sampleIter = sampleChunks.begin(); sampleIter != sampleChunks.end(); sampleIter++)
+ {
+ FileReader chunk(*sampleIter);
+ if(!newFormat)
+ {
+ // Original header
+ PSMOldSampleHeader sampleHeader;
+ if(!chunk.ReadConvertEndianness(sampleHeader))
{
- // Original header
- PSMOldSampleHeader sampleHeader;
- if(!chunk.ReadConvertEndianness(sampleHeader))
- {
- break;
- }
+ continue;
+ }
- SAMPLEINDEX smp = static_cast<SAMPLEINDEX>(sampleHeader.sampleNumber + 1);
- if(smp < MAX_SAMPLES)
- {
- m_nSamples = max(m_nSamples, smp);
- StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[smp], sampleHeader.sampleName);
-
- sampleHeader.ConvertToMPT(Samples[smp]);
- sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk);
- }
- } else
+ SAMPLEINDEX smp = static_cast<SAMPLEINDEX>(sampleHeader.sampleNumber + 1);
+ if(smp < MAX_SAMPLES)
{
- // Sinaria uses a slightly different sample header
- PSMNewSampleHeader sampleHeader;
- if(!chunk.ReadConvertEndianness(sampleHeader))
- {
- break;
- }
+ m_nSamples = max(m_nSamples, smp);
+ StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[smp], sampleHeader.sampleName);
- SAMPLEINDEX smp = static_cast<SAMPLEINDEX>(sampleHeader.sampleNumber + 1);
- if(smp < MAX_SAMPLES)
- {
- m_nSamples = max(m_nSamples, smp);
- StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[smp], sampleHeader.sampleName);
-
- sampleHeader.ConvertToMPT(Samples[smp]);
- sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk);
- }
+ sampleHeader.ConvertToMPT(Samples[smp]);
+ sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk);
}
+ } else
+ {
+ // Sinaria uses a slightly different sample header
+ PSMNewSampleHeader sampleHeader;
+ if(!chunk.ReadConvertEndianness(sampleHeader))
+ {
+ continue;
+ }
- break;
+ SAMPLEINDEX smp = static_cast<SAMPLEINDEX>(sampleHeader.sampleNumber + 1);
+ if(smp < MAX_SAMPLES)
+ {
+ m_nSamples = max(m_nSamples, smp);
+ StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[smp], sampleHeader.sampleName);
- default:
- break;
-
+ sampleHeader.ConvertToMPT(Samples[smp]);
+ sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk);
+ }
}
}
- if(m_nChannels == 0 || subsongs.size() == 0)
- {
- return false;
- }
-
// Make the default variables of the first subsong global
m_nDefaultSpeed = subsongs[0].defaultSpeed;
m_nDefaultTempo = subsongs[0].defaultTempo;
@@ -1405,4 +1400,4 @@
}
return true;
-}
\ No newline at end of file
+}
Modified: trunk/OpenMPT/soundlib/load_j2b.cpp
===================================================================
--- trunk/OpenMPT/soundlib/load_j2b.cpp 2012-06-15 21:48:47 UTC (rev 1300)
+++ trunk/OpenMPT/soundlib/load_j2b.cpp 2012-06-15 22:01:22 UTC (rev 1301)
@@ -13,6 +13,7 @@
#include "stdafx.h"
#include "Loaders.h"
+#include "ChunkReader.h"
#ifndef ZLIB_WINAPI
#define ZLIB_WINAPI
#endif // ZLIB_WINAPI
@@ -20,7 +21,7 @@
// First off, a nice vibrato translation LUT.
-static const uint8 j2bAutoVibratoTrans[] =
+static const uint8 j2bAutoVibratoTrans[] =
{
VIB_SINE, VIB_SQUARE, VIB_RAMP_UP, VIB_RAMP_DOWN, VIB_RANDOM,
};
@@ -78,9 +79,23 @@
idAS__ = 0x20205341,
};
+ typedef ChunkIdentifiers id_type;
+
uint32 id; // See ChunkIdentifiers
uint32 length; // Chunk size without header
+ size_t GetLength() const
+ {
+ uint32 l = length;
+ return SwapBytesLE(l);
+ }
+
+ id_type GetID() const
+ {
+ uint32 i = id;
+ return static_cast<id_type>(SwapBytesLE(i));
+ }
+
// Convert all multi-byte numeric values to current platform's endianness or vice versa.
void ConvertEndianness()
{
@@ -283,6 +298,7 @@
// Convert sample header to OpenMPT's internal format.
void ConvertToMPT(AMFFInstrumentHeader &instrHeader, ModSample &mptSmp) const
{
+ mptSmp.Initialize();
mptSmp.nPan = pan * 4;
mptSmp.nVolume = volume * 4;
mptSmp.nGlobalVol = 64;
@@ -495,6 +511,7 @@
// Convert sample header to OpenMPT's internal format.
void ConvertToMPT(AMInstrumentHeader &instrHeader, ModSample &mptSmp) const
{
+ mptSmp.Initialize();
mptSmp.nPan = Util::Min(pan, static_cast<uint16>(32767)) * 256 / 32767;
mptSmp.nVolume = Util::Min(volume, static_cast<uint16>(32767)) * 256 / 32767;
mptSmp.nGlobalVol = 64;
@@ -539,8 +556,8 @@
// Convert RIFF AM(FF) pattern data to MPT pattern data.
-bool ConvertAMPattern(FileReader &chunk, PATTERNINDEX pat, bool isAM, CSoundFile &sndFile)
-//----------------------------------------------------------------------------------------
+bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSoundFile &sndFile)
+//---------------------------------------------------------------------------------------
{
// Effect translation LUT
static const uint8 amEffTrans[] =
@@ -718,245 +735,227 @@
m_nSamples = 0;
m_nInstruments = 0;
- // go through all chunks now
- while(file.BytesLeft())
+ ChunkReader chunkFile(file);
+ // RIFF AM has a padding byte so that all chunks have an even size.
+ ChunkReader::ChunkList<AMFFRiffChunk> chunks = chunkFile.ReadChunks<AMFFRiffChunk>(isAM ? 2 : 1);
+
+ // "MAIN" - Song info (AMFF)
+ // "INIT" - Song info (AM)
+ FileReader chunk(chunks.GetChunk(isAM ? AMFFRiffChunk::idINIT : AMFFRiffChunk::idMAIN));
+ AMFFMainChunk mainChunk;
+ if(!chunk.IsValid() || !chunk.Read(mainChunk))
{
- AMFFRiffChunk chunkHeader;
- if(!file.ReadConvertEndianness(chunkHeader))
- {
- break;
- }
+ return false;
+ }
- FileReader chunk = file.GetChunk(chunkHeader.length);
- if(!chunk.IsValid())
- {
- continue;
- }
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], mainChunk.songname);
- switch(chunkHeader.id)
- {
- case AMFFRiffChunk::idMAIN: // "MAIN" - Song info (AMFF)
- case AMFFRiffChunk::idINIT: // "INIT" - Song info (AM)
- if((chunkHeader.id == AMFFRiffChunk::idMAIN && !isAM) || (chunkHeader.id == AMFFRiffChunk::idINIT && isAM))
- {
- AMFFMainChunk mainChunk;
- if(!chunk.Read(mainChunk))
- {
- break;
- }
+ m_dwSongFlags = SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX;
+ if(!(mainChunk.flags & AMFFMainChunk::amigaSlides))
+ {
+ m_dwSongFlags |= SONG_LINEARSLIDES;
+ }
+ if(mainChunk.channels < 1 || !chunk.CanRead(mainChunk.channels))
+ {
+ return false;
+ }
+ m_nChannels = min(mainChunk.channels, MAX_BASECHANNELS);
+ m_nDefaultSpeed = mainChunk.speed;
+ m_nDefaultTempo = mainChunk.tempo;
+ m_nDefaultGlobalVolume = mainChunk.globalvolume * 2;
+ m_nSamplePreAmp = m_nVSTiVolume = 48;
+ m_nType = MOD_TYPE_J2B;
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], mainChunk.songname);
+ ASSERT(mainChunk.unknown == LittleEndian(0xFF0001C5) || mainChunk.unknown == LittleEndian(0x35800716) || mainChunk.unknown == LittleEndian(0xFF00FFFF));
- m_dwSongFlags = SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX;
- if(!(mainChunk.flags & AMFFMainChunk::amigaSlides))
- {
- m_dwSongFlags |= SONG_LINEARSLIDES;
- }
- if(mainChunk.channels < 1 || !chunk.CanRead(mainChunk.channels))
- {
- return false;
- }
- m_nChannels = Util::Min(static_cast<CHANNELINDEX>(mainChunk.channels), MAX_BASECHANNELS);
- m_nDefaultSpeed = mainChunk.speed;
- m_nDefaultTempo = mainChunk.tempo;
- m_nDefaultGlobalVolume = mainChunk.globalvolume * 2;
- m_nSamplePreAmp = m_nVSTiVolume = 48;
- m_nType = MOD_TYPE_J2B;
+ // It seems like there's no way to differentiate between
+ // Muted and Surround channels (they're all 0xA0) - might
+ // be a limitation in mod2j2b.
+ for(CHANNELINDEX nChn = 0; nChn < m_nChannels; nChn++)
+ {
+ ChnSettings[nChn].nVolume = 64;
+ ChnSettings[nChn].nPan = 128;
- ASSERT(mainChunk.unknown == LittleEndian(0xFF0001C5) || mainChunk.unknown == LittleEndian(0x35800716) || mainChunk.unknown == LittleEndian(0xFF00FFFF));
+ uint8 pan = chunk.ReadUint8();
- // It seems like there's no way to differentiate between
- // Muted and Surround channels (they're all 0xA0) - might
- // be a limitation in mod2j2b.
- for(CHANNELINDEX nChn = 0; nChn < m_nChannels; nChn++)
- {
- ChnSettings[nChn].nVolume = 64;
- ChnSettings[nChn].nPan = 128;
+ if(isAM)
+ {
+ if(pan > 128)
+ ChnSettings[nChn].dwFlags = CHN_MUTE;
+ else
+ ChnSettings[nChn].nPan = pan * 2;
+ } else
+ {
+ if(pan >= 128)
+ ChnSettings[nChn].dwFlags = CHN_MUTE;
+ else
+ ChnSettings[nChn].nPan = pan * 4;
+ }
+ }
- uint8 pan = chunk.ReadUint8();
+ if(chunks.ChunkExists(AMFFRiffChunk::idORDR))
+ {
+ // "ORDR" - Order list
+ FileReader chunk(chunks.GetChunk(AMFFRiffChunk::idORDR));
+ uint8 numOrders = chunk.ReadUint8() + 1;
+ Order.ReadAsByte(chunk, numOrders);
+ }
- if(isAM)
- {
- if(pan > 128)
- ChnSettings[nChn].dwFlags = CHN_MUTE;
- else
- ChnSettings[nChn].nPan = pan * 2;
- } else
- {
- if(pan >= 128)
- ChnSettings[nChn].dwFlags = CHN_MUTE;
- else
- ChnSettings[nChn].nPan = pan * 4;
- }
- }
- }
- break;
+ // "PATT" - Pattern data for one pattern
+ vector<FileReader> pattChunks = chunks.GetAllChunks(AMFFRiffChunk::idPATT);
+ for(vector<FileReader>::iterator patternIter = pattChunks.begin(); patternIter != pattChunks.end(); patternIter++)
+ {
+ FileReader chunk(*patternIter);
+ PATTERNINDEX pat = chunk.ReadUint8();
+ size_t patternSize = chunk.ReadUint32LE();
+ ConvertAMPattern(chunk.GetChunk(patternSize), pat, isAM, *this);
+ }
- case AMFFRiffChunk::idORDR: // "ORDR" - Order list
+ if(!isAM)
+ {
+ // "INST" - Instrument (only in RIFF AMFF)
+ vector<FileReader> instChunks = chunks.GetAllChunks(AMFFRiffChunk::idINST);
+ for(vector<FileReader>::iterator instIter = instChunks.begin(); instIter != instChunks.end(); instIter++)
+ {
+ FileReader chunk(*instIter);
+ AMFFInstrumentHeader instrHeader;
+ if(!chunk.ReadConvertEndianness(instrHeader))
{
- uint8 numOrders = chunk.ReadUint8() + 1;
- Order.ReadAsByte(chunk, numOrders);
+ continue;
}
- break;
- case AMFFRiffChunk::idPATT: // "PATT" - Pattern data for one pattern
+ const INSTRUMENTINDEX nIns = instrHeader.index + 1;
+ if(nIns >= MAX_INSTRUMENTS)
+ continue;
+
+ if(Instruments[nIns] != nullptr)
+ delete Instruments[nIns];
+
+ try
{
- PATTERNINDEX pat = chunk.ReadUint8();
- size_t patternSize = chunk.ReadUint32LE();
- FileReader patternChunk = chunk.GetChunk(patternSize);
- ConvertAMPattern(patternChunk, pat, isAM, *this);
+ Instruments[nIns] = new ModInstrument();
+ } catch(MPTMemoryException)
+ {
+ continue;
}
- break;
+ ModInstrument *pIns = Instruments[nIns];
- case AMFFRiffChunk::idINST: // "INST" - Instrument (only in RIFF AMFF)
- if(!isAM)
+ m_nInstruments = max(m_nInstruments, nIns);
+
+ instrHeader.ConvertToMPT(*pIns, m_nSamples);
+
+ // read sample sub-chunks - this is a rather "flat" format compared to RIFF AM and has no nested RIFF chunks.
+ for(size_t samples = 0; samples < instrHeader.numSamples; samples++)
{
- AMFFInstrumentHeader instrHeader;
- if(!chunk.ReadConvertEndianness(instrHeader))
+ AMFFSampleHeader sampleHeader;
+
+ if(m_nSamples + 1 >= MAX_SAMPLES || !chunk.ReadConvertEndianness(sampleHeader))
{
- break;
+ continue;
}
- const INSTRUMENTINDEX nIns = instrHeader.index + 1;
- if(nIns >= MAX_INSTRUMENTS)
- break;
+ const SAMPLEINDEX smp = ++m_nSamples;
- if(Instruments[nIns] != nullptr)
- delete Instruments[nIns];
-
- try
+ if(sampleHeader.id != AMFFRiffChunk::idSAMP)
{
- Instruments[nIns] = new ModInstrument();
- } catch(MPTMemoryException)
- {
- break;
+ continue;
}
- ModInstrument *pIns = Instruments[nIns];
- m_nInstruments = max(m_nInstruments, nIns);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[smp], sampleHeader.name);
+ sampleHeader.ConvertToMPT(instrHeader, Samples[smp]);
+ sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk);
+ }
+ }
+ } else
+ {
+ // "RIFF" - Instrument (only in RIFF AM)
+ vector<FileReader> instChunks = chunks.GetAllChunks(AMFFRiffChunk::idRIFF);
+ for(vector<FileReader>::iterator instIter = instChunks.begin(); instIter != instChunks.end(); instIter++)
+ {
+ ChunkReader chunk(*instIter);
+ if(chunk.ReadUint32LE() != AMFFRiffChunk::idAI__)
+ {
+ continue;
+ }
- instrHeader.ConvertToMPT(*pIns, m_nSamples);
+ AMFFRiffChunk instChunk;
+ if(!chunk.ReadConvertEndianness(instChunk) || instChunk.id != AMFFRiffChunk::idINST)
+ {
+ continue;
+ }
- // read sample sub-chunks - this is a rather "flat" format compared to RIFF AM and has no nested RIFF chunks.
- for(size_t samples = 0; samples < instrHeader.numSamples; samples++)
- {
- AMFFSampleHeader sampleHeader;
+ AMInstrumentHeader instrHeader;
+ if(!chunk.ReadConvertEndianness(instrHeader))
+ {
+ continue;
+ }
+ ASSERT(instrHeader.headSize + 4 == sizeof(instrHeader));
- if(m_nSamples + 1 >= MAX_SAMPLES || !chunk.ReadConvertEndianness(sampleHeader))
- {
- break;
- }
+ const INSTRUMENTINDEX instr = instrHeader.index + 1;
+ if(instr >= MAX_INSTRUMENTS)
+ continue;
- const SAMPLEINDEX smp = ++m_nSamples;
+ if(Instruments[instr] != nullptr)
+ delete Instruments[instr];
- MemsetZero(Samples[smp]);
+ try
+ {
+ Instruments[instr] = new ModInstrument();
+ } catch(MPTMemoryException)
+ {
+ continue;
+ }
+ ModInstrument *pIns = Instruments[instr];
+ m_nInstruments = max(m_nInstruments, instr);
- if(sampleHeader.id != AMFFRiffChunk::idSAMP)
- {
- break;
- }
+ instrHeader.ConvertToMPT(*pIns, m_nSamples);
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[smp], sampleHeader.name);
- sampleHeader.ConvertToMPT(instrHeader, Samples[smp]);
- sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk);
- }
- }
- break;
+ // Read sample sub-chunks (RIFF nesting ftw)
+ ChunkReader::ChunkList<AMFFRiffChunk> sampleChunkFile = chunk.ReadChunks<AMFFRiffChunk>(2);
+ vector<FileReader> sampleChunks = sampleChunkFile.GetAllChunks(AMFFRiffChunk::idRIFF);
+ ASSERT(sampleChunks.size() == instrHeader.numSamples);
- case AMFFRiffChunk::idRIFF: // "RIFF" - Instrument (only in RIFF AM)
- if(isAM)
+ for(vector<FileReader>::iterator smpIter = sampleChunks.begin(); smpIter != sampleChunks.end(); smpIter++)
{
- if(chunk.ReadUint32LE() != AMFFRiffChunk::idAI__)
+ ChunkReader sampleChunk(*smpIter);
+
+ if(sampleChunk.ReadUint32LE() != AMFFRiffChunk::idAS__ || m_nSamples + 1 >= MAX_SAMPLES)
{
- break;
+ continue;
}
- AMFFRiffChunk instChunk;
- if(!chunk.ReadConvertEndianness(instChunk) || instChunk.id != AMFFRiffChunk::idINST)
+ // Don't read more samples than the instrument header claims to have.
+ if((instrHeader.numSamples--) == 0)
{
break;
}
- AMInstrumentHeader instrHeader;
- if(!chunk.ReadConvertEndianness(instrHeader))
+ const SAMPLEINDEX smp = ++m_nSamples;
+
+ // Aaand even more nested chunks! Great, innit?
+ AMFFRiffChunk sampleHeaderChunk;
+ if(!sampleChunk.ReadConvertEndianness(sampleHeaderChunk) || sampleHeaderChunk.id != AMFFRiffChunk::idSAMP)
{
break;
}
- ASSERT(instrHeader.headSize + 4 == sizeof(instrHeader));
- const INSTRUMENTINDEX instr = instrHeader.index + 1;
- if(instr >= MAX_INSTRUMENTS)
- break;
+ FileReader sampleFileChunk = sampleChunk.GetChunk(sampleHeaderChunk.length);
- if(Instruments[instr] != nullptr)
- delete Instruments[instr];
-
- try
+ AMSampleHeader sampleHeader;
+ if(!sampleFileChunk.ReadConvertEndianness(sampleHeader))
{
- Instruments[instr] = new ModInstrument();
- } catch(MPTMemoryException)
- {
break;
}
- ModInstrument *pIns = Instruments[instr];
- m_nInstruments = max(m_nInstruments, instr);
- instrHeader.ConvertToMPT(*pIns, m_nSamples);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[smp], sampleHeader.name);
- // Read sample sub-chunks (RIFF nesting ftw)
- for(size_t nSmpCnt = 0; nSmpCnt < instrHeader.numSamples; nSmpCnt++)
- {
- AMFFRiffChunk sampleChunk;
- if(!chunk.ReadConvertEndianness(sampleChunk) || sampleChunk.id != AMFFRiffChunk::idRIFF)
- {
- break;
- }
+ sampleHeader.ConvertToMPT(instrHeader, Samples[smp]);
- if(chunk.ReadUint32LE() != AMFFRiffChunk::idAS__ || m_nSamples + 1 >= MAX_SAMPLES)
- {
- break;
- }
-
- const SAMPLEINDEX smp = ++m_nSamples;
-
- // Aaand even more nested chunks! Great, innit?
- if(!chunk.ReadConvertEndianness(sampleChunk) || sampleChunk.id != AMFFRiffChunk::idSAMP)
- {
- break;
- }
-
- FileReader sampleFileChunk = chunk.GetChunk(sampleChunk.length);
-
- AMSampleHeader sampleHeader;
- if(!sampleFileChunk.ReadConvertEndianness(sampleHeader))
- {
- break;
- }
-
- MemsetZero(Samples[smp]);
-
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[smp], sampleHeader.name);
-
- sampleHeader.ConvertToMPT(instrHeader, Samples[smp]);
-
- sampleFileChunk.Seek(sampleHeader.headSize + 4);
- sampleHeader.GetSampleFormat().ReadSample(Samples[smp], sampleFileChunk);
-
- // RIFF AM has a padding byte so that all chunks have an even size.
- if((sampleChunk.length % 2) != 0)
- {
- chunk.Skip(1);
- }
- }
+ sampleFileChunk.Seek(sampleHeader.headSize + 4);
+ sampleHeader.GetSampleFormat().ReadSample(Samples[smp], sampleFileChunk);
}
- break;
+
}
-
- // RIFF AM has a padding byte so that all chunks have an even size.
- if(isAM && (chunkHeader.length % 2) != 0)
- {
- file.Skip(1);
- }
}
return true;
@@ -1008,4 +1007,4 @@
delete[] amFile;
return result;
-}
\ No newline at end of file
+}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|