From: <sv...@op...> - 2024-10-21 21:15:36
|
Author: sagamusix Date: Mon Oct 21 23:15:24 2024 New Revision: 21911 URL: https://source.openmpt.org/browse/openmpt/?op=revision&rev=21911 Log: [Ref] J2B: Small cleanup. Modified: trunk/OpenMPT/soundlib/load_j2b.cpp Modified: trunk/OpenMPT/soundlib/load_j2b.cpp ============================================================================== --- trunk/OpenMPT/soundlib/load_j2b.cpp Mon Oct 21 21:53:53 2024 (r21910) +++ trunk/OpenMPT/soundlib/load_j2b.cpp Mon Oct 21 23:15:24 2024 (r21911) @@ -6,7 +6,7 @@ * It seems like no other game used the AM(FF) format. * RIFF AM is the newer version of the format, generally following the RIFF "standard" closely. * Authors: Johannes Schultz (OpenMPT port, reverse engineering + loader implementation of the instrument format) - * kode54 (foo_dumb - this is almost a complete port of his code, thanks) + * Largely based on the J2B loader of kode54's DUMB fork * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ @@ -43,17 +43,18 @@ { // Magic Bytes // 32-Bit J2B header identifiers - enum : uint32 { + enum : uint32 + { magicDEADBEAF = 0xAFBEADDEu, magicDEADBABE = 0xBEBAADDEu }; - char signature[4]; // MUSE - uint32le deadbeaf; // 0xDEADBEAF (AM) or 0xDEADBABE (AMFF) - uint32le fileLength; // complete filesize - uint32le crc32; // checksum of the compressed data block - uint32le packedLength; // length of the compressed data block - uint32le unpackedLength; // length of the decompressed module + char signature[4]; // MUSE + uint32le deadbeaf; // 0xDEADBEAF (AM) or 0xDEADBABE (AMFF) + uint32le fileLength; // complete filesize + uint32le crc32; // checksum of the compressed data block + uint32le packedLength; // length of the compressed data block + uint32le unpackedLength; // length of the decompressed module }; MPT_BINARY_STRUCT(J2BFileHeader, 24) @@ -66,21 +67,21 @@ // 32-Bit chunk identifiers enum ChunkIdentifiers { - idRIFF = MagicLE("RIFF"), - idAMFF = MagicLE("AMFF"), - idAM__ = MagicLE("AM "), - idMAIN = MagicLE("MAIN"), - idINIT = MagicLE("INIT"), - idORDR = MagicLE("ORDR"), - idPATT = MagicLE("PATT"), - idINST = MagicLE("INST"), - idSAMP = MagicLE("SAMP"), - idAI__ = MagicLE("AI "), - idAS__ = MagicLE("AS "), + idRIFF = MagicLE("RIFF"), + idAMFF = MagicLE("AMFF"), + idAM__ = MagicLE("AM "), + idMAIN = MagicLE("MAIN"), + idINIT = MagicLE("INIT"), + idORDR = MagicLE("ORDR"), + idPATT = MagicLE("PATT"), + idINST = MagicLE("INST"), + idSAMP = MagicLE("SAMP"), + idAI__ = MagicLE("AI "), + idAS__ = MagicLE("AS "), }; - uint32le id; // See ChunkIdentifiers - uint32le length; // Chunk size without header + uint32le id; // See ChunkIdentifiers + uint32le length; // Chunk size without header size_t GetLength() const { @@ -110,8 +111,8 @@ uint8le channels; uint8le speed; uint8le tempo; - uint16le minPeriod; // 16x Amiga periods, but we should ignore them - otherwise some high notes in Medivo.j2b won't sound correct. - uint16le maxPeriod; // Ditto + uint16le minPeriod; // 16x Amiga periods, but we should ignore them - otherwise some high notes in Medivo.j2b won't sound correct. + uint16le maxPeriod; // Ditto uint8le globalvolume; }; @@ -132,14 +133,14 @@ struct EnvPoint { uint16le tick; - uint8le value; // 0...64 + uint8le value; // 0...64 }; - uint8le envFlags; // high nibble = pan env flags, low nibble = vol env flags (both nibbles work the same way) - uint8le envNumPoints; // high nibble = pan env length, low nibble = vol env length - uint8le envSustainPoints; // you guessed it... high nibble = pan env sustain point, low nibble = vol env sustain point - uint8le envLoopStarts; // I guess you know the pattern now. - uint8le envLoopEnds; // same here. + uint8le envFlags; // high nibble = pan env flags, low nibble = vol env flags (both nibbles work the same way) + uint8le envNumPoints; // high nibble = pan env length, low nibble = vol env length + uint8le envSustainPoints; // you guessed it... high nibble = pan env sustain point, low nibble = vol env sustain point + uint8le envLoopStarts; // I guess you know the pattern now. + uint8le envLoopEnds; // same here. EnvPoint volEnv[10]; EnvPoint panEnv[10]; @@ -191,8 +192,8 @@ // AMFF instrument header (old format) struct AMFFInstrumentHeader { - uint8le unknown; // 0x00 - uint8le index; // actual instrument number + uint8le unknown; // 0x00 + uint8le index; // actual instrument number char name[28]; uint8le numSamples; uint8le sampleMap[120]; @@ -229,16 +230,16 @@ // Sample flags (also used for RIFF AM) enum SampleFlags { - smp16Bit = 0x04, - smpLoop = 0x08, - smpPingPong = 0x10, - smpPanning = 0x20, - smpExists = 0x80, + smp16Bit = 0x04, + smpLoop = 0x08, + smpPingPong = 0x10, + smpPanning = 0x20, + smpExists = 0x80, // some flags are still missing... what is e.g. 0x8000? }; - uint32le id; // "SAMP" - uint32le chunkSize; // header + sample size + uint32le id; // "SAMP" + uint32le chunkSize; // header + sample size char name[28]; uint8le pan; uint8le volume; @@ -363,16 +364,15 @@ } }; -MPT_BINARY_STRUCT(AMEnvelope::EnvPoint, 4) MPT_BINARY_STRUCT(AMEnvelope, 48) // AM instrument header (new format) struct AMInstrumentHeader { - uint32le headSize; // Header size (i.e. the size of this struct) - uint8le unknown1; // 0x00 - uint8le index; // Actual instrument number + uint32le headSize; // Header size (i.e. the size of this struct) + uint8le unknown1; // 0x00 + uint8le index; // Actual instrument number char name[32]; uint8le sampleMap[128]; uint8le vibratoType; @@ -415,12 +415,12 @@ // AM sample header (new format) struct AMSampleHeader { - uint32le headSize; // Header size (i.e. the size of this struct), apparently not including headSize. + uint32le headSize; // Header size (i.e. the size of this struct), apparently not including headSize. char name[32]; uint16le pan; uint16le volume; uint16le flags; - uint16le unknown; // 0x0000 / 0x0080? + uint16le unknown; // 0x0000 / 0x0080? uint32le length; uint32le loopStart; uint32le loopEnd; @@ -490,30 +490,24 @@ enum { - rowDone = 0, // Advance to next row - channelMask = 0x1F, // Mask for retrieving channel information - volFlag = 0x20, // Volume effect present - noteFlag = 0x40, // Note + instr present - effectFlag = 0x80, // Effect information present - dataFlag = 0xE0, // Channel data present + rowDone = 0x00, // Advance to next row + channelMask = 0x1F, // Mask for retrieving channel information + volFlag = 0x20, // Volume effect present + noteFlag = 0x40, // Note + instr present + effectFlag = 0x80, // Effect information present + dataFlag = 0xE0, // Channel data present }; if(chunk.NoBytesLeft()) - { return false; - } - ROWINDEX numRows = Clamp(static_cast<ROWINDEX>(chunk.ReadUint8()) + 1, ROWINDEX(1), MAX_PATTERN_ROWS); + const ROWINDEX numRows = std::min(static_cast<ROWINDEX>(chunk.ReadUint8() + 1), MAX_PATTERN_ROWS); if(!sndFile.Patterns.Insert(pat, numRows)) return false; - const CHANNELINDEX channels = sndFile.GetNumChannels(); - if(channels == 0) - return false; - + const CHANNELINDEX lastChannel = sndFile.GetNumChannels() - 1; ROWINDEX row = 0; - while(row < numRows && chunk.CanRead(1)) { const uint8 flags = chunk.ReadUint8(); @@ -523,98 +517,95 @@ row++; continue; } + if(!(flags & dataFlag)) + continue; - ModCommand &m = *sndFile.Patterns[pat].GetpModCommand(row, std::min(static_cast<CHANNELINDEX>(flags & channelMask), static_cast<CHANNELINDEX>(channels - 1))); + ModCommand &m = *sndFile.Patterns[pat].GetpModCommand(row, std::min(static_cast<CHANNELINDEX>(flags & channelMask), lastChannel)); - if(flags & dataFlag) + if(flags & effectFlag) // effect { - if(flags & effectFlag) // effect + const auto [param, command] = chunk.ReadArray<uint8, 2>(); + if(command < std::size(amEffTrans)) + { + m.SetEffectCommand(amEffTrans[command], param); + } else { - m.param = chunk.ReadUint8(); - uint8 command = chunk.ReadUint8(); - - if(command < std::size(amEffTrans)) - { - // command translation - m.command = amEffTrans[command]; - } else - { #ifdef J2B_LOG - MPT_LOG_GLOBAL(LogDebug, "J2B", MPT_UFORMAT("J2B: Unknown command: 0x{}, param 0x{}")(mpt::ufmt::HEX0<2>(command), mpt::ufmt::HEX0<2>(m.param))); + MPT_LOG_GLOBAL(LogDebug, "J2B", MPT_UFORMAT("J2B: Unknown command: 0x{}, param 0x{}")(mpt::ufmt::HEX0<2>(command), mpt::ufmt::HEX0<2>(m.param))); #endif + m.command = CMD_NONE; + } + + // Handling special commands + switch(m.command) + { + case CMD_ARPEGGIO: + if(m.param == 0) m.command = CMD_NONE; + break; + case CMD_VOLUME: + if(m.volcmd == VOLCMD_NONE && !(flags & volFlag)) + { + m.SetVolumeCommand(VOLCMD_VOLUME, Clamp(m.param, uint8(0), uint8(64))); m.command = CMD_NONE; } - - // Handling special commands - switch(m.command) + break; + case CMD_TONEPORTAVOL: + case CMD_VIBRATOVOL: + case CMD_VOLUMESLIDE: + case CMD_GLOBALVOLSLIDE: + case CMD_PANNINGSLIDE: + if(m.param & 0xF0) + m.param &= 0xF0; + break; + case CMD_PANNING8: + if(m.param <= 0x80) + m.param = mpt::saturate_cast<ModCommand::PARAM>(m.param * 2); + else if(m.param == 0xA4) + m.SetEffectCommand(CMD_S3MCMDEX, 0x91u); + break; + case CMD_PATTERNBREAK: + m.param = static_cast<ModCommand::PARAM>(((m.param >> 4) * 10u) + (m.param & 0x0Fu)); + break; + case CMD_MODCMDEX: + m.ExtendedMODtoS3MEffect(); + break; + case CMD_TEMPO: + if(m.param <= 0x1F) + m.command = CMD_SPEED; + break; + case CMD_XFINEPORTAUPDOWN: + switch(m.param & 0xF0) { - case CMD_ARPEGGIO: - if(m.param == 0) m.command = CMD_NONE; - break; - case CMD_VOLUME: - if(m.volcmd == VOLCMD_NONE) - { - m.volcmd = VOLCMD_VOLUME; - m.vol = Clamp(m.param, uint8(0), uint8(64)); - m.command = CMD_NONE; - m.param = 0; - } + case 0x10: + m.command = CMD_PORTAMENTOUP; break; - case CMD_TONEPORTAVOL: - case CMD_VIBRATOVOL: - case CMD_VOLUMESLIDE: - case CMD_GLOBALVOLSLIDE: - case CMD_PANNINGSLIDE: - if (m.param & 0xF0) m.param &= 0xF0; - break; - case CMD_PANNING8: - if(m.param <= 0x80) m.param = mpt::saturate_cast<ModCommand::PARAM>(m.param * 2); - else if(m.param == 0xA4) m.SetEffectCommand(CMD_S3MCMDEX, 0x91u); - break; - case CMD_PATTERNBREAK: - m.param = static_cast<ModCommand::PARAM>(((m.param >> 4) * 10u) + (m.param & 0x0Fu)); - break; - case CMD_MODCMDEX: - m.ExtendedMODtoS3MEffect(); - break; - case CMD_TEMPO: - if(m.param <= 0x1F) m.command = CMD_SPEED; - break; - case CMD_XFINEPORTAUPDOWN: - switch(m.param & 0xF0) - { - case 0x10: - m.command = CMD_PORTAMENTOUP; - break; - case 0x20: - m.command = CMD_PORTAMENTODOWN; - break; - } - m.param = (m.param & 0x0F) | 0xE0; - break; - default: + case 0x20: + m.command = CMD_PORTAMENTODOWN; break; } + m.param = (m.param & 0x0F) | 0xE0; + break; + default: + break; } + } - if (flags & noteFlag) // note + ins - { - const auto [instr, note] = chunk.ReadArray<uint8, 2>(); - m.instr = instr; - m.note = note; - if(m.note == 0x80) m.note = NOTE_KEYOFF; - else if(m.note > 0x80) m.note = NOTE_FADE; // I guess the support for IT "note fade" notes was not intended in mod2j2b, but hey, it works! :-D - } + if(flags & noteFlag) // note + ins + { + const auto [instr, note] = chunk.ReadArray<uint8, 2>(); + m.instr = instr; + m.note = note; + if(m.note == 0x80) + m.note = NOTE_KEYOFF; + else if(m.note > 0x80) + m.note = NOTE_FADE; // I guess the support for IT "note fade" notes was not intended in mod2j2b, but hey, it works! :-D + } - if (flags & volFlag) // volume - { - m.volcmd = VOLCMD_VOLUME; - m.vol = chunk.ReadUint8(); - if(isAM) - { - m.vol = static_cast<ModCommand::VOL>(m.vol * 64u / 127u); - } - } + if(flags & volFlag) // volume + { + m.SetVolumeCommand(VOLCMD_VOLUME, chunk.ReadUint8()); + if(isAM) + m.vol = static_cast<ModCommand::VOL>(m.vol * 64u / 127u); } } @@ -727,7 +718,7 @@ FileReader chunkMain(chunks.GetChunk(mainChunkID)); AMFFMainChunk mainChunk; - if(!chunkMain.IsValid() + if(!chunkMain.IsValid() || !chunkMain.ReadStruct(mainChunk) || mainChunk.channels < 1 || !chunkMain.CanRead(mainChunk.channels)) |