diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt index 32751f7a..1399580f 100644 --- a/config/SOUE01/splits.txt +++ b/config/SOUE01/splits.txt @@ -2700,10 +2700,12 @@ d/snd/d_snd_bgm_sound.cpp: d/snd/d_snd_bgm_data_mgr.cpp: .text start:0x8037BA70 end:0x8037C518 align:16 + .data start:0x80549148 end:0x80549158 .sdata start:0x805742F0 end:0x805742F8 d/snd/d_snd_bgm_mml_parser_base.cpp: .text start:0x8037C520 end:0x8037D014 align:16 + .data start:0x80549158 end:0x80549168 d/snd/d_snd_bgm_mml_parsers.cpp: .text start:0x8037D020 end:0x8037D800 align:16 diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt index 4e5814d2..b20c48a9 100644 --- a/config/SOUE01/symbols.txt +++ b/config/SOUE01/symbols.txt @@ -20798,18 +20798,18 @@ fn_8037C370 = .text:0x8037C370; // type:function size:0x34 fn_8037C3B0 = .text:0x8037C3B0; // type:function size:0x58 fn_8037C410 = .text:0x8037C410; // type:function size:0x60 setHarpPitchSeqVars = .text:0x8037C470; // type:function size:0xA8 -fn_8037C520 = .text:0x8037C520; // type:function size:0x20 -fn_8037C540 = .text:0x8037C540; // type:function size:0x34 -fn_8037C580 = .text:0x8037C580; // type:function size:0x98 -fn_8037C620 = .text:0x8037C620; // type:function size:0x1E0 -dSndMmlParser__Parse = .text:0x8037C800; // type:function size:0x4F0 -dSndMmlParser__CommandProc = .text:0x8037CCF0; // type:function size:0x18C -dSndMmlParser__Read16 = .text:0x8037CE80; // type:function size:0x24 -dSndMmlParser__Read24 = .text:0x8037CEB0; // type:function size:0x38 -dSndMmlParser__ReadVar = .text:0x8037CEF0; // type:function size:0x30 -dSndMmlParser__ReadArg = .text:0x8037CF20; // type:function size:0xD8 -fn_8037D000 = .text:0x8037D000; // type:function size:0x4 -fn_8037D010 = .text:0x8037D010; // type:function size:0x4 +__ct__18dSndBgmMmlParser_cFPQ34nw4r3snd18SoundArchivePlayerPQ34nw4r3snd12SoundArchive = .text:0x8037C520; // type:function size:0x20 +initTrack__18dSndBgmMmlParser_cFlUl = .text:0x8037C540; // type:function size:0x34 +loadAndParse__18dSndBgmMmlParser_cFUlUlb = .text:0x8037C580; // type:function size:0x98 +readTracks__18dSndBgmMmlParser_cFUlUlb = .text:0x8037C620; // type:function size:0x1E0 +Parse__18dSndBgmMmlParser_cCFP12dBgmMmlTrack = .text:0x8037C800; // type:function size:0x4F0 +CommandProc___18dSndBgmMmlParser_cCFP12dBgmMmlTrackUlll = .text:0x8037CCF0; // type:function size:0x18C +Read16__18dSndBgmMmlParser_cCFPPCUc = .text:0x8037CE80; // type:function size:0x24 +Read24__18dSndBgmMmlParser_cCFPPCUc = .text:0x8037CEB0; // type:function size:0x38 +ReadVar__18dSndBgmMmlParser_cCFPPCUc = .text:0x8037CEF0; // type:function size:0x30 +ReadArg__18dSndBgmMmlParser_cCFPPCUcQ218dSndBgmMmlParser_c10SeqArgType = .text:0x8037CF20; // type:function size:0xD8 +CommandProc__18dSndBgmMmlParser_cCFUliUlll = .text:0x8037D000; // type:function size:0x4 +NoteOnCommandProc__18dSndBgmMmlParser_cCFUliiil = .text:0x8037D010; // type:function size:0x4 fn_8037D020 = .text:0x8037D020; // type:function size:0x64 fn_8037D090 = .text:0x8037D090; // type:function size:0xE8 fn_8037D180 = .text:0x8037D180; // type:function size:0x84 @@ -38042,7 +38042,7 @@ lbl_80549088 = .data:0x80549088; // type:object size:0x34 lbl_805490BC = .data:0x805490BC; // type:object size:0x44 lbl_80549100 = .data:0x80549100; // type:object size:0x48 lbl_80549148 = .data:0x80549148; // type:object size:0x10 -lbl_80549158 = .data:0x80549158; // type:object size:0x10 +__vt__18dSndBgmMmlParser_c = .data:0x80549158; // type:object size:0x10 lbl_80549168 = .data:0x80549168; // type:object size:0x10 lbl_80549178 = .data:0x80549178; // type:object size:0x10 lbl_80549188 = .data:0x80549188; // type:object size:0x10 diff --git a/configure.py b/configure.py index a7d5642c..36d34462 100644 --- a/configure.py +++ b/configure.py @@ -758,7 +758,7 @@ config.libs = [ Object(NonMatching, "d/snd/d_snd_bgm_sound_battle_callbacks.cpp"), Object(NonMatching, "d/snd/d_snd_bgm_sound.cpp"), Object(NonMatching, "d/snd/d_snd_bgm_data_mgr.cpp"), - Object(NonMatching, "d/snd/d_snd_bgm_mml_parser_base.cpp"), + Object(Matching, "d/snd/d_snd_bgm_mml_parser_base.cpp"), Object(NonMatching, "d/snd/d_snd_bgm_mml_parsers.cpp"), Object(NonMatching, "d/snd/d_snd_small_effect_mgr.cpp"), Object(NonMatching, "d/snd/d_snd_harp_mgr.cpp"), diff --git a/include/d/snd/d_snd_bgm_mml_parser_base.h b/include/d/snd/d_snd_bgm_mml_parser_base.h new file mode 100644 index 00000000..b69946c1 --- /dev/null +++ b/include/d/snd/d_snd_bgm_mml_parser_base.h @@ -0,0 +1,203 @@ +#ifndef D_SND_BGM_MML_PARSER_BASE_H +#define D_SND_BGM_MML_PARSER_BASE_H + +#include "common.h" +#include "nw4r/snd/snd_SoundArchive.h" +#include "nw4r/snd/snd_SoundArchivePlayer.h" + +// [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2cdd6 +struct dBgmMmlCallStack +{ + bool loopFlag; // size 0x01, offset 0x00 + u8 loopCount; // size 0x01, offset 0x01 + /* 2 bytes padding */ + byte_t const *address; // size 0x04, offset 0x04 +}; // size 0x08 + +/** + * Size: 0x28 + */ +struct dBgmMmlTrack { + /* 0x00 */ const byte_t *currentAddr; + /* 0x04 */ u8 mTrackNo; + /* 0x05 */ bool mTrackUsed; + /* 0x06 */ u8 cmpFlag; + /* 0x08 */ s32 wait; + /* 0x0C */ dBgmMmlCallStack callStack[3]; + /* 0x24 */ s32 callStackDepth; +}; + +/** + * @brief Parses harp key data + * + * Partial copy of nw4r::snd::detail::MmlParser + */ +class dSndBgmMmlParser_c { +private: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2d70c + // Swapped - or maybe just returns a boolean + enum ParseResult { + PARSE_RESULT_FINISH, + PARSE_RESULT_CONTINUE, + }; + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x31279 + enum SeqArgType { + SEQ_ARG_NONE, + + SEQ_ARG_U8, + SEQ_ARG_S16, + SEQ_ARG_VMIDI, + SEQ_ARG_RANDOM, + SEQ_ARG_VARIABLE, + }; + + enum MmlCommand { + MML_CMD_MIN = 0x80, // <80 -> MML note, not a command + MML_CMD_MAX = 0xff, + + MML_CMD_MASK = 0x80, + MML_CMD_SET_MASK = 0xf0, + + MML_WAIT = 0x80, + MML_SET_PRGNO, + + MML_OPEN_TRACK = 0x88, + MML_JUMP, + MML_CALL, + + MML_ARG_1_RANDOM = 0xa0, + MML_ARG_1_VARIABLE, + MML_EXEC_IF, + MML_ARG_2_S16, + MML_ARG_2_RANDOM, + MML_ARG_2_VARIABLE, + + MML_SET_TIMEBASE = 0xb0, + MML_SET_ENV_HOLD, + MML_SET_MONOPHONIC, + MML_SET_TRACK_VELOCITY_RANGE, + MML_SET_BIQUAD_TYPE, + MML_SET_BIQUAD_VALUE, + + MML_SET_PAN = 0xc0, + MML_SET_TRACK_VOLUME, + MML_SET_PLAYER_VOLUME, + MML_SET_TRANSPOSE, + MML_SET_PITCH_BEND, + MML_SET_BEND_RANGE, + MML_SET_PRIORITY, + MML_SET_NOTE_WAIT, + MML_SET_TIE, + MML_SET_PORTAMENTO, + MML_SET_LFO_DEPTH, + MML_SET_LFO_SPEED, + MML_SET_LFO_TARGET, + MML_SET_LFO_RANGE, + MML_SET_PORTASPEED, + MML_SET_PORTATIME, + + MML_SET_ATTACK = 0xd0, + MML_SET_DECAY, + MML_SET_SUSTAIN, + MML_SET_RELEASE, + MML_LOOP_START, + MML_SET_TRACK_VOLUME2, + MML_PRINT_VAR, + MML_SET_SURROUND_PAN, + MML_SET_LPF_FREQ, + MML_SET_FX_SEND_A, + MML_SET_FX_SEND_B, + MML_SET_MAIN_SEND, + MML_SET_INIT_PAN, + MML_SET_MUTE, + MML_SET_FX_SEND_C, + MML_SET_DAMPER, + + MML_SET_LFO_DELAY = 0xe0, + MML_SET_TEMPO, + MML_SET_E2, + MML_SET_SWEEP_PITCH, + + MML_RESET_ADSR = 0xfb, + MML_LOOP_END, + MML_RET, + MML_ALLOC_TRACK, + MML_EOF + }; + + enum MmlExCommand { + MML_EX_COMMAND = 0xf0, + + MML_EX_CMD_MAX = 0xffff, + + MML_EX_ARITHMETIC = 0x80, + MML_EX_SET = 0x80, + MML_EX_APL, + MML_EX_AMI, + MML_EX_AMU, + MML_EX_ADV, + MML_EX_ALS, + MML_EX_RND, + MML_EX_AAD, + MML_EX_AOR, + MML_EX_AER, + MML_EX_ACO, + MML_EX_AMD, + + MML_EX_LOGIC = 0x90, + MML_EX_EQ = 0x90, + MML_EX_GE, + MML_EX_GT, + MML_EX_LE, + MML_EX_LT, + MML_EX_NE, + + MML_EX_USERPROC = 0xe0, + }; + +public: + dSndBgmMmlParser_c(nw4r::snd::SoundArchivePlayer *player, nw4r::snd::SoundArchive *archive); + + virtual void CommandProc(u32 trackNo, int wait, u32 command, s32 commandArg1, s32 commandArg2) const; + virtual void NoteOnCommandProc(u32 trackNo, int wait, int key, int velocity, s32 length) const; + +protected: + bool loadAndParse(u32 soundId, u32 trackMask, bool noJumps); + +private: + bool readTracks(u32 soundId, u32 trackMask, bool noJumps); + void initTrack(s32 trackNo, u32 offset); + ParseResult Parse(dBgmMmlTrack *track) const; + void CommandProc_(dBgmMmlTrack *track, u32 command, s32 commandArg1, s32 commandArg2) const; + + u8 ReadByte(byte_t const **ptr) const { + return *(*ptr)++; + } + u16 Read16(byte_t const **ptr) const; + u32 Read24(byte_t const **ptr) const; + s32 ReadVar(byte_t const **ptr) const; + s32 ReadArg(byte_t const **ptr, SeqArgType argType) const; + + // static members +public: + static int const TEMPO_MAX = 1023; + static int const TEMPO_MIN = 0; + static int const CALL_STACK_DEPTH = 3; + static int const SURROUND_PAN_CENTER; + static int const PAN_CENTER = 64; + +private: + static bool mPrintVarEnabledFlag; + + // members +private: + /* vtable */ // size 0x04, offset 0x00 + /* 0x04 */ u8 field_0x04; + /* 0x08 */ nw4r::snd::SoundArchivePlayer *mpPlayer; + /* 0x0C */ nw4r::snd::SoundArchive *mpArchive; + /* 0x10 */ const byte_t *baseAddr; + /* 0x14 */ dBgmMmlTrack mTracks[16]; +}; // size 0x04 + +#endif diff --git a/src/d/snd/d_snd_bgm_mml_parser_base.cpp b/src/d/snd/d_snd_bgm_mml_parser_base.cpp new file mode 100644 index 00000000..37cf2c93 --- /dev/null +++ b/src/d/snd/d_snd_bgm_mml_parser_base.cpp @@ -0,0 +1,580 @@ +#include "d/snd/d_snd_bgm_mml_parser_base.h" + +#include "common.h" +#include "nw4r/snd/snd_SeqFile.h" +#include "nw4r/snd/snd_SoundArchive.h" +#include "nw4r/snd/snd_SoundArchivePlayer.h" + +dSndBgmMmlParser_c::dSndBgmMmlParser_c(nw4r::snd::SoundArchivePlayer *player, nw4r::snd::SoundArchive *archive) + : field_0x04(0), mpPlayer(player), mpArchive(archive) {} + +void dSndBgmMmlParser_c::initTrack(s32 trackNo, u32 offset) { + dBgmMmlTrack &trackParam = mTracks[trackNo]; + trackParam.currentAddr = baseAddr + offset; + trackParam.mTrackNo = trackNo; + trackParam.mTrackUsed = true; + trackParam.cmpFlag = 0; + trackParam.wait = 0; + trackParam.callStackDepth = 0; +} + +bool dSndBgmMmlParser_c::loadAndParse(u32 soundId, u32 trackMask, bool noJumps) { + if (!readTracks(soundId, trackMask, noJumps)) { + return false; + } + + for (int i = 0; i < 16; i++) { + if (mTracks[i].mTrackUsed) { + while (Parse(&mTracks[i]) != PARSE_RESULT_FINISH) {} + } + } + return true; +} + +bool dSndBgmMmlParser_c::readTracks(u32 soundId, u32 trackMask, bool noJumps) { + nw4r::snd::SoundArchivePlayer *player = mpPlayer; + nw4r::snd::SoundArchive *archive = mpArchive; + + field_0x04 = noJumps; + nw4r::snd::SoundArchive::SoundInfo info; + if (!archive->ReadSoundInfo(soundId, &info)) { + return false; + } + + if (archive->GetSoundType(soundId) != nw4r::snd::SoundArchive::SOUND_TYPE_SEQ) { + return false; + } + + const void *addr = player->detail_GetFileAddress(info.fileId); + if (addr == nullptr) { + return false; + } + + nw4r::snd::detail::SeqFileReader reader(addr); + baseAddr = (const byte_t *)reader.GetBaseAddress(); + for (int i = 0; i < 16; i++) { + mTracks[i].mTrackUsed = false; + } + initTrack(0, 0); + + const byte_t *ptr = baseAddr; + bool endReached = false; + + for (int i = 0; i < 20; i++) { + u32 cmd = ReadByte(&ptr); + switch (cmd) { + case MML_ALLOC_TRACK: + Read16(&ptr); + break; + case MML_OPEN_TRACK: { + u8 trackNo = ReadByte(&ptr); + u32 offset = Read24(&ptr); + if (((trackMask >> trackNo) & 1) != 0) { + initTrack(trackNo, offset); + } + break; + } + case MML_SET_TIMEBASE: + ptr += 1; + break; + default: + endReached = true; + break; + } + + if (endReached) { + mTracks[0].currentAddr = ptr - 1; + break; + } + } + + if (!endReached) { + return false; + } + + if ((trackMask & 1) == 0) { + mTracks[0].mTrackUsed = false; + } + return true; +} + +// clang-format off + +// modified from nw4r::snd - some changes I had to make to make it match might be fake +dSndBgmMmlParser_c::ParseResult dSndBgmMmlParser_c::Parse(dBgmMmlTrack *track) const { + dBgmMmlTrack &trackParam = *track; + + // Had to move these up for regswaps - maybe fake + s32 commandArg1; + s32 commandArg2; + + SeqArgType argType; + SeqArgType argType2 = SEQ_ARG_NONE; + + bool useArgType = false; + bool doExecCommand = true; + + + u32 cmd = ReadByte(&trackParam.currentAddr); + + if (cmd == MML_EXEC_IF) + { + cmd = ReadByte(&trackParam.currentAddr); + doExecCommand = trackParam.cmpFlag != false; + } + + if (cmd == MML_ARG_2_S16) + { + cmd = ReadByte(&trackParam.currentAddr); + argType2 = SEQ_ARG_S16; + } + else if (cmd == MML_ARG_2_RANDOM) + { + cmd = ReadByte(&trackParam.currentAddr); + argType2 = SEQ_ARG_RANDOM; + } + else if (cmd == MML_ARG_2_VARIABLE) + { + cmd = ReadByte(&trackParam.currentAddr); + argType2 = SEQ_ARG_VARIABLE; + } + + if (cmd == MML_ARG_1_RANDOM) + { + cmd = ReadByte(&trackParam.currentAddr); + argType = SEQ_ARG_RANDOM; + useArgType = true; + } + else if (cmd == MML_ARG_1_VARIABLE) + { + cmd = ReadByte(&trackParam.currentAddr); + argType = SEQ_ARG_VARIABLE; + useArgType = true; + } + + if (!(cmd & MML_CMD_MASK)) + { + // MML note data, not a command + u8 velocity = ReadByte(&trackParam.currentAddr); + + s32 length = ReadArg(&trackParam.currentAddr, + useArgType ? argType : SEQ_ARG_VMIDI); + + if (!doExecCommand) + return PARSE_RESULT_CONTINUE; + + int key = cmd; + + NoteOnCommandProc(trackParam.mTrackNo, trackParam.wait, key, velocity, + length > 0 ? length : -1); + } + else + { + // MML command + commandArg1 = 0; + commandArg2 = 0; + + switch (cmd & MML_CMD_SET_MASK) + { + case 0x80: + { + switch (cmd) + { + case MML_WAIT: + { + s32 arg = ReadArg(&trackParam.currentAddr, + useArgType ? argType : SEQ_ARG_VMIDI); + + if (doExecCommand) + trackParam.wait += arg; + } + break; + + case MML_SET_PRGNO: + commandArg1 = ReadArg(&trackParam.currentAddr, + useArgType ? argType : SEQ_ARG_VMIDI); + + if (doExecCommand) + CommandProc_(track, cmd, commandArg1, commandArg2); + + break; + + case MML_OPEN_TRACK: + { + // Had to change u8 -> u32 for regswaps - maybe fake + u32 trackNo = ReadByte(&trackParam.currentAddr); + u32 offset = Read24(&trackParam.currentAddr); + + if (doExecCommand) + { + commandArg1 = trackNo; + commandArg2 = offset; + CommandProc_(track, cmd, commandArg1, commandArg2); + } + } + break; + + case MML_JUMP: + case MML_CALL: + { + u32 offset = Read24(&trackParam.currentAddr); + + if (doExecCommand) + { + commandArg1 = offset; + CommandProc_(track, cmd, commandArg1, commandArg2); + } + } + break; + } + + break; + } + + case 0xb0: + case 0xc0: + case 0xd0: + { + u8 arg = ReadArg(&trackParam.currentAddr, + useArgType ? argType : SEQ_ARG_U8); + + if (argType2 != SEQ_ARG_NONE) + { + commandArg2 = + ReadArg(&trackParam.currentAddr, argType2); + } + + if (!doExecCommand) + break; + + switch (cmd) + { + case MML_SET_TRANSPOSE: + case MML_SET_PITCH_BEND: + commandArg1 = *reinterpret_cast(&arg); + break; + + default: + commandArg1 = *reinterpret_cast(&arg); + break; + } + + CommandProc_(track, cmd, commandArg1, commandArg2); + break; + } + + case 0x90: + if (doExecCommand) + CommandProc_(track, cmd, commandArg1, commandArg2); + + break; + + case 0xe0: + commandArg1 = + static_cast(ReadArg(&trackParam.currentAddr, + useArgType ? argType : SEQ_ARG_S16)); + + if (doExecCommand) + CommandProc_(track, cmd, commandArg1, commandArg2); + + break; + + case 0xf0: + { + switch (cmd) + { + case MML_ALLOC_TRACK: + Read16(&trackParam.currentAddr); + NW4RPanicMessage_Line( + 312, "seq: must use alloctrack in startup code"); + + break; + + case MML_EOF: + if (doExecCommand) + return PARSE_RESULT_FINISH; + + break; + + case MML_EX_COMMAND: + { + u32 cmdex = ReadByte(&trackParam.currentAddr); + + switch (cmdex & MML_CMD_SET_MASK) + { + case MML_EX_USERPROC: + commandArg1 = static_cast( + ReadArg(&trackParam.currentAddr, + useArgType ? argType : SEQ_ARG_S16)); + + if (doExecCommand) + { + CommandProc_(track, (cmd << 8) + cmdex, commandArg1, + commandArg2); + } + + break; + + case MML_EX_ARITHMETIC: + case MML_EX_LOGIC: + commandArg1 = ReadByte(&trackParam.currentAddr); + commandArg2 = static_cast( + ReadArg(&trackParam.currentAddr, + useArgType ? argType : SEQ_ARG_S16)); + + if (doExecCommand) + { + CommandProc_(track, (cmd << 8) + cmdex, commandArg1, + commandArg2); + } + + break; + } + } + ATTR_FALLTHROUGH; + + default: + if (doExecCommand) + CommandProc_(track, cmd, commandArg1, commandArg2); + + break; + } + } + break; + + case 0xa0: + NW4RPanicMessage_Line(392, "Invalid seqdata command: %d", cmd); + break; + } + } + + return PARSE_RESULT_CONTINUE; +} + +// very stripped down from nw4r::snd +void dSndBgmMmlParser_c::CommandProc_(dBgmMmlTrack *track, u32 command, s32 commandArg1, s32 commandArg2) const { + dBgmMmlTrack &trackParam = *track; + + switch (command) + { + case MML_JUMP: + if (field_0x04 == 0) + trackParam.currentAddr = baseAddr + commandArg1; + break; + + case MML_CALL: + { + if (trackParam.callStackDepth >= CALL_STACK_DEPTH) + { + NW4RWarningMessage_Line(665, + "nw4r::snd::MmlParser: cannot \'call\' " + "because already too deep"); + break; + } + + dBgmMmlCallStack *callStack = + &trackParam.callStack[trackParam.callStackDepth]; + + callStack->address = trackParam.currentAddr; + callStack->loopFlag = false; + + trackParam.callStackDepth++; + trackParam.currentAddr = baseAddr + commandArg1; + break; + } + + case MML_RET: + { + dBgmMmlCallStack *tmp; // needed for lbzu + dBgmMmlCallStack *callStack = nullptr; + + while (trackParam.callStackDepth > 0) + { + tmp = &trackParam.callStack[--trackParam.callStackDepth]; + + if (!tmp->loopFlag) + { + callStack = tmp; + break; + } + } + + if (!callStack) + { + NW4RWarningMessage_Line( + 688, + "nw4r::snd::MmlParser: unmatched sequence command \'ret\'"); + + break; + } + + trackParam.currentAddr = callStack->address; + } + break; + + case MML_LOOP_START: + { + if (trackParam.callStackDepth >= CALL_STACK_DEPTH) + { + NW4RWarningMessage_Line( + 698, "nw4r::snd::MmlParser: cannot \'loop_start\' because " + "already too deep"); + + break; + } + + dBgmMmlCallStack *callStack = + &trackParam.callStack[trackParam.callStackDepth]; + + callStack->address = trackParam.currentAddr; + callStack->loopFlag = true; + callStack->loopCount = commandArg1; + + trackParam.callStackDepth++; + } + break; + + case MML_LOOP_END: + { + if (trackParam.callStackDepth <= 0) + { + NW4RWarningMessage_Line(713, "nw4r::snd::MmlParser: unmatched " + "sequence command \'loop_end\'"); + break; + } + + dBgmMmlCallStack *callStack = + &trackParam.callStack[trackParam.callStackDepth - 1]; + + if (!callStack->loopFlag) + { + NW4RWarningMessage_Line(719, "nw4r::snd::MmlParser: unmatched " + "sequence command \'loop_end\'"); + break; + } + + u8 loop_count = callStack->loopCount; + + if (loop_count && --loop_count == 0) + { + trackParam.callStackDepth--; + } + else + { + callStack->loopCount = loop_count; + + trackParam.currentAddr = callStack->address; + } + } + break; + } + + CommandProc(track->mTrackNo, track->wait, command, commandArg1, commandArg2); +} + +// copied from nw4r::snd start +u16 dSndBgmMmlParser_c::Read16(byte_t const **ptr) const +{ + u16 ret = ReadByte(ptr); + + ret <<= 8; + ret |= ReadByte(ptr); + + return ret; +} + +u32 dSndBgmMmlParser_c::Read24(byte_t const **ptr) const +{ + u32 ret = ReadByte(ptr); + + ret <<= 8; + ret |= ReadByte(ptr); + + ret <<= 8; + ret |= ReadByte(ptr); + + return ret; +} + +s32 dSndBgmMmlParser_c::ReadVar(byte_t const **ptr) const +{ + s32 ret = 0; + byte_t b; + + for (int i = 0;; i++) + { + NW4RAssert_Line(940, i < 4); + + b = ReadByte(ptr); + ret <<= 7; + ret |= b & 0x7f; + + if (!(b & 0x80)) + break; + } + + return ret; +} + +s32 dSndBgmMmlParser_c::ReadArg(byte_t const **ptr, + SeqArgType argType) const +{ + s32 var; + + switch (argType) + { + case SEQ_ARG_U8: + var = ReadByte(ptr); + break; + + case SEQ_ARG_S16: + var = Read16(ptr); + break; + + case SEQ_ARG_VMIDI: + var = ReadVar(ptr); + break; + + case SEQ_ARG_VARIABLE: + { + var = ReadByte(ptr); + /* + u8 varNo = ReadByte(ptr); + + s16 const volatile *varPtr = GetVariablePtr(player, track, varNo); + + // ERRATUM: if varPtr is not valid then ReadArg returns garbage + if (varPtr) + var = *varPtr; + */ + } + break; + + case SEQ_ARG_RANDOM: + { + s32 rand; + + s16 min = Read16(ptr); + s16 max = Read16(ptr); + + rand = nw4r::snd::detail::Util::CalcRandom(); + rand *= max - min + 1; + rand >>= 16; + rand += min; + + var = rand; + } + break; + } + + return var; +} +// clang-format on + +void dSndBgmMmlParser_c::CommandProc(u32 trackNo, int wait, u32 command, s32 commandArg1, s32 commandArg2) const { + // noop +} + +void dSndBgmMmlParser_c::NoteOnCommandProc(u32 trackNo, int wait, int key, int velocity, s32 length) const { + // noop +}