Files
goldeneye-pc-port/port/shim/bondaicommands.h
T
James Dansereau 52539d10de Prepare for public release
- README, LICENSE (MIT for the port layer), NOTICE, CONTRIBUTING.
- Restructure docs: internals.md, porting-notes.md, dev-process.md +
  dev/agentic-development.md (the research write-up), dev/ (raw record).
- Replace decomp status-badge CI with a PC-port validate workflow.
- Remove session/agent scaffolding and stale planning docs from the tree.
- Add .gitattributes (normalize line endings to LF); add screenshots.

The pre-2026-08 history is the GoldenEye 007 decompilation by Larry Ficken
(KholdFuzion) and contributors, preserved unchanged. The PC-port work from
2026-08-17 on is mine.
2026-09-01 19:28:59 -04:00

120 lines
6.2 KiB
C

/*
* PC port shim for bondaicommands.h (see docs/internals.md §11 D7).
*
* The real header is src/bondaicommands.h. It was written for the N64/IDO
* preprocessor, which is more lenient than GCC in two ways that break the
* build (only chraidata.c exercises these paths; it is the sole file that
* uses CALL()/SWITCH()):
*
* 1. isSubroutine() calls DEFINED(SETUPSUBROUTINES(ID)). CPPLib's DEFINED
* does `CAT(_DEFINED, _##x##_)`; with x = SETUPSUBROUTINES(ID) (a
* multi-token macro call) GCC hard-errors on the `##` paste
* ("pasting ')' and '_' does not give a valid preprocessing token").
* IDO evidently expanded/dropped the paste so the CPPLib special case
* `_DEFINED_SETUPSUBROUTINES(ID)` was reached. The intended semantics:
* emit `| SETUPSUBROUTINES(ID)` iff the setup defined SETUPSUBROUTINES.
* No file in this tree defines it, so the OR-clause is always absent.
*
* 2. SWITCH() declares 49 fixed parameters (VAR + 16x3 case slots) and
* detects present cases via IS_EMPTY(CASE_CONTENTx). Game code calls it
* with 14-26 args, relying on IDO treating missing args as empty. GCC
* enforces the exact count. Worse, its content arguments (e.g.
* `PlayAnimation(...) BREAK`) are single arguments at parse time that
* expand to top-level comma lists; any re-parse of the forwarded stream
* splits them across the fixed CASE/VAL/CONTENT slots and mangles the
* output. A GCC-clean reimplementation would have to partition that
* stream, which the C preprocessor cannot do (content arity is not
* detectable). See §11 D7 for the full analysis.
*
* 3. The generated CALL() (aicommands2.h) concatenates SetReturnAiList()
* and SetChrAiList(), both of which end in their own trailing comma,
* and then appends its own separator comma. IDO accepted the resulting
* `... , ,` inside the array initializer; GCC hard-errors ("expected
* expression before ',' token"). The artifact byte is never executed:
* AI_SetChrAiList(CHR_SELF) switches to the called list at offset 0 and
* AI_Return resumes the return list at offset 0 (chrai.c), so anything
* after the SetChrAiList record in CALL is dead. Re-emitted without the
* artifact (§11 D11).
*
* Resolution: the three active SWITCH() call sites in chraidata.c are
* `#ifdef PORT`-gated to the hand-written equivalents already present in the
* file (each verified byte-identical to the IDO expansion of the SWITCH
* call, including the IFNewRandomGreaterThan == SetNewRandom()+
* IFRandomGreaterThan identity). The original 49-parameter SWITCH is left
* defined but unused on the port; this shim replaces it with a marker that
* fails loudly if new game code ever uses SWITCH() on the port.
*
* This shim includes the real header, then redefines isSubroutine and CALL
* to be GCC-clean while preserving the original expansion exactly.
*
* Inert in the N64 build (no -DPORT): pure pass-through.
*/
#if defined(PORT)
#include "src/bondaicommands.h"
/* --- D7.1: isSubroutine without the DEFINED(SETUPSUBROUTINES(ID)) paste ---
*
* Replicates the original OR-chain, replacing the
* `IF(NOT(DEFINED(SETUPSUBROUTINES(ID))))(| SETUPSUBROUTINES(ID))` tail with
* _PORT_SS_OR(ID), which expands to `| SETUPSUBROUTINES(ID)` only when the
* setup defined SETUPSUBROUTINES before including this header (the documented
* pattern; see the comment above isSubroutine in the real header). #ifdef
* works on function-like macros, so this is a faithful "is it defined" test.
* When undefined (the case for every file in this tree) it expands to
* nothing, matching the IDO result of DEFINED(SETUPSUBROUTINES(ID)) == 1.
*/
#ifdef SETUPSUBROUTINES
# define _PORT_SS_OR(ID) | SETUPSUBROUTINES(ID)
#else
# define _PORT_SS_OR(ID)
#endif
#undef isSubroutine
#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) \
_PORT_SS_OR(ID))
/* --- D7.2: SWITCH() is not shimmed ---------------------------------------
*
* See the header comment: the original 49-parameter SWITCH cannot be made
* GCC-clean without partitioning the caller's already-expanded argument
* stream, which the C preprocessor cannot express. All active call sites in
* chraidata.c are `#ifdef PORT`-gated to byte-identical hand-written
* equivalents, so SWITCH is never expanded on the port. Redefine it as a
* marker that produces an obvious error if new game code ever uses it here.
*/
#undef SWITCH
#define SWITCH(...) _PORT_SWITCH_NOT_SUPPORTED_ON_PORT /* D7: use a #ifdef PORT hand-written equivalent in chraidata.c style; see docs/internals.md §11 */
/* --- D11: CALL() without the double trailing comma ------------------------
*
* The generated CALL() is:
*
* IF_ELSE(DEFINED(THIS))(AI_ERR_NO_THIS)((SetReturnAiList(THIS)SetChrAiList(CHR_SELF, X))),
*
* SetReturnAiList()/SetChrAiList() are byte-list macros that each end in a
* trailing comma, and CALL appends one more as the array-initializer
* separator. IDO's compiler accepted the resulting `...lo , ,` (empty
* initializer element); GCC rejects it. The byte(s) after the SetChrAiList
* record are never read: AI_SetChrAiList with CHR_NUM == CHR_SELF sets
* AiListp/Offset to the called list at offset 0, and AI_Return resumes the
* return list at offset 0 (chrai.c), so CALL's artifact is dead.
*
* This redefinition emits the identical command bytes directly —
* AI_SetReturnAiList + id16(THIS) + AI_SetChrAiList + CHR_SELF + id16(X) —
* keeping CALL's own separator comma and leaving the THIS-undefined
* diagnostic branch (AI_ERR_NO_THIS) untouched. The extra paren level is
* required: IF_ELSE's selected branch must be a single macro argument, and
* TRY_EXPAND() then strips the parens and re-emits the byte list flat.
*/
#undef CALL
#define CALL(AI_LIST_ID) IF_ELSE(DEFINED(THIS))(AI_ERR_NO_THIS)((AI_SetReturnAiList, CharArrayFrom16(THIS), AI_SetChrAiList, CHR_SELF, CharArrayFrom16(AI_LIST_ID))),
#else
#include "src/bondaicommands.h"
#endif