#include "stdafx.h"
#include "Loaders.h"
OPENMPT_NAMESPACE_BEGIN
static void ReadAMSPattern(CPattern &pattern, bool newVersion, FileReader &patternChunk)
{
enum
{
emptyRow = 0xFF, endOfRowMask = 0x80, noteMask = 0x40, channelMask = 0x1F,
readNextCmd = 0x80, noteDataMask = 0x7F,
volCommand = 0x40, commandMask = 0x3F, };
static const ModCommand::COMMAND effTrans[] =
{
CMD_S3MCMDEX, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_RETRIG, CMD_NONE,
CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_NONE,
CMD_PANNINGSLIDE,
CMD_NONE,
CMD_VOLUMESLIDE, CMD_NONE,
CMD_CHANNELVOLUME, CMD_PATTERNBREAK, CMD_S3MCMDEX, CMD_NONE, CMD_KEYOFF, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_NONE,
CMD_NONE,
CMD_NONE,
CMD_NONE,
CMD_NONE,
CMD_NONE,
CMD_NONE,
CMD_GLOBALVOLSLIDE, CMD_NONE,
CMD_GLOBALVOLUME, };
ModCommand dummy;
for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++)
{
PatternRow baseRow = pattern.GetRow(row);
while(patternChunk.CanRead(1))
{
const uint8 flags = patternChunk.ReadUint8();
if(flags == emptyRow)
{
break;
}
const CHANNELINDEX chn = (flags & channelMask);
ModCommand &m = chn < pattern.GetNumChannels() ? baseRow[chn] : dummy;
bool moreCommands = true;
if(!(flags & noteMask))
{
uint8 note = patternChunk.ReadUint8();
moreCommands = (note & readNextCmd) != 0;
note &= noteDataMask;
if(note == 1)
{
m.note = NOTE_KEYOFF;
} else if(note >= 2 && note <= 121 && newVersion)
{
m.note = note - 2 + NOTE_MIN;
} else if(note >= 12 && note <= 108 && !newVersion)
{
m.note = note + 12 + NOTE_MIN;
}
m.instr = patternChunk.ReadUint8();
}
while(moreCommands)
{
ModCommand origCmd = m;
const uint8 command = patternChunk.ReadUint8(), effect = (command & commandMask);
moreCommands = (command & readNextCmd) != 0;
if(command & volCommand)
{
m.volcmd = VOLCMD_VOLUME;
m.vol = effect;
} else
{
m.param = patternChunk.ReadUint8();
if(effect < 0x10)
{
m.command = effect;
CSoundFile::ConvertModCommand(m);
switch(m.command)
{
case CMD_PANNING8:
m.command = CMD_PANNING8;
m.param = (m.param & 0x0F) * 0x11;
break;
case CMD_VOLUME:
m.command = CMD_NONE;
m.volcmd = VOLCMD_VOLUME;
m.vol = static_cast<ModCommand::VOL>(std::min((m.param + 1) / 2, 64));
break;
case CMD_MODCMDEX:
if(m.param == 0x80)
{
m.command = CMD_NONE;
} else
{
m.ExtendedMODtoS3MEffect();
}
break;
}
} else if(effect - 0x10 < (int)CountOf(effTrans))
{
m.command = effTrans[effect - 0x10];
switch(effect)
{
case 0x10:
if(m.param <= 0x01)
{
m.param |= 0x9E;
} else
{
m.command = CMD_NONE;
}
break;
case 0x11:
case 0x12:
m.param = static_cast<ModCommand::PARAM>(std::min(uint8(0x0F), m.param) | 0xE0);
break;
case 0x15:
case 0x16:
m.param = static_cast<ModCommand::PARAM>((std::min(0x10, m.param + 1) / 2) | 0xF0);
break;
case 0x1E:
switch(m.param >> 4)
{
case 0x1:
m.command = CMD_PORTAMENTOUP;
m.param |= 0xF0;
break;
case 0x2:
m.command = CMD_PORTAMENTODOWN;
m.param |= 0xF0;
break;
case 0xA:
m.command = CMD_VOLUMESLIDE;
m.param = ((((m.param & 0x0F) + 1) / 2) << 4) | 0x0F;
break;
case 0xB:
m.command = CMD_VOLUMESLIDE;
m.param = (((m.param & 0x0F) + 1) / 2) | 0xF0;
break;
default:
m.command = CMD_NONE;
break;
}
break;
case 0x1C:
m.param = static_cast<ModCommand::PARAM>(std::min((m.param + 1) / 2, 64));
break;
}
}
ModCommand::CombineEffects(m.command, m.param, origCmd.command, origCmd.param);
if(ModCommand::GetEffectWeight(origCmd.command) > ModCommand::GetEffectWeight(m.command))
{
if(m.volcmd == VOLCMD_NONE && ModCommand::ConvertVolEffect(m.command, m.param, true))
{
m.volcmd = m.command;
m.vol = m.param;
}
m.command = origCmd.command;
m.param = origCmd.param;
}
}
}
if(flags & endOfRowMask)
{
break;
}
}
}
}
struct AMSFileHeader
{
uint8le versionLow;
uint8le versionHigh;
uint8le channelConfig;
uint8le numSamps;
uint16le numPats;
uint16le numOrds;
uint8le midiChannels;
uint16le extraSize;
};
MPT_BINARY_STRUCT(AMSFileHeader, 11)
struct AMSSampleHeader
{
enum SampleFlags
{
smp16BitOld = 0x04, smp16Bit = 0x80, smpPacked = 0x03,
};
uint32le length;
uint32le loopStart;
uint32le loopEnd;
uint8le panFinetune; uint16le sampleRate;
uint8le volume; uint8le flags;
void ConvertToMPT(ModSample &mptSmp) const
{
mptSmp.Initialize();
mptSmp.nLength = length;
mptSmp.nLoopStart = std::min(loopStart, length);
mptSmp.nLoopEnd = std::min(loopEnd, length);
mptSmp.nVolume = (std::min<uint8>(127, volume) * 256 + 64) / 127;
if(panFinetune & 0xF0)
{
mptSmp.nPan = (panFinetune & 0xF0);
mptSmp.uFlags = CHN_PANNING;
}
mptSmp.nC5Speed = 2 * sampleRate;
if(sampleRate == 0)
{
mptSmp.nC5Speed = 2 * 8363;
}
uint32 newC4speed = ModSample::TransposeToFrequency(0, MOD2XMFineTune(panFinetune & 0x0F));
mptSmp.nC5Speed = (mptSmp.nC5Speed * newC4speed) / 8363;
if(mptSmp.nLoopStart < mptSmp.nLoopEnd)
{
mptSmp.uFlags.set(CHN_LOOP);
}
if(flags & (smp16Bit | smp16BitOld))
{
mptSmp.uFlags.set(CHN_16BIT);
}
}
};
MPT_BINARY_STRUCT(AMSSampleHeader, 17)
static bool ValidateHeader(const AMSFileHeader &fileHeader)
{
if(fileHeader.versionHigh != 0x01)
{
return false;
}
return true;
}
static uint64 GetHeaderMinimumAdditionalSize(const AMSFileHeader &fileHeader)
{
return fileHeader.extraSize + 3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u;
}
CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize)
{
if(!file.CanRead(7))
{
return ProbeWantMoreData;
}
if(!file.ReadMagic("Extreme"))
{
return ProbeFailure;
}
AMSFileHeader fileHeader;
if(!file.ReadStruct(fileHeader))
{
return ProbeWantMoreData;
}
if(!ValidateHeader(fileHeader))
{
return ProbeFailure;
}
return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
}
bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags)
{
file.Rewind();
if(!file.ReadMagic("Extreme"))
{
return false;
}
AMSFileHeader fileHeader;
if(!file.ReadStruct(fileHeader))
{
return false;
}
if(!ValidateHeader(fileHeader))
{
return false;
}
if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
{
return false;
}
if(!file.Skip(fileHeader.extraSize))
{
return false;
}
if(loadFlags == onlyVerifyHeader)
{
return true;
}
InitializeGlobals(MOD_TYPE_AMS);
m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS;
m_nChannels = (fileHeader.channelConfig & 0x1F) + 1;
m_nSamples = fileHeader.numSamps;
SetupMODPanning(true);
m_modFormat.formatName = U_("Extreme's Tracker");
m_modFormat.type = U_("ams");
m_modFormat.madeWithTracker = mpt::format(U_("Extreme's Tracker %1.%2"))(fileHeader.versionHigh, fileHeader.versionLow);
m_modFormat.charset = mpt::CharsetCP437;
std::vector<bool> packSample(fileHeader.numSamps);
STATIC_ASSERT(MAX_SAMPLES > 255);
for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
AMSSampleHeader sampleHeader;
file.ReadStruct(sampleHeader);
sampleHeader.ConvertToMPT(Samples[smp]);
packSample[smp - 1] = (sampleHeader.flags & AMSSampleHeader::smpPacked) != 0;
}
file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName);
for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_szNames[smp]);
}
for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
{
ChnSettings[chn].Reset();
file.ReadSizedString<uint8le, mpt::String::spacePadded>(ChnSettings[chn].szName);
}
Patterns.ResizeArray(fileHeader.numPats);
for(PATTERNINDEX pat = 0; pat < fileHeader.numPats; pat++)
{
char name[11];
file.ReadSizedString<uint8le, mpt::String::spacePadded>(name);
if(Patterns.Insert(pat, 64))
{
Patterns[pat].SetName(name);
}
}
const uint16 packedLength = file.ReadUint16LE();
if(packedLength && file.CanRead(packedLength))
{
std::vector<uint8> textIn;
file.ReadVector(textIn, packedLength);
std::string textOut;
textOut.reserve(packedLength);
for(auto c : textIn)
{
if(c & 0x80)
{
textOut.insert(textOut.end(), (c & 0x7F), ' ');
} else
{
textOut.push_back(c);
}
}
textOut = mpt::ToCharset(mpt::CharsetCP437, mpt::CharsetCP437AMS, textOut);
m_songMessage.ReadFixedLineLength(mpt::byte_cast<const mpt::byte*>(textOut.c_str()), textOut.length(), 76, 0);
}
ReadOrderFromFile<uint16le>(Order(), file, fileHeader.numOrds);
for(PATTERNINDEX pat = 0; pat < fileHeader.numPats && file.CanRead(4); pat++)
{
uint32 patLength = file.ReadUint32LE();
FileReader patternChunk = file.ReadChunk(patLength);
if((loadFlags & loadPatternData) && Patterns.IsValidPat(pat))
{
ReadAMSPattern(Patterns[pat], false, patternChunk);
}
}
if(loadFlags & loadSampleData)
{
for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
SampleIO(
Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
SampleIO::mono,
SampleIO::littleEndian,
packSample[smp - 1] ? SampleIO::AMS : SampleIO::signedPCM)
.ReadSample(Samples[smp], file);
}
}
return true;
}
struct AMS2FileHeader
{
enum FileFlags
{
linearSlides = 0x40,
};
uint8le versionLow; uint8le versionHigh; uint8le numIns; uint16le numPats; uint16le numOrds; };
MPT_BINARY_STRUCT(AMS2FileHeader, 7)
struct AMS2Envelope
{
uint8 speed; uint8 sustainPoint; uint8 loopStart; uint8 loopEnd; uint8 numPoints;
void ConvertToMPT(InstrumentEnvelope &mptEnv, FileReader &file)
{
file.ReadStruct(*this);
uint8 data[64][3];
file.ReadStructPartial(data, numPoints * 3);
if(numPoints <= 1)
{
return;
}
STATIC_ASSERT(MAX_ENVPOINTS >= CountOf(data));
mptEnv.resize(std::min(numPoints, uint8(CountOf(data))));
mptEnv.nLoopStart = loopStart;
mptEnv.nLoopEnd = loopEnd;
mptEnv.nSustainStart = mptEnv.nSustainEnd = sustainPoint;
for(uint32 i = 0; i < mptEnv.size(); i++)
{
if(i != 0)
{
mptEnv[i].tick = mptEnv[i - 1].tick + static_cast<uint16>(std::max(1, data[i][0] | ((data[i][1] & 0x01) << 8)));
}
mptEnv[i].value = data[i][2];
}
}
};
MPT_BINARY_STRUCT(AMS2Envelope, 5)
struct AMS2Instrument
{
enum EnvelopeFlags
{
envLoop = 0x01,
envSustain = 0x02,
envEnabled = 0x04,
volEnvShift = 0,
panEnvShift = 1,
vibEnvShift = 2,
vibAmpMask = 0x3000,
vibAmpShift = 12,
fadeOutMask = 0xFFF,
};
uint8le shadowInstr; uint16le vibampFadeout; uint16le envFlags;
void ApplyFlags(InstrumentEnvelope &mptEnv, EnvelopeFlags shift) const
{
const int flags = envFlags >> (shift * 3);
mptEnv.dwFlags.set(ENV_ENABLED, (flags & envEnabled) != 0);
mptEnv.dwFlags.set(ENV_LOOP, (flags & envLoop) != 0);
mptEnv.dwFlags.set(ENV_SUSTAIN, (flags & envSustain) != 0);
if(!(flags & envSustain) && (flags & envLoop) != 0 && (flags & (1 << (9 - shift * 2))) != 0)
{
mptEnv.nSustainStart = mptEnv.nLoopStart;
mptEnv.nSustainEnd = mptEnv.nLoopEnd;
mptEnv.dwFlags.set(ENV_SUSTAIN);
mptEnv.dwFlags.reset(ENV_LOOP);
}
}
};
MPT_BINARY_STRUCT(AMS2Instrument, 5)
struct AMS2SampleHeader
{
enum SampleFlags
{
smpPacked = 0x03,
smp16Bit = 0x04,
smpLoop = 0x08,
smpBidiLoop = 0x10,
smpReverse = 0x40,
};
uint32le length;
uint32le loopStart;
uint32le loopEnd;
uint16le sampledRate; uint8le panFinetune; uint16le c4speed; int8le relativeTone; uint8le volume; uint8le flags;
void ConvertToMPT(ModSample &mptSmp) const
{
mptSmp.Initialize();
mptSmp.nLength = length;
mptSmp.nLoopStart = std::min(loopStart, length);
mptSmp.nLoopEnd = std::min(loopEnd, length);
mptSmp.nC5Speed = c4speed * 2;
if(c4speed == 0)
{
mptSmp.nC5Speed = 8363 * 2;
}
uint32 newC4speed = ModSample::TransposeToFrequency(relativeTone, MOD2XMFineTune(panFinetune & 0x0F));
mptSmp.nC5Speed = (mptSmp.nC5Speed * newC4speed) / 8363;
mptSmp.nVolume = (std::min<uint8>(volume, 127) * 256 + 64) / 127;
if(panFinetune & 0xF0)
{
mptSmp.nPan = (panFinetune & 0xF0);
mptSmp.uFlags = CHN_PANNING;
}
if(flags & smp16Bit) mptSmp.uFlags.set(CHN_16BIT);
if((flags & smpLoop) && mptSmp.nLoopStart < mptSmp.nLoopEnd)
{
mptSmp.uFlags.set(CHN_LOOP);
if(flags & smpBidiLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP);
if(flags & smpReverse) mptSmp.uFlags.set(CHN_REVERSE);
}
}
};
MPT_BINARY_STRUCT(AMS2SampleHeader, 20)
struct AMS2Description
{
uint32le packedLen; uint32le unpackedLen;
uint8le packRoutine; uint8le preProcessing; uint8le packingMethod; };
MPT_BINARY_STRUCT(AMS2Description, 11)
static bool ValidateHeader(const AMS2FileHeader &fileHeader)
{
if(fileHeader.versionHigh != 2 || fileHeader.versionLow > 2)
{
return false;
}
return true;
}
static uint64 GetHeaderMinimumAdditionalSize(const AMS2FileHeader &fileHeader)
{
return 36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u;
}
CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize)
{
if(!file.CanRead(7))
{
return ProbeWantMoreData;
}
if(!file.ReadMagic("AMShdr\x1A"))
{
return ProbeFailure;
}
if(!file.CanRead(1))
{
return ProbeWantMoreData;
}
const uint8 songNameLength = file.ReadUint8();
if(!file.Skip(songNameLength))
{
return ProbeWantMoreData;
}
AMS2FileHeader fileHeader;
if(!file.ReadStruct(fileHeader))
{
return ProbeWantMoreData;
}
if(!ValidateHeader(fileHeader))
{
return ProbeFailure;
}
return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
}
bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags)
{
file.Rewind();
if(!file.ReadMagic("AMShdr\x1A"))
{
return false;
}
std::string songName;
if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(songName))
{
return false;
}
AMS2FileHeader fileHeader;
if(!file.ReadStruct(fileHeader))
{
return false;
}
if(!ValidateHeader(fileHeader))
{
return false;
}
if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
{
return false;
}
if(loadFlags == onlyVerifyHeader)
{
return true;
}
InitializeGlobals(MOD_TYPE_AMS);
m_songName = songName;
m_nInstruments = fileHeader.numIns;
m_nChannels = 32;
SetupMODPanning(true);
m_modFormat.formatName = U_("Velvet Studio");
m_modFormat.type = U_("ams");
m_modFormat.madeWithTracker = mpt::format(U_("Velvet Studio %1.%2"))(fileHeader.versionHigh.get(), mpt::ufmt::dec0<2>(fileHeader.versionLow.get()));
m_modFormat.charset = mpt::CharsetCP437;
uint16 headerFlags;
if(fileHeader.versionLow >= 2)
{
uint16 tempo = std::max(uint16(32 << 8), file.ReadUint16LE()); m_nDefaultTempo.SetRaw((tempo * TEMPO::fractFact) >> 8);
m_nDefaultSpeed = std::max(uint8(1), file.ReadUint8());
file.Skip(3); headerFlags = file.ReadUint16LE();
} else
{
m_nDefaultTempo.Set(std::max(uint8(32), file.ReadUint8()));
m_nDefaultSpeed = std::max(uint8(1), file.ReadUint8());
headerFlags = file.ReadUint8();
}
m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS | ((headerFlags & AMS2FileHeader::linearSlides) ? SONG_LINEARSLIDES : SongFlags(0));
std::vector<SAMPLEINDEX> firstSample; std::vector<uint16> sampleSettings; enum
{
instrIndexMask = 0xFF, sampleIndexMask = 0x7F00, sampleIndexShift = 8,
packStatusMask = 0x8000, };
STATIC_ASSERT(MAX_INSTRUMENTS > 255);
for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++)
{
ModInstrument *instrument = AllocateInstrument(ins);
if(instrument == nullptr
|| !file.ReadSizedString<uint8le, mpt::String::spacePadded>(instrument->name))
{
break;
}
uint8 numSamples = file.ReadUint8();
uint8 sampleAssignment[120];
MemsetZero(sampleAssignment);
if(numSamples == 0
|| (fileHeader.versionLow > 0 && !file.ReadArray(sampleAssignment)) || (fileHeader.versionLow == 0 && !file.ReadRaw(sampleAssignment + 12, 96))) {
continue;
}
STATIC_ASSERT(CountOf(instrument->Keyboard) >= CountOf(sampleAssignment));
for(size_t i = 0; i < 120; i++)
{
instrument->Keyboard[i] = sampleAssignment[i] + GetNumSamples() + 1;
}
AMS2Envelope volEnv, panEnv, vibratoEnv;
volEnv.ConvertToMPT(instrument->VolEnv, file);
panEnv.ConvertToMPT(instrument->PanEnv, file);
vibratoEnv.ConvertToMPT(instrument->PitchEnv, file);
AMS2Instrument instrHeader;
file.ReadStruct(instrHeader);
instrument->nFadeOut = (instrHeader.vibampFadeout & AMS2Instrument::fadeOutMask);
const int16 vibAmp = 1 << ((instrHeader.vibampFadeout & AMS2Instrument::vibAmpMask) >> AMS2Instrument::vibAmpShift);
instrHeader.ApplyFlags(instrument->VolEnv, AMS2Instrument::volEnvShift);
instrHeader.ApplyFlags(instrument->PanEnv, AMS2Instrument::panEnvShift);
instrHeader.ApplyFlags(instrument->PitchEnv, AMS2Instrument::vibEnvShift);
for(auto &p : instrument->VolEnv)
{
p.value = std::min(uint8(ENVELOPE_MAX), static_cast<uint8>((p.value * ENVELOPE_MAX + 64u) / 127u));
}
for(auto &p : instrument->PanEnv)
{
p.value = std::min(uint8(ENVELOPE_MAX), static_cast<uint8>((p.value * ENVELOPE_MAX + 128u) / 255u));
}
for(auto &p : instrument->PitchEnv)
{
#ifdef MODPLUG_TRACKER
p.value = std::min(uint8(ENVELOPE_MAX), static_cast<uint8>(32 + Util::muldivrfloor(static_cast<int8>(p.value - 128), vibAmp, 255)));
#else
p.value = static_cast<uint8>(128 + Util::muldivrfloor(static_cast<int8>(p.value - 128), vibAmp, 8));
#endif
}
const SAMPLEINDEX firstSmp = GetNumSamples() + 1;
for(SAMPLEINDEX smp = 0; smp < numSamples; smp++)
{
if(firstSmp + smp >= MAX_SAMPLES)
{
file.Skip(sizeof(AMS2SampleHeader));
break;
}
file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_szNames[firstSmp + smp]);
AMS2SampleHeader sampleHeader;
file.ReadStruct(sampleHeader);
sampleHeader.ConvertToMPT(Samples[firstSmp + smp]);
uint16 settings = (instrHeader.shadowInstr & instrIndexMask)
| ((smp << sampleIndexShift) & sampleIndexMask)
| ((sampleHeader.flags & AMS2SampleHeader::smpPacked) ? packStatusMask : 0);
sampleSettings.push_back(settings);
}
firstSample.push_back(firstSmp);
m_nSamples = static_cast<SAMPLEINDEX>(std::min(MAX_SAMPLES - 1, GetNumSamples() + numSamples));
}
uint8 composerLength = file.ReadUint8();
if(composerLength)
{
std::string str;
file.ReadString<mpt::String::spacePadded>(str, composerLength);
m_songArtist = mpt::ToUnicode(mpt::CharsetCP437AMS2, str);
}
for(CHANNELINDEX chn = 0; chn < 32; chn++)
{
ChnSettings[chn].Reset();
file.ReadSizedString<uint8le, mpt::String::spacePadded>(ChnSettings[chn].szName);
}
AMS2Description descriptionHeader;
if(!file.ReadStruct(descriptionHeader))
{
return true;
}
if(descriptionHeader.packedLen > sizeof(descriptionHeader) && file.CanRead(descriptionHeader.packedLen - sizeof(descriptionHeader)))
{
const size_t textLength = descriptionHeader.packedLen - sizeof(descriptionHeader);
std::vector<uint8> textIn;
file.ReadVector(textIn, textLength);
std::string textOut;
textOut.reserve(descriptionHeader.unpackedLen);
size_t readLen = 0;
while(readLen < textLength)
{
uint8 c = textIn[readLen++];
if(c == 0xFF && textLength - readLen >= 2)
{
c = textIn[readLen++];
uint32 count = textIn[readLen++];
textOut.insert(textOut.end(), count, c);
} else
{
textOut.push_back(c);
}
}
textOut = mpt::ToCharset(mpt::CharsetCP437, mpt::CharsetCP437AMS2, textOut);
m_songMessage.ReadFixedLineLength(mpt::byte_cast<const mpt::byte*>(textOut.c_str()), textOut.length(), 74, 0);
}
ReadOrderFromFile<uint16le>(Order(), file, fileHeader.numOrds);
if(loadFlags & loadPatternData)
Patterns.ResizeArray(fileHeader.numPats);
for(PATTERNINDEX pat = 0; pat < fileHeader.numPats && file.CanRead(4); pat++)
{
uint32 patLength = file.ReadUint32LE();
FileReader patternChunk = file.ReadChunk(patLength);
if(loadFlags & loadPatternData)
{
const ROWINDEX numRows = patternChunk.ReadUint8() + 1;
patternChunk.Skip(1);
if(!Patterns.Insert(pat, numRows))
{
continue;
}
char patternName[11];
patternChunk.ReadSizedString<uint8le, mpt::String::spacePadded>(patternName);
Patterns[pat].SetName(patternName);
ReadAMSPattern(Patterns[pat], true, patternChunk);
}
}
if(!(loadFlags & loadSampleData))
{
return true;
}
for(SAMPLEINDEX smp = 0; smp < GetNumSamples(); smp++)
{
if((sampleSettings[smp] & instrIndexMask) == 0)
{
SampleIO(
(Samples[smp + 1].uFlags & CHN_16BIT) ? SampleIO::_16bit : SampleIO::_8bit,
SampleIO::mono,
SampleIO::littleEndian,
(sampleSettings[smp] & packStatusMask) ? SampleIO::AMS : SampleIO::signedPCM)
.ReadSample(Samples[smp + 1], file);
}
}
for(SAMPLEINDEX smp = 0; smp < GetNumSamples(); smp++)
{
INSTRUMENTINDEX sourceInstr = (sampleSettings[smp] & instrIndexMask);
if(sourceInstr == 0
|| --sourceInstr >= firstSample.size())
{
continue;
}
SAMPLEINDEX sourceSample = ((sampleSettings[smp] & sampleIndexMask) >> sampleIndexShift) + firstSample[sourceInstr];
if(sourceSample > GetNumSamples() || !Samples[sourceSample].HasSampleData())
{
continue;
}
ModSample &sample = Samples[smp + 1];
ModSample &source = Samples[sourceSample];
sample.uFlags.set(CHN_16BIT, source.uFlags[CHN_16BIT]);
sample.nLength = source.nLength;
if(sample.AllocateSample())
{
memcpy(sample.sampleb(), source.sampleb(), source.GetSampleSizeInBytes());
}
}
return true;
}
void AMSUnpack(const int8 * const source, size_t sourceSize, void * const dest, const size_t destSize, char packCharacter)
{
std::vector<int8> tempBuf(destSize, 0);
size_t depackSize = destSize;
{
const int8 *in = source;
int8 *out = tempBuf.data();
size_t i = sourceSize, j = destSize;
while(i != 0 && j != 0)
{
int8 ch = *(in++);
if(--i != 0 && ch == packCharacter)
{
uint8 repCount = *(in++);
repCount = static_cast<uint8>(std::min(static_cast<size_t>(repCount), j));
if(--i != 0 && repCount)
{
ch = *(in++);
i--;
while(repCount-- != 0)
{
*(out++) = ch;
j--;
}
} else
{
*(out++) = packCharacter;
j--;
}
} else
{
*(out++) = ch;
j--;
}
}
depackSize -= j;
}
{
int8 *out = tempBuf.data();
uint16 bitcount = 0x80;
size_t k = 0;
uint8 *dst = static_cast<uint8 *>(dest);
for(size_t i = 0; i < depackSize; i++)
{
uint8 al = *out++;
uint16 dh = 0;
for(uint16 count = 0; count < 8; count++)
{
uint16 bl = al & bitcount;
bl = ((bl | (bl << 8)) >> ((dh + 8 - count) & 7)) & 0xFF;
bitcount = ((bitcount | (bitcount << 8)) >> 1) & 0xFF;
dst[k++] |= bl;
if(k >= destSize)
{
k = 0;
dh++;
}
}
bitcount = ((bitcount | (bitcount << 8)) >> dh) & 0xFF;
}
}
{
int8 old = 0;
int8 *out = static_cast<int8 *>(dest);
for(size_t i = depackSize; i != 0; i--)
{
int pos = *reinterpret_cast<uint8 *>(out);
if(pos != 128 && (pos & 0x80) != 0)
{
pos = -(pos & 0x7F);
}
old -= static_cast<int8>(pos);
*(out++) = old;
}
}
}
OPENMPT_NAMESPACE_END