mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
- 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.
120 lines
6.2 KiB
C
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
|