# Code patches applied by the recompiler (tools/recomp/gcrecomp.py): ADDRESS before|after C-STATEMENT.
# Addresses are this build's: main.dol as is, the REL at REL_BASE 0x81800000 (original runtime
# address - 0x803FBD00 + 0x81800000, the module having been loaded at 0x803FBD00).

# ---- Widescreen (any aspect): the camera projections' aspect ratio times g_aspect_scale
# (target aspect / (4/3); 1 = the original 4:3). The native form of gamemasterplc's "16:9 Widescreen"
# Gecko code for GXEE8P (Dolphin wiki, Sonic Riders), which multiplies by 0.6666667f then by 2.
decl extern volatile double g_aspect_scale;
# projection setup (fn_800645DC): f1 is the aspect ratio on entry
800645DC before { double k = g_aspect_scale; if (k != 1.0) { double v = to_single(c->f[1].ps0 * k); c->f[1].ps0 = v; c->f[1].ps1 = v; } }
# race camera (REL text 0x14115C, originally 0x8053CF18): the aspect ratio computed by this fmadds
81941218 after { double k = g_aspect_scale; if (k != 1.0) { double v = to_single(c->f[1].ps0 * k); c->f[1].ps0 = v; c->f[1].ps1 = v; } }

# ---- Diagnostics
decl void rt_log(const char* fmt, ...);
# the DVD library's command timeout fired (AlarmHandlerForTimeout): the drive model completed a
# command too late, or before the SDK armed this alarm (see runtime/src/di.c)
80012398 before { rt_log("[dvd] the SDK's drive timeout alarm fired"); }
# thread suspend/resume and DVD event ring for hang dumps (runtime/src/sched.c dbg_event)
decl void dbg_event(int kind, uint32_t a, uint32_t b);
80010B08 before { dbg_event(1, c->r[3], c->lr); }
80010D90 before { dbg_event(2, c->r[3], c->lr); }

# ---- SR_DETCLOCK (online play groundwork, runtime/src/vi.c): the frame end's 30 fps mode busy-waits on
# the retrace count (loop 800061C8-800061D0, no calls); mark it so a retrace is handed over exactly there
# The loop heads hand over what is due themselves (vi.c vi_det_loop_point, on the game thread): a host
# thread checking the marks could act just as the loop ends (a thread resumed with its data there).
decl void vi_det_loop_point(void);
decl extern volatile int g_det_spin;
800061C8 before { g_det_spin = 1; vi_det_loop_point(); }
800061D4 before { g_det_spin = 0; }
# the REL's streaming loader thread (fn 819594F4) busy-waits in 11 loops for the bytes-arrived counter of
# the chunk it decompresses; mark them (head: this thread waits; exit and return: no longer) so a retrace
# may be handed over while it waits there, not while it decompresses
decl extern volatile int g_det_lspin;
81959544 before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
81959564 before { g_det_lspin = 0; }
8195976C before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
8195978C before { g_det_lspin = 0; }
81959844 before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
81959868 before { g_det_lspin = 0; }
819598C8 before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
819598E8 before { g_det_lspin = 0; }
81959938 before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
8195995C before { g_det_lspin = 0; }
81959A18 before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
81959A38 before { g_det_lspin = 0; }
81959A88 before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
81959AAC before { g_det_lspin = 0; }
81959B30 before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
81959B50 before { g_det_lspin = 0; }
81959BA4 before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
81959BC4 before { g_det_lspin = 0; }
81959C2C before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
81959C54 before { g_det_lspin = 0; }
81959D1C before { g_det_lspin = (int)ld32(0x800000E4u); vi_det_loop_point(); }
81959D44 before { g_det_lspin = 0; }
81959DA4 before { g_det_lspin = 0; }
# ---- Ring cap (settings.c g_ring_cap: 999 by default, 100 = the original). The game holds a racer's rings
# in a word at racer + 0xB98 (racers 0x1080 apart from 0x81A0D740) and caps it at 100 in five places:
decl extern volatile int g_ring_cap;
# three copies of the per-frame racer loop: `if (rings > 100) rings = 100` (r3 + r6 = the ring word, r5 the
# value stored): with another cap the stored value is min(rings, cap)
81831A78 after { int32_t v = (int32_t)ld32(c->r[3] + c->r[6]); c->r[5] = (uint32_t)(v > g_ring_cap ? g_ring_cap : v); }
8182B828 after { int32_t v = (int32_t)ld32(c->r[3] + c->r[6]); c->r[5] = (uint32_t)(v > g_ring_cap ? g_ring_cap : v); }
81835F28 after { int32_t v = (int32_t)ld32(c->r[3] + c->r[6]); c->r[5] = (uint32_t)(v > g_ring_cap ? g_ring_cap : v); }
# a branchless min(rings, 100) (li 100, then rings - 100): both constants become the cap
81815CA8 after { c->r[0] = (uint32_t)g_ring_cap; }
81815CB0 after { c->r[4] = c->r[5] - (uint32_t)g_ring_cap; }
# the HUD's rolling ring counter counts up toward min(rings, 100) (rings in r0, from racer r21 / r21 / r26)
8182BC20 after { uint32_t v = ld32(c->r[21] + 0xB98u); c->r[0] = v > (uint32_t)g_ring_cap ? (uint32_t)g_ring_cap : v; }
81830628 after { uint32_t v = ld32(c->r[21] + 0xB98u); c->r[0] = v > (uint32_t)g_ring_cap ? (uint32_t)g_ring_cap : v; }
81831CF8 after { uint32_t v = ld32(c->r[26] + 0xB98u); c->r[0] = v > (uint32_t)g_ring_cap ? (uint32_t)g_ring_cap : v; }
# a hit (an opponent boosting into the racer, fn 81849394) sets the rings to 0 (r0, stored at 81849410; r30 = the
# rings before, also the count of rings scattered, at most 8): with the 999 cap a hit takes 100 rings, not all
81849404 after { if (g_ring_cap != 100) { int32_t v = (int32_t)c->r[30] - 100; c->r[0] = (uint32_t)(v > 0 ? v : 0); } }
# the 100-ring capsule (item switch case 8188A0E8, stored at 8188A0F0) sets the rings to 100, which with the
# original cap is the same as +100; with the 999 cap it adds 100 (up to the cap) so it never sets a racer back
8188A0E8 after { if (g_ring_cap != 100) { uint32_t v = ld32(c->r[21] + 0xB98u) + 100u; c->r[0] = v > (uint32_t)g_ring_cap ? (uint32_t)g_ring_cap : v; } }
# falling off the course: the respawn (fn 81817264) stores 0 (r6, which also clears other fields after it) as the
# rings; with the 999 cap a fall takes 100 rings like a hit (the old count kept before the store, rewritten after)
81817A58 before int32_t ring_before = (int32_t)ld32(c->r[25] + 0xB98u);
81817A58 after if (g_ring_cap != 100) st32(c->r[25] + 0xB98u, (uint32_t)(ring_before > 100 ? ring_before - 100 : 0));
