Last active
August 29, 2024 10:18
-
-
Save madrxx/0a60182074084a48345d1016da8c5d1f to your computer and use it in GitHub Desktop.
ImHex pattern language script for Sega .mpb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma endian little | |
#pragma loop_limit 1000000; | |
#include <std/core.pat> | |
#include <std/io.pat> | |
#include <std/string.pat> | |
struct Array<T, auto Size> { | |
T data[Size]; | |
}; | |
bitfield SplitToneDataFlags { | |
LinearADPCM : 1; | |
LoopSelect : 1; | |
padding : 6; | |
}; | |
struct SplitToneData { | |
u8 RefStartOfToneDataPrefix; | |
SplitToneDataFlags ToneDataFlags; | |
u16 RefStartOfToneData; | |
u16 LoopStart; // min 04 when loop on? | |
u16 NumberOfFrames; | |
if (RefStartOfToneData != 0 || RefStartOfToneDataPrefix != 0) | |
{ | |
u8 MaybeRawToneData[(NumberOfFrames + 1) / 2] /* int division rounding up */ | |
@ ((RefStartOfToneDataPrefix << 16) | RefStartOfToneData); | |
} | |
}; | |
struct Split { | |
SplitToneData ToneData; | |
u32 AmpEnvelope; | |
padding[2]; | |
u16 LFOSync; | |
u8 FXLevelAndInputCh; | |
padding[1]; | |
u8 Panpot; | |
u8 DirectLevel; | |
u128 OscAndFilterEnvelope; | |
u8 NoteRangeLow; | |
u8 NoteRangeHigh; | |
u8 BaseNote; | |
s8 FineTune; | |
padding[3]; | |
u8 VelocityLow; | |
u8 VelocityHigh; | |
u8 DrumMode; | |
u8 DrumGroupID; | |
padding[1]; | |
}; | |
struct Layer { | |
u32 RefStartOfLayer; | |
std::print(" Layer " + std::string::to_string(std::core::array_index())); | |
if (RefStartOfLayer == 0) | |
{ | |
continue; | |
} | |
else | |
{ | |
u32 StartOfLayer @ RefStartOfLayer; | |
std::print(" Start ref: " + std::string::to_string(RefStartOfLayer)); | |
u32 NumberOfSplits @ RefStartOfLayer; | |
std::print(" Splits: " + std::string::to_string(NumberOfSplits)); | |
u16 LayerDelay @ RefStartOfLayer + 0x08; | |
u8 BendRangeHigh @ RefStartOfLayer + 0x0C; | |
u8 BendRangeLow @ RefStartOfLayer + 0x0D; | |
u32 RefStartOfSplits @ RefStartOfLayer + 0x04; | |
Split Splits[NumberOfSplits] @ RefStartOfSplits; | |
} | |
}; | |
struct Program { | |
u32 RefStartOfProgram; | |
std::print("Program " + std::string::to_string(std::core::array_index())); | |
u32 StartOfProgram @ RefStartOfProgram; | |
Layer Layers[4] @ RefStartOfProgram; | |
}; | |
char FormatSignature[4] @ 0x00; | |
u32 FormatVersion @ 0x04; // maybe. consistent anyway. | |
u32 FileSize @ 0x08; | |
u32 RefStartOfProgramRefs @ 0x10; | |
u32 NumberOfPrograms @ 0x14; | |
u32 RefStartOfVelocity @ 0x18; | |
u32 NumberOfVelocities @ 0x1C; | |
u32 RefEndOfVelocity @ 0x20; | |
u32 RefEndOfVelocityPadding @ 0x28; | |
Array<Array<u8, 0x80>, NumberOfVelocities> Velocities @ RefStartOfVelocity; | |
Program Program[NumberOfPrograms] @ RefStartOfProgramRefs; | |
u32 Checksum @ FileSize - 8; | |
u32 CalcChecksum; | |
u32 Iterator = 4; | |
while (Iterator < FileSize - 8) | |
{ | |
u8 IterationAdd @ Iterator; | |
CalcChecksum += IterationAdd; | |
Iterator += 1; | |
} | |
std::print(std::string::to_string(CalcChecksum)); | |
//u32 CalcChecksum = FormatVersion + FileSize | |
//+ RefStartOfProgramRefs + NumberOfPrograms | |
//+ RefStartOfVelocity + NumberOfVelocities + RefEndOfVelocity | |
//+ RefEndOfVelocityPadding; |
woofmute (who showed me the Classic Mac OS tools I used to document this) just discovered and linked me to https://github.com/dakrk/manatools, which looks like a useful tool/resource for others looking at the Dreamcast manatee driver formats (including MPB) and dakrk mentions how they could have saved time if they found another older tool for MPBs that i also didn’t know about when I wrote this imhex script and never thought to link from here: https://github.com/X-Hax/sa_tools_research
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
'MaybeRawToneData' refers to the fact that the length is often seemingly incorrect - there is extra data following it before the next tone data piece starts. MaybeRawToneData measures 4 bits * the number of samples (rounded up to the end of the byte if it's an odd number of samples). Yamaha ADPCM for AICA or ADPCM in general might have some extra bytes with some other data (is there a predictor value or something?)? I don't know what I'm doing :)
anyway, the end of all the tone data sections must be at latest where the next tone data section starts, and that is a known value for each piece of the file, so when reading mpbs, the file could be divided at that point. the end of the final tone data section is the exception, but once we know the length of the checksum (i thiink it's a dword?) that's just before 'ENDB' (the end of the Bank), we'd have the same kind of latest possible endpoint.
once I know how to import these files back into Rez, it would be interesting to try replacing everything in a tone data space with the raw adpcm from wavecon.exe and changing the 'numberofframes' and subsequent 'refstartoftonedata' references to fit the size and sample length of the new tone data. Regardless of how the game knows how long the tone data (at least if it's: 1) knowing how long to read for based on the number of samples (numberofframes) in a way that I don't, 2) knowing the location of the next tone data / end of file and splitting it up there like I'm thinking we can do for reading the files, or 3) based on some information that ADPCM streams just include), it would probably just work to splice in a valid ADPCM stream, and then we don't really need to understand how the game knows (but i'm curious and it would be nice to figure out later).