Files
goldeneye-pc-port/docs/HANDOFF-ai-xmacro.md
T
James Dansereau 5ea46fabc1 PC port: full compile + link milestone (ge007.x86_64.exe builds clean)
All ~235 translation units now compile and the target links under MSYS2
GCC 16 + CMake/Ninja (ninja ge007 -k 0: 236/236, zero errors). N64 build
untouched; every src/ change is -DPORT-gated.

Portability fixes (findings D7-D17 in docs/PCPortResearch.md section 11):
- AI X-macro system: SWITCH() call sites activated to the author's
  hand-written byte-identical references under PORT; port/shim/
  bondaicommands.h + bondconstants.h + CPPLib.h re-emit the IDO paste
  tricks (isSubroutine, file-record macros, generated CALL) GCC-clean.
- 64-bit pointer-in-word initializers: monAnim jump targets now go
  through an index table resolved by the interpreter (chrai.h/propobj.c);
  UsetuplenZ.c credits pointer PORT-gated to 0.
- IDO printf engine compiled for PC (xprintf/xlitob/xldtob) with a
  shadow xstdio.h resolving the _Printf prototype clash.
- Host-libc gaps stubbed in port/: bcopy/bzero, __libm_qnan_f,
  tlbmanage*, g_ViXScales/YScales; chrObjRandom PRNG ported from .s.

Asset data strategy (D16): single-file assets compiled for real
(animation tables, fonts, textures, rareware logo); obseg per-file .c
excluded (colliding file-local globals the N64 build isolates via
per-ELF linking); 758 ROM-derived table symbols + ramrom_* + segment
markers stubbed in port/src/assetstubs.c / n64stubs.c.

fast3d is a no-op placeholder (port/src/gfxstub.c) until Phase 2 brings
in the real software RSP. Binary not yet runnable end-to-end: ROM
loading in romdata.c is still a TODO.
2026-08-20 20:27:06 -04:00

7.2 KiB
Raw Blame History

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:

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):

#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:
    #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):
    #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:
    #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.
  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:
    /* 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).