From: <sv...@op...> - 2024-04-18 19:11:47
|
Author: sagamusix Date: Thu Apr 18 21:11:34 2024 New Revision: 20618 URL: https://source.openmpt.org/browse/openmpt/?op=revision&rev=20618 Log: [New] Can now load ChipTracker modules, another SoundTracker clone using a track-based approach. As it shares the typical MOD sample structures, it is also placed in Load_mod.cpp. Modified: trunk/OpenMPT/soundlib/Load_mod.cpp trunk/OpenMPT/soundlib/Sndfile.cpp trunk/OpenMPT/soundlib/Sndfile.h trunk/OpenMPT/soundlib/Tables.cpp Modified: trunk/OpenMPT/soundlib/Load_mod.cpp ============================================================================== --- trunk/OpenMPT/soundlib/Load_mod.cpp Wed Apr 17 20:48:34 2024 (r20617) +++ trunk/OpenMPT/soundlib/Load_mod.cpp Thu Apr 18 21:11:34 2024 (r20618) @@ -1,7 +1,7 @@ /* * Load_mod.cpp * ------------ - * Purpose: MOD / NST (ProTracker / NoiseTracker), M15 / STK (Ultimate Soundtracker / Soundtracker) and ST26 (SoundTracker 2.6 / Ice Tracker) module loader / saver + * Purpose: MOD / NST (ProTracker / NoiseTracker), M15 / STK (Ultimate Soundtracker / Soundtracker), ST26 (SoundTracker 2.6 / Ice Tracker) and ChipTracker module loader / saver * Notes : "2000 LOC for processing MOD files?!" you say? Well, this file also contains loaders for some formats that are almost identical to MOD, and extensive * heuristics for more or less broken MOD files and files saved with tons of different trackers, to allow for the most optimal playback. * Authors: Olivier Lapicque @@ -2170,6 +2170,154 @@ } +CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderKRIS(MemoryFileReader file, const uint64 *pfilesize) +{ + if(!file.CanRead(952 + 4)) + return ProbeWantMoreData; + + file.Seek(952); + if(!file.ReadMagic("KRIS")) + return ProbeFailure; + + const auto [numOrders, restartPos] = file.ReadArray<uint8, 2>(); + if(numOrders > 128 || restartPos > 127) + return ProbeFailure; + + file.Seek(22); + uint32 invalidBytes = 0; + for(SAMPLEINDEX smp = 1; smp <= 31; smp++) + { + MODSampleHeader sampleHeader; + file.ReadStruct(sampleHeader); + invalidBytes += sampleHeader.GetInvalidByteScore(); + if(invalidBytes > MODSampleHeader::INVALID_BYTE_THRESHOLD) + return ProbeFailure; + } + + MPT_UNREFERENCED_PARAMETER(pfilesize); + return ProbeSuccess; +} + + +bool CSoundFile::ReadKRIS(FileReader &file, ModLoadingFlags loadFlags) +{ + if (!file.Seek(952) || !file.ReadMagic("KRIS")) + return false; + + const auto [numOrders, restartPos] = file.ReadArray<uint8, 2>(); + if(numOrders > 128 || restartPos > 127) + return false; + + std::array<std::array<uint8, 2>, 128 * 4> tracks; + if(!file.ReadArray(tracks)) + return false; + uint16 tracksOffset = 1984; + + InitializeGlobals(MOD_TYPE_MOD); + + file.Seek(0); + file.ReadString<mpt::String::spacePadded>(m_songName, 22); + + m_nSamples = 31; + uint32 invalidBytes = 0; + uint8 numSynthWaveforms = 0; + for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) + { + MODSampleHeader sampleHeader; + file.ReadStruct(sampleHeader); + if(sampleHeader.name[0] != 0) + { + invalidBytes += ReadSample(sampleHeader, Samples[smp], m_szNames[smp], true); + } else + { + // Unfinished feature. Synth parameters are stored in the module, but loading and saving of synth waveforms + // (which I'd assume the extra space before the track data is reserved for) is completely absent, making the feature useless. + Samples[smp].Initialize(MOD_TYPE_MOD); + m_szNames[smp] = "Synthetic"; + const uint8 maxWaveform = std::max({ sampleHeader.name[1], sampleHeader.name[5], sampleHeader.name[10], sampleHeader.name[19] }); + if(maxWaveform) + numSynthWaveforms = std::max(numSynthWaveforms, static_cast<uint8>(maxWaveform + 1)); + } + if(invalidBytes > MODSampleHeader::INVALID_BYTE_THRESHOLD) + return false; + } + tracksOffset += numSynthWaveforms * 64; + + if(loadFlags == onlyVerifyHeader) + return true; + + m_nChannels = 4; + SetupMODPanning(true); + Order().SetDefaultSpeed(6); + Order().SetDefaultTempoInt(125); + Order().SetRestartPos(restartPos); + m_nMinPeriod = 113 * 4; + m_nMaxPeriod = 856 * 4; + m_nSamplePreAmp = 64; + m_SongFlags.set(SONG_PT_MODE | SONG_IMPORTED); + m_playBehaviour.set(kMODIgnorePanning); + m_playBehaviour.set(kMODSampleSwap); + + Order().resize(numOrders); + if(loadFlags & loadPatternData) + Patterns.ResizeArray(numOrders); + for(PATTERNINDEX pat = 0; pat < numOrders; pat++) + { + if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) + break; + Order()[pat] = pat; + for(CHANNELINDEX chn = 0; chn < 4; chn++) + { + const uint8 track = tracks[pat * 4u + chn][0]; + const int8 transpose = tracks[pat * 4u + chn][1]; + if(!file.Seek(tracksOffset + track * 256u)) + return false; + ModCommand *m = Patterns[pat].GetpModCommand(0, chn); + for(ROWINDEX row = 0; row < 64; row++, m += 4) + { + const auto data = file.ReadArray<uint8, 4>(); + if(data[0] & 1) + return false; + if(data[0] >= 0x18 && data[0] <= 0x9E) + m->note = static_cast<ModCommand::NOTE>(Clamp(NOTE_MIDDLEC - 36 + (data[0] - 0x18) / 2 + transpose, NOTE_MIDDLEC - 12, NOTE_MIDDLEC + 23)); + else if(data[0] != 0xA8) + return false; + if(data[2] >> 4) + return false; + m->instr = data[1]; + ConvertModCommand(*m, data[2] & 0x0F, data[3]); + } + } + } + + m_modFormat.formatName = U_("ChipTracker"); + m_modFormat.type = U_("mod"); + m_modFormat.madeWithTracker = U_("ChipTracker"); + m_modFormat.charset = mpt::Charset::Amiga_no_C1; + + if(loadFlags & loadSampleData) + { + uint8 maxTrack = 0; + for(uint32 ord = 0; ord < numOrders * 4u; ord++) + { + maxTrack = std::max(tracks[ord][0], maxTrack); + } + file.Seek(tracksOffset + (maxTrack + 1u) * 256u); + for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) + if(Samples[smp].nLength) + { + SampleIO( + SampleIO::_8bit, + SampleIO::mono, + SampleIO::littleEndian, + SampleIO::signedPCM) + .ReadSample(Samples[smp], file); + } + } + + return true; +} + struct PT36Header { Modified: trunk/OpenMPT/soundlib/Sndfile.cpp ============================================================================== --- trunk/OpenMPT/soundlib/Sndfile.cpp Wed Apr 17 20:48:34 2024 (r20617) +++ trunk/OpenMPT/soundlib/Sndfile.cpp Thu Apr 18 21:11:34 2024 (r20618) @@ -310,6 +310,7 @@ MPT_DECLARE_FORMAT(STX), MPT_DECLARE_FORMAT(MOD), MPT_DECLARE_FORMAT(ICE), + MPT_DECLARE_FORMAT(KRIS), MPT_DECLARE_FORMAT(669), MPT_DECLARE_FORMAT(667), MPT_DECLARE_FORMAT(C67), Modified: trunk/OpenMPT/soundlib/Sndfile.h ============================================================================== --- trunk/OpenMPT/soundlib/Sndfile.h Wed Apr 17 20:48:34 2024 (r20617) +++ trunk/OpenMPT/soundlib/Sndfile.h Thu Apr 18 21:11:34 2024 (r20618) @@ -875,6 +875,7 @@ static ProbeResult ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderJ2B(MemoryFileReader file, const uint64 *pfilesize); + static ProbeResult ProbeFileHeaderKRIS(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderMUS_KM(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderM15(MemoryFileReader file, const uint64 *pfilesize); static ProbeResult ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize); @@ -931,6 +932,7 @@ bool ReadIT(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadITP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadJ2B(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); + bool ReadKRIS(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMUS_KM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadM15(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); bool ReadMDL(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); Modified: trunk/OpenMPT/soundlib/Tables.cpp ============================================================================== --- trunk/OpenMPT/soundlib/Tables.cpp Wed Apr 17 20:48:34 2024 (r20617) +++ trunk/OpenMPT/soundlib/Tables.cpp Thu Apr 18 21:11:34 2024 (r20618) @@ -51,6 +51,7 @@ { { UL_("OpenMPT"), "mptm" }, { UL_("ProTracker"), "mod" }, + { UL_("ChipTracker"), "mod" }, { UL_("Scream Tracker 3"), "s3m" }, { UL_("FastTracker 2"), "xm" }, { UL_("Impulse Tracker"), "it" }, |