From a4d6c74255aea53259240f562a9df725f5767710 Mon Sep 17 00:00:00 2001 From: nominalsan <135046937+nominalsan@users.noreply.github.com> Date: Tue, 10 Nov 2026 21:54:25 +0100 Subject: [PATCH] feat(pal,us2): port func_L00_002D5EB8 to both releases, retain one near-miss pair C ports of the matched USA v1.01 func_L00_002D5EB8 (src/levels/shared/L00_002D3798-g8.c), each its own -g8.c unit: - func_PAL_L00_002D6408 (27 levels): the 50 Hz duration 0x12C (300 frames) where v1.01 passes 0x168 (360), and PAL's own callees func_PAL_00288E20, func_PAL_L00_0030E648 and func_PAL_002B1588. - func_US2_L00_002D6258 (27 levels): the same C with v2.00's own callees func_US2_00288F30, func_US2_L00_0030D9F8 and func_US2_002B1880. Near misses (markers kept): func_PAL_002CCAC8 and func_US2_002CCC18, the PAL and v2.00 copies of func_002CBC58, are exact (28/28) only with the volatile qualifier on D_001AB910 that the merged USA C uses. Without it they are 2068/2100 bytes: an empty else-if arm loses its dead read. Three lawful forms were measured; the notes in nonmatching/game/ record why they cannot close and that the qualifier needs a maintainer's ruling. Checks: - tools/check.py: func_PAL_L00_002D6408 27 of 27, func_US2_L00_002D6258 27 of 27. - tools/build.py all --strict: 28 programs OK in each of USA v1.01, PAL and USA v2.00; both functions are in all 27 carrying placed.json. - python3 -m unittest discover -s tests: 540 tests OK (1 skipped); tests/check_repository.py: passed; tools/progress.py --check: passed. Report not regenerated. Co-Authored-By: Claude Opus 5.5 --- nonmatching/README.md | 2 + nonmatching/game/func_PAL_002CCAC8.c | 207 ++++++++++++++++++++++++ nonmatching/game/func_US2_002CCC18.c | 207 ++++++++++++++++++++++++ src/pal/levels/shared/L00_002D6408-g8.c | 198 +++++++++++++++++++++++ src/pal/levels/shared/L00_002D6408.c | 1 - src/us2/levels/shared/L00_002D6258-g8.c | 197 ++++++++++++++++++++++ src/us2/levels/shared/L00_002D6258.c | 1 - 7 files changed, 811 insertions(+), 2 deletions(-) create mode 100644 nonmatching/game/func_PAL_002CCAC8.c create mode 100644 nonmatching/game/func_US2_002CCC18.c create mode 100644 src/pal/levels/shared/L00_002D6408-g8.c create mode 100644 src/us2/levels/shared/L00_002D6258-g8.c diff --git a/nonmatching/README.md b/nonmatching/README.md index 017243a99..9b311fc40 100644 --- a/nonmatching/README.md +++ b/nonmatching/README.md @@ -272,3 +272,5 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are | [`func_PAL_L04_0041B5C0`](levels/func_PAL_L04_0041B5C0.c) | 652 | 9 November 2026: PAL, now 652/652 bytes, 0/2 places, 29 real rows (lead follow-ups: kx set after the corner coordinates; x, then the first label coordinate, then y gives retail's interleave; left: y's shifts recomputed after the dead test's boundary, so no $v1 copy; entry load pair; closing x1/y1 registers). First pass 648/652. Menu box with two labels, same shape as USA func_L04_00416E38 (menu_coord idiom). Retail loads the 371.0f constant after the width call and schedules the D_001A73C0/gp-offset loads before the slt; mine hoists them differently. Needs -G8 (L04_0041A660-g8.c). | | [`func_PAL_L03_003CB058`](levels/func_PAL_L03_003CB058.c) | 656 | 9 November 2026: PAL port of func_L03_003C7018 (USA best C, own -Wa,--r5900-la-addend=sn unit): 656/656 bytes, PAL 0/1, 13 masked positions. PAL retail equals USA retail apart from addresses; same leftovers (draw 2 ori/f15, buffer addiu after the key copy, first sprintf rematerialises the buffer). Outer-scope buffer gives 11 but swaps the frame. | | [`func_PAL_L03_00426DE8`](levels/func_PAL_L03_00426DE8.c) | 412 | 9 November 2026: PAL port of func_L03_00421930 (USA flat C, own -fno-schedule-insns unit): 412/412 bytes, PAL 0/1, 12 masked positions, all the USA p s1 / base s3 register swap. PAL retail equals USA retail apart from addresses. | +| [`func_PAL_002CCAC8`](game/func_PAL_002CCAC8.c) | 2100 | 10 November 2026: PAL copy of func_002CBC58 (same C, the release's callees), G8. 2100/2100 bytes and 28/28 places only with `volatile` on D_001AB910, which no known code writes, so it is a near miss. Plain `int`: 2068/2100, 0/28; the first two page tests are exact, the empty-arm D_0025A058 test with its dead `lw` of D_001AB910 is deleted. Equal-arm forms for a jump2 cross-jump give 2132, 2124 and 2140 bytes: flow's `(use (const_int 0))` after a call that falls into a label keeps the last copy of `func_002FC560(0x47,0x5360B)` apart. Needs a writer of D_001AB910 or a ruling, as func_PAL_002CC950 does. | +| [`func_US2_002CCC18`](game/func_US2_002CCC18.c) | 2100 | 10 November 2026: USA v2.00 copy of func_002CBC58 (same C, the release's callees), G8. 2100/2100 bytes and 28/28 places only with `volatile` on D_001AB910, which no known code writes, so it is a near miss. Plain `int`: 2068/2100, 0/28; the first two page tests are exact, the empty-arm D_0025A058 test with its dead `lw` of D_001AB910 is deleted. Equal-arm forms for a jump2 cross-jump give 2132, 2124 and 2140 bytes: flow's `(use (const_int 0))` after a call that falls into a label keeps the last copy of `func_002FC560(0x47,0x5360B)` apart. Needs a writer of D_001AB910 or a ruling, as func_US2_002CCAA0 does. | diff --git a/nonmatching/game/func_PAL_002CCAC8.c b/nonmatching/game/func_PAL_002CCAC8.c new file mode 100644 index 000000000..26493346d --- /dev/null +++ b/nonmatching/game/func_PAL_002CCAC8.c @@ -0,0 +1,207 @@ +/* 10 November 2026: exact in PAL (tools/check.py func_PAL_002CCAC8 --version pal: "28 of 28 places + * match", compiled as src/pal/game/002CCAC8-g8.c with the default `-O2 -G8 -ffunction-sections`), but only + * with the qualifier `volatile` on D_001AB910, which C that links may not carry: no code is known + * to write D_001AB910 (AGENTS.md, "`volatile` describes the memory"). The v1.01 original + * func_002CBC58 (src/game/002C7748-panels-g8.c) has the same qualifier; the same question holds + * func_PAL_002CC950. This is that C with the release's callees: + * func_00280600 is func_PAL_00280EC8, func_0028E458 is func_PAL_0028ECD8, func_0029BF08 is func_PAL_0029C8A0, func_0029C2D0 is func_PAL_0029CC68, + * func_0029C340 is func_PAL_0029CCD8, func_002D84B0 is func_PAL_002D9108, func_002DA9A8 is func_PAL_002DB6C8. + * + * What the qualifier buys is eight instructions, all in one place. The third page test + * else if (D_001F2840.page == D_0025A058 && !D_001AB910) { } + * has an empty arm, and retail keeps its page load, compare and the read of D_001AB910 + * (`beql $a0,$v1,` with `lw $v0,D_001AB910($gp)` in the slot). Plain `int D_001AB910`: + * 2068 bytes for 2100. The first two tests (D_0025A498, D_0025A190) are already exact without + * the qualifier; the jump pass deletes the whole third test and with it the second + * `lui`/`addiu` of &D_001F2840. + * + * Tried without `volatile` (PAL; all compiled, none closer than the last): + * 1. `if (page == D_0025A058) { if (!D_001AB910) { func_002FC560(0x47,0x5360B); goto drawn; } }` + * before the shared `func_002FC560(0x47,0x5360B); drawn:` (equal arms for jump2 to + * cross-jump, which would leave the dead load): 2132 bytes, the two copies stay. + * 2. The call written in every arm (D_0025A498 arm, the D_0025A058 arm, its `else`, and the + * `else` of `if (D_001AB91C)`): 2124 bytes. The three inner copies cross-jump and the + * `beql`/dead `lw` come out as in retail, but the outer copy stays, behind a `b`. + * 3. As 2 with `pending = D_001F2840.field168;` after each call, so that no copy ends in a + * call: 2140 bytes; the copies load the field through different registers and none merge. + * Why 1 and 2 stop there (-da dumps): flow's find_basic_blocks puts `(use (const_int 0))` + * after a call that ends a block before a label, so the copy that falls into the join is + * `call; use; label` and find_cross_jump (jump.c) stops at the `use` against the copy that + * ends `call; jump`. Only copies that all end in a jump merge with each other, and the copy + * retail keeps is the one that falls through. An equal-arms form therefore needs arms that + * do not end in a call, and this arm has nothing but the shared call after it. + * Next useful input: evidence of a second writer of D_001AB910 (it is only ever read: a + * debugger-set switch would be declared exactly so), or a ruling on it, which would also + * settle func_002CBC58 and func_002CBAE0 and their copies. + */ +#include "common.h" + +typedef struct PanelCBC58 PanelCBC58; +struct PanelCBC58 { + int unused; + int (*draw)(PanelCBC58 *); + int pad8[2]; + int flags, pad14; + int x, y, width, height; +}; +typedef struct { int x, y, width, height; } RectCBC58; +typedef struct { RectCBC58 bounds[14]; char padE0[12]; PanelCBC58 *panels[14]; } PageCBC58; +typedef struct { + int mode, active; + char pad8[12]; + PageCBC58 *page; + char pad18[8]; + void *buffer; + char pad24[0x144]; + int field168, field16C; + char pad170[0x50]; + float fade, step, target; +} StateCBC58; +extern StateCBC58 D_001F2840; +extern int D_001A7344, D_001A72DC; +extern int D_001AB91C __attribute__((sda)); +extern volatile int D_001AB910 __attribute__((sda)); +extern int D_001AA450[14] SDATA(D_001AA450); +extern int D_001AA43C __attribute__((sda)); +extern int D_001AB92C __attribute__((sda)); +extern int D_001AB930 __attribute__((sda)); +extern long D_001A7470 __attribute__((sda)); +extern char D_00259018[], D_0025A738[], D_0025A498[], D_0025A190[], D_0025A058[]; +extern PanelCBC58 D_00259EE0; +extern char D_001AB920[], D_001AB928[]; +typedef struct { char pad[0x1A0]; int flags; char pad1A4[0x20]; int pressed; } PadCBC58; +extern PadCBC58 D_00138180; +extern void (*D_00210AD0)(void) __attribute__((nosda)); +extern void func_00284F40(int); +extern void func_002FC990(void *, int, int); +extern void func_0027B858(void); +extern void func_0027DDC0(int,int,int,int); +extern void func_0027C540(int), func_0027C660(void); +extern void func_PAL_0028ECD8(void), func_PAL_0029C8A0(void), func_PAL_0029CC68(void), func_PAL_0029CCD8(void); +extern void func_002FC560(int, long); +extern void func_002A0250(void), func_002A01C8(void), func_002A01F0(void); +extern void func_0026FEB8(void *); +extern int func_002DBAD0(PanelCBC58 *), func_002DA118(PanelCBC58 *); +extern int func_PAL_002D9108(PanelCBC58 *), func_PAL_002DB6C8(PanelCBC58 *); +extern void func_00280510(int,int,int,float); +extern void func_0028FDD8(int,int,int,int,long,int,int); +extern void func_PAL_00280EC8(void); +extern void func_0027E018(int,int,int,int,int,int,int,int,long,long); +extern void func_002A0220(void),func_0029FEE0(void); +extern int func_002A0380(void); +extern void func_002FC3C0(unsigned long); +extern int func_00283CF0(float); + +void func_PAL_002CCAC8(int mode) +{ + PanelCBC58 **panels = 0; + int none = 0; + StateCBC58 *state = &D_001F2840; + int pass, i, j, x, y, w, h, lw, lh, result; + int left, top, right, bottom, tw, th; + float delta; + func_00284F40(0); + func_002FC990(state->buffer, D_001A72DC, D_001A7344 << 11); + if (!mode) { + func_0027B858(); + func_0027DDC0(0,0,0,0x38); + if (D_001F2840.mode == 4 && state->active) { + if (D_001AB91C) { + void *page = state->page; + if (page == D_00259018) { + func_0027C540(0); D_00210AD0(); func_0027C660(); return; + } + if (page == D_0025A738) { + func_0027C540(0); func_PAL_0028ECD8(); func_PAL_0029C8A0(); func_0027C660(); return; + } + if (page == D_0025A498 && !D_001AB910) { + func_0027C540(0); func_PAL_0029CC68(); func_0027C660(); + } else if (D_001F2840.page == (void *)D_0025A190 && !D_001AB910) { + func_0027C540(0); func_PAL_0029CCD8(); func_0027C660(); return; + } else if (D_001F2840.page == (void *)D_0025A058 && !D_001AB910) { + ; + } + } + func_002FC560(0x47,0x5360B); + if (D_001F2840.field168) { + func_002A0250(); func_002A01C8(); func_002A01F0(); + } + if (D_001F2840.page) { + panels = D_001F2840.page->panels; + panels = (PanelCBC58 **)((char *)panels + none); + } + if ((D_00138180.flags&15)==15 && (D_00138180.pressed&15)) + func_0026FEB8(D_001AB920); + for (j=0;j<14;j++) { + StateCBC58 *st = &D_001F2840; + if (D_001AA450[j]) { + PageCBC58 *pg = st->page; + if (panels && panels[j]) { + panels[j]->x = pg->bounds[j].x - pg->bounds[j].width/2; + panels[j]->y = st->page->bounds[j].y - st->page->bounds[j].height/2; + panels[j]->width = st->page->bounds[j].width; + panels[j]->height = st->page->bounds[j].height; + if (panels[j] == &D_00259EE0) panels[j]->height=256; + } + } + } + if ((D_00138180.flags&15)==15 && (D_00138180.pressed&15)) + func_0026FEB8(D_001AB928); + for (pass=0;pass<2;pass++) { + for (i=0;i<14;i++) { + PanelCBC58 *p; + if (!panels || !(p=panels[i]) || (p->flags&4) || !D_001AA450[i] || !p->draw) continue; + if (!pass && !(p->flags&2)) continue; + if (pass==1 && (p->flags&2)) continue; + if (p->flags&1) { p->draw(p); continue; } + x=p->x; y=p->y; w=p->width; h=p->height; + lw=7; while ((1<=18) lh--; + if ((panels[i]->draw == func_002DBAD0 || panels[i]->draw == func_002DA118 || + panels[i]->draw == func_PAL_002D9108 || panels[i]->draw == func_PAL_002DB6C8) && !(panels[i]->flags&8)) { + func_00280510(lw,lh,0,1.0f); + func_0028FDD8(0,0,1<draw(panels[i]); + func_PAL_00280EC8(); + } else result=panels[i]->draw(panels[i]); + func_002FC560(0x42,0x8000000064L); + func_002FC560(0x47,0x43); + if (result&1) continue; + tw=1< -1 ? left : 0; + top=(bottom-h)/2; + bottom-=top; + top=top > -1 ? top : 0; + if (twD_001F2840.step) delta=D_001F2840.step; + else if (delta < -D_001F2840.step) delta=-D_001F2840.step; + D_001F2840.fade+=delta; + } + if (D_001F2840.fade != 0.0f) + func_0027DDC0(0,0,0,func_00283CF0(D_001F2840.fade*128.0f)); + func_002FC560(0x42,0x8000000044L); + func_002FC560(0x47,0x5360B); + } + } +} diff --git a/nonmatching/game/func_US2_002CCC18.c b/nonmatching/game/func_US2_002CCC18.c new file mode 100644 index 000000000..5916d0c53 --- /dev/null +++ b/nonmatching/game/func_US2_002CCC18.c @@ -0,0 +1,207 @@ +/* 10 November 2026: exact in USA v2.00 (tools/check.py func_US2_002CCC18 --version us2: "28 of 28 places + * match", compiled as src/us2/game/002CCC18-g8.c with the default `-O2 -G8 -ffunction-sections`), but only + * with the qualifier `volatile` on D_001AB910, which C that links may not carry: no code is known + * to write D_001AB910 (AGENTS.md, "`volatile` describes the memory"). The v1.01 original + * func_002CBC58 (src/game/002C7748-panels-g8.c) has the same qualifier; the same question holds + * func_US2_002CCAA0. This is that C with the release's callees: + * func_00280600 is func_US2_00280FB8, func_0029BF08 is func_US2_0029CD18, func_0029C2D0 is func_US2_0029D108, + * func_0029C340 is func_US2_0029D178, func_002D84B0 is func_US2_002D9178, func_002DA9A8 is func_US2_002DB700. + * + * What the qualifier buys is eight instructions, all in one place. The third page test + * else if (D_001F2840.page == D_0025A058 && !D_001AB910) { } + * has an empty arm, and retail keeps its page load, compare and the read of D_001AB910 + * (`beql $a0,$v1,` with `lw $v0,D_001AB910($gp)` in the slot). Plain `int D_001AB910`: + * 2068 bytes for 2100. The first two tests (D_0025A498, D_0025A190) are already exact without + * the qualifier; the jump pass deletes the whole third test and with it the second + * `lui`/`addiu` of &D_001F2840. + * + * Tried without `volatile` (PAL; all compiled, none closer than the last): + * 1. `if (page == D_0025A058) { if (!D_001AB910) { func_002FC560(0x47,0x5360B); goto drawn; } }` + * before the shared `func_002FC560(0x47,0x5360B); drawn:` (equal arms for jump2 to + * cross-jump, which would leave the dead load): 2132 bytes, the two copies stay. + * 2. The call written in every arm (D_0025A498 arm, the D_0025A058 arm, its `else`, and the + * `else` of `if (D_001AB91C)`): 2124 bytes. The three inner copies cross-jump and the + * `beql`/dead `lw` come out as in retail, but the outer copy stays, behind a `b`. + * 3. As 2 with `pending = D_001F2840.field168;` after each call, so that no copy ends in a + * call: 2140 bytes; the copies load the field through different registers and none merge. + * Why 1 and 2 stop there (-da dumps): flow's find_basic_blocks puts `(use (const_int 0))` + * after a call that ends a block before a label, so the copy that falls into the join is + * `call; use; label` and find_cross_jump (jump.c) stops at the `use` against the copy that + * ends `call; jump`. Only copies that all end in a jump merge with each other, and the copy + * retail keeps is the one that falls through. An equal-arms form therefore needs arms that + * do not end in a call, and this arm has nothing but the shared call after it. + * Next useful input: evidence of a second writer of D_001AB910 (it is only ever read: a + * debugger-set switch would be declared exactly so), or a ruling on it, which would also + * settle func_002CBC58 and func_002CBAE0 and their copies. + */ +#include "common.h" + +typedef struct PanelCBC58 PanelCBC58; +struct PanelCBC58 { + int unused; + int (*draw)(PanelCBC58 *); + int pad8[2]; + int flags, pad14; + int x, y, width, height; +}; +typedef struct { int x, y, width, height; } RectCBC58; +typedef struct { RectCBC58 bounds[14]; char padE0[12]; PanelCBC58 *panels[14]; } PageCBC58; +typedef struct { + int mode, active; + char pad8[12]; + PageCBC58 *page; + char pad18[8]; + void *buffer; + char pad24[0x144]; + int field168, field16C; + char pad170[0x50]; + float fade, step, target; +} StateCBC58; +extern StateCBC58 D_001F2840; +extern int D_001A7344, D_001A72DC; +extern int D_001AB91C __attribute__((sda)); +extern volatile int D_001AB910 __attribute__((sda)); +extern int D_001AA450[14] SDATA(D_001AA450); +extern int D_001AA43C __attribute__((sda)); +extern int D_001AB92C __attribute__((sda)); +extern int D_001AB930 __attribute__((sda)); +extern long D_001A7470 __attribute__((sda)); +extern char D_00259018[], D_0025A738[], D_0025A498[], D_0025A190[], D_0025A058[]; +extern PanelCBC58 D_00259EE0; +extern char D_001AB920[], D_001AB928[]; +typedef struct { char pad[0x1A0]; int flags; char pad1A4[0x20]; int pressed; } PadCBC58; +extern PadCBC58 D_00138180; +extern void (*D_00210AD0)(void) __attribute__((nosda)); +extern void func_00284F40(int); +extern void func_002FC990(void *, int, int); +extern void func_0027B858(void); +extern void func_0027DDC0(int,int,int,int); +extern void func_0027C540(int), func_0027C660(void); +extern void func_0028E458(void), func_US2_0029CD18(void), func_US2_0029D108(void), func_US2_0029D178(void); +extern void func_002FC560(int, long); +extern void func_002A0250(void), func_002A01C8(void), func_002A01F0(void); +extern void func_0026FEB8(void *); +extern int func_002DBAD0(PanelCBC58 *), func_002DA118(PanelCBC58 *); +extern int func_US2_002D9178(PanelCBC58 *), func_US2_002DB700(PanelCBC58 *); +extern void func_00280510(int,int,int,float); +extern void func_0028FDD8(int,int,int,int,long,int,int); +extern void func_US2_00280FB8(void); +extern void func_0027E018(int,int,int,int,int,int,int,int,long,long); +extern void func_002A0220(void),func_0029FEE0(void); +extern int func_002A0380(void); +extern void func_002FC3C0(unsigned long); +extern int func_00283CF0(float); + +void func_US2_002CCC18(int mode) +{ + PanelCBC58 **panels = 0; + int none = 0; + StateCBC58 *state = &D_001F2840; + int pass, i, j, x, y, w, h, lw, lh, result; + int left, top, right, bottom, tw, th; + float delta; + func_00284F40(0); + func_002FC990(state->buffer, D_001A72DC, D_001A7344 << 11); + if (!mode) { + func_0027B858(); + func_0027DDC0(0,0,0,0x38); + if (D_001F2840.mode == 4 && state->active) { + if (D_001AB91C) { + void *page = state->page; + if (page == D_00259018) { + func_0027C540(0); D_00210AD0(); func_0027C660(); return; + } + if (page == D_0025A738) { + func_0027C540(0); func_0028E458(); func_US2_0029CD18(); func_0027C660(); return; + } + if (page == D_0025A498 && !D_001AB910) { + func_0027C540(0); func_US2_0029D108(); func_0027C660(); + } else if (D_001F2840.page == (void *)D_0025A190 && !D_001AB910) { + func_0027C540(0); func_US2_0029D178(); func_0027C660(); return; + } else if (D_001F2840.page == (void *)D_0025A058 && !D_001AB910) { + ; + } + } + func_002FC560(0x47,0x5360B); + if (D_001F2840.field168) { + func_002A0250(); func_002A01C8(); func_002A01F0(); + } + if (D_001F2840.page) { + panels = D_001F2840.page->panels; + panels = (PanelCBC58 **)((char *)panels + none); + } + if ((D_00138180.flags&15)==15 && (D_00138180.pressed&15)) + func_0026FEB8(D_001AB920); + for (j=0;j<14;j++) { + StateCBC58 *st = &D_001F2840; + if (D_001AA450[j]) { + PageCBC58 *pg = st->page; + if (panels && panels[j]) { + panels[j]->x = pg->bounds[j].x - pg->bounds[j].width/2; + panels[j]->y = st->page->bounds[j].y - st->page->bounds[j].height/2; + panels[j]->width = st->page->bounds[j].width; + panels[j]->height = st->page->bounds[j].height; + if (panels[j] == &D_00259EE0) panels[j]->height=256; + } + } + } + if ((D_00138180.flags&15)==15 && (D_00138180.pressed&15)) + func_0026FEB8(D_001AB928); + for (pass=0;pass<2;pass++) { + for (i=0;i<14;i++) { + PanelCBC58 *p; + if (!panels || !(p=panels[i]) || (p->flags&4) || !D_001AA450[i] || !p->draw) continue; + if (!pass && !(p->flags&2)) continue; + if (pass==1 && (p->flags&2)) continue; + if (p->flags&1) { p->draw(p); continue; } + x=p->x; y=p->y; w=p->width; h=p->height; + lw=7; while ((1<=18) lh--; + if ((panels[i]->draw == func_002DBAD0 || panels[i]->draw == func_002DA118 || + panels[i]->draw == func_US2_002D9178 || panels[i]->draw == func_US2_002DB700) && !(panels[i]->flags&8)) { + func_00280510(lw,lh,0,1.0f); + func_0028FDD8(0,0,1<draw(panels[i]); + func_US2_00280FB8(); + } else result=panels[i]->draw(panels[i]); + func_002FC560(0x42,0x8000000064L); + func_002FC560(0x47,0x43); + if (result&1) continue; + tw=1< -1 ? left : 0; + top=(bottom-h)/2; + bottom-=top; + top=top > -1 ? top : 0; + if (twD_001F2840.step) delta=D_001F2840.step; + else if (delta < -D_001F2840.step) delta=-D_001F2840.step; + D_001F2840.fade+=delta; + } + if (D_001F2840.fade != 0.0f) + func_0027DDC0(0,0,0,func_00283CF0(D_001F2840.fade*128.0f)); + func_002FC560(0x42,0x8000000044L); + func_002FC560(0x47,0x5360B); + } + } +} diff --git a/src/pal/levels/shared/L00_002D6408-g8.c b/src/pal/levels/shared/L00_002D6408-g8.c new file mode 100644 index 000000000..11af1137e --- /dev/null +++ b/src/pal/levels/shared/L00_002D6408-g8.c @@ -0,0 +1,198 @@ +/* func_PAL_L00_002D6408: the PAL copy of func_L00_002D5EB8 (src/levels/shared/L00_002D3798-g8.c). + * The same C with the 50 Hz duration 0x12C (300 frames) where v1.01 passes 0x168 (360), and + * the callees that are the release's own functions: + * func_00288508 is func_PAL_00288E20, + * func_L00_0030C7F8 is func_PAL_L00_0030E648, + * func_002B0A18 is func_PAL_002B1588. + */ +#include "common.h" + +/* The hero block: the two fields the loop tests. */ +typedef struct { + char pad0000[0x340]; + int f340; /* 0x0340 */ + char pad0344[0xF04]; + int weapon; /* 0x1248 */ +} Hero5940; + +extern Hero5940 D_00189E20; +extern int D_L00_001B19C8[] __attribute__((sda)); +extern int D_0018C350[]; +extern unsigned char D_001A7A00[] __attribute__((sda)); +extern int D_0018C2EC[]; +extern unsigned char D_001A7A28[] MACRO_ADDR; +extern unsigned char D_00139568[]; +extern int D_L00_001A903C; +extern unsigned short D_0018C958 __attribute__((section(".data"))); +extern int D_00139868[]; +extern int D_001A7398[] MACRO_ADDR; +extern unsigned char D_001A7AF8[] MACRO_ADDR; + +/* One 0xE0-byte entry of D_L00_00262570, indexed through D_00139568. */ +typedef struct { + int exists; /* 0x00 */ + unsigned char state; /* 0x04 */ + char pad05[0x7]; + int index; /* 0x0C */ + char pad10[0x3A]; + short value; /* 0x4A */ + char pad4C[0x20]; + int limit; /* 0x6C */ + char pad70[0x70]; +} Entry1269; + +extern Entry1269 D_L00_00262570[]; +extern int func_L00_002D5E30(int *, int *, int *); +extern void func_0028A180(int, int); +extern void func_L00_003F1300(void); +extern void func_PAL_L00_0030E648(int); +extern void func_PAL_002B1588(int, int); +extern int func_002E5E58(int, int, void *); +extern int func_00288D58(void); +extern int func_00287F60(int); +extern void func_L00_0037E6A8(int, int); +extern void func_00287EB0(int); +extern void func_PAL_00288E20(int); +extern int ref_PAL_L00_002D6408_00 __attribute__((sda)); + +static inline int special(int i) +{ + int r = 0; + + switch (i) { + case 12: + case 14: + case 17: + case 18: + case 53: + r = 1; + break; + } + return r; +} + +static inline int unlocked(Entry1269 *e, int i) +{ + if (e->state >= 2 || (e->state == 1 && special(i))) + return 1; + return 0; +} + +static inline int owned(int i) +{ + if (D_L00_00262570[D_00139568[i]].exists) { + int n = func_00287F60(i); + if (n >= 2 || (n == 1 && special(i))) + return 1; + } + return 0; +} + +void func_PAL_L00_002D6408(void) +{ + int count, i, j, ok; + + if ((D_L00_001B19C8[0] & 15) == 1 && D_L00_001B19C8[0] != 1) + return; + count = 4; + { + int *p = D_0018C350; + func_L00_002D5E30(p, p - 1, &count); + } + for (j = 0; j < 10; j++) + if (D_001A7A28[j]) + count++; + if (*(int *)D_001A7A00 != count) { + if (ref_PAL_L00_002D6408_00 >= 11 && *(int *)D_001A7A00 < count) { + if (!D_0018C958) + func_0028A180(0x9A9, 0); + func_L00_003F1300(); + func_PAL_L00_0030E648(0x12C); + func_PAL_002B1588(0x1235, -1); + func_002E5E58(6, 0, 0); + } + D_0018C2EC[0] = count; + *(int *)D_001A7A00 = count; + } + + for (i = 0; i < 56; i++) { + if ((D_L00_00262570 + D_00139568[i])->state) { + if (!func_00288D58()) + continue; + if (func_00287F60(i) == 1) + continue; + } + { + Entry1269 *e = &D_L00_00262570[D_00139568[i]]; + int sel = 0; + if (e->exists) + sel = e->state == 3; + if (sel) + continue; + } + if (func_00287F60(i) <= 0) + continue; + { + Entry1269 *e = &D_L00_00262570[D_00139568[i]]; + if ((D_00139868[i] >> 5) < e->limit || e->limit <= 0) + continue; + if (i == 31 || i == 9 || i == 45) { + func_L00_0037E6A8(i, D_L00_00262570[D_00139568[i]].value); + continue; + } + if (D_00189E20.weapon == i) { + if (!D_00189E20.f340) + continue; + if (D_001A7398[e->index] != i) + continue; + func_L00_0037E6A8(i, e->value); + continue; + } + } + if (func_00288D58()) { + Entry1269 *e = &D_L00_00262570[D_00139568[i]]; + if (!e->exists) + continue; + if (unlocked(e, i)) { + if (owned(i)) { + func_00287EB0(i); + func_PAL_00288E20(i); + } + } + } else { + func_00287EB0(i); + func_PAL_00288E20(i); + } + } + + if ((D_L00_001B19C8[0] & 127) || D_001A7A00[0x7A]) + return; + ok = 1; + for (i = 0; i < 56; i++) { + int ex = D_L00_00262570[D_00139568[i]].exists; + if (ex && i == 10) + continue; + if (ex && i == 9) + continue; + if (ex) { + if (!D_001A7AF8[i]) { + ok = 0; + break; + } + if (i == 14 || i == 12 || i == 17 || i == 18 || i == 53) + continue; + if (func_00287F60(i) <= 0) + continue; + if (!D_L00_00262570[D_00139568[i]].state) { + ok = 0; + break; + } + } + } + if (ok && !D_001A7A00[0x7A]) { + D_001A7A00[0x7A] = 1; + func_002E5E58(1, 0, 0); + func_PAL_002B1588(0x1233, -1); + D_L00_001A903C = 1; + } +} diff --git a/src/pal/levels/shared/L00_002D6408.c b/src/pal/levels/shared/L00_002D6408.c index 25eecfdd7..877bd62d6 100644 --- a/src/pal/levels/shared/L00_002D6408.c +++ b/src/pal/levels/shared/L00_002D6408.c @@ -1,3 +1,2 @@ #include "include_asm.h" -INCLUDE_ASM("asm/pal/levels", func_PAL_L00_002D6408); diff --git a/src/us2/levels/shared/L00_002D6258-g8.c b/src/us2/levels/shared/L00_002D6258-g8.c new file mode 100644 index 000000000..723f8613d --- /dev/null +++ b/src/us2/levels/shared/L00_002D6258-g8.c @@ -0,0 +1,197 @@ +/* func_US2_L00_002D6258: the v2.00 copy of func_L00_002D5EB8 (src/levels/shared/L00_002D3798-g8.c). + * The same C; only callees that are the release's own functions differ: + * func_00288508 is func_US2_00288F30, + * func_L00_0030C7F8 is func_US2_L00_0030D9F8, + * func_002B0A18 is func_US2_002B1880. + */ +#include "common.h" + +/* The hero block: the two fields the loop tests. */ +typedef struct { + char pad0000[0x340]; + int f340; /* 0x0340 */ + char pad0344[0xF04]; + int weapon; /* 0x1248 */ +} Hero5940; + +extern Hero5940 D_00189E20; +extern int D_L00_001B19C8[] __attribute__((sda)); +extern int D_0018C350[]; +extern unsigned char D_001A7A00[] __attribute__((sda)); +extern int D_0018C2EC[]; +extern unsigned char D_001A7A28[] MACRO_ADDR; +extern unsigned char D_00139568[]; +extern int D_L00_001A903C; +extern unsigned short D_0018C958 __attribute__((section(".data"))); +extern int D_00139868[]; +extern int D_001A7398[] MACRO_ADDR; +extern unsigned char D_001A7AF8[] MACRO_ADDR; + +/* One 0xE0-byte entry of D_L00_00262570, indexed through D_00139568. */ +typedef struct { + int exists; /* 0x00 */ + unsigned char state; /* 0x04 */ + char pad05[0x7]; + int index; /* 0x0C */ + char pad10[0x3A]; + short value; /* 0x4A */ + char pad4C[0x20]; + int limit; /* 0x6C */ + char pad70[0x70]; +} Entry1269; + +extern Entry1269 D_L00_00262570[]; +extern int func_L00_002D5E30(int *, int *, int *); +extern void func_0028A180(int, int); +extern void func_L00_003F1300(void); +extern void func_US2_L00_0030D9F8(int); +extern void func_US2_002B1880(int, int); +extern int func_002E5E58(int, int, void *); +extern int func_00288D58(void); +extern int func_00287F60(int); +extern void func_L00_0037E6A8(int, int); +extern void func_00287EB0(int); +extern void func_US2_00288F30(int); +extern int ref_US2_L00_002D6258_00 __attribute__((sda)); + +static inline int special(int i) +{ + int r = 0; + + switch (i) { + case 12: + case 14: + case 17: + case 18: + case 53: + r = 1; + break; + } + return r; +} + +static inline int unlocked(Entry1269 *e, int i) +{ + if (e->state >= 2 || (e->state == 1 && special(i))) + return 1; + return 0; +} + +static inline int owned(int i) +{ + if (D_L00_00262570[D_00139568[i]].exists) { + int n = func_00287F60(i); + if (n >= 2 || (n == 1 && special(i))) + return 1; + } + return 0; +} + +void func_US2_L00_002D6258(void) +{ + int count, i, j, ok; + + if ((D_L00_001B19C8[0] & 15) == 1 && D_L00_001B19C8[0] != 1) + return; + count = 4; + { + int *p = D_0018C350; + func_L00_002D5E30(p, p - 1, &count); + } + for (j = 0; j < 10; j++) + if (D_001A7A28[j]) + count++; + if (*(int *)D_001A7A00 != count) { + if (ref_US2_L00_002D6258_00 >= 11 && *(int *)D_001A7A00 < count) { + if (!D_0018C958) + func_0028A180(0x9A9, 0); + func_L00_003F1300(); + func_US2_L00_0030D9F8(0x168); + func_US2_002B1880(0x1235, -1); + func_002E5E58(6, 0, 0); + } + D_0018C2EC[0] = count; + *(int *)D_001A7A00 = count; + } + + for (i = 0; i < 56; i++) { + if ((D_L00_00262570 + D_00139568[i])->state) { + if (!func_00288D58()) + continue; + if (func_00287F60(i) == 1) + continue; + } + { + Entry1269 *e = &D_L00_00262570[D_00139568[i]]; + int sel = 0; + if (e->exists) + sel = e->state == 3; + if (sel) + continue; + } + if (func_00287F60(i) <= 0) + continue; + { + Entry1269 *e = &D_L00_00262570[D_00139568[i]]; + if ((D_00139868[i] >> 5) < e->limit || e->limit <= 0) + continue; + if (i == 31 || i == 9 || i == 45) { + func_L00_0037E6A8(i, D_L00_00262570[D_00139568[i]].value); + continue; + } + if (D_00189E20.weapon == i) { + if (!D_00189E20.f340) + continue; + if (D_001A7398[e->index] != i) + continue; + func_L00_0037E6A8(i, e->value); + continue; + } + } + if (func_00288D58()) { + Entry1269 *e = &D_L00_00262570[D_00139568[i]]; + if (!e->exists) + continue; + if (unlocked(e, i)) { + if (owned(i)) { + func_00287EB0(i); + func_US2_00288F30(i); + } + } + } else { + func_00287EB0(i); + func_US2_00288F30(i); + } + } + + if ((D_L00_001B19C8[0] & 127) || D_001A7A00[0x7A]) + return; + ok = 1; + for (i = 0; i < 56; i++) { + int ex = D_L00_00262570[D_00139568[i]].exists; + if (ex && i == 10) + continue; + if (ex && i == 9) + continue; + if (ex) { + if (!D_001A7AF8[i]) { + ok = 0; + break; + } + if (i == 14 || i == 12 || i == 17 || i == 18 || i == 53) + continue; + if (func_00287F60(i) <= 0) + continue; + if (!D_L00_00262570[D_00139568[i]].state) { + ok = 0; + break; + } + } + } + if (ok && !D_001A7A00[0x7A]) { + D_001A7A00[0x7A] = 1; + func_002E5E58(1, 0, 0); + func_US2_002B1880(0x1233, -1); + D_L00_001A903C = 1; + } +} diff --git a/src/us2/levels/shared/L00_002D6258.c b/src/us2/levels/shared/L00_002D6258.c index 8b8b5c12d..877bd62d6 100644 --- a/src/us2/levels/shared/L00_002D6258.c +++ b/src/us2/levels/shared/L00_002D6258.c @@ -1,3 +1,2 @@ #include "include_asm.h" -INCLUDE_ASM("asm/us2/levels", func_US2_L00_002D6258);