diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt index 98f90ab3..9f58be2c 100644 --- a/config/SOUE01/symbols.txt +++ b/config/SOUE01/symbols.txt @@ -17211,7 +17211,7 @@ addCalcAngleT__4sLibFPsssss_s = .text:0x802DE8F0; // type:function size:0xB4 addCalcAngle__4sLibFPssss = .text:0x802DE9B0; // type:function size:0x10 addCalcAngleT__4sLibFPssss_v = .text:0x802DE9C0; // type:function size:0x4C fn_802DEA10 = .text:0x802DEA10; // type:function size:0x164 -fn_802DEB80 = .text:0x802DEB80; // type:function size:0x6C +chaseUC__4sLibFPUcUcUc = .text:0x802DEB80; // type:function size:0x6C chase__4sLibFPsss = .text:0x802DEBF0; // type:function size:0xC chaseT__4sLibFPsss_i = .text:0x802DEC00; // type:function size:0x58 chase__4sLibFPiii = .text:0x802DEC60; // type:function size:0x4 diff --git a/configure.py b/configure.py index 6e94cf01..5946d12b 100644 --- a/configure.py +++ b/configure.py @@ -71,11 +71,6 @@ parser.add_argument( action="store_true", help="generate map file(s)", ) -parser.add_argument( - "--no-asm", - action="store_true", - help="don't incorporate .s files from asm directory", -) parser.add_argument( "--debug", action="store_true", @@ -94,6 +89,12 @@ parser.add_argument( type=Path, help="path to decomp-toolkit binary or source (optional)", ) +parser.add_argument( + "--objdiff", + metavar="BINARY | DIR", + type=Path, + help="path to objdiff-cli binary or source (optional)", +) parser.add_argument( "--sjiswrap", metavar="EXE", @@ -128,15 +129,16 @@ config.non_matching = args.non_matching config.sjiswrap_path = args.sjiswrap if not is_windows(): config.wrapper = args.wrapper -if args.no_asm: +if not config.non_matching: config.asm_dir = None # Tool versions config.binutils_tag = "2.42-1" -config.compilers_tag = "20231018" -config.dtk_tag = "v0.9.0" +config.compilers_tag = "20240706" +config.dtk_tag = "v0.9.4" +config.objdiff_tag = "v2.0.0-beta.3" config.sjiswrap_tag = "v1.1.1" -config.wibo_tag = "0.6.14" +config.wibo_tag = "0.6.11" # Project config.config_path = Path("config") / config.version / "config.yml" @@ -286,9 +288,10 @@ def nw4rLib(lib_name, objects, extra_cflags=[]): Matching = True NonMatching = False +Equivalent = config.non_matching # Object should be linked when configured with --non-matching -config.warn_missing_config = False -config.warn_missing_source = False # TODO +config.warn_missing_config = False +config.warn_missing_source = False config.libs = [ { "lib": "framework", @@ -335,6 +338,17 @@ config.libs = [ Object(NonMatching, "f/f_base.cpp"), Object(Matching, "f/f_list.cpp"), Object(Matching, "f/f_manager.cpp"), + Object(Matching, "DynamicLink.cpp"), + # framework (f_name) + # d stuff (d_name) + ], + }, + { + "lib": "mlib", + "mw_version": "Wii/1.5", + "cflags": cflags_framework, + "host": False, + "objects": [ Object(Matching, "m/m_allocator.cpp"), Object(Matching, "m/m_angle.cpp"), Object(Matching, "m/m_color_fader.cpp"), @@ -344,15 +358,20 @@ config.libs = [ Object(Matching, "m/m_heap.cpp"), Object(NonMatching, "m/m_mtx.cpp"), Object(Matching, "m/m_pad.cpp"), + ], + }, + { + "lib": "slib", + "mw_version": "Wii/1.5", + "cflags": cflags_framework, + "host": False, + "objects": [ Object(Matching, "s/s_Crc.cpp"), Object(NonMatching, "s/s_Math.cpp"), Object(Matching, "s/s_StateId.cpp"), Object(Matching, "s/s_StateMethod.cpp"), Object(Matching, "s/s_StateMethodUsr_FI.cpp"), Object(Matching, "s/s_Phase.cpp"), - Object(Matching, "DynamicLink.cpp"), - # framework (f_name) - # d stuff (d_name) ], }, # DolphinLib( diff --git a/include/s/s_Math.h b/include/s/s_Math.h index bd49c2a3..78d7c1d4 100644 --- a/include/s/s_Math.h +++ b/include/s/s_Math.h @@ -13,13 +13,13 @@ BOOL chase(int *value, int target, int stepSize); BOOL chase(float *value, float target, float stepSize); BOOL chaseAngle(short *value, short target, short stepSize); - // Inofficial names float extrapolate(float start, float end, float scale); BOOL isInRange(float val, float min, float max); void addCalcScaledDiff(float *value, float target, float ratio, float maxStepSize); void addCalcScaled(float *value, float stepSize, float maxStep); int absDiff(short a1, short a2); +BOOL chaseUC(u8 *value, u8 target, u8 stepSize); } // namespace sLib diff --git a/src/s/s_Math.cpp b/src/s/s_Math.cpp index 064ee233..ebc8504b 100644 --- a/src/s/s_Math.cpp +++ b/src/s/s_Math.cpp @@ -80,25 +80,25 @@ T addCalcAngleT(T *value, T target, T ratio, T maxStepSize, T minStepSize) { T step = diff / ratio; // TODO this is simpler in the original assembly if (step > minStepSize || step < -minStepSize) { - T actualStep = maxStepSize; - if (maxStepSize > step) { - actualStep = step; - if (step < -maxStepSize) { - actualStep = -maxStepSize; - } - } - *value += actualStep; - } else if (target - *value >= 0) { - T newVal = *value + minStepSize; - *value = newVal; - if ((T)(target - newVal) <= 0) { - *value = target; + if (step > maxStepSize) { + step = maxStepSize; + } else if (step < -maxStepSize) { + step = -maxStepSize; } + *value += step; } else { - T newVal = *value - minStepSize; - *value = newVal; - if ((T)(target - newVal) >= 0) { - *value = target; + if (0 <= diff) { + T newVal = *value + minStepSize; + *value = newVal; + if ((T)(target - *value) <= 0) { + *value = target; + } + } else { + T newVal = *value - minStepSize; + *value = newVal; + if ((T)(target - newVal) >= 0) { + *value = target; + } } } } @@ -127,25 +127,37 @@ void addCalcAngle(short *value, short target, short ratio, short maxStepSize) { return addCalcAngleT(value, target, ratio, maxStepSize); } -extern "C" BOOL fn_802DEB80(u8 *value, u8 target, u8 stepSize) { - if (stepSize != 0) { +extern "C" s16 fn_802DEA10(s16 *pValue, s16 target, s16 scale, s16 maxStep, s16 minStep) { + // NYI +} + +// Is the same as cLib_chaseUC +BOOL chaseUC(u8 *value, u8 target, u8 stepSize) { + if (stepSize) { s16 val = *value; s16 tgt = target; - s16 szs = stepSize; + s16 szs; + if (val > tgt) { szs = -stepSize; + } else { + szs = stepSize; } - s16 step = val + szs; - if (szs * (step - tgt) >= 0) { - *value = tgt; - return 1; + + val += szs; + + if (szs * (val - tgt) >= 0) { + *value = target; + return TRUE; + } else { + *value = val; } - *value = step; - } else if (*value == target) { - return 1; } - return 0; + else if (*value == target) { + return TRUE; + } + return FALSE; } template @@ -154,7 +166,7 @@ BOOL chaseT(T *value, T target, T stepSize) { return 1; } - if (stepSize != 0) { + if (stepSize) { if (*value > target) { stepSize = -stepSize; } @@ -186,9 +198,9 @@ template BOOL isInRangeT(T val, T min, T max) { BOOL ret; if (min < max) { - return val >= min && val <= max ? 1 : 0; + return val >= min && val <= max; } else { - return val >= max && val <= min ? 1 : 0; + return val >= max && val <= min; } return ret; } @@ -220,30 +232,34 @@ BOOL chaseAngle(short *value, short target, short stepSize) { // Found in NSMBW (0x801618c0), but no symbol name found yet extern "C" BOOL fn_802DEE10(short *value, short target, short stepSize) { if (*value == target) { - return 1; + return TRUE; } - if (stepSize != 0) { - short szs = stepSize; + if (stepSize) { + short step = stepSize; + if (stepSize < 0) { - szs = 0x7fff; if (stepSize != 0x8000) { - szs = -stepSize; + stepSize = -stepSize; + } else { + stepSize = 0x7FFF; } } + if ((short)(*value - target) > 0) { - szs = -szs; + stepSize = -stepSize; } - *value += stepSize; - int b = stepSize * szs; - if (b > 0 && stepSize * (short)(*value - target) >= 0) { - *value = target; - return 1; + *value += step; + + if (step * stepSize > 0) { + if (step * (short)(*value - target) >= 0) { + *value = target; + return TRUE; + } } } - - return 0; + return FALSE; } } // namespace sLib diff --git a/tools/decompctx.py b/tools/decompctx.py index dc17d0ee..290946f1 100644 --- a/tools/decompctx.py +++ b/tools/decompctx.py @@ -20,12 +20,11 @@ root_dir = os.path.abspath(os.path.join(script_dir, "..")) src_dir = os.path.join(root_dir, "src") include_dirs = [ os.path.join(root_dir, "include"), - os.path.join(root_dir, "include/MSL_C"), # Add additional include directories here ] -include_pattern = re.compile(r'^#include\s*[<"](.+?)[>"]$') -guard_pattern = re.compile(r"^#ifndef\s+(.*)$") +include_pattern = re.compile(r'^#\s*include\s*[<"](.+?)[>"]$') +guard_pattern = re.compile(r"^#\s*ifndef\s+(.*)$") defines = set() diff --git a/tools/download_tool.py b/tools/download_tool.py index 7b386a4b..69ef96a7 100644 --- a/tools/download_tool.py +++ b/tools/download_tool.py @@ -55,6 +55,21 @@ def dtk_url(tag: str) -> str: repo = "https://github.com/encounter/decomp-toolkit" return f"{repo}/releases/download/{tag}/dtk-{system}-{arch}{suffix}" +def objdiff_cli_url(tag: str) -> str: + uname = platform.uname() + suffix = "" + system = uname.system.lower() + if system == "darwin": + system = "macos" + elif system == "windows": + suffix = ".exe" + arch = uname.machine.lower() + if arch == "amd64": + arch = "x86_64" + + repo = "https://github.com/encounter/objdiff" + return f"{repo}/releases/download/{tag}/objdiff-cli-{system}-{arch}{suffix}" + def sjiswrap_url(tag: str) -> str: repo = "https://github.com/encounter/sjiswrap" @@ -70,6 +85,7 @@ TOOLS: Dict[str, Callable[[str], str]] = { "binutils": binutils_url, "compilers": compilers_url, "dtk": dtk_url, + "objdiff-cli": objdiff_cli_url, "sjiswrap": sjiswrap_url, "wibo": wibo_url, } diff --git a/tools/project.py b/tools/project.py index 15795f1f..373285cf 100644 --- a/tools/project.py +++ b/tools/project.py @@ -69,6 +69,8 @@ class ProjectConfig: self.wrapper: Optional[Path] = None # If None, download wibo on Linux self.sjiswrap_tag: Optional[str] = None # Git tag self.sjiswrap_path: Optional[Path] = None # If None, download + self.objdiff_tag: Optional[str] = None # Git tag + self.objdiff_path: Optional[Path] = None # If None, download # Project config self.non_matching: bool = False @@ -85,15 +87,21 @@ class ProjectConfig: self.warn_missing_config: bool = False # Warn on missing unit configuration self.warn_missing_source: bool = False # Warn on missing source file self.rel_strip_partial: bool = True # Generate PLFs with -strip_partial - self.rel_empty_file: Optional[ - str - ] = None # Object name for generating empty RELs + self.rel_empty_file: Optional[str] = ( + None # Object name for generating empty RELs + ) self.shift_jis = ( True # Convert source files from UTF-8 to Shift JIS automatically ) self.reconfig_deps: Optional[List[Path]] = ( None # Additional re-configuration dependency files ) + self.custom_build_rules: Optional[List[Dict[str, Any]]] = ( + None # Custom ninja build rules + ) + self.custom_build_steps: Optional[Dict[str, List[Dict[str, Any]]]] = ( + None # Custom build steps, types are ["pre-compile", "post-compile", "post-link", "post-build"] + ) # Progress output and progress.json config self.progress_all: bool = True # Include combined "all" category @@ -231,6 +239,7 @@ def generate_build_ninja( build_path = config.out_path() progress_path = build_path / "progress.json" + report_path = build_path / "report.json" build_tools_path = config.build_dir / "tools" download_tool = config.tools_dir / "download_tool.py" n.rule( @@ -248,17 +257,27 @@ def generate_build_ninja( deps="gcc", ) + cargo_rule_written = False + + def write_cargo_rule(): + nonlocal cargo_rule_written + if not cargo_rule_written: + n.pool("cargo", 1) + n.rule( + name="cargo", + command="cargo build --release --manifest-path $in --bin $bin --target-dir $target", + description="CARGO $bin", + pool="cargo", + depfile=Path("$target") / "release" / "$bin.d", + deps="gcc", + ) + cargo_rule_written = True + if config.dtk_path is not None and config.dtk_path.is_file(): dtk = config.dtk_path elif config.dtk_path is not None: dtk = build_tools_path / "release" / f"dtk{EXE}" - n.rule( - name="cargo", - command="cargo build --release --manifest-path $in --bin $bin --target-dir $target", - description="CARGO $bin", - depfile=Path("$target") / "release" / "$bin.d", - deps="gcc", - ) + write_cargo_rule() n.build( outputs=dtk, rule="cargo", @@ -283,6 +302,35 @@ def generate_build_ninja( else: sys.exit("ProjectConfig.dtk_tag missing") + if config.objdiff_path is not None and config.objdiff_path.is_file(): + objdiff = config.objdiff_path + elif config.objdiff_path is not None: + objdiff = build_tools_path / "release" / f"objdiff-cli{EXE}" + write_cargo_rule() + n.build( + outputs=objdiff, + rule="cargo", + inputs=config.objdiff_path / "Cargo.toml", + implicit=config.objdiff_path / "Cargo.lock", + variables={ + "bin": "objdiff-cli", + "target": build_tools_path, + }, + ) + elif config.objdiff_tag: + objdiff = build_tools_path / f"objdiff-cli{EXE}" + n.build( + outputs=objdiff, + rule="download_tool", + implicit=download_tool, + variables={ + "tool": "objdiff-cli", + "tag": config.objdiff_tag, + }, + ) + else: + sys.exit("ProjectConfig.objdiff_tag missing") + if config.sjiswrap_path: sjiswrap = config.sjiswrap_path elif config.sjiswrap_tag: @@ -361,6 +409,17 @@ def generate_build_ninja( n.newline() + ### + # Helper rule for downloading all tools + ### + n.comment("Download all tools") + n.build( + outputs="tools", + rule="phony", + inputs=[dtk, sjiswrap, wrapper, compilers, binutils, objdiff], + ) + n.newline() + ### # Build rules ### @@ -443,6 +502,49 @@ def generate_build_ninja( ) n.newline() + if len(config.custom_build_rules or {}) > 0: + n.comment("Custom project build rules (pre/post-processing)") + for rule in config.custom_build_rules or {}: + n.rule( + name=rule.get("name"), + command=rule.get("command"), + description=rule.get("description", None), + depfile=rule.get("depfile", None), + generator=rule.get("generator", False), + pool=rule.get("pool", None), + restat=rule.get("restat", False), + rspfile=rule.get("rspfile", None), + rspfile_content=rule.get("rspfile_content", None), + deps=rule.get("deps", None), + ) + n.newline() + + def write_custom_step(step: str) -> List[str]: + implicit = [] + if config.custom_build_steps and step in config.custom_build_steps: + n.comment(f"Custom build steps ({step})") + for custom_step in config.custom_build_steps[step]: + outputs = custom_step.get("outputs") + + if isinstance(outputs, list): + implicit.extend(outputs) + else: + implicit.append(outputs) + + n.build( + outputs=outputs, + rule=custom_step.get("rule"), + inputs=custom_step.get("inputs", None), + implicit=custom_step.get("implicit", None), + order_only=custom_step.get("order_only", None), + variables=custom_step.get("variables", None), + implicit_outputs=custom_step.get("implicit_outputs", None), + pool=custom_step.get("pool", None), + dyndep=custom_step.get("dyndep", None), + ) + n.newline() + return implicit + n.comment("Host build") n.variable("host_cflags", "-I include -Wno-trigraphs") n.variable( @@ -461,6 +563,9 @@ def generate_build_ninja( ) n.newline() + # Add all build steps needed before we compile (e.g. processing assets) + precompile_implicit = write_custom_step("pre-compile") + ### # Source files ### @@ -511,16 +616,15 @@ def generate_build_ninja( outputs=elf_path, rule="link", inputs=self.inputs, - implicit=[self.ldscript, *mwld_implicit], + implicit=[ + *precompile_implicit, + self.ldscript, + *mwld_implicit, + *postcompile_implicit, + ], implicit_outputs=elf_map, variables={"ldflags": elf_ldflags}, ) - n.build( - outputs=dol_path, - rule="elf2dol", - inputs=elf_path, - implicit=dtk, - ) else: preplf_path = build_path / self.name / f"{self.name}.preplf" plf_path = build_path / self.name / f"{self.name}.plf" @@ -633,7 +737,11 @@ def generate_build_ninja( return src_obj_path def asm_build( - obj: Object, options: Dict[str, Any], lib_name: str, src_path: Path + obj: Object, + options: Dict[str, Any], + lib_name: str, + src_path: Path, + build_path: Path, ) -> Optional[Path]: asflags = options["asflags"] or config.asflags if asflags is None: @@ -643,7 +751,7 @@ def generate_build_ninja( extra_asflags_str = make_flags_str(options["extra_asflags"]) asflags_str += " " + extra_asflags_str - asm_obj_path = build_asm_path / f"{obj.base_name}.o" + asm_obj_path = build_path / f"{obj.base_name}.o" # Avoid creating duplicate build rules if asm_obj_path in source_added: @@ -700,7 +808,9 @@ def generate_build_ninja( built_obj_path = c_build(obj, options, lib_name, unit_src_path) elif unit_src_path.suffix == ".s": # Add assembler build rule - built_obj_path = asm_build(obj, options, lib_name, unit_src_path) + built_obj_path = asm_build( + obj, options, lib_name, unit_src_path, build_src_path + ) else: sys.exit(f"Unknown source file type {unit_src_path}") else: @@ -711,7 +821,9 @@ def generate_build_ninja( # Assembly overrides if unit_asm_path is not None and unit_asm_path.exists(): link_built_obj = True - built_obj_path = asm_build(obj, options, lib_name, unit_asm_path) + built_obj_path = asm_build( + obj, options, lib_name, unit_asm_path, build_asm_path + ) if link_built_obj and built_obj_path is not None: # Use the source-built object @@ -760,6 +872,9 @@ def generate_build_ninja( if config.compilers_path and not os.path.exists(mw_path): sys.exit(f"Linker {mw_path} does not exist") + # Add all build steps needed before we link and after compiling objects + postcompile_implicit = write_custom_step("post-compile") + ### # Link ### @@ -768,6 +883,19 @@ def generate_build_ninja( link_outputs.append(step.output()) n.newline() + # Add all build steps needed after linking and before GC/Wii native format generation + postlink_implicit = write_custom_step("post-link") + + ### + # Generate DOL + ### + n.build( + outputs=link_steps[0].output(), + rule="elf2dol", + inputs=link_steps[0].partial_output(), + implicit=[*postlink_implicit, dtk], + ) + ### # Generate RELs ### @@ -830,6 +958,9 @@ def generate_build_ninja( ) n.newline() + # Add all build steps needed post-build (re-building archives and such) + postbuild_implicit = write_custom_step("post-build") + ### # Helper rule for building all source files ### @@ -867,7 +998,7 @@ def generate_build_ninja( outputs=ok_path, rule="check", inputs=config.check_sha_path, - implicit=[dtk, *link_outputs], + implicit=[dtk, *link_outputs, *postbuild_implicit], ) n.newline() @@ -886,6 +1017,21 @@ def generate_build_ninja( implicit=[ok_path, configure_script, python_lib, config.config_path], ) + ### + # Generate progress report + ### + n.comment("Generate progress report") + n.rule( + name="report", + command=f"{objdiff} report generate -o $out", + description="REPORT", + ) + n.build( + outputs=report_path, + rule="report", + implicit=[objdiff, "all_source"], + ) + ### # Helper tools ### @@ -967,7 +1113,7 @@ def generate_build_ninja( configure_script, python_lib, python_lib_dir / "ninja_syntax.py", - *(config.reconfig_deps or []) + *(config.reconfig_deps or []), ], ) n.newline() @@ -1116,10 +1262,14 @@ def generate_objdiff_config( if compiler_version is None: print(f"Missing scratch compiler mapping for {options['mw_version']}") else: + cflags_str = make_flags_str(cflags) + if options["extra_cflags"] is not None: + extra_cflags_str = make_flags_str(options["extra_cflags"]) + cflags_str += " " + extra_cflags_str unit_config["scratch"] = { "platform": "gc_wii", "compiler": compiler_version, - "c_flags": make_flags_str(cflags), + "c_flags": cflags_str, "ctx_path": src_ctx_path, "build_ctx": True, }