#define _CRT_SECURE_NO_WARNINGS 1 // for fopen #include #include #include #include #include #include #include #include #include "../src/libsm64.h" extern "C" { #define SDL_MAIN_HANDLED #include "level.h" #include "context.h" #include "renderer.h" #include "gl33core/gl33core_renderer.h" #include "gl20/gl20_renderer.h" } #include "imgui/imgui.h" #include "imgui/backends/imgui_impl_sdl2.h" #include "imgui/backends/imgui_impl_opengl3.h" #include "audio.h" #include "sm64.h" // Add this include near the top, after libsm64.h // AnimInfo is defined in the decomp headers, not the public API #include "../src/decomp/include/types.h" // or wherever AnimInfo is fully defined in your tree // ------------------------------------------------------- // Named struct so it can be used as a parameter type struct FlagEntry { uint32_t bit; const char *name; }; static const FlagEntry sMarioFlags[] = { {MARIO_NORMAL_CAP, "MARIO_NORMAL_CAP"}, {MARIO_VANISH_CAP, "MARIO_VANISH_CAP"}, {MARIO_METAL_CAP, "MARIO_METAL_CAP"}, {MARIO_WING_CAP, "MARIO_WING_CAP"}, {MARIO_CAP_ON_HEAD, "MARIO_CAP_ON_HEAD"}, {MARIO_CAP_IN_HAND, "MARIO_CAP_IN_HAND"}, {MARIO_METAL_SHOCK, "MARIO_METAL_SHOCK"}, {MARIO_TELEPORTING, "MARIO_TELEPORTING"}, {MARIO_UNKNOWN_08, "MARIO_UNKNOWN_08"}, {MARIO_UNKNOWN_13, "MARIO_UNKNOWN_13"}, {MARIO_ACTION_SOUND_PLAYED, "MARIO_ACTION_SOUND_PLAYED"}, {MARIO_MARIO_SOUND_PLAYED, "MARIO_MARIO_SOUND_PLAYED"}, {MARIO_UNKNOWN_18, "MARIO_UNKNOWN_18"}, {MARIO_PUNCHING, "MARIO_PUNCHING"}, {MARIO_KICKING, "MARIO_KICKING"}, {MARIO_TRIPPING, "MARIO_TRIPPING"}, {MARIO_UNKNOWN_25, "MARIO_UNKNOWN_25"}, {MARIO_UNKNOWN_30, "MARIO_UNKNOWN_30"}, {MARIO_UNKNOWN_31, "MARIO_UNKNOWN_31"}, }; static const FlagEntry sParticleFlags[] = { {PARTICLE_DUST, "PARTICLE_DUST"}, {PARTICLE_VERTICAL_STAR, "PARTICLE_VERTICAL_STAR"}, {PARTICLE_2, "PARTICLE_2"}, {PARTICLE_SPARKLES, "PARTICLE_SPARKLES"}, {PARTICLE_HORIZONTAL_STAR, "PARTICLE_HORIZONTAL_STAR"}, {PARTICLE_BUBBLE, "PARTICLE_BUBBLE"}, {PARTICLE_WATER_SPLASH, "PARTICLE_WATER_SPLASH"}, {PARTICLE_IDLE_WATER_WAVE, "PARTICLE_IDLE_WATER_WAVE"}, {PARTICLE_SHALLOW_WATER_WAVE, "PARTICLE_SHALLOW_WATER_WAVE"}, {PARTICLE_PLUNGE_BUBBLE, "PARTICLE_PLUNGE_BUBBLE"}, {PARTICLE_WAVE_TRAIL, "PARTICLE_WAVE_TRAIL"}, {PARTICLE_FIRE, "PARTICLE_FIRE"}, {PARTICLE_SHALLOW_WATER_SPLASH, "PARTICLE_SHALLOW_WATER_SPLASH"}, {PARTICLE_LEAF, "PARTICLE_LEAF"}, {PARTICLE_SNOW, "PARTICLE_SNOW"}, {PARTICLE_DIRT, "PARTICLE_DIRT"}, {PARTICLE_MIST_CIRCLE, "PARTICLE_MIST_CIRCLE"}, {PARTICLE_BREATH, "PARTICLE_BREATH"}, {PARTICLE_TRIANGLE, "PARTICLE_TRIANGLE"}, {PARTICLE_19, "PARTICLE_19"}, }; static const char *GetActionName(uint32_t action) { switch (action) { case ACT_UNINITIALIZED: return "ACT_UNINITIALIZED"; // stationary case ACT_IDLE: return "ACT_IDLE"; case ACT_START_SLEEPING: return "ACT_START_SLEEPING"; case ACT_SLEEPING: return "ACT_SLEEPING"; case ACT_WAKING_UP: return "ACT_WAKING_UP"; case ACT_PANTING: return "ACT_PANTING"; case ACT_HOLD_PANTING_UNUSED: return "ACT_HOLD_PANTING_UNUSED"; case ACT_HOLD_IDLE: return "ACT_HOLD_IDLE"; case ACT_HOLD_HEAVY_IDLE: return "ACT_HOLD_HEAVY_IDLE"; case ACT_STANDING_AGAINST_WALL: return "ACT_STANDING_AGAINST_WALL"; case ACT_COUGHING: return "ACT_COUGHING"; case ACT_SHIVERING: return "ACT_SHIVERING"; case ACT_IN_QUICKSAND: return "ACT_IN_QUICKSAND"; case ACT_UNKNOWN_0002020E: return "ACT_UNKNOWN_0002020E"; case ACT_CROUCHING: return "ACT_CROUCHING"; case ACT_START_CROUCHING: return "ACT_START_CROUCHING"; case ACT_STOP_CROUCHING: return "ACT_STOP_CROUCHING"; case ACT_START_CRAWLING: return "ACT_START_CRAWLING"; case ACT_STOP_CRAWLING: return "ACT_STOP_CRAWLING"; case ACT_SLIDE_KICK_SLIDE_STOP: return "ACT_SLIDE_KICK_SLIDE_STOP"; case ACT_SHOCKWAVE_BOUNCE: return "ACT_SHOCKWAVE_BOUNCE"; case ACT_FIRST_PERSON: return "ACT_FIRST_PERSON"; case ACT_BACKFLIP_LAND_STOP: return "ACT_BACKFLIP_LAND_STOP"; case ACT_JUMP_LAND_STOP: return "ACT_JUMP_LAND_STOP"; case ACT_DOUBLE_JUMP_LAND_STOP: return "ACT_DOUBLE_JUMP_LAND_STOP"; case ACT_FREEFALL_LAND_STOP: return "ACT_FREEFALL_LAND_STOP"; case ACT_SIDE_FLIP_LAND_STOP: return "ACT_SIDE_FLIP_LAND_STOP"; case ACT_HOLD_JUMP_LAND_STOP: return "ACT_HOLD_JUMP_LAND_STOP"; case ACT_HOLD_FREEFALL_LAND_STOP: return "ACT_HOLD_FREEFALL_LAND_STOP"; case ACT_AIR_THROW_LAND: return "ACT_AIR_THROW_LAND"; case ACT_TWIRL_LAND: return "ACT_TWIRL_LAND"; case ACT_LAVA_BOOST_LAND: return "ACT_LAVA_BOOST_LAND"; case ACT_TRIPLE_JUMP_LAND_STOP: return "ACT_TRIPLE_JUMP_LAND_STOP"; case ACT_LONG_JUMP_LAND_STOP: return "ACT_LONG_JUMP_LAND_STOP"; case ACT_GROUND_POUND_LAND: return "ACT_GROUND_POUND_LAND"; case ACT_BRAKING_STOP: return "ACT_BRAKING_STOP"; case ACT_BUTT_SLIDE_STOP: return "ACT_BUTT_SLIDE_STOP"; case ACT_HOLD_BUTT_SLIDE_STOP: return "ACT_HOLD_BUTT_SLIDE_STOP"; // moving case ACT_WALKING: return "ACT_WALKING"; case ACT_HOLD_WALKING: return "ACT_HOLD_WALKING"; case ACT_TURNING_AROUND: return "ACT_TURNING_AROUND"; case ACT_FINISH_TURNING_AROUND: return "ACT_FINISH_TURNING_AROUND"; case ACT_BRAKING: return "ACT_BRAKING"; case ACT_RIDING_SHELL_GROUND: return "ACT_RIDING_SHELL_GROUND"; case ACT_HOLD_HEAVY_WALKING: return "ACT_HOLD_HEAVY_WALKING"; case ACT_CRAWLING: return "ACT_CRAWLING"; case ACT_BURNING_GROUND: return "ACT_BURNING_GROUND"; case ACT_DECELERATING: return "ACT_DECELERATING"; case ACT_HOLD_DECELERATING: return "ACT_HOLD_DECELERATING"; case ACT_BEGIN_SLIDING: return "ACT_BEGIN_SLIDING"; case ACT_HOLD_BEGIN_SLIDING: return "ACT_HOLD_BEGIN_SLIDING"; case ACT_BUTT_SLIDE: return "ACT_BUTT_SLIDE"; case ACT_STOMACH_SLIDE: return "ACT_STOMACH_SLIDE"; case ACT_HOLD_BUTT_SLIDE: return "ACT_HOLD_BUTT_SLIDE"; case ACT_HOLD_STOMACH_SLIDE: return "ACT_HOLD_STOMACH_SLIDE"; case ACT_DIVE_SLIDE: return "ACT_DIVE_SLIDE"; case ACT_MOVE_PUNCHING: return "ACT_MOVE_PUNCHING"; case ACT_CROUCH_SLIDE: return "ACT_CROUCH_SLIDE"; case ACT_SLIDE_KICK_SLIDE: return "ACT_SLIDE_KICK_SLIDE"; case ACT_HARD_BACKWARD_GROUND_KB: return "ACT_HARD_BACKWARD_GROUND_KB"; case ACT_HARD_FORWARD_GROUND_KB: return "ACT_HARD_FORWARD_GROUND_KB"; case ACT_BACKWARD_GROUND_KB: return "ACT_BACKWARD_GROUND_KB"; case ACT_FORWARD_GROUND_KB: return "ACT_FORWARD_GROUND_KB"; case ACT_SOFT_BACKWARD_GROUND_KB: return "ACT_SOFT_BACKWARD_GROUND_KB"; case ACT_SOFT_FORWARD_GROUND_KB: return "ACT_SOFT_FORWARD_GROUND_KB"; case ACT_GROUND_BONK: return "ACT_GROUND_BONK"; case ACT_DEATH_EXIT_LAND: return "ACT_DEATH_EXIT_LAND"; case ACT_JUMP_LAND: return "ACT_JUMP_LAND"; case ACT_FREEFALL_LAND: return "ACT_FREEFALL_LAND"; case ACT_DOUBLE_JUMP_LAND: return "ACT_DOUBLE_JUMP_LAND"; case ACT_SIDE_FLIP_LAND: return "ACT_SIDE_FLIP_LAND"; case ACT_HOLD_JUMP_LAND: return "ACT_HOLD_JUMP_LAND"; case ACT_HOLD_FREEFALL_LAND: return "ACT_HOLD_FREEFALL_LAND"; case ACT_QUICKSAND_JUMP_LAND: return "ACT_QUICKSAND_JUMP_LAND"; case ACT_HOLD_QUICKSAND_JUMP_LAND: return "ACT_HOLD_QUICKSAND_JUMP_LAND"; case ACT_TRIPLE_JUMP_LAND: return "ACT_TRIPLE_JUMP_LAND"; case ACT_LONG_JUMP_LAND: return "ACT_LONG_JUMP_LAND"; case ACT_BACKFLIP_LAND: return "ACT_BACKFLIP_LAND"; // airborne case ACT_JUMP: return "ACT_JUMP"; case ACT_DOUBLE_JUMP: return "ACT_DOUBLE_JUMP"; case ACT_TRIPLE_JUMP: return "ACT_TRIPLE_JUMP"; case ACT_BACKFLIP: return "ACT_BACKFLIP"; case ACT_STEEP_JUMP: return "ACT_STEEP_JUMP"; case ACT_WALL_KICK_AIR: return "ACT_WALL_KICK_AIR"; case ACT_SIDE_FLIP: return "ACT_SIDE_FLIP"; case ACT_LONG_JUMP: return "ACT_LONG_JUMP"; case ACT_WATER_JUMP: return "ACT_WATER_JUMP"; case ACT_DIVE: return "ACT_DIVE"; case ACT_FREEFALL: return "ACT_FREEFALL"; case ACT_TOP_OF_POLE_JUMP: return "ACT_TOP_OF_POLE_JUMP"; case ACT_BUTT_SLIDE_AIR: return "ACT_BUTT_SLIDE_AIR"; case ACT_FLYING_TRIPLE_JUMP: return "ACT_FLYING_TRIPLE_JUMP"; case ACT_SHOT_FROM_CANNON: return "ACT_SHOT_FROM_CANNON"; case ACT_FLYING: return "ACT_FLYING"; case ACT_RIDING_SHELL_JUMP: return "ACT_RIDING_SHELL_JUMP"; case ACT_RIDING_SHELL_FALL: return "ACT_RIDING_SHELL_FALL"; case ACT_VERTICAL_WIND: return "ACT_VERTICAL_WIND"; case ACT_HOLD_JUMP: return "ACT_HOLD_JUMP"; case ACT_HOLD_FREEFALL: return "ACT_HOLD_FREEFALL"; case ACT_HOLD_BUTT_SLIDE_AIR: return "ACT_HOLD_BUTT_SLIDE_AIR"; case ACT_HOLD_WATER_JUMP: return "ACT_HOLD_WATER_JUMP"; case ACT_TWIRLING: return "ACT_TWIRLING"; case ACT_FORWARD_ROLLOUT: return "ACT_FORWARD_ROLLOUT"; case ACT_AIR_HIT_WALL: return "ACT_AIR_HIT_WALL"; case ACT_RIDING_HOOT: return "ACT_RIDING_HOOT"; case ACT_GROUND_POUND: return "ACT_GROUND_POUND"; case ACT_SLIDE_KICK: return "ACT_SLIDE_KICK"; case ACT_AIR_THROW: return "ACT_AIR_THROW"; case ACT_JUMP_KICK: return "ACT_JUMP_KICK"; case ACT_BACKWARD_ROLLOUT: return "ACT_BACKWARD_ROLLOUT"; case ACT_CRAZY_BOX_BOUNCE: return "ACT_CRAZY_BOX_BOUNCE"; case ACT_SPECIAL_TRIPLE_JUMP: return "ACT_SPECIAL_TRIPLE_JUMP"; case ACT_BACKWARD_AIR_KB: return "ACT_BACKWARD_AIR_KB"; case ACT_FORWARD_AIR_KB: return "ACT_FORWARD_AIR_KB"; case ACT_HARD_FORWARD_AIR_KB: return "ACT_HARD_FORWARD_AIR_KB"; case ACT_HARD_BACKWARD_AIR_KB: return "ACT_HARD_BACKWARD_AIR_KB"; case ACT_BURNING_JUMP: return "ACT_BURNING_JUMP"; case ACT_BURNING_FALL: return "ACT_BURNING_FALL"; case ACT_SOFT_BONK: return "ACT_SOFT_BONK"; case ACT_LAVA_BOOST: return "ACT_LAVA_BOOST"; case ACT_GETTING_BLOWN: return "ACT_GETTING_BLOWN"; case ACT_THROWN_FORWARD: return "ACT_THROWN_FORWARD"; case ACT_THROWN_BACKWARD: return "ACT_THROWN_BACKWARD"; // submerged case ACT_WATER_IDLE: return "ACT_WATER_IDLE"; case ACT_HOLD_WATER_IDLE: return "ACT_HOLD_WATER_IDLE"; case ACT_WATER_ACTION_END: return "ACT_WATER_ACTION_END"; case ACT_HOLD_WATER_ACTION_END: return "ACT_HOLD_WATER_ACTION_END"; case ACT_DROWNING: return "ACT_DROWNING"; case ACT_BACKWARD_WATER_KB: return "ACT_BACKWARD_WATER_KB"; case ACT_FORWARD_WATER_KB: return "ACT_FORWARD_WATER_KB"; case ACT_WATER_DEATH: return "ACT_WATER_DEATH"; case ACT_WATER_SHOCKED: return "ACT_WATER_SHOCKED"; case ACT_BREASTSTROKE: return "ACT_BREASTSTROKE"; case ACT_SWIMMING_END: return "ACT_SWIMMING_END"; case ACT_FLUTTER_KICK: return "ACT_FLUTTER_KICK"; case ACT_HOLD_BREASTSTROKE: return "ACT_HOLD_BREASTSTROKE"; case ACT_HOLD_SWIMMING_END: return "ACT_HOLD_SWIMMING_END"; case ACT_HOLD_FLUTTER_KICK: return "ACT_HOLD_FLUTTER_KICK"; case ACT_WATER_SHELL_SWIMMING: return "ACT_WATER_SHELL_SWIMMING"; case ACT_WATER_THROW: return "ACT_WATER_THROW"; case ACT_WATER_PUNCH: return "ACT_WATER_PUNCH"; case ACT_WATER_PLUNGE: return "ACT_WATER_PLUNGE"; case ACT_CAUGHT_IN_WHIRLPOOL: return "ACT_CAUGHT_IN_WHIRLPOOL"; case ACT_METAL_WATER_STANDING: return "ACT_METAL_WATER_STANDING"; case ACT_HOLD_METAL_WATER_STANDING: return "ACT_HOLD_METAL_WATER_STANDING"; case ACT_METAL_WATER_WALKING: return "ACT_METAL_WATER_WALKING"; case ACT_HOLD_METAL_WATER_WALKING: return "ACT_HOLD_METAL_WATER_WALKING"; case ACT_METAL_WATER_FALLING: return "ACT_METAL_WATER_FALLING"; case ACT_HOLD_METAL_WATER_FALLING: return "ACT_HOLD_METAL_WATER_FALLING"; case ACT_METAL_WATER_FALL_LAND: return "ACT_METAL_WATER_FALL_LAND"; case ACT_HOLD_METAL_WATER_FALL_LAND: return "ACT_HOLD_METAL_WATER_FALL_LAND"; case ACT_METAL_WATER_JUMP: return "ACT_METAL_WATER_JUMP"; case ACT_HOLD_METAL_WATER_JUMP: return "ACT_HOLD_METAL_WATER_JUMP"; case ACT_METAL_WATER_JUMP_LAND: return "ACT_METAL_WATER_JUMP_LAND"; case ACT_HOLD_METAL_WATER_JUMP_LAND: return "ACT_HOLD_METAL_WATER_JUMP_LAND"; // cutscene case ACT_DISAPPEARED: return "ACT_DISAPPEARED"; case ACT_INTRO_CUTSCENE: return "ACT_INTRO_CUTSCENE"; case ACT_STAR_DANCE_EXIT: return "ACT_STAR_DANCE_EXIT"; case ACT_STAR_DANCE_WATER: return "ACT_STAR_DANCE_WATER"; case ACT_FALL_AFTER_STAR_GRAB: return "ACT_FALL_AFTER_STAR_GRAB"; case ACT_READING_AUTOMATIC_DIALOG: return "ACT_READING_AUTOMATIC_DIALOG"; case ACT_READING_NPC_DIALOG: return "ACT_READING_NPC_DIALOG"; case ACT_STAR_DANCE_NO_EXIT: return "ACT_STAR_DANCE_NO_EXIT"; case ACT_READING_SIGN: return "ACT_READING_SIGN"; case ACT_JUMBO_STAR_CUTSCENE: return "ACT_JUMBO_STAR_CUTSCENE"; case ACT_WAITING_FOR_DIALOG: return "ACT_WAITING_FOR_DIALOG"; case ACT_DEBUG_FREE_MOVE: return "ACT_DEBUG_FREE_MOVE"; case ACT_STANDING_DEATH: return "ACT_STANDING_DEATH"; case ACT_QUICKSAND_DEATH: return "ACT_QUICKSAND_DEATH"; case ACT_ELECTROCUTION: return "ACT_ELECTROCUTION"; case ACT_SUFFOCATION: return "ACT_SUFFOCATION"; case ACT_DEATH_ON_STOMACH: return "ACT_DEATH_ON_STOMACH"; case ACT_DEATH_ON_BACK: return "ACT_DEATH_ON_BACK"; case ACT_EATEN_BY_BUBBA: return "ACT_EATEN_BY_BUBBA"; case ACT_END_PEACH_CUTSCENE: return "ACT_END_PEACH_CUTSCENE"; case ACT_CREDITS_CUTSCENE: return "ACT_CREDITS_CUTSCENE"; case ACT_END_WAVING_CUTSCENE: return "ACT_END_WAVING_CUTSCENE"; case ACT_PULLING_DOOR: return "ACT_PULLING_DOOR"; case ACT_PUSHING_DOOR: return "ACT_PUSHING_DOOR"; case ACT_WARP_DOOR_SPAWN: return "ACT_WARP_DOOR_SPAWN"; case ACT_EMERGE_FROM_PIPE: return "ACT_EMERGE_FROM_PIPE"; case ACT_SPAWN_SPIN_AIRBORNE: return "ACT_SPAWN_SPIN_AIRBORNE"; case ACT_SPAWN_SPIN_LANDING: return "ACT_SPAWN_SPIN_LANDING"; case ACT_EXIT_AIRBORNE: return "ACT_EXIT_AIRBORNE"; case ACT_EXIT_LAND_SAVE_DIALOG: return "ACT_EXIT_LAND_SAVE_DIALOG"; case ACT_DEATH_EXIT: return "ACT_DEATH_EXIT"; case ACT_UNUSED_DEATH_EXIT: return "ACT_UNUSED_DEATH_EXIT"; case ACT_FALLING_DEATH_EXIT: return "ACT_FALLING_DEATH_EXIT"; case ACT_SPECIAL_EXIT_AIRBORNE: return "ACT_SPECIAL_EXIT_AIRBORNE"; case ACT_SPECIAL_DEATH_EXIT: return "ACT_SPECIAL_DEATH_EXIT"; case ACT_FALLING_EXIT_AIRBORNE: return "ACT_FALLING_EXIT_AIRBORNE"; case ACT_UNLOCKING_KEY_DOOR: return "ACT_UNLOCKING_KEY_DOOR"; case ACT_UNLOCKING_STAR_DOOR: return "ACT_UNLOCKING_STAR_DOOR"; case ACT_ENTERING_STAR_DOOR: return "ACT_ENTERING_STAR_DOOR"; case ACT_SPAWN_NO_SPIN_AIRBORNE: return "ACT_SPAWN_NO_SPIN_AIRBORNE"; case ACT_SPAWN_NO_SPIN_LANDING: return "ACT_SPAWN_NO_SPIN_LANDING"; case ACT_BBH_ENTER_JUMP: return "ACT_BBH_ENTER_JUMP"; case ACT_BBH_ENTER_SPIN: return "ACT_BBH_ENTER_SPIN"; case ACT_TELEPORT_FADE_OUT: return "ACT_TELEPORT_FADE_OUT"; case ACT_TELEPORT_FADE_IN: return "ACT_TELEPORT_FADE_IN"; case ACT_SHOCKED: return "ACT_SHOCKED"; case ACT_SQUISHED: return "ACT_SQUISHED"; case ACT_HEAD_STUCK_IN_GROUND: return "ACT_HEAD_STUCK_IN_GROUND"; case ACT_BUTT_STUCK_IN_GROUND: return "ACT_BUTT_STUCK_IN_GROUND"; case ACT_FEET_STUCK_IN_GROUND: return "ACT_FEET_STUCK_IN_GROUND"; case ACT_PUTTING_ON_CAP: return "ACT_PUTTING_ON_CAP"; // automatic case ACT_HOLDING_POLE: return "ACT_HOLDING_POLE"; case ACT_GRAB_POLE_SLOW: return "ACT_GRAB_POLE_SLOW"; case ACT_GRAB_POLE_FAST: return "ACT_GRAB_POLE_FAST"; case ACT_CLIMBING_POLE: return "ACT_CLIMBING_POLE"; case ACT_TOP_OF_POLE_TRANSITION: return "ACT_TOP_OF_POLE_TRANSITION"; case ACT_TOP_OF_POLE: return "ACT_TOP_OF_POLE"; case ACT_START_HANGING: return "ACT_START_HANGING"; case ACT_HANGING: return "ACT_HANGING"; case ACT_HANG_MOVING: return "ACT_HANG_MOVING"; case ACT_LEDGE_GRAB: return "ACT_LEDGE_GRAB"; case ACT_LEDGE_CLIMB_SLOW_1: return "ACT_LEDGE_CLIMB_SLOW_1"; case ACT_LEDGE_CLIMB_SLOW_2: return "ACT_LEDGE_CLIMB_SLOW_2"; case ACT_LEDGE_CLIMB_DOWN: return "ACT_LEDGE_CLIMB_DOWN"; case ACT_LEDGE_CLIMB_FAST: return "ACT_LEDGE_CLIMB_FAST"; case ACT_GRABBED: return "ACT_GRABBED"; case ACT_IN_CANNON: return "ACT_IN_CANNON"; case ACT_TORNADO_TWIRLING: return "ACT_TORNADO_TWIRLING"; // object case ACT_PUNCHING: return "ACT_PUNCHING"; case ACT_PICKING_UP: return "ACT_PICKING_UP"; case ACT_DIVE_PICKING_UP: return "ACT_DIVE_PICKING_UP"; case ACT_STOMACH_SLIDE_STOP: return "ACT_STOMACH_SLIDE_STOP"; case ACT_PLACING_DOWN: return "ACT_PLACING_DOWN"; case ACT_THROWING: return "ACT_THROWING"; case ACT_HEAVY_THROW: return "ACT_HEAVY_THROW"; case ACT_PICKING_UP_BOWSER: return "ACT_PICKING_UP_BOWSER"; case ACT_HOLDING_BOWSER: return "ACT_HOLDING_BOWSER"; case ACT_RELEASING_BOWSER: return "ACT_RELEASING_BOWSER"; default: return "ACT_UNKNOWN"; } } // ------------------------------------------------------- // All helpers defined BEFORE DrawMarioState static void DrawBitFlags( const char *label, uint32_t value, const FlagEntry *table, int count) { ImGui::Text("%s: 0x%08X", label, value); ImGui::Indent(); for (int i = 0; i < count; i++) { bool set = (value & table[i].bit) != 0; ImGui::TextColored( set ? ImVec4(0.2f, 1.0f, 0.4f, 1.0f) : ImVec4(0.4f, 0.4f, 0.4f, 1.0f), "[%c] %s", set ? 'X' : ' ', table[i].name); } ImGui::Unindent(); } static void DrawAnimInfo(const AnimInfo *anim) { if (!anim) { ImGui::TextDisabled("animInfo: (null)"); return; } if (ImGui::TreeNode("animInfo")) { ImGui::Text("animID: %d", anim->animID); ImGui::Text("animYTrans: %d", anim->animYTrans); ImGui::Text("curAnim: %p", (void *)anim->curAnim); ImGui::Text("animFrame: %d", anim->animFrame); ImGui::Text("animTimer: %u", anim->animTimer); ImGui::Text("animFrameAccelAssist: %d", anim->animFrameAccelAssist); ImGui::Text("animAccel: %d", anim->animAccel); ImGui::TreePop(); } } static void DrawMarioState(const SM64MarioState *m) { ImGui::Begin("Mario State"); if (ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Text("Position: (%.2f, %.2f, %.2f)", m->position[0], m->position[1], m->position[2]); ImGui::Text("Velocity: (%.2f, %.2f, %.2f)", m->velocity[0], m->velocity[1], m->velocity[2]); ImGui::Text("Face Angle: %.4f rad (%.1f deg)", m->faceAngle, m->faceAngle * (180.0f / 3.14159265f)); ImGui::Text("Fwd Velocity: %.2f", m->forwardVelocity); } if (ImGui::CollapsingHeader("Status", ImGuiTreeNodeFlags_DefaultOpen)) { int hpSegs = (m->health & 0xFF00) >> 8; ImGui::Text("Health: 0x%04X (HP segments: %d / 8)", (uint16_t)m->health, hpSegs); ImGui::ProgressBar(hpSegs / 8.0f, ImVec2(-1, 0)); ImGui::Text("Invinc Timer: %d", m->invincTimer); } if (ImGui::CollapsingHeader("Action", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Text("0x%08X (%s)", m->action, GetActionName(m->action)); ImGui::Indent(); // group switch (m->action & ACT_GROUP_MASK) { case ACT_GROUP_STATIONARY: ImGui::TextDisabled("group: STATIONARY"); break; case ACT_GROUP_MOVING: ImGui::TextColored({0.2f, 1.0f, 0.4f, 1.0f}, "group: MOVING"); break; case ACT_GROUP_AIRBORNE: ImGui::TextColored({0.2f, 0.8f, 1.0f, 1.0f}, "group: AIRBORNE"); break; case ACT_GROUP_SUBMERGED: ImGui::TextColored({0.2f, 0.4f, 1.0f, 1.0f}, "group: SUBMERGED"); break; case ACT_GROUP_CUTSCENE: ImGui::TextColored({1.0f, 0.8f, 0.2f, 1.0f}, "group: CUTSCENE"); break; case ACT_GROUP_AUTOMATIC: ImGui::TextColored({0.8f, 0.4f, 1.0f, 1.0f}, "group: AUTOMATIC"); break; case ACT_GROUP_OBJECT: ImGui::TextColored({1.0f, 0.5f, 0.2f, 1.0f}, "group: OBJECT"); break; } // relevant flags if (m->action & ACT_FLAG_AIR) ImGui::Text("[x] ACT_FLAG_AIR"); if (m->action & ACT_FLAG_SWIMMING) ImGui::Text("[x] ACT_FLAG_SWIMMING"); if (m->action & ACT_FLAG_ATTACKING) ImGui::Text("[x] ACT_FLAG_ATTACKING"); if (m->action & ACT_FLAG_INVULNERABLE) ImGui::Text("[x] ACT_FLAG_INVULNERABLE"); if (m->action & ACT_FLAG_INTANGIBLE) ImGui::Text("[x] ACT_FLAG_INTANGIBLE"); if (m->action & ACT_FLAG_RIDING_SHELL) ImGui::Text("[x] ACT_FLAG_RIDING_SHELL"); if (m->action & ACT_FLAG_ON_POLE) ImGui::Text("[x] ACT_FLAG_ON_POLE"); if (m->action & ACT_FLAG_HANGING) ImGui::Text("[x] ACT_FLAG_HANGING"); if (m->action & ACT_FLAG_DIVING) ImGui::Text("[x] ACT_FLAG_DIVING"); if (m->action & ACT_FLAG_SWIMMING_OR_FLYING)ImGui::Text("[x] ACT_FLAG_SWIMMING_OR_FLYING"); if (m->action & ACT_FLAG_IDLE) ImGui::Text("[x] ACT_FLAG_IDLE"); if (m->action & ACT_FLAG_THROWING) ImGui::Text("[x] ACT_FLAG_THROWING"); ImGui::Unindent(); } if (ImGui::CollapsingHeader("Flags")) DrawBitFlags("flags", m->flags, sMarioFlags, (int)(sizeof(sMarioFlags) / sizeof(sMarioFlags[0]))); if (ImGui::CollapsingHeader("Particle Flags")) DrawBitFlags("particleFlags", m->particleFlags, sParticleFlags, (int)(sizeof(sParticleFlags) / sizeof(sParticleFlags[0]))); if (ImGui::CollapsingHeader("Animation")) DrawAnimInfo(m->animInfo); ImGui::End(); } static SDL_Haptic *gHaptic = nullptr; static void InitHaptics(SDL_GameController *controller) { if (controller == nullptr || gHaptic != nullptr) { return; } SDL_Joystick *joy = SDL_GameControllerGetJoystick(controller); if (joy == nullptr) { return; } gHaptic = SDL_HapticOpenFromJoystick(joy); if (gHaptic == nullptr) { return; } if (SDL_HapticRumbleInit(gHaptic) != 0) { SDL_HapticClose(gHaptic); gHaptic = nullptr; } } uint8_t *utils_read_file_alloc(const char *path, size_t *fileLength) { FILE *f = fopen(path, "rb"); if (!f) return nullptr; fseek(f, 0, SEEK_END); size_t length = (size_t)ftell(f); rewind(f); uint8_t *buffer = (uint8_t *)malloc(length + 1); fread(buffer, 1, length, f); buffer[length] = 0; fclose(f); if (fileLength) *fileLength = length; return buffer; } static float read_axis(int16_t val) { float result = (float)val / 32767.0f; if (result < 0.2f && result > -0.2f) return 0.0f; return result > 0.0f ? (result - 0.2f) / 0.8f : (result + 0.2f) / 0.8f; } float lerp(float a, float b, float amount) { return a + (b - a) * amount; } static void DebugPrint(const char *message) { if (message == nullptr) { return; } printf("[SM64] %s\n", message); fflush(stdout); } static void Rumble(int32_t marioId, int16_t level, int16_t time) { if (gHaptic == nullptr) { return; } float strength = (float)level; Uint32 duration = (Uint32)time * 2; SDL_HapticRumblePlay(gHaptic, strength, duration); } int main(void) { size_t romSize; uint8_t *rom = utils_read_file_alloc("baserom.us.z64", &romSize); if (rom == nullptr) { printf("\nFailed to read ROM file \"baserom.us.z64\"\n\n"); return 1; } uint8_t *texture = (uint8_t *)malloc(4 * SM64_TEXTURE_WIDTH * SM64_TEXTURE_HEIGHT); sm64_global_terminate(); sm64_global_init(rom, texture); sm64_audio_init(rom); // sm64_register_debug_print_function(DebugPrint); sm64_register_rumble_callback_function(Rumble); sm64_static_surfaces_load(surfaces, surfaces_count); int32_t marioId = sm64_mario_create(-6558, 0, 6464); printf("MarioID: %i\n", marioId); free(rom); RenderState renderState; renderState.mario.index = nullptr; vec3 cameraPos = {0, 0, 0}; float cameraRot = 0.0f; struct Renderer *renderer; int major, minor; #ifdef GL33_CORE major = 3; minor = 3; renderer = &gl33core_renderer; #else major = 2; minor = 0; renderer = &gl20_renderer; #endif context_init("libsm64", 1280, 720, major, minor); renderer->init(&renderState, texture); IMGUI_CHECKVERSION(); ImGui::CreateContext(); // Setup Dear ImGui style ImGui::StyleColorsDark(); // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForOpenGL(context_get_window(), context_get_gl_context()); ImGui_ImplOpenGL3_Init("#version 130"); // Use #version 130 for OpenGL 3.0+ struct SM64MarioInputs marioInputs; struct SM64MarioState marioState; struct SM64MarioGeometryBuffers marioGeometry; // interpolation float lastPos[3], currPos[3]; float lastGeoPos[9 * SM64_GEO_MAX_TRIANGLES], currGeoPos[9 * SM64_GEO_MAX_TRIANGLES]; marioGeometry.position = (float *)malloc(sizeof(float) * 9 * SM64_GEO_MAX_TRIANGLES); marioGeometry.color = (float *)malloc(sizeof(float) * 9 * SM64_GEO_MAX_TRIANGLES); marioGeometry.normal = (float *)malloc(sizeof(float) * 9 * SM64_GEO_MAX_TRIANGLES); marioGeometry.uv = (float *)malloc(sizeof(float) * 6 * SM64_GEO_MAX_TRIANGLES); marioGeometry.numTrianglesUsed = 0; float tick = 0; uint32_t lastTicks = SDL_GetTicks(); audio_init(); sm64_play_music(0, 0x03, 0); // from decomp/include/seq_ids.h: SEQ_LEVEL_WATER | SEQ_VARIATION do { // --- ImGui frame setup --- ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); // --- Define ImGui content --- // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). ImGui::SetNextWindowCollapsed(true, ImGuiCond_Appearing); ImGui::ShowDemoWindow(); // --- Game logic and input --- float dt = (SDL_GetTicks() - lastTicks) / 1000.f; lastTicks = SDL_GetTicks(); tick += dt; SDL_GameController *controller = context_get_controller(); InitHaptics(controller); float x_axis, y_axis, x0_axis; if (!controller) // keyboard { const Uint8 *state = SDL_GetKeyboardState(nullptr); float dir; float spd = 0; if (state[SDL_SCANCODE_UP] && state[SDL_SCANCODE_RIGHT]) { dir = -M_PI * 0.25f; spd = 1; } else if (state[SDL_SCANCODE_UP] && state[SDL_SCANCODE_LEFT]) { dir = -M_PI * 0.75f; spd = 1; } else if (state[SDL_SCANCODE_DOWN] && state[SDL_SCANCODE_RIGHT]) { dir = M_PI * 0.25f; spd = 1; } else if (state[SDL_SCANCODE_DOWN] && state[SDL_SCANCODE_LEFT]) { dir = M_PI * 0.75f; spd = 1; } else if (state[SDL_SCANCODE_UP]) { dir = -M_PI * 0.5f; spd = 1; } else if (state[SDL_SCANCODE_DOWN]) { dir = M_PI * 0.5f; spd = 1; } else if (state[SDL_SCANCODE_LEFT]) { dir = M_PI; spd = 1; } else if (state[SDL_SCANCODE_RIGHT]) { dir = 0; spd = 1; } x_axis = cosf(dir) * spd; y_axis = sinf(dir) * spd; x0_axis = state[SDL_SCANCODE_LSHIFT] ? 1 : state[SDL_SCANCODE_RSHIFT] ? -1 : 0; marioInputs.buttonA = state[SDL_SCANCODE_X]; marioInputs.buttonB = state[SDL_SCANCODE_C]; marioInputs.buttonZ = state[SDL_SCANCODE_Z]; } else { x_axis = read_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)); y_axis = read_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)); x0_axis = read_axis(SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)); marioInputs.buttonA = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_A); marioInputs.buttonB = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_X); marioInputs.buttonZ = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); } cameraRot += x0_axis * dt * 2; cameraPos[0] = marioState.position[0] + 1000.0f * cosf(cameraRot); cameraPos[1] = marioState.position[1] + 200.0f; cameraPos[2] = marioState.position[2] + 1000.0f * sinf(cameraRot); marioInputs.camLookX = marioState.position[0] - cameraPos[0]; marioInputs.camLookZ = marioState.position[2] - cameraPos[2]; marioInputs.stickX = x_axis; marioInputs.stickY = y_axis; while (tick >= 1.f / 30) { memcpy(lastPos, currPos, sizeof(currPos)); memcpy(lastGeoPos, currGeoPos, sizeof(currGeoPos)); tick -= 1.f / 30; sm64_mario_tick(marioId, &marioInputs, &marioState, &marioGeometry); memcpy(currPos, marioState.position, sizeof(currPos)); memcpy(currGeoPos, marioGeometry.position, sizeof(currGeoPos)); } for (int i = 0; i < 3; i++) marioState.position[i] = lerp(lastPos[i], currPos[i], tick / (1.f / 30)); for (int i = 0; i < marioGeometry.numTrianglesUsed * 9; i++) marioGeometry.position[i] = lerp(lastGeoPos[i], currGeoPos[i], tick / (1.f / 30)); // --- Game Rendering --- renderer->draw(&renderState, cameraPos, &marioState, &marioGeometry); DrawMarioState(&marioState); // --- ImGui Rendering --- ImGui::Render(); // Prepare draw data ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); // Render ImGui draw data } while (context_flip_frame_poll_events()); sm64_stop_background_music(sm64_get_current_background_music()); sm64_global_terminate(); ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); context_terminate(); if (gHaptic != nullptr) { SDL_HapticClose(gHaptic); gHaptic = nullptr; } return 0; }