Files
goldeneye-pc-port/docs/HANDOFF-ai-xmacro.md
T
James Dansereau 9d118e0b13 M-13: 21-level sweep matrix + D123 (C1 fix) + D124 (C2 Jungle fix)
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).
2026-08-29 16:36:18 -04:00

158 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> **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).