123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- #include "stdafx.h"
- #include "Loaders.h"
- OPENMPT_NAMESPACE_BEGIN
- struct MTMFileHeader
- {
- char id[3];
- uint8le version;
- char songName[20];
- uint16le numTracks;
- uint8le lastPattern;
- uint8le lastOrder;
- uint16le commentSize;
- uint8le numSamples;
- uint8le attribute;
- uint8le beatsPerTrack;
- uint8le numChannels;
- uint8le panPos[32];
- };
- MPT_BINARY_STRUCT(MTMFileHeader, 66)
- struct MTMSampleHeader
- {
- char samplename[22];
- uint32le length;
- uint32le loopStart;
- uint32le loopEnd;
- int8le finetune;
- uint8le volume;
- uint8le attribute;
-
- void ConvertToMPT(ModSample &mptSmp) const
- {
- mptSmp.Initialize();
- mptSmp.nVolume = std::min(uint16(volume * 4), uint16(256));
- if(length > 2)
- {
- mptSmp.nLength = length;
- mptSmp.nLoopStart = loopStart;
- mptSmp.nLoopEnd = std::max(loopEnd.get(), uint32(1)) - 1;
- LimitMax(mptSmp.nLoopEnd, mptSmp.nLength);
- if(mptSmp.nLoopStart + 4 >= mptSmp.nLoopEnd)
- mptSmp.nLoopStart = mptSmp.nLoopEnd = 0;
- if(mptSmp.nLoopEnd > 2)
- mptSmp.uFlags.set(CHN_LOOP);
- mptSmp.nFineTune = finetune;
- mptSmp.nC5Speed = ModSample::TransposeToFrequency(0, finetune * 16);
- if(attribute & 0x01)
- {
- mptSmp.uFlags.set(CHN_16BIT);
- mptSmp.nLength /= 2;
- mptSmp.nLoopStart /= 2;
- mptSmp.nLoopEnd /= 2;
- }
- }
- }
- };
- MPT_BINARY_STRUCT(MTMSampleHeader, 37)
- static bool ValidateHeader(const MTMFileHeader &fileHeader)
- {
- if(std::memcmp(fileHeader.id, "MTM", 3)
- || fileHeader.version >= 0x20
- || fileHeader.lastOrder > 127
- || fileHeader.beatsPerTrack > 64
- || fileHeader.numChannels > 32
- || fileHeader.numChannels == 0
- )
- {
- return false;
- }
- return true;
- }
- static uint64 GetHeaderMinimumAdditionalSize(const MTMFileHeader &fileHeader)
- {
- return sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize;
- }
- CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMTM(MemoryFileReader file, const uint64 *pfilesize)
- {
- MTMFileHeader fileHeader;
- if(!file.ReadStruct(fileHeader))
- {
- return ProbeWantMoreData;
- }
- if(!ValidateHeader(fileHeader))
- {
- return ProbeFailure;
- }
- return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
- }
- bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags)
- {
- file.Rewind();
- MTMFileHeader 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_MTM);
- m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName);
- m_nSamples = fileHeader.numSamples;
- m_nChannels = fileHeader.numChannels;
-
- m_modFormat.formatName = U_("MultiTracker");
- m_modFormat.type = U_("mtm");
- m_modFormat.madeWithTracker = MPT_UFORMAT("MultiTracker {}.{}")(fileHeader.version >> 4, fileHeader.version & 0x0F);
- m_modFormat.charset = mpt::Charset::CP437;
-
- for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
- {
- MTMSampleHeader sampleHeader;
- file.ReadStruct(sampleHeader);
- sampleHeader.ConvertToMPT(Samples[smp]);
- m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.samplename);
- }
-
- for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
- {
- ChnSettings[chn].Reset();
- ChnSettings[chn].nPan = ((fileHeader.panPos[chn] & 0x0F) << 4) + 8;
- }
-
- uint8 orders[128];
- file.ReadArray(orders);
- ReadOrderFromArray(Order(), orders, fileHeader.lastOrder + 1, 0xFF, 0xFE);
-
- const ROWINDEX rowsPerPat = fileHeader.beatsPerTrack ? fileHeader.beatsPerTrack : 64;
- FileReader tracks = file.ReadChunk(192 * fileHeader.numTracks);
- if(loadFlags & loadPatternData)
- Patterns.ResizeArray(fileHeader.lastPattern + 1);
- bool hasSpeed = false, hasTempo = false;
- for(PATTERNINDEX pat = 0; pat <= fileHeader.lastPattern; pat++)
- {
- if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, rowsPerPat))
- {
- file.Skip(64);
- continue;
- }
- for(CHANNELINDEX chn = 0; chn < 32; chn++)
- {
- uint16 track = file.ReadUint16LE();
- if(track == 0 || track > fileHeader.numTracks || chn >= GetNumChannels())
- {
- continue;
- }
- tracks.Seek(192 * (track - 1));
- ModCommand *m = Patterns[pat].GetpModCommand(0, chn);
- for(ROWINDEX row = 0; row < rowsPerPat; row++, m += GetNumChannels())
- {
- const auto [noteInstr, instrCmd, par] = tracks.ReadArray<uint8, 3>();
- if(noteInstr & 0xFC)
- m->note = (noteInstr >> 2) + 36 + NOTE_MIN;
- m->instr = ((noteInstr & 0x03) << 4) | (instrCmd >> 4);
- uint8 cmd = instrCmd & 0x0F;
- uint8 param = par;
- if(cmd == 0x0A)
- {
- if(param & 0xF0) param &= 0xF0; else param &= 0x0F;
- } else if(cmd == 0x08)
- {
-
- cmd = param = 0;
- } else if(cmd == 0x0E)
- {
-
- switch(param & 0xF0)
- {
- case 0x00:
- case 0x30:
- case 0x40:
- case 0x60:
- case 0x70:
- case 0xF0:
- cmd = param = 0;
- break;
- }
- }
- if(cmd != 0 || param != 0)
- {
- m->command = cmd;
- m->param = param;
- ConvertModCommand(*m);
- #ifdef MODPLUG_TRACKER
- m->Convert(MOD_TYPE_MTM, MOD_TYPE_S3M, *this);
- #endif
- if(m->command == CMD_SPEED)
- hasSpeed = true;
- else if(m->command == CMD_TEMPO)
- hasTempo = true;
- }
- }
- }
- }
-
-
-
-
-
-
- if(hasSpeed && hasTempo)
- {
- bool hasSpeedAndTempoOnSameRow = false;
- for(const auto &pattern : Patterns)
- {
- for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++)
- {
- const auto rowBase = pattern.GetRow(row);
- bool hasSpeedOnRow = false, hasTempoOnRow = false;
- for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
- {
- if(rowBase[chn].command == CMD_SPEED)
- hasSpeedOnRow = true;
- else if(rowBase[chn].command == CMD_TEMPO)
- hasTempoOnRow = true;
- }
- if(hasSpeedOnRow && hasTempoOnRow)
- {
- hasSpeedAndTempoOnSameRow = true;
- break;
- }
- }
- if(hasSpeedAndTempoOnSameRow)
- break;
- }
- if(!hasSpeedAndTempoOnSameRow)
- {
- for(auto &pattern : Patterns)
- {
- for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++)
- {
- const auto rowBase = pattern.GetRow(row);
- for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
- {
- if(rowBase[chn].command == CMD_SPEED || rowBase[chn].command == CMD_TEMPO)
- {
- const bool writeTempo = rowBase[chn].command == CMD_SPEED;
- pattern.WriteEffect(EffectWriter(writeTempo ? CMD_TEMPO : CMD_SPEED, writeTempo ? 125 : 6).Row(row));
- break;
- }
- }
- }
- }
- }
- }
- if(fileHeader.commentSize != 0)
- {
-
-
- m_songMessage.ReadFixedLineLength(file, fileHeader.commentSize, 39, 1);
- }
-
- if(loadFlags & loadSampleData)
- {
- for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
- {
- SampleIO(
- Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
- SampleIO::mono,
- SampleIO::littleEndian,
- SampleIO::unsignedPCM)
- .ReadSample(Samples[smp], file);
- }
- }
- m_nMinPeriod = 64;
- m_nMaxPeriod = 32767;
- return true;
- }
- OPENMPT_NAMESPACE_END
|