From dceffd3d0d6949a46d763baf7eab066f19ab4b09 Mon Sep 17 00:00:00 2001 From: PeachyPeach <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Thu, 12 Feb 2026 00:41:28 +0100 Subject: [PATCH] Validate DynOS behaviors (#1115) --- data/dynos_bin_behavior.cpp | 54 +++++++++++++++++++++++++++++++++++- src/engine/behavior_script.c | 2 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/data/dynos_bin_behavior.cpp b/data/dynos_bin_behavior.cpp index c6150025f..805a31ac8 100644 --- a/data/dynos_bin_behavior.cpp +++ b/data/dynos_bin_behavior.cpp @@ -2449,6 +2449,48 @@ static void ParseBehaviorScriptSymbol(GfxData *aGfxData, DataNode &aCommands) { + u8 bhvCommand = (*aBhv >> 24) & 0xFF; + for (const auto &commandToCheck : aCommands) { + if (bhvCommand == ((commandToCheck >> 24) & 0xFF)) { + return true; + } + } + return false; +} + +static bool DynOS_Bhv_Validate(GfxData *aGfxData, const DataNode *aNode) { + + // 1st command must be BEGIN + if (!DynOS_Bhv_CheckCommands(aNode->mData + 0, { BEGIN(0) })) { + PrintDataError(" ERROR: Validation failed for behavior %s: First command of the script must be BEGIN.", aNode->mName.begin()); + return false; + } + + // 2nd command must be ID + if (!DynOS_Bhv_CheckCommands(aNode->mData + 1, { ID(0) })) { + PrintDataError(" ERROR: Validation failed for behavior %s: Second command of the script must be ID.", aNode->mName.begin()); + return false; + } + + // Last command must be a terminating command + if (!DynOS_Bhv_CheckCommands(aNode->mData + aNode->mSize - 1, { + CALL(0), + RETURN(), + GOTO(0), + END_LOOP(), + BREAK(), + DEACTIVATE(), + CALL_EXT(0), + GOTO_EXT(0), + })) { + PrintDataError(" ERROR: Validation failed for behavior %s: Last command of the script must be one of:\n CALL, RETURN, GOTO, END_LOOP, BREAK, DEACTIVATE", aNode->mName.begin()); + return false; + } + + return true; +} + DataNode *DynOS_Bhv_Parse(GfxData *aGfxData, DataNode *aNode, bool aDisplayPercent) { if (aNode->mData) return aNode; @@ -2460,9 +2502,13 @@ DataNode *DynOS_Bhv_Parse(GfxData *aGfxData, DataNodemErrorCount == 0) { PrintNoNewLine("%3d%%\b\b\b\b", (s32) (_TokenIndex * 100) / aNode->mTokens.Count()); } } - if (aDisplayPercent && aGfxData->mErrorCount == 0) { Print("100%%"); } aNode->mSize = (u32)(_Head - aNode->mData); aNode->mLoadIndex = aGfxData->mLoadIndex++; + + // Validate behavior script + DynOS_Bhv_Validate(aGfxData, aNode); + + if (aDisplayPercent && aGfxData->mErrorCount == 0) { Print("100%%"); } return aNode; } @@ -2592,6 +2638,12 @@ static DataNode *DynOS_Bhv_Load(BinFile *aFile, GfxData *aGfxDat } } + // Validate it + if (!DynOS_Bhv_Validate(aGfxData, _Node)) { + Delete(_Node); + return NULL; + } + // Add it if (aGfxData != NULL) { aGfxData->mBehaviorScripts.Add(_Node); diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c index 52aedaba1..bd6218775 100644 --- a/src/engine/behavior_script.c +++ b/src/engine/behavior_script.c @@ -988,7 +988,7 @@ static s32 bhv_cmd_call_native_ext(void) { const char *funcStr = dynos_behavior_get_token(behavior, BHV_CMD_GET_U32(1)); if (!funcStr) { - LOG_LUA("Could not retrieve function name from behavior command. Do you have an unclosed behavior script?"); + LOG_LUA("Could not retrieve function name from behavior command."); gCurBhvCommand += 2; return BHV_PROC_CONTINUE; }