From 5f9df00adb7c63c7424ff10aba0fb37a40d3af67 Mon Sep 17 00:00:00 2001 From: ManuCorralPazos Date: Mon, 5 Oct 2026 14:01:20 +0200 Subject: [PATCH 1/3] Match `KartState::init` Match the 56-byte function at 0x8059455C. Verified with Objdiff at 100.0% and: ninja all_source progress build/RMCP01/report.json AI assistance: Used an AI coding assistant for build-environment troubleshooting, report/candidate analysis, target-assembly interpretation, and drafting the implementation. I reviewed the change and independently verified the final match and full local build. --- src/kart/KartState.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/kart/KartState.cpp b/src/kart/KartState.cpp index c405564a..9827c415 100644 --- a/src/kart/KartState.cpp +++ b/src/kart/KartState.cpp @@ -56,4 +56,10 @@ KartState::KartState(KartSettings* settings) { set(KART_FLAG_DEMO_LOSS); } } + +void KartState::init() { + reset(); + mWipeState = -1; + mWipeFrame = -1; +} } From d282d2ded024466ecca98a03863d16e33a896d17 Mon Sep 17 00:00:00 2001 From: ManuCorralPazos Date: Mon, 5 Oct 2026 17:25:10 +0200 Subject: [PATCH 2/3] Improve KartCollide::checkNeighborhood Enable and refine the existing partial implementation. Objdiff: 77.65% -> 99.60% Function size: 0x224 / 548 bytes (matches target). Original partial implementation by em-eight (7974069c). Known remaining mismatch: equivalent float constants use a different read-only-data layout. The target uses @5034 and lbl_1_rodata_2020; the current build uses anonymous local literals at different offsets. AI-assisted: OpenAI Codex was used for assembly analysis, source-shape experiments, and Ninja/Objdiff build iteration. --- src/kart/KartCollide.cpp | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/kart/KartCollide.cpp b/src/kart/KartCollide.cpp index 160a00d2..ef12f3f9 100644 --- a/src/kart/KartCollide.cpp +++ b/src/kart/KartCollide.cpp @@ -3,6 +3,8 @@ extern "C" EGG::Vector3f RKSystem_ex; +extern u32* lbl_1_rodata_16FAC; + using namespace Field; namespace Kart { @@ -208,40 +210,44 @@ bool KartCollide::processWall(KartCollisionInfo& kartColInfo, const Field::ColIn } } -# ifdef NON_MATCHING namespace Kart { void KartCollide::checkNeighborhood(KartCollisionInfo& kartColInfo, const Hitbox& hitbox, const Field::ColInfo& colInfo) { f32 colPerp = colInfo.colPerpendicularity; - if (colPerp> 0.0f) { - if (_68 < colPerp) { + if (colPerp > 0.0f) { + if (colPerp > _68) { _68 = colPerp; } - if ((kartColInfo.flags & (COL_FLAG_WALL_AT_LEFT_CLOSER | COL_FLAG_WALL_AT_RIGHT_CLOSER)) != 0) { - if (fabs(hitbox.bsp->pos.x) > 10.0f) { - if (colPerp > 0.0f) { - kartColInfo.flags |= COL_FLAG_WALL_AT_LEFT_CLOSER; - } else { - kartColInfo.flags |= COL_FLAG_WALL_AT_RIGHT_CLOSER; - } + if ((kartColInfo.flags & (COL_FLAG_WALL_AT_LEFT_CLOSER | COL_FLAG_WALL_AT_RIGHT_CLOSER)) == 0) { + if (fabsf(hitbox.bsp->pos.x) > 10.0f) { + if (hitbox.bsp->pos.x > 0.0f) { + ((vu32&)kartColInfo.flags) |= COL_FLAG_WALL_AT_RIGHT_CLOSER; + } else { + ((vu32&)kartColInfo.flags) |= COL_FLAG_WALL_AT_LEFT_CLOSER; + } kartColInfo.colPerpendicularity = colInfo.colPerpendicularity; } else { - EGG::Vector3f localRight; - kartDynamics()->mainRot.rotateVector(RKSystem_ex, localRight); - f32 offs[2]; + EGG::Vector3f localRight; + kartDynamics()->mainRot.rotateVector(RKSystem_ex, localRight); + u32* typeMaskOut = lbl_1_rodata_16FAC; + const EGG::Vector3f* lastPos = &hitbox.lastPos; + f32 offs[2] = {0.0f, 0.0f}; f32 sign; - for (s32 i = 0; i < 2; i++) { + for (u8 i = 0; i < 2; i++) { if (i == 1) { sign = -1.0f; } else { sign = 1.0f; } sign *= hitbox.radius; - EGG::Vector3f offset = hitbox.pos + localRight * sign; + EGG::Vector3f offset; + offset.x = hitbox.pos.x + sign * localRight.x; + offset.y = hitbox.pos.y + sign * localRight.y; + offset.z = hitbox.pos.z + sign * localRight.z; ColInfoPartial tmpInfo; tmpInfo.bboxLow.setZero(); tmpInfo.bboxHigh.setZero(); - if (CourseModel::spInstance->checkSphereCachedPartial(offset, hitbox.lastPos, KCL_TYPE_DRIVER_WALL, &tmpInfo, &lbl_808a66cc, hitbox.radius, 0)) { + if (CourseModel::spInstance->checkSphereCachedPartial(offset, *lastPos, KCL_TYPE_DRIVER_WALL, &tmpInfo, typeMaskOut, hitbox.radius, 0)) { offs[i] = colInfo.tangentOff.lenSq(); } } @@ -258,7 +264,6 @@ void KartCollide::checkNeighborhood(KartCollisionInfo& kartColInfo, const Hitbox } } } -#endif namespace Kart { void KartCollide::processCannon(u32* colTypeMask) { From bf6a07ae3f0d5c740b7a8e8c11bdc53f8c5abd5e Mon Sep 17 00:00:00 2001 From: ManuCorralPazos Date: Mon, 5 Oct 2026 19:05:30 +0200 Subject: [PATCH 3/3] Decompile `RaceinfoPlayer_getLapSplit` Objdiff: 98.910255% (312/312 bytes). AI-assisted: OpenAI Codex was used for target-assembly analysis, source-shape experiments, and Ninja/Objdiff build iteration. --- src/system/GhostFile.hpp | 18 +++++++++++++++++ src/system/RaceManager.cpp | 40 ++++++++++++++++++++++++++++++++++++++ src/system/RaceManager.hpp | 1 + 3 files changed, 59 insertions(+) diff --git a/src/system/GhostFile.hpp b/src/system/GhostFile.hpp index a6d5e01d..363055a5 100644 --- a/src/system/GhostFile.hpp +++ b/src/system/GhostFile.hpp @@ -55,6 +55,24 @@ class Time { mIsValid = isValid; } + void copyFrom(const Time& other) { + mMinutes = other.mMinutes; + mSeconds = other.mSeconds; + mMilliseconds = other.mMilliseconds; + mIsValid = other.mIsValid; + } + + void setTime(u16 minutes, u8 seconds, u16 milliseconds) { + mMinutes = minutes; + mSeconds = seconds; + mMilliseconds = milliseconds; + } + + u16 minutes() const { return mMinutes; } + u8 seconds() const { return mSeconds; } + u16 milliseconds() const { return mMilliseconds; } + bool isValid() const { return mIsValid; } + private: u16 mMinutes; u8 mSeconds; diff --git a/src/system/RaceManager.cpp b/src/system/RaceManager.cpp index 1a3a5922..75d58cb4 100644 --- a/src/system/RaceManager.cpp +++ b/src/system/RaceManager.cpp @@ -127,6 +127,46 @@ KrtFile** RaceManager::getKrtFile() { return (files[0] != nullptr) ? files : nullptr; } } + +extern "C" void RaceinfoPlayer_getLapSplit(RaceManagerPlayer* player, u32 lap, Time* split) { + if (lap < 2) { + split->copyFrom(*player->lapFinishTimes); + return; + } + + if (RaceManager::spInstance->timerManager->hasSpecialTimerState()) { + split->set(99, 59, 999, true); + return; + } + + u32 earlierLap = lap - 2; + u32 laterLap = lap - 1; + s32 seconds = 0; + s32 minutes = 0; + const Time* earlier = player->lapFinishTimes + earlierLap; + const Time* later = player->lapFinishTimes + laterLap; + s32 milliseconds = later->milliseconds() - earlier->milliseconds(); + if (milliseconds < 0) { + seconds = -1; + milliseconds += 1000; + } + seconds += later->seconds() - earlier->seconds(); + if (seconds < 0) { + minutes = -1; + seconds += 60; + } + minutes += later->minutes() - earlier->minutes(); + if (minutes < 0) { + minutes = 0; + seconds = 0; + milliseconds = 0; + } + + split->set(minutes, seconds, milliseconds, true); + if (!player->lapFinishTimes[laterLap].isValid() || !player->lapFinishTimes[earlierLap].isValid()) { + split->setTime(0xFFFF, 0, 0); + } +} } // needs KartObjectManager definitions diff --git a/src/system/RaceManager.hpp b/src/system/RaceManager.hpp index 49522509..07e10b9b 100644 --- a/src/system/RaceManager.hpp +++ b/src/system/RaceManager.hpp @@ -78,6 +78,7 @@ class TimerManagerBase { virtual ~TimerManagerBase(); virtual void reset(); virtual void update(); + bool hasSpecialTimerState() const { return static_cast(field26_0x40) != 0; } protected: Time timer1;