From 7aaff93001e4681de3b201fb81dad866a9e52c02 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Mon, 10 Feb 2025 22:02:10 +0100 Subject: [PATCH] Sanitize size of all event commands Also fixed a oob access in ShowBattleAnimation and OpenSaveMenu --- src/game_interpreter.cpp | 254 ++++++++++++++++---------------- src/game_interpreter_battle.cpp | 36 ++--- src/game_interpreter_map.cpp | 87 +++++------ src/game_interpreter_shared.h | 18 ++- 4 files changed, 203 insertions(+), 192 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 5d40af1ef8..af7a369082 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -582,218 +582,218 @@ bool Game_Interpreter::ExecuteCommand() { bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) { switch (static_cast(com.code)) { case Cmd::ShowMessage: - return CommandShowMessage(com); + return CmdSetup<&Game_Interpreter::CommandShowMessage, 0>(com); case Cmd::MessageOptions: - return CommandMessageOptions(com); + return CmdSetup<&Game_Interpreter::CommandMessageOptions, 4>(com); case Cmd::ChangeFaceGraphic: - return CommandChangeFaceGraphic(com); + return CmdSetup<&Game_Interpreter::CommandChangeFaceGraphic, 3>(com); case Cmd::ShowChoice: - return CommandShowChoices(com); + return CmdSetup<&Game_Interpreter::CommandShowChoices, 1>(com); case Cmd::ShowChoiceOption: - return CommandShowChoiceOption(com); + return CmdSetup<&Game_Interpreter::CommandShowChoiceOption, 1>(com); case Cmd::ShowChoiceEnd: - return CommandShowChoiceEnd(com); + return CmdSetup<&Game_Interpreter::CommandShowChoiceEnd, 0>(com); case Cmd::InputNumber: - return CommandInputNumber(com); + return CmdSetup<&Game_Interpreter::CommandInputNumber, 2>(com); case Cmd::ControlSwitches: - return CommandControlSwitches(com); + return CmdSetup<&Game_Interpreter::CommandControlSwitches, 4>(com); case Cmd::ControlVars: - return CommandControlVariables(com); + return CmdSetup<&Game_Interpreter::CommandControlVariables, 7>(com); case Cmd::TimerOperation: - return CommandTimerOperation(com); + return CmdSetup<&Game_Interpreter::CommandTimerOperation, 5>(com); case Cmd::ChangeGold: - return CommandChangeGold(com); + return CmdSetup<&Game_Interpreter::CommandChangeGold, 3>(com); case Cmd::ChangeItems: - return CommandChangeItems(com); + return CmdSetup<&Game_Interpreter::CommandChangeItems, 5>(com); case Cmd::ChangePartyMembers: - return CommandChangePartyMember(com); + return CmdSetup<&Game_Interpreter::CommandChangePartyMember, 3>(com); case Cmd::ChangeExp: - return CommandChangeExp(com); + return CmdSetup<&Game_Interpreter::CommandChangeExp, 6>(com); case Cmd::ChangeLevel: - return CommandChangeLevel(com); + return CmdSetup<&Game_Interpreter::CommandChangeLevel, 6>(com); case Cmd::ChangeParameters: - return CommandChangeParameters(com); + return CmdSetup<&Game_Interpreter::CommandChangeParameters, 6>(com); case Cmd::ChangeSkills: - return CommandChangeSkills(com); + return CmdSetup<&Game_Interpreter::CommandChangeSkills, 5>(com); case Cmd::ChangeEquipment: - return CommandChangeEquipment(com); + return CmdSetup<&Game_Interpreter::CommandChangeEquipment, 5>(com); case Cmd::ChangeHP: - return CommandChangeHP(com); + return CmdSetup<&Game_Interpreter::CommandChangeHP, 6>(com); case Cmd::ChangeSP: - return CommandChangeSP(com); + return CmdSetup<&Game_Interpreter::CommandChangeSP, 5>(com); case Cmd::ChangeCondition: - return CommandChangeCondition(com); + return CmdSetup<&Game_Interpreter::CommandChangeCondition, 4>(com); case Cmd::FullHeal: - return CommandFullHeal(com); + return CmdSetup<&Game_Interpreter::CommandFullHeal, 2>(com); case Cmd::SimulatedAttack: - return CommandSimulatedAttack(com); + return CmdSetup<&Game_Interpreter::CommandSimulatedAttack, 8>(com); case Cmd::Wait: - return CommandWait(com); + return CmdSetup<&Game_Interpreter::CommandWait, 1>(com); case Cmd::PlayBGM: - return CommandPlayBGM(com); + return CmdSetup<&Game_Interpreter::CommandPlayBGM, 4>(com); case Cmd::FadeOutBGM: - return CommandFadeOutBGM(com); + return CmdSetup<&Game_Interpreter::CommandFadeOutBGM, 1>(com); case Cmd::PlaySound: - return CommandPlaySound(com); + return CmdSetup<&Game_Interpreter::CommandPlaySound, 3>(com); case Cmd::EndEventProcessing: - return CommandEndEventProcessing(com); + return CmdSetup<&Game_Interpreter::CommandEndEventProcessing, 0>(com); case Cmd::Comment: case Cmd::Comment_2: - return CommandComment(com); + return CmdSetup<&Game_Interpreter::CommandComment, 0>(com); case Cmd::GameOver: - return CommandGameOver(com); + return CmdSetup<&Game_Interpreter::CommandGameOver, 0>(com); case Cmd::ChangeHeroName: - return CommandChangeHeroName(com); + return CmdSetup<&Game_Interpreter::CommandChangeHeroName, 1>(com); case Cmd::ChangeHeroTitle: - return CommandChangeHeroTitle(com); + return CmdSetup<&Game_Interpreter::CommandChangeHeroTitle, 1>(com); case Cmd::ChangeSpriteAssociation: return CmdSetup<&Game_Interpreter::CommandChangeSpriteAssociation, 3>(com); case Cmd::ChangeActorFace: - return CmdSetup<&Game_Interpreter::CommandChangeActorFace, 4>(com); + return CmdSetup<&Game_Interpreter::CommandChangeActorFace, 2>(com); case Cmd::ChangeVehicleGraphic: - return CommandChangeVehicleGraphic(com); + return CmdSetup<&Game_Interpreter::CommandChangeVehicleGraphic, 2>(com); case Cmd::ChangeSystemBGM: - return CommandChangeSystemBGM(com); + return CmdSetup<&Game_Interpreter::CommandChangeSystemBGM, 5>(com); case Cmd::ChangeSystemSFX: - return CommandChangeSystemSFX(com); + return CmdSetup<&Game_Interpreter::CommandChangeSystemSFX, 4>(com); case Cmd::ChangeSystemGraphics: - return CommandChangeSystemGraphics(com); + return CmdSetup<&Game_Interpreter::CommandChangeSystemGraphics, 2>(com); case Cmd::ChangeScreenTransitions: - return CommandChangeScreenTransitions(com); + return CmdSetup<&Game_Interpreter::CommandChangeScreenTransitions, 2>(com); case Cmd::MemorizeLocation: - return CommandMemorizeLocation(com); + return CmdSetup<&Game_Interpreter::CommandMemorizeLocation, 3>(com); case Cmd::SetVehicleLocation: - return CommandSetVehicleLocation(com); + return CmdSetup<&Game_Interpreter::CommandSetVehicleLocation, 5>(com); case Cmd::ChangeEventLocation: - return CommandChangeEventLocation(com); + return CmdSetup<&Game_Interpreter::CommandChangeEventLocation, 4>(com); case Cmd::TradeEventLocations: - return CommandTradeEventLocations(com); + return CmdSetup<&Game_Interpreter::CommandTradeEventLocations, 2>(com); case Cmd::StoreTerrainID: - return CommandStoreTerrainID(com); + return CmdSetup<&Game_Interpreter::CommandStoreTerrainID, 4>(com); case Cmd::StoreEventID: - return CommandStoreEventID(com); + return CmdSetup<&Game_Interpreter::CommandStoreEventID, 4>(com); case Cmd::EraseScreen: - return CommandEraseScreen(com); + return CmdSetup<&Game_Interpreter::CommandEraseScreen, 1>(com); case Cmd::ShowScreen: - return CommandShowScreen(com); + return CmdSetup<&Game_Interpreter::CommandShowScreen, 1>(com); case Cmd::TintScreen: - return CommandTintScreen(com); + return CmdSetup<&Game_Interpreter::CommandTintScreen, 6>(com); case Cmd::FlashScreen: - return CommandFlashScreen(com); + return CmdSetup<&Game_Interpreter::CommandFlashScreen, 6>(com); case Cmd::ShakeScreen: - return CommandShakeScreen(com); + return CmdSetup<&Game_Interpreter::CommandShakeScreen, 4>(com); case Cmd::WeatherEffects: - return CommandWeatherEffects(com); + return CmdSetup<&Game_Interpreter::CommandWeatherEffects, 2>(com); case Cmd::ShowPicture: - return CommandShowPicture(com); + return CmdSetup<&Game_Interpreter::CommandShowPicture, 14>(com); case Cmd::MovePicture: - return CommandMovePicture(com); + return CmdSetup<&Game_Interpreter::CommandMovePicture, 16>(com); case Cmd::ErasePicture: - return CommandErasePicture(com); + return CmdSetup<&Game_Interpreter::CommandErasePicture, 1>(com); case Cmd::PlayerVisibility: - return CommandPlayerVisibility(com); + return CmdSetup<&Game_Interpreter::CommandPlayerVisibility, 1>(com); case Cmd::MoveEvent: - return CommandMoveEvent(com); + return CmdSetup<&Game_Interpreter::CommandMoveEvent, 4>(com); case Cmd::MemorizeBGM: - return CommandMemorizeBGM(com); + return CmdSetup<&Game_Interpreter::CommandMemorizeBGM, 0>(com); case Cmd::PlayMemorizedBGM: - return CommandPlayMemorizedBGM(com); + return CmdSetup<&Game_Interpreter::CommandPlayMemorizedBGM, 0>(com); case Cmd::KeyInputProc: - return CommandKeyInputProc(com); + return CmdSetup<&Game_Interpreter::CommandKeyInputProc, 5>(com); case Cmd::ChangeMapTileset: - return CommandChangeMapTileset(com); + return CmdSetup<&Game_Interpreter::CommandChangeMapTileset, 1>(com); case Cmd::ChangePBG: - return CommandChangePBG(com); + return CmdSetup<&Game_Interpreter::CommandChangePBG, 6>(com); case Cmd::ChangeEncounterSteps: - return CommandChangeEncounterSteps(com); + return CmdSetup<&Game_Interpreter::CommandChangeEncounterSteps, 1>(com); case Cmd::TileSubstitution: - return CommandTileSubstitution(com); + return CmdSetup<&Game_Interpreter::CommandTileSubstitution, 3>(com); case Cmd::TeleportTargets: - return CommandTeleportTargets(com); + return CmdSetup<&Game_Interpreter::CommandTeleportTargets, 6>(com); case Cmd::ChangeTeleportAccess: - return CommandChangeTeleportAccess(com); + return CmdSetup<&Game_Interpreter::CommandChangeTeleportAccess, 1>(com); case Cmd::EscapeTarget: - return CommandEscapeTarget(com); + return CmdSetup<&Game_Interpreter::CommandEscapeTarget, 5>(com); case Cmd::ChangeEscapeAccess: - return CommandChangeEscapeAccess(com); + return CmdSetup<&Game_Interpreter::CommandChangeEscapeAccess, 1>(com); case Cmd::ChangeSaveAccess: - return CommandChangeSaveAccess(com); + return CmdSetup<&Game_Interpreter::CommandChangeSaveAccess, 1>(com); case Cmd::ChangeMainMenuAccess: - return CommandChangeMainMenuAccess(com); + return CmdSetup<&Game_Interpreter::CommandChangeMainMenuAccess, 1>(com); case Cmd::ConditionalBranch: - return CommandConditionalBranch(com); + return CmdSetup<&Game_Interpreter::CommandConditionalBranch, 6>(com); case Cmd::Label: return true; case Cmd::JumpToLabel: - return CommandJumpToLabel(com); + return CmdSetup<&Game_Interpreter::CommandJumpToLabel, 1>(com); case Cmd::Loop: - return CommandLoop(com); + return CmdSetup<&Game_Interpreter::CommandLoop, 0>(com); case Cmd::BreakLoop: - return CommandBreakLoop(com); + return CmdSetup<&Game_Interpreter::CommandBreakLoop, 0>(com); case Cmd::EndLoop: - return CommandEndLoop(com); + return CmdSetup<&Game_Interpreter::CommandEndLoop, 0>(com); case Cmd::EraseEvent: - return CommandEraseEvent(com); + return CmdSetup<&Game_Interpreter::CommandEraseEvent, 0>(com); case Cmd::CallEvent: - return CommandCallEvent(com); + return CmdSetup<&Game_Interpreter::CommandCallEvent, 3>(com); case Cmd::ReturntoTitleScreen: - return CommandReturnToTitleScreen(com); + return CmdSetup<&Game_Interpreter::CommandReturnToTitleScreen, 0>(com); case Cmd::ChangeClass: - return CommandChangeClass(com); + return CmdSetup<&Game_Interpreter::CommandChangeClass, 7>(com); case Cmd::ChangeBattleCommands: - return CommandChangeBattleCommands(com); + return CmdSetup<&Game_Interpreter::CommandChangeBattleCommands, 4>(com); case Cmd::ElseBranch: - return CommandElseBranch(com); + return CmdSetup<&Game_Interpreter::CommandElseBranch, 0>(com); case Cmd::EndBranch: - return CommandEndBranch(com); + return CmdSetup<&Game_Interpreter::CommandEndBranch, 0>(com); case Cmd::ExitGame: - return CommandExitGame(com); + return CmdSetup<&Game_Interpreter::CommandExitGame, 0>(com); case Cmd::ToggleFullscreen: - return CommandToggleFullscreen(com); + return CmdSetup<&Game_Interpreter::CommandToggleFullscreen, 0>(com); case Cmd::OpenVideoOptions: - return CommandOpenVideoOptions(com); + return CmdSetup<&Game_Interpreter::CommandOpenVideoOptions, 0>(com); case Cmd::Maniac_GetSaveInfo: - return CommandManiacGetSaveInfo(com); + return CmdSetup<&Game_Interpreter::CommandManiacGetSaveInfo, 12>(com); case Cmd::Maniac_Load: - return CommandManiacLoad(com); + return CmdSetup<&Game_Interpreter::CommandManiacLoad, 4>(com); case Cmd::Maniac_Save: - return CommandManiacSave(com); + return CmdSetup<&Game_Interpreter::CommandManiacSave, 3>(com); case Cmd::Maniac_EndLoadProcess: - return CommandManiacEndLoadProcess(com); + return CmdSetup<&Game_Interpreter::CommandManiacEndLoadProcess, 0>(com); case Cmd::Maniac_GetMousePosition: - return CommandManiacGetMousePosition(com); + return CmdSetup<&Game_Interpreter::CommandManiacGetMousePosition, 2>(com); case Cmd::Maniac_SetMousePosition: - return CommandManiacSetMousePosition(com); + return CmdSetup<&Game_Interpreter::CommandManiacSetMousePosition, 3>(com); case Cmd::Maniac_ShowStringPicture: - return CommandManiacShowStringPicture(com); + return CmdSetup<&Game_Interpreter::CommandManiacShowStringPicture, 23>(com); case Cmd::Maniac_GetPictureInfo: - return CommandManiacGetPictureInfo(com); + return CmdSetup<&Game_Interpreter::CommandManiacGetPictureInfo, 8>(com); case Cmd::Maniac_ControlVarArray: - return CommandManiacControlVarArray(com); + return CmdSetup<&Game_Interpreter::CommandManiacControlVarArray, 5>(com); case Cmd::Maniac_KeyInputProcEx: - return CommandManiacKeyInputProcEx(com); + return CmdSetup<&Game_Interpreter::CommandManiacKeyInputProcEx, 4>(com); case Cmd::Maniac_RewriteMap: - return CommandManiacRewriteMap(com); + return CmdSetup<&Game_Interpreter::CommandManiacRewriteMap, 9>(com); case Cmd::Maniac_ControlGlobalSave: - return CommandManiacControlGlobalSave(com); + return CmdSetup<&Game_Interpreter::CommandManiacControlGlobalSave, 6>(com); case Cmd::Maniac_ChangePictureId: - return CommandManiacChangePictureId(com); + return CmdSetup<&Game_Interpreter::CommandManiacChangePictureId, 6>(com); case Cmd::Maniac_SetGameOption: - return CommandManiacSetGameOption(com); + return CmdSetup<&Game_Interpreter::CommandManiacSetGameOption, 4>(com); case Cmd::Maniac_ControlStrings: - return CommandManiacControlStrings(com); + return CmdSetup<&Game_Interpreter::CommandManiacControlStrings, 8>(com); case Cmd::Maniac_CallCommand: - return CommandManiacCallCommand(com); + return CmdSetup<&Game_Interpreter::CommandManiacCallCommand, 6>(com); case Cmd::Maniac_GetGameInfo: - return CommandManiacGetGameInfo(com); + return CmdSetup<&Game_Interpreter::CommandManiacGetGameInfo, 8>(com); case Cmd::EasyRpg_SetInterpreterFlag: - return CommandEasyRpgSetInterpreterFlag(com); + return CmdSetup<&Game_Interpreter::CommandEasyRpgSetInterpreterFlag, 2>(com); case Cmd::EasyRpg_ProcessJson: - return CommandEasyRpgProcessJson(com); + return CmdSetup<&Game_Interpreter::CommandEasyRpgProcessJson, 8>(com); case Cmd::EasyRpg_CloneMapEvent: - return CommandEasyRpgCloneMapEvent(com); + return CmdSetup<&Game_Interpreter::CommandEasyRpgCloneMapEvent, 10>(com); case Cmd::EasyRpg_DestroyMapEvent: - return CommandEasyRpgDestroyMapEvent(com); + return CmdSetup<&Game_Interpreter::CommandEasyRpgDestroyMapEvent, 2>(com); default: return true; } @@ -1001,7 +1001,6 @@ bool Game_Interpreter::CommandShowChoiceEnd(lcf::rpg::EventCommand const& /* com return true; } - bool Game_Interpreter::CommandInputNumber(lcf::rpg::EventCommand const& com) { // code 10150 if (!Game_Message::CanShowMessage(main_flag)) { return false; @@ -2739,6 +2738,11 @@ bool Game_Interpreter::CommandShowPicture(lcf::rpg::EventCommand const& com) { / } if (param_size > 16 && (Player::IsRPG2k3ECommands() || Player::IsPatchManiac())) { + if (param_size < 30) { + // Ensure correct size + return CmdSetup<&Game_Interpreter::CommandShowPicture, 30>(com); + } + // Handling of RPG2k3 1.12 chunks if (Player::IsPatchManiac()) { pic_id = ValueOrVariableBitfield(com.parameters[17], 0, pic_id); @@ -2874,6 +2878,11 @@ bool Game_Interpreter::CommandMovePicture(lcf::rpg::EventCommand const& com) { / if (Player::IsRPG2k() || Player::IsRPG2k3E() || Player::IsPatchManiac()) { if (param_size > 17 && (Player::IsRPG2k3ECommands() || Player::IsPatchManiac())) { + if (param_size < 22) { + // Ensure correct size + return CmdSetup<&Game_Interpreter::CommandMovePicture, 22>(com); + } + // Handling of RPG2k3 1.12 chunks // Maniac Patch uses the upper bits for "wait is variable", mask it away pic_id = ValueOrVariable(ManiacBitmask(com.parameters[17], 0xFF), pic_id); @@ -2890,13 +2899,15 @@ bool Game_Interpreter::CommandMovePicture(lcf::rpg::EventCommand const& com) { / } params.magnify_width = ValueOrVariableBitfield(com.parameters[20], 0, params.magnify_width); - if (Player::IsPatchManiac() && com.parameters.size() > 18 && com.parameters[20] >= 16) { + + if (Player::IsPatchManiac() && com.parameters.size() > 20 && com.parameters[20] >= 16) { // The >= 16 check is needed because this bit is set when independent width/height scaling is used // Since version 240423, Maniacs supports width/height scaling for special effects pictures. params.magnify_height = ValueOrVariableBitfield((com.parameters[20] >> 1), 1, com.parameters[18]); } else { params.magnify_height = params.magnify_width; } + params.top_trans = ValueOrVariable(com.parameters[21], params.top_trans); } @@ -2976,7 +2987,7 @@ bool Game_Interpreter::CommandErasePicture(lcf::rpg::EventCommand const& com) { // Handling of RPG2k3 1.12 chunks int id_type = com.parameters[1]; - int pic_id_max; + int pic_id_max = 0; switch (id_type) { case 0: // Erase single picture specified by constant @@ -2987,7 +2998,9 @@ bool Game_Interpreter::CommandErasePicture(lcf::rpg::EventCommand const& com) { break; case 2: // Erase [Arg0, Arg2] - pic_id_max = com.parameters[2]; + if (com.parameters.size() > 2) { + pic_id_max = com.parameters[2]; + } break; case 3: // Erase [V[Arg0], V[Arg2]] @@ -2995,7 +3008,9 @@ bool Game_Interpreter::CommandErasePicture(lcf::rpg::EventCommand const& com) { return true; } pic_id = Main_Data::game_variables->Get(pic_id); - pic_id_max = Main_Data::game_variables->Get(com.parameters[2]); + if (com.parameters.size() > 2) { + pic_id_max = Main_Data::game_variables->Get(com.parameters[2]); + } break; case 4: // Erase single picture referenced by variable indirect @@ -3238,11 +3253,11 @@ bool Game_Interpreter::CommandKeyInputProc(lcf::rpg::EventCommand const& com) { } else { // Since RPG2k3 1.05 // Support for RPG2k >=1.50 games imported into RPG2k3 - _keyinput.keys[Keys::eShift] = com.parameters[5] != 0; - _keyinput.keys[Keys::eDown] = com.parameters[6] != 0; - _keyinput.keys[Keys::eLeft] = com.parameters[7] != 0; - _keyinput.keys[Keys::eRight] = com.parameters[8] != 0; - _keyinput.keys[Keys::eUp] = com.parameters[9] != 0; + _keyinput.keys[Keys::eShift] = check_key(5u); + _keyinput.keys[Keys::eDown] = check_key(6u); + _keyinput.keys[Keys::eLeft] = check_key(7u); + _keyinput.keys[Keys::eRight] = check_key(8u); + _keyinput.keys[Keys::eUp] = check_key(9u); } } @@ -4237,7 +4252,6 @@ bool Game_Interpreter::CommandManiacGetGameInfo(lcf::rpg::EventCommand const& co return true; } - bool Game_Interpreter::CommandManiacGetSaveInfo(lcf::rpg::EventCommand const& com) { if (!Player::IsPatchManiac()) { return true; @@ -5434,11 +5448,7 @@ bool Game_Interpreter::CommandEasyRpgCloneMapEvent(lcf::rpg::EventCommand const& int src_event = ValueOrVariable(com.parameters[2], com.parameters[3]); int target_x = ValueOrVariable(com.parameters[4], com.parameters[5]); int target_y = ValueOrVariable(com.parameters[6], com.parameters[7]); - - int target_event = 0; - if (com.parameters.size() >= 10) { - target_event = ValueOrVariable(com.parameters[8], com.parameters[9]); - } + int target_event = ValueOrVariable(com.parameters[8], com.parameters[9]); std::string target_name = ToString(CommandStringOrVariable(com, 10, 11)); @@ -5465,10 +5475,6 @@ bool Game_Interpreter::CommandEasyRpgDestroyMapEvent(lcf::rpg::EventCommand cons return true; } - if (com.parameters.size() < 2) { - return true; - } - int target_event = ValueOrVariable(com.parameters[0], com.parameters[1]); _async_op = AsyncOp::MakeDestroyMapEvent(target_event); diff --git a/src/game_interpreter_battle.cpp b/src/game_interpreter_battle.cpp index da2ec95d08..ba807ef747 100644 --- a/src/game_interpreter_battle.cpp +++ b/src/game_interpreter_battle.cpp @@ -222,39 +222,39 @@ int Game_Interpreter_Battle::ScheduleNextPage(lcf::rpg::TroopPageCondition::Flag bool Game_Interpreter_Battle::ExecuteCommand(lcf::rpg::EventCommand const& com) { switch (static_cast(com.code)) { case Cmd::CallCommonEvent: - return CommandCallCommonEvent(com); + return CmdSetup<&Game_Interpreter_Battle::CommandCallCommonEvent, 1>(com); case Cmd::ForceFlee: - return CommandForceFlee(com); + return CmdSetup<&Game_Interpreter_Battle::CommandForceFlee, 3>(com); case Cmd::EnableCombo: - return CommandEnableCombo(com); + return CmdSetup<&Game_Interpreter_Battle::CommandEnableCombo, 3>(com); case Cmd::ChangeMonsterHP: - return CommandChangeMonsterHP(com); + return CmdSetup<&Game_Interpreter_Battle::CommandChangeMonsterHP, 5>(com); case Cmd::ChangeMonsterMP: - return CommandChangeMonsterMP(com); + return CmdSetup<&Game_Interpreter_Battle::CommandChangeMonsterMP, 4>(com); case Cmd::ChangeMonsterCondition: - return CommandChangeMonsterCondition(com); + return CmdSetup<&Game_Interpreter_Battle::CommandChangeMonsterCondition, 3>(com); case Cmd::ShowHiddenMonster: - return CommandShowHiddenMonster(com); + return CmdSetup<&Game_Interpreter_Battle::CommandShowHiddenMonster, 1>(com); case Cmd::ChangeBattleBG: - return CommandChangeBattleBG(com); + return CmdSetup<&Game_Interpreter_Battle::CommandChangeBattleBG, 0>(com); case Cmd::ShowBattleAnimation_B: - return CommandShowBattleAnimation(com); + return CmdSetup<&Game_Interpreter_Battle::CommandShowBattleAnimation, 3>(com); case Cmd::TerminateBattle: - return CommandTerminateBattle(com); + return CmdSetup<&Game_Interpreter_Battle::CommandTerminateBattle, 0>(com); case Cmd::ConditionalBranch_B: - return CommandConditionalBranchBattle(com); + return CmdSetup<&Game_Interpreter_Battle::CommandConditionalBranchBattle, 5>(com); case Cmd::ElseBranch_B: - return CommandElseBranchBattle(com); + return CmdSetup<&Game_Interpreter_Battle::CommandElseBranchBattle, 0>(com); case Cmd::EndBranch_B: - return CommandEndBranchBattle(com); + return CmdSetup<&Game_Interpreter_Battle::CommandEndBranchBattle, 0>(com); case Cmd::Maniac_ControlBattle: - return CommandManiacControlBattle(com); + return CmdSetup<&Game_Interpreter_Battle::CommandManiacControlBattle, 4>(com); case Cmd::Maniac_ControlAtbGauge: - return CommandManiacControlAtbGauge(com); + return CmdSetup<&Game_Interpreter_Battle::CommandManiacControlAtbGauge, 7>(com); case Cmd::Maniac_ChangeBattleCommandEx: - return CommandManiacChangeBattleCommandEx(com); + return CmdSetup<&Game_Interpreter_Battle::CommandManiacChangeBattleCommandEx, 2>(com); case Cmd::Maniac_GetBattleInfo: - return CommandManiacGetBattleInfo(com); + return CmdSetup<&Game_Interpreter_Battle::CommandManiacGetBattleInfo, 5>(com); default: return Game_Interpreter::ExecuteCommand(com); } @@ -470,7 +470,7 @@ bool Game_Interpreter_Battle::CommandShowBattleAnimation(lcf::rpg::EventCommand bool waiting_battle_anim = com.parameters[2] != 0; bool allies = false; - if (Player::IsRPG2k3()) { + if (Player::IsRPG2k3() && com.parameters.size() > 3) { allies = com.parameters[3] != 0; } diff --git a/src/game_interpreter_map.cpp b/src/game_interpreter_map.cpp index c0445e9402..6a25531866 100644 --- a/src/game_interpreter_map.cpp +++ b/src/game_interpreter_map.cpp @@ -184,63 +184,67 @@ bool Game_Interpreter_Map::RequestMainMenuScene(int subscreen_id, int actor_inde bool Game_Interpreter_Map::ExecuteCommand(lcf::rpg::EventCommand const& com) { switch (static_cast(com.code)) { case Cmd::RecallToLocation: - return CommandRecallToLocation(com); + return CmdSetup<&Game_Interpreter_Map::CommandRecallToLocation, 3>(com); case Cmd::EnemyEncounter: - return CommandEnemyEncounter(com); + if (Player::IsRPG2k()) { + return CmdSetup<&Game_Interpreter_Map::CommandEnemyEncounter, 6>(com); + } else { + return CmdSetup<&Game_Interpreter_Map::CommandEnemyEncounter, 10>(com); + } case Cmd::VictoryHandler: - return CommandVictoryHandler(com); + return CmdSetup<&Game_Interpreter_Map::CommandVictoryHandler, 0>(com); case Cmd::EscapeHandler: - return CommandEscapeHandler(com); + return CmdSetup<&Game_Interpreter_Map::CommandEscapeHandler, 0>(com); case Cmd::DefeatHandler: - return CommandDefeatHandler(com); + return CmdSetup<&Game_Interpreter_Map::CommandDefeatHandler, 0>(com); case Cmd::EndBattle: - return CommandEndBattle(com); + return CmdSetup<&Game_Interpreter_Map::CommandEndBattle, 0>(com); case Cmd::OpenShop: - return CommandOpenShop(com); + return CmdSetup<&Game_Interpreter_Map::CommandOpenShop, 4>(com); case Cmd::Transaction: - return CommandTransaction(com); + return CmdSetup<&Game_Interpreter_Map::CommandTransaction, 0>(com); case Cmd::NoTransaction: - return CommandNoTransaction(com); + return CmdSetup<&Game_Interpreter_Map::CommandNoTransaction, 0>(com); case Cmd::EndShop: - return CommandEndShop(com); + return CmdSetup<&Game_Interpreter_Map::CommandEndShop, 0>(com); case Cmd::ShowInn: - return CommandShowInn(com); + return CmdSetup<&Game_Interpreter_Map::CommandShowInn, 3>(com); case Cmd::Stay: - return CommandStay(com); + return CmdSetup<&Game_Interpreter_Map::CommandStay, 0>(com); case Cmd::NoStay: - return CommandNoStay(com); + return CmdSetup<&Game_Interpreter_Map::CommandNoStay, 0>(com); case Cmd::EndInn: - return CommandEndInn(com); + return CmdSetup<&Game_Interpreter_Map::CommandEndInn, 0>(com); case Cmd::EnterHeroName: - return CommandEnterHeroName(com); + return CmdSetup<&Game_Interpreter_Map::CommandEnterHeroName, 3>(com); case Cmd::Teleport: - return CommandTeleport(com); + return CmdSetup<&Game_Interpreter_Map::CommandTeleport, 3>(com); case Cmd::EnterExitVehicle: - return CommandEnterExitVehicle(com); + return CmdSetup<&Game_Interpreter_Map::CommandEnterExitVehicle, 0>(com); case Cmd::PanScreen: - return CommandPanScreen(com); + return CmdSetup<&Game_Interpreter_Map::CommandPanScreen, 5>(com); case Cmd::ShowBattleAnimation: - return CommandShowBattleAnimation(com); + return CmdSetup<&Game_Interpreter_Map::CommandShowBattleAnimation, 4>(com); case Cmd::FlashSprite: - return CommandFlashSprite(com); + return CmdSetup<&Game_Interpreter_Map::CommandFlashSprite, 7>(com); case Cmd::ProceedWithMovement: - return CommandProceedWithMovement(com); + return CmdSetup<&Game_Interpreter_Map::CommandProceedWithMovement, 0>(com); case Cmd::HaltAllMovement: - return CommandHaltAllMovement(com); + return CmdSetup<&Game_Interpreter_Map::CommandHaltAllMovement, 0>(com); case Cmd::PlayMovie: - return CommandPlayMovie(com); + return CmdSetup<&Game_Interpreter_Map::CommandPlayMovie, 5>(com); case Cmd::OpenSaveMenu: - return CommandOpenSaveMenu(com); + return CmdSetup<&Game_Interpreter_Map::CommandOpenSaveMenu, 0>(com); case Cmd::OpenMainMenu: - return CommandOpenMainMenu(com); + return CmdSetup<&Game_Interpreter_Map::CommandOpenMainMenu, 0>(com); case Cmd::OpenLoadMenu: - return CommandOpenLoadMenu(com); + return CmdSetup<&Game_Interpreter_Map::CommandOpenLoadMenu, 0>(com); case Cmd::ToggleAtbMode: - return CommandToggleAtbMode(com); + return CmdSetup<&Game_Interpreter_Map::CommandToggleAtbMode, 0>(com); case Cmd::EasyRpg_TriggerEventAt: - return CommandEasyRpgTriggerEventAt(com); + return CmdSetup<&Game_Interpreter_Map::CommandEasyRpgTriggerEventAt, 4>(com); case Cmd::EasyRpg_WaitForSingleMovement: - return CommandEasyRpgWaitForSingleMovement(com); + return CmdSetup<&Game_Interpreter_Map::CommandEasyRpgWaitForSingleMovement, 6>(com); default: return Game_Interpreter::ExecuteCommand(com); } @@ -771,7 +775,10 @@ bool Game_Interpreter_Map::CommandOpenSaveMenu(lcf::rpg::EventCommand const& com Scene::instance->SetRequestedScene(std::make_shared()); - const int current_system_function = com.parameters[0]; + int current_system_function = 0; + if (com.parameters.size() > 0) { + current_system_function = com.parameters[0]; + } // Handle save menu (default behavior) if (!Player::IsPatchManiac() || current_system_function <= 0) { @@ -783,15 +790,15 @@ bool Game_Interpreter_Map::CommandOpenSaveMenu(lcf::rpg::EventCommand const& com // Command "Call System Functions" switch (current_system_function) { case 1: // Load menu - return CommandOpenLoadMenu(com); + return CmdSetup<&Game_Interpreter_Map::CommandOpenLoadMenu, 100>(com); case 2: // Game menu - return CommandOpenMainMenu(com); + return CmdSetup<&Game_Interpreter_Map::CommandOpenMainMenu, 100>(com); case 3: // Toggle fullscreen // TODO Implement fullscreen mode once maniacs supports it // const int fullscreen_mode = com.parameters[1]; // Broken in Maniac. - return CommandToggleFullscreen(com); + return CmdSetup<&Game_Interpreter_Map::CommandToggleFullscreen, 100>(com); case 4: // Settings menu - return CommandOpenVideoOptions(com); + return CmdSetup<&Game_Interpreter_Map::CommandOpenVideoOptions, 100>(com); case 5: // Debug menu // const int pause_while_debugging = com.parameters[1]; // unused in our ingame debug screen. Scene::instance->SetRequestedScene(std::make_shared()); @@ -801,7 +808,7 @@ bool Game_Interpreter_Map::CommandOpenSaveMenu(lcf::rpg::EventCommand const& com // TODO Implement license information menu return true; case 7: // Reset game - return CommandReturnToTitleScreen(com); + return CmdSetup<&Game_Interpreter_Map::CommandReturnToTitleScreen, 100>(com); default: if (Player::HasEasyRpgExtensions() && current_system_function >= 200 && current_system_function < 210) { const int actor_index = ValueOrVariable(com.parameters[1], com.parameters[2]); @@ -897,14 +904,8 @@ bool Game_Interpreter_Map::CommandEasyRpgWaitForSingleMovement(lcf::rpg::EventCo if (!_state.easyrpg_active) { event_id = ValueOrVariable(com.parameters[0], com.parameters[1]); - - if (com.parameters.size() >= 4) { - failure_limit = ValueOrVariable(com.parameters[2], com.parameters[3]); - } - - if (com.parameters.size() >= 6) { - output_var = ValueOrVariable(com.parameters[4], com.parameters[5]); - } + failure_limit = ValueOrVariable(com.parameters[2], com.parameters[3]); + output_var = ValueOrVariable(com.parameters[4], com.parameters[5]); } _state.easyrpg_active = false; diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index 8afc2f52d8..99e3cb3d59 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -176,13 +176,17 @@ class Game_BaseInterpreterContext { bool CmdSetup(lcf::rpg::EventCommand const& com) { using ClassType = decltype(MemFnCls(CMDFN)); - if (EP_UNLIKELY(com.parameters.size() < MIN_SIZE)) { - // Slow resizing of the parameters - // Only happens for malformed commands - auto ncom = com; - ncom.parameters = lcf::DBArray(MIN_SIZE); - std::copy(com.parameters.begin(), com.parameters.end(), ncom.parameters.begin()); - return (static_cast(this)->*CMDFN)(ncom); + static_assert(std::is_base_of::value, "ClassType must inherit from Game_BaseInterpreterContext"); + + if constexpr (MIN_SIZE > 0) { + if (EP_UNLIKELY(com.parameters.size() < MIN_SIZE)) { + // Slow resizing of the parameters + // Only happens for malformed commands + auto ncom = com; + ncom.parameters = lcf::DBArray(MIN_SIZE); + std::copy(com.parameters.begin(), com.parameters.end(), ncom.parameters.begin()); + return (static_cast(this)->*CMDFN)(ncom); + } } return (static_cast(this)->*CMDFN)(com);