From b863cc80c80fa5b25ede6f199f6e8febabfad482 Mon Sep 17 00:00:00 2001 From: MysterD Date: Wed, 10 May 2023 13:25:41 -0700 Subject: [PATCH] Prevent several possible crashes and hangs, limit more struct fields to read-only --- autogen/convert_structs.py | 31 +++++++++++++++++-- docs/lua/structs.md | 22 +++++++------- src/engine/graph_node.c | 1 + src/game/area.c | 3 ++ src/game/behaviors/purple_switch.inc.c | 2 +- src/game/camera.c | 6 ++-- src/game/interaction.c | 18 +++++++---- src/game/mario.c | 2 ++ src/game/obj_behaviors.c | 5 +++ src/game/object_collision.c | 21 ++++++++++--- src/game/object_helpers.c | 5 +++ src/game/object_list_processor.c | 7 +++++ src/game/rendering_graph_node.c | 42 +++++++++++++++++++------- src/game/shadow.c | 1 + src/game/spawn_object.c | 4 +-- src/pc/lua/smlua_cobject_autogen.c | 24 +++++++-------- 16 files changed, 141 insertions(+), 53 deletions(-) diff --git a/autogen/convert_structs.py b/autogen/convert_structs.py index 886e299ce..fe636d33c 100644 --- a/autogen/convert_structs.py +++ b/autogen/convert_structs.py @@ -83,7 +83,8 @@ override_field_invisible = { } override_field_immutable = { - "MarioState": [ "playerIndex", "controller" ], + "MarioState": [ "playerIndex", "controller", "marioObj", "marioBodyState", "statusForCamera" ], + "ObjectNode": [ "next", "prev" ], "Character": [ "*" ], "NetworkPlayer": [ "*" ], "TextureInfo": [ "*" ], @@ -96,7 +97,11 @@ override_field_immutable = { "ModFile": [ "*" ], "BassAudio": [ "*" ], "Painting": [ "id", "imageCount", "textureType", "textureWidth", "textureHeight" ], - "SpawnInfo": [ "syncID" ] + "SpawnInfo": [ "syncID" ], + "CustomLevelInfo": [ "next" ], + "GraphNode": [ "next", "prev", "parent" ], + "ObjectWarpNode": [ "next "], + "SpawnInfo": [ "next" ], } override_field_version_excludes = { @@ -265,7 +270,29 @@ def get_struct_field_info(struct, field): return fid, ftype, fimmutable, lvt, lot +def output_nuke_struct(struct): + sid = struct['identifier'] + print('function Nuke' + sid + "(struct)") + for field in struct['fields']: + fid, ftype, fimmutable, lvt, lot = get_struct_field_info(struct, field) + if fimmutable == 'true': + continue + if sid in override_field_invisible: + if fid in override_field_invisible[sid]: + continue + if lvt == 'LVT_COBJECT': + print(' Nuke' + ftype.replace('struct ', '') + '(struct.' + fid + ')') + elif lvt == 'LVT_COBJECT_P': + print(' struct.' + fid + ' = nil') + else: + print(' struct.' + fid + ' = 0') + print('end') + print('') + def build_struct(struct): + # debug print out lua nuke functions + # output_nuke_struct(struct) + sid = struct['identifier'] # build up table and track column width diff --git a/docs/lua/structs.md b/docs/lua/structs.md index 48111bbcc..b175c66b4 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -539,7 +539,7 @@ | fullName | `string` | read-only | | levelNum | `integer` | | | modIndex | `integer` | | -| next | [CustomLevelInfo](structs.md#CustomLevelInfo) | | +| next | [CustomLevelInfo](structs.md#CustomLevelInfo) | read-only | | script | `Pointer` <`LevelScript`> | read-only | | scriptEntryName | `string` | read-only | | shortName | `string` | read-only | @@ -802,9 +802,9 @@ | children | [GraphNode](structs.md#GraphNode) | | | extraFlags | `integer` | | | flags | `integer` | | -| next | [GraphNode](structs.md#GraphNode) | | -| parent | [GraphNode](structs.md#GraphNode) | | -| prev | [GraphNode](structs.md#GraphNode) | | +| next | [GraphNode](structs.md#GraphNode) | read-only | +| parent | [GraphNode](structs.md#GraphNode) | read-only | +| prev | [GraphNode](structs.md#GraphNode) | read-only | | type | `integer` | | [:arrow_up_small:](#) @@ -1070,8 +1070,8 @@ | invincTimer | `integer` | | | isSnoring | `integer` | | | knockbackTimer | `integer` | | -| marioBodyState | [MarioBodyState](structs.md#MarioBodyState) | | -| marioObj | [Object](structs.md#Object) | | +| marioBodyState | [MarioBodyState](structs.md#MarioBodyState) | read-only | +| marioObj | [Object](structs.md#Object) | read-only | | minimumBoneY | `number` | | | nonInstantWarpPos | [Vec3f](structs.md#Vec3f) | read-only | | numCoins | `integer` | | @@ -1095,7 +1095,7 @@ | splineKeyframeFraction | `number` | | | splineState | `integer` | | | squishTimer | `integer` | | -| statusForCamera | [PlayerCameraState](structs.md#PlayerCameraState) | | +| statusForCamera | [PlayerCameraState](structs.md#PlayerCameraState) | read-only | | terrainSoundAddend | `integer` | | | twirlYaw | `integer` | | | unkB0 | `integer` | | @@ -1996,8 +1996,8 @@ | Field | Type | Access | | ----- | ---- | ------ | | gfx | [GraphNodeObject](structs.md#GraphNodeObject) | read-only | -| next | [ObjectNode](structs.md#ObjectNode) | | -| prev | [ObjectNode](structs.md#ObjectNode) | | +| next | [ObjectNode](structs.md#ObjectNode) | read-only | +| prev | [ObjectNode](structs.md#ObjectNode) | read-only | [:arrow_up_small:](#) @@ -2214,10 +2214,10 @@ | activeAreaIndex | `integer` | | | areaIndex | `integer` | | | behaviorArg | `integer` | | -| next | [SpawnInfo](structs.md#SpawnInfo) | | +| next | [SpawnInfo](structs.md#SpawnInfo) | read-only | | startAngle | [Vec3s](structs.md#Vec3s) | read-only | | startPos | [Vec3s](structs.md#Vec3s) | read-only | -| syncID | `integer` | read-only | +| syncID | `integer` | | | unk18 | [GraphNode](structs.md#GraphNode) | | [:arrow_up_small:](#) diff --git a/src/engine/graph_node.c b/src/engine/graph_node.c index 365704630..71eba0088 100644 --- a/src/engine/graph_node.c +++ b/src/engine/graph_node.c @@ -594,6 +594,7 @@ struct GraphNode *geo_remove_child(struct GraphNode *graphNode) { if (graphNode == NULL) { return NULL; } parent = graphNode->parent; + if (!parent) { return NULL; } firstChild = &parent->children; // Remove link with siblings diff --git a/src/game/area.c b/src/game/area.c index 48b10b751..3d82ef9a9 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -296,7 +296,10 @@ void load_mario_area(void) { stop_sounds_in_continuous_banks(); load_area(gMarioSpawnInfo->areaIndex); + if (!gCurrentArea) { return; } + for (s32 i = 0; i < MAX_PLAYERS; i++) { + if (!gMarioStates[i].spawnInfo) { continue; } gMarioStates[i].spawnInfo->areaIndex = gCurrentArea->index; } diff --git a/src/game/behaviors/purple_switch.inc.c b/src/game/behaviors/purple_switch.inc.c index 18a7a6d3c..662df4a7d 100644 --- a/src/game/behaviors/purple_switch.inc.c +++ b/src/game/behaviors/purple_switch.inc.c @@ -16,7 +16,7 @@ void bhv_purple_switch_loop(void) { u8 anyPlayerOnPlatform = FALSE; for (s32 i = 0; i < MAX_PLAYERS; i++) { if (!is_player_active(&gMarioStates[i])) { continue; } - if (gMarioStates[i].marioObj->platform == o) { + if (gMarioStates[i].marioObj && gMarioStates[i].marioObj->platform == o) { anyPlayerOnPlatform = TRUE; break; } diff --git a/src/game/camera.c b/src/game/camera.c index 41ba7ed0f..61d184527 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -11579,7 +11579,7 @@ void fov_default(struct MarioState *m) { camera_approach_f32_symmetric_bool(&gFOVState.fov, 45.f, (45.f - gFOVState.fov) / 30.f); gFOVState.unusedIsSleeping = 0; } - if (m->area->camera->cutscene == CUTSCENE_0F_UNUSED) { + if (m->area && m->area->camera && m->area->camera->cutscene == CUTSCENE_0F_UNUSED) { gFOVState.fov = 45.f; } } @@ -11600,7 +11600,7 @@ void approach_fov_60(UNUSED struct MarioState *m) { void approach_fov_45(struct MarioState *m) { f32 targetFoV = gFOVState.fov; - if (m->area->camera->mode == CAMERA_MODE_FIXED && m->area->camera->cutscene == 0) { + if (m->area && m->area->camera && m->area->camera->mode == CAMERA_MODE_FIXED && m->area->camera->cutscene == 0) { targetFoV = 45.f; } else { targetFoV = 45.f; @@ -11620,7 +11620,7 @@ void approach_fov_80(UNUSED struct MarioState *m) { void set_fov_bbh(struct MarioState *m) { f32 targetFoV = gFOVState.fov; - if (m->area->camera->mode == CAMERA_MODE_FIXED && m->area->camera->cutscene == 0) { + if (m->area && m->area->camera && m->area->camera->mode == CAMERA_MODE_FIXED && m->area->camera->cutscene == 0) { targetFoV = 60.f; } else { targetFoV = 45.f; diff --git a/src/game/interaction.c b/src/game/interaction.c index 7f18e852e..faaf633f5 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -345,9 +345,11 @@ void mario_drop_held_object(struct MarioState *m) { // ! When dropping an object instead of throwing it, it will be put at Mario's // y-positon instead of the HOLP's y-position. This fact is often exploited when // cloning objects. - m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0]; - m->heldObj->oPosY = m->pos[1]; - m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2]; + if (m->marioBodyState) { + m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0]; + m->heldObj->oPosY = m->pos[1]; + m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2]; + } m->heldObj->oMoveAngleYaw = m->faceAngle[1]; @@ -369,9 +371,11 @@ void mario_throw_held_object(struct MarioState *m) { obj_set_held_state(m->heldObj, bhvCarrySomething5); - m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0] + 32.0f * sins(m->faceAngle[1]); - m->heldObj->oPosY = m->marioBodyState->heldObjLastPosition[1]; - m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2] + 32.0f * coss(m->faceAngle[1]); + if (m->marioBodyState) { + m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0] + 32.0f * sins(m->faceAngle[1]); + m->heldObj->oPosY = m->marioBodyState->heldObjLastPosition[1]; + m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2] + 32.0f * coss(m->faceAngle[1]); + } m->heldObj->oMoveAngleYaw = m->faceAngle[1]; @@ -1278,6 +1282,8 @@ static u8 resolve_player_collision(struct MarioState* m, struct MarioState* m2) f32 extentY = m->marioObj->hitboxHeight; f32 radius = m->marioObj->hitboxRadius * 2.0f; + if (!m->marioBodyState || !m2->marioBodyState) { return FALSE; } + f32* localTorso = m->marioBodyState->torsoPos; f32* remoteTorso = m2->marioBodyState->torsoPos; diff --git a/src/game/mario.c b/src/game/mario.c index beda5e912..27c2e8d76 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -85,6 +85,7 @@ s32 is_anim_past_end(struct MarioState *m) { */ s16 set_mario_animation(struct MarioState *m, s32 targetAnimID) { struct Object *o = m->marioObj; + if (!o || !m->animation) { return 0; } struct Animation *targetAnim = m->animation->targetAnim; if (load_patchable_table(m->animation, targetAnimID)) { @@ -118,6 +119,7 @@ s16 set_mario_animation(struct MarioState *m, s32 targetAnimID) { */ s16 set_mario_anim_with_accel(struct MarioState *m, s32 targetAnimID, s32 accel) { struct Object *o = m->marioObj; + if (!o || !m->animation) { return 0; } struct Animation *targetAnim = m->animation->targetAnim; if (load_patchable_table(m->animation, targetAnimID)) { diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index 5fa2c8584..1094fd916 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -523,6 +523,7 @@ s8 is_point_within_radius_of_mario(f32 x, f32 y, f32 z, s32 dist) { if (!is_player_active(&gMarioStates[i])) { continue; } if (!gMarioStates[i].visibleToEnemies) { continue; } struct Object* player = gMarioStates[i].marioObj; + if (!player) { continue; } f32 mGfxX = player->header.gfx.pos[0]; f32 mGfxY = player->header.gfx.pos[1]; f32 mGfxZ = player->header.gfx.pos[2]; @@ -540,6 +541,7 @@ s8 is_point_within_radius_of_any_player(f32 x, f32 y, f32 z, s32 dist) { for (s32 i = 0; i < MAX_PLAYERS; i++) { if (!is_player_active(&gMarioStates[i])) { continue; } struct Object* player = gMarioStates[i].marioObj; + if (!player) { continue; } f32 mGfxX = player->header.gfx.pos[0]; f32 mGfxY = player->header.gfx.pos[1]; f32 mGfxZ = player->header.gfx.pos[2]; @@ -606,6 +608,7 @@ struct MarioState* nearest_mario_state_to_object(struct Object *obj) { struct MarioState* nearest = NULL; f32 nearestDist = 0; for (s32 i = 0; i < MAX_PLAYERS; i++) { + if (!gMarioStates[i].marioObj) { continue; } if (gMarioStates[i].marioObj == obj) { continue; } if (!gMarioStates[i].visibleToEnemies) { continue; } if (!is_player_active(&gMarioStates[i])) { continue; } @@ -624,6 +627,7 @@ struct MarioState* nearest_possible_mario_state_to_object(struct Object *obj) { struct MarioState* nearest = NULL; f32 nearestDist = 0; for (s32 i = 0; i < MAX_PLAYERS; i++) { + if (!gMarioStates[i].marioObj) { continue; } if (gMarioStates[i].marioObj == obj) { continue; } if (!is_player_active(&gMarioStates[i])) { continue; } float dist = dist_between_objects(obj, gMarioStates[i].marioObj); @@ -655,6 +659,7 @@ struct MarioState *nearest_interacting_mario_state_to_object(struct Object *obj) f32 nearestDist = 0; for (s32 i = 0; i < MAX_PLAYERS; i++) { + if (!gMarioStates[i].marioObj) { continue; } if (gMarioStates[i].marioObj == obj) { continue; } if (gMarioStates[i].interactObj != obj) { continue; } if (!gMarioStates[i].visibleToEnemies) { continue; } diff --git a/src/game/object_collision.c b/src/game/object_collision.c index 6634139e6..14501ee70 100644 --- a/src/game/object_collision.c +++ b/src/game/object_collision.c @@ -9,6 +9,7 @@ #include "pc/network/network_player.h" struct Object *debug_print_obj_collision(struct Object *a) { + if (!a) { return NULL; } struct Object *sp24; for (s32 i = 0; i < a->numCollidedObjs; i++) { @@ -22,6 +23,7 @@ struct Object *debug_print_obj_collision(struct Object *a) { } int detect_player_hitbox_overlap(struct MarioState* local, struct MarioState* remote, f32 scale) { + if (!local || !remote) { return FALSE; } if (local->marioObj == NULL || local->marioObj->oIntangibleTimer != 0) { return FALSE; } if (remote->marioObj == NULL || remote->marioObj->oIntangibleTimer != 0) { return FALSE; } @@ -63,6 +65,7 @@ int detect_player_hitbox_overlap(struct MarioState* local, struct MarioState* re } s32 detect_object_hitbox_overlap(struct Object *a, struct Object *b) { + if (!a || !b) { return 0; } f32 sp3C = a->oPosY - a->hitboxDownOffset; f32 sp38 = b->oPosY - b->hitboxDownOffset; f32 dx = a->oPosX - b->oPosX; @@ -105,6 +108,8 @@ s32 detect_object_hitbox_overlap(struct Object *a, struct Object *b) { } s32 detect_object_hurtbox_overlap(struct Object *a, struct Object *b) { + if (!a || !b) { return 0; } + f32 sp3C = a->oPosY - a->hitboxDownOffset; f32 sp38 = b->oPosY - b->hitboxDownOffset; f32 sp34 = a->oPosX - b->oPosX; @@ -137,26 +142,30 @@ s32 detect_object_hurtbox_overlap(struct Object *a, struct Object *b) { } void clear_object_collision(struct Object *a) { + if (!a) { return; } struct Object *sp4 = (struct Object *) a->header.next; - while (sp4 != a) { + while (sp4 && sp4 != a) { sp4->numCollidedObjs = 0; sp4->collidedObjInteractTypes = 0; if (sp4->oIntangibleTimer > 0) { sp4->oIntangibleTimer--; } + if (sp4 == (struct Object *)sp4->header.next) { break; } sp4 = (struct Object *) sp4->header.next; } } void check_collision_in_list(struct Object *a, struct Object *b, struct Object *c) { + if (!a) { return; } if (a->oIntangibleTimer == 0) { - while (b != c) { + while (b && b != c) { if (b->oIntangibleTimer == 0) { if (detect_object_hitbox_overlap(a, b) && b->hurtboxRadius != 0.0f) { detect_object_hurtbox_overlap(a, b); } } + if (b == (struct Object *)b->header.next) { break; } b = (struct Object *) b->header.next; } } @@ -166,7 +175,7 @@ void check_player_object_collision(void) { struct Object *sp1C = (struct Object *) &gObjectLists[OBJ_LIST_PLAYER]; struct Object *sp18 = (struct Object *) sp1C->header.next; - while (sp18 != sp1C) { + while (sp18 && sp18 != sp1C) { check_collision_in_list(sp18, (struct Object *) sp18->header.next, sp1C); check_collision_in_list(sp18, (struct Object *) gObjectLists[OBJ_LIST_POLELIKE].next, (struct Object *) &gObjectLists[OBJ_LIST_POLELIKE]); @@ -202,8 +211,9 @@ void check_pushable_object_collision(void) { struct Object *sp1C = (struct Object *) &gObjectLists[OBJ_LIST_PUSHABLE]; struct Object *sp18 = (struct Object *) sp1C->header.next; - while (sp18 != sp1C) { + while (sp18 && sp18 != sp1C) { check_collision_in_list(sp18, (struct Object *) sp18->header.next, sp1C); + if (sp18 == (struct Object *)sp18->header.next) { break; } sp18 = (struct Object *) sp18->header.next; } } @@ -212,7 +222,7 @@ void check_destructive_object_collision(void) { struct Object *sp1C = (struct Object *) &gObjectLists[OBJ_LIST_DESTRUCTIVE]; struct Object *sp18 = (struct Object *) sp1C->header.next; - while (sp18 != sp1C) { + while (sp18 && sp18 != sp1C) { if (sp18->oDistanceToMario < 2000.0f && !(sp18->activeFlags & ACTIVE_FLAG_UNK9)) { check_collision_in_list(sp18, (struct Object *) sp18->header.next, sp1C); check_collision_in_list(sp18, (struct Object *) gObjectLists[OBJ_LIST_GENACTOR].next, @@ -222,6 +232,7 @@ void check_destructive_object_collision(void) { check_collision_in_list(sp18, (struct Object *) gObjectLists[OBJ_LIST_SURFACE].next, (struct Object *) &gObjectLists[OBJ_LIST_SURFACE]); } + if (sp18 == (struct Object *)sp18->header.next) { break; } sp18 = (struct Object *) sp18->header.next; } } diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index 8401189a9..11e32ebc3 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -2532,6 +2532,7 @@ s32 cur_obj_wait_then_blink(s32 timeUntilBlinking, s32 numBlinks) { s32 cur_obj_is_mario_ground_pounding_platform(void) { for (s32 i = 0; i < MAX_PLAYERS; i++) { if (!is_player_active(&gMarioStates[i])) { continue; } + if (!gMarioStates[i].marioObj) { continue; } if (gMarioStates[i].marioObj->platform == o) { if (gMarioStates[i].action == ACT_GROUND_POUND_LAND) { return TRUE; @@ -2554,6 +2555,7 @@ void spawn_mist_particles_with_sound(u32 sp18) { void cur_obj_push_mario_away(f32 radius) { for (s32 i = 0; i < MAX_PLAYERS; i++) { struct Object* player = gMarioStates[i].marioObj; + if (!player) { continue; } f32 marioRelX = player->oPosX - o->oPosX; f32 marioRelZ = player->oPosZ - o->oPosZ; f32 marioDist = sqrtf(sqr(marioRelX) + sqr(marioRelZ)); @@ -2570,6 +2572,7 @@ void cur_obj_push_mario_away(f32 radius) { void cur_obj_push_mario_away_from_cylinder(f32 radius, f32 extentY) { for (s32 i = 0; i < MAX_PLAYERS; i++) { struct Object* player = gMarioStates[i].marioObj; + if (!player) { continue; } f32 marioRelY = player->oPosY - o->oPosY; if (marioRelY < 0.0f) { @@ -2733,6 +2736,7 @@ s32 cur_obj_mario_far_away(void) { for (s32 i = 0; i < MAX_PLAYERS; i++) { if (!is_player_active(&gMarioStates[i])) { continue; } struct Object* player = gMarioStates[i].marioObj; + if (!player) { continue; } f32 dx = o->oHomeX - player->oPosX; f32 dy = o->oHomeY - player->oPosY; f32 dz = o->oHomeZ - player->oPosZ; @@ -2880,6 +2884,7 @@ void cur_obj_if_hit_wall_bounce_away(void) { } s32 cur_obj_hide_if_mario_far_away_y(f32 distY) { + if (!gMarioStates[0].marioObj) { return FALSE; } if (absf(o->oPosY - gMarioStates[0].marioObj->oPosY) < distY * draw_distance_scalar()) { cur_obj_unhide(); return FALSE; diff --git a/src/game/object_list_processor.c b/src/game/object_list_processor.c index 9004725ad..f094ad58f 100644 --- a/src/game/object_list_processor.c +++ b/src/game/object_list_processor.c @@ -347,10 +347,13 @@ void bhv_mario_update(void) { * including firstObj itself. Return the number of objects that were updated. */ s32 update_objects_starting_at(struct ObjectNode *objList, struct ObjectNode *firstObj) { + if (!firstObj) { return 0; } + s32 count = 0; while (objList != firstObj) { gCurrentObject = (struct Object *) firstObj; + if (!gCurrentObject) { break; } gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_HAS_ANIMATION; cur_obj_update(); @@ -372,11 +375,13 @@ s32 update_objects_starting_at(struct ObjectNode *objList, struct ObjectNode *fi * updated) */ s32 update_objects_during_time_stop(struct ObjectNode *objList, struct ObjectNode *firstObj) { + if (!firstObj) { return 0; } s32 count = 0; s32 unfrozen; while (objList != firstObj) { gCurrentObject = (struct Object *) firstObj; + if (!gCurrentObject) { break; } unfrozen = FALSE; @@ -433,10 +438,12 @@ s32 update_objects_in_list(struct ObjectNode *objList) { * Unload any objects in the list that have been deactivated. */ s32 unload_deactivated_objects_in_list(struct ObjectNode *objList) { + if (!objList) { return 0; } struct ObjectNode *obj = objList->next; while (objList != obj) { gCurrentObject = (struct Object *) obj; + if (!gCurrentObject) { break; } obj = obj->next; diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index 767ffb1ec..4527cf51f 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -42,7 +42,7 @@ * */ -#define MATRIX_STACK_SIZE 32 +#define MATRIX_STACK_SIZE 64 f32 gProjectionMaxNearValue = 5; s16 gProjectionVanillaNearValue = 100; @@ -327,9 +327,18 @@ void patch_mtx_interpolated(f32 delta) { static u8 increment_mat_stack() { Mtx *mtx = alloc_display_list(sizeof(*mtx)); Mtx *mtxPrev = alloc_display_list(sizeof(*mtxPrev)); - if (mtx == NULL || mtxPrev == NULL) { LOG_ERROR("Failed to allocate our matrices for the matrix stack."); return FALSE; } + if (mtx == NULL || mtxPrev == NULL) { + LOG_ERROR("Failed to allocate our matrices for the matrix stack."); + return FALSE; + } gMatStackIndex++; + if (gMatStackIndex >= MATRIX_STACK_SIZE) { + LOG_ERROR("Exceeded matrix stack size."); + gMatStackIndex = MATRIX_STACK_SIZE - 1; + return FALSE; + } + mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); mtxf_to_mtx(mtxPrev, gMatStackPrev[gMatStackIndex]); gMatStackFixed[gMatStackIndex] = mtx; @@ -529,7 +538,7 @@ static void geo_process_camera(struct GraphNodeCamera *node) { Mat4 cameraTransform; // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } Mtx *rollMtx = alloc_display_list(sizeof(*rollMtx)); if (rollMtx == NULL) { return; } @@ -588,7 +597,7 @@ static void geo_process_translation_rotation(struct GraphNodeTranslationRotation Vec3f translation; // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } vec3s_to_vec3f(translation, node->translation); mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation); @@ -617,7 +626,7 @@ static void geo_process_translation(struct GraphNodeTranslation *node) { Vec3f translation; // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } vec3s_to_vec3f(translation, node->translation); mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero); @@ -645,7 +654,7 @@ static void geo_process_rotation(struct GraphNodeRotation *node) { Mat4 mtxf; // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation); mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); @@ -681,7 +690,7 @@ static void geo_process_scale(struct GraphNodeScale *node) { Vec3f prevScaleVec; // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } vec3f_set(scaleVec, node->scale, node->scale, node->scale); mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec); @@ -717,7 +726,7 @@ static void geo_process_billboard(struct GraphNodeBillboard *node) { Vec3f translation; // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } s16 nextMatStackIndex = gMatStackIndex + 1; @@ -890,7 +899,7 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) { Vec3f translationPrev; // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } u16 *animAttribute = gCurrAnimAttribute; u8 animType = gCurAnimType; @@ -983,7 +992,7 @@ static void geo_process_shadow(struct GraphNodeShadow *node) { f32 shadowScale; // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } if (gCurGraphNodeCamera != NULL && gCurGraphNodeObject != NULL) { if (gCurGraphNodeHeldObject != NULL) { @@ -1191,6 +1200,9 @@ static void geo_process_object(struct Object *node) { s32 hasAnimation = (node->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0; Vec3f scalePrev; + // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\. + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } + if (node->hookRender) { smlua_call_event_hooks_object_param(HOOK_ON_OBJECT_RENDER, node); } @@ -1379,7 +1391,7 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { Vec3f scalePrev; // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } #ifdef F3DEX_GBI_2 gSPLookAt(gDisplayListHead++, &lookAt); @@ -1476,6 +1488,7 @@ void geo_process_node_and_siblings(struct GraphNode *firstNode) { s16 iterateChildren = TRUE; struct GraphNode *curGraphNode = firstNode; if (curGraphNode == NULL) { return; } + u32 depthSanity = 0; struct GraphNode *parent = curGraphNode->parent; @@ -1489,6 +1502,13 @@ void geo_process_node_and_siblings(struct GraphNode *firstNode) { if (curGraphNode == NULL) { break; } + + // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\. + if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { break; } + + // Break out of endless loops + if (++depthSanity > 5000) { break; } + if (curGraphNode->flags & GRAPH_RENDER_ACTIVE) { if (curGraphNode->flags & GRAPH_RENDER_CHILDREN_FIRST) { geo_try_process_children(curGraphNode); diff --git a/src/game/shadow.c b/src/game/shadow.c index fb0cd74c2..e386c8334 100644 --- a/src/game/shadow.c +++ b/src/game/shadow.c @@ -583,6 +583,7 @@ s8 correct_shadow_solidity_for_animations(s32 playerIndex, u8 initialSolidity, s extern struct MarioState gMarioStates[]; player = gMarioStates[playerIndex].marioObj; + if (!player) { return SHADOW_SOLIDITY_NO_SHADOW; } animFrame = player->header.gfx.animInfo.animFrame; switch (player->header.gfx.animInfo.animID) { diff --git a/src/game/spawn_object.c b/src/game/spawn_object.c index 78c9aaaf2..36fe439bd 100644 --- a/src/game/spawn_object.c +++ b/src/game/spawn_object.c @@ -141,8 +141,8 @@ void unused_deallocate(struct LinkedList *freeList, struct LinkedList *node) { static void deallocate_object(struct ObjectNode *freeList, struct ObjectNode *obj) { if (!obj || !freeList) { return; } // Remove from object list - obj->next->prev = obj->prev; - obj->prev->next = obj->next; + if (obj->next) { obj->next->prev = obj->prev; } + if (obj->prev) { obj->prev->next = obj->next; } // Insert at beginning of free list obj->next = freeList->next; diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index c063834c0..ca1300d2f 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -404,7 +404,7 @@ static struct LuaObjectField sCustomLevelInfoFields[LUA_CUSTOM_LEVEL_INFO_FIELD_ { "fullName", LVT_STRING_P, offsetof(struct CustomLevelInfo, fullName), true, LOT_NONE }, { "levelNum", LVT_S16, offsetof(struct CustomLevelInfo, levelNum), false, LOT_NONE }, { "modIndex", LVT_S32, offsetof(struct CustomLevelInfo, modIndex), false, LOT_NONE }, - { "next", LVT_COBJECT_P, offsetof(struct CustomLevelInfo, next), false, LOT_CUSTOMLEVELINFO }, + { "next", LVT_COBJECT_P, offsetof(struct CustomLevelInfo, next), true, LOT_CUSTOMLEVELINFO }, { "script", LVT_LEVELSCRIPT_P, offsetof(struct CustomLevelInfo, script), true, LOT_POINTER }, { "scriptEntryName", LVT_STRING_P, offsetof(struct CustomLevelInfo, scriptEntryName), true, LOT_NONE }, { "shortName", LVT_STRING_P, offsetof(struct CustomLevelInfo, shortName), true, LOT_NONE }, @@ -630,9 +630,9 @@ static struct LuaObjectField sGraphNodeFields[LUA_GRAPH_NODE_FIELD_COUNT] = { { "extraFlags", LVT_U8, offsetof(struct GraphNode, extraFlags), false, LOT_NONE }, { "flags", LVT_S16, offsetof(struct GraphNode, flags), false, LOT_NONE }, // { "georef", LVT_???, offsetof(struct GraphNode, georef), true, LOT_??? }, <--- UNIMPLEMENTED - { "next", LVT_COBJECT_P, offsetof(struct GraphNode, next), false, LOT_GRAPHNODE }, - { "parent", LVT_COBJECT_P, offsetof(struct GraphNode, parent), false, LOT_GRAPHNODE }, - { "prev", LVT_COBJECT_P, offsetof(struct GraphNode, prev), false, LOT_GRAPHNODE }, + { "next", LVT_COBJECT_P, offsetof(struct GraphNode, next), true, LOT_GRAPHNODE }, + { "parent", LVT_COBJECT_P, offsetof(struct GraphNode, parent), true, LOT_GRAPHNODE }, + { "prev", LVT_COBJECT_P, offsetof(struct GraphNode, prev), true, LOT_GRAPHNODE }, { "type", LVT_S16, offsetof(struct GraphNode, type), false, LOT_NONE }, }; @@ -855,8 +855,8 @@ static struct LuaObjectField sMarioStateFields[LUA_MARIO_STATE_FIELD_COUNT] = { { "invincTimer", LVT_S16, offsetof(struct MarioState, invincTimer), false, LOT_NONE }, { "isSnoring", LVT_U8, offsetof(struct MarioState, isSnoring), false, LOT_NONE }, { "knockbackTimer", LVT_U8, offsetof(struct MarioState, knockbackTimer), false, LOT_NONE }, - { "marioBodyState", LVT_COBJECT_P, offsetof(struct MarioState, marioBodyState), false, LOT_MARIOBODYSTATE }, - { "marioObj", LVT_COBJECT_P, offsetof(struct MarioState, marioObj), false, LOT_OBJECT }, + { "marioBodyState", LVT_COBJECT_P, offsetof(struct MarioState, marioBodyState), true, LOT_MARIOBODYSTATE }, + { "marioObj", LVT_COBJECT_P, offsetof(struct MarioState, marioObj), true, LOT_OBJECT }, { "minimumBoneY", LVT_F32, offsetof(struct MarioState, minimumBoneY), false, LOT_NONE }, { "nonInstantWarpPos", LVT_COBJECT, offsetof(struct MarioState, nonInstantWarpPos), true, LOT_VEC3F }, { "numCoins", LVT_S16, offsetof(struct MarioState, numCoins), false, LOT_NONE }, @@ -880,7 +880,7 @@ static struct LuaObjectField sMarioStateFields[LUA_MARIO_STATE_FIELD_COUNT] = { { "splineKeyframeFraction", LVT_F32, offsetof(struct MarioState, splineKeyframeFraction), false, LOT_NONE }, { "splineState", LVT_S32, offsetof(struct MarioState, splineState), false, LOT_NONE }, { "squishTimer", LVT_U8, offsetof(struct MarioState, squishTimer), false, LOT_NONE }, - { "statusForCamera", LVT_COBJECT_P, offsetof(struct MarioState, statusForCamera), false, LOT_PLAYERCAMERASTATE }, + { "statusForCamera", LVT_COBJECT_P, offsetof(struct MarioState, statusForCamera), true, LOT_PLAYERCAMERASTATE }, { "terrainSoundAddend", LVT_U32, offsetof(struct MarioState, terrainSoundAddend), false, LOT_NONE }, { "twirlYaw", LVT_S16, offsetof(struct MarioState, twirlYaw), false, LOT_NONE }, { "unkB0", LVT_S16, offsetof(struct MarioState, unkB0), false, LOT_NONE }, @@ -1759,9 +1759,9 @@ static struct LuaObjectField sObjectHitboxFields[LUA_OBJECT_HITBOX_FIELD_COUNT] #define LUA_OBJECT_NODE_FIELD_COUNT 3 static struct LuaObjectField sObjectNodeFields[LUA_OBJECT_NODE_FIELD_COUNT] = { - { "gfx", LVT_COBJECT, offsetof(struct ObjectNode, gfx), true, LOT_GRAPHNODEOBJECT }, - { "next", LVT_COBJECT_P, offsetof(struct ObjectNode, next), false, LOT_OBJECTNODE }, - { "prev", LVT_COBJECT_P, offsetof(struct ObjectNode, prev), false, LOT_OBJECTNODE }, + { "gfx", LVT_COBJECT, offsetof(struct ObjectNode, gfx), true, LOT_GRAPHNODEOBJECT }, + { "next", LVT_COBJECT_P, offsetof(struct ObjectNode, next), true, LOT_OBJECTNODE }, + { "prev", LVT_COBJECT_P, offsetof(struct ObjectNode, prev), true, LOT_OBJECTNODE }, }; #define LUA_OBJECT_WARP_NODE_FIELD_COUNT 3 @@ -1922,10 +1922,10 @@ static struct LuaObjectField sSpawnInfoFields[LUA_SPAWN_INFO_FIELD_COUNT] = { { "areaIndex", LVT_S8, offsetof(struct SpawnInfo, areaIndex), false, LOT_NONE }, { "behaviorArg", LVT_U32, offsetof(struct SpawnInfo, behaviorArg), false, LOT_NONE }, // { "behaviorScript", LVT_???, offsetof(struct SpawnInfo, behaviorScript), false, LOT_??? }, <--- UNIMPLEMENTED - { "next", LVT_COBJECT_P, offsetof(struct SpawnInfo, next), false, LOT_SPAWNINFO }, + { "next", LVT_COBJECT_P, offsetof(struct SpawnInfo, next), true, LOT_SPAWNINFO }, { "startAngle", LVT_COBJECT, offsetof(struct SpawnInfo, startAngle), true, LOT_VEC3S }, { "startPos", LVT_COBJECT, offsetof(struct SpawnInfo, startPos), true, LOT_VEC3S }, - { "syncID", LVT_U32, offsetof(struct SpawnInfo, syncID), true, LOT_NONE }, + { "syncID", LVT_U32, offsetof(struct SpawnInfo, syncID), false, LOT_NONE }, { "unk18", LVT_COBJECT_P, offsetof(struct SpawnInfo, unk18), false, LOT_GRAPHNODE }, };