From 38247eb808113f09170b74a4f2ff3cbd787da113 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 24 Jul 2026 21:44:59 -0700 Subject: [PATCH 1/3] Fix the lbshadow bezier constant and lay out its .sdata2 pool The cubic bezier tangent wrote bez1 as (-4.0f * t) + 1.0f, but retail's .sdata2 holds +4.0f and contains no -4.0f at all, and the target emits fnmsubs where the negated form emits fmadds. Writing it as 1.0f - (4.0f * t) restores both, and drops the pool from eight constants to retail's seven. MWCC orders the literal pool by first appearance in the source, so the recovered constant alone reshuffles it. Add the usual sdata2_order anchor - as itzako, grrcruise and friends already do - to pin the pool to retail's order. .sdata2 goes from 86.79% to 94.34%; the remainder is a second 0.0f and 1.0f that retail holds separately, which one translation unit cannot reproduce. lbShadow_8000E9F0 keeps one two-instruction scheduling transposition (fnmsubs against the t2 multiply); every other function and .data are byte-identical to before. --- src/melee/lb/lbshadow.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/melee/lb/lbshadow.c b/src/melee/lb/lbshadow.c index 494f7814db..b774f6116a 100644 --- a/src/melee/lb/lbshadow.c +++ b/src/melee/lb/lbshadow.c @@ -1,5 +1,7 @@ #include "lbshadow.h" +#include + #include #include #include @@ -22,6 +24,32 @@ #include #include +/// @todo Emitted only to lay out the .sdata2 literal pool in retail order. +static void sdata2_order(void) +{ + (void) -3.0f; + (void) 4.0f; + (void) 1.0f; + (void) 3.0f; + (void) 2.0f; + (void) 0.5f; + (void) 0.0f; + (void) S32_TO_F32; + (void) 0.001f; + (void) 5000.0f; + (void) 0.45f; + (void) -0.45f; + (void) 1.2f; + (void) -1.1f; + (void) 0.5; + (void) 3.0; + (void) 0.0000010000001f; + (void) 100.0f; + (void) -1.0f; + (void) 128.0f; + (void) -128.0f; +} + void lbShadow_8000E9F0(Vec3* p, HSD_Spline* spline, f32 u) { Vec3* cp; @@ -54,7 +82,7 @@ void lbShadow_8000E9F0(Vec3* p, HSD_Spline* spline, f32 u) f32 t2, u_1, bez1, bez0, bez2; cp = &spline->cv[idx * 3]; t2 = 3.0F * (t * t); - bez1 = (-4.0F * t) + 1.0F; + bez1 = 1.0F - (4.0F * t); u_1 = t - 1.0F; bez0 = -3.0F * u_1 * u_1; bez1 = 3.0F * (bez1 + t2); From a2528021d6cb9d62e48c708d1a4560c4cb3c635d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 24 Jul 2026 22:36:56 -0700 Subject: [PATCH 2/3] Split lbshadow at its original TU boundary The spline tangent evaluator at 8000E9F0 shares no .sdata2 constants with the rest of lbshadow, while every later function draws from one shared pool: lbShadow_8000F38C reuses 0.001f and 1.2f from earlier functions, and 0.0f/1.0f each appear twice, once on either side of the boundary. One translation unit cannot emit those duplicates, so the original build compiled the evaluator separately. With the split, lbshadow's pool lays out in natural first-appearance order and drops the sdata2_order anchor entirely; lb_E9F0 keeps a private eight-constant anchor. Both .sdata2 sections are byte-identical to retail, lbshadow's other five functions are exact, and its .data/.sdata contents are unchanged. ground, grmutecity, and grbigblueroute compile byte-identically with the moved declaration. lbShadow_8000E9F0 keeps its two-instruction scheduling transposition and lbShadow_8000F38C still differs by one recycled zero init. --- config/GALE01/splits.txt | 8 ++- configure.py | 1 + src/melee/gr/grbigblueroute.c | 2 +- src/melee/gr/grmutecity.c | 2 +- src/melee/gr/ground.c | 2 +- src/melee/lb/lb_E9F0.c | 106 +++++++++++++++++++++++++++++++ src/melee/lb/lb_E9F0.h | 10 +++ src/melee/lb/lbshadow.c | 114 +--------------------------------- src/melee/lb/lbshadow.h | 1 - 9 files changed, 127 insertions(+), 119 deletions(-) create mode 100644 src/melee/lb/lb_E9F0.c create mode 100644 src/melee/lb/lb_E9F0.h diff --git a/config/GALE01/splits.txt b/config/GALE01/splits.txt index 018f6d769f..c615e11a17 100644 --- a/config/GALE01/splits.txt +++ b/config/GALE01/splits.txt @@ -45,11 +45,15 @@ melee/lb/lbvector.c: .sdata start:0x804D3720 end:0x804D3728 .sdata2 start:0x804D7AA8 end:0x804D7B30 +melee/lb/lb_E9F0.c: + .text start:0x8000E9F0 end:0x8000ED54 + .sdata2 start:0x804D7B30 end:0x804D7B58 + melee/lb/lbshadow.c: - .text start:0x8000E9F0 end:0x8000F9F8 + .text start:0x8000ED54 end:0x8000F9F8 .data start:0x803BA0D8 end:0x803BA150 .sdata start:0x804D3728 end:0x804D3740 - .sdata2 start:0x804D7B30 end:0x804D7BA0 + .sdata2 start:0x804D7B58 end:0x804D7BA0 melee/lb/lbspdisplay.c: .text start:0x8000F9F8 end:0x80014ABC diff --git a/configure.py b/configure.py index f3d3d21644..721a57131d 100755 --- a/configure.py +++ b/configure.py @@ -494,6 +494,7 @@ def MatchingFor(*versions): Object(Matching, "melee/lb/lb_00B0.c"), Object(Matching, "melee/lb/lb_00CE.c"), Object(Matching, "melee/lb/lbvector.c"), + Object(NonMatching, "melee/lb/lb_E9F0.c"), Object(NonMatching, "melee/lb/lbshadow.c"), Object(NonMatching, "melee/lb/lbspdisplay.c"), Object(Matching, "melee/lb/lbarq.c"), diff --git a/src/melee/gr/grbigblueroute.c b/src/melee/gr/grbigblueroute.c index 228ce0b259..c04472b751 100644 --- a/src/melee/gr/grbigblueroute.c +++ b/src/melee/gr/grbigblueroute.c @@ -17,7 +17,7 @@ #include "gr/types.h" #include "if/ifhazard.h" #include "lb/lb_00B0.h" -#include "lb/lbshadow.h" +#include "lb/lb_E9F0.h" #include "lb/lbspdisplay.h" #include "lb/lbvector.h" diff --git a/src/melee/gr/grmutecity.c b/src/melee/gr/grmutecity.c index 8738832865..85cd04fa64 100644 --- a/src/melee/gr/grmutecity.c +++ b/src/melee/gr/grmutecity.c @@ -20,7 +20,7 @@ #include "if/ifhazard.h" #include "lb/lb_00B0.h" #include "lb/lbaudio_ax.h" -#include "lb/lbshadow.h" +#include "lb/lb_E9F0.h" #include "lb/lbspdisplay.h" #include "lb/lbvector.h" #include "mp/mplib.h" diff --git a/src/melee/gr/ground.c b/src/melee/gr/ground.c index 552088c979..76c87876bf 100644 --- a/src/melee/gr/ground.c +++ b/src/melee/gr/ground.c @@ -32,7 +32,7 @@ #include "lb/lb_00B0.h" #include "lb/lbaudio_ax.h" #include "lb/lbdvd.h" -#include "lb/lbshadow.h" +#include "lb/lb_E9F0.h" #include "lb/lbspdisplay.h" #include "lb/lbvector.h" #include "mp/mpcoll.h" diff --git a/src/melee/lb/lb_E9F0.c b/src/melee/lb/lb_E9F0.c new file mode 100644 index 0000000000..b90f7a5ac4 --- /dev/null +++ b/src/melee/lb/lb_E9F0.c @@ -0,0 +1,106 @@ +#include "lb_E9F0.h" + +#include + +#include + +/// @todo Emitted only to lay out the .sdata2 literal pool in retail order. +static void sdata2_order(void) +{ + (void) -3.0f; + (void) 4.0f; + (void) 1.0f; + (void) 3.0f; + (void) 2.0f; + (void) 0.5f; + (void) 0.0f; + (void) S32_TO_F32; +} + +/// @todo Case 1 emits its @c fnmsubs after the @c t2 multiply. +void lbShadow_8000E9F0(Vec3* p, HSD_Spline* spline, f32 u) +{ + Vec3* cp; + s16 idx; + f32 t; + f32 orig_u; + + PAD_STACK(8); + + if (u < 0.0F || u > 1.0F) { + return; + } + + orig_u = u; + u *= spline->numcv - 1; + idx = (s16) u; + t = u - (f32) idx; + + switch (spline->type) { + case 0: + if (orig_u == 1.0F) { + idx -= 1; + } + cp = &spline->cv[idx]; + p->x = cp[1].x - cp[0].x; + p->y = cp[1].y - cp[0].y; + p->z = cp[1].z - cp[0].z; + return; + case 1: { + f32 t2, u_1, bez1, bez0, bez2; + cp = &spline->cv[idx * 3]; + t2 = 3.0F * (t * t); + bez1 = 1.0F - (4.0F * t); + u_1 = t - 1.0F; + bez0 = -3.0F * u_1 * u_1; + bez1 = 3.0F * (bez1 + t2); + bez2 = 3.0F * ((2.0F * t) - t2); + p->x = (cp[3].x * t2) + + ((cp[2].x * bez2) + ((cp[0].x * bez0) + (cp[1].x * bez1))); + p->y = (cp[3].y * t2) + + ((cp[2].y * bez2) + ((cp[0].y * bez0) + (cp[1].y * bez1))); + p->z = (cp[3].z * t2) + + ((cp[2].z * bez2) + ((cp[0].z * bez0) + (cp[1].z * bez1))); + return; + } + case 2: { + f32 b3, b2, b1, b0, half, u_1, u2; + cp = &spline->cv[idx]; + u2 = t * t; + u_1 = 1.0F - t; + half = 0.5F; + { + f32 b0_tmp = u_1 * (-half * u_1); + b0 = b0_tmp; + } + b1 = half * ((3.0F * u2) - (4.0F * t)); + b2 = half * (1.0F + ((-3.0F * u2) + (2.0F * t))); + b3 = half * u2; + p->x = (cp[3].x * b3) + + ((cp[2].x * b2) + ((cp[0].x * b0) + (cp[1].x * b1))); + p->y = (cp[3].y * b3) + + ((cp[2].y * b2) + ((cp[0].y * b0) + (cp[1].y * b1))); + p->z = (cp[3].z * b3) + + ((cp[2].z * b2) + ((cp[0].z * b0) + (cp[1].z * b1))); + return; + } + case 3: { + f32 u2 = t * t; + f32 tension = spline->tension; + f32 car1, car0, car3, car2; + cp = &spline->cv[idx]; + car0 = tension * (((-3.0F * u2) + (4.0F * t)) - 1.0F); + car1 = (3.0F * (2.0F - tension) * u2) + (2.0F * (tension - 3.0F) * t); + car2 = tension + ((3.0F * (tension - 2.0F) * u2) + + (2.0F * -((2.0F * tension) - 3.0F) * t)); + car3 = tension * ((3.0F * u2) - (2.0F * t)); + p->x = (cp[3].x * car3) + + ((cp[2].x * car2) + ((cp[0].x * car0) + (cp[1].x * car1))); + p->y = (cp[3].y * car3) + + ((cp[2].y * car2) + ((cp[0].y * car0) + (cp[1].y * car1))); + p->z = (cp[3].z * car3) + + ((cp[2].z * car2) + ((cp[0].z * car0) + (cp[1].z * car1))); + break; + } + } +} diff --git a/src/melee/lb/lb_E9F0.h b/src/melee/lb/lb_E9F0.h new file mode 100644 index 0000000000..40af468180 --- /dev/null +++ b/src/melee/lb/lb_E9F0.h @@ -0,0 +1,10 @@ +#ifndef MELEE_LB_LB_E9F0_H +#define MELEE_LB_LB_E9F0_H + +#include + +#include + +/* 00E9F0 */ void lbShadow_8000E9F0(Vec3*, HSD_Spline*, f32); + +#endif diff --git a/src/melee/lb/lbshadow.c b/src/melee/lb/lbshadow.c index b774f6116a..b9c09d5235 100644 --- a/src/melee/lb/lbshadow.c +++ b/src/melee/lb/lbshadow.c @@ -24,119 +24,6 @@ #include #include -/// @todo Emitted only to lay out the .sdata2 literal pool in retail order. -static void sdata2_order(void) -{ - (void) -3.0f; - (void) 4.0f; - (void) 1.0f; - (void) 3.0f; - (void) 2.0f; - (void) 0.5f; - (void) 0.0f; - (void) S32_TO_F32; - (void) 0.001f; - (void) 5000.0f; - (void) 0.45f; - (void) -0.45f; - (void) 1.2f; - (void) -1.1f; - (void) 0.5; - (void) 3.0; - (void) 0.0000010000001f; - (void) 100.0f; - (void) -1.0f; - (void) 128.0f; - (void) -128.0f; -} - -void lbShadow_8000E9F0(Vec3* p, HSD_Spline* spline, f32 u) -{ - Vec3* cp; - s16 idx; - f32 t; - f32 orig_u; - - PAD_STACK(8); - - if (u < 0.0F || u > 1.0F) { - return; - } - - orig_u = u; - u *= spline->numcv - 1; - idx = (s16) u; - t = u - (f32) idx; - - switch (spline->type) { - case 0: - if (orig_u == 1.0F) { - idx -= 1; - } - cp = &spline->cv[idx]; - p->x = cp[1].x - cp[0].x; - p->y = cp[1].y - cp[0].y; - p->z = cp[1].z - cp[0].z; - return; - case 1: { - f32 t2, u_1, bez1, bez0, bez2; - cp = &spline->cv[idx * 3]; - t2 = 3.0F * (t * t); - bez1 = 1.0F - (4.0F * t); - u_1 = t - 1.0F; - bez0 = -3.0F * u_1 * u_1; - bez1 = 3.0F * (bez1 + t2); - bez2 = 3.0F * ((2.0F * t) - t2); - p->x = (cp[3].x * t2) + - ((cp[2].x * bez2) + ((cp[0].x * bez0) + (cp[1].x * bez1))); - p->y = (cp[3].y * t2) + - ((cp[2].y * bez2) + ((cp[0].y * bez0) + (cp[1].y * bez1))); - p->z = (cp[3].z * t2) + - ((cp[2].z * bez2) + ((cp[0].z * bez0) + (cp[1].z * bez1))); - return; - } - case 2: { - f32 b3, b2, b1, b0, half, u_1, u2; - cp = &spline->cv[idx]; - u2 = t * t; - u_1 = 1.0F - t; - half = 0.5F; - { - f32 b0_tmp = u_1 * (-half * u_1); - b0 = b0_tmp; - } - b1 = half * ((3.0F * u2) - (4.0F * t)); - b2 = half * (1.0F + ((-3.0F * u2) + (2.0F * t))); - b3 = half * u2; - p->x = (cp[3].x * b3) + - ((cp[2].x * b2) + ((cp[0].x * b0) + (cp[1].x * b1))); - p->y = (cp[3].y * b3) + - ((cp[2].y * b2) + ((cp[0].y * b0) + (cp[1].y * b1))); - p->z = (cp[3].z * b3) + - ((cp[2].z * b2) + ((cp[0].z * b0) + (cp[1].z * b1))); - return; - } - case 3: { - f32 u2 = t * t; - f32 tension = spline->tension; - f32 car1, car0, car3, car2; - cp = &spline->cv[idx]; - car0 = tension * (((-3.0F * u2) + (4.0F * t)) - 1.0F); - car1 = (3.0F * (2.0F - tension) * u2) + (2.0F * (tension - 3.0F) * t); - car2 = tension + ((3.0F * (tension - 2.0F) * u2) + - (2.0F * -((2.0F * tension) - 3.0F) * t)); - car3 = tension * ((3.0F * u2) - (2.0F * t)); - p->x = (cp[3].x * car3) + - ((cp[2].x * car2) + ((cp[0].x * car0) + (cp[1].x * car1))); - p->y = (cp[3].y * car3) + - ((cp[2].y * car2) + ((cp[0].y * car0) + (cp[1].y * car1))); - p->z = (cp[3].z * car3) + - ((cp[2].z * car2) + ((cp[0].z * car0) + (cp[1].z * car1))); - break; - } - } -} - void lbShadow_8000ED54(LbShadow* lbshadow, HSD_JObj* jobj) { HSD_Shadow* shadow; @@ -331,6 +218,7 @@ static inline f32 lbShadow_Sqrtf(f32 x) return x; } +/// @todo Retail recycles @c lobj's zero into @c fallback's init. void lbShadow_8000F38C(s32 arg0) { HSD_ViewingRect rect; diff --git a/src/melee/lb/lbshadow.h b/src/melee/lb/lbshadow.h index 04a3377e2e..ceac9c3788 100644 --- a/src/melee/lb/lbshadow.h +++ b/src/melee/lb/lbshadow.h @@ -10,7 +10,6 @@ #include #include -/* 00E9F0 */ void lbShadow_8000E9F0(Vec3*, HSD_Spline*, f32); /* 00ED54 */ void lbShadow_8000ED54(LbShadow*, HSD_JObj*); /* 00EE8C */ void lbShadow_8000EE8C(LbShadow*); /* 00EEE0 */ void lbShadow_8000EEE0(HSD_GObj*); From 84ea803c171c3acc0622aa044df9220bd28b9d3c Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 24 Jul 2026 22:42:12 -0700 Subject: [PATCH 3/3] Sort the moved lb_E9F0.h includes --- src/melee/gr/grmutecity.c | 2 +- src/melee/gr/ground.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/melee/gr/grmutecity.c b/src/melee/gr/grmutecity.c index 85cd04fa64..e479fedefb 100644 --- a/src/melee/gr/grmutecity.c +++ b/src/melee/gr/grmutecity.c @@ -19,8 +19,8 @@ #include "cm/camera.h" #include "if/ifhazard.h" #include "lb/lb_00B0.h" -#include "lb/lbaudio_ax.h" #include "lb/lb_E9F0.h" +#include "lb/lbaudio_ax.h" #include "lb/lbspdisplay.h" #include "lb/lbvector.h" #include "mp/mplib.h" diff --git a/src/melee/gr/ground.c b/src/melee/gr/ground.c index 76c87876bf..2385b35431 100644 --- a/src/melee/gr/ground.c +++ b/src/melee/gr/ground.c @@ -30,9 +30,9 @@ #include "it/itzako.h" #include "it/types.h" #include "lb/lb_00B0.h" +#include "lb/lb_E9F0.h" #include "lb/lbaudio_ax.h" #include "lb/lbdvd.h" -#include "lb/lb_E9F0.h" #include "lb/lbspdisplay.h" #include "lb/lbvector.h" #include "mp/mpcoll.h"