mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
WS4 level sweep driven to completion: 12/21 solo levels PASS (load + render + no-crash). Full matrix in docs/LEVEL-STATUS.md, reusable runner tools_pc/level_sweep.sh, WS6 checklist docs/LEVEL-PLAYTEST.md. Fixes: - D123 (tools_pc/d88_propdefs.py): crash class C1 on 6 levels (Dam/Runway/Frigate/Statue/Streets/Cradle). D122 fallout - the widened Vehichle/AircraftRecord.ailist slot was zeroed instead of carrying its read-before-write integer AI-list id, so prop.c -> ailistFindById(0) -> GAILIST_AIM_AT_BOND -> ai() ran a CHR aim list with a NULL chr -> chrIsNotDeadOrShot(NULL). New OBJ_ID_WORDS arm. 4 levels -> PASS, Runway falls through to C2. BUNKER1/Silo unregressed. - D124 (port/src/gimgfixup.c): crash class C2 on Jungle. Compiled explosion-DL pointer-sync keyed on an IMAGESEG marker that texLoad() had already erased -> compiled globalDL_0xNNN arrays kept link-time IMAGESEG words -> 0xABCDxxxx into fast3d on first explosion draw. Latent on every level. Detect the slot from the compiled array instead. Diagnosed, NOT fixed (write-ups in PCPortResearch.md SF): - C2-GDL (D124-Facility addendum): Facility/Runway - model texture-pool path yields a garbage tex->data; every model file loads at an odd bank base (mempAllocBytesInBank does no alignment). Suspect d43_emit.py/pcmodels sidecar texture-blob offsets (N64 8B vs PC 16B Gfx). - C3/C6 (D125): Aztec/Bunker2/Surface2 - converted propDefs blob in RAM doesn't match d88_propdefs.py offline output (garbage after record 0); runtime sizepropdef() walk drifts ~100 records -> wrong door/modelid. d88_propdefs.py convert_stream + sizepropdef() PORT strides verified correct; suspect a dest-offset/pd_end bug in d88_emit.py propdefs emit. Remaining: 9 crashes / 6 classes (C2-GDL, C3/C6, C2m Jungle G_MTX, C5 Control BG portal, C4 Depot BG tile/room, C7 Surface1 sndSetupSound). Priority order in docs/LEVEL-STATUS.md. Also folds in the in-progress docs restructure (HANDOFF/AGENTS/CLAUDE, PCPortResearch D121/D122 SF/SH entries, PLAN-linear-level-sweep, HANDOFF-ARCHIVE, BACKLOG).
158 lines
7.4 KiB
Markdown
158 lines
7.4 KiB
Markdown
> **RESOLVED — historical.** D7 closed at the compile+link milestone.
|
||
> Kept for the AI X-macro analysis. Not active work.
|
||
|
||
# HANDOFF — AI X-macro system (D7), the last build blocker
|
||
|
||
## Status
|
||
|
||
Full `ge007` build: **224 objects compile; only `chraidata.c` fails.** All other
|
||
`.c` files build clean. Findings D1–D6 are resolved and recorded in
|
||
`docs/PCPortResearch.md` §11. D7 (this file) is the remaining blocker.
|
||
|
||
Build command:
|
||
```sh
|
||
export PATH="/c/msys64/mingw64/bin:$PATH"
|
||
cd build-pc && cmake --build . --target ge007 2>&1 | grep -A3 -i "chraidata\|aicommands\|bondaicommands"
|
||
```
|
||
|
||
## The failing file and its includes
|
||
|
||
`src/game/chraidata.c` includes (lines 14–17):
|
||
```c
|
||
#include <ultra64.h>
|
||
#include <bondgame.h>
|
||
#include <bondaicommands.h> // <- pulls in the whole AI X-macro system
|
||
#include "chrobjdata.h"
|
||
```
|
||
|
||
`bondaicommands.h` includes:
|
||
- `bondconstants.h` (which at line 17 includes `<CPPLib.h>`, and at line 731
|
||
includes `"aicommands.def"` to build the `AI_CMD` enum)
|
||
- `aicommands2.h` (line 864) — a **pre-generated** header (from
|
||
`tools/cmdbuilder.c`), already preprocessed.
|
||
|
||
So the failing path is the **raw** `aicommands.def` include (bondconstants.h:731)
|
||
plus the `bondaicommands.h` macros (`isSubroutine`, `SWITCH`, `CALL`, …) that
|
||
chraidata.c uses directly.
|
||
|
||
## Exact error chain (verified by rebuild)
|
||
|
||
Trigger: `CALL(GAILIST_PLAY_IDLE_ANIMATION)` at chraidata.c:114 (and similar at
|
||
123, 126, …). The chain:
|
||
|
||
1. `CALL(...)` expands to code that calls `isSubroutine(<ID>)`.
|
||
2. `isSubroutine(ID)` (bondaicommands.h:275–284) contains:
|
||
```c
|
||
#define isSubroutine(ID) ((ID == GAILIST_PLAY_IDLE_ANIMATION) |\
|
||
(ID == GAILIST_BASH_KEYBOARD) | \
|
||
(ID == GAILIST_ATTACK_BOND) | \
|
||
(ID == GAILIST_RUN_TO_BOND) | \
|
||
(ID == GAILIST_STARTLE_AND_RUN_TO_BOND) | \
|
||
(ID == GAILIST_WAIT_ONE_SECOND) \
|
||
IF(NOT(DEFINED(SETUPSUBROUTINES(ID))))\
|
||
(\
|
||
| SETUPSUBROUTINES(ID)\
|
||
))
|
||
```
|
||
3. `DEFINED(x)` (CPPLib.h:279–280):
|
||
```c
|
||
#define DEFINED(x) _DEFINED(x)
|
||
#define _DEFINED(x) IS_PROBE(CAT(_DEFINED, _##x##_))
|
||
```
|
||
With `x = SETUPSUBROUTINES(ID)`, the `##` pasting `_##x##_` tries to paste
|
||
`_` against `SETUPSUBROUTINES(ID)` — a **macro call (multiple tokens)**, not a
|
||
single pp-token. GCC hard-errors:
|
||
```
|
||
bondaicommands.h:281:63: error: pasting ')' and '_' does not give a valid
|
||
preprocessing token
|
||
```
|
||
4. CPPLib.h:287 has a special case that was *meant* to handle this:
|
||
```c
|
||
#define _DEFINED_SETUPSUBROUTINES(ID) PROBE(~),
|
||
```
|
||
i.e. the intent is that `_##SETUPSUBROUTINES(ID)##_` →
|
||
`_DEFINED_SETUPSUBROUTINES(ID)_` → the defined macro above. But GCC's `##`
|
||
requires each side to be a single pp-token, so it never reaches that special
|
||
case. IDO's preprocessor evidently tolerated / expanded this differently.
|
||
|
||
### Secondary errors (same root family)
|
||
|
||
- `chraidata.c:183/202/249/640`: `macro 'SWITCH' requires 49 arguments, but only
|
||
20/14/26/26 given`. The `SWITCH` macro (bondaicommands.h, ~line 776) has **49
|
||
fixed parameters** (1 VAR + 16×3 case slots) and uses `IF_VA(NOT(IS_EMPTY(..)))`
|
||
to detect which cases are present. Game code calls it with fewer args, relying
|
||
on IDO leniency. GCC enforces the exact arg count.
|
||
- `aicommands.def:514:270 / 550:21 / 2846:21`: "expected expression before ','"
|
||
/ "expected '}' before 'AI_Return'" / "expected '}' before
|
||
'AI_IFICouldSeeBond'". These are downstream of the `isSubroutine`/`DEFINED`
|
||
failure (the `CALL` expansion aborts mid-macro, leaving dangling tokens). Note
|
||
the reported line numbers (514/550/2846) do **not** match the raw
|
||
`aicommands.def` lines (those are comment/brace lines) — they are positions in
|
||
the *expanded* macro text, not the source file. Don't chase them as source
|
||
lines.
|
||
|
||
## Key facts / ground truth
|
||
|
||
- `aicommands.def` call counts: **255** `_AI_CMD(`, **119** `_AI_CMD_POLYMORPH(`,
|
||
**2** `_AI_DEBUG(`.
|
||
- `_AI_CMD_POLYMORPH` definitions across the tree:
|
||
- `aicommands.def:97` — 4-arg `(PRE, NAME, CMD_PARAMS_POST, POLY_PARAMS)` but
|
||
inside a `#if 0` block (lines 88–100) → **inactive**.
|
||
- `bondconstants.h:721` — 5-arg `(CMD, A, P, Q, D)` under `#ifdef __sgi`
|
||
(not us).
|
||
- `bondconstants.h:726` — **variadic `(...)`** under `#else` (this is the one
|
||
active for GCC when building the `AI_CMD` enum at bondconstants.h:731).
|
||
- `chrai.c:170` — 5-arg `(CMD, A, P, Q, D)`.
|
||
- `chrai.c:915` — 5-arg `(C, N, P1, P2, D)`.
|
||
- `aicommands.def:85–87`: `#ifndef __GNUC__ #include <CPPLib.h> #endif` → for GCC
|
||
CPPLib.h is **not** included from aicommands.def itself; it comes in via
|
||
`bondconstants.h:17`.
|
||
- `aicommands.def:11888`: `#undef _AI_CMD_POLYMORPH` (late in the file).
|
||
- `aicommands2.h` is pre-generated (header says "Do not edit … generated by
|
||
cmdbuilder from src/aicommands.def"). It is included by bondaicommands.h:864
|
||
and deb_loadallmodels.c:8. It is **not** the failing path.
|
||
- `isSubroutine` is the only macro that feeds a *macro call*
|
||
(`SETUPSUBROUTINES(ID)`) into `DEFINED()`. That is the unique trigger for the
|
||
`##` pasting error.
|
||
|
||
## Recommended next steps (in order)
|
||
|
||
1. **Fix the `DEFINED(SETUPSUBROUTINES(ID))` pasting** — the single root cause of
|
||
the `CALL`/`isSubroutine` errors. Preferred: a `port/` shim that redefines
|
||
`DEFINED` (or `_DEFINED`) *after* CPPLib.h is included, special-casing the
|
||
`SETUPSUBROUTINES(ID)` argument so it does not `##`-paste a multi-token
|
||
macro-call. Sketch:
|
||
```c
|
||
/* port/shim/... after <CPPLib.h> */
|
||
#undef DEFINED
|
||
#define DEFINED(x) _PORT_DEFINED(x)
|
||
#define _PORT_DEFINED(x) /* if x is SETUPSUBROUTINES(ID) -> PROBE(~), else fall back */
|
||
```
|
||
The tricky part: detecting "x is exactly `SETUPSUBROUTINES(ID)`" in the
|
||
preprocessor. Alternative: redefine `isSubroutine` in the shim to compute the
|
||
`SETUPSUBROUTINES` presence without `DEFINED()` (e.g. via a per-setup
|
||
`#define HAS_SETUPSUBROUTINES 0/1` flag set before the include). Investigate
|
||
which is less invasive before committing.
|
||
- Constraint: `bondaicommands.h` and `CPPLib.h` are game files — do **not**
|
||
edit them. All fixes go in `port/` (shim headers found first via include
|
||
path) or, if truly unavoidable, a `-DPORT`-gated `src/` change with a
|
||
`PC port:` comment.
|
||
2. **Fix the `SWITCH` 49-arg requirement** — make a shim `SWITCH` variadic (or
|
||
pad the call sites). Verify the `IF_VA`/`IS_EMPTY` case-detection still works
|
||
under GCC once variadic.
|
||
3. **Re-verify the aicommands.def:514/550/2846 errors disappear** once (1) and
|
||
(2) are in — they are likely downstream of the `CALL` expansion abort, not
|
||
independent bugs.
|
||
4. **Rebuild** and confirm `chraidata.c` compiles → full `ge007` links.
|
||
5. **Record the resolution** in `docs/PCPortResearch.md` §11 D7 (replace the
|
||
"OPEN" / "TBD" text) and delete this handoff file.
|
||
|
||
## Do NOT
|
||
|
||
- Do not edit `tools/`, `Makefile`, `rsp/`, `ld/` (N64 build, non-negotiable).
|
||
- Do not edit `bondaicommands.h`, `bondconstants.h`, `CPPLib.h`,
|
||
`aicommands.def`, `aicommands2.h` unless a `-DPORT`-gated, layout/semantics-
|
||
preserving change is truly unavoidable (and then flag it `PC port:`).
|
||
- Do not chase the aicommands.def:514/550/2846 line numbers as source lines
|
||
(they are expanded-macro positions).
|