#include "dynos.cpp.h" extern "C" { #include "game/segment2.h" #include "game/save_file.h" #include "levels/scripts.h" #include "pc/lua/utils/smlua_level_utils.h" #ifdef VERSION_EU #include "eu_translation.h" #endif } // // Const // extern "C" { extern const BehaviorScript *sWarpBhvSpawnTable[]; #include "engine/level_script.h" } #define DYNOS_LEVEL_TEXT_EMPTY "" #define DYNOS_LEVEL_TEXT_CASTLE "CASTLE" #define DYNOS_LEVEL_TEXT_BOWSER_1 "BOWSER 1" #define DYNOS_LEVEL_TEXT_BOWSER_2 "BOWSER 2" #define DYNOS_LEVEL_TEXT_BOWSER_3 "BOWSER 3" #define DYNOS_LEVEL_TEXT_100_COINS_STAR "100 COINS STAR" #define DYNOS_LEVEL_TEXT_RED_COINS_STAR "RED COINS STAR" #define DYNOS_LEVEL_TEXT_ONE_SECRET_STAR "ONE OF THE CASTLE'S SECRET STARS!" static void SetConvertedTextToBuffer(u8 *aBuffer, const char *aText) { u8 *_ConvertedText = DynOS_String_Convert(aText, false); memcpy(aBuffer, _ConvertedText, DynOS_String_Length(_ConvertedText) + 1); } // // Data // struct DynosWarp { /* 0 */ s16 mArea = 0; /* 1 */ s16 mId = 0; /* 2 */ s16 mType = -1; /* 3 */ s16 mPosX = 0; /* 4 */ s16 mPosY = 0; /* 5 */ s16 mPosZ = 0; /* 6 */ s16 mAngle = 0; /* 7 */ s16 mDestLevel = 0; /* 8 */ s16 mDestArea = 0; /* 9 */ s16 mDestId = 0; }; struct DynosLevelScript { void *mLevelScript; s32 mModIndex; }; #define DYNOS_LEVEL_MOD_INDEX_VANILLA (-1) static DynosLevelScript sDynosLevelScripts[LEVEL_COUNT] = { { NULL, DYNOS_LEVEL_MOD_INDEX_VANILLA } }; static void *sDynosLevelScriptsOriginal[LEVEL_COUNT] = { NULL }; static Array sDynosLevelWarps[LEVEL_COUNT] = { Array() }; static Array sDynosLevelList = Array(); // Ordered by Course Id, COURSE_NONE excluded u64 DynOS_Level_CmdGet(void *aCmd, u64 aOffset) { u64 _Offset = (((aOffset) & 3llu) | (((aOffset) & ~3llu) << (sizeof(void *) >> 3llu))); return *((u64 *) (u64(aCmd) + _Offset)); } void *DynOS_Level_CmdNext(void *aCmd, u64 aCmdSize) { u64 _Offset = (((aCmdSize) & 3llu) | (((aCmdSize) & ~3llu) << (sizeof(void *) >> 3llu))); return (void *) (u64(aCmd) + _Offset); } void DynOS_Level_ParseScript(const void *aScript, s32 (*aPreprocessFunction)(u8, void *)); // // Init // static s32 DynOS_Level_PreprocessMasterScript(u8 aType, void *aCmd) { static bool sDynosScriptExecLevelTable = false; static s32 sDynosLevelNum = -1; if (!sDynosScriptExecLevelTable) { // JUMP_LINK if (aType == 0x06) { sDynosScriptExecLevelTable = true; return 0; } } else { // JUMP_IF if (aType == 0x0C) { sDynosLevelNum = (s32) DynOS_Level_CmdGet(aCmd, 0x04); return 0; } // EXECUTE if (aType == 0x00) { void *_Script = (void *) DynOS_Level_CmdGet(aCmd, 0x0C); if (sDynosLevelNum >= 0 && sDynosLevelNum < LEVEL_COUNT && !sDynosLevelScripts[sDynosLevelNum].mLevelScript) { sDynosLevelScripts[sDynosLevelNum].mLevelScript = _Script; sDynosLevelScripts[sDynosLevelNum].mModIndex = DYNOS_LEVEL_MOD_INDEX_VANILLA; sDynosLevelScriptsOriginal[sDynosLevelNum] = _Script; } sDynosLevelNum = -1; return 2; } // EXIT if (aType == 0x02) { return 3; } // SLEEP if (aType == 0x03) { return 3; } } return 0; } static s32 sDynosCurrentLevelNum; static s32 DynOS_Level_PreprocessScript(u8 aType, void *aCmd) { static u8 sDynosAreaIndex = 0; static auto _GetWarpStruct = [](u8 aArea, u8 aId) -> DynosWarp * { for (s32 i = 0; i != sDynosLevelWarps[sDynosCurrentLevelNum].Count(); ++i) { if (sDynosLevelWarps[sDynosCurrentLevelNum][i].mArea == aArea && sDynosLevelWarps[sDynosCurrentLevelNum][i].mId == aId) { return &sDynosLevelWarps[sDynosCurrentLevelNum][i]; } } DynosWarp _Warp; _Warp.mArea = aArea; _Warp.mId = aId; sDynosLevelWarps[sDynosCurrentLevelNum].Add(_Warp); return &sDynosLevelWarps[sDynosCurrentLevelNum][sDynosLevelWarps[sDynosCurrentLevelNum].Count() - 1]; }; // AREA if (aType == 0x1F) { sDynosAreaIndex = (u8) DynOS_Level_CmdGet(aCmd, 2); } // OBJECT else if (aType == 0x24) { const BehaviorScript *bhv = (const BehaviorScript *) DynOS_Level_CmdGet(aCmd, 20); for (s32 i = 0; i < 20; ++i) { if (sWarpBhvSpawnTable[i] == bhv) { DynosWarp *_Warp = _GetWarpStruct(sDynosAreaIndex, ((((u32) DynOS_Level_CmdGet(aCmd, 16)) >> 16) & 0xFF)); if (_Warp->mType == -1) { _Warp->mType = i; _Warp->mPosX = (s16) DynOS_Level_CmdGet(aCmd, 4); _Warp->mPosY = (s16) DynOS_Level_CmdGet(aCmd, 6); _Warp->mPosZ = (s16) DynOS_Level_CmdGet(aCmd, 8); _Warp->mAngle = (s16)((((s32)((s16) DynOS_Level_CmdGet(aCmd, 12))) * 0x8000) / 180); } break; } } } // WARP_NODE else if (aType == 0x26) { DynosWarp *_Warp = _GetWarpStruct(sDynosAreaIndex, (u8) DynOS_Level_CmdGet(aCmd, 2)); if (_Warp->mDestLevel == 0) { _Warp->mDestLevel = (u8) DynOS_Level_CmdGet(aCmd, 3); _Warp->mDestArea = (u8) DynOS_Level_CmdGet(aCmd, 4); _Warp->mDestId = (u8) DynOS_Level_CmdGet(aCmd, 5); } } // PAINTING_WARP_NODE else if (aType == 0x27) { DynosWarp *_Warp = _GetWarpStruct(sDynosAreaIndex, (u8) DynOS_Level_CmdGet(aCmd, 2)); if (_Warp->mDestLevel == 0) { _Warp->mDestLevel = (u8) DynOS_Level_CmdGet(aCmd, 3); _Warp->mDestArea = (u8) DynOS_Level_CmdGet(aCmd, 4); _Warp->mDestId = (u8) DynOS_Level_CmdGet(aCmd, 5); } } // SLEEP // SLEEP_BEFORE_EXIT else if (aType == 0x03 || aType == 0x04) { return 3; } return 0; } // Runs only once static void DynOS_Level_Init() { static bool sInited = false; if (!sInited) { // Level scripts DynOS_Level_ParseScript(level_main_scripts_entry, DynOS_Level_PreprocessMasterScript); // Level warps for (sDynosCurrentLevelNum = 0; sDynosCurrentLevelNum != LEVEL_COUNT; ++sDynosCurrentLevelNum) { if (sDynosLevelScripts[sDynosCurrentLevelNum].mLevelScript) { DynOS_Level_ParseScript(sDynosLevelScripts[sDynosCurrentLevelNum].mLevelScript, DynOS_Level_PreprocessScript); } } // Level list ordered by course id for (s32 i = COURSE_MIN; i <= COURSE_MAX; ++i) { if (i == COURSE_CAKE_END) continue; for (s32 j = 1; j != LEVEL_COUNT; ++j) { if (get_level_course_num(j - 1) == i) { sDynosLevelList.Add(j); } } } // Done sInited = true; } } // // Common // s32 DynOS_Level_GetCount() { DynOS_Level_Init(); return sDynosLevelList.Count(); } const s32 *DynOS_Level_GetList() { DynOS_Level_Init(); return sDynosLevelList.begin(); } s32 DynOS_Level_GetCourse(s32 aLevel) { return (s32) gLevelToCourseNumTable[aLevel - 1]; } void DynOS_Level_Override(void* originalScript, void* newScript, s32 modIndex) { for (s32 i = 0; i < LEVEL_COUNT; i++) { if (sDynosLevelScripts[i].mLevelScript == originalScript) { sDynosCurrentLevelNum = i; sDynosLevelWarps[i].Clear(); DynOS_Level_ParseScript(newScript, DynOS_Level_PreprocessScript); sDynosLevelScripts[i].mLevelScript = newScript; sDynosLevelScripts[i].mModIndex = modIndex; return; } } } void DynOS_Level_Unoverride() { for (s32 i = 0; i < LEVEL_COUNT; i++) { sDynosCurrentLevelNum = i; sDynosLevelWarps[i].Clear(); sDynosLevelScripts[i].mLevelScript = sDynosLevelScriptsOriginal[i]; sDynosLevelScripts[i].mModIndex = DYNOS_LEVEL_MOD_INDEX_VANILLA; DynOS_Level_ParseScript(sDynosLevelScripts[i].mLevelScript, DynOS_Level_PreprocessScript); } } const void *DynOS_Level_GetScript(s32 aLevel) { if (aLevel >= CUSTOM_LEVEL_NUM_START) { struct CustomLevelInfo* info = smlua_level_util_get_info(aLevel); if (!info || !info->script) { return NULL; } return info->script; } DynOS_Level_Init(); return sDynosLevelScripts[aLevel].mLevelScript; } s32 DynOS_Level_GetModIndex(s32 aLevel) { if (aLevel >= CUSTOM_LEVEL_NUM_START) { struct CustomLevelInfo* info = smlua_level_util_get_info(aLevel); if (!info || !info->script) { return DYNOS_LEVEL_MOD_INDEX_VANILLA; } return info->modIndex; } DynOS_Level_Init(); return sDynosLevelScripts[aLevel].mModIndex; } bool DynOS_Level_IsVanillaLevel(s32 aLevel) { if (aLevel >= 0 && aLevel < LEVEL_COUNT) { return sDynosLevelScripts[aLevel].mLevelScript == sDynosLevelScriptsOriginal[aLevel]; } return false; } // // Course name // const u8 *DynOS_Level_GetName(s32 aLevel, bool aDecaps, bool aAddCourseNumber) { DynOS_Level_Init(); static u8 sBuffer[256]; memset(sBuffer, 0xFF, 256); s32 _Course = DynOS_Level_GetCourse(aLevel); // Level name if (aLevel == LEVEL_BOWSER_1) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_BOWSER_1); } else if (aLevel == LEVEL_BOWSER_2) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_BOWSER_2); } else if (aLevel == LEVEL_BOWSER_3) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_BOWSER_3); } else if (_Course < COURSE_BOB) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_CASTLE); } else if (_Course >= COURSE_CAKE_END) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_CASTLE); } else { #ifdef VERSION_EU const u8 *_CourseName = ((const u8 **) course_name_table_eu_en)[_Course - COURSE_BOB] + 3; #else const u8 *_CourseName = ((const u8 **) seg2_course_name_table)[_Course - COURSE_BOB] + 3; #endif memcpy(sBuffer, _CourseName, DynOS_String_Length(_CourseName)); } // Decaps if (aDecaps) { DynOS_String_Decapitalize(sBuffer); } // Course number if (aAddCourseNumber && (_Course >= COURSE_BOB) && (_Course <= COURSE_STAGES_MAX)) { memmove(sBuffer + 5, sBuffer, DynOS_String_Length(sBuffer)); sBuffer[0] = ((_Course / 10) == 0 ? 158 : (_Course / 10)); sBuffer[1] = (_Course % 10); sBuffer[2] = 158; sBuffer[3] = 159; sBuffer[4] = 158; } return sBuffer; } // // Act/Star name // const u8 *DynOS_Level_GetActName(s32 aLevel, s32 aAct, bool aDecaps, bool aAddStarNumber) { DynOS_Level_Init(); static u8 sBuffer[256]; memset(sBuffer, 0xFF, 256); s32 _Course = DynOS_Level_GetCourse(aLevel); // Star name if (_Course < COURSE_BOB) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_ONE_SECRET_STAR); } else if (aLevel == LEVEL_BITDW) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_RED_COINS_STAR); } else if (aLevel == LEVEL_BITFS) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_RED_COINS_STAR); } else if (aLevel == LEVEL_BITS) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_RED_COINS_STAR); } else if (_Course > COURSE_STAGES_MAX) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_EMPTY); } else if (aAct >= 7) { SetConvertedTextToBuffer(sBuffer, DYNOS_LEVEL_TEXT_100_COINS_STAR); } else { #ifdef VERSION_EU const u8 *_ActName = ((const u8 **) act_name_table_eu_en)[(_Course - COURSE_BOB) * 6 + (aAct - 1)]; #else const u8 *_ActName = ((const u8 **) seg2_act_name_table)[(_Course - COURSE_BOB) * 6 + (aAct - 1)]; #endif memcpy(sBuffer, _ActName, DynOS_String_Length(_ActName)); } // Decaps if (aDecaps) { DynOS_String_Decapitalize(sBuffer); } // Star number if (aAddStarNumber && (_Course >= COURSE_BOB) && (_Course <= COURSE_STAGES_MAX)) { memmove(sBuffer + 5, sBuffer, DynOS_String_Length(sBuffer)); sBuffer[0] = ((aAct / 10) == 0 ? 158 : (aAct / 10)); sBuffer[1] = (aAct % 10); sBuffer[2] = 158; sBuffer[3] = 159; sBuffer[4] = 158; } return sBuffer; } const u8 *DynOS_Level_GetAreaName(s32 aLevel, s32 aArea, bool aDecaps) { DynOS_Level_Init(); static const char *sAreaNamesPerLevel[][4] = { { "", "", "", "" }, { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* BoB */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* WF */ { "MAIN AREA", "SUNKEN SHIP", "NOT AVAILABLE", "NOT AVAILABLE" }, /* JRB */ { "MAIN AREA", "COTTAGE SLIDE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* CCM */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* BBH */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* HMC */ { "MAIN AREA", "VOLCANO", "NOT AVAILABLE", "NOT AVAILABLE" }, /* LLL */ { "MAIN AREA", "PYRAMID", "EYEROCK'S ROOM", "NOT AVAILABLE" }, /* SSL */ { "MAIN AREA", "DOCKS", "NOT AVAILABLE", "NOT AVAILABLE" }, /* DDD */ { "MAIN AREA", "IGLOO", "NOT AVAILABLE", "NOT AVAILABLE" }, /* SL */ { "MAIN AREA", "DOWNTOWN", "NOT AVAILABLE", "NOT AVAILABLE" }, /* WDW */ { "MAIN AREA", "SECRET SLIDE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* TTM */ { "HUGE ISLAND", "TINY ISLAND", "WIGGLER'S ROOM", "NOT AVAILABLE" }, /* THI */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* TTC */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* RR */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* BITDW */ { "BOWSER BATTLE", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Bowser 1 */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* BITFS */ { "BOWSER BATTLE", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Bowser 2 */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* BITS */ { "BOWSER BATTLE", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Bowser 3 */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* PSS */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* TOTWC */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* COTMC */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* VCUTM */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* WMOTR */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* SA */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Castle grounds */ { "FIRST FLOOR", "SECOND FLOOR", "BASEMENT", "NOT AVAILABLE" }, /* Castle inside */ { "MAIN AREA", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Castle courtyard */ { "ENDING", "NOT AVAILABLE", "NOT AVAILABLE", "NOT AVAILABLE" }, /* Ending */ }; static u8 sBuffer[256]; memset(sBuffer, 0xFF, 256); // Area name switch (aLevel) { case LEVEL_BOB: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[1][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_WF: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[2][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_JRB: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[3][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_CCM: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[4][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_BBH: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[5][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_HMC: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[6][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_LLL: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[7][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_SSL: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[8][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_DDD: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[9][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_SL: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[10][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_WDW: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[11][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_TTM: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[12][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_THI: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[13][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_TTC: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[14][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_RR: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[15][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_BITDW: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[16][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_BOWSER_1: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[17][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_BITFS: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[18][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_BOWSER_2: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[19][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_BITS: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[20][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_BOWSER_3: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[21][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_PSS: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[22][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_TOTWC: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[23][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_COTMC: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[24][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_VCUTM: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[25][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_WMOTR: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[26][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_SA: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[27][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_CASTLE_GROUNDS: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[28][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_CASTLE: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[29][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_CASTLE_COURTYARD: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[30][MIN(MAX(aArea - 1, 0), 3)]); break; case LEVEL_ENDING: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[31][MIN(MAX(aArea - 1, 0), 3)]); break; default: SetConvertedTextToBuffer(sBuffer, sAreaNamesPerLevel[0][MIN(MAX(aArea - 1, 0), 3)]); break; } // Decaps if (aDecaps) { DynOS_String_Decapitalize(sBuffer); } return sBuffer; } // // Level Script Preprocessing // By default, // - Ifs are always true // - Skips are always false // - Loops break after the first loop // struct LvlCmd { u8 mType; u8 mSize; }; struct Stack { u64 mData[32]; s32 mBaseIndex; s32 mTopIndex; }; template static void StackPush(Stack& aStack, const T &aValue) { if (aStack.mTopIndex >= 0) { aStack.mData[aStack.mTopIndex] = u64(aValue); aStack.mTopIndex++; } } template static T StackPop(Stack& aStack) { if (aStack.mTopIndex <= 0) { return (T) 0; } aStack.mTopIndex--; return (T) aStack.mData[aStack.mTopIndex]; } static LvlCmd *DynOS_Level_CmdExecute(Stack &aStack, LvlCmd *aCmd) { StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize)); StackPush(aStack, aStack.mBaseIndex); aStack.mBaseIndex = aStack.mTopIndex; return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 12); } static LvlCmd *DynOS_Level_CmdExitAndExecute(Stack &aStack, LvlCmd *aCmd) { aStack.mTopIndex = aStack.mBaseIndex; return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 12); } static LvlCmd *DynOS_Level_CmdExit(Stack &aStack, LvlCmd *aCmd) { aStack.mTopIndex = aStack.mBaseIndex; aStack.mBaseIndex = StackPop(aStack); return StackPop(aStack); } static LvlCmd *DynOS_Level_CmdSleep(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSleepBeforeExit(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdJump(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 4); } static LvlCmd *DynOS_Level_CmdJumpLink(Stack &aStack, LvlCmd *aCmd) { StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize)); return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 4); } static LvlCmd *DynOS_Level_CmdReturn(Stack &aStack, UNUSED LvlCmd *aCmd) { return StackPop(aStack); } static LvlCmd *DynOS_Level_CmdJumpLinkPushArg(Stack &aStack, LvlCmd *aCmd) { StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize)); StackPush(aStack, DynOS_Level_CmdGet(aCmd, 2)); return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdJumpRepeat(Stack &aStack, LvlCmd *aCmd) { aStack.mTopIndex -= 2; return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoopBegin(Stack &aStack, LvlCmd *aCmd) { StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize)); StackPush(aStack, 0); return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoopUntil(Stack &aStack, LvlCmd *aCmd) { aStack.mTopIndex -= 2; return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdJumpIf(Stack &aStack, LvlCmd *aCmd) { StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize)); /* Not an error, that's intentional */ return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 8); } static LvlCmd *DynOS_Level_CmdJumpLinkIf(Stack &aStack, LvlCmd *aCmd) { StackPush(aStack, DynOS_Level_CmdNext(aCmd, aCmd->mSize)); return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 8); } static LvlCmd *DynOS_Level_CmdSkipIf(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSkip(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSkipNop(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdCall(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdCallLoop(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetRegister(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdPushPool(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdPopPool(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoadFixed(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoadRaw(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoadMIO0(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoadMarioHead(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoadMIO0Texture(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdInitLevel(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdClearLevel(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdAllocLevelPool(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdFreeLevelPool(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdBeginArea(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdEndArea(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoadModelFromDL(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoadModelFromGeo(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_Cmd23(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdMario(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdObject(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdWarpNode(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdInstantWarp(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetTerrainType(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdPaintingWarpNode(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_Cmd3A(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetWhirlpool(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetBlackout(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetGamma(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetTerrain(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetRooms(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdMacroObjects(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoadArea(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdUnloadArea(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetMarioStartPos(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_Cmd2C(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_Cmd2D(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetTransition(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdNop(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdShowDialog(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetBackgroundMusic(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdSetMenuMusic(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdStopMusic(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdGetOrSet(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdAdvanceDemo(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdClearDemoPointer(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdPlaceObjectExt(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdPlaceObjectExt2(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdLoadModelFromGeoExt(Stack &aStack, LvlCmd *aCmd) { return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } static LvlCmd *DynOS_Level_CmdJumpArea(Stack &aStack, LvlCmd *aCmd, s32 (*aPreprocessFunction)(u8, void *)) { DynOS_Level_ParseScript((const void *) DynOS_Level_CmdGet(aCmd, 8), aPreprocessFunction); return (LvlCmd *) DynOS_Level_CmdNext(aCmd, aCmd->mSize); } void DynOS_Level_ParseScript(const void *aScript, s32 (*aPreprocessFunction)(u8, void *)) { Stack _Stack; _Stack.mBaseIndex = -1; _Stack.mTopIndex = 0; for (LvlCmd *_Cmd = (LvlCmd *) aScript; _Cmd != NULL;) { u8 _CmdType = (_Cmd->mType & 0xFF); s32 _Action = aPreprocessFunction(_CmdType, (void *) _Cmd); switch (_Action) { case 0: switch (_CmdType) { case 0x00: _Cmd = DynOS_Level_CmdExecute(_Stack, _Cmd); break; case 0x01: _Cmd = DynOS_Level_CmdExitAndExecute(_Stack, _Cmd); break; case 0x02: _Cmd = DynOS_Level_CmdExit(_Stack, _Cmd); break; case 0x03: _Cmd = DynOS_Level_CmdSleep(_Stack, _Cmd); break; case 0x04: _Cmd = DynOS_Level_CmdSleepBeforeExit(_Stack, _Cmd); break; case 0x05: _Cmd = DynOS_Level_CmdJump(_Stack, _Cmd); break; case 0x06: _Cmd = DynOS_Level_CmdJumpLink(_Stack, _Cmd); break; case 0x07: _Cmd = DynOS_Level_CmdReturn(_Stack, _Cmd); break; case 0x08: _Cmd = DynOS_Level_CmdJumpLinkPushArg(_Stack, _Cmd); break; case 0x09: _Cmd = DynOS_Level_CmdJumpRepeat(_Stack, _Cmd); break; case 0x0A: _Cmd = DynOS_Level_CmdLoopBegin(_Stack, _Cmd); break; case 0x0B: _Cmd = DynOS_Level_CmdLoopUntil(_Stack, _Cmd); break; case 0x0C: _Cmd = DynOS_Level_CmdJumpIf(_Stack, _Cmd); break; case 0x0D: _Cmd = DynOS_Level_CmdJumpLinkIf(_Stack, _Cmd); break; case 0x0E: _Cmd = DynOS_Level_CmdSkipIf(_Stack, _Cmd); break; case 0x0F: _Cmd = DynOS_Level_CmdSkip(_Stack, _Cmd); break; case 0x10: _Cmd = DynOS_Level_CmdSkipNop(_Stack, _Cmd); break; case 0x11: _Cmd = DynOS_Level_CmdCall(_Stack, _Cmd); break; case 0x12: _Cmd = DynOS_Level_CmdCallLoop(_Stack, _Cmd); break; case 0x13: _Cmd = DynOS_Level_CmdSetRegister(_Stack, _Cmd); break; case 0x14: _Cmd = DynOS_Level_CmdPushPool(_Stack, _Cmd); break; case 0x15: _Cmd = DynOS_Level_CmdPopPool(_Stack, _Cmd); break; case 0x16: _Cmd = DynOS_Level_CmdLoadFixed(_Stack, _Cmd); break; case 0x17: _Cmd = DynOS_Level_CmdLoadRaw(_Stack, _Cmd); break; case 0x18: _Cmd = DynOS_Level_CmdLoadMIO0(_Stack, _Cmd); break; case 0x19: _Cmd = DynOS_Level_CmdLoadMarioHead(_Stack, _Cmd); break; case 0x1A: _Cmd = DynOS_Level_CmdLoadMIO0Texture(_Stack, _Cmd); break; case 0x1B: _Cmd = DynOS_Level_CmdInitLevel(_Stack, _Cmd); break; case 0x1C: _Cmd = DynOS_Level_CmdClearLevel(_Stack, _Cmd); break; case 0x1D: _Cmd = DynOS_Level_CmdAllocLevelPool(_Stack, _Cmd); break; case 0x1E: _Cmd = DynOS_Level_CmdFreeLevelPool(_Stack, _Cmd); break; case 0x1F: _Cmd = DynOS_Level_CmdBeginArea(_Stack, _Cmd); break; case 0x20: _Cmd = DynOS_Level_CmdEndArea(_Stack, _Cmd); break; case 0x21: _Cmd = DynOS_Level_CmdLoadModelFromDL(_Stack, _Cmd); break; case 0x22: _Cmd = DynOS_Level_CmdLoadModelFromGeo(_Stack, _Cmd); break; case 0x23: _Cmd = DynOS_Level_Cmd23(_Stack, _Cmd); break; case 0x24: _Cmd = DynOS_Level_CmdObject(_Stack, _Cmd); break; case 0x25: _Cmd = DynOS_Level_CmdMario(_Stack, _Cmd); break; case 0x26: _Cmd = DynOS_Level_CmdWarpNode(_Stack, _Cmd); break; case 0x27: _Cmd = DynOS_Level_CmdPaintingWarpNode(_Stack, _Cmd); break; case 0x28: _Cmd = DynOS_Level_CmdInstantWarp(_Stack, _Cmd); break; case 0x29: _Cmd = DynOS_Level_CmdLoadArea(_Stack, _Cmd); break; case 0x2A: _Cmd = DynOS_Level_CmdUnloadArea(_Stack, _Cmd); break; case 0x2B: _Cmd = DynOS_Level_CmdSetMarioStartPos(_Stack, _Cmd); break; case 0x2C: _Cmd = DynOS_Level_Cmd2C(_Stack, _Cmd); break; case 0x2D: _Cmd = DynOS_Level_Cmd2D(_Stack, _Cmd); break; case 0x2E: _Cmd = DynOS_Level_CmdSetTerrain(_Stack, _Cmd); break; case 0x2F: _Cmd = DynOS_Level_CmdSetRooms(_Stack, _Cmd); break; case 0x30: _Cmd = DynOS_Level_CmdShowDialog(_Stack, _Cmd); break; case 0x31: _Cmd = DynOS_Level_CmdSetTerrainType(_Stack, _Cmd); break; case 0x32: _Cmd = DynOS_Level_CmdNop(_Stack, _Cmd); break; case 0x33: _Cmd = DynOS_Level_CmdSetTransition(_Stack, _Cmd); break; case 0x34: _Cmd = DynOS_Level_CmdSetBlackout(_Stack, _Cmd); break; case 0x35: _Cmd = DynOS_Level_CmdSetGamma(_Stack, _Cmd); break; case 0x36: _Cmd = DynOS_Level_CmdSetBackgroundMusic(_Stack, _Cmd); break; case 0x37: _Cmd = DynOS_Level_CmdSetMenuMusic(_Stack, _Cmd); break; case 0x38: _Cmd = DynOS_Level_CmdStopMusic(_Stack, _Cmd); break; case 0x39: _Cmd = DynOS_Level_CmdMacroObjects(_Stack, _Cmd); break; case 0x3A: _Cmd = DynOS_Level_Cmd3A(_Stack, _Cmd); break; case 0x3B: _Cmd = DynOS_Level_CmdSetWhirlpool(_Stack, _Cmd); break; case 0x3C: _Cmd = DynOS_Level_CmdGetOrSet(_Stack, _Cmd); break; case 0x3D: _Cmd = DynOS_Level_CmdAdvanceDemo(_Stack, _Cmd); break; case 0x3E: _Cmd = DynOS_Level_CmdClearDemoPointer(_Stack, _Cmd); break; // coop case 0x3F: _Cmd = DynOS_Level_CmdPlaceObjectExt(_Stack, _Cmd); break; case 0x40: _Cmd = DynOS_Level_CmdPlaceObjectExt2(_Stack, _Cmd); break; case 0x41: _Cmd = DynOS_Level_CmdLoadModelFromGeoExt(_Stack, _Cmd); break; case 0x42: _Cmd = DynOS_Level_CmdJumpArea(_Stack, _Cmd, aPreprocessFunction); break; } break; case 1: _Cmd = (LvlCmd *) DynOS_Level_CmdNext(_Cmd, _Cmd->mSize); break; case 2: _Cmd = DynOS_Level_CmdReturn(_Stack, _Cmd); break; case 3: return; } } } // // Level Script Utilities // s16 *DynOS_Level_GetWarp(s32 aLevel, s32 aArea, u8 aWarpId) { if (aLevel >= CUSTOM_LEVEL_NUM_START) { struct CustomLevelInfo* info = smlua_level_util_get_info(aLevel); if (!info || !info->script) { return NULL; } sDynosCurrentLevelNum = 1; DynOS_Level_ParseScript(info->script, DynOS_Level_PreprocessScript); for (const auto &_Warp : sDynosLevelWarps[1]) { if (_Warp.mArea == aArea) { if (_Warp.mId == aWarpId) { return (s16 *) &_Warp; } } } return NULL; } DynOS_Level_Init(); for (const auto &_Warp : sDynosLevelWarps[aLevel]) { if (_Warp.mArea == aArea) { if (_Warp.mId == aWarpId) { return (s16 *) &_Warp; } } } return NULL; } s16 *DynOS_Level_GetWarpEntry(s32 aLevel, s32 aArea) { DynOS_Level_Init(); if (aLevel == LEVEL_TTM && aArea > 2) return NULL; // override vanilla castle warps if (DynOS_Level_GetCourse(aLevel) == COURSE_NONE) { extern const LevelScript level_castle_grounds_entry[]; extern const LevelScript level_castle_inside_entry[]; extern const LevelScript level_castle_courtyard_entry[]; if (sDynosLevelScripts[aLevel].mLevelScript == level_castle_inside_entry) { return DynOS_Level_GetWarp(aLevel, aArea, (aArea == 3) ? 0x00 : 0x01); } else if (sDynosLevelScripts[aLevel].mLevelScript == level_castle_grounds_entry) { return DynOS_Level_GetWarp(aLevel, aArea, 0x00); } else if (sDynosLevelScripts[aLevel].mLevelScript == level_castle_courtyard_entry) { return DynOS_Level_GetWarp(aLevel, aArea, 0x01); } } return DynOS_Level_GetWarp(aLevel, aArea, 0x0A); } s16 *DynOS_Level_GetWarpDeath(s32 aLevel, s32 aArea) { DynOS_Level_Init(); s16 *_Warp = DynOS_Level_GetWarp(aLevel, aArea, 0xF1); if (!_Warp) _Warp = DynOS_Level_GetWarp(aLevel, aArea, 0xF3); return _Warp; }