From 78d9b53a52e9bb2295792505867645b34119f7a4 Mon Sep 17 00:00:00 2001 From: LagoLunatic Date: Wed, 3 Sep 2025 22:20:36 -0400 Subject: [PATCH] Add hacks to fix PCH .data causing fake mismatches with decompctx --- configure.py | 7 +++ include/JSystem/J3DGraphAnimator/J3DJoint.h | 4 ++ include/JSystem/J3DGraphBase/J3DMatBlock.h | 4 ++ include/d/dolzel.h | 2 +- include/d/dolzel.pch | 8 ++++ include/d/dolzel_rel.h | 2 +- include/weak_bss_3569.h | 9 ++++ include/weak_data.h | 27 +++++++++++ .../MSL/MSL_C/MSL_Common/Include/math.h | 5 ++ tools/decompctx.py | 48 ++++++++++++++++++- tools/project.py | 26 ++++++++-- 11 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 include/weak_data.h diff --git a/configure.py b/configure.py index 36391eadf..89eaef7c4 100755 --- a/configure.py +++ b/configure.py @@ -191,6 +191,13 @@ config.reconfig_deps = [] # Can be overridden in libraries or objects config.scratch_preset_id = 72 # The Wind Waker (DOL) +# Globs to exclude from context files +# *.mch excludes precompiled header output (which cannot be parsed) +config.context_exclude_globs = ["*.mch"] + +# Macro definitions to inject into context files +config.context_defines = ["DECOMPCTX"] + # Base flags, common to most GC/Wii games. # Generally leave untouched, with overrides added below. cflags_base = [ diff --git a/include/JSystem/J3DGraphAnimator/J3DJoint.h b/include/JSystem/J3DGraphAnimator/J3DJoint.h index cc110153f..eb98d6965 100644 --- a/include/JSystem/J3DGraphAnimator/J3DJoint.h +++ b/include/JSystem/J3DGraphAnimator/J3DJoint.h @@ -51,7 +51,9 @@ public: virtual ~J3DMtxCalcBasic() {} virtual void init(const Vec& vec, const Mtx& mtx) { J3DSys::mCurrentS = vec; +#ifndef DECOMPCTX // Hack, see comment in dolzel.pch for details J3DSys::mParentS = (Vec){1.0f, 1.0f, 1.0f}; +#endif J3DSys::mCurrentMtx[0][0] = mtx[0][0] * J3DSys::mCurrentS.x; J3DSys::mCurrentMtx[0][1] = mtx[0][1] * J3DSys::mCurrentS.y; J3DSys::mCurrentMtx[0][2] = mtx[0][2] * J3DSys::mCurrentS.z; @@ -93,7 +95,9 @@ public: J3DMtxCalcMaya() : J3DMtxCalcBasic() {} virtual ~J3DMtxCalcMaya() {} virtual void init(const Vec& vec, const Mtx& mtx) { +#ifndef DECOMPCTX // Hack, see comment in dolzel.pch for details J3DSys::mParentS = (Vec){1.0f, 1.0f, 1.0f}; +#endif J3DSys::mCurrentS = vec; J3DSys::mCurrentMtx[0][0] = mtx[0][0] * J3DSys::mCurrentS.x; J3DSys::mCurrentMtx[0][1] = mtx[0][1] * J3DSys::mCurrentS.y; diff --git a/include/JSystem/J3DGraphBase/J3DMatBlock.h b/include/JSystem/J3DGraphBase/J3DMatBlock.h index 979cf465d..f7ff1a812 100644 --- a/include/JSystem/J3DGraphBase/J3DMatBlock.h +++ b/include/JSystem/J3DGraphBase/J3DMatBlock.h @@ -987,8 +987,12 @@ struct J3DColorChan { } J3DColorChan(u16 id) : mChanCtrl(id) {} u8 getAttnFn() const { +#ifdef DECOMPCTX // Hack, see comment in dolzel.pch for details + return 0; +#else u8 attnFnTbl[] = { GX_AF_NONE, GX_AF_SPEC, GX_AF_NONE, GX_AF_SPOT }; return attnFnTbl[(u32)(mChanCtrl & (3 << 9)) >> 9]; +#endif } u8 getDiffuseFn() const { return ((u32)(mChanCtrl & (3 << 7)) >> 7); } u8 getLightMask() const { return ((mChanCtrl >> 2) & 0x0f) | ((mChanCtrl >> 11) & 0x0f) << 4; } diff --git a/include/d/dolzel.h b/include/d/dolzel.h index d93de8926..4245726de 100644 --- a/include/d/dolzel.h +++ b/include/d/dolzel.h @@ -1,7 +1,7 @@ #ifndef DOLZEL_H #define DOLZEL_H -#ifdef __MWERKS__ +#if defined(__MWERKS__) && !defined(DECOMPCTX) #include "d/dolzel.mch" #else #include "d/dolzel.pch" diff --git a/include/d/dolzel.pch b/include/d/dolzel.pch index d8d034e96..608cceab3 100644 --- a/include/d/dolzel.pch +++ b/include/d/dolzel.pch @@ -1,6 +1,14 @@ #ifndef DOLZEL_PCH #define DOLZEL_PCH +#ifdef DECOMPCTX +// Hack to mitigate fake mismatches when building from decompctx output +// (which doesn't support precompiled headers). +// When built without a PCH, these constants would end up .rodata instead of .data +// which causes a variety of knock-on effects in individual functions' assembly. +#include "weak_data.h" // IWYU pragma: export +#endif + // Fixes weak .data #include "math.h" // IWYU pragma: export #include "JSystem/J3DGraphBase/J3DMatBlock.h" // IWYU pragma: export diff --git a/include/d/dolzel_rel.h b/include/d/dolzel_rel.h index 49d70ca55..341f7c179 100644 --- a/include/d/dolzel_rel.h +++ b/include/d/dolzel_rel.h @@ -1,7 +1,7 @@ #ifndef DOLZEL_REL_H #define DOLZEL_REL_H -#ifdef __MWERKS__ +#if defined(__MWERKS__) && !defined(DECOMPCTX) #include "d/dolzel_rel.mch" #else #include "d/dolzel_rel.pch" diff --git a/include/weak_bss_3569.h b/include/weak_bss_3569.h index 9475d52a0..399afcf76 100644 --- a/include/weak_bss_3569.h +++ b/include/weak_bss_3569.h @@ -6,10 +6,19 @@ // Goes in either .bss (for RELs) or .rodata (for main.dol: d_snap, J3DUClipper). // Its true source is currently unknown, so include this header in TUs that need it to match for now. +#ifdef DECOMPCTX // Hack, see comment in dolzel.pch for details +#include "dolphin/types.h" +template struct bss_3569 { u8 val[0xC]; }; + +static inline void dummy_bss_3569() { + bss_3569(); // @3569 +} +#else #include "dolphin/mtx/mtx.h" static inline void dummy_bss_3569() { Vec vec = (Vec){0.0f, 0.0f, 0.0f}; // @3569 / @3599 } +#endif #endif /* WEAK_BSS_3569_H */ diff --git a/include/weak_data.h b/include/weak_data.h new file mode 100644 index 000000000..6aa0ceca9 --- /dev/null +++ b/include/weak_data.h @@ -0,0 +1,27 @@ +#ifndef WEAK_DATA_H +#define WEAK_DATA_H + +#ifdef DECOMPCTX +#include "dolphin/mtx/mtx.h" + +static inline void fake_data_2100() { + static Vec data_2100 = {1.0f, 1.0f, 1.0f}; +} + +static inline void fake_data_2080() { + static Vec data_2080 = {1.0f, 1.0f, 1.0f}; +} + +#include "dolphin/gx/GXEnum.h" + +static inline void fake_getAttnFn() { + static u8 attnFnTbl_1811[] = { GX_AF_NONE, GX_AF_SPEC, GX_AF_NONE, GX_AF_SPOT }; +} + +extern inline void fake_sqrtf(float x) { + static double _three[1] = {3.0}; + static double _half[1] = {.5}; +} +#endif + +#endif /* WEAK_DATA_H */ diff --git a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/math.h b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/math.h index 3b10706d9..a4b714c2e 100644 --- a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/math.h +++ b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/math.h @@ -86,8 +86,13 @@ inline float tanf(float x) { return tan(x); } namespace std { #endif extern inline float sqrtf(float x) { +#ifdef DECOMPCTX // Hack, see comment in dolzel.pch for details + const double _half = .5; + const double _three = 3.0; +#else static const double _half = .5; static const double _three = 3.0; +#endif volatile float y; if (x > 0.0f) { double guess = __frsqrte((double)x); // returns an approximation to diff --git a/tools/decompctx.py b/tools/decompctx.py index f2f31dfac..02eb24505 100755 --- a/tools/decompctx.py +++ b/tools/decompctx.py @@ -11,6 +11,7 @@ ### import argparse +import fnmatch import os import re from typing import List @@ -19,6 +20,7 @@ script_dir = os.path.dirname(os.path.realpath(__file__)) root_dir = os.path.abspath(os.path.join(script_dir, "..")) src_dir = os.path.join(root_dir, "src") include_dirs: List[str] = [] # Set with -I flag +exclude_globs: List[str] = [] # Set with -x flag include_pattern = re.compile(r'^#\s*include\s*[<"](.+?)[>"]') guard_pattern = re.compile(r"^#\s*ifndef\s+(.*)$") @@ -28,6 +30,23 @@ defines = set() deps = [] +def generate_prelude(defines) -> str: + if len(defines) == 0: + return "" + + out_text = "/* decompctx prelude */\n" + for define in defines: + parts = define.split("=", 1) + if len(parts) == 2: + macro_name, macro_val = parts + out_text += f"#define {macro_name} {macro_val}\n" + else: + out_text += f"#define {parts[0]}\n" + out_text += "/* end decompctx prelude */\n\n" + + return out_text + + def import_h_file(in_file: str, r_path: str) -> str: rel_path = os.path.join(root_dir, r_path, in_file) if os.path.exists(rel_path): @@ -73,8 +92,17 @@ def process_file(in_file: str, lines: List[str]) -> str: print("Processing file", in_file) include_match = include_pattern.match(line.strip()) if include_match and not include_match[1].endswith(".s"): + excluded = False + for glob in exclude_globs: + if fnmatch.fnmatch(include_match[1], glob): + excluded = True + break + out_text += f'/* "{in_file}" line {idx} "{include_match[1]}" */\n' - out_text += import_h_file(include_match[1], os.path.dirname(in_file)) + if excluded: + out_text += "/* Skipped excluded file */\n" + else: + out_text += import_h_file(include_match[1], os.path.dirname(in_file)) out_text += f'/* end "{include_match[1]}" */\n' else: out_text += line @@ -111,13 +139,29 @@ def main(): help="""Include directory""", action="append", ) + parser.add_argument( + "-x", + "--exclude", + help="""Excluded file name glob""", + action="append", + ) + parser.add_argument( + "-D", + "--define", + help="""Macro definition""", + action="append", + ) args = parser.parse_args() if args.include is None: exit("No include directories specified") global include_dirs include_dirs = args.include - output = import_c_file(args.c_file) + global exclude_globs + exclude_globs = args.exclude or [] + prelude_defines = args.define or [] + output = generate_prelude(prelude_defines) + output += import_c_file(args.c_file) with open(os.path.join(root_dir, args.output), "w", encoding="utf-8") as f: f.write(output) diff --git a/tools/project.py b/tools/project.py index 2d00df463..9c8bcb342 100644 --- a/tools/project.py +++ b/tools/project.py @@ -198,6 +198,12 @@ class ProjectConfig: self.link_order_callback: Optional[Callable[[int, List[str]], List[str]]] = ( None # Callback to add/remove/reorder units within a module ) + self.context_exclude_globs: List[str] = ( + [] # Globs to exclude from context files + ) + self.context_defines: List[str] = ( + [] # Macros to define at the top of context files + ) # Progress output and report.json config self.progress = True # Enable report.json generation and CLI progress output @@ -492,7 +498,7 @@ def generate_build_ninja( decompctx = config.tools_dir / "decompctx.py" n.rule( name="decompctx", - command=f"$python {decompctx} $in -o $out -d $out.d $includes", + command=f"$python {decompctx} $in -o $out -d $out.d $includes $excludes $defines", description="CTX $in", depfile="$out.d", deps="gcc", @@ -1048,12 +1054,19 @@ def generate_build_ninja( ): include_dirs.append(flag[3:]) includes = " ".join([f"-I {d}" for d in include_dirs]) + excludes = " ".join([f"-x {d}" for d in config.context_exclude_globs]) + defines = " ".join([f"-D {d}" for d in config.context_defines]) + n.build( outputs=obj.ctx_path, rule="decompctx", inputs=src_path, implicit=decompctx, - variables={"includes": includes}, + variables={ + "includes": includes, + "excludes": excludes, + "defines": defines, + }, ) n.newline() @@ -1545,13 +1558,20 @@ def generate_objdiff_config( "build_target": False, "watch_patterns": [ "*.c", + "*.cc", "*.cp", "*.cpp", + "*.cxx", + "*.c++", "*.h", + "*.hh", + "*.hp", "*.hpp", - "*.inc", + "*.hxx", + "*.h++", "*.pch", "*.pch++", + "*.inc", "*.py", "*.yml", "*.txt",