Skip to content

Fix championship mode progression (win recorded as loss) - #2

Merged
jessetbh merged 2 commits into
mainfrom
fix/1-championship-progression
Jul 22, 2026
Merged

Fix championship mode progression (win recorded as loss)#2
jessetbh merged 2 commits into
mainfrom
fix/1-championship-progression

Conversation

@jessetbh

@jessetbh jessetbh commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Fixes #1.

Symptom

In championship/story mode, every match was recorded as a loss regardless of the actual result — no prize money, and the story advanced down the failure branch. Confirmed on World Heavyweight and IC titles (matches the issue report; reproduced on Windows).

Root cause

A novel N64Recomp miscompile. The storyline-condition evaluator func_800EE1F0 dispatches through a jump table at 0x801036D0 that is indexed by a loop-carried induction pointer ($s2: base set once above the loop, +4 per iteration on the back-edge). With no in-block sll idx,2 for N64Recomp to pattern-match, it emitted a constant index (jr_addend = 0) — the only non-register addend in the whole build — collapsing the 5-way switch to case 0. The per-slot mask sequence {0x7, 0x3000, 0x70, 0xF00, 0x70000} degenerated to all 0x7, so the win condition could never match and the evaluator returned the lose/default node.

fix_switches does not catch this: the table is fully present; only the index is wrong.

Fix

New post-regen pipeline step tools/fix_jraddend.py. It detects the jr_addend_* = 0 signature and rewrites it to (uint32_t)ctx->rD - <table_base>, so the live induction pointer drives the dispatch. The (uint32_t) cast is required — the guest pointer is sign-extended to 64 bits, and a zero-extended base subtraction would overflow the switch into switch_error (that failure mode was hit and corrected during bring-up). Idempotent; fails loud. Registered after fix_switches in BUILDING.md, validate.yml, and the PR template.

The generated RecompiledFuncs/ is gitignored, so the fix ships as the tool — CI regenerates and applies fix_jraddend during the build. This PR's CI run exercises exactly that path.

Testing

  • Rebuilt locally; the evaluator now selects the correct masks (index 0–4 verified across the full range).
  • Played a championship match to a win: money awarded and the win storyline path taken (previously: crash after an interim broken revision, then correct after the cast fix).

Also

  • Bumps version to 0.1.1 for the patch release.
  • The same tool was ported to the sister repos as defense-in-depth (separate; all reported 0 affected sites — No Mercy was the only game hit).

jessetbh added 2 commits July 21, 2026 20:31
… jump table

Championship/story mode recorded every match as a loss regardless of the
actual result (no prize money, story took the failure branch). Root cause is
a novel N64Recomp miscompile: the storyline-condition evaluator func_800EE1F0
dispatches through a jump table (0x801036D0) indexed by a LOOP-CARRIED
INDUCTION POINTER (base set once above the loop, +4 per iteration). With no
in-block `sll idx,2` to pattern-match, N64Recomp emitted a constant index
(jr_addend = 0), collapsing the 5-way switch to case 0 -- the mask sequence
{0x7,0x3000,0x70,0xF00,0x70000} degenerated to {0x7,...}, so the win
condition never matched and the evaluator fell through to the lose node.

fix_switches does not catch this: the table is fully present, only the index
is wrong. New post-regen pipeline step tools/fix_jraddend.py detects the
`jr_addend_* = 0` signature (the only non-register addend N64Recomp emits) and
rewrites it to `(uint32_t)ctx->rD - <table_base>` -- the live induction pointer
drives the dispatch. The (uint32_t) cast is required: the guest pointer is
sign-extended to 64 bits, so a zero-extended base subtraction would overflow
the switch into switch_error. Idempotent; fails loud. Registered after
fix_switches in BUILDING.md, validate.yml, and the PR template.

The fix ships as the tool: RecompiledFuncs/ is generated (gitignored), so CI
regenerates and applies fix_jraddend during the release build.

Claude-Session: https://claude.ai/code/session_018PLyfT4WLQsgPdjQ3RguJH
Patch release carrying the championship-progression fix (#1).

Claude-Session: https://claude.ai/code/session_018PLyfT4WLQsgPdjQ3RguJH
@jessetbh
jessetbh merged commit 2a9e4eb into main Jul 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Championship mode does not progress correctly

1 participant