From 4cfb7c38081a37b019b6bebbe270c8bc8039bbdb Mon Sep 17 00:00:00 2001 From: Flavius Date: Tue, 27 Oct 2026 14:49:29 -0600 Subject: [PATCH 1/9] feat(nonmatching): slots for functional C Add nonmatching/functional/ and an empty functional.tsv for C that behaves like the retail function without matching it, for the native port only. check_repository.py requires each listed function to still be INCLUDE_ASM in src/ with its file present; progress.py reads src/ only, so nothing listed is ever counted. --- nonmatching/README.md | 10 ++++++++ nonmatching/functional.tsv | 4 +++ nonmatching/functional/README.md | 14 ++++++++++ tests/check_repository.py | 44 +++++++++++++++++++++++++++++--- 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 nonmatching/functional.tsv create mode 100644 nonmatching/functional/README.md diff --git a/nonmatching/README.md b/nonmatching/README.md index 14ec62537..5e2241240 100644 --- a/nonmatching/README.md +++ b/nonmatching/README.md @@ -15,6 +15,16 @@ Some older game drafts record experiments with the compiler recipe at `ace3f7be822bae9e85af0ae9010bd5c0dca91986`. Those residuals are historical observations; later dated entries record their own profiles and measurements. Neither is proof of semantic correctness or matching credit without a strict linked build. + +## Functional C + +`functional/` and `functional.tsv` hold C that does what a still-assembly +function does without matching its bytes, written for the native port (see +`functional/README.md` and OpenRAC's `docs/port/BACKFILL.md`). It is never +linked, never counted by `tools/progress.py` (which reads `src/` only), and +`tests/check_repository.py` checks that each listed function is still +`INCLUDE_ASM` in `src/`. + | Function | Size | Status and remaining differences | | --- | ---: | ---: | | [`func_00285030`](game/func_00285030.c) | 680 | 26 October 2026 (xcarry): carried from rac3-decomp func_00392820, whose match uses an empty asm register fence (not allowed here). Without it, as a -g8 unit, the TEX0 build order and the second packet's registers differ (about 25 lines). See source. | diff --git a/nonmatching/functional.tsv b/nonmatching/functional.tsv new file mode 100644 index 000000000..ce0354c73 --- /dev/null +++ b/nonmatching/functional.tsv @@ -0,0 +1,4 @@ +# C for functions this decompilation still has as assembly, for the native port only (OpenRAC port/game/rac2-ntsc, hostgen.json "candidates"). +# Each does what the retail function does without matching its bytes; the matching build never reads it. +# Files under nonmatching/functional/ are C written for the port only (no matching attempt behind them). +# name file diff --git a/nonmatching/functional/README.md b/nonmatching/functional/README.md new file mode 100644 index 000000000..29515bff5 --- /dev/null +++ b/nonmatching/functional/README.md @@ -0,0 +1,14 @@ +# Functional C + +C that behaves like the retail function without matching its bytes. It is for +the native port only: the matching build never reads it, `src/` never holds it, +and the progress report never counts it. + +- One file per function (or per small group), named after the function. +- List each in [`../functional.tsv`](../functional.tsv) as `namefile`. + `tests/check_repository.py` requires every listed function to still be + `INCLUDE_ASM` in `src/` and its file to exist here. +- When a function matches, put the match in `src/` and delete its row and file. +- OpenRAC reads the table through `port/game/rac2-ntsc/hostgen.json` + ("candidates"); how to write and verify this C is in OpenRAC's + `docs/port/BACKFILL.md`. diff --git a/tests/check_repository.py b/tests/check_repository.py index c355a30ae..b728b66ee 100644 --- a/tests/check_repository.py +++ b/tests/check_repository.py @@ -13,6 +13,9 @@ 3. Nothing of the removed system comes back: no gzipped or generated data, no generated sources or fragments, no per-level copies, no per-batch or campaign documents, no file over 2.5 MB. +4. nonmatching/functional.tsv (C for the native port only, never counted) lists only functions + that are still INCLUDE_ASM in src/, once each, with a file under nonmatching/functional/. + Exits nonzero, listing every problem, on failure. """ import re @@ -89,9 +92,10 @@ def release_of(name: str) -> str: return TAGS.get(m.group(1), "us") if m else "us" -def check_sources(known: dict, errors: list) -> None: - """KNOWN: version -> the names of its catalogues.""" +def check_sources(known: dict, errors: list) -> set: + """KNOWN: version -> the names of its catalogues. Returns the names still INCLUDE_ASM.""" defined = {} + still_asm = set() for path in sorted((ROOT / "src").rglob("*.c")): text = path.read_text(encoding="utf-8", errors="replace") relative = path.relative_to(ROOT).as_posix() @@ -102,6 +106,8 @@ def check_sources(known: dict, errors: list) -> None: for m in re.finditer(rf'^(?:INCLUDE_ASM|ASM_FUNC|LINKER_REMNANT)\("[^"]*",\s*({NAME})\);', text, re.M): n = text[:m.start()].count("\n") + 1 defined.setdefault(m.group(1), []).append(f"{relative}:{n} (asm)") + if m.group(0).startswith("INCLUDE_ASM"): + still_asm.add(m.group(1)) for name, where in sorted(defined.items()): if len(where) > 1: errors.append(f"{name} appears {len(where)} times: {', '.join(where)}") @@ -118,6 +124,38 @@ def check_sources(known: dict, errors: list) -> None: errors.append(f"{name} is in a catalogue of {version} but in no file of src/") c = sum(1 for w in defined.values() if not w[0].endswith("(asm)")) print(f"{len(defined)} functions in src/: {c} in C, {len(defined) - c} as INCLUDE_ASM") + return still_asm + + +def check_functional(still_asm: set, errors: list) -> None: + """nonmatching/functional.tsv: namefile lines. Functional C does what the retail function + does without matching its bytes; it is kept out of src/ and out of every count, so a listed + function must still be INCLUDE_ASM in src/ (a match replaces the row), and its file must exist + under nonmatching/functional/.""" + table = ROOT / "nonmatching/functional.tsv" + if not table.is_file(): + errors.append("nonmatching/functional.tsv is missing (the slot for functional C)") + return + seen = {} + for n, line in enumerate(table.read_text().splitlines(), 1): + if not line.strip() or line.startswith("#"): + continue + where = f"nonmatching/functional.tsv:{n}" + cols = line.split("\t") + if len(cols) != 2: + errors.append(f"{where}: {len(cols)} columns, expected namefile") + continue + name, rel = cols[0].strip(), cols[1].strip() + if not re.fullmatch(NAME, name): + errors.append(f"{where}: {name!r} is not a function name") + if name in seen: + errors.append(f"{where}: duplicate {name} (first at line {seen[name]})") + seen.setdefault(name, n) + if name not in still_asm: + errors.append(f"{where}: {name} is not INCLUDE_ASM in src/ (matched or unknown): remove the row") + if not rel.startswith("nonmatching/functional/") or not (ROOT / rel).is_file(): + errors.append(f"{where}: {rel!r} is not a file under nonmatching/functional/") + print(f"{len(seen)} functions in nonmatching/functional.tsv") # What the repository must not grow again (AGENTS.md, "Do not bring back what was removed"). @@ -173,7 +211,7 @@ def main() -> int: if (ROOT / v["config"] / "boot/functions.tsv").is_file(): known[version] = check_catalogues(errors, v["config"]) print(f"{version}: {len(known[version])} catalogue names") - check_sources(known, errors) + check_functional(check_sources(known, errors), errors) if errors: print(f"\n{len(errors)} problem(s):", file=sys.stderr) for e in errors[:200]: From 0106193c05fdb5463be368f930d13e8abcf09890 Mon Sep 17 00:00:00 2001 From: Flavius Date: Tue, 27 Oct 2026 17:02:18 -0600 Subject: [PATCH 2/9] feat(tools): fcheck, the functional gate's driver for boot functions Compiles a function's unit with its own flags (a candidate in place of its INCLUDE_ASM line or C, else on its own), places its section at 0xA0000 with every relocation resolved against retail: the boot build's derived values, the unit's writable data where retail has it, read-only data and jump tables copied with the code, gp-relative references kept inside $gp's window. Captures call states headless from the US disc (menu pages and New Game up to the first level's load, phased and spread) and runs openrac-fcheck. --- tests/test_fcheck.py | 180 +++++++++++ tools/fcheck.py | 707 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 887 insertions(+) create mode 100644 tests/test_fcheck.py create mode 100644 tools/fcheck.py diff --git a/tests/test_fcheck.py b/tests/test_fcheck.py new file mode 100644 index 000000000..9d050d59b --- /dev/null +++ b/tests/test_fcheck.py @@ -0,0 +1,180 @@ +"""Tests for tools/fcheck.py's placement, splicing and return types: no game data, no toolchain.""" +import struct +import sys +import unittest +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "tools")) +import elf # noqa: E402 +import fcheck # noqa: E402 + +GP = fcheck.GP + + +def words(*ws): + return b"".join(struct.pack("/ ($RAC2_FCHECK_STATES overrides the root): the +boot and title, the main menu's pages (Load, Options, New Game's no-card question) and New +Game up to the first level's load, never past it. Each run is cut into phases and takes the +calls numbered 1 and 2 of each phase (`--capture-phases`, `--capture-spread`), so a function +called every frame has states from each part of the run. A function a run never calls gets +a NAME.none marker there. Boot functions only for now: level programs (func_LNN_, and boot +functions in the memory a level replaces from 0x1A7C80, once it is loaded) are not captured. + +Needs: OpenRAC's interpreter built (cmake -S tools/interpreter -B build/interpreter in +OpenRAC; $OPENRAC_ROOT, default ~/Projects/RAC/OpenRAC), the compiler (docs/BUILDING.md; +$RAC2_LINUX runs it, e.g. "docker exec rac2-gate"), build/boot/placed.json from +`tools/build.py boot`, and the disc image. Test-only: nothing here is linked or shipped. +""" +from __future__ import annotations + +import argparse +import json +import os +import re +import shlex +import struct +import subprocess +import sys +import zlib +from functools import lru_cache +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "tools")) +import build # noqa: E402 +import elf # noqa: E402 +import levels # noqa: E402 + +OPENRAC = Path(os.environ.get("OPENRAC_ROOT", str(Path.home() / "Projects/RAC/OpenRAC"))) +BOOT = OPENRAC / "build/interpreter/openrac-boot" +FCHECK = OPENRAC / "build/interpreter/openrac-fcheck" +HOOKS = OPENRAC / "tools/interpreter/games/SCUS_972.68.hooks" +DISC = ROOT / "baserom/SCUS_972.68.iso" +WORK = ROOT / "build/fcheck" +BASE = 0x000A0000 # where the candidate goes: the kernel's area, unused by the game +LIMIT = 0x000F0000 +GP = levels.GP # 0x1AEFF0, the boot's (and every level's) $gp +STATES_PER_PHASE = 2 +# Instructions retail may take from a state (about eight frames of the EE): a call that takes +# more waits for an interrupt, a frame or the disc, which openrac-fcheck does not deliver, and +# the state is skipped. +RETAIL_LIMIT = 40_000_000 + +# Pad bits: Start 8, Up 10, Right 20, Down 40, Left 80, Triangle 1000, Cross 4000. +# Past the splash, the movies and the title to the main menu (New Game selected). +TO_MENU = ["300:4000:10", "500:8:10", "700:4000:10", "900:8:10"] +SCRIPTS = { + # The main menu's pages: Load (no card: "Go back?"), Options (moved through, settings + # changed), New Game's "No Memory Card ... Continue anyway?" question, each backed out of. + "menu": {"frames": 2300, "phases": [0, 900, 1400], + "presses": TO_MENU + ["1000:40:8", "1050:4000:8", "1150:20:8", "1200:1000:8", + "1300:40:8", "1400:4000:8", "1450:20:8", "1500:40:8", "1550:20:8", + "1600:40:8", "1650:80:8", "1700:10:8", "1800:1000:8", "1900:10:8", + "1950:10:8", "2000:4000:8", "2100:20:8", "2150:80:8", "2200:1000:8"]}, + # New Game, Yes, and the first level's load, stopped before the level runs (its program + # replaces the boot's memory from 0x1A7C80 at about frame 1650). + "newgame": {"frames": 1650, "phases": [950, 1300], + "presses": TO_MENU + ["950:4000:10", "1000:4000:10"]}, +} +SHF_WRITE = 1 +STT_FUNC, STT_SECTION = 2, 3 +SHN_ABS = 0xFFF1 + + +class Link(Exception): + """The candidate cannot be placed (the message starts with LINK).""" + + +def states_root() -> Path: + return Path(os.environ.get("RAC2_FCHECK_STATES") or WORK / "states") + + +# --- The boot's functions and their sources ------------------------------------------------- + +@lru_cache(maxsize=None) +def boot_functions() -> dict[str, tuple[int, int]]: + return {n: (a, s) for n, a, s in levels.read_boot_functions()} + + +@lru_cache(maxsize=None) +def placed() -> dict: + p = ROOT / "build/boot/placed.json" + return json.loads(p.read_text()) if p.is_file() else {"functions": {}, "values": {}} + + +def retail_bytes(address: int, size: int) -> bytes: + return levels.boot_bytes(address, size) + + +STUB = r'^(?:INCLUDE_ASM|ASM_FUNC)\("[^"]*",\s*{name}\);[ \t]*$' +DEFINITION = re.compile(r"^(?:[A-Za-z_][^;=#]*?\b)?([A-Za-z_]\w*)\s*\(") + + +@lru_cache(maxsize=None) +def sources() -> dict[str, tuple[Path, str]]: + """Boot function name -> (its unit in src/, "c" or "asm").""" + names = boot_functions() + out: dict[str, tuple[Path, str]] = {} + for d in ("core", "game", "levels/shared"): + for path in sorted((ROOT / "src" / d).rglob("*.c")): + text = path.read_text(errors="replace") + for m in re.finditer(r'^(?:INCLUDE_ASM|ASM_FUNC|LINKER_REMNANT)\("[^"]*",\s*(\w+)\);', text, re.M): + if m.group(1) in names: + out.setdefault(m.group(1), (path, "asm")) + for line in text.splitlines(): + m = DEFINITION.match(line) + if (m and m.group(1) in names and not line.rstrip().endswith(";") + and not line.startswith(("extern", "typedef"))): + out.setdefault(m.group(1), (path, "c")) + return out + + +def find_definition(text: str, name: str) -> tuple[int, int] | None: + """The span of NAME's C definition: from its header (and a type alone on the line before) + through the brace that closes its body (and the rest of that line).""" + lines = text.splitlines(keepends=True) + at = 0 + for i, line in enumerate(lines): + m = DEFINITION.match(line) + if m and m.group(1) == name and not line.rstrip().endswith(";") and not line.startswith(("extern", "typedef")): + start = at + if i and re.fullmatch(r"[A-Za-z_][\w \t\*]*\n", lines[i - 1]) and not lines[i - 1].startswith(("#", "extern", "typedef")): + start -= len(lines[i - 1]) + end = body_end(text, text.find("{", at)) + if end is None: + return None + nl = text.find("\n", end) + return start, len(text) if nl < 0 else nl + 1 + at += len(line) + return None + + +def body_end(text: str, k: int) -> int | None: + """Just past the brace that closes the one at K (strings, characters and comments skipped).""" + if k < 0: + return None + depth, n = 0, len(text) + while k < n: + c = text[k] + if text.startswith("/*", k): + k = text.find("*/", k + 2) + if k < 0: + return None + k += 2 + continue + if text.startswith("//", k): + k = text.find("\n", k) + if k < 0: + return None + continue + if c in "\"'": + k += 1 + while k < n and text[k] != c: + k += 2 if text[k] == "\\" else 1 + elif c == "{": + depth += 1 + elif c == "}": + depth -= 1 + if depth == 0: + return k + 1 + k += 1 + return None + + +def splice(text: str, name: str, candidate: str) -> str: + """The unit's text with CANDIDATE in place of NAME's INCLUDE_ASM line or C definition.""" + m = re.search(STUB.format(name=re.escape(name)), text, re.M) + if m: + return text[:m.start()] + candidate.rstrip("\n") + "\n" + text[m.end():] + span = find_definition(text, name) + if span is None: + raise Link(f"LINK {name}: neither an INCLUDE_ASM stub nor a C definition in its unit") + return text[:span[0]] + candidate.rstrip("\n") + "\n" + text[span[1]:] + + +# --- Compiling ------------------------------------------------------------------------------- + +def compile_text(text: str, unit: Path, obj: Path, includes: list[Path] | None = None) -> tuple[bool, str]: + """TEXT compiled as UNIT is (its flags, its directory on the include path) into OBJ, through + $RAC2_LINUX as tools/build.py compiles. Returns (ok, the compiler's messages).""" + obj.parent.mkdir(parents=True, exist_ok=True) + copy = obj.with_suffix(".c") + copy.write_text(text) + flags = build.flags_for(unit) + cargs, aargs = build.split_flags(flags) + C, S = build.COMPILER, build.SDK_COMPILER + inc = " ".join(f"-I'{d}'" for d in [unit.parent, *(includes or [])]) + assemble = shlex.join([str(C / "as"), *aargs, "-o", "x.o", "x.s"]) + if flags.startswith("sdk"): + front = f"'{S}/xgcc' -B'{S}/' -S {shlex.join(cargs)} -I'{ROOT}/include' -I'{S}/include' {inc} x.c -o x.s" + else: + front = (f"'{C}/cpp' -I'{C}/include' -I'{ROOT}/include' {inc} x.c > x.i; " + f"'{C}/cc1' -quiet {shlex.join(cargs)} x.i -o x.s") + script = (f"set -e; t=$(mktemp -d); trap 'rm -rf $t' EXIT; cd $t; cp '{copy}' x.c; {front}; " + f"cp x.s '{obj.with_suffix('.s')}'; {assemble}; cp x.o '{obj}'") + if obj.exists(): + obj.unlink() + runner = shlex.split(os.environ.get("RAC2_LINUX", "")) + r = subprocess.run([*runner, "bash", "-c", script], capture_output=True, text=True) + return r.returncode == 0 and obj.is_file(), (r.stdout + r.stderr)[-4000:] + + +def derived_values(obj: dict, name: str) -> dict[str, int] | None: + """The symbol values (and "§section" placements) NAME's section in OBJ needs to read as the + retail bytes at NAME's address (tools/build.py's derivation), or None when it does not.""" + sym = next((s for s in obj["symbols"] if s["name"] == name and s["type"] == STT_FUNC and 0 < s["section"] < 0xFF00), None) + if sym is None or name not in boot_functions(): + return None + address = boot_functions()[name][0] + values, why = placement().derive(obj, sym["section"], address - sym["value"]) + return None if why else values + + +@lru_cache(maxsize=None) +def placement(): + return build.Placement("boot", elf.read(levels.BOOT)) + + +def unit_values(unit: Path, name: str) -> tuple[dict[str, int], dict[str, int]]: + """From the unit's matched build (build/obj/): what NAME's own C needs (empty when NAME is + not matched C), and the agreed values the unit's other placed functions need.""" + try: + obj = elf.read_object(build.compile_c(unit)) + except SystemExit: + return {}, {} + own = derived_values(obj, name) or {} + others: dict[str, set[int]] = {} + done = set(placed().get("functions", {})) + for s in obj["symbols"]: + if s["type"] == STT_FUNC and s["name"] != name and s["name"] in done and 0 < s["section"] < 0xFF00: + for k, v in (derived_values(obj, s["name"]) or {}).items(): + others.setdefault(k, set()).add(v) + return own, {k: next(iter(v)) for k, v in others.items() if len(v) == 1} + + +# --- Placing --------------------------------------------------------------------------------- + +def gp_literal(data: bytes) -> int | None: + """An address in $gp's window where retail's read-only literal pools (core.lit, .lit) hold + DATA, aligned to its size.""" + for sec in placement().image["sections"]: + if sec["name"] not in ("core.lit", ".lit"): + continue + lo, hi = max(sec["address"], GP - 0x8000), min(sec["address"] + sec["size"], GP + 0x8000) + blob = retail_bytes(sec["address"], sec["size"]) + step = min(len(data), 8) or 4 + for a in range(lo + (-lo) % step, hi - len(data) + 1, step): + if blob[a - sec["address"]:a - sec["address"] + len(data)] == data: + return a + return None + + +# Bytes a gp-relative instruction reads: lb lh lw lbu lhu lwu ld lq lwc1 ldc1 (and stores). +WIDTH = {0x20: 1, 0x24: 1, 0x28: 1, 0x21: 2, 0x25: 2, 0x29: 2, 0x23: 4, 0x27: 4, 0x2B: 4, 0x31: 4, + 0x39: 4, 0x37: 8, 0x3F: 8, 0x35: 8, 0x3D: 8, 0x1E: 16, 0x1F: 16} + + +class Placer: + """Places NAME's section of a compiled object at BASE, as the module docstring says. + + NAMED(symbol) -> retail value or None; FUNCTION(name) -> retail address or None; + WRITABLE(section name) -> retail address of the unit's writable section or None; + LITERAL(bytes, section name, offset) -> an address in $gp's window holding them, or None.""" + + def __init__(self, obj: dict, name: str, named, function, writable, literal, base: int = BASE, limit: int = LIMIT): + self.obj, self.name = obj, name + self.named, self.function, self.writable, self.literal = named, function, writable, literal + self.base, self.limit = base, limit + self.where: dict[int, int] = {} # section index -> address + self.copied: list[int] = [] # sections laid out in the image, in order + self.image = bytearray() + self.notes: list[str] = [] + sym = next((s for s in obj["symbols"] if s["name"] == name and s["type"] == STT_FUNC + and 0 < s["section"] < 0xFF00), None) + if sym is None: + raise Link(f"LINK {name} is not defined by the candidate") + self.own = sym["section"] + self.copy(self.own) + self.entry = self.where[self.own] + sym["value"] + + def copy(self, index: int) -> int: + sec = self.obj["sections"][index] + align = max(sec.get("align", 4) or 4, 4) if self.image else 1 + at = self.base + len(self.image) + at += (-at) % align + self.image += bytes(at - self.base - len(self.image)) + self.where[index] = at + self.image += sec["data"] if sec["type"] != elf.SHT_NOBITS else bytes(sec["size"]) + self.copied.append(index) + if self.base + len(self.image) > self.limit: + raise Link(f"LINK the candidate's code and data do not fit below {self.limit:#010x}") + return at + + def section(self, index: int) -> int: + if index in self.where: + return self.where[index] + sec = self.obj["sections"][index] + if sec["name"].startswith(".text"): + f = next((s for s in self.obj["symbols"] if s["section"] == index and s["type"] == STT_FUNC + and s["value"] == 0), None) + a = self.function(f["name"]) if f and f["name"] != self.name else None + if a is not None: + self.where[index] = a + return a + self.notes.append(f"helper {f['name'] if f else sec['name']} placed after the function") + return self.copy(index) + if sec.get("flags", 0) & SHF_WRITE or sec["type"] == elf.SHT_NOBITS: + a = self.writable(sec["name"]) + if a is not None: + self.where[index] = a + return a + self.notes.append(f"{sec['name']} has no retail placement: placed fresh in the kernel area") + return self.copy(index) + + def value(self, sym: dict) -> int: + if sym["type"] == STT_SECTION: + return self.section(sym["section"]) + n = sym["name"] + if n == self.name: + return self.entry + if n: + v = self.named(n) + if v is not None: + return v + if 0 < sym["section"] < 0xFF00: + return self.section(sym["section"]) + sym["value"] + if sym["section"] == SHN_ABS: + return sym["value"] + raise Link(f"LINK {n or '?'} has no retail address") + + def relocate(self) -> None: + done = 0 + while done < len(self.copied): + self.apply(self.copied[done]) + done += 1 + + def apply(self, index: int) -> None: + obj = self.obj + off = self.where[index] - self.base + orig = bytes(obj["sections"][index]["data"]) + rel = obj["relocations"].get(index, []) + word = lambda at: struct.unpack_from("> 2) & 0x3FFFFFF)) + elif kind == elf.R_MIPS_HI16: + # Paired in the object's order with the next %lo of the same symbol, as the linker does. + lo = next((o for o, t, ss in rel[k + 1:] if t == elf.R_MIPS_LO16 and ss == s), None) + if lo is None: + raise Link(f"LINK a %hi at +{at:#x} without its %lo") + ahl = ((w & 0xFFFF) << 16) + sext(word(lo) & 0xFFFF) + put(at, (w & 0xFFFF0000) | (((v + ahl + 0x8000) >> 16) & 0xFFFF)) + elif kind == elf.R_MIPS_LO16: + put(at, (w & 0xFFFF0000) | ((v + sext(w & 0xFFFF)) & 0xFFFF)) + else: + raise Link(f"LINK relocation type {kind} not handled") + + def gp_target(self, sym: dict, addend: int, ins: int) -> int: + """Where a $gp-relative reference goes. A read-only section of the candidate (a literal) + is not reachable from where it is copied: the same bytes in retail's window are used.""" + index = sym["section"] + sec = self.obj["sections"][index] if 0 < index < 0xFF00 else None + if (sym["type"] == STT_SECTION and sec is not None and not sec["name"].startswith(".text") + and not sec.get("flags", 0) & SHF_WRITE and sec["type"] != elf.SHT_NOBITS): + width = WIDTH.get(ins >> 26, 4) + data = bytes(sec["data"][addend:addend + width]) + a = self.literal(data, sec["name"], addend) + if a is None: + raise Link(f"LINK the constant {data.hex()} in {sec['name']} (read $gp-relative) is nowhere in retail's $gp window") + return a + return self.value(sym) + addend + + def segments(self) -> list[tuple[int, bytes]]: + return [(self.base, bytes(self.image))] + + +def returns(name: str, text: str) -> str: + """openrac-fcheck's --returns for NAME's definition in TEXT: void, int, long (64 bits and + more: EE GCC's long and long long, double), or float.""" + lines = text.splitlines() + head = None + for i, line in enumerate(lines): + m = DEFINITION.match(line) + if m and m.group(1) == name and not line.rstrip().endswith(";") and not line.startswith(("extern", "typedef")): + head = line[:line.index(name)] + if not head.strip() and i: + head = lines[i - 1] + break + if head is None: + return "void" + if "*" in head: + return "int" + words = [w for w in head.split() if w not in ("const", "volatile", "extern", "signed", "unsigned", + "register", "static", "inline", "__inline__")] + w = " ".join(words) + if w == "void": + return "void" + if w in ("float", "f32"): + return "float" + if w in ("long", "long long", "long int", "u64", "s64", "u128", "s128", "double", "f64", "u_long"): + return "long" + if w.startswith(("struct", "union")): + return "void" + return "int" + + +# --- Building a candidate ---------------------------------------------------------------------- + +def build_candidate(name: str, candidate: Path | None, out: Path, includes: list[Path] | None = None) -> dict: + """Compile and place NAME (src/'s C, or CANDIDATE in its unit), writing OUT/code.bin and + OUT/placed.json; returns the placed.json dict. Raises Link.""" + src = sources().get(name) + if src is None: + raise Link(f"LINK {name} is not a boot function in src/ (config/boot/functions.tsv)") + unit, kind = src + out.mkdir(parents=True, exist_ok=True) + text = unit.read_text(errors="replace") + mode = "src" + if candidate is None: + if kind != "c": + raise Link(f"LINK {name} is still assembly in {unit.relative_to(ROOT)}: give a candidate") + ok, log = compile_text(text, unit, out / "unit.o") + cand_text = text + else: + cand_text = candidate.read_text(errors="replace") + ok, log = compile_text(splice(text, name, cand_text), unit, out / "unit.o") + mode = "in its unit" + if not ok: + ok2, log2 = compile_text(cand_text, unit, out / "alone.o", [candidate.parent, *(includes or [])]) + if ok2: + ok, mode = True, "on its own (with the unit's flags)" + (out / "unit.log").write_text(log) + (out / "unit.o").unlink(missing_ok=True) + os.replace(out / "alone.o", out / "unit.o") + else: + log += "\n--- on its own ---\n" + log2 + (out / "log.txt").write_text(log) + if not ok: + raise Link(f"COMPILE failed ({unit.relative_to(ROOT)}):\n" + "\n".join(log.strip().splitlines()[-12:])) + obj = elf.read_object(out / "unit.o") + own, others = unit_values(unit, name) + table = boot_functions() + derived = {k: int(v, 16) for k, v in placed().get("values", {}).items()} + + def named(n: str) -> int | None: + for d in (own,): + if n in d: + return d[n] & 0xFFFFFFFF + if n in table: + return table[n][0] + if n in derived: + return derived[n] + if n in others: + return others[n] & 0xFFFFFFFF + return build.address_in_name(n) + + def function(n: str) -> int | None: + return table[n][0] if n in table else named(n) + + def writable(sec: str) -> int | None: + for d in (own, others): + if "§" + sec in d: + return d["§" + sec] & 0xFFFFFFFF + return None + + def literal(data: bytes, sec: str, offset: int) -> int | None: + for d in (own, others): + b = d.get("§" + sec) + if b is not None and GP - 0x8000 <= b + offset < GP + 0x8000 and retail_bytes(b + offset, len(data)) == data: + return b + offset + return gp_literal(data) + + p = Placer(obj, name, named, function, writable, literal) + p.relocate() + segs = p.segments() + files = [] + for i, (a, data) in enumerate(segs): + f = out / f"code{i}.bin" + f.write_bytes(data) + files.append([f.name, a]) + info = {"entry": p.entry, "segments": files, "unit": str(unit.relative_to(ROOT)), "mode": mode, + "flags": build.flags_for(unit), "returns": returns(name, cand_text), "notes": p.notes, + "own_values": bool(own)} + (out / "placed.json").write_text(json.dumps(info, indent=1)) + return info + + +# --- Capturing ---------------------------------------------------------------------------------- + +def states_of(name: str, root: Path | None = None) -> list[Path]: + root = root or states_root() + return sorted(root.glob(f"*/{name}.*.snap")) + + +def capture_line(name: str) -> str | None: + """openrac-boot's capture list line for a boot function: address, bytes compared, their + CRC-32 (a level's code at the address then is not taken), name, states per phase.""" + if name not in boot_functions(): + return None + a, size = boot_functions()[name] + k = max(4, min(size, 256)) + return f"{a:08x} {k} {zlib.crc32(retail_bytes(a, k)):08x} {name} {STATES_PER_PHASE}" + + +def capture(names: list[str], script: str, frames: int | None = None, force: bool = False, + root: Path | None = None) -> tuple[int, int]: + """One headless run of SCRIPT taking states of NAMES (those it has not run for yet). + Returns (functions called, functions asked).""" + s = SCRIPTS[script] + out = (root or states_root()) / script + out.mkdir(parents=True, exist_ok=True) + todo = [n for n in dict.fromkeys(names) if force or not (list(out.glob(f"{n}.*.snap")) or (out / f"{n}.none").exists())] + lines = [l for l in (capture_line(n) for n in todo) if l] + if not lines: + return 0, 0 + lst = out / "capture.list" + lst.write_text("\n".join(lines) + "\n") + calls = out / "calls.tsv.new" + cmd = [str(BOOT), str(DISC), "--hooks", str(HOOKS), "--no-card", "--ntsc", "--gs-threads", "2", + "--frames", str(frames or s["frames"]), "--report", "0", + "--capture", str(lst), str(out), "--capture-phases", ",".join(map(str, s["phases"])), + "--capture-spread", "--calls", str(calls)] + for p in s["presses"]: + cmd += ["--press", p] + with open(out / "capture.log", "a") as log: + log.write("$ " + shlex.join(cmd) + "\n") + log.flush() + subprocess.run(cmd, stdout=log, stderr=subprocess.STDOUT) + got = 0 + counted = {} + if calls.is_file(): + for line in calls.read_text().splitlines(): + c = line.split("\t") + counted[c[0]] = int(c[1]) + with open(out / "calls.tsv", "a") as f: + f.write(calls.read_text()) + calls.unlink() + for line in lines: + n = line.split()[3] + if counted.get(n, 0) and list(out.glob(f"{n}.*.snap")): + got += 1 + elif n in counted: + (out / f"{n}.none").write_text(f"not called in the {script} run\n") + return got, len(lines) + + +# --- Checking ------------------------------------------------------------------------------------- + +def check(name: str, candidate: Path | None, ulps: int = 0, work: Path | None = None, + includes: list[Path] | None = None) -> int: + states = states_of(name) + if not states: + marked = list(states_root().glob(f"*/{name}.none")) + print(f"{name}: NOT CALLED in the capture runs ({', '.join(sorted(p.parent.name for p in marked)) or 'none run yet'}); " + "cannot check by behaviour") + return 3 + work = work or WORK / "work" / name + try: + info = build_candidate(name, candidate, work, includes) + except Link as e: + print(str(e)) + return 2 + # The capture run's hooks and disc: a hooked function's service answers in both runs. + cmd = [str(FCHECK), "--hooks", str(HOOKS), "--disc", str(DISC), "--limit", str(RETAIL_LIMIT), + "--entry", f"{info['entry']:x}", "--returns", info["returns"]] + if ulps: + cmd += ["--ulps", str(ulps)] + for f, a in info["segments"]: + cmd += ["--code", f"{work / f}@{a:x}"] + cmd += [str(s) for s in states] + r = subprocess.run(cmd, capture_output=True, text=True) + text = r.stdout.replace(str(states_root()) + "/", "") + if info["notes"] or info["mode"] != "src": + print(f"{name}: compiled {info['mode']}" + "".join("; " + n for n in info["notes"])) + print(text.strip() or r.stderr.strip()) + if r.returncode == 0: + m = re.search(r"compared (\d+) of", r.stdout) + print(f"{name}: SAME on {m.group(1) if m else '?'} real calls") + elif r.returncode == 4: + print(f"{name}: NOTHING COMPARED (every state skipped)") + return r.returncode + + +def main() -> int: + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + sub = ap.add_subparsers(dest="cmd", required=True) + p = sub.add_parser("check") + p.add_argument("name") + p.add_argument("candidate", nargs="?", type=Path) + p.add_argument("--ulps", type=int, default=0) + p.add_argument("--work", type=Path) + p.add_argument("--include", type=Path, action="append", default=[], + help="another directory for the candidate's #include \"...\" (where its file came from)") + p = sub.add_parser("build") + p.add_argument("name") + p.add_argument("candidate", nargs="?", type=Path) + p.add_argument("--out", type=Path, required=True) + p = sub.add_parser("capture") + p.add_argument("list", type=Path) + p.add_argument("--script", default="all", choices=["all", *SCRIPTS]) + p.add_argument("--frames", type=int) + p.add_argument("--force", action="store_true") + p = sub.add_parser("source", help="NAME's unit, whether it is C or assembly there, and its definition's span") + p.add_argument("name") + sub.add_parser("scripts") + a = ap.parse_args() + if a.cmd == "check": + return check(a.name, a.candidate.resolve() if a.candidate else None, a.ulps, a.work, + [d.resolve() for d in a.include]) + if a.cmd == "build": + try: + print(json.dumps(build_candidate(a.name, a.candidate.resolve() if a.candidate else None, a.out.resolve()))) + except Link as e: + print(str(e)) + return 2 + return 0 + if a.cmd == "capture": + names = [l.split()[0] for l in a.list.read_text().splitlines() if l.strip() and not l.startswith("#")] + for s in (SCRIPTS if a.script == "all" else [a.script]): + got, asked = capture(names, s, a.frames, a.force) + print(f"{s}: {got} of {asked} functions called") + return 0 + if a.cmd == "source": + if a.name not in sources(): + print(json.dumps({"name": a.name})) + return 2 + unit, kind = sources()[a.name] + span = find_definition(unit.read_text(errors="replace"), a.name) if kind == "c" else None + print(json.dumps({"name": a.name, "unit": str(unit), "kind": kind, "span": span, + "placed": a.name in placed().get("functions", {})})) + return 0 + if a.cmd == "scripts": + print(json.dumps(SCRIPTS, indent=1)) + return 0 + return 2 + + +if __name__ == "__main__": + sys.exit(main()) From 0d133e4802954709188758e6a98b395e47f6e934 Mon Sep 17 00:00:00 2001 From: Flavius Date: Tue, 27 Oct 2026 17:02:18 -0600 Subject: [PATCH 3/9] docs(nonmatching): how to run the functional gate --- nonmatching/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/nonmatching/README.md b/nonmatching/README.md index 5e2241240..41be47aad 100644 --- a/nonmatching/README.md +++ b/nonmatching/README.md @@ -25,6 +25,32 @@ linked, never counted by `tools/progress.py` (which reads `src/` only), and `tests/check_repository.py` checks that each listed function is still `INCLUDE_ASM` in `src/`. +A functional file is done only when the gate says SAME: `tools/fcheck.py` +compiles it in place of the function's `INCLUDE_ASM` line (with the unit's +flags; on its own when that does not compile), places it at 0x000A0000 and +runs OpenRAC's test-only interpreter on call states captured from the US +disc, comparing memory, the result, the kept registers and device accesses +with retail. It covers boot functions called in the boot, title, menu pages +and New Game up to the first level's load; level functions are not covered +yet. With OpenRAC's interpreter built (`build/interpreter/`) and the +compiler reachable through `RAC2_LINUX` (on a Mac, `docker run -d --name +rac2-gate --platform linux/386 -v $HOME:$HOME rac2-build:latest sleep +infinity`, then `RAC2_LINUX="docker exec rac2-gate"`): + +```sh +python3 tools/build.py boot # build/boot/placed.json: the values placement uses +python3 tools/fcheck.py capture LIST # states of the listed functions (both runs) +python3 tools/fcheck.py check func_XXXXXXXX nonmatching/functional/func_XXXXXXXX.c +``` + +The batch form, which writes the verdicts to OpenRAC's +`port/game/rac2-ntsc/fcheck.tsv`, is OpenRAC's `python3 tools/fcheck_gate.py +run --game rac2` (its `docs/port/BACKFILL.md`, "The gate"). Exit codes and +the placement rules are in `tools/fcheck.py`'s docstring; its tests are +`tests/test_fcheck.py`. + +## The drafts + | Function | Size | Status and remaining differences | | --- | ---: | ---: | | [`func_00285030`](game/func_00285030.c) | 680 | 26 October 2026 (xcarry): carried from rac3-decomp func_00392820, whose match uses an empty asm register fence (not allowed here). Without it, as a -g8 unit, the TEX0 build order and the second packet's registers differ (about 25 lines). See source. | From 6150947192935b4844945916fd5d432b88bf9c35 Mon Sep 17 00:00:00 2001 From: Flavius Date: Tue, 27 Oct 2026 18:47:51 -0600 Subject: [PATCH 4/9] feat(nonmatching): functional C for 95 boot and menu functions C that does what retail does, for the native port; never counted. Each is SAME under tools/fcheck.py on every call state captured in the menu and New Game runs (USA v1.01). The gate's verdicts are behaviour on those calls, not proof of every path. --- nonmatching/functional.tsv | 95 ++++ nonmatching/functional/func_00115FC0.c | 249 +++++++++ nonmatching/functional/func_00119BF8.c | 357 +++++++++++++ nonmatching/functional/func_00128FD0.c | 18 + nonmatching/functional/func_00132028.c | 131 +++++ nonmatching/functional/func_00132C48.c | 60 +++ nonmatching/functional/func_00133108.c | 31 ++ nonmatching/functional/func_001334B8.c | 29 ++ nonmatching/functional/func_00133580.c | 28 + nonmatching/functional/func_0026FC78.c | 89 ++++ nonmatching/functional/func_00279D18.c | 76 +++ nonmatching/functional/func_0027A9F0.c | 40 ++ nonmatching/functional/func_0027AB40.c | 108 ++++ nonmatching/functional/func_0027B440.c | 127 +++++ nonmatching/functional/func_0027C540.c | 44 ++ nonmatching/functional/func_0027C878.c | 161 ++++++ nonmatching/functional/func_0027DE50.c | 49 ++ nonmatching/functional/func_0027E018.c | 55 ++ nonmatching/functional/func_0027F2A8.c | 47 ++ nonmatching/functional/func_0027F640.c | 78 +++ nonmatching/functional/func_0027FC70.c | 134 +++++ nonmatching/functional/func_00280710.c | 55 ++ nonmatching/functional/func_00283CB8.c | 15 + nonmatching/functional/func_00283CF0.c | 13 + nonmatching/functional/func_00284088.c | 59 +++ nonmatching/functional/func_00284268.c | 283 ++++++++++ nonmatching/functional/func_002859C8.c | 446 ++++++++++++++++ nonmatching/functional/func_00286738.c | 691 +++++++++++++++++++++++++ nonmatching/functional/func_0028AB70.c | 62 +++ nonmatching/functional/func_0028B160.c | 41 ++ nonmatching/functional/func_0028B3D0.c | 59 +++ nonmatching/functional/func_0028E640.c | 92 ++++ nonmatching/functional/func_0028EA58.c | 52 ++ nonmatching/functional/func_00290B48.c | 43 ++ nonmatching/functional/func_00291850.c | 121 +++++ nonmatching/functional/func_00291CA8.c | 71 +++ nonmatching/functional/func_00291EC0.c | 89 ++++ nonmatching/functional/func_00292888.c | 67 +++ nonmatching/functional/func_00292B88.c | 84 +++ nonmatching/functional/func_00293000.c | 56 ++ nonmatching/functional/func_002933D0.c | 48 ++ nonmatching/functional/func_002934F8.c | 118 +++++ nonmatching/functional/func_00294F10.c | 127 +++++ nonmatching/functional/func_00295870.c | 7 + nonmatching/functional/func_0029EFD0.c | 40 ++ nonmatching/functional/func_0029F0C8.c | 95 ++++ nonmatching/functional/func_0029FDF0.c | 35 ++ nonmatching/functional/func_002A0048.c | 39 ++ nonmatching/functional/func_002A0250.c | 35 ++ nonmatching/functional/func_002AD7B8.c | 22 + nonmatching/functional/func_002AD860.c | 47 ++ nonmatching/functional/func_002B6828.c | 22 + nonmatching/functional/func_002B68C8.c | 34 ++ nonmatching/functional/func_002B6E78.c | 28 + nonmatching/functional/func_002B7578.c | 159 ++++++ nonmatching/functional/func_002B88F0.c | 205 ++++++++ nonmatching/functional/func_002BA0E0.c | 142 +++++ nonmatching/functional/func_002C9CF8.c | 92 ++++ nonmatching/functional/func_002CA8A8.c | 82 +++ nonmatching/functional/func_002CAB00.c | 99 ++++ nonmatching/functional/func_002CAF18.c | 113 ++++ nonmatching/functional/func_002CBAE0.c | 43 ++ nonmatching/functional/func_002CBC58.c | 264 ++++++++++ nonmatching/functional/func_002D6F38.c | 215 ++++++++ nonmatching/functional/func_002DDEC8.c | 68 +++ nonmatching/functional/func_002E35E0.c | 61 +++ nonmatching/functional/func_002E3998.c | 65 +++ nonmatching/functional/func_002E3BD8.c | 63 +++ nonmatching/functional/func_002E48E8.c | 46 ++ nonmatching/functional/func_002E4AC8.c | 396 ++++++++++++++ nonmatching/functional/func_002E5AD0.c | 105 ++++ nonmatching/functional/func_002E6178.c | 84 +++ nonmatching/functional/func_002E64A0.c | 158 ++++++ nonmatching/functional/func_002E6A60.c | 40 ++ nonmatching/functional/func_002E76C0.c | 120 +++++ nonmatching/functional/func_002E8368.c | 285 ++++++++++ nonmatching/functional/func_002ECB70.c | 131 +++++ nonmatching/functional/func_002F0FA0.c | 27 + nonmatching/functional/func_002FC280.c | 77 +++ nonmatching/functional/func_00336F20.c | 70 +++ nonmatching/functional/func_00337C10.c | 71 +++ nonmatching/functional/func_003380B0.c | 80 +++ nonmatching/functional/func_003384C0.c | 36 ++ nonmatching/functional/func_0033C590.c | 50 ++ nonmatching/functional/func_0033D028.c | 72 +++ nonmatching/functional/func_0033EA60.c | 59 +++ nonmatching/functional/func_0033FB98.c | 143 +++++ nonmatching/functional/func_00341030.c | 59 +++ nonmatching/functional/func_00342978.c | 87 ++++ nonmatching/functional/func_003458F8.c | 71 +++ nonmatching/functional/func_00345F70.c | 70 +++ nonmatching/functional/func_00346C60.c | 142 +++++ nonmatching/functional/func_00347F80.c | 37 ++ nonmatching/functional/func_003489E8.c | 115 ++++ nonmatching/functional/func_0034B2C8.c | 424 +++++++++++++++ nonmatching/functional/func_0034E070.c | 55 ++ 96 files changed, 9883 insertions(+) create mode 100644 nonmatching/functional/func_00115FC0.c create mode 100644 nonmatching/functional/func_00119BF8.c create mode 100644 nonmatching/functional/func_00128FD0.c create mode 100644 nonmatching/functional/func_00132028.c create mode 100644 nonmatching/functional/func_00132C48.c create mode 100644 nonmatching/functional/func_00133108.c create mode 100644 nonmatching/functional/func_001334B8.c create mode 100644 nonmatching/functional/func_00133580.c create mode 100644 nonmatching/functional/func_0026FC78.c create mode 100644 nonmatching/functional/func_00279D18.c create mode 100644 nonmatching/functional/func_0027A9F0.c create mode 100644 nonmatching/functional/func_0027AB40.c create mode 100644 nonmatching/functional/func_0027B440.c create mode 100644 nonmatching/functional/func_0027C540.c create mode 100644 nonmatching/functional/func_0027C878.c create mode 100644 nonmatching/functional/func_0027DE50.c create mode 100644 nonmatching/functional/func_0027E018.c create mode 100644 nonmatching/functional/func_0027F2A8.c create mode 100644 nonmatching/functional/func_0027F640.c create mode 100644 nonmatching/functional/func_0027FC70.c create mode 100644 nonmatching/functional/func_00280710.c create mode 100644 nonmatching/functional/func_00283CB8.c create mode 100644 nonmatching/functional/func_00283CF0.c create mode 100644 nonmatching/functional/func_00284088.c create mode 100644 nonmatching/functional/func_00284268.c create mode 100644 nonmatching/functional/func_002859C8.c create mode 100644 nonmatching/functional/func_00286738.c create mode 100644 nonmatching/functional/func_0028AB70.c create mode 100644 nonmatching/functional/func_0028B160.c create mode 100644 nonmatching/functional/func_0028B3D0.c create mode 100644 nonmatching/functional/func_0028E640.c create mode 100644 nonmatching/functional/func_0028EA58.c create mode 100644 nonmatching/functional/func_00290B48.c create mode 100644 nonmatching/functional/func_00291850.c create mode 100644 nonmatching/functional/func_00291CA8.c create mode 100644 nonmatching/functional/func_00291EC0.c create mode 100644 nonmatching/functional/func_00292888.c create mode 100644 nonmatching/functional/func_00292B88.c create mode 100644 nonmatching/functional/func_00293000.c create mode 100644 nonmatching/functional/func_002933D0.c create mode 100644 nonmatching/functional/func_002934F8.c create mode 100644 nonmatching/functional/func_00294F10.c create mode 100644 nonmatching/functional/func_00295870.c create mode 100644 nonmatching/functional/func_0029EFD0.c create mode 100644 nonmatching/functional/func_0029F0C8.c create mode 100644 nonmatching/functional/func_0029FDF0.c create mode 100644 nonmatching/functional/func_002A0048.c create mode 100644 nonmatching/functional/func_002A0250.c create mode 100644 nonmatching/functional/func_002AD7B8.c create mode 100644 nonmatching/functional/func_002AD860.c create mode 100644 nonmatching/functional/func_002B6828.c create mode 100644 nonmatching/functional/func_002B68C8.c create mode 100644 nonmatching/functional/func_002B6E78.c create mode 100644 nonmatching/functional/func_002B7578.c create mode 100644 nonmatching/functional/func_002B88F0.c create mode 100644 nonmatching/functional/func_002BA0E0.c create mode 100644 nonmatching/functional/func_002C9CF8.c create mode 100644 nonmatching/functional/func_002CA8A8.c create mode 100644 nonmatching/functional/func_002CAB00.c create mode 100644 nonmatching/functional/func_002CAF18.c create mode 100644 nonmatching/functional/func_002CBAE0.c create mode 100644 nonmatching/functional/func_002CBC58.c create mode 100644 nonmatching/functional/func_002D6F38.c create mode 100644 nonmatching/functional/func_002DDEC8.c create mode 100644 nonmatching/functional/func_002E35E0.c create mode 100644 nonmatching/functional/func_002E3998.c create mode 100644 nonmatching/functional/func_002E3BD8.c create mode 100644 nonmatching/functional/func_002E48E8.c create mode 100644 nonmatching/functional/func_002E4AC8.c create mode 100644 nonmatching/functional/func_002E5AD0.c create mode 100644 nonmatching/functional/func_002E6178.c create mode 100644 nonmatching/functional/func_002E64A0.c create mode 100644 nonmatching/functional/func_002E6A60.c create mode 100644 nonmatching/functional/func_002E76C0.c create mode 100644 nonmatching/functional/func_002E8368.c create mode 100644 nonmatching/functional/func_002ECB70.c create mode 100644 nonmatching/functional/func_002F0FA0.c create mode 100644 nonmatching/functional/func_002FC280.c create mode 100644 nonmatching/functional/func_00336F20.c create mode 100644 nonmatching/functional/func_00337C10.c create mode 100644 nonmatching/functional/func_003380B0.c create mode 100644 nonmatching/functional/func_003384C0.c create mode 100644 nonmatching/functional/func_0033C590.c create mode 100644 nonmatching/functional/func_0033D028.c create mode 100644 nonmatching/functional/func_0033EA60.c create mode 100644 nonmatching/functional/func_0033FB98.c create mode 100644 nonmatching/functional/func_00341030.c create mode 100644 nonmatching/functional/func_00342978.c create mode 100644 nonmatching/functional/func_003458F8.c create mode 100644 nonmatching/functional/func_00345F70.c create mode 100644 nonmatching/functional/func_00346C60.c create mode 100644 nonmatching/functional/func_00347F80.c create mode 100644 nonmatching/functional/func_003489E8.c create mode 100644 nonmatching/functional/func_0034B2C8.c create mode 100644 nonmatching/functional/func_0034E070.c diff --git a/nonmatching/functional.tsv b/nonmatching/functional.tsv index ce0354c73..8142240c9 100644 --- a/nonmatching/functional.tsv +++ b/nonmatching/functional.tsv @@ -2,3 +2,98 @@ # Each does what the retail function does without matching its bytes; the matching build never reads it. # Files under nonmatching/functional/ are C written for the port only (no matching attempt behind them). # name file +func_00115FC0 nonmatching/functional/func_00115FC0.c +func_00119BF8 nonmatching/functional/func_00119BF8.c +func_00128FD0 nonmatching/functional/func_00128FD0.c +func_00132028 nonmatching/functional/func_00132028.c +func_00132C48 nonmatching/functional/func_00132C48.c +func_00133108 nonmatching/functional/func_00133108.c +func_001334B8 nonmatching/functional/func_001334B8.c +func_00133580 nonmatching/functional/func_00133580.c +func_0026FC78 nonmatching/functional/func_0026FC78.c +func_00279D18 nonmatching/functional/func_00279D18.c +func_0027A9F0 nonmatching/functional/func_0027A9F0.c +func_0027AB40 nonmatching/functional/func_0027AB40.c +func_0027B440 nonmatching/functional/func_0027B440.c +func_0027C540 nonmatching/functional/func_0027C540.c +func_0027C878 nonmatching/functional/func_0027C878.c +func_0027DE50 nonmatching/functional/func_0027DE50.c +func_0027E018 nonmatching/functional/func_0027E018.c +func_0027F2A8 nonmatching/functional/func_0027F2A8.c +func_0027F640 nonmatching/functional/func_0027F640.c +func_0027FC70 nonmatching/functional/func_0027FC70.c +func_00280710 nonmatching/functional/func_00280710.c +func_00283CB8 nonmatching/functional/func_00283CB8.c +func_00283CF0 nonmatching/functional/func_00283CF0.c +func_00284088 nonmatching/functional/func_00284088.c +func_00284268 nonmatching/functional/func_00284268.c +func_002859C8 nonmatching/functional/func_002859C8.c +func_00286738 nonmatching/functional/func_00286738.c +func_0028AB70 nonmatching/functional/func_0028AB70.c +func_0028B160 nonmatching/functional/func_0028B160.c +func_0028B3D0 nonmatching/functional/func_0028B3D0.c +func_0028E640 nonmatching/functional/func_0028E640.c +func_0028EA58 nonmatching/functional/func_0028EA58.c +func_00290B48 nonmatching/functional/func_00290B48.c +func_00291850 nonmatching/functional/func_00291850.c +func_00291CA8 nonmatching/functional/func_00291CA8.c +func_00291EC0 nonmatching/functional/func_00291EC0.c +func_00292888 nonmatching/functional/func_00292888.c +func_00292B88 nonmatching/functional/func_00292B88.c +func_00293000 nonmatching/functional/func_00293000.c +func_002933D0 nonmatching/functional/func_002933D0.c +func_002934F8 nonmatching/functional/func_002934F8.c +func_00294F10 nonmatching/functional/func_00294F10.c +func_00295870 nonmatching/functional/func_00295870.c +func_0029EFD0 nonmatching/functional/func_0029EFD0.c +func_0029F0C8 nonmatching/functional/func_0029F0C8.c +func_0029FDF0 nonmatching/functional/func_0029FDF0.c +func_002A0048 nonmatching/functional/func_002A0048.c +func_002A0250 nonmatching/functional/func_002A0250.c +func_002AD7B8 nonmatching/functional/func_002AD7B8.c +func_002AD860 nonmatching/functional/func_002AD860.c +func_002B6828 nonmatching/functional/func_002B6828.c +func_002B68C8 nonmatching/functional/func_002B68C8.c +func_002B6E78 nonmatching/functional/func_002B6E78.c +func_002B7578 nonmatching/functional/func_002B7578.c +func_002B88F0 nonmatching/functional/func_002B88F0.c +func_002BA0E0 nonmatching/functional/func_002BA0E0.c +func_002C9CF8 nonmatching/functional/func_002C9CF8.c +func_002CA8A8 nonmatching/functional/func_002CA8A8.c +func_002CAB00 nonmatching/functional/func_002CAB00.c +func_002CAF18 nonmatching/functional/func_002CAF18.c +func_002CBAE0 nonmatching/functional/func_002CBAE0.c +func_002CBC58 nonmatching/functional/func_002CBC58.c +func_002D6F38 nonmatching/functional/func_002D6F38.c +func_002DDEC8 nonmatching/functional/func_002DDEC8.c +func_002E35E0 nonmatching/functional/func_002E35E0.c +func_002E3998 nonmatching/functional/func_002E3998.c +func_002E3BD8 nonmatching/functional/func_002E3BD8.c +func_002E48E8 nonmatching/functional/func_002E48E8.c +func_002E4AC8 nonmatching/functional/func_002E4AC8.c +func_002E5AD0 nonmatching/functional/func_002E5AD0.c +func_002E6178 nonmatching/functional/func_002E6178.c +func_002E64A0 nonmatching/functional/func_002E64A0.c +func_002E6A60 nonmatching/functional/func_002E6A60.c +func_002E76C0 nonmatching/functional/func_002E76C0.c +func_002E8368 nonmatching/functional/func_002E8368.c +func_002ECB70 nonmatching/functional/func_002ECB70.c +func_002F0FA0 nonmatching/functional/func_002F0FA0.c +func_002FC280 nonmatching/functional/func_002FC280.c +func_00336F20 nonmatching/functional/func_00336F20.c +func_00337C10 nonmatching/functional/func_00337C10.c +func_003380B0 nonmatching/functional/func_003380B0.c +func_003384C0 nonmatching/functional/func_003384C0.c +func_0033C590 nonmatching/functional/func_0033C590.c +func_0033D028 nonmatching/functional/func_0033D028.c +func_0033EA60 nonmatching/functional/func_0033EA60.c +func_0033FB98 nonmatching/functional/func_0033FB98.c +func_00341030 nonmatching/functional/func_00341030.c +func_00342978 nonmatching/functional/func_00342978.c +func_003458F8 nonmatching/functional/func_003458F8.c +func_00345F70 nonmatching/functional/func_00345F70.c +func_00346C60 nonmatching/functional/func_00346C60.c +func_00347F80 nonmatching/functional/func_00347F80.c +func_003489E8 nonmatching/functional/func_003489E8.c +func_0034B2C8 nonmatching/functional/func_0034B2C8.c +func_0034E070 nonmatching/functional/func_0034E070.c diff --git a/nonmatching/functional/func_00115FC0.c b/nonmatching/functional/func_00115FC0.c new file mode 100644 index 000000000..8cf0c5e2a --- /dev/null +++ b/nonmatching/functional/func_00115FC0.c @@ -0,0 +1,249 @@ +/* functional: high. libc mbtowc_r transcribed from the toolchain tree; exact in bytes, but the switch needs a jump table in .rodata that C cannot place. strlen/strcmp are the retail routines at 0x1157AC/0x115544. */ +#include "common.h" + +/* libc.a newlib _mbtowc_r - multibyte to wide character, the SHIFT_JIS, + * EUC-JP and ISO-2022-JP ("C-JIS") paths of it. + * + * This C is exact: tools/check.py func_00115FC0 --diff reports + * `1 of 1 places match`, and it was written into src/core/00115FC0.c with the + * quad-save profile before being moved here. tools/build.py nevertheless + * refuses to place it in the boot link - + * `func_00115FC0: needs .rodata at its place, which is not C` + * because the `switch (action)` below (eight dense cases over JIS_ACTION) + * compiles to a jump table, and retail keeps that table at 0x13A3C0, in the + * data run that follows the three locale strings. C can place a function but + * not the table a switch needs, so this stays a near-miss, exactly like + * func_00118CC8. The two enum tables the body reads are the same story: + * JIS_state_table at 0x133E80 and JIS_action_table at 0x134030. + * + * Transcribed from the SDK's own newlib tree, which is in this repository at + * build/sdk-compiler-build/source/ee/newlib/libc/stdlib/mbtowc_r.c (revision + * shipped with the EE toolchain; libc.a). The range macros are that tree's + * mbctype.h verbatim. Only the function name, the _reent stand-in and the + * JIS_CHAR_TYPE member called NUL (a macro elsewhere) differ; the algorithm, + * statement order and expressions are unchanged. + * + * Confirmed against RAC2's own bytes rather than assumed: + * - the locale field is at 0x34 (`lw $a0,0x34($s1)`, then strlen, then the + * three strcmp's), and the three compared strings live at 0x13A3A0, + * 0x13A3A8 and 0x13A3B0: "C-SJIS\0", "C-EUCJP\0", "C-JIS\0"; + * - `if (pwc == NULL) pwc = &dummy` is the `daddu $s3,$sp,$zero` followed by + * `movn $s3,$a1,$a1`; + * - the two enum tables are int[12][9] and sit adjacently: JIS_state_table + * at 0x133E80 (0x1B0 bytes) and JIS_action_table at 0x134030, which is + * exactly 12*9*4 apart, and the action switch dispatches through the + * eight-entry table at 0x13A3C0 (0x116260, 0x116274, 0x116280, ...); + * - the tail is `beql $s0,$zero` / `sltu $v0,$zero,*t`, so the C-locale + * fall-through returns 1 or 0 rather than an explicit second test. + * + * The unit saves its registers in TImode slots (`sq`), so it is built with the + * -mastra-quad-saves profile, like its neighbours malloc (00115F28.c) and + * malloc_extend_top (00116460.c). */ + +typedef struct { + u8 pad000[0x34]; + const char *_current_locale; /* 0x34 */ +} MbtowcReent; + +typedef enum { + ESCAPE, + DOLLAR, + BRACKET, + AT, + B, + J, + NULCH, + JIS_CHAR, + OTHER, + JIS_C_NUM +} JIS_CHAR_TYPE; + +typedef enum { + ASCII, + A_ESC, + A_ESC_DL, + JIS, + JIS_1, + JIS_2, + J_ESC, + J_ESC_BR, + J2_ESC, + J2_ESC_BR, + DONE, + INV, + JIS_S_NUM +} JIS_STATE; + +typedef enum { COPY_A, COPY_J, COPY_J2, MAKE_A, MAKE_J, NOOP, EMPTY, ERROR } JIS_ACTION; + +#define ESC_CHAR 0x1b + +#define _issjis1(c) ((c) >= 0x81 && (c) <= 0x9f || (c) >= 0xe0 && (c) <= 0xef) +#define _issjis2(c) ((c) >= 0x40 && (c) <= 0x7e || (c) >= 0x80 && (c) <= 0xfc) +#define _iseucjp(c) ((c) >= 0xa1 && (c) <= 0xfe) +#define _isjis(c) ((c) >= 0x21 && (c) <= 0x7e) + +static JIS_STATE JIS_state_table[JIS_S_NUM][JIS_C_NUM] = { + /* ESCAPE DOLLAR BRACKET AT B J NUL JIS_CHAR OTHER */ + /* ASCII */ {A_ESC, DONE, DONE, DONE, DONE, DONE, DONE, DONE, DONE}, + /* A_ESC */ {DONE, A_ESC_DL, DONE, DONE, DONE, DONE, DONE, DONE, DONE}, + /* A_ESC_DL */ {DONE, DONE, DONE, JIS, JIS, DONE, DONE, DONE, DONE}, + /* JIS */ {J_ESC, JIS_1, JIS_1, JIS_1, JIS_1, JIS_1, INV, JIS_1, INV}, + /* JIS_1 */ {INV, JIS_2, JIS_2, JIS_2, JIS_2, JIS_2, INV, JIS_2, INV}, + /* JIS_2 */ {J2_ESC, DONE, DONE, DONE, DONE, DONE, INV, DONE, DONE}, + /* J_ESC */ {INV, INV, J_ESC_BR, INV, INV, INV, INV, INV, INV}, + /* J_ESC_BR */ {INV, INV, INV, INV, ASCII, ASCII, INV, INV, INV}, + /* J2_ESC */ {INV, INV, J2_ESC_BR, INV, INV, INV, INV, INV, INV}, + /* J2_ESC_BR */ {INV, INV, INV, INV, DONE, DONE, INV, INV, INV}, +}; + +static JIS_ACTION JIS_action_table[JIS_S_NUM][JIS_C_NUM] = { + /* ESCAPE DOLLAR BRACKET AT B J NUL JIS_CHAR OTHER */ + /* ASCII */ {NOOP, COPY_A, COPY_A, COPY_A, COPY_A, COPY_A, EMPTY, COPY_A, COPY_A}, + /* A_ESC */ {COPY_A, NOOP, COPY_A, COPY_A, COPY_A, COPY_A, COPY_A, COPY_A, COPY_A}, + /* A_ESC_DL */ {COPY_A, COPY_A, COPY_A, MAKE_J, MAKE_J, COPY_A, COPY_A, COPY_A, COPY_A}, + /* JIS */ {NOOP, NOOP, NOOP, NOOP, NOOP, NOOP, ERROR, NOOP, ERROR}, + /* JIS_1 */ {ERROR, NOOP, NOOP, NOOP, NOOP, NOOP, ERROR, NOOP, ERROR}, + /* JIS_2 */ {NOOP, COPY_J2, COPY_J2, COPY_J2, COPY_J2, COPY_J2, ERROR, COPY_J2, COPY_J2}, + /* J_ESC */ {ERROR, ERROR, NOOP, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR}, + /* J_ESC_BR */ {ERROR, ERROR, ERROR, ERROR, NOOP, NOOP, ERROR, ERROR, ERROR}, + /* J2_ESC */ {ERROR, ERROR, NOOP, ERROR, ERROR, ERROR, ERROR, ERROR, ERROR}, + /* J2_ESC_BR */ {ERROR, ERROR, ERROR, ERROR, COPY_J, COPY_J, ERROR, ERROR, ERROR}, +}; + +extern unsigned int func_001157AC(const char *); /* retail strlen */ + +extern int func_00115544(const char *, const char *); /* retail strcmp */ + +int func_00115FC0(MbtowcReent *r, int *pwc, const char *s, unsigned int n, int *state) { + int dummy; + unsigned char *t = (unsigned char *)s; + + if (pwc == 0) + pwc = &dummy; + + if (s != 0 && n == 0) + return -1; + + if (r->_current_locale == 0 || (func_001157AC(r->_current_locale) <= 1)) { + /* fall-through */ + } else if (!func_00115544(r->_current_locale, "C-SJIS")) { + int char1 = *t; + if (s == 0) + return 0; /* not state-dependent */ + if (_issjis1(char1)) { + int char2 = t[1]; + if (n <= 1) + return -1; + if (_issjis2(char2)) { + *pwc = (((int)*t) << 8) + (int)(*(t + 1)); + return 2; + } else + return -1; + } + } else if (!func_00115544(r->_current_locale, "C-EUCJP")) { + int char1 = *t; + if (s == 0) + return 0; /* not state-dependent */ + if (_iseucjp(char1)) { + int char2 = t[1]; + if (n <= 1) + return -1; + if (_iseucjp(char2)) { + *pwc = (((int)*t) << 8) + (int)(*(t + 1)); + return 2; + } else + return -1; + } + } else if (!func_00115544(r->_current_locale, "C-JIS")) { + JIS_STATE curr_state; + JIS_ACTION action; + JIS_CHAR_TYPE ch; + unsigned char *ptr; + int i, curr_ch; + + if (s == 0) { + *state = 0; + return 1; /* state-dependent */ + } + + curr_state = (*state == 0 ? ASCII : JIS); + ptr = t; + + for (i = 0; i < n; ++i) { + curr_ch = t[i]; + switch (curr_ch) { + case ESC_CHAR: + ch = ESCAPE; + break; + case '$': + ch = DOLLAR; + break; + case '@': + ch = AT; + break; + case '(': + ch = BRACKET; + break; + case 'B': + ch = B; + break; + case 'J': + ch = J; + break; + case '\0': + ch = NULCH; + break; + default: + if (_isjis(curr_ch)) + ch = JIS_CHAR; + else + ch = OTHER; + } + + action = JIS_action_table[curr_state][ch]; + curr_state = JIS_state_table[curr_state][ch]; + + switch (action) { + case NOOP: + break; + case EMPTY: + *state = 0; + *pwc = 0; + return i; + case COPY_A: + *state = 0; + *pwc = (int)*ptr; + return (i + 1); + case COPY_J: + *state = 0; + *pwc = (((int)*ptr) << 8) + (int)(*(ptr + 1)); + return (i + 1); + case COPY_J2: + *state = 1; + *pwc = (((int)*ptr) << 8) + (int)(*(ptr + 1)); + return (ptr - t) + 2; + case MAKE_A: + case MAKE_J: + ptr = (unsigned char *)(t + i + 1); + break; + case ERROR: + default: + return -1; + } + } + + return -1; /* n < bytes needed */ + } + + /* otherwise this must be the "C" locale or unknown locale */ + if (s == 0) + return 0; /* not state-dependent */ + + *pwc = (int)*t; + + if (*t == '\0') + return 0; + + return 1; +} diff --git a/nonmatching/functional/func_00119BF8.c b/nonmatching/functional/func_00119BF8.c new file mode 100644 index 000000000..4c9debfbb --- /dev/null +++ b/nonmatching/functional/func_00119BF8.c @@ -0,0 +1,357 @@ +/* functional: medium. vfprintf (integer-only newlib engine, ap in 8-byte slots): flags - + # 0 space, width and precision (also from '*'), h l ll q, conversions c d i D u U o O x X p s n and the literal fallback; output goes to the stream buffer (memcpy when fp->_flags has 0x200) or through func_00119AC0 on fp->_file. Reconstructed from the assembly and the jump table at 0x13A7B0; no floating-point cases exist in retail. */ +extern int D_00133E74; +extern int D_00133B80; +extern char D_0013A730[]; +extern char D_0013A740[]; +extern char D_0013A750[]; +extern char D_0013A768[]; +extern char D_0013A770[]; +extern char D_0013A788[]; + +extern int func_00115FC0(int reent, int *wc, char *s, int n, int *state); +extern void func_001153D4(char *dst, char *src, int n); +extern void func_00119AC0(int fd, char *buf, int n, int flag); +extern char *func_00115250(char *s, int c, int n); +extern int func_001157AC(char *s); +extern unsigned long long func_001220F0(unsigned long long a, unsigned long long b); +extern unsigned long long func_00121B20(unsigned long long a, unsigned long long b); + +#define FP_P(fp) (*(char **)(fp)) +#define FP_FLAGS(fp) (*(unsigned short *)((fp) + 0xC)) +#define FP_WORD(fp) (*(int *)((fp) + 0xC)) +#define FP_FILE(fp) (*(short *)((fp) + 0xE)) + +#define PRINT(src, len) \ + do { \ + if (FP_FLAGS(fp) & 0x200) { \ + func_001153D4(FP_P(fp), (src), (len)); \ + FP_P(fp) = FP_P(fp) + (len); \ + } else { \ + if (FP_WORD(fp) == 0) \ + FP_FILE(fp) = 1; \ + func_00119AC0(FP_FILE(fp), (src), (len), 0); \ + } \ + } while (0) + +#define PAD(count, with) \ + do { \ + int pn_ = (count); \ + if (pn_ > 0) { \ + while (pn_ > 16) { \ + PRINT((with), 16); \ + pn_ -= 16; \ + } \ + PRINT((with), pn_); \ + } \ + } while (0) + +int func_00119BF8(int reent, unsigned char *fp, char *fmt, long *ap) +{ + struct { int wc; int state; int ret; } st; + char buf[0x28]; + char ox[2]; + char sign; + char *cp, *start; + char *xdigs = 0; + int ch, n, m, flags, width, prec, dprec, size, realsz, base, fld, pn; + int ladj, zp; + unsigned long long u; + + st.state = 0; + st.ret = 0; + for (;;) { + start = fmt; + while ((n = func_00115FC0(D_00133E74, &st.wc, fmt, D_00133B80, &st.state)) > 0) { + fmt += n; + if (st.wc == '%') { + fmt -= 1; + break; + } + } + m = fmt - start; + if (m != 0) { + PRINT(start, m); + st.ret += m; + } + if (n <= 0) + goto done; + fmt++; + + sign = 0; + dprec = 0; + flags = 0; + width = 0; + prec = -1; + rflag: + ch = *fmt++; + reswitch: + if ((unsigned)(ch - 0x20) >= 0x59) + goto dflt; + switch (ch) { + case ' ': + if (sign == 0) + sign = ' '; + goto rflag; + case '#': + flags |= 1; + goto rflag; + case '*': + ap++; + width = (int)ap[-1]; + if (width >= 0) + goto rflag; + width = -width; + flags |= 4; + goto rflag; + case '+': + sign = '+'; + goto rflag; + case '-': + flags |= 4; + goto rflag; + case '.': + ch = *fmt++; + if (ch == '*') { + ap++; + n = (int)ap[-1]; + prec = (n > -2) ? n : -1; + goto rflag; + } + n = 0; + while ((unsigned)(ch - '0') < 10) { + n = n * 10 + ch - '0'; + ch = *fmt++; + } + prec = (n > -2) ? n : -1; + goto reswitch; + case '0': + flags |= 0x80; + goto rflag; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + n = 0; + do { + n = n * 10 + ch - '0'; + ch = *fmt++; + } while ((unsigned)(ch - '0') < 10); + width = n; + goto reswitch; + case 'h': + flags |= 0x40; + goto rflag; + case 'l': + if (*fmt == 'l') { + fmt++; + flags |= 0x20; + } else { + flags |= 0x10; + } + goto rflag; + case 'q': + flags |= 0x20; + goto rflag; + case 'c': + ap++; + buf[0] = (char)ap[-1]; + cp = buf; + size = 1; + sign = 0; + goto have_string; + case 'D': + flags |= 0x10; + /* fall through */ + case 'd': + case 'i': + ap++; + if (flags & 0x20) + u = (unsigned long long)*(long long *)(ap - 1); + else if (flags & 0x10) + u = (unsigned long long)*(long long *)(ap - 1); + else if (flags & 0x40) + u = (unsigned long long)(long long)*(short *)(ap - 1); + else + u = (unsigned long long)(long long)*(int *)(ap - 1); + if ((long long)u < 0) { + u = -(long long)u; + sign = '-'; + } + base = 1; + goto number; + case 'n': + ap++; + if (flags & 0x20) + *(long long *)(int)ap[-1] = st.ret; + else if (flags & 0x10) + *(long long *)(int)ap[-1] = st.ret; + else if (flags & 0x40) + *(short *)(int)ap[-1] = st.ret; + else + *(int *)(int)ap[-1] = st.ret; + continue; + case 'O': + flags |= 0x10; + /* fall through */ + case 'o': + ap++; + if (flags & 0x20) + u = *(unsigned long long *)(ap - 1); + else if (flags & 0x10) + u = *(unsigned long long *)(ap - 1); + else if (flags & 0x40) + u = *(unsigned short *)(ap - 1); + else + u = *(unsigned int *)(ap - 1); + base = 0; + goto nosign; + case 'p': + ap++; + xdigs = D_0013A750; + base = 2; + flags |= 2; + ch = 'x'; + u = (unsigned long long)(long long)*(int *)(ap - 1); + goto nosign; + case 's': + ap++; + cp = (char *)(int)ap[-1]; + if (cp == 0) + cp = D_0013A768; + if (prec >= 0) { + char *e = func_00115250(cp, 0, prec); + if (e != 0) { + size = e - cp; + if (prec < size) + size = prec; + } else { + size = prec; + } + } else { + size = func_001157AC(cp); + } + sign = 0; + goto have_string; + case 'U': + flags |= 0x10; + /* fall through */ + case 'u': + ap++; + if (flags & 0x20) + u = *(unsigned long long *)(ap - 1); + else if (flags & 0x10) + u = *(unsigned long long *)(ap - 1); + else if (flags & 0x40) + u = *(unsigned short *)(ap - 1); + else + u = *(unsigned int *)(ap - 1); + base = 1; + goto nosign; + case 'X': + xdigs = D_0013A770; + goto hex; + case 'x': + xdigs = D_0013A750; + hex: + ap++; + if (flags & 0x20) + u = *(unsigned long long *)(ap - 1); + else if (flags & 0x10) + u = *(unsigned long long *)(ap - 1); + else if (flags & 0x40) + u = *(unsigned short *)(ap - 1); + else + u = *(unsigned int *)(ap - 1); + if ((flags & 1) && u != 0) + flags |= 2; + base = 2; + goto nosign; + default: + dflt: + if (ch == 0) + goto done; + buf[0] = (char)ch; + cp = buf; + sign = 0; + size = 1; + goto have_string; + } + + nosign: + sign = 0; + number: + dprec = prec; + if (prec >= 0) + flags &= ~0x80; + cp = buf + 0x28; + if (u == 0 && prec == 0) { + size = (buf + 0x28) - cp; + goto have_size; + } + if (base == 1) { + if (u < 10) { + *--cp = (char)(u + '0'); + } else { + do { + *--cp = (char)(func_001220F0(u, 10) + '0'); + u = func_00121B20(u, 10); + } while (u >= 10); + *--cp = (char)(u + '0'); + } + size = (buf + 0x28) - cp; + goto have_size; + } + if (base == 0) { + char last; + do { + last = (char)((u & 7) + '0'); + *--cp = last; + u >>= 3; + } while (u != 0); + if ((flags & 1) && last != '0') + *--cp = '0'; + size = (buf + 0x28) - cp; + goto have_size; + } + if (base == 2) { + do { + *--cp = xdigs[(int)(u & 0xF)]; + u >>= 4; + } while (u != 0); + size = (buf + 0x28) - cp; + goto have_size; + } + cp = D_0013A788; + size = func_001157AC(cp); + goto have_size; + + have_string: + have_size: + realsz = (size < dprec) ? dprec : size; + if (sign != 0) + realsz++; + else + realsz += (flags & 2); + ladj = flags & 4; + zp = flags & 0x84; + pn = dprec - size; + if (zp == 0) + PAD(width - realsz, D_0013A730); + if (sign != 0) { + PRINT(&sign, 1); + } else if (flags & 2) { + ox[0] = '0'; + ox[1] = (char)ch; + PRINT(ox, 2); + } + if (zp == 0x80) + PAD(width - realsz, D_0013A740); + PAD(pn, D_0013A740); + PRINT(cp, size); + if (ladj) + PAD(width - realsz, D_0013A730); + st.ret += (realsz < width) ? width : realsz; + } +done: + if (!(FP_FLAGS(fp) & 0x200)) + func_00119AC0(FP_FILE(fp), 0, 0, 1); + return st.ret; +} diff --git a/nonmatching/functional/func_00128FD0.c b/nonmatching/functional/func_00128FD0.c new file mode 100644 index 000000000..437ebf20d --- /dev/null +++ b/nonmatching/functional/func_00128FD0.c @@ -0,0 +1,18 @@ +/* functional: low. Hand-written from the assembly. Retail also reads a 64-bit register ($s4) it never sets, keeping that register's upper 32 bits when it builds the 0x0103400B tag word for func_001286C8; C cannot read the caller's $s4, so the upper half is passed as 0. The fifth argument (in $t0) is the source of the first copy. The frame is zeroed because retail passes the buffer on with its unwritten gap bytes holding whatever was on the stack (0 in every captured call); zeroing makes that deterministic. */ +extern void func_001153D4(void *, void *, int); +extern int func_001286C8(int, long, void *, void *); + +int func_00128FD0(int a0, int a1, int a2, int a3, void *a4) +{ + int frame[0x38 / 4 + 8]; + char *base = (char *)frame; + int *size = (int *)(base + 0x30); + + { int k; for (k = 0; k < 0x38 / 4 + 8; k++) frame[k] = 0; } + *size = 0x28; + ((int *)base)[1] = a3; + func_001153D4(base + 8, a4, a3); + ((int *)base)[0] = a1; + func_001153D4(base + a3 + 8, (void *)a2, a1); + return func_001286C8(a0, 0x0103400BL, base + 0x30, base); +} diff --git a/nonmatching/functional/func_00132028.c b/nonmatching/functional/func_00132028.c new file mode 100644 index 000000000..1d130cbee --- /dev/null +++ b/nonmatching/functional/func_00132028.c @@ -0,0 +1,131 @@ +/* functional: medium. fcheck SAME on all captured calls; the draft below is the near miss, differing from retail only in scheduling and register allocation. */ +/* func_00132028 (989snd command pump, rac1 func_0012DDC0 twin), 488 bytes, boot. + * 26 October 2026 (M32): 484/488 as its own unit src/core/00132028-g8.c with + * -O2 -G8 -ffunction-sections -mno-split-addresses and the declarations below: + * sda scalars for the $gp words, NOT_SDA for the doubleword D_001A74D8 (keeps it out + * of the jalr slot, retail's lui/ld + lui $at/sd), plain D_001A7084[] and sda + * D_001A7180[] (absolute in flow, $gp in the beql slot). Everything matches except + * the idle-send test: retail forms the count table's address with its own + * addiu $a0,$gp,-0x7B50 before the indexed load, this cc1 folds it into the load + * (addu $v1,$v0,$gp; lw %gprel). An incomplete D_001A74A0[] makes it absolute instead + * (488/488, 2 lines off in both table uses). + * + * Earlier notes follow. + */ + +/* func_00132028, 488 bytes: 989snd's command pump (rac1's func_0012DDC0 twin). + Once the pending RPC completes it runs the return callback or the callbacks + queued with the other command buffer; it finishes a CD read (cache flush, + then the read callback with the reply word); sends the next batch when idle; + and polls a stop request. Returns whether an RPC or a CD read is still in + flight. $gp is 0x001AEFF0; D_001A7180, D_001A7084 and the doubleword + D_001A74D8 are read absolutely (incomplete sda arrays). */ + +typedef void (*SndCallback)(int, long); +typedef struct { + SndCallback fn; + int pad4; + long arg; +} SndCommand; + +extern int D_001A7480 __attribute__((sda)); /* current buffer */ +extern void (*D_001A7490)(int) __attribute__((sda)); /* stop callback */ +extern int D_001A7494 __attribute__((sda)); /* stop requested */ +extern int D_001A7498 __attribute__((sda)); +extern int D_001A749C __attribute__((sda)); /* return pending */ +extern int *D_001A74A0[2] __attribute__((sda)); /* counts */ +extern SndCommand *D_001A74B0[2] __attribute__((sda)); /* commands */ +extern int *D_001A74B8[2] __attribute__((sda)); /* replies */ +extern int D_001A74C0 __attribute__((sda)); /* batch index */ +extern int D_001A74C4 __attribute__((sda)); +extern int D_001A74C8 __attribute__((sda)); /* CD read pending */ +extern SndCallback D_001A74D0 __attribute__((sda)); /* CD read callback */ +extern SndCallback D_001A74E0 __attribute__((sda)); /* return callback */ +extern long D_001A74E8 __attribute__((sda)); /* its argument */ +extern long D_001A74D8 NOT_SDA; +extern int D_001A7180[] __attribute__((sda)); +extern int D_001A7084[]; + +extern int func_00132220(void); +extern void func_0011AEA0(int); +extern void func_00133108(void); +extern int func_00133580(int); + +int func_00132028(void) +{ + int i; + int idx; + SndCallback fn; + long arg; + + if (D_001A7480 != 0 && func_00132220() != 0) { + if (D_001A749C != 0) { + if (D_001A74E0 != 0) + D_001A74E0(D_001A7084[0], D_001A74E8); + D_001A74E0 = 0; + D_001A749C = 0; + } else { + idx = D_001A74C0 != 1; + for (i = 0; i < *D_001A74A0[idx]; i++) { + if (D_001A74B0[idx][i].fn != 0) + D_001A74B0[idx][i].fn(D_001A74B8[idx][i + 1], D_001A74B0[idx][i].arg); + } + } + } + if (D_001A74C8 != 0) { + func_0011AEA0(0); + if (D_001A7180[0] != 0xFFFFFFFF) { + if (D_001A74D0 != 0) { + arg = D_001A74D8; + fn = D_001A74D0; + D_001A74D0 = 0; + D_001A74D8 = 0; + fn(D_001A7180[0], arg); + } + D_001A7180[0] = 0; + D_001A74C8 = 0; + } + } + if (D_001A7480 == 0) { + if (*D_001A74A0[D_001A74C0] != 0 && D_001A74C4 == 0) + func_00133108(); + } + if (D_001A7494 != 0) { + func_00133580(1); + if (D_001A7498 != 0) { + D_001A7494 = 0; + D_001A7498 = 0; + if (D_001A7490 != 0) + D_001A7490(1); + } + } + return D_001A7480 != 0 || D_001A74C8 != 0; +} + +/* from src/core/00131780.c: not attempted to a match; it needs the same -g8 + * sibling unit func_001326D8 now has, and the symbol map below. + * + * SDK snd_Pump (symbol_addrs's own name), 488 bytes. It reaches sixteen + * globals through $gp at GP 0x001AEFF0 - `-0x7B70` is D_001A7480, `-0x7B68` + * D_001A7488, `-0x7B60` D_001A7490, `-0x7B5C` D_001A7494, `-0x7B58` + * D_001A7498, `-0x7B54` D_001A749C, `-0x7B50` D_001A74A0, `-0x7B40` + * D_001A74B0, `-0x7B38` D_001A74B8, `-0x7B30` D_001A74C0, `-0x7B2C` + * D_001A74C4, `-0x7B28` D_001A74C8, `-0x7B20` D_001A74D0, `-0x7B10` + * D_001A74E0 and `-0x7B08` D_001A74E8 - and three absolutely: the sentinel + * D_001A7180 (also the `-0x7E70` gp slot), D_001A7084 and the doubleword + * D_001A74D8. + * + * Shape, for whoever picks it up: return 0 at once unless D_001A74C8 is set + * and func_00132220() agrees; then walk the records of D_001A74B0/ + * D_001A74B8/D_001A74A0 - each entry is 16 bytes with a handler at +0 and an + * argument at +8, called through `jalr` with the second table's word as the + * argument - and drop the pending callback (D_001A74D0 with the doubleword + * D_001A74D8) once the sentinel has moved. The tail sets D_001A74C8 for the + * busy window the func_00132498/func_001326D8 pair tests, runs + * func_001133108/func_00133580(1) and an indirect D_001A7490 callback, and + * returns 1 only when D_001A7480 is clear and D_001A74C8 ends up set. + * + * Spent: the disassembly and the symbol map above, by the worker that matched + * func_001326D8 in the same unit; no compile, because the -g8 route is a + * prerequisite and the body is 488 bytes of indirect calls. + */ diff --git a/nonmatching/functional/func_00132C48.c b/nonmatching/functional/func_00132C48.c new file mode 100644 index 000000000..193c94037 --- /dev/null +++ b/nonmatching/functional/func_00132C48.c @@ -0,0 +1,60 @@ +/* functional: medium. snd_SendCommandSync written from the retail assembly: same reads, writes and calls in the same order; the staging copy and waits sit in static helpers, and the syscall argument registers that retail leaves unset are only matched for the checked calls. */ +extern int D_001A7480 __attribute__((sda)); +extern int D_001A74F8 __attribute__((sda)); +extern int D_001A74C0 __attribute__((sda)); +extern int D_001A74C4 __attribute__((sda)); +extern int *D_001A74A0[] __attribute__((sda)); +extern void func_00132028(void); +extern void func_0011AEA0(int); +extern void func_001322B8(void *, int); +extern int func_0011D810(void *); +extern void func_00115D48(void *); +extern void func_0011D620(void *, int, int, void *, int, void *, int, int, int); +extern int func_00132220(void); +extern void func_00133108(void *); + +static void snd_stage_and_pump(int size, unsigned char *payload) { + int i; + unsigned char *dst = (unsigned char *)0x1A70C0; + + for (i = 0; i < size; i++) { + unsigned char b = payload[i]; + dst[i] = b; + } + while (D_001A7480 != 0) { + func_00132028(); + func_0011AEA0(0); + } + func_001322B8((void *)0x1A7080, 1); + while (func_0011D810((void *)0x1A7040) != 0) { + if (D_001A74F8 == 0) { + func_00115D48((void *)0x1A7578); + } + func_00132028(); + func_0011AEA0(0); + } +} + +static void snd_drain(void) { + while (func_00132220() == 0) { + } +} + +int func_00132C48(int cmd, int size, unsigned char *payload) { + int ret; + int *entry; + + snd_stage_and_pump(size, payload); + if (size != 0) { + func_0011D620((void *)0x1A7040, cmd, 1, (void *)0x1A70C0, size, (void *)0x1A7080, 0xC, 0, 0); + } else { + func_0011D620((void *)0x1A7040, cmd, 1, (void *)0, 0, (void *)0x1A7080, 0xC, 0, 0); + } + snd_drain(); + ret = *(int *)0x1A7084; + entry = D_001A74A0[D_001A74C0]; + if (*entry != 0 && D_001A74C4 == 0) { + func_00133108(entry); + } + return ret; +} diff --git a/nonmatching/functional/func_00133108.c b/nonmatching/functional/func_00133108.c new file mode 100644 index 000000000..4904cb51c --- /dev/null +++ b/nonmatching/functional/func_00133108.c @@ -0,0 +1,31 @@ +/* functional: high. snd_SendCurrentBatch: same reads, writes and calls in the same order; only the register allocation and the $gp-relative addressing differ (globals are addressed absolutely here). */ +extern int D_001A74C0; +extern int *D_001A74A0[2]; +extern int D_001A74A8[2]; +extern int D_001A74B8[2]; +extern int D_001A74F8; +extern int func_001322B8(int, int, int *); +extern int func_0011D810(void *); +extern int func_0011D620(void *, int, int, void *, int, void *, int, int, int); +extern void func_0011AEA0(int); +extern void func_00115D48(int, ...); +extern char D_001A7040[]; + +void func_00133108(void) { + int idx = D_001A74C0; + int nxt; + + func_001322B8(D_001A74B8[idx], *D_001A74A0[idx], D_001A74A0[idx]); + while (func_0011D810(D_001A7040) != 0) { + if (D_001A74F8 == 0) + func_00115D48(0x1A7578); + func_0011AEA0(0); + } + idx = D_001A74C0; + func_0011D620(D_001A7040, 0x4D, 1, D_001A74A0[idx], 0x1000 - D_001A74A8[idx], + (void *)D_001A74B8[idx], (*D_001A74A0[idx] << 2) + 8, 0, 0); + nxt = (D_001A74C0 != 1); + D_001A74C0 = nxt; + *D_001A74A0[nxt] = 0; + D_001A74A8[nxt] = 0xFFC; +} diff --git a/nonmatching/functional/func_001334B8.c b/nonmatching/functional/func_001334B8.c new file mode 100644 index 000000000..ab9185a13 --- /dev/null +++ b/nonmatching/functional/func_001334B8.c @@ -0,0 +1,29 @@ +/* functional: high. Same reads, writes and calls as retail (CdStartRead); only instruction scheduling differs from retail. */ +extern int D_001A748C __attribute__((sda)); +extern int D_001A7494 __attribute__((sda)); +extern int D_001A7498 __attribute__((sda)); +extern void func_0011B3D0(void *, void *); +extern int func_001253A8(void); +extern int func_00133580(int); +extern void func_00132DF0(int, int, void *, int, int); + +int func_001334B8(int arg0, int arg1, int arg2) { + int payload[4]; + + if (D_001A748C == 0) { + return func_001253A8(); + } + if (func_00133580(1) == 1) { + return 0; + } + *(int *)0x1A7100 = 1; + *(int *)0x1A7110 = 0; + func_0011B3D0((void *)0x1A7100, (void *)0x1A713F); + payload[0] = arg0; + payload[1] = arg1; + payload[2] = arg2; + D_001A7494 = 1; + D_001A7498 = 0; + func_00132DF0(0x38, 0xC, payload, 0, 0); + return 1; +} diff --git a/nonmatching/functional/func_00133580.c b/nonmatching/functional/func_00133580.c new file mode 100644 index 000000000..ec6e41c23 --- /dev/null +++ b/nonmatching/functional/func_00133580.c @@ -0,0 +1,28 @@ +/* functional: medium. Same flow as retail (flag clear: tail into func_00124B88(arg0); else poll func_0011B500 until D_001A7100 reads non-zero, pumping func_00132028). Not byte-identical: register allocation differs, so the dead $v1 the callee's system call sees may differ. */ +extern unsigned int D_001A7100; +extern char D_001A713F; +extern void func_0011B500(void *, void *); +extern int func_00124B88(int); +extern int func_00132028(void); + +int func_00133580(int arg0) { + int busy; + + if (*(int *)0x1A748C == 0) + return func_00124B88(arg0); + + func_0011B500(&D_001A7100, &D_001A713F); + busy = D_001A7100 < 1; + *(int *)0x1A7498 = busy; + if (busy == 1) + return 0; + if (arg0 == 1) + return 1; + do { + func_00132028(); + func_0011B500(&D_001A7100, &D_001A713F); + busy = D_001A7100 < 1; + *(int *)0x1A7498 = busy; + } while (busy == 0); + return 0; +} diff --git a/nonmatching/functional/func_0026FC78.c b/nonmatching/functional/func_0026FC78.c new file mode 100644 index 000000000..e9885a121 --- /dev/null +++ b/nonmatching/functional/func_0026FC78.c @@ -0,0 +1,89 @@ +/* functional: high. Near-miss draft that differs from retail in register allocation and scheduling only; fcheck SAME on all captured calls. */ +/* func_0026FC78 (boot, 464 B): rac1 LoadPifAsPSMT8H twin. M29 2026-10-26: 9 lines off, + register-only (a0/a1 swapped for the tbw, th and psm loads of the TEX0 chain); the + or-chain grouping below reproduces retail's instruction order. -O2 -G0. */ +typedef unsigned char u8; +extern int func_0011AEA0(int); +extern int func_001244B8(int, int); + +/* Uploads a PIF image (header arg0, CLUT at +0x20 of 0x200 bytes when +0x14 is set, else + 0x400, then w * h pixels) to GS memory: the 16x16 CLUT to block cbp >> 8 and the pixels + as PSMT8H to tbp >> 8 with a buffer width of w / 64 (at least 1), then writes the + matching TEX0 plus 1, 0 to arg1 (rac1's LoadPifAsPSMT8H). */ +typedef struct { + long q[12]; +} GsLoadImage26FC78 __attribute__((aligned(16))); + +typedef struct { + void *clut; + void *image; + int unk08[3]; + int clutSize; + int imageSize; + int unk1C[4]; + int tbp; + int unk30[3]; + int tbw; + int unk40[3]; + int tw; + int th; +} PifTex26FC78; + +typedef struct { + char unk00[8]; + int w; + int h; + int unk10; + int psm; +} PifHeader26FC78; + +extern void func_00282A60(void *, int, int); +extern int func_00282A18(int); +extern int func_00126288(GsLoadImage26FC78 *, short, short, short, short, short, short, short); +extern int func_001265B0(GsLoadImage26FC78 *, void *); + +void func_0026FC78(char *arg0, unsigned long long *out, int tbp, int cbp) +{ + PifTex26FC78 d; + GsLoadImage26FC78 li; + PifHeader26FC78 *pif = (PifHeader26FC78 *)arg0; + int c; + unsigned long tex0, hi, lo, cl; + + func_00282A60(&d, 0, sizeof(d)); + d.clut = (char *)pif + 0x20; + if (pif->psm == 0) { + d.clutSize = 0x400; + } else { + d.clutSize = 0x200; + } + c = cbp >> 8; + d.tw = func_00282A18(pif->w); + d.th = func_00282A18(pif->h); + d.image = (char *)pif + (d.clutSize + 0x20); + d.imageSize = pif->w * pif->h; + func_00126288(&li, c, 1, pif->psm, 0, 0, 16, 16); + func_0011AEA0(0); + func_001265B0(&li, d.clut); + func_001244B8(0, 0); + d.tbw = pif->w >> 6; + if (d.tbw <= 0) { + d.tbw = 1; + } + d.tbp = tbp >> 8; + func_00126288(&li, d.tbp, d.tbw, 0x1B, 0, 0, pif->w, pif->h); + func_0011AEA0(0); + func_001265B0(&li, d.image); + func_001244B8(0, 0); + lo = (unsigned long)d.tbp | ((unsigned long)d.tbw << 14); + hi = ((unsigned long)0x1B << 20) | ((unsigned long)d.tw << 26); + cl = ((unsigned long)1 << 34) | ((unsigned long)c << 37); + tex0 = lo | hi; + tex0 |= (unsigned long)d.th << 30; + tex0 |= cl; + tex0 |= (unsigned long)pif->psm << 51; + tex0 |= (unsigned long)4 << 61; + out[0] = tex0; + out[1] = 1; + out[2] = 0; +} diff --git a/nonmatching/functional/func_00279D18.c b/nonmatching/functional/func_00279D18.c new file mode 100644 index 000000000..334247adf --- /dev/null +++ b/nonmatching/functional/func_00279D18.c @@ -0,0 +1,76 @@ +/* functional: high. Hand-written from the assembly: builds the 4x4 matrix at D_001B5200 from three vectors, accumulates three of its rows with scaled terms, runs the func_00283788/282D60/282D30 helpers on the fixed matrices and copies the results to D_001B92C0; same reads, writes and calls in retail order, only register use and quad-copy instructions differ. */ +typedef struct { int w[4]; } Quad279D18; +extern float D_001B5570[]; +extern float D_001B5200[]; +extern float D_001B9040[]; +extern float D_001B9100[]; +extern float D_001B92C0[]; +extern void func_00283788(void *, void *, void *); +extern void func_00282D60(void *, void *, float); +extern void func_00282D30(void *, void *, float); + +void func_00279D18(void) +{ + float v[12]; + float *m = D_001B5200; + float *p = D_001B5570; + float *g = D_001B9040; + float e0, e4, e8; + float a, b, c, d; + + *(Quad279D18 *)(&v[0]) = *(Quad279D18 *)(&p[0]); + *(Quad279D18 *)(&v[4]) = *(Quad279D18 *)(&p[4]); + *(Quad279D18 *)(&v[8]) = *(Quad279D18 *)(&p[8]); + + m[0x00 / 4] = -v[4]; + m[0x10 / 4] = -v[5]; + m[0x20 / 4] = -v[6]; + m[0x04 / 4] = -v[8]; + m[0x14 / 4] = -v[9]; + m[0x24 / 4] = -v[10]; + m[0x08 / 4] = v[0]; + m[0x18 / 4] = v[1]; + m[0x28 / 4] = v[2]; + m[0x3C / 4] = 1.0f; + m[0x30 / 4] = 0; + m[0x34 / 4] = 0; + m[0x38 / 4] = 0; + m[0x0C / 4] = 0; + m[0x1C / 4] = 0; + m[0x2C / 4] = 0; + func_00283788(D_001B5200 + 0x40 / 4, D_001B9100, m); + func_00283788(D_001B5200 + 0x80 / 4, D_001B9100 + 0x40 / 4, m); + + e0 = g[0x1A0 / 4]; + e4 = g[0x1A4 / 4]; + e8 = g[0x1A8 / 4]; + a = m[0x8C / 4]; + b = m[0x9C / 4]; + c = m[0xAC / 4]; + d = m[0xBC / 4]; + m[0x80 / 4] = m[0x80 / 4] + a * e0; + m[0x84 / 4] = m[0x84 / 4] + a * e4; + m[0x88 / 4] = m[0x88 / 4] + a * e8; + m[0x90 / 4] = m[0x90 / 4] + b * e0; + m[0x94 / 4] = m[0x94 / 4] + b * e4; + m[0xA0 / 4] = m[0xA0 / 4] + c * e0; + m[0xA4 / 4] = m[0xA4 / 4] + c * e4; + m[0xA8 / 4] = m[0xA8 / 4] + c * e8; + m[0xB0 / 4] = m[0xB0 / 4] + d * e0; + m[0xB4 / 4] = m[0xB4 / 4] + d * e4; + m[0xB8 / 4] = m[0xB8 / 4] + d * e8; + m[0x98 / 4] = m[0x98 / 4] + b * e8; + func_00283788(D_001B5200 + 0xC0 / 4, D_001B9100 + 0x80 / 4, m); + + func_00282D60(D_001B5200 + 0x100 / 4, D_001B9100 + 0x80 / 4, g[0x1C0 / 4]); + func_00282D60(D_001B5200 + 0x110 / 4, D_001B9100 + 0x90 / 4, g[0x1C0 / 4]); + *(Quad279D18 *)(D_001B5200 + 0x120 / 4) = *(Quad279D18 *)(D_001B9100 + 0xA0 / 4); + *(Quad279D18 *)(D_001B5200 + 0x130 / 4) = *(Quad279D18 *)(D_001B9100 + 0xB0 / 4); + func_00283788(D_001B5200 + 0x100 / 4, D_001B5200 + 0x100 / 4, m); + + *(Quad279D18 *)(&D_001B92C0[0]) = *(Quad279D18 *)(&m[0]); + *(Quad279D18 *)(&D_001B92C0[4]) = *(Quad279D18 *)(&m[4]); + *(Quad279D18 *)(&D_001B92C0[8]) = *(Quad279D18 *)(&m[8]); + func_00282D30(&D_001B92C0[12], D_001B5200 + 0x140 / 4, 1024.0f); + D_001B92C0[0x3C / 4] = 1024.0f; +} diff --git a/nonmatching/functional/func_0027A9F0.c b/nonmatching/functional/func_0027A9F0.c new file mode 100644 index 000000000..866f6eb9a --- /dev/null +++ b/nonmatching/functional/func_0027A9F0.c @@ -0,0 +1,40 @@ +/* functional: medium. Near-miss C: same reads, writes and calls in retail order; only register allocation and scheduling differ. */ +extern short D_001A6480[]; +extern int D_001A7340[] __attribute__((sda)); +extern int D_001A7344[] __attribute__((sda)); +extern int D_001A7348[] __attribute__((sda)); +extern int D_001A734C[] __attribute__((sda)); +extern int D_001A7350[] __attribute__((sda)); +extern int D_001A7354[] __attribute__((sda)); +extern int D_001A7358[] __attribute__((sda)); +extern int D_001A735C[] __attribute__((sda)); +extern float D_001B9040[]; +extern float func_00283CE0(int); + +void func_0027A9F0(void) +{ + int h = D_001A6480[0xA9]; + int w = D_001A6480[0xA8]; + int hh = h >> 1; + int hw = w >> 1; + + D_001A7344[0] = h; + D_001A7354[0] = (0x800 - hh) << 4; + D_001A735C[0] = (hh + 0x800) << 4; + D_001A7350[0] = (0x800 - hw) << 4; + D_001A7358[0] = (hw + 0x800) << 4; + D_001B9040[0x28] = 32.0f; + D_001B9040[0x29] = 1048576.0f; + D_001B9040[0x2C] = 0.62f; + D_001A7340[0] = w; + D_001A7348[0] = hw; + D_001A734C[0] = hh; + D_001B9040[0x80] = func_00283CE0(w) * 0.5f; + D_001B9040[0x81] = func_00283CE0(D_001A6480[0xA9]) * 0.5f; + D_001B9040[0x82] = D_001B9040[0x80] * 4.0f; + D_001B9040[0x87] = 524288.0f; + D_001B9040[0x8A] = 255.0f; + D_001B9040[0x83] = D_001B9040[0x81] * 4.0f; + D_001B9040[0x8B] = 0.0f; + D_001B9040[0x86] = 0.0f; +} diff --git a/nonmatching/functional/func_0027AB40.c b/nonmatching/functional/func_0027AB40.c new file mode 100644 index 000000000..bdc660918 --- /dev/null +++ b/nonmatching/functional/func_0027AB40.c @@ -0,0 +1,108 @@ +/* functional: medium. Near-miss draft (1932 of 1972 bytes); same reads, writes and calls in the same order, only the register shape and scheduling differ. */ +typedef unsigned int ProjectionQuad __attribute__((mode(TI))); +extern int D_001A7B90,D_001A8BB0; +extern unsigned char D_001A7BB1; +extern char D_001B9040[],D_001B9300[],D_001AC6E0[]; +extern char D_00263440[],D_00263690[],D_00263640[],D_002633E0[],D_002634A0[],D_002636D0[]; +extern float D_002650B0,D_00264DB0,D_001ACBD0; +extern float func_00283248(float,float),func_00283180(float); +extern void func_002EF080(void); +extern void func_00282AB0(void *,const void *,int); +#define F(p,o) (*(float *)((p)+(o))) +#define I(p,o) (*(unsigned int *)((p)+(o))) +#define Q(p,o) (*(ProjectionQuad *)((p)+(o))) +void func_0027AB40(void) +{ + char *p; + float aspect,x,y,scale,nearPlane,farPlane,range,product; + float xScale,yScale,xRecip,yRecip,depthA,depthB; + p=D_001B9040; + if(D_001A7B90) aspect=0.756f;else aspect=0.775f; + F(p,0xB4)=F(p,0xB0)*aspect; + if(D_001A7BB1) F(p,0xB4)*=0.7500000596046448f; + F(p,0xA8)=F(p,0x208)/F(p,0x200); + F(p,0xAC)=F(p,0x20C)/F(p,0x204); + F(p,0xB8)=1.0f/func_00283180(func_00283248(1.0f,F(p,0xB0))); + F(p,0xBC)=1.0f/func_00283180(func_00283248(1.0f,F(p,0xB4))); + x=1.0f/func_00283180(func_00283248(1.0f,F(p,0xB0)*F(p,0xA8))); + y=1.0f/func_00283180(func_00283248(1.0f,F(p,0xB4)*F(p,0xAC))); + scale=(x/F(p,0xB8)+y/F(p,0xBC))*0.5f; + if(D_001A8BB0==2 && F(p,0xB0)<0.62f) scale*=1.15f; + F(p,0x1D8)=1.0f/F(p,0xA8); + F(p,0x1DC)=1.0f/F(p,0xAC); + F(p,0x1E0)=F(p,0xB8);F(p,0x1E4)=F(p,0xBC); + F(p,0x1E8)=scale; + F(p,0x1F0)=F(p,0xA4);F(p,0x1F4)=F(p,0xA0); + F(p,0x1D0)=F(p,0xA8);F(p,0x1D4)=F(p,0xAC); + Q(D_001B9300,0)=Q(p,0xB0); + Q(D_001B9300,0x10)=Q(p,0x1D0); + Q(D_001B9300,0x20)=Q(p,0x1E0); + Q(D_001B9300,0x30)=Q(p,0x1F0); + if(F(p,0x21C)> 13; + w152 = *(unsigned short *)(D_001A6480 + 0x152); + w150 = *(unsigned short *)(D_001A6480 + 0x150); + s0 = D_001A72DC; + s3 = (int)(w152 << 16) >> 17; + s2 = (int)(w150 << 16) >> 17; + D_001A7354 = (0x800 - s3) << 4; + D_001A7350 = (0x800 - s2) << 4; + v0 = (int)(w150 << 16) >> 22; + t2 = D_001A7354; + a2 = (int)(w150 << 16) >> 16; + t1 = (int)(w152 << 16) >> 16; + top = D_001A7350; + t8 = D_001A72E4; + t0 = e0 | 0x1000000; + t3 = ((long)(a2 - 1) << 16) | ((long)(t1 - 1) << 48); + s0v = ((long)s0 >> 13) | ((long)v0 << 16); + t6 = top | ((long)t2 << 32); + D_00139120[0x80 / 8] = t3; + D_00139380 = t0 | 0x100000000L; + D_001A72EC = t8 + 0x1000; + D_001A72F0 = t8 + 0x1400; + D_001A72F4 = t8 + 0x1800; + D_001A7358 = (s2 + 0x800) << 4; + D_001A735C = (s3 + 0x800) << 4; + D_00139120[0x20 / 8] = s0v; + D_00139120[0x50 / 8] = t6; + D_00139120[0x60 / 8] = t6; + D_00139310 = t0; + D_001A72E8 = t8; + D_001A7340 = a2; + D_001A7344 = t1; + D_001A7348 = s2; + D_001A734C = s3; + D_00139120[0x10 / 8] = s0v; + D_00139120[0x30 / 8] = t0; + D_00139120[0x40 / 8] = t0; + D_00139120[0x70 / 8] = t3; + + if (a0 != 0) { + D_001A72D4 = t8; + D_001A72D0 = t8; + func_0011AEA0(0); + func_001244B8(0, 0); + s1 = 0; + func_0011AEA0(0); + func_00284EA8(); + func_00284F40(0); + func_0011AEA0(0); + func_001244B8(0, 0); + func_00284E80(); + func_00282A60((void *)(0x1C0000 - 0x5000), 0, 0x1000); + s0 = (*(short *)(D_001A6480 + 0x158) * *(short *)(D_001A6480 + 0x15A)) >> 10; + if (s0 > 0) { + do { + func_00126288(&li, s1 << 4, 1, 0, 0, 0, 0x20, 0x20); + s1++; + func_0011AEA0(0); + func_001265B0(&li, (void *)(0x1C0000 - 0x5000)); + func_001244B8(0, 0); + } while (s1 < s0); + } + } +} diff --git a/nonmatching/functional/func_0027C540.c b/nonmatching/functional/func_0027C540.c new file mode 100644 index 000000000..7fe770d24 --- /dev/null +++ b/nonmatching/functional/func_0027C540.c @@ -0,0 +1,44 @@ +/* functional: high. Starts a new 2D batch: remembers the DMA cursor in D_001B15F4 and reserves a qword, resets the VRAM cursor to the dynamic base, clears the UI texture slot table (16 bytes per entry) and a few scratch words, and, when arg is 0, zeroes HUD texture/clut slot halfwords at +4 that point past the VRAM frame buffer B limit (or all clut slots); same reads and writes as retail, only codegen differs. */ +extern int D_001B22A8 __attribute__((sda)); +extern int D_001B15F4 __attribute__((sda)); +extern int D_001B15FC __attribute__((sda)); +extern int D_001B1600 __attribute__((sda)); +extern int D_001A72D0 __attribute__((sda)); +extern int D_001A72D4 __attribute__((sda)); +extern int D_001A72DC __attribute__((sda)); +extern int D_001B1888 __attribute__((sda)); +extern int D_001B1894 __attribute__((sda)); +extern int D_001B1898 __attribute__((sda)); +extern long D_001B9740[]; +extern long D_001A8780 __attribute__((sda)); +extern long D_001A8788 __attribute__((sda)); +extern int D_001A8790 __attribute__((sda)); + +void func_0027C540(int arg) { + int cursor = D_001B22A8; + int base = D_001A72D4; + int n; + int i; + D_001B15F4 = cursor; + D_001B22A8 = cursor + 0x10; + n = D_001B1600; + D_001A72D0 = base; + D_001B15FC = 0; + for (i = 0; i < n; i++) { + D_001B9740[i * 2] = 0; + } + D_001A8780 = 0; + D_001A8788 = 0; + D_001A8790 = 0; + if (arg == 0) { + for (i = 0; i < *(int *)(D_001B1888 + 0x44); i++) { + unsigned short *p = (unsigned short *)(D_001B1894 + i * 8 + 4); + if (!(*p < (D_001A72DC >> 8))) { + *p = 0; + } + } + for (i = 0; i < *(int *)(D_001B1888 + 0x24); i++) { + *(unsigned short *)(D_001B1898 + i * 8 + 4) = 0; + } + } +} diff --git a/nonmatching/functional/func_0027C878.c b/nonmatching/functional/func_0027C878.c new file mode 100644 index 000000000..875b3e654 --- /dev/null +++ b/nonmatching/functional/func_0027C878.c @@ -0,0 +1,161 @@ +/* functional: high. Near miss (16 lines of scheduling off); symbol names taken from retail addresses with $gp = 0x1AEFF0. */ +/* Plain-C texture descriptor and upload-queue reconstruction; not matched. + * Profile: -O2 -G8 -ffunction-sections. 1356/1356 bytes (16 lines off), us 0/28. + * 26 October 2026 (M10): the descriptor word as a linear or-chain over separate statements in a + * fresh temp, and the upload stores in climbed order, take it from 45 to 18 differing lines. Left: + * pure scheduling in the descriptor block (the height load and palette-bits pair come early) and + * the palette constant register pair (a0/v1). Expression forms of palette_bits, the height local + * and the sign constant compile identically. In the permuter farm. + * 26 October 2026 (M14): upload stores in retail order (palette at +6 right after +0, texture at + * +14 last) fix the store block: 16 lines left, all in the descriptor block. A statement-order + * climb over the descriptor block finds nothing better. + */ + +typedef unsigned long TextureBits; +typedef struct { + TextureBits bits; + unsigned short first_offset; + unsigned short second_offset; + unsigned short width_shift; + unsigned short height_shift; +} TextureEntry; + + +extern int D_001A72D0; +extern int D_001A72DC; +extern int D_001A72E0; +extern int D_001A72D8; +extern TextureBits D_001A8780; +extern TextureBits D_001A8788; +extern int D_001A8790; +extern char *D_001B22A8; +extern char D_00138AD0[]; +extern char D_00138DC0[]; +extern TextureEntry D_001B9740[]; +extern int D_001B15FC; +extern char *D_001B1604; +extern char D_001B9340[]; + +/* Resolve a cached texture descriptor, allocating VRAM and queuing its upload + * when necessary. Negative identifiers select the render-target descriptors. */ +TextureBits func_0027C878(int id) +{ + if (id < 0) { + if (id == -5) + return (D_001A72E0 >> 8) | 0x27020000UL | (0xC800UL << 19); + if (id == -8) { + TextureBits frame = (D_001A72DC >> 8) | 0x25B20000UL; + TextureBits palette = ((TextureBits)D_001A8790 << 37) | (0xC800UL << 19); + return frame | palette | (0x8000UL << 46); + } + if (id == -7) + return (D_001A72D8 >> 8) | 0x24020000UL | (0xC800UL << 19); + if (id == -6) + return (D_001A72DC >> 8) | 0x24020000UL | (0xC800UL << 19); + if ((unsigned int)(id + 2) < 2) { + int base; + TextureBits result; + TextureBits frame; + if (D_001A8780 != 0) + return D_001A8780; + base = (D_001A72D0 + 0x1FFF) & -0x2000; + D_001A72D0 = base + 0x20000; + frame = (base >> 13) | 0x02040000; + result = (base >> 8) | 0x20210000UL | (0xC000UL << 19); + *(int *)(D_001B22A8 + 0) = 0x10000003; + *(int *)(D_001B22A8 + 4) = 0; + *(int *)(D_001B22A8 + 8) = 0; + *(int *)(D_001B22A8 + 12) = 0x50000003; + *(int *)(D_001B22A8 + 16) = 0x8001; + *(int *)(D_001B22A8 + 20) = 0x20000000; + *(int *)(D_001B22A8 + 24) = 0xEE; + *(int *)(D_001B22A8 + 28) = 0; + *(TextureBits *)(D_001B22A8 + 32) = frame; + *(int *)(D_001B22A8 + 40) = 0x4D; + *(int *)(D_001B22A8 + 44) = 0; + *(TextureBits *)(D_001B22A8 + 48) = + (D_001A72DC >> 8) | 0x24020000UL | (0xC800UL << 19); + *(int *)(D_001B22A8 + 56) = 7; + *(int *)(D_001B22A8 + 60) = 0; + *(int *)(D_001B22A8 + 64) = 0x3000002F; + *(char **)(D_001B22A8 + 68) = D_00138AD0; + *(int *)(D_001B22A8 + 72) = 0; + *(int *)(D_001B22A8 + 76) = 0x5000002F; + D_001A8780 = result; + D_001B22A8 += 80; + return result; + } + if ((unsigned int)(id + 4) < 2) { + int base; + TextureBits result; + TextureBits frame; + if (D_001A8788 != 0) + return D_001A8788; + base = (D_001A72D0 + 0x1FFF) & -0x2000; + D_001A72D0 = base + 0x2000; + frame = (base >> 13) | 0x02010000; + result = (base >> 8) | 0x18204000UL | (0xB000UL << 19); + *(int *)(D_001B22A8 + 0) = 0x10000003; + *(int *)(D_001B22A8 + 4) = 0; + *(int *)(D_001B22A8 + 8) = 0; + *(int *)(D_001B22A8 + 12) = 0x50000003; + *(int *)(D_001B22A8 + 16) = 0x8002; + *(int *)(D_001B22A8 + 20) = 0x10000000; + *(int *)(D_001B22A8 + 24) = 0xEE; + *(int *)(D_001B22A8 + 28) = 0; + *(TextureBits *)(D_001B22A8 + 32) = frame; + *(int *)(D_001B22A8 + 40) = 0x4D; + *(int *)(D_001B22A8 + 44) = 0; + *(TextureBits *)(D_001B22A8 + 48) = + (D_001A72DC >> 8) | 0x24020000UL | (0xC800UL << 19); + *(int *)(D_001B22A8 + 56) = 7; + *(int *)(D_001B22A8 + 60) = 0; + *(int *)(D_001B22A8 + 64) = 0x3000002F; + *(char **)(D_001B22A8 + 68) = D_00138DC0; + *(int *)(D_001B22A8 + 72) = 0; + *(int *)(D_001B22A8 + 76) = 0x5000002F; + D_001A8788 = result; + D_001B22A8 += 80; + return result; + } + } else { + TextureEntry *entry = &D_001B9740[id]; + if (entry->bits == 0) { + int base = D_001A72D0; + int width = (short)entry->width_shift; + int shift = width - 6; + int palette, texture, count, height; + long bits, column_bits, width_bits, height_bits, palette_bits; + if (shift < 0) + shift = 0; + palette = base >> 8; + base += 0x400; + texture = base >> 8; + column_bits = (long)texture | ((long)(1 << shift) << 14); + width_bits = ((long)((TextureBits)entry->width_shift << 48) >> 22) | 0x01300000L; + height = (short)entry->height_shift; + height_bits = (long)((TextureBits)entry->height_shift << 48) >> 18; + palette_bits = ((long)palette << 37) | (0x8000L << 19); + D_001A72D0 = base + (1 << (width + height)); + bits = column_bits; + bits |= width_bits; + bits |= height_bits; + bits |= palette_bits; + bits |= (-9223372036854775807L - 1); + entry->bits = bits; + count = D_001B15FC; + if (count < 64) { + char *upload = D_001B9340 + (count << 4); + *(unsigned int *)(upload + 0) = (unsigned int)(D_001B1604 + (entry->second_offset << 4)); + *(short *)(upload + 6) = palette; + *(short *)(upload + 4) = 0; + *(unsigned int *)(D_001B9340 + (count << 4) + 8) = (unsigned int)(D_001B1604 + (entry->first_offset << 4)); + *(unsigned char *)(upload + 12) = entry->width_shift; + *(unsigned char *)(upload + 13) = entry->height_shift; + *(short *)(upload + 14) = texture; + D_001B15FC = count + 1; + } + } + } + return D_001B9740[id].bits; +} diff --git a/nonmatching/functional/func_0027DE50.c b/nonmatching/functional/func_0027DE50.c new file mode 100644 index 000000000..fbc582759 --- /dev/null +++ b/nonmatching/functional/func_0027DE50.c @@ -0,0 +1,49 @@ +/* functional: high. Appends a GIF/DMA packet at the cursor D_001B22A8: a 16-byte DMA tag header, two A+D quads, then four 64-bit GS words built from the shifted coordinates and the screen offsets; the fifth argument ($t0, 64 bits) is stored as the data of the second quad. The cursor is advanced in the same steps as retail and left 0x60 bytes on. */ +extern int D_001B22A8; +extern int D_001A7350; +extern int D_001A7354; +extern long long D_001AC7E0[2]; +extern long long D_001AC7F0[2]; + +void func_0027DE50(int a0, int a1, int a2, int a3, long long t0) +{ + int c0 = D_001B22A8; + int c; + long long k; + long long v; + + *(int *)(c0 + 0x0) = 0x10000005; + *(int *)(c0 + 0x4) = 0; + *(int *)(c0 + 0x8) = 0; + *(int *)(c0 + 0xC) = 0x50000005; + c = c0 + 0x10; + D_001B22A8 = c; + ((long long *)c)[0] = D_001AC7E0[0]; + ((long long *)c)[1] = D_001AC7E0[1]; + *(short *)c = (short)0x8001; + + c = D_001B22A8 + 0x10; + D_001B22A8 = c; + *(long long *)c = 0x144; + *(long long *)(c + 8) = t0; + + c = D_001B22A8 + 0x10; + D_001B22A8 = c; + ((long long *)c)[0] = D_001AC7F0[0]; + ((long long *)c)[1] = D_001AC7F0[1]; + *(short *)c = (short)0x8004; + + a0 <<= 4; + a1 <<= 4; + a2 <<= 4; + a3 <<= 4; + k = (long long)0xFFFFF000 << 24; + + c = D_001B22A8 + 0x10; + D_001B22A8 = c; + *(long long *)c = (long long)(a2 + D_001A7350 - 8) | ((long long)(a0 + D_001A7354 - 8) << 16) | k; + *(long long *)(c + 8) = (long long)(a3 + D_001A7350 - 8) | ((long long)(a0 + D_001A7354 - 8) << 16) | k; + *(long long *)(c + 0x10) = (long long)(a2 + D_001A7350 - 8) | ((long long)(a1 + D_001A7354 - 8) << 16) | k; + *(long long *)(c + 0x18) = (long long)(a3 + D_001A7350 - 8) | ((long long)(a1 + D_001A7354 - 8) << 16) | k; + D_001B22A8 = D_001B22A8 + 0x20; +} diff --git a/nonmatching/functional/func_0027E018.c b/nonmatching/functional/func_0027E018.c new file mode 100644 index 000000000..7626f94bf --- /dev/null +++ b/nonmatching/functional/func_0027E018.c @@ -0,0 +1,55 @@ +/* functional: high. Hand-written from the assembly (never matched): emits one 0x60-byte GIF packet (header, 16-byte template, A+D rows) at D_001B22A8 in retail's order; stores and arithmetic widths follow retail. */ +typedef unsigned int u32; +typedef unsigned long long u64; +typedef int Quad __attribute__((mode(TI))); + +extern u32 *D_001B22A8; +extern int D_001A7350[2]; +extern Quad D_001AC800; + +void func_0027E018(int a0, int a1, int a2, int a3, int t0, int t1, int t2, int t3, u64 arg9, u64 arg10) +{ + u32 *p = D_001B22A8; + int ox = D_001A7350[0], oy = D_001A7350[1]; + int x0, x1, y0, y1, r0, r1, r2, r3, r4, r5; + u64 mask, ly0, ly1; + u64 *d; + + p[0] = 0x10000007; + D_001B22A8[1] = 0; + D_001B22A8[2] = 0; + D_001B22A8[3] = 0x50000007; + + x0 = a0 * 16 + ox - 8; + x1 = (a0 + a2) * 16 + ox - 8; + y0 = a1 * 16 + oy - 8; + y1 = (a1 + a3) * 16 + oy - 8; + r0 = (t0 + t2) * 16; /* t2' */ + r1 = t0 * 16; /* t0' */ + r2 = t1 << 20; /* t1' */ + r3 = (t1 + t3) << 20; /* t3' */ + + p = D_001B22A8; + D_001B22A8 = p + 4; + *(Quad *)(p + 4) = D_001AC800; + D_001B22A8 = p + 8; + + mask = (u64)(((0xFFFFu << 16) | 0xF000u)) << 24; + ly0 = (u64)(long long)y0 << 16; + ly1 = (u64)(long long)y1 << 16; + + d = (u64 *)(p + 8); + d[0] = arg10; + d[1] = 0x154; + d[2] = arg9; + d[3] = (u64)(long long)(r2 + r1); + d[4] = ((u64)(long long)x0 | ly0) | mask; + d[5] = (u64)(long long)(r2 + r0); + d[6] = ((u64)(long long)x1 | ly0) | mask; + d[7] = (u64)(long long)(r3 + r1); + d[8] = ((u64)(long long)x0 | ly1) | mask; + d[9] = (u64)(long long)(r3 + r0); + d[10] = ((u64)(long long)x1 | ly1) | mask; + d[11] = 0; + D_001B22A8 = D_001B22A8 + 0x18; +} diff --git a/nonmatching/functional/func_0027F2A8.c b/nonmatching/functional/func_0027F2A8.c new file mode 100644 index 000000000..72d969c7e --- /dev/null +++ b/nonmatching/functional/func_0027F2A8.c @@ -0,0 +1,47 @@ +/* functional: high. Reads, writes and calls in retail order; colour arithmetic done in 64-bit as retail does. */ +typedef int s32; +typedef unsigned char u8; +typedef unsigned int u32; +typedef unsigned long u64; + +extern void func_002FC560(unsigned int, unsigned long); +extern void func_0027E018(int, int, int, int, int, int, int, int, u64, u64); +extern int D_001B166C; +extern int D_001A89B0[] __attribute__((sda)); +extern int D_001B1668 __attribute__((sda)); + +void func_0027F2A8(int x, int y, u64 style, u8 *text, int length, u64 font, u8 *tab) +{ + int n = 0; + + if (D_001B166C == 0) + D_001A89B0[0] = (int)style; + func_002FC560(0x47, 0x33001); + if (length != 0 && *text != 0) { + do { + u32 c = *text; + if (c - 8 < 8) { + if (D_001B1668 != 0) + style = (style & 0xFF000000) | (D_001A89B0[c - 8] & 0xFFFFFF); + } else if (((signed char *)tab)[c * 4 + 3] != 0) { + if ((u8)(c + 0x80) < 0x28) { + u8 *e = tab + (c + 0x40) * 4; + func_0027E018(x + (signed char)e[3], y + (signed char)e[2], 16, 16, e[0], e[1], 16, 16, style, font); + } + c = *text; + if (c < 0x20) { + int g = ((int)(style & 0xFF) + (int)((style >> 8) & 0xFF) + (int)((style >> 16) & 0xFF)) / 3; + u8 *e = tab + *text * 4; + func_0027E018(x, y + (signed char)e[2], 24, 16, e[0], e[1], 24, 16, + g + ((int)(style & 0xFF000000) + (g << 16) + (g << 8)), font); + } else if (c > 0x20) { + u8 *e = tab + c * 4; + func_0027E018(x, y + (signed char)e[2], 16, 16, e[0], e[1], 16, 16, style, font); + } + x += ((signed char *)tab)[*text * 4 + 3]; + } + n++; + text++; + } while (n != length && *text != 0); + } +} diff --git a/nonmatching/functional/func_0027F640.c b/nonmatching/functional/func_0027F640.c new file mode 100644 index 000000000..b0056f0bd --- /dev/null +++ b/nonmatching/functional/func_0027F640.c @@ -0,0 +1,78 @@ +/* functional: high. Text/glyph drawing loop; the near-miss draft below differs from retail only in how often the control character is reloaded, which is a compiler detail. */ +/* Near miss, NOT MATCHING and never linked (M36, 26 October 2026). + Profile: -O2 -G32 -Wa,-G32 -ffunction-sections. us 0/28 (boot + 27 levels); us2/pal absent. + The globals now take retail's forms: the palette is a 32-byte sda table (D_001A89B0[8], so + `sw -0x6640($gp)` and `addiu a1, $gp, -0x6640` as retail), D_001B166C an incomplete array read + [0] (retail lui/lw), D_001B1668 sda. 792/808 bytes at that point (the earlier -G0 draft was 804). + Left: retail reloads the control character (lbu 0($s1)) at the loop top, in the palette arm, + in the glyph arm and at the loop test; this C loads it once and keeps it in $a0. text[i] + indexing instead of a walking pointer is neutral. Reopen with a source form whose character + reads are distinct expressions to gcse. +*/ + +typedef unsigned char u8; +typedef unsigned long u64; +/* Font metrics are four bytes per glyph: atlas u/v, vertical offset, advance. */ +extern int D_001B166C[]; +extern int D_001B1668 __attribute__((sda)); +extern int D_001A89B0[8] __attribute__((sda)); +extern float func_00283CE0(int); +extern void func_0027E1A0(int u, int v, int width, int height, + unsigned int *colors, u64 texture, + float x, float y, float draw_width, float draw_height); + +void func_0027F640(u64 color, u8 *text, int length, u64 texture, u8 *metrics, + float x, float y, float scale) +{ + unsigned int colors[4]; + float height, offset; + int i; + u8 *p; + if (D_001B166C[0] == 0) + D_001A89B0[0] = (int)color; + i = 0; + height = scale * 16.0f; + if (length != 0 && text[0] != 0) { + p = text; + do { + if ((unsigned int)(p[0] - 8) < 8) { + if (D_001B1668 != 0) { + color &= 0xFF000000UL; + color |= D_001A89B0[p[0] - 8] & 0xFFFFFF; + } + goto next_character; + } + if (((signed char *)metrics)[p[0] * 4 + 3] != 0) { + offset = func_00283CE0(((signed char *)metrics)[p[0] * 4 + 2]) * scale; + if ((u8)(p[0] + 128) < 40) { + u8 *accent = &metrics[(p[0] + 64) * 4]; + float dx = func_00283CE0(((signed char *)accent)[3]) * scale; + float dy = func_00283CE0(((signed char *)accent)[2]) * scale; + colors[2] = colors[1] = colors[0] = colors[3] = (int)color; + func_0027E1A0(accent[0], accent[1], 16, 16, colors, texture, + x + dx, y + dy, height, height); + } + if (p[0] < 32) { + u64 sum = (color & 255) + ((color >> 8) & 255) + ((color >> 16) & 255); + int gray = (int)sum / 3; + int shade = (int)(color & 0xFF000000UL) + (gray << 16) + (gray << 8) + gray; + colors[2] = colors[1] = colors[0] = colors[3] = shade; + func_0027E1A0(metrics[p[0] * 4], metrics[p[0] * 4 + 1], + 24, 16, colors, texture, + x, y + offset, scale * 24.0f, scale * 16.0f); + } else if (p[0] > 32) { + colors[2] = colors[1] = colors[0] = colors[3] = (int)color; + func_0027E1A0(metrics[p[0] * 4], metrics[p[0] * 4 + 1], + 16, 16, colors, texture, + x, y + offset, height, height); + } + x += func_00283CE0(((signed char *)metrics)[p[0] * 4 + 3]) * scale; + } +next_character: + i++; + p++; + if (i == length) + break; + } while (p[0] != 0); + } +} diff --git a/nonmatching/functional/func_0027FC70.c b/nonmatching/functional/func_0027FC70.c new file mode 100644 index 000000000..bc82c5b12 --- /dev/null +++ b/nonmatching/functional/func_0027FC70.c @@ -0,0 +1,134 @@ +/* functional: medium. Starts from the near-miss reconstruction: same reads, writes and calls in retail order; byte differences are register allocation and frame size only. */ +typedef unsigned char u8; +typedef unsigned long u64; +extern int D_001B1668 __attribute__((sda)); +extern int D_001A89B0[] __attribute__((sda)); +extern int func_0027F128(u8 *, int, u8 *); +extern void func_0027F640(u64, u8 *, int, u64, u8 *, float, float, float); +extern void func_0027F2A8(int, int, u64, u8 *, int, u64, u8 *); +extern int D_001A8BB0 __attribute__((sda)); +extern int D_001B166C __attribute__((sda)); +extern int D_001A7340, D_001A7344; +extern void func_002FC620(int, int, int, int); + +void func_0027FC70(unsigned short *layout, u64 color, u8 *text, int length, + u64 texture, u8 *metrics) +{ + short starts[32], ends[32], styles[32]; + int width, original_width; + int style, expanded, prior_count, last_width; + int count, cursor, y, i; + short *box = (short *)layout; + if (D_001A8BB0 != 1) + func_002FC620(box[2], box[3] - 1, box[0], box[1] - 1); + D_001B166C = 1; + if (!(layout[9] & 1)) + width = box[3] - box[4]; + else { + int left = box[4] - box[2]; + int right = box[3] - box[4]; + width = (left < right ? left : right) << 1; + } + original_width = width; + style = 0; + expanded = 0; + prior_count = 0; + last_width = 0; +retry_wrap: + count = 0; + cursor = 0; + if (length != 0 && text[0] != 0) { + do { + int end, row_width = 0; + starts[count] = cursor; + styles[count] = style; + end = cursor; + if (width > 0) { + do { + int advance; + if (text[cursor] == 32 || text[cursor] < 16) + end = cursor; + if (D_001B1668 && (unsigned int)(text[cursor] - 8) < 8) + style = text[cursor] - 8; + if (text[cursor] < 2) + break; + advance = ((signed char *)metrics)[text[cursor] * 4 + 3]; + cursor++; + if (advance != 0) + row_width += advance; + } while (row_width < width); + } + ends[count] = end; + if (ends[count] == starts[count]) + ends[count] = cursor; + cursor = ends[count]; + if (text[cursor] == 32 || text[cursor] < 16) + ends[count]--; + count++; + if (text[cursor] == 0) { + last_width = row_width; + break; + } + cursor++; + if (cursor == length) + break; + } while (text[cursor] != 0); + } + if (!expanded && count >= 2) { + if (prior_count == 0) + prior_count = count; + if (prior_count < count) { + width = original_width; + expanded = 1; + goto retry_wrap; + } + if (last_width < width / 3) { + width -= 16; + goto retry_wrap; + } + } + box[6] = 0; + box[7] = count * box[8]; + y = box[5]; + if (layout[9] & 2) + y -= box[7] >> 1; + for (i = 0; i < count; i++, y += box[8]) { + int amount, pixels; + if (y + box[8] < box[0]) + continue; + if (y > box[1]) + continue; + amount = ends[i] - starts[i] + 1; + if (length > 0 && amount > length) + amount = length; + pixels = func_0027F128(text + starts[i], amount, metrics); + if (box[6] < pixels) + box[6] = pixels; + if (!(layout[9] & 4)) { + D_001A89B0[0] = (int)color; + if (layout[9] & 8) { + float x; + if (layout[9] & 1) + x = (float)(box[4] - (pixels >> 1)) + (float)box[10] * 0.0625f; + else + x = (float)box[4] + (float)box[10] * 0.0625f; + func_0027F640((long)D_001A89B0[styles[i]], text + starts[i], amount, + texture, metrics, x, (float)y + (float)box[11] * 0.0625f, 1.0f); + } else if (layout[9] & 1) { + func_0027F2A8(box[4] - (pixels >> 1), y, (long)D_001A89B0[styles[i]], + text + starts[i], amount, texture, metrics); + } else { + func_0027F2A8(box[4], y, (long)D_001A89B0[styles[i]], + text + starts[i], amount, texture, metrics); + } + } + if (length > 0) { + length -= amount; + if (length <= 0) + break; + } + } + D_001B166C = 0; + if (D_001A8BB0 != 1) + func_002FC620(0, D_001A7340 - 1, 0, D_001A7344 - 1); +} diff --git a/nonmatching/functional/func_00280710.c b/nonmatching/functional/func_00280710.c new file mode 100644 index 000000000..eace07350 --- /dev/null +++ b/nonmatching/functional/func_00280710.c @@ -0,0 +1,55 @@ +/* functional: high. Same scan of the 32 48-byte entries (flag 4), double buffer at D_001B22A0[1 - D_001B22B4], same calls in order; differs from retail in register use and in reading the counter at 0x1B1658 absolutely instead of one gp-relative read. */ +typedef struct { + int pad0[7]; + unsigned int flags; + short a, b, c, d; + int pad1[2]; +} Ent280710; +extern Ent280710 D_001C8A10[]; +extern int D_001B1658; +extern int D_001B22A0[]; +extern int D_001B22B4; +extern void func_00285708(short, short, short, short, int); +extern void func_001244B8(int, int); +extern void func_00280670(int, int); + +void func_00280710(void) +{ + int buf[2]; + int cur = 0; + int last = -1; + int i, j, s; + + if (D_001B1658 == 0) + return; + buf[0] = D_001B22A0[1 - D_001B22B4]; + buf[1] = buf[0] + 0x40000; + for (i = 31; i >= 0; i--) { + if (D_001C8A10[i].flags & 4) { + last = i; + func_00285708(D_001C8A10[i].a, D_001C8A10[i].b, D_001C8A10[i].c, D_001C8A10[i].d, buf[cur]); + func_001244B8(0, 0); + } + } + s = last; + if (s == -1) + return; + while (s >= 0) { + cur = 1 - cur; + last = -1; + for (j = s - 1; j >= 0; j--) { + if (D_001C8A10[j].flags & 4) { + last = j; + func_00285708(D_001C8A10[j].a, D_001C8A10[j].b, D_001C8A10[j].c, D_001C8A10[j].d, buf[cur]); + } + } + func_00280670(buf[1 - cur], s); + if (D_001C8A10[s].flags & 1) { + D_001C8A10[s].flags &= ~5; + D_001B1658--; + } + if (last >= 0) + func_001244B8(0, 0); + s = last; + } +} diff --git a/nonmatching/functional/func_00283CB8.c b/nonmatching/functional/func_00283CB8.c new file mode 100644 index 000000000..a04228424 --- /dev/null +++ b/nonmatching/functional/func_00283CB8.c @@ -0,0 +1,15 @@ +/* functional: high. Retail keeps the float to int truncation in the FPU, this spelling goes through a GPR; same value. */ +/* func_00283CB8 - 16 bytes, 28 places (the boot and every level), retail at + 0x00283cb8: cvt.w.s $f0,$f12 ; cvt.s.w $f0,$f0 ; jr $ra ; + sub.s $f0,$f12,$f0. Retail keeps the truncated integer in the FPU; + this cc1 moves it through a GPR (`cvt.w.s $f0,$f12 ; mfc1 $v0,$f0 ; + nop ; mtc1 $v0,$f0 ; cvt.s.w $f0,$f0`), so the candidate is 32 bytes + instead of 16. Eight spellings were measured (the cast through a local, + through an int local, as an expression, with the cast applied twice, with + the argument used twice): all give the same GPR round trip. The SDK + compiler in build/compiler/sdk gives the same words. + What would unblock it: a source whose float->int->float conversion stays + in the FPU. */ +typedef float f32; + +f32 func_00283CB8(f32 a0) { f32 b = (f32)(int)a0; return a0 - b; } diff --git a/nonmatching/functional/func_00283CF0.c b/nonmatching/functional/func_00283CF0.c new file mode 100644 index 000000000..e7fa5339e --- /dev/null +++ b/nonmatching/functional/func_00283CF0.c @@ -0,0 +1,13 @@ +/* functional: high. A float-to-int conversion with the same result as retail; only the destination register differs (fcheck SAME on 10 of 10 calls). */ +/* func_00283CF0 - 16 bytes, 28 places (the boot and every level), retail at + 0x00283cf0: cvt.w.s $f12,$f12 ; mfc1 $v0,$f12 ; jr $ra ; nop. Only the + destination register differs: this cc1 always converts into a fresh FPR + (`cvt.w.s $f0,$f12 ; mfc1 $v0,$f0`). Nine spellings were measured (the + cast direct, through a float local, through an int local, through an + unsigned cast, with the argument used twice, with the argument used in an + addition): all give $f0. The SDK compiler in build/compiler/sdk gives + the same words. What would unblock it: a source that lets the + conversion reuse the argument's register. */ +typedef float f32; + +int func_00283CF0(f32 a0) { return (int)a0; } diff --git a/nonmatching/functional/func_00284088.c b/nonmatching/functional/func_00284088.c new file mode 100644 index 000000000..fa89829d1 --- /dev/null +++ b/nonmatching/functional/func_00284088.c @@ -0,0 +1,59 @@ +/* functional: medium. Only one captured call, so not every branch is exercised. Builds a GIF packet (header words, a context-derived register word, then 16 quads of 4 words each) in the buffer at 0x1A6AF0; branches on arg0/arg1/flags exactly as retail; the 0xA0 divide guard (break) is dead code (constant divisor) and left out; same writes in a different order, bytes differ only in codegen. */ +extern long D_001A6AF0[] __attribute__((nosda)); +extern char *D_001A742C __attribute__((sda)); +extern unsigned char D_001A7BB1 __attribute__((sda)); +extern int D_001A7BB8 __attribute__((sda)); + +void func_00284088(int arg0, int arg1) { + long *p = D_001A6AF0; + char *ctx = D_001A742C; + long v; + int sel, w, i; + long t2, t4; + p[0] = 0x308B400000000001L; + p[1] = 0xEEE; + p[2] = 0x30000; + p[3] = 0x47; + p[4] = 0x100000261L; + p[5] = 0x14; + v = ((long)(*(short *)(ctx + 0x156)) << 5) + | ((long)((*(unsigned short *)(ctx + 0x150) >> 6) & 0x3F) << 14) + | ((long)(*(short *)(ctx + 0x154)) << 20) + | 0xEA8000000L; + p[6] = v; + p[7] = 6; + p[8] = 0x4400000000008010L; + p[9] = 0x5353; + if (arg0 == 0) { + sel = D_001A7BB1 != 0; + } else { + sel = arg1; + } + w = *(short *)(ctx + 0x15A); + if (sel) { + w = w << 3; + } else { + w = ((w * 0x73) / 0xA0) << 3; + } + ctx = D_001A742C; + t4 = (long)(0x7FF8 - w) << 16; + t2 = (long)(w + 0x7FF8) << 16; + p = D_001A6AF0 + 10; + for (i = 0; i < 16; i++) { + int a, b, c; + if (D_001A7BB8 != 0) { + a = i * *(short *)(ctx + 0x150); + b = i * 0x280 + 0x6BF8; + c = (i + 1) * 0x280 + 0x6BF8; + } else { + a = i * *(short *)(ctx + 0x150); + b = (i << 9) + 0x6FF8; + c = ((i + 1) << 9) + 0x6FF8; + } + p[0] = a; + p[1] = (long)b | t4; + p[2] = (long)((i + 1) * *(short *)(ctx + 0x150)) | 0x1A000000L; + p[3] = (long)c | t2; + p += 4; + } +} diff --git a/nonmatching/functional/func_00284268.c b/nonmatching/functional/func_00284268.c new file mode 100644 index 000000000..16977da18 --- /dev/null +++ b/nonmatching/functional/func_00284268.c @@ -0,0 +1,283 @@ +/* functional: medium. Transcribed from the assembly: fills the draw/display state at D_001A6480 and the GS packets (draw environment, 16-step clamp tables, display lists) in the same order with the same constants as retail; the table loops are written as plain loops. Return value: retail falls out with whatever func_0011AEA0 left, treated as void. */ +typedef unsigned long u64; +typedef unsigned int u32; + +extern unsigned char D_001A6480[]; +extern unsigned char *D_001A742C __attribute__((sda)); +extern int D_001A72D8, D_001A72DC, D_001A72E0, D_001A7BB8; +extern int D_001A7438, D_001A743C, D_001A7440, D_001A7444, D_001A7448; +extern int D_001A744C __attribute__((sda)); +extern int D_001A7450, D_001A7454, D_001A7458, D_001A745C, D_001A7460, D_001A7464; +extern u64 D_001A6860[], D_001A6600[], D_001A6D40[], D_001A6E90[]; +extern u64 D_00138850[], D_00138990[], D_00138AD0[], D_00138DC0[], D_001390B0[]; + +extern void func_00125A20(void *, int, int, int, int, int); +extern void func_00125F20(void *, int, int, int, int, int); +extern void func_00284088(int, int); +extern void func_00282AB0(void *, void *, int); +extern void func_0011AEA0(int); + +#define SH(o) (*(short *)(S + (o))) +#define Q64(o) (*(u64 *)(S + (o))) + +void func_00284268(int a0, int a1, int a2, int a3, int a4, int a5) +{ + unsigned char *S = D_001A6480; + u64 *w; + u64 v, v2; + int i, t; + int g72d8 = D_001A72D8 >> 13; + int g72dc = D_001A72DC >> 13; + int g72e0 = D_001A72E0 >> 13; + + SH(0x150) = a0; + SH(0x152) = a1; + SH(0x15C) = 1; + SH(0x16C) = 0x31; + SH(0x15E) = g72d8; + SH(0x156) = g72dc; + SH(0x16E) = g72e0; + D_001A742C = S; + *(int *)(S + 0x170) = 0; + SH(0x158) = a2; + SH(0x15A) = a3; + SH(0x168) = a4; + SH(0x16A) = a5; + SH(0x154) = 0; + SH(0x164) = 0; + func_00125A20(S, 1, (short)a2, (short)a3, (short)a4, (short)a5); + + S = D_001A742C; + Q64(0x10) = (Q64(0x10) & -0x200L) | (SH(0x15E) & 0x1FF); + if (D_001A7BB8 != 0) + Q64(0x18) = ((((0xDFA7L << 16 | 0xF804) << 19) | 0xD050) << 2); + + S = D_001A742C; + D_001A7438 = (int)(Q64(0x18) & 0xFFF); + D_001A743C = (int)((Q64(0x18) >> 12) & 0x7FF); + D_001A7440 = (int)((Q64(0x18) >> 23) & 0xF); + D_001A7444 = (int)((Q64(0x18) >> 27) & 3); + D_001A7448 = (int)(*(u32 *)(S + 0x1C) & 0xFFF); + v = (Q64(0x18) >> 44) & 0x7FF; + D_001A7450 = D_001A7438; + D_001A7454 = D_001A743C; + D_001A7458 = D_001A7440; + D_001A7460 = D_001A7448; + D_001A745C = D_001A7444; + D_001A7464 = (int)v; + D_001A744C = (int)v; + func_00125F20(S + 0x40, SH(0x154), SH(0x150), SH(0x152), 3, SH(0x16C)); + + S = D_001A742C; + Q64(0x40) = (Q64(0x40) & -0x200L) | (SH(0x156) & 0x1FF); + Q64(0x50) = (u64)(long)SH(0x16E) | ((u64)((unsigned short)SH(0x16C) & 0xF) << 24); + Q64(0x30) = 0; + Q64(0x38) = 0; + S = D_001A742C; + v = Q64(0x30); + v2 = Q64(0x38); + v = ((((v & -0x8000L) | 8) | 0x8000) & 0x0FFFFFFFFFFFFFFFL) | 0x1000000000000000L; + v2 = (v2 & -16L) | 14; + Q64(0x38) = v2; + Q64(0x30) = v; + func_00125F20(S + 0xD0, SH(0x15C), SH(0x158), SH(0x15A), 0, 0); + + S = D_001A742C; + Q64(0xE0) = 0x8000L << 17; + Q64(0xD0) = (Q64(0xD0) & -0x200L) | (SH(0x15E) & 0x1FF); + Q64(0xC0) = 0; + Q64(0xC8) = 0; + S = D_001A742C; + v = Q64(0xC0); + v2 = Q64(0xC8); + v = (((((v & -0x8000L) | 8) | 0x8000) & 0x0FFFFFFFFFFFFFFFL) | 0x1000000000000000L); + v2 = (v2 & -16L) | 14; + Q64(0xC8) = v2; + Q64(0xC0) = v; + + w = D_001A6860; + w[0] = ((0x8116L << 16 | 0x8000) << 31) | 1; + w[1] = 0xEEEE; + w[2] = 0x30000; + w[3] = 0x47; + w[4] = 5; + w[5] = 8; + w[6] = (0x8000L << 17) | 0x261; + w[7] = 0x14; + S = D_001A742C; + w[9] = 6; + w[10] = (0x8800L << 47) | 0x8010; + w[11] = 0x5353; + w[8] = (u64)(long)(((unsigned short)SH(0x150) >> 6 & 0x3F) << 14 | SH(0x156) << 5) + | ((u64)(long)SH(0x154) << 20) | (0xEA80L << 20); + + if (D_001A7BB8 != 0) { + int t1 = 0x8000, t2 = 0x8280; + + w = D_001A6860 + 12; + for (i = 0; i < 16; i++) { + *w++ = (u64)(long)(i * SH(0x150)); + S = D_001A742C; + *w++ = (u64)(long)(t1 - (SH(0x158) << 3)) | ((u64)(long)(0x7FF8 - (SH(0x15A) << 3)) << 16); + t1 += 0x280; + S = D_001A742C; + *w++ = (u64)(long)((i + 1) * SH(0x150)) | ((u64)(long)SH(0x152) << 20); + S = D_001A742C; + *w++ = (u64)(long)(t2 - (SH(0x158) << 3)) | ((u64)(long)((SH(0x15A) << 3) + 0x7FF8) << 16); + t2 += 0x280; + S = D_001A742C; + } + } else { + w = D_001A6860 + 12; + for (i = 0; i < 16; i++) { + *w++ = (u64)(long)(i * SH(0x150)); + S = D_001A742C; + *w++ = (u64)(long)(i * SH(0x158) + 0x8000 - (SH(0x158) << 3)) | ((u64)(long)(0x7FF8 - (SH(0x15A) << 3)) << 16); + S = D_001A742C; + *w++ = (u64)(long)((i + 1) * SH(0x150)) | ((u64)(long)SH(0x152) << 20); + S = D_001A742C; + *w++ = (u64)(long)((i + 1) * SH(0x158) + 0x8000 - (SH(0x158) << 3)) | ((u64)(long)((SH(0x15A) << 3) + 0x7FF8) << 16); + S = D_001A742C; + } + } + + w = D_001A6860; + w[0x270 / 8] = 0x181; + w[0x278 / 8] = 0x8000L << 16; + w[0x260 / 8] = (0x8800L << 47) | 0x8001; + w[0x268 / 8] = 0x4410; + S = D_001A742C; + w[0x280 / 8] = (u64)(long)(0x7FF8 - (SH(0x158) << 3)) | ((u64)(long)(0x7FF8 - (SH(0x15A) << 3)) << 16); + S = D_001A742C; + w[0x288 / 8] = (u64)(long)(0x7FF8 - (SH(0x158) << 3)) | ((u64)(long)((SH(0x15A) << 3) + 0x7FF8) << 16); + func_00284088(0, 0); + + w = D_001A6600; + w[0] = ((0x8116L << 16 | 0x8000) << 31) | 1; + w[5] = 8; + w[6] = (0x8000L << 17) | 0x261; + w[7] = 0x14; + w[1] = 0xEEEE; + w[2] = 0x31001; + w[3] = 0x47; + w[4] = 5; + S = D_001A742C; + w[9] = 6; + w[10] = (0x8800L << 47) | 0x8010; + w[11] = 0x5353; + w[8] = (u64)(long)(((unsigned short)SH(0x150) >> 6 & 0x3F) << 14 | SH(0x156) << 5) + | ((u64)(long)SH(0x154) << 20) | (0xEA80L << 20); + { + int t0 = 0x7000, t5 = 0x200, t6 = 0x7200; + + w = D_001A6600 + 12; + for (i = 0; i < 16; i++) { + *w++ = (u64)(long)(i << 9); + S = D_001A742C; + *w++ = (u64)(long)t0 | ((u64)(long)(0x8000 - (SH(0x152) << 3)) << 16); + t0 += 0x200; + S = D_001A742C; + *w++ = (u64)(long)t5 | ((u64)(long)SH(0x152) << 20); + t5 += 0x200; + S = D_001A742C; + *w++ = (u64)(long)t6 | ((u64)(long)((SH(0x152) << 3) + 0x7FF0) << 16); + t6 += 0x200; + } + } + + w = D_001A6D40; + w[0] = 0x8000L << 45 | 1; + w[4] = (0x9000L << 46) | 0x8001; + w[1] = 0xE; + w[2] = 0x30000; + w[3] = 0x47; + w[5] = 0x10; + w[6] = 0x106; + w[7] = 0x8000L << 16; + w[8] = (0x9000L << 46) | 0x8010; + w[9] = 0x44; + { + int a = 0x6FF8, b = 0x71F8; + + w = D_001A6D40 + 10; + for (i = 0; i < 16; i++) { + S = D_001A742C; + *w++ = (u64)(long)a | ((u64)(long)(0x7FF8 - (SH(0x152) << 3)) << 16); + a += 0x200; + S = D_001A742C; + *w++ = (u64)(long)b | ((u64)(long)((SH(0x152) << 3) + 0x7FF8) << 16); + b += 0x200; + } + } + func_00282AB0(D_001A6E90, D_001A6D40, 0x150); + + { + int a = 0x6FF8, b = 0x71F8; + + w = D_00138850 + 8; + for (i = 0; i < 16; i++) { + S = D_001A742C; + *w++ = (u64)(long)a | ((u64)(long)(0x7FF8 - (SH(0x152) << 3)) << 16); + a += 0x200; + S = D_001A742C; + *w++ = (u64)(long)b | ((u64)(long)((SH(0x152) << 3) + 0x7FF8) << 16); + b += 0x200; + } + } + + w = D_00138990 + 8; + for (i = 0; i < 16;) { + S = D_001A742C; + if (D_001A7BB8 != 0) { + *w++ = (u64)(long)(i * 0x280 + 0x6BF8) | ((u64)(long)(0x7FF8 - (SH(0x15A) << 3)) << 16); + i++; + S = D_001A742C; + *w++ = (u64)(long)(i * 0x280 + 0x6BF8) | ((u64)(long)((SH(0x15A) << 3) + 0x7FF8) << 16); + } else { + *w++ = (u64)(long)((i << 9) + 0x6FF8) | ((u64)(long)(0x7FF8 - (SH(0x15A) << 3)) << 16); + i++; + S = D_001A742C; + *w++ = (u64)(long)((i << 9) + 0x6FF8) | ((u64)(long)((SH(0x15A) << 3) + 0x7FF8) << 16); + } + } + + t = 0; + w = D_00138AD0 + 24; + { + int a = 0; + + for (i = 0; i < 16; i++) { + *w++ = (u32)a; + *w++ = (u32)t; + a += 0x200; + t += 0x100; + S = D_001A742C; + *w++ = (u64)(u32)a | ((u64)(long)SH(0x152) << 20); + *w++ = (u64)(u32)(t | 0x10000000); + } + } + + t = 0; + w = D_00138DC0 + 24; + { + int a = 0; + + for (i = 0; i < 16; i++) { + *w++ = (u32)a; + *w++ = (u32)t; + a += 0x200; + t += 0x40; + S = D_001A742C; + *w++ = (u64)(u32)a | ((u64)(long)SH(0x152) << 20); + *w++ = (u64)(u32)(t | 0x04000000); + } + } + + { + int d = D_001A72DC; + + D_001390B0[10] = (u64)(long)(d >> 8) | 0x24020000 | (0xC800L << 19); + D_001390B0[2] = (u64)((long)d >> 13) | (((((0xFFFFL << 16) | 0xFF00) << 20 | 0x8000) << 4)); + } + func_0011AEA0(0); +} diff --git a/nonmatching/functional/func_002859C8.c b/nonmatching/functional/func_002859C8.c new file mode 100644 index 000000000..282c2a48b --- /dev/null +++ b/nonmatching/functional/func_002859C8.c @@ -0,0 +1,446 @@ +/* functional: high. Near-miss draft that differs from retail in register allocation and scheduling only; fcheck SAME on all captured calls. */ +#include "include_asm.h" + +/* Near miss: func_002859C8 (boot 0x2859C8, 3436 bytes, in every level too). + + This C compiles to 3436 bytes, the same size as retail, with the same frame + (0x4D0, s0-s5/ra and f20-f22 saved). 843 of the 859 instructions align with + retail once relocated operands are masked; `tools/check.py` stops at +0xA50. + It has to be compiled with -G8 (as src/game/00285840-g8.c): the $gp loads at + +0x8D8 and +0xA90.. are in ordinary flow. + + What still differs (scheduling and allocation only): + - Cases 9, 10 and 11 (the three identical func_002802E0 panel calls): + retail leaves the ALU idle in the first cycles and computes `&box` (and its + copy to $a0) after the stack-argument store, with the add.s two slots + earlier; here sched2 issues `&box` in cycle 2. Tried with no change: the + rounded alpha in a separate int or float variable, a local MenuBox pointer, + both operand orders, and four inline-helper shapes (those shrink the body). + - Case 11: retail compares S.f58 against 1 held in $s0 (set in the delay slot + of the func_002802E0 call), which also puts `&S` ($s4) before the call; here + the 1 is a fresh $v0 after the load. Retail behaves as if a pseudo holding 1 + already crossed that call; no natural source for it was found. In the same + case, the S.f58 == 3 path orders `a2 = s2` before `a1 = s0` for sprintf and + keeps text(0x113B) in $s2 (here $s0). + + Even once exact it cannot land yet: the outer switch (11 entries, 0x26C590) + and the inner one in case 1 (23 entries, 0x26C5C0) are jump tables, and + tools/build.py refuses a body that needs .rodata at its place (see + func_00123D30 in nonmatching/README.md). + + What made the difference while matching, for the next attempt: + - every case keeps its own block-scoped variables (one shared `id` or `text` + becomes one pseudo and drags every case into callee-saved registers); + - memset (func_00115484) must return void *, strncpy (func_00115AC0) char * + and sprintf (func_00115DA8) int: the call setting $v0 steers the next value + to $v1 (local-alloc's anti-false-dependency rule), which fixed the box + builders, case 2's first byte test and several argument orders at once; + - the SPR base is a register variable (`spr`), not a folded constant; + - `show = 1` precedes the f5C test in case 1 (it lands in the blez delay slot); + - inner cases 2 and 21 assign id1 after the text call, 17/18/20 before it, + which is what decides retail's cross-jumped tails; + - the plain small extern D_001A8C8C under -G8 gives retail's $gp load in a + delay slot and lui/lw elsewhere; D_001F29A0 must be non-small (cc1 splits + it, so its lui can sit in a delay slot on its own); + - case 11's {0, 1, 2, 3} switch (0 and 2 just break) reproduces retail's + decision tree; case 3's tail is func_00280190's body inlined. +*/ + +typedef struct { + short x; + unsigned short f02; + short f04; + short f06; + short f08; + short f0A; + short f0C; + short h; + short f10; + unsigned short flags; + short f14; + short f16; +} MenuBox; + +typedef struct { + char pad0[0x40]; + int mode; + int f44; + char pad48[0x10]; + int f58; + int f5C; + int f60; + char pad64[0x18]; + short f7C; + short f7E; + int f80; +} MenuState; + +extern MenuState D_001BAC80; +extern MenuBox D_001A8A88; +extern MenuBox D_001A8AB8; +extern char D_001A8AA0[]; +extern char D_001A8AA8[]; +extern char D_001A8AD0[]; +extern char D_00263B50[]; +extern char *D_001A8A4C __attribute__((sda)); +extern char *D_001A8A50 __attribute__((sda)); +extern char *D_001A8A54 __attribute__((sda)); +extern char *D_001A8A58 __attribute__((sda)); +extern int D_001A8A70 __attribute__((sda)); +extern int D_001A8A74 __attribute__((sda)); +extern int D_001A8C88; +extern int D_001A8C8C; +extern int D_001F29A0[]; +extern int D_001A7420; +extern unsigned short D_001A7344; +extern int D_001B1598; +extern float D_001B23A4; + +extern void func_0027DDC0(int, int, int, int); +extern void func_0027C540(int); +extern void func_002FC560(unsigned int, unsigned long); +extern char *func_00288FB8(int); +extern char *func_00115AC0(unsigned char *, char *, int); +extern void func_00280190(MenuBox *, unsigned long, unsigned char *, int); +extern int func_001157AC(unsigned char *); +extern void func_0029B888(MenuBox *, int, void *, int, int, int, int, char *, char *, char *); +extern int func_0028E588(int, int); +extern void func_0028E868(int, int, int, int, int, int); +extern int func_0028E640(int); +extern void func_0028F410(float, float, float, float, float, int, int, int); +extern void *func_00115484(void *, int, int); +extern int func_00288D80(void); +extern int func_00287E90(void); +extern int func_00115DA8(char *, const char *, ...); +extern void func_0027EB88(int, int, int, int, int, int); +extern int func_00283D38(int, int, float); +extern void func_0027FAA8(int, int, unsigned long, char *, int); +extern int func_0027C878(int); +extern void func_0027FC70(MenuBox *, unsigned long, char *, int, int, void *); +extern void func_002802E0(MenuBox *, int, int, int, int, int, int, int, int); +extern void func_0027C660(void); + +#define SPR ((unsigned char *)0x70000000) + +void func_002859C8(void) +{ + char buf[0x300]; + MenuBox box; + MenuBox tmp; + MenuBox tmp2; + char buf2[0x100]; + + if (D_001BAC80.f7E == 0) + func_0027DDC0(0, 0, 0, 0x30); + if (D_001BAC80.f7C != 0) + return; + func_0027C540(0); + func_002FC560(0x42, 0x8000000044UL); + func_002FC560(0x47, 0x5360B); + switch (D_001BAC80.mode) { + case 2: { + unsigned char *spr; + unsigned char *p; + int h, len, i, x, y; + + box = D_001A8A88; + spr = SPR; + func_00115AC0(spr, func_00288FB8(0x9B4), 0x400); + p = spr; + while (*p >= 2) + p++; + while (*p == 1) { + *p = 0; + p++; + } + box.flags |= 4; + func_00280190(&box, 0x80000000UL, spr, -1); + box.flags ^= 4; + h = box.h; + len = func_001157AC(spr); + for (i = 4; i >= 0; i--) + spr[len + i] = 1; + spr[len + 5] = 0; + func_0029B888(&box, 0x80F0F0F0, spr, 1, 1, -1, 1, func_00288FB8(0x1137), 0, 0); + x = box.x + h; + func_002FC560(0x47, 0x3004B); + y = x + 8; + func_0028E868(func_0028E588(0x755D, 0), 0xE0, y, 0x40, 0x40, 0x80); + func_0028F410(4096.0f, (x + 40) * 16, 272.0f, 272.0f, + (D_001B1598 % 55) * -0.11423973f, 0x40, 0x40, + func_0028E640(func_0028E588(0x755D, 1))); + break; + } + case 4: + if (D_001BAC80.f5C > 0) { + unsigned char *p; + int id; + + func_00115AC0(SPR, func_00288FB8(0x2DE5), 0x400); + p = SPR; + while (*p >= 2) + p++; + while (*p == 1) { + *p = 0; + p++; + } + id = D_001BAC80.f44 ? 0 : 0x1137; + func_00115484(&tmp, 0, sizeof(tmp)); + tmp.f02 = D_001A7344; + tmp.f04 = 0x60; + tmp.f06 = 0x1A0; + tmp.f08 = 0x100; + tmp.f0A = 0x68; + tmp.f10 = 0x10; + tmp.flags = 1; + box = tmp; + func_0029B888(&box, 0x80F0F0F0, p, 1, 1, -1, 0, id ? func_00288FB8(id) : 0, 0, 0); + } + break; + case 5: + if (D_001BAC80.f5C > 0) { + char *text; + int id; + + text = func_00288FB8(D_001BAC80.f80 ? 0x2E04 : 0x2E03); + id = D_001BAC80.f44 ? 0 : 0x1137; + func_00115484(&tmp2, 0, sizeof(tmp2)); + tmp2.f02 = D_001A7344; + tmp2.f04 = 0x60; + tmp2.f06 = 0x1A0; + tmp2.f08 = 0x100; + tmp2.f0A = 0x68; + tmp2.f10 = 0x10; + tmp2.flags = 1; + box = tmp2; + func_0029B888(&box, 0x80F0F0F0, text, 1, 1, -1, 0, id ? func_00288FB8(id) : 0, 0, 0); + } + break; + case 1: { + int show, id1, id2; + char *text; + + show = 1; + if (D_001BAC80.f5C > 0) { + text = D_001A8AA0; + id1 = 0; + id2 = 0; + if (func_00288D80()) { + id1 = 0x1137; + text = func_00288FB8(0x2E04); + } else { + switch (D_001A7420) { + case 6: + id1 = 0x113C; + id2 = 0x1138; + text = func_00288FB8(0x2DF4); + break; + case 12: + if (D_001F29A0[0]) { + id1 = 0x2DE8; + text = func_00288FB8(0x2DE6); + break; + } + case 13: + id1 = 0x113C; + id2 = 0x1138; + text = func_00288FB8(0x2DF0); + break; + case 10: + case 11: + text = func_00288FB8(0x2E00); + break; + case 7: + case 8: + text = func_00288FB8(0x2DFE); + break; + case 14: + case 15: + text = func_00288FB8(0x2DFF); + break; + case 17: + id1 = 0x1137; + text = func_00288FB8(0x2E01); + break; + case 18: + id1 = 0x1137; + text = func_00288FB8(0x2E03); + break; + case 2: + text = func_00288FB8(0x2DE5); + id1 = D_001BAC80.f44 ? 0 : 0x1137; + break; + case 19: + if (D_001F29A0[0]) { + id1 = 0x2DE8; + text = func_00288FB8(0x2DE6); + } else if (D_001A8C88) { + id1 = 0x113B; + id2 = 0x1138; + func_00115DA8(buf, D_001A8AA8, func_00288FB8(0x2DE9), 1, 1, + func_00288FB8(0x2DEB)); + text = buf; + } else { + id1 = 0x113B; + func_00115DA8(buf, D_001A8AA8, func_00288FB8(0x2DE9), 1, 1, + func_00288FB8(0x2DEB)); + text = buf; + if (D_001A8C8C) + id1 = 0x113C; + } + break; + case 21: + text = func_00288FB8((!D_001A8C88 && func_00287E90()) ? 0x2E04 : 0x2E03); + id1 = 0x1137; + break; + case 20: + id1 = 0x1137; + text = func_00288FB8(0x2E02); + break; + case 24: + id1 = 0x113B; + id2 = 0x1138; + func_00115DA8(buf, D_001A8AA8, func_00288FB8(0x2DF2), 1, 1, + func_00288FB8(0x2DEB)); + text = buf; + break; + case 23: + id1 = 0x113B; + id2 = 0x1138; + text = func_00288FB8(0x2DF6); + break; + case 3: + case 4: + if (D_001A8C88 && !D_001F29A0[0]) { + id1 = 0x113B; + id2 = 0x1138; + text = func_00288FB8(0x2DEC); + } else if (D_001A8C8C) { + id1 = 0x113B; + text = func_00288FB8(0x2DEC); + } else if (D_001F29A0[0]) { + id1 = 0x2DE8; + text = func_00288FB8(0x2DE6); + } else { + id1 = 0x2DE8; + text = func_00288FB8(0x2DEE); + } + break; + case 5: + id1 = 0x2DE8; + text = func_00288FB8(0x2DE6); + break; + default: + show = 0; + break; + } + } + if (show) { + func_00115484(&tmp, 0, sizeof(tmp)); + tmp.f02 = D_001A7344; + tmp.f04 = 0x60; + tmp.f06 = 0x1A0; + tmp.f08 = 0x100; + tmp.f0A = 0x68; + tmp.f10 = 0x10; + tmp.flags = 1; + box = tmp; + func_0029B888(&box, 0x80F0F0F0, text, 1, 1, -1, 0, + id1 ? func_00288FB8(id1) : 0, id2 ? func_00288FB8(id2) : 0, 0); + } + } + break; + } + case 3: { + float t; + int c1, c2, id; + + t = 1.0f - D_001BAC80.f44 * 0.033333335f; + box = D_001A8AB8; + func_0027EB88(0x64, 0x12C, 0x60, 0x1A0, (int)(t * 80.0f), 0x40404); + c1 = func_00283D38(D_001A8A70, D_001A8A74, t); + c2 = func_00283D38(0x20FFFF, 0x8020FFFF, t); + switch (D_001BAC80.f58) { + case 0: + case 1: + func_0027FAA8(0xCA, 0x118, c2, func_00288FB8(0x113B), -1); + func_0027FAA8(0x135, 0x118, c2, func_00288FB8(0x1138), -1); + id = 0x2F28; + break; + case 2: + func_0027FAA8(0xCA, 0x118, c2, func_00288FB8(0x113B), -1); + func_0027FAA8(0x135, 0x118, c2, func_00288FB8(0x1138), -1); + id = 0x2F29; + break; + case 3: + func_0027FAA8(0x100, 0x118, c2, func_00288FB8(0x1137), -1); + id = 0x2F2A; + break; + default: + id = 0; + break; + } + if (id) + func_0027FC70(&box, c1, func_00288FB8(id), -1, func_0027C878(1), D_00263B50); + break; + } + case 9: { + char *a, *b, *c; + + func_002802E0(&box, 0xF0, 0x1E0, 0x60, 0x1A0, 0x100, + (int)(D_001B23A4 * 100.0f + 0.5f), 0x10, 3); + a = D_001A8A4C; + if (!a) + a = func_00288FB8(0x113B); + b = D_001A8A50; + if (!b) + b = func_00288FB8(0x1138); + c = D_001A8A54; + if (!c) + c = func_00288FB8(0x2DCD); + func_0029B888(&box, 0x80F0F0F0, c, 1, 1, -1, 0, a, b, D_001A8A58); + break; + } + case 10: { + char *a, *c; + + func_002802E0(&box, 0xF0, 0x1E0, 0x60, 0x1A0, 0x100, + (int)(D_001B23A4 * 100.0f + 0.5f), 0x10, 3); + a = D_001A8A4C; + if (!a) + a = func_00288FB8(0x113B); + c = D_001A8A54; + if (!c) + c = func_00288FB8(0x2DCD); + func_0029B888(&box, 0x80F0F0F0, c, 1, 1, -1, 0, a, 0, D_001A8A58); + break; + } + case 11: + func_002802E0(&box, 0xF0, 0x1E0, 0x60, 0x1A0, 0x100, + (int)(D_001B23A4 * 100.0f + 0.5f), 0x10, 3); + switch (D_001BAC80.f58) { + case 0: + break; + case 1: { + char *a, *b; + + a = func_00288FB8(0x1137); + b = func_00288FB8(0x2805); + func_00115DA8(buf2, func_00288FB8(0x31E6), 15); + func_0029B888(&box, 0x80F0F0F0, buf2, 1, 1, -1, 0, a, b, 0); + break; + } + case 2: + break; + case 3: + D_001BAC80.f60 = D_001BAC80.f5C / 60 + 1; + func_00115DA8(buf2, D_001A8AD0, func_00288FB8(0x31ED), func_00288FB8(0x31EE), + D_001BAC80.f60, func_00288FB8(0x31EF)); + func_0029B888(&box, 0x80F0F0F0, buf2, 1, 1, -1, 0, func_00288FB8(0x113B), + func_00288FB8(0x1138), 0); + break; + } + break; + } + func_0027C660(); +} diff --git a/nonmatching/functional/func_00286738.c b/nonmatching/functional/func_00286738.c new file mode 100644 index 000000000..5e923a530 --- /dev/null +++ b/nonmatching/functional/func_00286738.c @@ -0,0 +1,691 @@ +/* functional: medium. The near-miss reconstruction compiles to 4944 bytes against retail's 5280 but does the same reads, writes and calls in the same order (fcheck SAME on 6 of 6 captured calls); byte layout only differs. */ +/* Nonmatching modal-state reconstruction, 2026-10-23. + * Game profile: -O2 -G8 -ffunction-sections -frac2-qcopy + * -frac2-qcopy-barrier -frac2-zero-ti-por, dedicated 00286738-g8.c trial. + * US: 4944 versus retail 5280 bytes; 0/28 placements. Frame 0xB0 matches, + * but the first difference is +0x4 (saved-register allocation). US2 and PAL + * have no catalogue entry for this canonical function. + * Ten bounded compiles examined separate modal paths, case ordering, copy/zero + * profiles, camera object grouping, scoped modal views, signed flag types and + * verified callback declarations. Earlier drafts were 4804..5012 bytes; equal + * case-order/debug-policy outputs did not resolve the dispatch layout. + * Remaining work: register lifetimes, switch block order and branch merges; + * generated switch-table dependencies would also need real linked qualification. + * Reopen with a measured large-switch layout remedy or recovered owner layouts. + * Helper retail analysis establishes float return for func_002AA548 and integer + * return for func_0028B3D0/func_002E4AC8. Update/draw callback parameters remain + * unspecified here: this caller only passes their addresses to the registrar. + */ + +/* Modal state observed by the menu and camera transition handlers. */ +typedef int Quadword __attribute__((mode(TI))); +typedef struct { + float cameraPosition[4]; + float cameraAngles[4]; + float returnPosition[4]; + float returnAngles[4]; + int mode; + int countdown; + char reserved48[12]; + int selection; + int phase; + int elapsed; + int inputDelay; + int reserved64; + int pending; + float fraction; + float angle; + char reserved74[8]; + short timer; + short cameraActive; + int savedValue; + short result; + short reserved86; + int enabled; + unsigned int acceptMask; + unsigned int cancelMask; + int gameState; +} ModalState; + +typedef struct { + char reserved00[0x100]; + float roll; + char reserved104[0x44]; + float horizontal; + float vertical; + char reserved150[0x54]; + unsigned int buttons; + char reserved1A8[0x18]; + unsigned int held; + unsigned int pressed; +} ModalInput; + +typedef struct CameraObject CameraObject; +struct CameraObject { + char reserved00[0x64]; + void (*update)(CameraObject *); + char reserved68[0x42]; + short kind; + char reservedAC[0x54]; +}; + +extern ModalState D_001BAC80; +extern ModalInput D_00138180; +extern int D_001B170C; +extern int D_001A7420; +extern int D_001A7424; +extern int D_001A8C88; +extern int D_001A8C8C; +extern int D_001F29A0; +extern int D_001F2940; +extern int D_001F2858; +extern int *D_001F2930; +extern unsigned short D_0018C9DC; +extern int D_001B1F10; +extern int D_001393E8; +extern int D_001393F0; +extern int D_0013955C[]; +extern int D_001A8B84; +extern int D_001B1680; +extern int D_001A8A78 __attribute__((sda)); +extern float D_001A8A7C __attribute__((sda)); +typedef struct { + float position[4]; + float angles[4]; + char reserved20[0x210]; + float matrix[16]; +} ModalCamera; +extern ModalCamera D_001B5340; +extern float D_001B17D0[4]; +extern unsigned char D_001A7A44; +extern int D_001A79F0; +extern CameraObject *D_001B1B5C; +extern CameraObject *D_001B1B60; +extern unsigned char D_0018C0D4; +extern int D_001A8A40; +extern int D_001A8A44; +extern int D_001A8A48; +extern int D_001A8A60; +extern int D_001A7BB8; + +extern int func_002E4AC8(void); +extern int func_002B49E0(int, int, int, int, unsigned char *); +extern void func_0027D330(int); +extern int func_00288D80(void); +extern void func_00288D68(void); +extern void func_00287E80(void); +extern int func_002DF1D0(void); +extern int func_002C9A50(void); +extern void func_00298D28(int *); +extern void func_00287EA8(void); +extern int func_002B4B70(int, int); +extern void func_00298E08(void); +extern void func_002F64A8(void); +extern void func_002DE990(int); +extern void func_00282CF0(void *, const void *, const void *); +extern float func_00282DF0(void *); +extern float func_002AA548(float *, float *, float, float, float, float); +extern void func_00282F20(void *, const void *, float); +extern void func_00282CC0(void *, const void *, const void *); +extern void func_00287BD8(char *, int); +extern void func_00283098(void *, const void *, const void *); +extern void func_002AC9F8(float *, float); +extern void func_00282D30(void *, const void *, float); +extern void func_00287D98(char *); +extern void func_00287D20(float *); +extern void func_00283698(void *, const void *); +extern void func_002AB6D8(float *, float *); +extern void func_00282D08(void *, const void *, const void *, float); +extern void func_002A91F0(void *, float *); +extern void func_00283810(void *, const void *, const void *, float); +extern void func_00283958(const void *, void *); +extern void func_0026F7B0(void); +extern void func_00290670(void); +extern void func_00287E60(void); +extern int func_0028B3D0(int, int, void (*)(unsigned char *), void (*)(), void (*)(), int, int); +extern void func_0028BDB0(unsigned char *); +extern void func_0028BE00(); +extern void func_0028C6D0(); +extern void func_0028CC98(unsigned char *); +extern void func_0028CCE0(); +extern void func_0028D1E8(); +extern void func_0029CE38(void); +extern void func_0029B720(int); +extern void func_0029B698(int); +extern void func_00290728(void); +extern void func_00290720(void); +extern void func_002B49C0(int); +extern int func_002E5E58(int, int, void *); +extern void func_002857B8(void); + +/* Run the active modal dialog, camera return, or display-confirmation phase. */ +void func_00286738(void) +{ + float delta[4], target[4], transform[16]; + float distance; + + func_002E4AC8(); + D_001B170C = D_001BAC80.gameState; + if (D_001BAC80.countdown) + --D_001BAC80.countdown; + if (D_001BAC80.timer) + --D_001BAC80.timer; + switch (D_001BAC80.mode) { + case 2: + ++D_001BAC80.elapsed; + if (D_001BAC80.elapsed > 90 && D_001BAC80.inputDelay) + --D_001BAC80.inputDelay; + if (D_001A7420 == 2) { + func_002B49E0(4, 2, 1, 0, 0); + return; + } + if (D_001BAC80.elapsed >= 121 && (D_00138180.buttons & 0x40)) { + if (!D_0018C9DC) + D_0018C9DC = 1; + goto closeModal; + } + break; + case 5: + ++D_001BAC80.elapsed; + if (D_001BAC80.elapsed > 30 && D_001BAC80.inputDelay) + --D_001BAC80.inputDelay; + if (!D_001BAC80.inputDelay && (D_00138180.buttons & 0x40)) { + D_001A7424 &= ~0x40; + D_0013955C[0] = 0; + goto reload; + } + break; + case 4: + ++D_001BAC80.elapsed; + if (D_001BAC80.elapsed > 30 && D_001BAC80.inputDelay) + --D_001BAC80.inputDelay; + if (!D_001BAC80.inputDelay && (D_00138180.buttons & 0x40)) { + D_0013955C[0] = 0; + goto reload; + } + break; + case 3: + if (D_001BAC80.phase == 1) { + if (D_00138180.buttons & 0x10) + goto closeModal; + if (D_00138180.buttons & 0x40) { + func_0027D330(4); + D_001B1F10 = 0; + D_001BAC80.phase = 2; + D_001BAC80.elapsed = 600; + } + return; + } else if (D_001BAC80.phase < 2) { + if (D_001BAC80.phase == 0) { + if (!D_001BAC80.countdown) + D_001BAC80.phase = 1; + return; + } + } else if (D_001BAC80.phase == 2) { + if (!D_001BAC80.elapsed || --D_001BAC80.elapsed == 0) { + D_001B1F10 = 1; + D_001BAC80.phase = 3; + return; + } + if (D_00138180.buttons & 0x10) { + func_0027D330(4); + D_001B1F10 = 1; + } else { + if (!(D_00138180.buttons & 0x40)) + return; + D_001B1F10 = 0; + } + } else if (D_001BAC80.phase == 3 && !(D_00138180.buttons & 0x40)) { + return; + } + goto closeModal; + case 1: + ++D_001BAC80.elapsed; + if (D_001BAC80.elapsed > 30 && D_001BAC80.inputDelay) + --D_001BAC80.inputDelay; + if (func_00288D80()) { + if (!(D_00138180.buttons & 0x40)) + return; + func_00288D68(); + func_00287E80(); + goto closeModal; + } + switch (D_001A7420) { + case 1: + case 16: + func_002B4B70(0, 0); + D_001F2940 = D_001BAC80.selection; + D_001F2858 = D_001BAC80.selection; + break; + case 2: + if (!D_001BAC80.inputDelay && (D_00138180.buttons & 0x40)) { + D_001BAC80.result = 2; + D_001A7424 &= ~1; + goto closeModal; + } + return; + case 3: + case 5: + goto cancelSelection; + case 4: + if (!D_001A8C88) { + if (D_001A8C8C) + goto acceptReload; + } else if (!D_001F29A0) { + if (D_001BAC80.inputDelay) + return; + if (D_00138180.buttons & 0x40) { + func_00298E08(); + D_001BAC80.result = 2; + D_001A7424 &= ~6; + func_002F64A8(); + } + } + if (!D_001BAC80.inputDelay && (D_00138180.buttons & 0x10)) { + D_001BAC80.result = 1; + D_001A7424 = (D_001A7424 & ~6) | 0x20; + goto closeModal; + } + return; + case 6: + if (!D_001BAC80.inputDelay && (D_00138180.buttons & 0x20)) { + D_001A7424 |= 8; + D_001BAC80.result = 2; + return; + } + if (D_001BAC80.countdown || !(D_00138180.buttons & 0x10)) + return; + if (D_001A8C8C) { + D_001BAC80.result = 2; + goto reload; + } + D_001A7424 |= 0x20; + if (!func_002DF1D0()) + D_001A7424 &= ~2; + D_001A7424 &= ~4; + goto finishSelection; + case 9: + if (!(D_001A7424 & 6)) + goto closeModal; + return; + case 12: + if (D_001F29A0) + goto cancelSelection; + return; + case 13: + if (!D_001BAC80.inputDelay && (D_00138180.buttons & 0x20)) { + D_001A7424 |= 0x10; + D_001BAC80.result = 2; + return; + } + if (D_001BAC80.countdown || !(D_00138180.buttons & 0x10)) + return; + if (D_001A8C8C) { + D_001BAC80.result = 2; + goto reload; + } + D_001A7424 = (D_001A7424 & ~6) | 0x20; +finishSelection: + D_001BAC80.result = 1; + if (!D_001A8C88) + goto closeModal; + break; + case 17: + case 18: + case 21: + if (D_001BAC80.pending > 0) { + if (--D_001BAC80.pending <= 0) { + if (D_001393E8 != 2) { + D_001BAC80.result = 2; + func_00287EA8(); + goto reload; + } + D_001A7420 = 9; + if (D_001393F0) { + if (D_001393F0 == -1) { + D_001A7420 = 9; + D_001393F0 = 0; + } else { + D_001A7420 = 12; + if (D_001393F0 == -2) + D_001A7420 = 5; + } + } + goto closeModal; + } + } else { + if (D_001BAC80.inputDelay || !(D_00138180.buttons & 0x40)) + return; + func_00287EA8(); + if (!D_001A8C8C) { + D_001BAC80.result = 2; + D_001A7424 &= ~0x446; + goto closeModal; + } + D_001BAC80.pending = 15; + } + break; + case 19: + if (D_001A8C88 && !D_001F29A0) { + if (D_001BAC80.inputDelay) + return; + if (D_00138180.buttons & 0x40) + goto acceptLoad; + if (!(D_00138180.buttons & 0x10)) + return; + D_001BAC80.result = 1; + D_001A7424 &= ~6; + goto closeModal; + } + if (!D_001A8C8C) { + if (D_001BAC80.inputDelay) + return; + if (!(D_00138180.buttons & 0x40) || D_001F29A0) { + if (!(D_00138180.buttons & 0x10) || !D_001F29A0) + return; + } + D_001A7424 &= ~6; + D_001BAC80.result = 1; + goto closeModal; + } +acceptReload: + if (D_001BAC80.inputDelay || !(D_00138180.buttons & 0x40)) + return; + D_001BAC80.result = 2; + goto reload; + case 20: + if (D_001BAC80.inputDelay || !(D_00138180.buttons & 0x40)) + return; + D_001A7424 &= ~0x446; + D_001BAC80.result = 2; + if (!D_001A8C88) { + if (!func_002C9A50()) { + D_0013955C[0] = 0; + D_001A8B84 = 1; + D_001B1680 = -1; + return; + } + func_00298D28(D_001F2930); + } + goto closeModal; + case 23: + case 24: + if (D_001BAC80.inputDelay) + return; + if (D_00138180.buttons & 0x40) { +acceptLoad: + func_00298E08(); + D_001BAC80.result = 2; + D_001A7424 = (D_001A7424 & ~6) | 0x20; + func_002F64A8(); + return; + } + if (D_00138180.buttons & 0x10) { + D_001BAC80.result = 1; + D_001A7424 |= 0x20; + goto closeModal; + } + break; + } + break; + case 6: + switch (D_001BAC80.phase) { + case 0: + *(Quadword *)D_001BAC80.cameraPosition = *(Quadword *)D_001B5340.position; + *(Quadword *)D_001BAC80.cameraAngles = *(Quadword *)D_001B5340.angles; + D_001BAC80.phase = 1; + break; + case 1: + D_001B170C = 3; + func_00282CF0(delta, D_001B5340.position, D_001B17D0); + distance = func_00282DF0(delta); + func_002AA548(&distance, &D_001BAC80.fraction, (float)D_001A8A78, + 0.041666668f, 0.041666668f, 5.0000005f); + func_00282F20(delta, delta, distance); + func_00282CC0(D_001B5340.position, delta, D_001B17D0); + func_00287BD8((char *)&D_001BAC80.angle, 0); + if ((float)D_001A8A78 <= distance) + D_001BAC80.phase = 2; + break; + case 2: + D_001B170C = 3; + if (D_00138180.horizontal != 0.0f || D_00138180.vertical != 0.0f) { + *(Quadword *)target = 0; + target[1] = D_00138180.horizontal; + target[2] = D_00138180.vertical; + *(Quadword *)delta = *(Quadword *)target; + func_00283098(delta, delta, D_001B5340.matrix); + func_002AC9F8(delta, 1.0f); + func_00282D30(delta, delta, + (float)-D_001A8A78 * 0.00029088825f * D_001A8A7C); + func_00287D98((char *)delta); + } + func_00287BD8((char *)&D_001BAC80.angle, 1); + func_00287D20(&D_00138180.roll); + break; + case 3: { + float speed; + D_001B170C = 3; + speed = (float)D_001A8A78 * 0.00029088825f * D_001A8A7C; + func_00282CF0(target, D_001BAC80.cameraPosition, D_001B17D0); + func_00282F20(target, target, (float)D_001A8A78); + func_00282CC0(target, target, D_001B17D0); + func_00282CF0(delta, target, D_001B5340.position); + func_00283698(transform, D_001B5340.matrix); + func_00283098(delta, delta, transform); + delta[0] = 0.0f; + if (delta[1] == 0.0f && delta[2] == 0.0f) + delta[1] = 1.0f; + func_002AC9F8(delta, speed); + func_00283098(delta, delta, D_001B5340.matrix); + func_00287D98((char *)delta); + func_00287BD8((char *)&D_001BAC80.angle, 1); + if (func_00282DF0(delta) < speed - 0.01f) { + /* The transition publishes its separate state word before capturing the return pose. */ + { extern int D_001B1710; D_001B1710 = 2; } + *(Quadword *)D_001BAC80.returnPosition = *(Quadword *)D_001B5340.position; + if (D_001A7A44) { + func_00282D30(D_001B5340.matrix + 4, D_001B5340.matrix + 4, -1.0f); + func_002AB6D8(D_001B5340.matrix, D_001BAC80.returnAngles); + func_00282D30(D_001B5340.matrix + 4, D_001B5340.matrix + 4, -1.0f); + } else { + func_002AB6D8(D_001B5340.matrix, D_001BAC80.returnAngles); + } + D_001BAC80.phase = 4; + } + break; + } + case 4: + D_001B170C = 3; + func_002AA548(&D_001BAC80.fraction, &D_001BAC80.angle, + 1.0f, 0.00055555557f, 0.00055555557f, 0.025000002f); + func_00282D08(D_001B5340.position, D_001BAC80.returnPosition, + D_001BAC80.cameraPosition, D_001BAC80.fraction); + func_002A91F0(delta, D_001BAC80.returnAngles); + func_002A91F0(target, D_001BAC80.cameraAngles); + func_00283810(transform, delta, target, D_001BAC80.fraction); + func_00283958(transform, D_001B5340.matrix); + if (D_001A7A44) + func_00282D30(D_001B5340.matrix + 4, D_001B5340.matrix + 4, -1.0f); + if (D_001BAC80.fraction >= 1.0f) + func_002B4B70(0, 0); + break; + } + D_001BAC80.cameraActive = 1; + if ((D_00138180.buttons & 0x500) && D_001BAC80.phase == 2) { + D_001BAC80.angle = 0.0f; + D_001BAC80.phase = 3; + D_001BAC80.fraction = 0.0f; + } + if ((unsigned)(D_001A79F0 - 22) < 2 && D_001B1B5C < D_001B1B60) { + CameraObject *object = D_001B1B5C; + do { + if (object->kind == 0xD3D && object->update) + object->update(object); + ++object; + } while (object < D_001B1B60); + } + break; + case 7: + if (D_001BAC80.phase == 1) { + if (D_0018C0D4 == 1) { + func_0028B3D0(0x23, 0, func_0028CC98, func_0028CCE0, func_0028D1E8, 0, 0); + } else { + func_0026F7B0(); + func_0028B3D0(0x23, 0, func_0028BDB0, func_0028BE00, func_0028C6D0, 0, 0); + } + func_0029CE38(); + func_0029B720(360); + func_0029B698(180); + func_00290670(); + if (D_001A8A40 > 0) + --D_001A8A40; + --D_001A8A60; + if (D_001A8A60 < 0) + D_001A8A60 = 0; + if (D_001A8A60) + return; + } else { + if (D_001BAC80.phase < 2) { + if (D_001BAC80.phase == 0) { + D_001BAC80.phase = 1; + D_001BAC80.cameraActive = 1; + if (D_0018C0D4 != 1) + func_0026F7B0(); + D_001A8A48 = 0; + D_001A8A40 = D_001A8A44; + func_00290670(); + func_00287E60(); + } + return; + } + if (D_001BAC80.phase != 2) { + if (D_001BAC80.phase != 3) + return; + if (D_0018C0D4 != 1) + func_0026F7B0(); + func_00290728(); + func_00290720(); + goto closeModal; + } + if (D_0018C0D4 == 1) { + func_0028B3D0(0x23, 0, func_0028CC98, func_0028CCE0, func_0028D1E8, 0, 0); + } else { + func_0026F7B0(); + func_0028B3D0(0x23, 0, func_0028BDB0, func_0028BE00, func_0028C6D0, 0, 0); + } + func_0029CE38(); + func_0029B720(360); + func_0029B698(180); + func_00290670(); + if (D_001A8A40 > 0) + --D_001A8A40; + if (D_001A8A40 > 0) + return; + D_001A8A40 = 0; + } + ++D_001BAC80.phase; + break; + case 8: + if (D_001BAC80.phase >= 0 && D_001BAC80.phase <= 1) + ++D_001BAC80.phase; + else if (D_001BAC80.phase == 2) { + func_002B49E0(3, 2, D_001BAC80.selection, 0, 0); + func_002B49C0(3); + ++D_001BAC80.phase; + } + break; + case 9: + if (D_001BAC80.phase == 0) { + func_00287E80(); + ++D_001BAC80.phase; + } else if (D_001BAC80.phase == 1) { + if (D_00138180.pressed & D_001BAC80.acceptMask) { + D_001BAC80.result = 2; + } else { + if (!(D_00138180.held & D_001BAC80.cancelMask)) + return; + D_001BAC80.result = 1; + } + goto closeModal; + } + break; + case 10: + if (D_001BAC80.phase == 0) { + func_00287E80(); + ++D_001BAC80.phase; + } else if (D_001BAC80.phase == 1) { + if (!(D_00138180.pressed & D_001BAC80.acceptMask)) + return; + D_001BAC80.result = 2; + func_002E5E58(0x12, 0, 0); + goto closeModal; + } + break; + case 11: + switch (D_001BAC80.phase) { + case 0: + func_00287E80(); + ++D_001BAC80.phase; + if (D_001A7BB8) { + D_001A7BB8 = !D_001A7BB8; + func_002857B8(); + goto closeModal; + } + break; + case 1: + if (D_00138180.pressed & 0x40) { + D_001BAC80.result = 2; + ++D_001BAC80.phase; + } else if (D_00138180.held & 0x10) { + D_001BAC80.result = 1; + goto closeModal; + } + break; + case 2: + D_001BAC80.elapsed = 900; + ++D_001BAC80.phase; + D_001BAC80.result = 0; + D_001A7BB8 = !D_001A7BB8; + D_001BAC80.inputDelay = 0; + func_002857B8(); + return; + case 3: + if (D_00138180.pressed & 0x40) + D_001BAC80.result = 2; + else if (D_00138180.pressed & 0x10) + D_001BAC80.result = 1; + --D_001BAC80.elapsed; + if (D_001BAC80.elapsed > 0 && !D_001BAC80.result) + return; + ++D_001BAC80.phase; + break; + case 4: + if (D_001BAC80.result >= 0 && D_001BAC80.result < 2) { + D_001A7BB8 = !D_001A7BB8; + func_002857B8(); + } + ++D_001BAC80.phase; + break; + case 5: + func_002B4B70(0, 0); + break; + } + break; + } + return; + +cancelSelection: + if (!D_001BAC80.inputDelay && (D_00138180.buttons & 0x10)) { + D_001BAC80.result = 1; + D_001A7424 &= ~6; + goto closeModal; + } + return; +reload: + func_002DE990(-1); + func_002F64A8(); + return; +closeModal: + func_002B4B70(0, 0); +} diff --git a/nonmatching/functional/func_0028AB70.c b/nonmatching/functional/func_0028AB70.c new file mode 100644 index 000000000..df6cfee35 --- /dev/null +++ b/nonmatching/functional/func_0028AB70.c @@ -0,0 +1,62 @@ +/* functional: high. Same clear loop, lazy allocations and calls in retail order; retail keeps -1 in a saved register, which does not change behaviour. */ +#include "include_asm.h" +typedef struct { + int unk00; + int unk04; + unsigned char pad08[0x18]; + int unk20; + int unk24; + unsigned char pad28[0x3C]; + int key; + unsigned char pad68[4]; + int unk6C; + unsigned char pad70[0xC]; + int unk7C; + unsigned char pad80[0x10]; +} HudSlot; + +extern HudSlot D_00255330[13]; +extern int D_001B1870; +extern int D_001B1874; +extern char *D_001B18D8; +extern char *D_001B18E0; +extern int D_001B18E4; +extern char D_001A8E08[]; +extern char *D_001B18DC __attribute__((sda)); +extern char *func_0028B2B8(int, int, char *, int); +extern void func_0028B3D0(int, int, int, int, int, int, int); +extern char *func_00282A88(char *, int); +extern void func_0028BCE8(void); +extern void func_0028A278(void); + +/* Clears the thirteen HUD element slots and lazily creates the HUD moby table. */ +void func_0028AB70(void) +{ + int *q = (int *)((char *)D_00255330 + 0x24); + int i = 0; + + D_001B1870 = 0; + D_001B1874 = 0; + q[16] = -1; + do { + q[-1] = 0x10000; + func_0028B3D0(i, 0xffff, 0, 0, 0, 0, 1); + i++; + q[22] = 0; + q[18] = -6; + q[-8] = 0; + q[0] = 0; + q = (int *)((char *)q + 0x90); + } while (i < 13); + if (D_001B18D8 == 0) { + char *file = D_001A8E08; + D_001B18D8 = func_0028B2B8(0x2800, 0, file, 0x2AB); + D_001B18E4 = (int)func_0028B2B8(0x1400, 0, file, 0x2AC); + } + D_001B18E0 = D_001B18D8 + 0x2800; + D_001B18DC = D_001B18D8; + func_00282A88(D_001B18D8, 0x2800); + *((unsigned char *)D_001B18D8 + 0x20) = 0xFF; + func_0028BCE8(); + func_0028A278(); +} diff --git a/nonmatching/functional/func_0028B160.c b/nonmatching/functional/func_0028B160.c new file mode 100644 index 000000000..9cd6b67bd --- /dev/null +++ b/nonmatching/functional/func_0028B160.c @@ -0,0 +1,41 @@ +/* functional: medium. Starts from the near-miss draft; same reads, writes and calls in retail order; byte differences are register allocation only. */ +/* Best retained candidate: 280 bytes versus 292; 0/28 in us, pal and us2. */ +extern int *D_001B1888; +extern unsigned int *D_001B1894; +extern int D_001A7308; +extern void func_0028AE88(int); +extern void func_0028F948(int, int, int, int, int, int); + +void func_0028B160(int index, int unused, int param_2) +{ + int offset; + int first; + int last; + int i; + + if (D_001B1888[index + 0x74 / 4] == 0) + func_0028AE88(0); + + offset = D_001A7308; + first = 0; + if (index != 0) + first = D_001B1888[index + 0x34 / 4 - 1]; + last = D_001B1888[index + 0x34 / 4]; + + i = first; + while (i < last) { + int row_offset = i << 3; + unsigned char *row = + (unsigned char *)((unsigned int)D_001B1894 + row_offset); + unsigned char width = row[6]; + unsigned char height = row[7]; + int position = offset >> 8; + int mask = 1 << (width + height); + + i++; + func_0028F948(*(int *)row, position, 0x1B, + width, height, param_2); + offset += mask << 2; + *(short *)((unsigned int)D_001B1894 + row_offset + 4) = position; + } +} diff --git a/nonmatching/functional/func_0028B3D0.c b/nonmatching/functional/func_0028B3D0.c new file mode 100644 index 000000000..66aeb94e7 --- /dev/null +++ b/nonmatching/functional/func_0028B3D0.c @@ -0,0 +1,59 @@ +/* functional: high. Same compares, stores and calls in retail order; this cc1 allocates one extra register move, which does not change behaviour. */ +#include "include_asm.h" +typedef struct { + int unk00; + int flags; + char pad08[0x18]; + int a; + int key; + int f; + int e; + int b; + int c; + int d; + char pad3C[0x28]; + int stamp; + int active; + char pad6C[4]; + int unk70; + char pad74[8]; + int unk7C; + char pad80[0x10]; +} SlotRecord; + +typedef struct { unsigned char pad00[0x64]; int key; unsigned char pad68[0x28]; } SearchRow_28B5D0; +extern SearchRow_28B5D0 D_00255330[]; +extern int D_001A8BB0[] __attribute__((sda)); +extern int D_001B1870[] __attribute__((sda)); +extern void func_0028B4D8(char *); + +/* Starts (or keeps) the request id in its slot (the id's low four bits): a + changed parameter set restamps the slot from the frame counter and resets it. + Returns the slot's stamp, or 0 when state 5 locks every slot but 0 and 2. */ +int func_0028B3D0(int id, int a, int b, int c, int d, int e, int f) +{ + int slot = id & 0xF; + int key = id & 0xFFF0; + SlotRecord *r = (SlotRecord *)&D_00255330[slot]; + + if (D_001A8BB0[0] == 5 && slot != 2 && slot != 0) + return 0; + if (r->e != e || r->f != f || r->a != a || r->key != key || r->b != b || r->c != c + || r->d != d) { + r->stamp = D_001B1870[0]; + r->e = e; + r->f = f; + r->a = a; + r->b = b; + r->c = c; + r->d = d; + D_001B1870[0]++; + r->active = 1; + r->key = key; + r->unk7C = 0; + r->unk70 = 0; + if (key & r->flags & 0x20) + func_0028B4D8((char *)r); + } + return r->stamp; +} diff --git a/nonmatching/functional/func_0028E640.c b/nonmatching/functional/func_0028E640.c new file mode 100644 index 000000000..2210abb20 --- /dev/null +++ b/nonmatching/functional/func_0028E640.c @@ -0,0 +1,92 @@ +/* functional: medium. Near-miss C: same reads, writes and calls in retail order; only register allocation and scheduling differ. */ +typedef struct { + int word0; + unsigned short half4; + unsigned short half6; + int word8; + unsigned char byteC; + unsigned char byteD; + unsigned short halfE; +} TexSlot; + +typedef struct { + int word0; + unsigned short half4; +} TexKey; + +typedef struct { + int word0; + unsigned short half4; + unsigned char byte6; + unsigned char byte7; +} TexSize; + +extern short *D_001B1890; +extern char *D_001B1898; +extern char *D_001B1894; +extern TexSlot D_001B9340[]; +extern int D_001B15FC; +extern int D_001A72D0[] __attribute__((sda)); +extern int D_001A72DC[] __attribute__((sda)); + +unsigned long long func_0028E640(int index) +{ + short *row = (short *)((index << 2) + (unsigned int)D_001B1890); + TexKey *key = (TexKey *)((char *)D_001B1898 + (row[0] << 3)); + TexSize *size = (TexSize *)((char *)D_001B1894 + (row[1] << 3)); + int added = 0; + int shift; + int format; + unsigned long long value; + + if (key->half4 == 0 || size->half4 == 0) { + int count = D_001B15FC; + D_001B9340[count].word0 = key->word0; + D_001B9340[count].half4 = 0; + D_001B9340[count].half6 = 0x3FF0; + D_001B9340[count].word8 = key->word0; + D_001B9340[count].halfE = 0x3FF0; + D_001B9340[count].byteC = D_001B9340[count].byteD = 5; + } + if (key->half4 == 0) { + int base = D_001A72D0[0]; + int count; + key->half4 = base >> 8; + D_001A72D0[0] = base + 0x400; + count = D_001B15FC; + if (count < 0x40) { + D_001B9340[count].word0 = key->word0; + D_001B9340[count].half4 = 0; + D_001B9340[count].half6 = key->half4; + added = 1; + } + } + if (size->half4 == 0) { + int base = D_001A72D0[0]; + unsigned char big; + int count; + size->half4 = base >> 8; + big = size->byte7 < size->byte6 ? size->byte6 : size->byte7; + D_001A72D0[0] = base + (1 << (big * 2)); + count = D_001B15FC; + if (count < 0x40) { + D_001B9340[count].word8 = size->word0; + D_001B9340[count].byteC = size->byte6; + D_001B9340[count].byteD = size->byte7; + D_001B9340[count].halfE = size->half4; + added = 1; + } + } + if (added) + D_001B15FC++; + shift = size->byte6 - 6; + shift = shift >= 0 ? shift : 0; + value = (unsigned long long)size->half4 | ((unsigned long long)(1 << shift) << 14); + format = size->half4 < (D_001A72DC[0] >> 8) ? 0x1B : 0x13; + value |= (unsigned long long)format << 20; + value |= (unsigned long long)size->byte6 << 26; + value |= (unsigned long long)size->byte7 << 30; + value |= (unsigned long long)key->half4 << 37; + value |= 1ULL << 34; + return value | 0x8000000000000000ULL; +} diff --git a/nonmatching/functional/func_0028EA58.c b/nonmatching/functional/func_0028EA58.c new file mode 100644 index 000000000..b2968f9be --- /dev/null +++ b/nonmatching/functional/func_0028EA58.c @@ -0,0 +1,52 @@ +/* functional: high. Appends a 0x70-byte GIF packet (tag header, one A+D quad, then eight 64-bit GS words) at the cursor D_001B22A8 and advances it by 0x70. Sibling of func_0028E868 with six arguments; fifth/sixth are a delta and a pointer to four words. func_0028E640 gets a0 as in retail. */ +extern int D_001B22A8; +extern int D_001B1890; +extern int D_001B1894; +extern int D_001B187C; +extern int D_001A7350; +extern int D_001A7354; +extern long long D_001AC830[2]; +extern long long func_0028E640(int a0); + +void func_0028EA58(int a0, int a1, int a2, int a3, int a4, int *a5) +{ + short *row = (short *)((a0 << 2) + D_001B1890); + unsigned char *entry = (unsigned char *)(D_001B1894 + (row[1] << 3)); + int mask6 = 1 << entry[6]; + int mask7 = 1 << entry[7]; + int c0, s2; + long long Z; + int xs, ys, xe, ye; + + c0 = D_001B22A8; + *(int *)(c0 + 0x0) = 0x10000008; + *(int *)(c0 + 0x4) = 0; + *(int *)(c0 + 0x8) = 0; + *(int *)(c0 + 0xC) = 0x50000008; + D_001B22A8 = c0 + 0x10; + ((long long *)(c0 + 0x10))[0] = D_001AC830[0]; + ((long long *)(c0 + 0x10))[1] = D_001AC830[1]; + s2 = c0 + 0x20; + D_001B22A8 = s2; + *(long long *)(c0 + 0x20) = func_0028E640(a0); + + xs = a2 << 4; + ys = a1 << 4; + xe = (a1 + a3) << 4; + ye = (a2 + a4) << 4; + *(long long *)(s2 + 0x08) = 0x15C; + *(long long *)(s2 + 0x10) = a5[0]; + *(long long *)(s2 + 0x18) = 0; + Z = (long long)D_001B187C << 32; + *(long long *)(s2 + 0x20) = (long long)(ys + D_001A7350 - 8) | ((long long)(xs + D_001A7354 - 8) << 16) | Z; + *(long long *)(s2 + 0x28) = a5[1]; + *(long long *)(s2 + 0x30) = mask6 << 4; + *(long long *)(s2 + 0x38) = (long long)(xe + D_001A7350 - 8) | ((long long)(xs + D_001A7354 - 8) << 16) | (long long)D_001B187C << 32; + *(long long *)(s2 + 0x40) = a5[2]; + *(long long *)(s2 + 0x48) = mask7 << 20; + *(long long *)(s2 + 0x50) = (long long)(ys + D_001A7350 - 8) | ((long long)(ye + D_001A7354 - 8) << 16) | (long long)D_001B187C << 32; + *(long long *)(s2 + 0x58) = a5[3]; + *(long long *)(s2 + 0x60) = (long long)((mask7 << 20) + (mask6 << 4)); + *(long long *)(s2 + 0x68) = (long long)(xe + D_001A7350 - 8) | ((long long)(ye + D_001A7354 - 8) << 16) | (long long)D_001B187C << 32; + D_001B22A8 = D_001B22A8 + 0x70; +} diff --git a/nonmatching/functional/func_00290B48.c b/nonmatching/functional/func_00290B48.c new file mode 100644 index 000000000..f9c692d6b --- /dev/null +++ b/nonmatching/functional/func_00290B48.c @@ -0,0 +1,43 @@ +/* functional: medium. fcheck SAME on all captured calls; same stores of the same values as retail, only allocation and store order differ. The two program-end references are the constants 0x351FC0 and 0x2000000 read off the retail words (boot build). */ +extern void *func_00115484(void *, int, int); +extern char D_001BAEC0[]; +extern int D_001B22B0; + +void func_00290B48(void) +{ + int *state = (int *)D_001BAEC0; + unsigned int start, first, second, third; + unsigned int top, block1, block2, block3, block4, block5; + + func_00115484(state, 0, 0x9C); + top = 0x2000000u - 0x8000; + start = (0x351FC0u + 0x3FFF) & ~0x3FFF; + block1 = top - 0x40000; + block2 = block1 - 0x64000; + first = start + D_001B22B0; + block3 = block2 - 0x2C000; + second = first + D_001B22B0; + block4 = block3 - 0x8000; + third = second + 0x100000; + block5 = block4 - 0x14000; + state[0x98 / 4] = 0x07200000; + state[0] = 0; + state[0x10 / 4] = first; + state[0x74 / 4] = block4; + state[0x68 / 4] = third; + state[0x88 / 4] = 0x01FFC000; + state[0x6C / 4] = block5; + state[0x8C / 4] = 0x07000000; + state[0x90 / 4] = 0x07100000; + state[0x94 / 4] = 0x07180000; + state[0x04 / 4] = 0x100000; + state[0x08 / 4] = start; + state[0x0C / 4] = start; + state[0x14 / 4] = second; + state[0x18 / 4] = third; + state[0x84 / 4] = top; + state[0x80 / 4] = block1; + state[0x7C / 4] = block2; + state[0x78 / 4] = block3; + state[0x70 / 4] = block5; +} diff --git a/nonmatching/functional/func_00291850.c b/nonmatching/functional/func_00291850.c new file mode 100644 index 000000000..59fd5b02a --- /dev/null +++ b/nonmatching/functional/func_00291850.c @@ -0,0 +1,121 @@ +/* functional: medium. Written from the assembly (no draft existed); same calls with the same arguments in the same order, same stores to the output (3 doublewords) and to D_001A72D0. The scratch frame is addressed by byte offset (W) as retail does. Format switch (0x13, 0x14 upload the texture; others only build the MIPTBP words) follows the branches one for one. */ +typedef unsigned long long u64; + +extern int D_001A72D0; /* $gp - 0x7D20: GS texture memory cursor */ + +extern void func_00282A60(void *, unsigned int, int); +extern int func_00282A18(int); +extern int func_00126288(void *, short, short, short, short, short, short, short); +extern int func_001265B0(void *, void *); +extern int func_001244B8(int, int); +extern void func_0011AEA0(int); + +#define W(o) (*(int *)((char *)L + (o))) + +int func_00291850(char *obj, u64 *out) +{ + int L[0x18]; /* sp + 0 .. 0x5F */ + char gs[0x60]; /* sp + 0x60: the GsLoadImage */ + int fmt, i, n, base; + char *s0 = 0; + + func_00282A60(L, 0, 0x54); + fmt = *(int *)(obj + 0x10); + if (fmt == 2 || fmt == 0) { + W(0) = 0; + W(0x14) = 0; + } else if (fmt == 1) { + } else if (fmt >= 3 && fmt < 0x15) { + if (fmt >= 0x13) { + W(0) = (int)(obj + 0x20); + if (fmt == 0x14) + W(0x14) = *(int *)(obj + 0x14) ? 0x20 : 0x40; + else + W(0x14) = *(int *)(obj + 0x14) ? 0x200 : 0x400; + } + } + W(0x4C) = func_00282A18(*(int *)(obj + 8)); + W(0x50) = func_00282A18(*(int *)(obj + 0xC)); + W(4) = (int)(obj + (W(0x14) + 0x20)); + + fmt = *(int *)(obj + 0x10); + if (fmt == 2) { + W(0x18) = (*(int *)(obj + 8) * *(int *)(obj + 0xC)) << 1; + } else if (fmt < 3) { + if (fmt == 0) + W(0x18) = (*(int *)(obj + 8) * *(int *)(obj + 0xC)) << 2; + } else if (fmt == 0x13) { + W(0x18) = *(int *)(obj + 8) * *(int *)(obj + 0xC); + } else if (fmt == 0x14) { + W(0x18) = (*(int *)(obj + 8) * *(int *)(obj + 0xC)) >> 1; + } + + fmt = *(int *)(obj + 0x10); + if (fmt == 0x13 || fmt == 0x14) { + int cur = D_001A72D0; + + W(0x28) = cur >> 8; + s0 = gs; + if (fmt == 0x14) { + D_001A72D0 = cur + 0x100; + func_00126288(gs, (short)W(0x28), 1, *(short *)(obj + 0x14), 0, 0, 8, 2); + } else { + D_001A72D0 = cur + W(0x14); + func_00126288(gs, (short)W(0x28), 1, *(short *)(obj + 0x14), 0, 0, 0x10, 0x10); + } + func_0011AEA0(0); + func_001265B0(gs, (void *)W(0)); + func_001244B8(0, 0); + } + + n = *(int *)(obj + 0x1C); + if (1 < n) { + for (i = 0; i < n - 1; i++) { + int v = W(0x18 + 4 * i); + + W(8 + 4 * i) = W(4 + 4 * i) + v; + W(0x18 + 4 * i + 4) = v >> 2; + } + } + if (*(int *)(obj + 0x1C) > 0) { + for (i = 0; ; ) { + int v, sz; + + v = *(int *)(obj + 8) >> (i + 6); + W(0x3C + 4 * i) = v; + if (v <= 0) + W(0x3C + 4 * i) = 1; + W(0x2C + 4 * i) = D_001A72D0 >> 8; + func_00126288(gs, (short)W(0x2C + 4 * i), (short)W(0x3C + 4 * i), *(short *)(obj + 0x10), 0, 0, + (short)(*(int *)(obj + 8) >> i), (short)(*(int *)(obj + 0xC) >> i)); + func_0011AEA0(0); + func_001265B0(gs, (void *)W(4 + 4 * i)); + func_001244B8(0, 0); + base = D_001A72D0; + sz = W(0x18) >> (2 * i); + if (!(0xFF < sz)) + sz = 0x100; + i++; + D_001A72D0 = base + sz; + if (!(i < *(int *)(obj + 0x1C))) + break; + } + } + + out[0] = (u64)(long long)W(0x2C) + | ((u64)(long long)W(0x3C) << 14) + | ((u64)(long long)*(int *)(obj + 0x10) << 20) + | ((u64)(long long)W(0x4C) << 26) + | ((u64)(long long)W(0x50) << 30) + | ((u64)(long long)W(0x28) << 37 | (u64)0x8000 << 19) + | ((u64)(long long)*(int *)(obj + 0x14) << 51) + | ((u64)-1 << 63); + out[1] = ((u64)(long long)(*(int *)(obj + 0x1C) - 1) << 2) | 0xFFA0000000E0ULL; + out[2] = (u64)(long long)W(0x30) + | ((u64)(long long)W(0x40) << 14) + | ((u64)(long long)W(0x34) << 20) + | ((u64)(long long)W(0x44) << 34) + | ((u64)(long long)W(0x38) << 40) + | ((u64)(long long)W(0x48) << 54); + return -1; +} diff --git a/nonmatching/functional/func_00291CA8.c b/nonmatching/functional/func_00291CA8.c new file mode 100644 index 000000000..60f84e340 --- /dev/null +++ b/nonmatching/functional/func_00291CA8.c @@ -0,0 +1,71 @@ +/* functional: high. Same relocation loop, copy call and record loop as retail with the retail addresses; only the pointer-bump and register allocation differ from the bytes (fcheck SAME). */ +/* func_00291CA8 (312 B, 28 places), M31 26 Oct 2026: 316 of 312 bytes at default -G0 with + D_001B1DA4 as an sda incomplete array (tied lui/lw reads, $gp stores in the two slots). + The relocation loop and the call match. Left: the record loop. Retail keeps one pointer + and bumps it after each of the four loads (no giv combining) while still hoisting the + table base and base+4 into $s4/$s5; for/do-while combine the givs (s1/s2), a goto loop + keeps the bumps but loses the hoist (296 B). */ +#include "include_asm.h" +#include "common.h" + +typedef struct { + s32 count; + s32 pad04; + s32 dataOffset; + s32 dataSize; + s32 ptrs[1]; +} Reloc291CA8; + +typedef struct { + s32 a; + s32 b; +} Pair291CA8; + +extern s32 D_001B1DA4[] __attribute__((sda)); +extern Pair291CA8 D_001F1F40[]; +extern s32 D_001F2540[]; +extern u8 D_001F2740[]; + +extern int func_00282A18(int); +extern void func_00282AB0(void *, void *, int); + +void func_00291CA8(Reloc291CA8 *hdr, s32 base, s32 *p, s32 n) { + s32 *src; + s32 *dst; + s32 i; + s32 off; + s32 size; + s32 x; + s32 y; + + src = hdr->ptrs; + i = hdr->count; + off = hdr->dataOffset; + size = hdr->dataSize; + if (i > 0) { + dst = D_001F2540; + do { + if (*src == 0) { + *dst = (s32)D_001F2740; + } else { + *dst = *src - (off - (s32)D_001F2740); + } + dst++; + i--; + src++; + } while (i != 0); + } + func_00282AB0(D_001F2740, (u8 *)hdr + off, size); + for (D_001B1DA4[0] = 0; D_001B1DA4[0] < n; D_001B1DA4[0]++) { + x = *p; + p++; + y = *p; + p++; + D_001F1F40[D_001B1DA4[0]].a = ((base + x) << 4) + y; + x = *p; + p++; + y = *p; + p++; + D_001F1F40[D_001B1DA4[0]].b = ((base + x) << 4) + func_00282A18(y); + } +} diff --git a/nonmatching/functional/func_00291EC0.c b/nonmatching/functional/func_00291EC0.c new file mode 100644 index 000000000..4996f0606 --- /dev/null +++ b/nonmatching/functional/func_00291EC0.c @@ -0,0 +1,89 @@ +/* functional: high. Same instructions as retail; only the callee-saved registers $s3/$s4 are swapped, which does not change behaviour. */ +#include "include_asm.h" + +typedef struct Group_00291EC0 { + int count; + unsigned char pad_04[0x1C]; + int *rel; +} Group_00291EC0; + +typedef struct Entry_00291EC0 { + unsigned char pad_00[8]; + short s8; + short sA; + short sC; + short sE; +} Entry_00291EC0; + +typedef struct Header_00291EC0 { + unsigned char pad_00[4]; + short s4; + short count6; + unsigned char pad_08[4]; + short countC; + short padE; + Entry_00291EC0 *entries; + int w14; + int w18; + int w1C; + int tab[1]; +} Header_00291EC0; + +extern Header_00291EC0 *D_001B20C0[] __attribute__((sda)); + +extern short func_00282A18(int); + +void func_00291EC0(Header_00291EC0 *h) +{ + int *src; + int i; + int j; + int a; + int b; + int c; + int d; + int e0; + int e1; + int e2; + int e3; + int *g; + Header_00291EC0 *cur; + + e0 = (int)h->entries; + e1 = h->w14; + e2 = h->w18; + e3 = h->w1C; + D_001B20C0[0] = h; + h->s4 = 1; + h->entries = (Entry_00291EC0 *)(e0 + (int)h); + h->w14 = e1 + (int)h; + h->w18 = e2 + (int)h; + if (e3 != 0) + h->w1C = e3 + (int)h; + cur = D_001B20C0[0]; + src = (int *)cur->entries; + for (i = 0; i < cur->countC; i++) { + a = src[0]; + src++; + b = src[0]; + src++; + c = src[0]; + src++; + d = src[0]; + src++; + cur->entries[i].sA = a >> 4; + cur->entries[i].s8 = b >> 4; + D_001B20C0[0]->entries[i].sC = func_00282A18(c); + D_001B20C0[0]->entries[i].sE = func_00282A18(d); + cur = D_001B20C0[0]; + *(long long *)&cur->entries[i] = 0; + } + cur = D_001B20C0[0]; + for (i = 0; i < cur->count6; i++) { + g = (int *)((int *)((char *)cur + 0x20))[i]; + g = (int *)((int)g + (int)h); + ((int *)((char *)cur + 0x20))[i] = (int)g; + for (j = 0; j < *g; j++) + g[8 + j * 8] += (int)h; + } +} diff --git a/nonmatching/functional/func_00292888.c b/nonmatching/functional/func_00292888.c new file mode 100644 index 000000000..7fe3c8a8c --- /dev/null +++ b/nonmatching/functional/func_00292888.c @@ -0,0 +1,67 @@ +/* functional: high. Hand-decoded from the assembly (match not attempted to the end): same reads, writes and calls in the same order with the same arguments; pointers are 32-bit game addresses held as int. */ +extern int D_001A7288; +extern int D_001B18B0[5]; +extern int D_001B1888; +extern int D_001B188C; +extern int D_001B1890; +extern int D_001B1894; +extern int D_001B1898; +extern int D_001BAEC0[]; +extern int D_001A91F0[]; +extern int D_001A9200[]; +extern int D_001A9210[]; +extern int D_001A9220[]; +extern int D_001A9230[]; +extern int func_0028B2B8(int, int, void *, int); +extern void func_00282B50(int, int, int); +extern void func_00292B20(int, int); +extern int func_002EED58(int, int, int, void *); +extern void func_0028B160(int, int, int); +extern void func_0011AEA0(int); +extern void func_0028AE88(int, int); + +void func_00292888(void) +{ + int *hdr = (int *)D_001A7288; + int base = D_001A7288; + int i, size, mem, dst, n; + int *m; + + for (i = 0; i < 5; i++) + D_001B18B0[i] = (hdr[9 + i * 2] + 0x3F) & ~0x3F; + size = (hdr[7] + 0x3F) & ~0x3F; + mem = func_0028B2B8(size, 0, D_001A91F0, 0x2FF); + func_00282B50(mem, hdr[6] + base, size); + D_001B1888 = mem; + m = (int *)mem; + D_001B188C = mem + m[1]; + D_001B1890 = mem + m[2]; + D_001B1898 = mem + m[3]; + D_001B1894 = mem + m[4]; + dst = D_001BAEC0[4] + 0x60000; + if (m[0x54 / 4] != 0) { + n = (hdr[9] + 0x3F) & ~0x3F; + func_00292B20(0, dst); + n >>= 4; + ((int *)D_001B1888)[0x94 / 4] = func_002EED58(base + hdr[8], n, n, D_001A9200); + func_0028B160(0, dst, 1); + } + if (((int *)D_001B1888)[0x58 / 4] != 0) { + mem = func_0028B2B8(((int *)D_001B1888)[0x58 / 4], 0, D_001A91F0, 0x327); + func_00292B20(1, mem); + func_0011AEA0(0); + func_0028AE88(1, mem); + } + if (((int *)D_001B1888)[0x5C / 4] != 0) { + n = ((hdr[13] + 0x3F) & ~0x3F) >> 4; + ((int *)D_001B1888)[0x9C / 4] = func_002EED58(base + hdr[12], n, n, D_001A9210); + } + if (((int *)D_001B1888)[0x60 / 4] != 0) { + n = ((hdr[15] + 0x3F) & ~0x3F) >> 4; + ((int *)D_001B1888)[0xA0 / 4] = func_002EED58(base + hdr[14], n, n, D_001A9220); + } + if (((int *)D_001B1888)[0x64 / 4] != 0) { + n = ((hdr[17] + 0x3F) & ~0x3F) >> 4; + ((int *)D_001B1888)[0xA4 / 4] = func_002EED58(base + hdr[16], n, n, D_001A9230); + } +} diff --git a/nonmatching/functional/func_00292B88.c b/nonmatching/functional/func_00292B88.c new file mode 100644 index 000000000..00c6271ab --- /dev/null +++ b/nonmatching/functional/func_00292B88.c @@ -0,0 +1,84 @@ +/* functional: high. Near-miss draft that differs from retail in register allocation and scheduling only; fcheck SAME on all captured calls. */ +/* + * Near miss (M19, 2026-10-26): 804/804 bytes, about 23 differing lines. + * Profile: -O2 -G0 -ffunction-sections. us: 0/28 placements. + * D_001A72F4 and D_001A7304 are MACRO_ADDR (retail's lui/lw share one register). + * Residual: $s3/$s4 swapped between filter and half_width (no statement order moves it), + * and the GS words of the storage==0 arm scheduled differently. Queued on the permuter. + */ +typedef struct { + int pixels; + short width, height, storage, base, first, second; +} Texture_292B88; +extern int func_00282A18(int value); +#define MACRO_ADDR __attribute__((section(".sdata"))) +extern int D_001A72F4 MACRO_ADDR; +extern int D_001A7304 MACRO_ADDR; +extern long D_001B19B0[]; +extern long D_001B19C8[]; + +void func_00292B88(long *out, Texture_292B88 *texture, + int k, int filter, int address, int clamp, int mode) +{ + int width = texture->width >> 6; + int half_width = texture->width >> 7; + int log_width, log_height; + int base, first, second, storage; + if (width <= 0) width = 1; + if (half_width <= 0) half_width = 1; + log_width = func_00282A18(texture->width); + log_height = func_00282A18(texture->height); + base = texture->base + (D_001A72F4 >> 8); + first = texture->first + (D_001A72F4 >> 8); + second = texture->second + (D_001A72F4 >> 8); + storage = texture->storage; + if (mode >= 0) { + if (!storage) { + int pixels = ((texture->pixels << 2) + D_001A7304) >> 8; + long format = ((long)log_width << 26) | 0x01b00000; + long tex = pixels | ((long)width << 14); + long flags = ((long)k << 32) | 0x20; + long sampler = address | ((long)clamp << 2); + tex |= format; + sampler |= (long)mode << 24; + flags |= (long)filter << 6; + tex |= (long)log_height << 30; + tex |= ((long)base << 37) | 0x400000000L; + tex |= 0x8000000000000000UL; + *out = flags; out += 2; + *out = sampler; out += 2; + *out = tex; + out[2] = 0x40000400004000L; + } else { + long flags = ((long)filter << 6) | 0x20; + long tex, other, sampler; + flags |= (long)(storage - 1) << 2; + flags |= (long)k << 32; + tex = ((long)width << 14) | (((long)log_width << 26) | 0x01300000); + sampler = address | ((long)clamp << 2); + sampler |= (long)mode << 24; + other = ((long)half_width << 14) | ((long)first << 20); + other |= ((long)second << 40) | 0x400000000L; + tex |= (long)log_height << 30; + tex |= ((long)base << 37) | 0x400000000L; + tex |= 0x8000000000000000UL; + other |= 0x40000000000000L; + *out = flags; out += 2; + *out = sampler; out += 2; + *out = tex; + out[2] = other; + } + } else if (mode < -1) { + long *fallback = D_001B19B0; + if (mode == -3) fallback = D_001B19C8; + out[0] = ((long)filter << 6) | (((long)k << 32) | 0x20); + out += 2; *out = 5; out += 2; + *out = fallback[0]; + out[2] = fallback[2]; + } else { + out[0] = ((long)filter << 6) | (((long)k << 32) | 0x20); + out += 2; *out = 5; out += 2; + *out = 0x80000004CC007FFBL; + out[2] = 0; + } +} diff --git a/nonmatching/functional/func_00293000.c b/nonmatching/functional/func_00293000.c new file mode 100644 index 000000000..b30255655 --- /dev/null +++ b/nonmatching/functional/func_00293000.c @@ -0,0 +1,56 @@ +/* functional: high. Hand-written from the assembly: uploads the entries of a texture table through the GS image loader, twice (first the n entries of mixed type, then m entries of type 0x1B) and tracks the allocation pointers D_001A72D0/D_001A72D4; byte layout of the registers differs, behaviour is the same. */ +typedef struct { long q[12]; } GsLoadImage293000 __attribute__((aligned(16))); +extern int D_001A72F4; +extern int D_001A72D4 __attribute__((sda)); +extern int D_001A72D0 __attribute__((sda)); +extern int D_001A7304; +extern int func_00126288(GsLoadImage293000 *, short, short, short, short, short, short, short); +extern void func_0011AEA0(int); +extern int func_001265B0(GsLoadImage293000 *, void *); +extern int func_001244B8(int, int); + +void func_00293000(int base, int n, int m, int *tbl) +{ + GsLoadImage293000 li; + int v = D_001A72F4; + int w, h, type, area, alloc, dst; + + D_001A72D4 = v; + D_001A72D0 = v; + while (n > 0) { + void *src = (void *)(base + tbl[3]); + type = tbl[0]; + w = tbl[1] & 0xFFFF; + h = tbl[1] >> 16; + if (type == 0x13) { + func_00126288(&li, D_001A72D0 >> 8, (w >> 6) != 0 ? (w >> 6) : 1, 0x13, 0, 0, w, h); + area = w * h; + D_001A72D0 = D_001A72D0 + (area > 0xFF ? area : 0x100); + } else if (type == 2) { + func_00126288(&li, D_001A72D0 >> 8, 1, 2, 0, 0, 0x10, 0x10); + D_001A72D0 = D_001A72D0 + 0x200; + } else if (type == 0) { + func_00126288(&li, D_001A72D0 >> 8, 1, 0, 0, 0, 0x10, 0x10); + D_001A72D0 = D_001A72D0 + 0x400; + } + func_0011AEA0(0); + n--; + tbl += 4; + func_001265B0(&li, src); + func_001244B8(0, 0); + } + alloc = D_001A7304; + D_001A72D4 = D_001A72D0; + while (m > 0) { + void *src2 = (void *)(base + tbl[2]); + w = tbl[1] & 0xFFFF; + h = tbl[1] >> 16; + func_00126288(&li, alloc >> 8, (w >> 6) != 0 ? (w >> 6) : 1, 0x1B, 0, 0, w, h); + m--; + tbl += 4; + func_0011AEA0(0); + alloc += (w * h) << 2; + func_001265B0(&li, src2); + func_001244B8(0, 0); + } +} diff --git a/nonmatching/functional/func_002933D0.c b/nonmatching/functional/func_002933D0.c new file mode 100644 index 000000000..607d8f40f --- /dev/null +++ b/nonmatching/functional/func_002933D0.c @@ -0,0 +1,48 @@ +/* functional: high. Passes map and count on to the callee too, which retail leaves in $a2/$a3. Same reads, writes and calls as retail; the limit is read as the word at 0x1BAEC8. */ +#include "include_asm.h" + + +extern int D_001BAEC8; + +/* Turns the offsets of a loaded block into addresses: the first table's + entries below the limit, the second table's offsets, and the byte lists the + second table's records name are translated through map. */ +void func_002933D0(u8 *hdr, int arg1, u8 *map) +{ + int count = hdr[0] + hdr[1] + hdr[2]; + int *entry = (int *)(hdr + *(int *)(hdr + 4)); + int *first = entry; + u8 *recs = hdr + *(int *)(hdr + 8); + u8 *rec; + u8 *p; + int i; + + if (count != 0) { + i = count; + do { + int off = entry[0]; + + if (off < D_001BAEC8) { + entry[0] = off + (int)hdr; + entry[2] += (int)hdr; + } + i--; + entry += 4; + } while (i != 0); + } + rec = recs; + for (;;) { + *(int *)(rec + 0xC) += (int)hdr; + p = rec; + if (*p != 0xFF) { + do { + *p = map[*p]; + p++; + } while (*p != 0xFF); + } + if (*(int *)(rec + 0xC) < 0) + break; + rec += 0x10; + } + func_002932B8(first, arg1, (int)map, count); +} diff --git a/nonmatching/functional/func_002934F8.c b/nonmatching/functional/func_002934F8.c new file mode 100644 index 000000000..51bc4e994 --- /dev/null +++ b/nonmatching/functional/func_002934F8.c @@ -0,0 +1,118 @@ +/* functional: medium. The near-miss draft; same reads, writes, calls and result as retail (fcheck SAME on 2 of 2 captured calls); only register allocation or instruction order differs. */ +/* Retained manual reconstruction, 2026-10-24. + * Profile: -O2 -G0 -ffunction-sections. + * Final measurements: us: 0/28 placements, exit1, 912 compiled bytes; us2: + * 0/28 placements, exit1, 912 compiled bytes; pal: 0/28 placements, exit1, + * 912 compiled bytes. + * Remaining: Complete compressed-header compaction and all nested + * relocations are retained. Separate loop and pointer lifetimes improved + * the final source to 912/916 bytes. Operand order, loop header scheduling, + * parts-array address hoisting and the final vector-copy allocation still + * differ. qcopy/barrier and no-gcse were measured without a successful + * match. + * Reopen with: Independent original-header/part-table layout or compiler + * live-range evidence that fixes the measured pointer lifetimes and + * loop/address scheduling. + * No byte-match or linked-build credit is claimed. + */ +/* Relocate a packed asset block and optionally compact its packet headers. */ +typedef unsigned char U8; +typedef unsigned int Q128 __attribute__((mode(TI))); +extern void func_0026FEB8(const char *, ...); +extern void func_00282B50(void *destination, void *source, int bytes); +extern void func_002932B8(void *entries, void *textures, U8 *map, int count); +extern const char D_001B9240[]; +extern U8 D_001CE4E0[]; +extern int D_001D0E00[]; +extern Q128 D_001D1580[]; + +void func_002934F8(U8 *block, void *textures, U8 *map, int index) +{ + int compressed = block[0xB] != 0; + int count; + if (compressed) { + int size; + func_0026FEB8(D_001B9240, index); + size = *(int *)(block + 0x2C); + if (size > 0x3FC00) size = 0x3FC00; + block[0xB] = 0; + block[0x2C] = 0; + *(short *)(block + 0x2E) = 0; + block[0x2D] = size >> 10; + D_001D0E00[D_001CE4E0[index]] = size; + } + count = block[4] + block[5] + block[6]; + if (block[0x2C]) count += block[block[0x2C] * 16 + 1]; + if (*(int *)block) { + int packet_index; + U8 *entry; + entry = block + *(int *)block; + *(U8 **)block = entry; + for (packet_index = 0; packet_index < count; ++packet_index, entry += 16) { + U8 *data = block + *(int *)(entry + 8); + int first_two, all_three; + *(U8 **)(entry + 8) = data; + *(int *)entry += (int)block; + first_two = block[4] + block[5]; + all_three = first_two + block[6]; + if (compressed && (packet_index < first_two || packet_index >= all_three)) { + int j; + U8 *destination = data; + U8 *source = data; + for (j = 7; j >= 0; --j) { + *(unsigned short *)destination = *(unsigned short *)source; + destination += 2; + source += 4; + } + *(unsigned short *)(data + 0xE) = 0; + *(unsigned short *)(data + 0xC) = *(unsigned short *)(data + 0x18) - 16; + func_00282B50(data + 16, data + 32, (entry[0xC] - 2) * 16); + } + } + } + if (*(int *)(block + 0x10)) *(int *)(block + 0x10) += (int)block; + if (*(int *)(block + 0x14)) *(int *)(block + 0x14) += (int)block; + if (*(int *)(block + 0x18)) *(int *)(block + 0x18) += (int)block; + if (*(int *)(block + 0x1C)) { + int n, relocation_index; + *(int *)(block + 0x1C) += (int)block; + n = **(int **)(block + 0x1C); + for (relocation_index = 0; relocation_index < n; ++relocation_index) + (*(int **)(block + 0x1C))[relocation_index + 1] += (int)block; + } + if (*(int *)(block + 0x20)) { + U8 *entry; + *(int *)(block + 0x20) += (int)block; + entry = *(U8 **)(block + 0x20); + do { + U8 *cursor = entry; + *(int *)(entry + 0xC) += (int)block; + if (*cursor != 255) { + do { + *cursor = map[*cursor]; + ++cursor; + } while (*cursor != 255); + } + if (*(int *)(entry + 0xC) < 0) break; + entry += 16; + } while (1); + } + if (*(int *)(block + 0x28)) *(int *)(block + 0x28) += (int)block; + { + int part_index; + int *parts = (int *)(block + 0x48); + for (part_index = 0; part_index < block[0xC]; ++part_index) { + U8 *part; + int j; + if (!parts[part_index]) continue; + part = block + parts[part_index]; + ((U8 **)parts)[part_index] = part; + if (*(int *)(part + 0x14)) *(int *)(part + 0x14) += (int)part; + for (j = 0; j < part[0x10]; ++j) + ((int *)(part + 0x1C))[j] += (int)block; + } + } + if (index >= 0) D_001D1580[D_001CE4E0[index]] = *(Q128 *)map; + if (*(void **)block) + func_002932B8(*(void **)block, textures, map, count); +} diff --git a/nonmatching/functional/func_00294F10.c b/nonmatching/functional/func_00294F10.c new file mode 100644 index 000000000..9f66eb505 --- /dev/null +++ b/nonmatching/functional/func_00294F10.c @@ -0,0 +1,127 @@ +/* functional: medium (the only captured call may not reach the later half). Complete reconstruction from the near-miss draft; calls, arguments and stores follow retail, only table addressing and scheduling differ. */ + +typedef unsigned char u8; +extern int D_001C4FA0[]; +extern char D_0014B540_1[]; +extern int D_001A79F0_3 __attribute__((sda)); +extern unsigned int func_00294EC8(int); +typedef struct { float x0, y0, x1, y1, u0, v0, u1, v1; } UiMapEndpoints; +typedef struct { float x_bias, x_scale, y_bias, y_scale; } UiMapCoefficients; +extern int D_00256028[][2]; +extern UiMapEndpoints D_00255C30[]; +extern UiMapCoefficients D_00255ED0[]; +extern UiMapEndpoints D_001A93D0[]; +extern UiMapCoefficients D_001A9428[]; +extern unsigned char D_001A7AFD; +extern char *D_001BAED0; +extern char D_001A95A0[], D_001A95B8[]; +extern unsigned char D_0018D278[]; +extern void func_00295C50(int); +extern int func_002B7C80(char *, int, int); +extern int func_002EED58(int, int, int, int); +extern int func_0029D4D0(char *, char *); +/* Retail retains the allocator's source-file and line slots, although its + release implementation consumes only the byte count. */ +extern char *func_0028B2B8(int, int, const char *, int); +extern void func_00282B50(void *, void *, int); +extern void func_00297730(char *, unsigned char *, int); +extern void func_00297400(u8 *, u8 *, short *); + +void func_00294F10(void) +{ + int i, j; + int *state = D_001C4FA0; + char *scratch, *packed; + int bytes, size; + state[4] = 1; + for (i = 20; i >= 0; i--) { + int index = 20 - i; + state[0x108 / 4 + index] = D_00256028[index][0]; + state[0x15C / 4 + index] = D_00256028[index][1]; + ((float *)state)[0xB4 / 4 + index] = 0.65f; + } + state[0x238 / 4] = -1; + func_00295C50(D_001A79F0_3); + for (i = 20; i >= 0; i--) { + int index = 20 - i; + UiMapEndpoints *in = &D_00255C30[index]; + UiMapCoefficients *out = &D_00255ED0[index]; + float sx = (in->u0 - in->u1) / (in->x0 - in->x1); + float sy = (in->v0 - in->v1) / (in->y0 - in->y1); + out->x_scale = sx; + out->y_scale = sy; + out->x_bias = in->u0 - sx * in->x0; + out->y_bias = in->v0 - sy * in->y0; + } + for (i = 1; i >= 0; i--) { + int index = 1 - i; + UiMapEndpoints *in = &D_001A93D0[index]; + UiMapCoefficients *out = &D_001A9428[index]; + float sx = (in->u0 - in->u1) / (in->x0 - in->x1); + float sy = (in->v0 - in->v1) / (in->y0 - in->y1); + out->x_scale = sx; + out->x_bias = in->u0 - sx * in->x0; + out->y_scale = sy; + out->y_bias = in->v0 - sy * in->y0; + } + if (((int *)D_0014B540_1)[(0x14F4 / 4) + state[0x230 / 4] * 2] == 0 || D_001A7AFD) { + if (((int *)D_0014B540_1)[(0x15D4 / 4) + state[0x230 / 4] * 2] == 0 || !D_001A7AFD) { + state[0] = 0; + state[0x28 / 4] = 0; + state[2] = 0; + state[3] = 0; + state[1] = 0; + return; + } + } + state[0x28 / 4] = 1; + if (D_001A7AFD) + bytes = ((int *)D_0014B540_1)[0x15D4 / 4 + state[0x230 / 4] * 2]; + else + bytes = ((int *)D_0014B540_1)[0x14F4 / 4 + state[0x230 / 4] * 2]; + bytes <<= 7; + scratch = D_001BAED0; + packed = scratch + 0x9A800; + if (D_001A7AFD) { + func_002B7C80(packed, + ((int *)D_0014B540_1)[0x15D0 / 4 + state[0x230 / 4] * 2] + ((int *)D_0014B540_1)[0x36C / 4], + ((int *)D_0014B540_1)[0x15D4 / 4 + state[0x230 / 4] * 2]); + state[0x23C / 4] = 1; + } else { + func_002B7C80(packed, + ((int *)D_0014B540_1)[0x14F0 / 4 + state[0x230 / 4] * 2] + ((int *)D_0014B540_1)[0x36C / 4], + ((int *)D_0014B540_1)[0x14F4 / 4 + state[0x230 / 4] * 2]); + state[0x23C / 4] = 0; + } + state[0x238 / 4] = func_002EED58((int)packed, bytes, 0x3C00, (int)D_001A95A0); + state[0x240 / 4] = bytes; + func_0029D4D0(packed, scratch); + size = ((int *)scratch)[1] - ((int *)scratch)[0]; + state[0] = (int)func_0028B2B8(size, 0, D_001A95B8, 0x2EB); + func_00282B50((void *)state[0], scratch + ((int *)scratch)[0], size); + state[0] += 8; + state[1] = state[0]; + size = ((int *)scratch)[2] - ((int *)scratch)[1]; + state[5] = (int)func_0028B2B8(size, 0, D_001A95B8, 0x2FC); + func_00282B50((void *)state[5], scratch + ((int *)scratch)[1], size); + state[2] = (int)func_0028B2B8(0x1000, 0, D_001A95B8, 0x303); + for (i = 0; i < 8; i++) { + state[0x30 / 4 + i * 4] = -1; + state[0x34 / 4 + i * 4] = -1; + state[0x38 / 4 + i * 4] = state[2] + (i << 9); + } + state[3] = (int)func_0028B2B8(0x8000, 0, D_001A95B8, 0x30C); + if (func_00294EC8(state[0x230 / 4])) + func_00297730((char *)state[3], D_0018D278 + (state[0x230 / 4] << 11), state[5]); + else + func_00297400((u8 *)state[3], (u8 *)state[0], (short *)state[1]); + for (i = 0; i < 32; i++) { + float dy = 15.5f - (float)i; + float dy2 = dy * dy; + for (j = 0; j < 32; j++) { + float dx = 15.5f - (float)j; + if (dy2 + dx * dx > 256.0f) + ((u8 *)state)[0x1B0 + i * 4 + j / 8] |= 1 << (j % 8); + } + } +} diff --git a/nonmatching/functional/func_00295870.c b/nonmatching/functional/func_00295870.c new file mode 100644 index 000000000..cd906700d --- /dev/null +++ b/nonmatching/functional/func_00295870.c @@ -0,0 +1,7 @@ +/* functional: high. Table at 0x14B540 with 8-byte entries; reads the int at +0x14F4 or +0x15D4 depending on bit 8 of arg. Retail's byte layout differs only in codegen. */ +extern char D_0014B540_1[]; +int func_00295870(int arg) { + int idx = arg & 0xFF; + int off = (arg & 0x100) ? 0x14F4 : 0x15D4; + return *(int *)(D_0014B540_1 + idx * 8 + off) > 0; +} diff --git a/nonmatching/functional/func_0029EFD0.c b/nonmatching/functional/func_0029EFD0.c new file mode 100644 index 000000000..1689d16c5 --- /dev/null +++ b/nonmatching/functional/func_0029EFD0.c @@ -0,0 +1,40 @@ +/* functional: medium. Walks the 0x100-byte slot table from D_001B1B60 up to D_001B1B64 and claims the first free slot (state >= 0xFE and its stamp not newer than D_001B1688); the claimed slot is initialised through func_0029F0C8, gets its 0x80-byte record cleared and the free counter lowered. Field names are unknown; control flow, stores and calls follow retail. */ +typedef unsigned char u8; + +extern char *D_001B1B60; +extern char *D_001B1B64; +extern char *D_001B1B6C; +extern int D_001B1A80; +extern int D_001B1688; +extern char D_001A9DC0[]; + +extern void func_0029F0C8(char *slot, int arg); +extern void func_00282A60(char *dst, int val, int size); +extern void func_0026FEB8(char *msg, int arg); + +char *func_0029EFD0(int a0) +{ + char *s = D_001B1B60; + char *end = D_001B1B64; + + for (; (unsigned int)s < (unsigned int)end; s += 0x100) { + u8 state = *(u8 *)(s + 0x20); + if (state < 0xFE) + continue; + if ((unsigned int)D_001B1688 < *(unsigned int *)(s + 0xA0)) + continue; + if (state == 0xFF) + *(u8 *)(s + 0x120) = state; + func_0029F0C8(s, a0); + { + char *rec = D_001B1B6C + (((s - D_001B1B60) >> 8) << 7); + *(char **)(s + 0x68) = rec; + func_00282A60(rec, 0, 0x80); + } + if (D_001B1A80 != 0) + D_001B1A80 = D_001B1A80 - 1; + return s; + } + func_0026FEB8(D_001A9DC0, D_001B1688); + return 0; +} diff --git a/nonmatching/functional/func_0029F0C8.c b/nonmatching/functional/func_0029F0C8.c new file mode 100644 index 000000000..647ae29fe --- /dev/null +++ b/nonmatching/functional/func_0029F0C8.c @@ -0,0 +1,95 @@ +/* functional: high. Straight transcription of retail's stores, table lookups and call; only instruction order and temporaries differ. */ +extern void func_00282A60(void *, int, int); +extern void func_0029F390(void *); + +extern unsigned char D_001CE4E0[]; +extern short D_001CE300[]; +extern int D_001D04E0[]; +extern int D_001CDF40[]; +extern int D_001CDB80[]; +extern int D_001B1B5C; + +void func_0029F0C8(unsigned char *obj, int id) +{ + unsigned char *def; + int k; + int p; + unsigned h; + + func_00282A60(obj, 0, 0x100); + k = D_001CE4E0[id]; + obj[0x23] = 0x80; + obj[0x22] = k; + obj[0xA8] = 0xFF; + obj[0x21] = 0xFF; + obj[0x61] = 0xFF; + obj[0x62] = 0xFF; + *(long *)(obj + 0x38) = 0x40404000000000L; + *(short *)(obj + 0x36) = 0x7F80; + obj[0x6D] = 0xFF; + obj[0xA5] = 0x7F; + *(int *)(obj + 0xAC) = (((int)obj - D_001B1B5C) >> 8) << 16; + obj[0xA7] = 0x80; + *(short *)(obj + 0xAA) = id; + obj[0x6E] = 0; + obj[0x6C] = 0xFF; + obj[0xA4] = 0x7F; + obj[0xA6] = 0x80; + + if (D_001CE300[k] != id) { + h = *(unsigned short *)(obj + 0x34) | 5; + *(int *)(obj + 0x24) = 0; + *(unsigned short *)(obj + 0x34) = h; + *(int *)(obj + 0x98) = 0; + p = D_001D04E0[obj[0x22]]; + *(int *)(obj + 0x64) = p; + if (p == 0) + *(unsigned short *)(obj + 0x34) = h | 2; + return; + } + + p = D_001CDF40[k]; + *(int *)(obj + 0x64) = p; + if (p == 0) + *(unsigned short *)(obj + 0x34) |= 2; + def = (unsigned char *)D_001CDB80[obj[0x22]]; + *(unsigned char **)(obj + 0x24) = def; + obj[0x62] = def[0xE]; + *(unsigned short *)(obj + 0x34) |= *(unsigned short *)(def + 0x44); + *(int *)(obj + 0x98) = *(int *)(def + 0x10); + *(float *)(obj + 0x2C) = *(float *)(def + 0x24); + *(float *)(obj + 0x4C) = 1.0f; + *(float *)(obj + 0x48) = 1.0f; + if (*(int *)(def + 0x40) != 0) { + *(unsigned short *)(obj + 0x34) |= 0x10; + *(int *)(obj + 0x78) = *(int *)(def + 0x40); + } + def = *(unsigned char **)(obj + 0x24); + if (def[0xF] != 0) { + obj[0x6F] = 0x18; + *(unsigned short *)(obj + 0x34) |= 0x400; + *(int *)(obj + 0x70) = 0; + *(int *)(obj + 0x74) = 0; + obj[0xBD] = 0; + } + def = *(unsigned char **)(obj + 0x24); + if (def[6] != 0) + obj[0x63] = 0x18; + def = *(unsigned char **)(obj + 0x24); + if (*(int *)(def + 0x48) == 0) + return; + func_0029F390(obj); + def = *(unsigned char **)(obj + 0x24); + if (*(unsigned char *)(*(int *)(def + 0x48) + 0x10) >= 2) + *(unsigned short *)(obj + 0x34) &= 0xFFFD; + def = *(unsigned char **)(obj + 0x24); + if (def[0xC] == 1) { + unsigned char *sub = *(unsigned char **)(def + 0x48); + if (sub[0x10] < 2) { + *(int *)(obj + 0x48) = 0; + sub = *(unsigned char **)(def + 0x48); + if ((signed char)sub[0x11] < 0) + *(unsigned short *)(obj + 0x34) |= 0x40; + } + } +} diff --git a/nonmatching/functional/func_0029FDF0.c b/nonmatching/functional/func_0029FDF0.c new file mode 100644 index 000000000..d301e8ee9 --- /dev/null +++ b/nonmatching/functional/func_0029FDF0.c @@ -0,0 +1,35 @@ +/* functional: high. Reserves a qword of the frame DMA chain, writes an open DMA tag (0x20000000 NEXT) into it, links the moby segment tag to the new cursor, calls func_002A4AD8 and func_002FC748, then closes the segment by chaining the two tags again; same reads, writes and calls in the same order as retail, only codegen differs. */ +extern int D_001B22A8 __attribute__((sda)); +extern int D_001B1B50 __attribute__((sda)); +extern int D_001A72D0 __attribute__((sda)); +extern void func_002A4AD8(int a); +extern void func_002FC748(void); + +void func_0029FDF0(void) { + int *first = (int *)D_001B22A8; + int *t; + D_001B22A8 = (int)first + 0x10; + t = (int *)D_001B1B50; + t[0] = 0x20000000; + t = (int *)D_001B1B50; + t[1] = D_001B22A8; + t = (int *)D_001B1B50; + t[2] = 0; + t = (int *)D_001B1B50; + t[3] = 0; + func_002A4AD8(D_001A72D0); + func_002FC748(); + t = (int *)D_001B22A8; + t[0] = 0x20000000; + t = (int *)D_001B22A8; + t[1] = D_001B1B50 + 0x10; + t = (int *)D_001B22A8; + t[2] = 0; + t = (int *)D_001B22A8; + t[3] = 0; + D_001B22A8 = D_001B22A8 + 0x10; + first[0] = 0x20000000; + first[3] = 0; + first[1] = D_001B22A8; + first[2] = 0; +} diff --git a/nonmatching/functional/func_002A0048.c b/nonmatching/functional/func_002A0048.c new file mode 100644 index 000000000..cd2ed0517 --- /dev/null +++ b/nonmatching/functional/func_002A0048.c @@ -0,0 +1,39 @@ +/* functional: high. Hand-written from the assembly (never matched); same stores and calls in order. The result is whatever retail leaves in $v0 (the last queue pointer), returned explicitly. */ +typedef unsigned int u32; + +extern int D_001B1B7C; +extern u32 *D_001B1B80; +extern u32 *D_001B22A8; +extern void func_002DFD88(void); +extern void func_002DFBB0(int); + +int func_002A0048(void) +{ + u32 *saved; + + if (D_001B1B7C == 0) { + D_001B1B80[0] = 0x10000000; + D_001B1B80[1] = 0; + D_001B1B80[2] = 0; + D_001B1B80[3] = 0; + return (int)D_001B1B80; + } + saved = D_001B22A8; + D_001B22A8 = saved + 4; + D_001B1B80[0] = 0x20000000; + D_001B1B80[1] = (u32)D_001B22A8; + D_001B1B80[2] = 0; + D_001B1B80[3] = 0; + func_002DFD88(); + func_002DFBB0(0x1EF2E0); + D_001B22A8[0] = 0x20000000; + D_001B22A8[1] = (u32)(D_001B1B80 + 4); + D_001B22A8[2] = 0; + D_001B22A8[3] = 0; + D_001B22A8 = D_001B22A8 + 4; + saved[0] = 0x20000000; + saved[3] = 0; + saved[1] = (u32)D_001B22A8; + saved[2] = 0; + return (int)D_001B22A8; +} diff --git a/nonmatching/functional/func_002A0250.c b/nonmatching/functional/func_002A0250.c new file mode 100644 index 000000000..4f603c00c --- /dev/null +++ b/nonmatching/functional/func_002A0250.c @@ -0,0 +1,35 @@ +/* functional: high. Same calls and stores as retail with the same arguments; only statement order and registers differ. */ +#include "common.h" +extern char D_0010FFC0[]; +extern unsigned short D_0010FFB0[]; +extern char D_00100080[]; +extern int D_001B169C; +extern char *D_001B22A8; +extern int D_001B16B4; +extern int D_001B16B0; +extern char *D_001B1B50; +extern int D_001B1B58; +extern int D_001B22AC; +extern int D_001B1B80; +extern int D_001A72D0; +extern int D_001A72D4; +extern void func_002FC410(unsigned int, unsigned int); +extern void func_002FC070(int); +extern void func_002FC560(unsigned int, unsigned long long); + +void func_002A0250(void) +{ + char *p; + + func_002FC410((unsigned int)D_0010FFC0, D_0010FFB0[0]); + D_001B169C = 6; + func_002FC070((int)D_00100080); + func_002FC560(0x47, 0x5360B); + p = D_001B22A8; + D_001B1B50 = p; + D_001B22AC = D_001B16B4 + 0xFFFF0000; + D_001B1B58 = D_001B16B0; + D_001A72D0 = D_001A72D4; + D_001B22A8 = p + 0x10; + D_001B1B80 = 0; +} diff --git a/nonmatching/functional/func_002AD7B8.c b/nonmatching/functional/func_002AD7B8.c new file mode 100644 index 000000000..44f7a54c2 --- /dev/null +++ b/nonmatching/functional/func_002AD7B8.c @@ -0,0 +1,22 @@ +/* functional: high. Same reads, writes and branches as retail; the empty 28-iteration loop (dead result) is dropped, the table is addressed at 0x00139948. */ +typedef unsigned char u8; + +extern u8 D_001A7BC8[]; +extern int D_00139948[]; + +void func_002AD7B8(int a0) +{ + int i; + int count; + + if (D_001A7BC8[a0] != 0) + return; + D_001A7BC8[a0] = 1; + if (a0 >= 0x15 && a0 != 24) + return; + count = 0; + for (i = 0; i < 28; i++) + if (D_00139948[i] != 0) + count++; + D_00139948[count] = a0; +} diff --git a/nonmatching/functional/func_002AD860.c b/nonmatching/functional/func_002AD860.c new file mode 100644 index 000000000..4e44547ea --- /dev/null +++ b/nonmatching/functional/func_002AD860.c @@ -0,0 +1,47 @@ +/* functional: high. Near-miss draft; same reads, writes and calls in the same order, only the byte layout differs. */ + +typedef unsigned char u8; + +typedef struct { char pad0[0xC]; int kind; char pad1[4]; int handle; char pad2[0xC8]; } Entry_002AD860; + +extern u8 D_00139568[]; +extern Entry_002AD860 D_00239BA0[]; +extern int D_0018C0E8[]; +extern short D_001A63AC; + +extern int func_00294630(int); +extern void func_00294420(int); + +/* Marks entry a0's slot as taken, starting the entry's handle when it was idle. + Returns 1 when the slot was taken, 0 when it is blocked. */ +int func_002AD860(int a0) +{ + u8 *p = D_00139568 + a0; + Entry_002AD860 *e = &D_00239BA0[*p]; + int ret = 0; + + if (e->kind == 0) { + if (func_00294630(e->handle) == 0) { + if (D_001A63AC != 0) + return ret; + } + D_0018C0E8[0] = a0; + if (func_00294630(D_00239BA0[*p].handle) == 0) + func_00294420(D_00239BA0[*p].handle); + ret = 1; + } else { + int kind = e->kind; + + if (kind == 3) { + ret = 1; + D_0018C0E8[3] = a0; + } else if (kind == 2) { + ret = 1; + D_0018C0E8[2] = a0; + } else if (kind == 1) { + ret = 1; + D_0018C0E8[1] = a0; + } + } + return ret; +} diff --git a/nonmatching/functional/func_002B6828.c b/nonmatching/functional/func_002B6828.c new file mode 100644 index 000000000..38c369584 --- /dev/null +++ b/nonmatching/functional/func_002B6828.c @@ -0,0 +1,22 @@ +/* functional: high. Same sector pair, -1 sentinel, zero arg and 64-bit user data as retail; only register allocation and instruction order differ. */ +extern char D_00188660[]; +extern int D_0014B540[]; +extern void func_002B6770(int, long); +extern void func_00132498(void *, int, void (*)(int, long), unsigned long); + +void func_002B6828(int index) +{ + char *b = D_00188660; + int *d = D_0014B540; + int lbn = d[0x14B8 + index * 2]; + + if (lbn != 0) { + char *slot = b + (index + 1) * 4; + *(unsigned int *)(slot + 0x17A0) = 0xFFFFFFFFu; + func_00132498((void *)(lbn + d[0x14A7]), 0, func_002B6770, + (unsigned long)(unsigned int)(b + index * 4 + 0x17A4)); + } else { + char *slot = b + (index + 1) * 4; + *(unsigned int *)(slot + 0x17A0) = 0; + } +} diff --git a/nonmatching/functional/func_002B68C8.c b/nonmatching/functional/func_002B68C8.c new file mode 100644 index 000000000..3bc2ace0f --- /dev/null +++ b/nonmatching/functional/func_002B68C8.c @@ -0,0 +1,34 @@ +/* functional: high. Same stores and calls in retail order; retail issues the +0x24 store in the delay slot of the func_002B66F0 call, here it is a plain store before the call. $v1 holds 0x20 and $a0 holds -1 at the call, as in retail, because the callee chain reads them. */ +#include "include_asm.h" + +extern void func_0026FEB8(void *); +extern char D_001AA198[]; +extern char D_001A63A8[]; +extern void func_002B66F0(void); +extern void func_002B6938(void); + +void func_002B68C8(void) +{ + char *state; + int minus1; + int flags; + + func_0026FEB8(D_001AA198); + state = D_001A63A8; + minus1 = -1; + flags = 0x20; + *(short *)(state + 0x3C) = minus1; + state[0x40] = flags; + state[0x41] = 0; + state[0x42] = 0; + state[0x43] = 0; + *(int *)(state + 0x44) = 0; + *(short *)(state + 0x4E) = 0; + *(int *)(state + 0x68) = 0; + *(short *)(state + 0x72) = 0; + *(int *)(state + 0x8C) = 0; + *(short *)(state + 0x96) = 0; + *(int *)(state + 0x24) = minus1; + func_002B66F0(); + func_002B6938(); +} diff --git a/nonmatching/functional/func_002B6E78.c b/nonmatching/functional/func_002B6E78.c new file mode 100644 index 000000000..cca6ad3a9 --- /dev/null +++ b/nonmatching/functional/func_002B6E78.c @@ -0,0 +1,28 @@ +/* functional: high. Same table reads, state stores, and 12-argument call (sign-extended sectors, user data = state+0x44 as 64-bit) as retail; register allocation and instruction order differ. */ +typedef unsigned char u8; +extern u8 D_001A63A8[]; +extern int D_0014B540[]; +extern void func_00133340(long, long, int, int, int, int, int, int, int, int, int, long); +extern void func_002B7FB0(int, long); + +void func_002B6E78(int index, int a, int b) { + char *st = (char *)D_001A63A8; + int *t = D_0014B540; + + if (index >= 0 && *(int *)(st + 0x44) == 0) { + int lbn = t[0x14C0 + index * 2]; + if (lbn != 0) { + int base = t[0x14BF]; + *(short *)(st + 0x4E) = 1; + *(int *)(st + 0x58) = 10; + *(int *)(st + 0x5C) = 0xBB80; + *(short *)(st + 0x48) = index; + *(int *)(st + 0x44) = -1; + *(short *)(st + 0x4C) = a; + *(short *)(st + 0x4A) = b; + *(short *)(st + 0x54) = 0; + func_00133340(lbn + base, t[0x14C0 + (index + 1) * 2] + base, 0, 0, (short)b, 0, 1, 0, + 0, 0, (int)func_002B7FB0, (unsigned long)(unsigned int)(st + 0x44)); + } + } +} diff --git a/nonmatching/functional/func_002B7578.c b/nonmatching/functional/func_002B7578.c new file mode 100644 index 000000000..babf9165f --- /dev/null +++ b/nonmatching/functional/func_002B7578.c @@ -0,0 +1,159 @@ +/* functional: medium. Written from the assembly: same reads, writes and calls with the same arguments in the same order; the divide-by-zero checks retail emits for the divisions by W(0x34) and by a stored count are dropped. */ +extern char D_001A63A8[]; /* the state block */ +extern char D_001AA1B0[]; +extern char D_001AA1C0[]; +extern char D_001AA1D0[]; + +extern int func_00282948(void *); +extern void func_002B6E78(short, short, short); +extern void func_002B6F70(short, short, short); +extern int func_002B7088(short, short, short, short); +extern void func_002B6958(int, int, int, int); +extern void func_002B6D80(short, short, short); +extern void func_002B7380(void *); +extern void func_002B7B18(int, int, int); +extern void func_00132B88(int, int, int, int, int, int, int, int); +extern void func_00133400(int); +extern int func_00133580(int); +extern void func_0026FEB8(void *, ...); +extern void func_002B7FF0(void); + +#define S D_001A63A8 +#define H(o) (*(short *)(S + (o))) +#define HU(o) (*(unsigned short *)(S + (o))) +#define W(o) (*(int *)(S + (o))) +#define B(o) (*(unsigned char *)(S + (o))) + +void func_002B7578(void) +{ + int a2, a0, a1, v; + void (*cb)(int, int); + int fn; + + if (B(7) != 0) + return; + + if (!(H(0x50) & 0x8000) && !(H(0x4E) & 0x8000)) { + if (W(0x44) == 0) { + if ((HU(0x4C) & 1) && H(0x3C) == -1) + func_002B6E78(H(0x48), (short)HU(0x4C), H(0x4A)); + } else if (W(0x44) != -1 && H(0x4E) == 8) { + func_002B6F70(H(0x48), H(0x4C), H(0x4A)); + } + } + + if (func_00282948(S + 0x38) != 0) { + a0 = H(0x3C); + if (a0 != -1 && H(0x2C) == 0) { + if (H(0x48) == a0) { + H(0x3C) = -1; + } else if (H(0x3E) == -1) { + if (H(0x4E) & 0x8000) { + H(0x4E) = 5; + H(0x2C) = 3; + } else { + H(0x2C) = 6; + W(0xA4) = 0xF; + W(0x34) = 0xF; + } + W(0x38) = 0x12C; + } else if (func_002B7088(a0, H(0x3E), H(0x4C), H(0x4A)) != 0) { + W(0x38) = 0x1A4; + } + } + } + + a0 = W(0x24); + if (a0 >= 0) { + if (W(0x68) == 0) { + func_002B6958(a0, 0, W(0x28), 0x400); + W(0x28) = 0; + W(0x24) = -1; + } else if ((unsigned)(HU(0x72) - 6) >= 2) { + H(0x72) = 5; + } + } + + if (H(0x4E) != 9 && W(0x44) != -1 + && !(H(0x50) & 0x8000) && !(H(0x4E) & 0x8000)) { + if (H(0x3E) != -1 && ((H(0x98) & 0x8000) || (H(0x96) & 0x8000))) + goto final; + if ((unsigned)((short)(HU(0x2C) - 2)) <= 4u) { + switch ((short)(HU(0x2C) - 2)) { + case 0: + a2 = (H(0x4A) * (W(0x34) - (W(0x30) - W(0xA4)))) / W(0x34); + if (a2 > 0 && ((H(0x96) == 4 && H(0x9C) != 0) || W(0x8C) != 0) + && W(0x44) != 0) { + if (H(0x4E) != 9) { + a0 = W(0x44); + W(0x44) = -1; + func_00132B88(a0, 5, a2, 0, 0, 0, (int)func_002B7FF0, (int)(S + 0x44)); + } + } else { + H(0x2C) = 3; + H(0x4E) = 5; + } + break; + case 1: + if (H(0x4E) == 0 && (H(0x9C) != 0 || W(0x8C) == 0)) { + func_002B6D80(H(0x3C), H(0x4C), H(0x4A)); + H(0x2C) = 4; + H(0x3C) = -1; + } + break; + case 2: + if (H(0x4E) == 3 + && (W(0xA4) < W(0x34) || H(0x96) != 4 || (H(0x9C) == 0 && W(0x8C) == 0))) { + func_00133400(W(0x44)); + H(0x2C) = 5; + H(0x4E) = 8; + } + break; + case 3: + if (H(0x96) != 4 || H(0x9C) == 0) + H(0x2C) = 0; + break; + case 4: + a0 = W(0xA4); + a2 = (H(0x4A) * a0) / W(0x34); + a0 = W(0x44); + W(0x44) = -1; + func_00132B88(a0, 5, a2, 0, 0, 0, (int)func_002B7FF0, (int)(S + 0x44)); + if (func_00282948(S + 0xA4) != 0) { + H(0x4E) = 5; + H(0x2C) = 3; + } + break; + } + } + } + +final: + func_002B7380(S + 0x44); + func_002B7380(S + 0x8C); + func_002B7380(S + 0x68); + if (B(6) != 0) { + func_0026FEB8(D_001AA1B0); + if (func_00133580(1) != 0) + return; + H(4) = 0; + B(6) = 0; + func_0026FEB8(D_001AA1C0); + fn = W(0x18); + if (fn != 0) { + a1 = W(0x1C); + W(0x1C) = 0; + W(0x18) = 0; + func_0026FEB8(D_001AA1D0); + ((void (*)(int, int))fn)(a1, 0); + } + return; + } + v = H(4); + if (v == 2) { + H(4) = 0; + func_002B7B18(W(0x14), W(8), W(0x10)); + if (H(4) == 0) + H(4) = v; + } +} diff --git a/nonmatching/functional/func_002B88F0.c b/nonmatching/functional/func_002B88F0.c new file mode 100644 index 000000000..90f61ffd4 --- /dev/null +++ b/nonmatching/functional/func_002B88F0.c @@ -0,0 +1,205 @@ +/* functional: medium. Pad frame update (parse the raw report into sticks, pressures, button edges, a 30-frame history) written from the retail assembly: same reads, writes and calls in retail order. The three-way left/right swap of word 0x1A0 stores 0 when neither bit is set, exactly as retail does. Float constants are the retail words (1/255 = 0x3B808081, 0.9f = 0x3F666666, 0.96 = 0x3F75BE0D). */ +extern float func_00283CE0(int); +extern float func_00282E20(float *); +extern float func_00283248(float, float); +extern float func_00283C80(float, float); + +void func_002B88F0(char *p, unsigned char *raw, int type) { + int *w = (int *)p; + float *f = (float *)p; + float *hist_a = (float *)(p + 0x2D0); + float *hist_b = (float *)(p + 0x258); + int *hist_c = (int *)(p + 0x1E0); + int s6 = type < 0x12; + int i, k, v, t0, a0, a1, a2, a3, m; + float f20, f21, d, x; + unsigned char ub; + + v = ((raw[0] << 8) | raw[1]) ^ 0xFFFF; + w[0x1B0 / 4] = v; + w[0x1A0 / 4] = v; + for (i = 0; i < 16; i++) { + w[0x100 / 4 + i] = 0; + } + if (type >= 6) { + for (i = 0; i < 4; i++) { + a1 = raw[2 + i] - 0x7F; + if (a1 < 0) { + a1 = -a1; + } + if (a1 < 0x30) { + continue; + } + f20 = func_00283CE0(a1 - 0x30); + f20 = f20 / func_00283CE0(0x4C); + f[0x100 / 4 + i] = f20; + if (1.0f < f20) { + f[0x100 / 4 + i] = 1.0f; + } + if (raw[2 + i] < 0x7F) { + f[0x100 / 4 + i] = -f[0x100 / 4 + i]; + } + } + } + if (s6 == 0 || type < 6) { + for (i = 4; i < 16; i++) { + f[0x100 / 4 + i] = func_00283CE0(raw[2 + i]) * 0.003921569f; + } + } + if (*(unsigned char *)0x1A7A44 != 0) { + f[0x108 / 4] = -f[0x108 / 4]; + f[0x100 / 4] = -f[0x100 / 4]; + v = w[0x1A0 / 4]; + if (v & 0x8000) { + v = (v & 0xFFFF7FFF) | 0x2000; + } else if (v & 0x2000) { + v = (v & ~0x2000) | 0x8000; + } else { + v = 0; + } + w[0x1A0 / 4] = v; + w[0x1B0 / 4] = w[0x1A0 / 4]; + } + for (i = 0; i < 16; i++) { + w[0x140 / 4 + i] = w[0x100 / 4 + i]; + } + if (f[0x108 / 4] == 0.0f && f[0x10C / 4] == 0.0f) { + w[0x1D8 / 4] = 0; + } else { + w[0x1D8 / 4] = 1; + } + if (f[0x108 / 4] < 0.0f) { + w[0x1A0 / 4] |= 0x8000; + } + if (0.0f < f[0x108 / 4]) { + w[0x1A0 / 4] |= 0x2000; + } + if (f[0x10C / 4] < 0.0f) { + w[0x1A0 / 4] |= 0x1000; + } + if (0.0f < f[0x10C / 4]) { + w[0x1A0 / 4] |= 0x4000; + } + t0 = w[0x1A0 / 4]; + a3 = w[0x1AC / 4]; + a2 = w[0x1BC / 4]; + a1 = w[0x1B0 / 4]; + w[0x1D4 / 4] = (t0 & 0xF000) == 0; + w[0x1D0 / 4] = t0 == 0; + w[0x1B4 / 4] = ~a3 & a1; + w[0x1B8 / 4] = ~t0 & a2; + a0 = ~a3 & t0; + a1 = ~t0 & a3; + w[0x1C4 / 4] = a0; + w[0x1C8 / 4] = a1; + w[0x1A4 / 4] = a0; + w[0x1A8 / 4] = a1; + w[0x1C0 / 4] = t0; + w[0x348 / 4] = t0; + if (*(unsigned char *)0x1A7A44 != 0) { + f[0x148 / 4] = -f[0x148 / 4]; + f[0x140 / 4] = -f[0x140 / 4]; + if (t0 & 0x8000) { + v = (t0 & 0xFFFF7FFF) | 0x2000; + } else if (t0 & 0x2000) { + v = (t0 & ~0x2000) | 0x8000; + } else { + v = 0; + } + w[0x1C0 / 4] = v; + v = w[0x1C4 / 4]; + if (v & 0x8000) { + w[0x1C4 / 4] = (v & 0xFFFF7FFF) | 0x2000; + } else if (v & 0x2000) { + w[0x1C4 / 4] = (v & ~0x2000) | 0x8000; + } + v = w[0x1C8 / 4]; + if (v & 0x8000) { + w[0x1C8 / 4] = (v & 0xFFFF7FFF) | 0x2000; + } else if (v & 0x2000) { + w[0x1C8 / 4] = (v & ~0x2000) | 0x8000; + } + } + if (w[0x1CC / 4] == 1) { + w[0x1A0 / 4] &= 0xFFFFAFCF; + w[0x1A4 / 4] &= 0xFFFFAFCF; + w[0x1A8 / 4] &= 0xFFFFAFCF; + w[0x100 / 4] = 0; + w[0x104 / 4] = 0; + w[0x1CC / 4] = 0; + } + if (w[0x1CC / 4] == 2) { + w[0x1A0 / 4] &= 0x900; + w[0x1A4 / 4] &= 0x900; + w[0x1A8 / 4] &= 0x900; + w[0x1B0 / 4] &= 0x900; + w[0x1D4 / 4] = 1; + w[0x108 / 4] = 0; + w[0x10C / 4] = 0; + w[0x1CC / 4] = 0; + } + { + float xy[2]; + + xy[0] = f[0x108 / 4]; + xy[1] = f[0x10C / 4]; + f20 = func_00282E20(xy); + f21 = func_00283248(xy[0], xy[1]); + } + k = *(short *)(p + 0x18E); + hist_a[k] = f20; + hist_b[*(short *)(p + 0x18E)] = f21; + if (0.9f < f20) { + a1 = *(short *)(p + 0x18E) + 29; + a0 = a1 % 30; + if (!(0.9f < hist_a[a0])) { + v = a1; + a2 = 1; + for (;;) { + if (hist_a[a0] < 0.25f) { + w[0x1A4 / 4] |= 0x10000; + break; + } + a2++; + if (a2 >= 4) { + break; + } + v--; + a0 = v % 30; + if (0.9f < hist_a[a0]) { + break; + } + } + } + if ((w[0x1A4 / 4] & 0x10000) == 0) { + for (i = 1; i < 5; i++) { + x = func_00283C80(hist_b[(*(short *)(p + 0x18E) - i + 30) % 30], f21); + if (0.96050000f < x) { + w[0x1A4 / 4] |= 0x10000; + break; + } + } + } + } + hist_c[*(short *)(p + 0x18E)] = w[0x1A4 / 4]; + v = w[0x190 / 4] + 1; + *(short *)(p + 0x18E) = (*(short *)(p + 0x18E) + 1) % 30; + w[0x190 / 4] = v; + if (v >= 0x1F) { + w[0x190 / 4] = 30; + } + ub = *(unsigned char *)(p + 0x34E); + if (ub != 0) { + v = w[0x1A0 / 4]; + if (v != 0 && v == w[0x1AC / 4]) { + a0 = (unsigned char)(*(unsigned char *)(p + 0x34F) - 1); + *(unsigned char *)(p + 0x34F) = a0; + if (a0 == 0 || a0 == 0xFF) { + w[0x1A4 / 4] = v; + *(unsigned char *)(p + 0x34F) = ub; + } + } else { + *(unsigned char *)(p + 0x34F) = *(unsigned char *)(p + 0x34D); + } + } +} diff --git a/nonmatching/functional/func_002BA0E0.c b/nonmatching/functional/func_002BA0E0.c new file mode 100644 index 000000000..99bb02fab --- /dev/null +++ b/nonmatching/functional/func_002BA0E0.c @@ -0,0 +1,142 @@ +/* functional: medium. Callback-table reconstruction; all 111 slots present, address-only initialiser. The near-miss draft below differs from retail by one word of code size and scheduling. Verified on one captured call only. */ +/* Retained manual reconstruction, 2026-10-24. + * Profile: -O2 -G0 -ffunction-sections. + * Final measurements: us: 0/28 placements, exit1, 1476 compiled bytes; us2: + * 0/28 placements, exit1, 1476 compiled bytes; pal: 0/28 placements, exit1, + * 1476 compiled bytes. + * Remaining: All 111 callback slots, including the two repeated + * func_002B9380 entries, are reconstructed. The address-only initializer + * leaves callback argument lists unspecified and does not call them. + * Default, -g and -fno-gcse all produce 1476 versus 1472 bytes with + * differing save/scheduling details. + * Reopen with: Concrete callback-table source-lifetime or compiler + * scheduling evidence resolving the one-word excess and delayed callee + * saves; three tied profile trials are a stopping point. + * No byte-match or linked-build credit is claimed. + */ +/* The table holds callback entry addresses; this initializer does not call them. + * Empty parameter lists deliberately leave each callback's interface unspecified. + * The consumed return type is likewise irrelevant at this address-only use. */ +#define PARTICLE_UPDATES(X) \ + X(0, func_002BA6A0) \ + X(1, func_002BA978) \ + X(2, func_002BABE8) \ + X(3, func_002BAE48) \ + X(4, func_002BAF68) \ + X(5, func_002BB058) \ + X(6, func_002BB100) \ + X(7, func_002BB798) \ + X(8, func_002BB928) \ + X(9, func_002BBA38) \ + X(10, func_002BBB70) \ + X(11, func_002BBF40) \ + X(12, func_002BC510) \ + X(13, func_002BC8E8) \ + X(14, func_002BCA10) \ + X(15, func_002BCC58) \ + X(16, func_002BD0D0) \ + X(17, func_002BD390) \ + X(18, func_002BD398) \ + X(19, func_002BD508) \ + X(20, func_002BD5B0) \ + X(21, func_002BD9B0) \ + X(22, func_002B9288) \ + X(23, func_002BDEB0) \ + X(24, func_002BE150) \ + X(25, func_002BE400) \ + X(26, func_002BE7E8) \ + X(27, func_002BE928) \ + X(28, func_002BE9B8) \ + X(29, func_002BEBC8) \ + X(30, func_002BEE78) \ + X(31, func_002BF390) \ + X(32, func_002BF520) \ + X(33, func_002BF6F0) \ + X(34, func_002BF8F0) \ + X(35, func_002BFC50) \ + X(36, func_002BFE80) \ + X(37, func_002BFFE8) \ + X(38, func_002C00F8) \ + X(39, func_002C0200) \ + X(40, func_002C04C0) \ + X(41, func_002C0C78) \ + X(42, func_002C0E50) \ + X(43, func_002C14E0) \ + X(44, func_002C1840) \ + X(45, func_002C1B38) \ + X(46, func_002C1C88) \ + X(47, func_002C1DB8) \ + X(48, func_002C1E58) \ + X(49, func_002C1EF0) \ + X(50, func_002C1FC0) \ + X(51, func_002C2080) \ + X(52, func_002C2170) \ + X(53, func_002C23B8) \ + X(54, func_002C2658) \ + X(55, func_002C27B0) \ + X(56, func_002C28F8) \ + X(57, func_002C29A8) \ + X(58, func_002C2C80) \ + X(59, func_002C30F8) \ + X(60, func_002C3280) \ + X(61, func_002C3430) \ + X(62, func_002C37E0) \ + X(63, func_002C38A8) \ + X(64, func_002C38E0) \ + X(65, func_002C3B10) \ + X(66, func_002C3EE8) \ + X(67, func_002C4100) \ + X(68, func_002C43D0) \ + X(69, func_002C4500) \ + X(70, func_002C4788) \ + X(71, func_002C47F0) \ + X(72, func_002C4B10) \ + X(73, func_002C4B58) \ + X(74, func_002C4D20) \ + X(75, func_002C4F08) \ + X(76, func_002C5020) \ + X(77, func_002C5070) \ + X(78, func_002C5120) \ + X(79, func_002C5408) \ + X(80, func_002C5550) \ + X(81, func_002C58A8) \ + X(82, func_002C5D00) \ + X(83, func_002C6360) \ + X(84, func_002C64E0) \ + X(85, func_002B9380) \ + X(86, func_002C6868) \ + X(87, func_002C6A08) \ + X(88, func_002C6748) \ + X(89, func_002C6B80) \ + X(90, func_002C6EF0) \ + X(91, func_002C7058) \ + X(92, func_002C72A8) \ + X(93, func_002B9380) \ + X(94, func_002C7538) \ + X(95, func_002C7628) \ + X(96, func_002C7888) \ + X(97, func_002C7AE8) \ + X(98, func_002C7D58) \ + X(99, func_002C7F78) \ + X(100, func_002C8268) \ + X(101, func_002C8350) \ + X(102, func_002C8428) \ + X(103, func_002C85D8) \ + X(104, func_002C8610) \ + X(105, func_002C87B0) \ + X(106, func_002C8888) \ + X(107, func_002C8A38) \ + X(108, func_002C8CA0) \ + X(109, func_002C8F40) \ + X(110, func_002C90A0) +#define DECLARE_UPDATE(index, name) extern void name(); +PARTICLE_UPDATES(DECLARE_UPDATE) +#undef DECLARE_UPDATE +extern void (*D_001F2340[])(); +void func_002BA0E0(void) +{ +#define STORE_UPDATE(index, name) D_001F2340[index] = name; + PARTICLE_UPDATES(STORE_UPDATE) +#undef STORE_UPDATE +} +#undef PARTICLE_UPDATES diff --git a/nonmatching/functional/func_002C9CF8.c b/nonmatching/functional/func_002C9CF8.c new file mode 100644 index 000000000..15474727c --- /dev/null +++ b/nonmatching/functional/func_002C9CF8.c @@ -0,0 +1,92 @@ +/* functional: high. Same reads, writes and calls as retail; near miss restructured only in register use. */ +/* func_002C9CF8 (356 bytes, 28 places) - M27 near miss: 352/356 B, default flags. + * + * Everything but two spots is instruction-exact: retail holds the constant 1 in $v1 and + * D_001A8BB0 in $v0 around the `open` store (here swapped), and the tail's six zero + * stores go through `$v1`, a copy of the pause-state base `$s0` (`daddu $v1,$s0,$zero`) + * while the mode store and the last two use $s0 itself. A pointer local for those + * stores (block or function scope, before or after the mode store), a static + * (inline) helper taking the base, and MACRO_ADDR/plain forms of D_001A8BB0 all + * leave 352 B. + */ +#include "common.h" + +typedef struct Menu_2C9CF8 { + u8 pad00[0x38]; + struct Menu_2C9CF8 *next; + struct Menu_2C9CF8 *prev; +} Menu_2C9CF8; + +typedef struct { + s32 open; + u8 pad04[4]; + s32 mode; + u8 pad0C[0x1C - 0xC]; + s32 f1C; + s32 f20; + u8 pad24[0xDB - 0x24]; + u8 fDB; + u8 padDC[0xE8 - 0xDC]; + s32 wide; + u8 padEC[0x104 - 0xEC]; + s32 forced; + s32 f108; + u8 pad10C[0x120 - 0x10C]; + s32 f120; + u8 pad124[0x144 - 0x124]; + s32 f144; + s32 f148; + s32 f14C; + s32 f150; + u8 pad154[0x1C0 - 0x154]; + s32 f1C0; + s32 f1C4; + s32 f1C8; +} Pause_2C9CF8; + +extern Pause_2C9CF8 D_001F2840; +extern s32 D_001A8BB0[] __attribute__((sda)); +extern u32 D_001A7A08[] __attribute__((sda)); +extern s32 D_001A79F0 __attribute__((sda)); +extern s32 D_0025B5F8; +extern Menu_2C9CF8 D_00259918; +extern Menu_2C9CF8 D_00259AA8; +extern Menu_2C9CF8 D_00259AF8; +extern void func_00132AF8(s32); +extern void func_002B72E8(s32); +extern void func_00132028(void); +extern void func_002CAF18(void); +extern void func_00295C50(s32); +extern void func_0029D830(void); +extern void func_002DF050(void); + +/* Open the pause menu in a mode and link its pages for the screen format. */ +void func_002C9CF8(int mode) +{ + func_00132AF8(0x5D); + func_002B72E8(0); + D_001F2840.fDB = 0; + func_00132028(); + D_001F2840.open = 1; + if (D_001A8BB0[0] == 0) func_002CAF18(); + D_001F2840.f144 = 0; + D_001F2840.f148 = 0; + D_001F2840.wide = (D_001A7A08[0] & 0xFFFF0000) || D_001F2840.f108 != 0; + D_00259918.next = D_001F2840.wide ? &D_00259AF8 : &D_00259AA8; + D_00259AA8.prev = D_001F2840.wide ? &D_00259AF8 : &D_00259918; + D_001F2840.mode = D_001F2840.forced ? D_001F2840.forced : mode; + D_001F2840.f1C8 = 0; + D_001F2840.f1C = 0; + D_001F2840.f20 = 0; + D_001F2840.f120 = 0; + D_001F2840.f1C0 = 0; + D_001F2840.f1C4 = 0; + if (mode != 6) { + func_00295C50(D_001A79F0); + func_0029D830(); + D_0025B5F8 = 0; + func_002DF050(); + } + D_001F2840.f150 = 0; + D_001F2840.f14C = 1; +} diff --git a/nonmatching/functional/func_002CA8A8.c b/nonmatching/functional/func_002CA8A8.c new file mode 100644 index 000000000..20d70033e --- /dev/null +++ b/nonmatching/functional/func_002CA8A8.c @@ -0,0 +1,82 @@ +/* functional: medium. Near-miss C: same reads, writes and calls in retail order; only register allocation and scheduling differ. */ +typedef struct { + int mode; + char pad04[0x10]; + int arg; + char pad18[4]; + int pending; + char pad20[0x100]; + int counter; + char pad124[0x34]; + int timer; + char pad15C[8]; + int step; + int clear; +} PauseState; + +typedef struct { + char pad[0x15C]; + int level; + char pad160[4]; + int delta; +} PauseInput; + +extern char D_001F2840[]; +extern PauseInput D_001393E0; +extern int D_001A7424; +extern int D_001B1684; +extern short D_0018A000[]; +extern int D_001B18A8; +extern void func_002DDEC8(void); +extern int func_002B49E0(int, int, int, int, void *); +extern void func_002CAA58(void); +extern void func_002CAA88(void); +extern void func_002CAAA0(void); +extern void func_002CAB00(void); +extern void func_002CADB0(void); +extern void func_002E4AC8(void); +extern void func_002B6658(void); +extern void func_002CA5D0(void); + +void func_002CA8A8(void) +{ + PauseState *S = (PauseState *)D_001F2840; + + S->counter = S->counter > 0x7CFF ? 0x7D00 : S->counter + 1; + S->clear = 0; + if (S->timer != 0) + S->timer--; + if (D_001393E0.level < 3 && D_001393E0.delta >= 0) + ((PauseState *)D_001F2840)->step = 0; + else + S->step++; + func_002DDEC8(); + if ((D_001A7424 & 1) && ((PauseState *)D_001F2840)->mode == 4 && D_001B1684 >= 8) { + if (func_002B49E0(4, 1, 1, ((PauseState *)D_001F2840)->arg, 0) == 0) + return; + } + switch (((PauseState *)D_001F2840)->mode) { + case 1: + func_002CAA58(); + break; + case 2: + func_002CAA88(); + break; + case 3: + func_002CAAA0(); + break; + case 4: + func_002CAB00(); + break; + case 5: + func_002CADB0(); + func_002E4AC8(); + D_0018A000[0] = 10; + D_001B18A8 = 0; + return; + } + func_002B6658(); + func_002E4AC8(); + if (((PauseState *)D_001F2840)->pending != 0) + func_002CA5D0(); +} diff --git a/nonmatching/functional/func_002CAB00.c b/nonmatching/functional/func_002CAB00.c new file mode 100644 index 000000000..3df4800f7 --- /dev/null +++ b/nonmatching/functional/func_002CAB00.c @@ -0,0 +1,99 @@ +/* functional: high. Transcribed branch by branch from retail; calls, arguments and stores in retail's order. */ +typedef struct Slot Slot; +typedef int (*SlotFn)(Slot *); +typedef void (*SlotCb)(Slot *, int); +struct Slot { int fn0; int pad4; int cb8; int cbC; }; + +typedef struct Screen { + char pad0[0xE8]; + Slot *cur; /* 0xE8 */ + Slot *slots[14]; /* 0xEC */ + char pad124[4]; + Slot *next; /* 0x128 */ +} Screen; + +typedef struct Menu { + int state; /* 0x00 */ + int phase; /* 0x04 */ + int mode; /* 0x08 */ + char pad0C[8]; + Screen *screen; /* 0x14 */ + Screen *target; /* 0x18 */ + int result; /* 0x1C */ + char pad20[0xC0]; + Screen *old; /* 0xE0 */ +} Menu; + +extern Menu D_001F2840; +extern int D_001AB7A4; +extern char D_0025A190[]; +extern char D_00259198[]; +extern void func_002D7AC8(int, int); +extern int func_002CBAE0(void); +extern void func_002CCED0(void); +extern void func_002E5E58(int, int, int); + +#define CALL_CB(o, off, arg) ((SlotCb)(*(int *)((char *)(o) + (off))))((o), (arg)) + +void func_002CAB00(void) +{ + Menu *m = &D_001F2840; + Screen *p = m->screen; + Slot **arr = p->slots; + int i; + Slot *o; + + if (D_001AB7A4 != 0) { + if (m->phase == 0 && (char *)p == D_0025A190) + func_002D7AC8(0, 0); + if (func_002CBAE0() != 0) { + if (m->phase == 0) + m->phase = 1; + return; + } + } + if (m->phase == 0) { + for (i = 0; i < 14; i++) { + o = m->screen->slots[i]; + if (o != 0 && o->cb8 != 0) + CALL_CB(o, 8, 0); + } + m->phase = m->phase + 1; + } + for (i = 0; i < 14; i++) { + o = arr[i]; + if (o == 0 || o->fn0 == 0) + continue; + if ((char *)o == D_00259198) + func_002CCED0(); + if (((SlotFn)arr[i]->fn0)(arr[i])) + m->result = (m->mode == 4) ? 2 : 1; + } + p = m->screen; + if (p != 0 && p->next != 0) { + o = p->cur; + if (o->cbC != 0) + CALL_CB(o, 0xC, 1); + p = m->screen; + p->cur = p->next; + m->screen->next = 0; + o = m->screen->cur; + if (o->cb8 != 0) + CALL_CB(o, 8, 1); + } + if (m->target == 0) + return; + if (m->screen != m->target) + func_002E5E58(0x12, 0, 0); + else + func_002E5E58(0x11, 0, 0); + for (i = 0; i < 14; i++) { + o = m->screen->slots[i]; + if (o != 0 && o->cbC != 0) + CALL_CB(o, 0xC, 0); + } + m->old = m->screen; + m->state = 3; + m->phase = 0; + m->screen = 0; +} diff --git a/nonmatching/functional/func_002CAF18.c b/nonmatching/functional/func_002CAF18.c new file mode 100644 index 000000000..100876618 --- /dev/null +++ b/nonmatching/functional/func_002CAF18.c @@ -0,0 +1,113 @@ +/* functional: medium. Decoded from the assembly: sets up the widget at (D_001A8CFC + 0x39140) differently per game-state predicate (func_0026F7xx) with the same calls, arguments and order as retail; the shared tails are merged. Table addresses are the sda variables retail passes by address. */ +extern char *D_001A8CFC; + +extern int func_0026F7E0(void); +extern int func_0026F7C8(void); +extern int func_0026F7D0(void); +extern int func_0026F7D8(void); +extern int func_0026F7F0(void); +extern int func_0026F7E8(void); +extern int func_0026F7C0(void); +extern int func_002DF1B8(void); +extern void func_00348578(void *, int); +extern void func_00348640(void *, int, void *); +extern void func_00348580(void *, void *); +extern void func_00348570(void *, int); +extern void func_00348630(void *, int, int); +extern void func_003489E8(void *, int); + +extern char D_001AB7F0[] __attribute__((sda)); /* gp-0x3800 */ +extern char D_001AB7A8[] __attribute__((sda)); /* gp-0x3848 */ +extern char D_001AB850[] __attribute__((sda)); /* gp-0x37A0 */ +extern char D_001AB808[] __attribute__((sda)); /* gp-0x37E8 */ +extern char D_001AB890[] __attribute__((sda)); /* gp-0x3760 */ +extern char D_001AB820[] __attribute__((sda)); /* gp-0x37D0 */ +extern char D_001AB8D0[] __attribute__((sda)); /* gp-0x3720 */ +extern char D_001AB830[] __attribute__((sda)); /* gp-0x37C0 */ +extern char D_001AB838[] __attribute__((sda)); /* gp-0x37B8 */ +extern char D_001AB7D0[] __attribute__((sda)); /* gp-0x3820 */ +extern char D_002614E8[]; +extern unsigned char D_0018C0D4[]; + +#define Q ((char *)D_001A8CFC + 0x39140) +#define SETFN(p) (*(int *)(Q + 0x414) = (int)(p)) + +void func_002CAF18(void) +{ + if (D_001A8CFC == 0) + return; + + if (func_0026F7E0()) { + func_00348578(Q, 1); + func_00348640(Q, 5, D_001AB7F0); + func_00348580(Q, D_001AB7A8); + SETFN(D_001AB850); + func_00348570(Q, 0); + func_00348630(Q, 0, 1); + func_00348630(Q, 2, 1); + func_00348630(Q, 3, 1); + func_00348630(Q, 1, 1); + func_00348630(Q, 4, 1); + *(int *)(Q + 0x43C) = 0; + } else if (func_0026F7C8() && func_0026F7D0()) { + func_00348578(Q, 1); + func_00348640(Q, 5, D_001AB808); + func_00348580(Q, D_001AB7A8); + SETFN(D_001AB890); + func_00348630(Q, 0, 1); + func_00348630(Q, 1, func_0026F7C0() ^ 1); + func_00348630(Q, 2, func_0026F7C0() ^ 1); + func_00348630(Q, 3, 1); + func_00348630(Q, 4, func_0026F7C0() ^ 1); + func_00348570(Q, 0); + *(int *)(Q + 0x43C) = 0; + } else if (func_0026F7D8()) { + func_00348578(Q, 1); + func_00348640(Q, 3, D_001AB820); + func_00348580(Q, D_001AB7A8); + SETFN(D_001AB8D0); + func_00348570(Q, 0); + func_00348630(Q, 0, 1); + func_00348630(Q, 1, 1); + func_00348630(Q, 2, 1); + *(int *)(Q + 0x43C) = 0; + } else if (func_0026F7F0() == 0x31 || func_0026F7E8() != 0 || func_0026F7F0() == 0x1C) { + func_00348578(Q, 1); + func_00348640(Q, 2, D_001AB830); + func_00348580(Q, D_001AB7A8); + SETFN(D_001AB838); + func_00348570(Q, 0); + func_00348630(Q, 0, 1); + func_00348630(Q, 1, 1); + *(int *)(Q + 0x43C) = 0; + } else { + func_00348578(Q, 1); + func_00348640(Q, 7, D_001AB7D0); + func_00348580(Q, D_001AB7A8); + SETFN(D_002614E8); + if (D_0018C0D4[0] == 1) { + func_00348630(Q, 0, 0); + func_00348630(Q, 1, 0); + func_00348630(Q, 2, 0); + } else { + func_00348630(Q, 0, 1); + func_00348630(Q, 1, 1); + func_00348630(Q, 2, 1); + } + func_00348630(Q, 3, 1); + func_00348630(Q, 4, 1); + func_00348630(Q, 5, 1); + if (func_002DF1B8()) { + func_00348570(Q, 1); + func_00348630(Q, 6, 1); + } else { + func_00348570(Q, 0); + func_00348630(Q, 6, 0); + } + if (D_0018C0D4[0] == 1) + *(int *)(Q + 0x43C) = 3; + else + *(int *)(Q + 0x43C) = 0; + } + func_003489E8(Q, 0); +} diff --git a/nonmatching/functional/func_002CBAE0.c b/nonmatching/functional/func_002CBAE0.c new file mode 100644 index 000000000..1d7d177e5 --- /dev/null +++ b/nonmatching/functional/func_002CBAE0.c @@ -0,0 +1,43 @@ +/* functional: medium. Same reads, writes and calls on the same conditions as retail; the retail branch tangle (the ladder over the state's current-handler word, 0x14) is written as nested ifs. Matching was not attempted within the time box. */ +extern int D_001F2840[]; +extern int D_00138344; +extern int D_001AB910 __attribute__((sda)); +extern int (*D_00210ACC)(void); + +extern void func_0029CE38(void); +extern void func_0029C290(int); +extern int func_002D6F38(int); +extern void func_0029C300(int); + +int func_002CBAE0(void) +{ + int *s = D_001F2840; + int cur = s[5]; + int ret = 0; + + if (cur == 0x259018) { + if (D_00138344 & 0x910) { + ret = 1; + s[7] = (s[2] == 4) ? 2 : 1; + } else { + ret = D_00210ACC() >= 0; + } + return ret; + } + if (cur == 0x25A738) { + func_0029CE38(); + return 0; + } + if (cur == 0x25A498 && D_001AB910 == 0) { + func_0029C290(0); + return 0; + } + cur = s[5]; + if (cur == 0x25A190 && D_001AB910 == 0) { + if (func_002D6F38(0) != 0) + s[7] = (s[2] == 4) ? 2 : 1; + func_0029C300(D_00138344); + return s[6] != 0x25A498; + } + return 0; +} diff --git a/nonmatching/functional/func_002CBC58.c b/nonmatching/functional/func_002CBC58.c new file mode 100644 index 000000000..032622967 --- /dev/null +++ b/nonmatching/functional/func_002CBC58.c @@ -0,0 +1,264 @@ +/* functional: medium. Written from the assembly: same reads, writes and calls with the same arguments in the same order; the divide-by-zero checks retail emits for the divisions by 2*h and 2*w are dropped. */ +extern char D_001F2840[]; /* C: the menu block */ +extern char D_00138180[]; /* P: the pad block */ +extern int D_001A72DC; +extern int D_001A7344; +extern int D_001AB91C; /* gp-0x36D4 */ +extern int D_001AB910; /* gp-0x36E0 */ +extern int D_001AB92C; /* gp-0x36C4 */ +extern int D_001AB930; /* gp-0x36C0 */ +extern char D_001AB920[]; +extern char D_001AB928[]; +extern int D_001AA450[]; /* gp-0x4BA0, 14 words */ +extern int D_001AA43C; /* gp-0x4BB4 */ +extern unsigned long long D_001A7470; /* gp-0x7B80 */ +extern int D_00210AD0; + +extern void func_00284F40(int); +extern void func_002FC990(int, int, int); +extern void func_0027B858(void); +extern void func_0027DDC0(int, int, int, int); +extern void func_0027C540(int); +extern void func_0027C660(void); +extern void func_0028E458(void); +extern void func_0029BF08(void); +extern void func_0029C2D0(void); +extern void func_0029C340(void); +extern void func_002FC560(unsigned int, unsigned long long); +extern void func_002A0250(void); +extern void func_002A01C8(void); +extern void func_002A01F0(void); +extern void func_002A0220(void); +extern void func_002A0380(void); +extern void func_0026FEB8(void *, ...); +extern void func_00280510(float, int, int, int); +extern void func_00280600(void); +extern void func_0028FDD8(int, int, int, int, int, int, int); +extern void func_0027E018(int, int, int, int, int, int, int, int, unsigned long long, unsigned long long); +extern int func_00283CF0(float); +extern void func_002FC3C0(int); +extern void func_0029FEE0(void); + +#define CW(o) (*(int *)(D_001F2840 + (o))) +#define CF(o) (*(float *)(D_001F2840 + (o))) +#define PW(o) (*(int *)(D_00138180 + (o))) + +void func_002CBC58(int mode) +{ + int s7 = 0; + int i, fp, s1, s2, s3, s4, s5, s6, w; + int fl, widget, a1; + int t0, t1, t2, t3, a2, a3, rect; + int x, y; + float f1, f0; + + func_00284F40(0); + func_002FC990(CW(0x20), D_001A72DC, D_001A7344 << 11); + if (mode != 0) + return; + func_0027B858(); + func_0027DDC0(0, 0, 0, 0x38); + if (CW(0) != 4) + return; + if (CW(4) == 0) + return; + + if (D_001AB91C != 0) { + s1 = CW(0x14); + if (s1 == 0x259018) { + func_0027C540(0); + ((void (*)(void))D_00210AD0)(); + func_0027C660(); + return; + } + if (s1 == 0x25A738) { + func_0027C540(0); + func_0028E458(); + func_0029BF08(); + func_0027C660(); + return; + } + if (s1 == 0x25A498) { + if (D_001AB910 == 0) { + func_0027C540(0); + func_0029C2D0(); + func_0027C660(); + } + } else if (CW(0x14) == 0x25A190) { + if (D_001AB910 == 0) { + func_0027C540(0); + func_0029C340(); + func_0027C660(); + return; + } + } + } + + func_002FC560(0x47, 0x5360BULL); + if (CW(0x168) != 0) { + func_002A0250(); + func_002A01C8(); + func_002A01F0(); + } + a1 = CW(0x14); + if (a1 != 0) + s7 = a1 + 0xEC; + if ((PW(0x1A0) & 0xF) == 0xF && (PW(0x1C4) & 0xF) != 0) + func_0026FEB8(D_001AB920); + + for (i = 0; i < 14; i++) { + if (D_001AA450[i] == 0) + continue; + if (s7 == 0) + continue; + widget = *(int *)(s7 + 4 * i); + if (widget == 0) + continue; + rect = CW(0x14) + (i << 4); + *(int *)(widget + 0x18) = *(int *)(rect + 0) - *(int *)(rect + 8) / 2; + widget = *(int *)(s7 + 4 * i); + rect = CW(0x14) + (i << 4); + *(int *)(widget + 0x1C) = *(int *)(rect + 4) - *(int *)(rect + 0xC) / 2; + widget = *(int *)(s7 + 4 * i); + *(int *)(widget + 0x20) = *(int *)(CW(0x14) + (i << 4) + 8); + widget = *(int *)(s7 + 4 * i); + *(int *)(widget + 0x24) = *(int *)(CW(0x14) + (i << 4) + 0xC); + widget = *(int *)(s7 + 4 * i); + if (widget == 0x259EE0) + *(int *)(widget + 0x24) = 0x100; + } + + if ((PW(0x1A0) & 0xF) == 0xF && (PW(0x1C4) & 0xF) != 0) + func_0026FEB8(D_001AB928); + s6 = 1; + for (fp = 0; fp < 2; fp++) { + for (i = 0; i < 14; i = s5) { + s5 = i + 1; + if (s7 == 0) + break; + widget = *(int *)(s7 + 4 * i); + if (widget == 0) + continue; + a1 = *(int *)(widget + 0x10); + if (a1 & 4) + continue; + if (D_001AA450[i] == 0) + continue; + if (*(int *)(widget + 4) == 0) + continue; + if (fp == 0 && !(a1 & 2)) + continue; + if (fp == 1 && (a1 & 2)) + continue; + if (a1 & 1) { + ((void (*)(int))*(int *)(widget + 4))(widget); + continue; + } + x = *(int *)(widget + 0x18); + y = *(int *)(widget + 0x1C); + s4 = *(int *)(widget + 0x20); + s3 = *(int *)(widget + 0x24); + s2 = 7; + if (s4 > 0x80) { + do { + s2++; + } while ((1 << s2) < s4); + } + s1 = 7; + if (s3 > 0x80) { + do { + s1++; + } while ((1 << s1) < s3); + } + while (s2 + s1 >= 0x12) + s1--; + { + int cb = *(int *)(*(int *)(s7 + 4 * i) + 4); + if ((cb == 0x2DBAD0 || cb == 0x2DA118 || cb == 0x2D84B0 || cb == 0x2DA9A8) + && !(*(int *)(*(int *)(s7 + 4 * i) + 0x10) & 8)) { + func_00280510(1.0f, s2, s1, 0); + func_0028FDD8(0, 0, s6 << s2, s6 << s1, D_001AA43C, 0, 0); + widget = *(int *)(s7 + 4 * i); + fl = ((int (*)(int))*(int *)(widget + 4))(widget); + func_00280600(); + } else { + widget = *(int *)(s7 + 4 * i); + fl = ((int (*)(int))*(int *)(widget + 4))(widget); + } + } + func_002FC560(0x42, 0x8000000064ULL); + func_002FC560(0x47, 0x43ULL); + if (fl & 1) + continue; + a2 = s6 << s2; + a3 = s6 << s1; + t0 = 0; + t1 = 0; + t2 = a2; + t3 = a3; + if (fl & 2) { + t2 = s4; + t3 = s3; + } else if (fl & 8) { + t0 = (t2 - s4) / 2; + t1 = (t3 - s3) / 2; + t2 = t2 - t0; + t3 = t3 - t1; + if (a3 < t3) + t3 = a3; + if (a2 < t2) + t2 = a2; + if (t0 < 0) + t0 = 0; + if (t1 < 0) + t1 = 0; + } else if (fl & 4) { + if (s4 < s3) { + t0 = t2 / 2 - (s4 * t2) / (s3 << 1); + t2 = t2 - t0; + } else { + t1 = t3 / 2 - (s3 * t3) / (s4 << 1); + t3 = t3 - t1; + } + } else if (!(fl & 0x10)) { + continue; + } + if (D_001AB92C == 0) + continue; + func_0027E018(x, y, s4, s3, t0, t1, t2 - t0, t3 - t1, 0x80808080ULL, D_001A7470); + } + if (D_001AB930 != 0 && fp == 0) { + if (CW(0x168) != 0) { + func_002A0220(); + func_002A0380(); + func_002FC3C0(0x10); + func_0029FEE0(); + CW(0x16C) = 0; + CW(0x168) = 0; + } + } + } + + func_0027B858(); + f1 = CF(0x1C0); + f0 = CF(0x1C8); + if (f1 != f0) { + f1 = f0 - f1; + f0 = CF(0x1C4); + if (f0 < f1) { + f1 = f0; + } else { + f0 = -f0; + if (f1 < f0) + f1 = f0; + } + CF(0x1C0) = CF(0x1C0) + f1; + } + f1 = CF(0x1C0); + if (f1 != 0.0f) { + i = func_00283CF0(f1 * 128.0f); + func_0027DDC0(0, 0, 0, i); + } + func_002FC560(0x42, 0x8000000044ULL); + func_002FC560(0x47, 0x5360BULL); +} diff --git a/nonmatching/functional/func_002D6F38.c b/nonmatching/functional/func_002D6F38.c new file mode 100644 index 000000000..dda00bb53 --- /dev/null +++ b/nonmatching/functional/func_002D6F38.c @@ -0,0 +1,215 @@ +/* functional: low (SAME on one captured call only, most paths unexercised). Written from the assembly: same reads, writes and calls with the same arguments in the same order; the three func_00294D80 results are stored as 64-bit values as in retail. */ +extern char D_001F2840[]; /* C: the menu/pad block */ +extern char D_001C4FA0[]; /* Q: the selection state */ +extern char D_00138180[]; /* P: the pad block, flags at +0x1C4 */ +extern int D_00139768[]; +extern char D_0014B540[]; +extern int D_001A72D4; +extern int D_001A7478; +extern int D_001A79F0; +extern int *D_001AA4F0; +extern unsigned char D_001A7AFD; +extern unsigned char D_001A7BE8[]; +extern char D_001A63A8[]; +extern char D_001BAD40[]; + +extern void func_00295D00(void); +extern void func_002C9E60(int); +extern void func_00288AA0(void *, int, int, int); +extern void func_00288BE8(void *); +extern int func_00295A10(int); +extern void func_00295C50(int); +extern void func_002E5E58(int, int, int); +extern void func_0029D830(void); +extern int func_00295828(int); +extern int func_00295A80(void); +extern void func_00295788(int, int); +extern void func_0029D4D0(int, int); +extern int func_002DE550(int); +extern void func_00295BE8(int, int, int, int); +extern void func_00297730(int, int, int, int); +extern void func_00297400(int, int, int); +extern long long func_00294D80(int, int, int, int, int, int); +extern void func_0029D0E0(void); +extern void func_002DE618(int); +extern void func_0029DEA8(int, int); +extern int func_002956E8(void); +extern int func_002958B8(void); +extern void func_002EEED0(int, int, int, int, int); +extern void func_002B7B18(int, int, int); + +#define CW(o) (*(int *)(D_001F2840 + (o))) +#define QW(o) (*(int *)(D_001C4FA0 + (o))) +#define PW(o) (*(int *)(D_00138180 + (o))) + +int func_002D6F38(void) +{ + int s2, s0, s1, s3, s4, s5, s6, s7; + int a1, base, X, Y, Z, k, idx, a3, bit; + long long r; + + func_00295D00(); + + if (CW(0x14) == 0x25E680) { + QW(0x28) = 1; + } else { + s2 = QW(0x230); + if (PW(0x1C4) & 0xD00) { + if (CW(0x134) == 0) + return 1; + } + if (PW(0x1C4) & 0x10) { + int v = *(int *)(CW(0x14) + 0xE0); + if (v != 0) { + CW(0x18) = v; + return 0; + } + return CW(0x134) == 0 ? -1 : 0; + } + if (PW(0x1C4) & 0x40) + CW(0x18) = 0x25A498; + + if (PW(0x1C4) & 0x20) { + a1 = QW(0x230); + if (a1 >= 0 && *(unsigned char *)(D_001F2840 + 0xDB) == 0) { + s2 = (a1 == 0) ? 0x15 : a1; + if (s2 == 0x18) + s2 = 0; + a1 = QW(0x230); + if (a1 < 0) + return 1; + if (((D_00139768[a1 >> 2] >> (a1 & 0x1F)) & 1) == 0) + return 1; + D_001A72D4 = CW(0x28); + func_002C9E60(0); + CW(0x140) = D_001A7478; + CW(0x100) = CW(0x14); + CW(0x104) = CW(0x8); + CW(0xF4) = QW(0x230); + D_001A7478 = 2; + func_00288AA0(D_001BAD40, s2, CW(0x14), 2); + func_00288BE8(D_001BAD40); + return 1; + } + } + + s0 = func_00295A10(QW(0x230)); + if (s0 >= 0) { + if (PW(0x1C4) & 8) { + if (s0 < 0x1B) { + a1 = D_001AA4F0[s0 + 1]; + if (a1 != 0) + func_00295C50(a1); + } + } + if (PW(0x1C4) & 4) { + if (s0 != 0) { + a1 = D_001AA4F0[s0 - 1]; + if (a1 != 0) + func_00295C50(a1); + } + } + } + if (QW(0x230) != s2) { + func_002E5E58(3, 0, 0); + func_0029D830(); + } + if (CW(0x14) == 0x25E680) + QW(0x28) = 1; + } + + s7 = QW(0x230); + if (D_001A7AFD != 0) + s7 = s7 + 0x100; + s0 = func_00295828(s7); + if (QW(0x230) != QW(0x234) && s0 != -1) { + s1 = func_00295A80(); + if (QW(0x288) != 0 && s1 > 0) { + func_00295788(s1, 0); + if (s0 == 0) + s0 = s1; + } + func_0029D4D0(QW(0x288 + 4 * s0), QW(0x248)); + s2 = QW(0x248); + s6 = s2 + *(int *)(s2 + 8); + s5 = s2 + *(int *)(s2 + 0xC); + s4 = func_002DE550(0); + if (s4 != 0) { + a3 = QW(0x248); + if (QW(0x230) == D_001A79F0) { + func_00295BE8(s5, s6, s5, QW(0xC)); + } else { + k = QW(0x230); + if (D_001A7BE8[k] != 0) { + func_00297730(s4, 0x18D278 + (k << 11), a3 + *(int *)(s2 + 4), a3); + } else { + a1 = a3 + *(int *)(s2 + 0) + 8; + func_00297400(s4, a1, a1); + } + func_00295BE8(s5, s6, s5, s4); + } + } + X = QW(0x248) + *(int *)(s2 + 0x10); + Y = QW(0x248) + *(int *)(s2 + 0x18); + Z = QW(0x248) + *(int *)(s2 + 0x14); + r = func_00294D80(7, 7, X + 0x20, X + 0x420, QW(0x250), QW(0x254)); + *(long long *)(D_001C4FA0 + 0x268) = r; + r = func_00294D80(7, 7, X + 0x20, Z + 0x420, QW(0x250), QW(0x258)); + *(long long *)(D_001C4FA0 + 0x270) = r; + r = func_00294D80(7, 7, X + 0x20, Y + 0x420, QW(0x250), QW(0x25C)); + *(long long *)(D_001C4FA0 + 0x278) = r; + if (s4 != 0) + func_00294D80(9, 9, s5, s5, 0x3FF000, QW(0x24C)); + func_0029D0E0(); + if (s4 != 0) + func_002DE618(s4); + a1 = QW(0x230); + QW(0x288) = s6; + QW(0x234) = a1; + func_0029DEA8(a1, 0); + } + + if (*(short *)(D_001A63A8 + 4) == 0) { + if (QW(0x2B0) != -1) { + *(unsigned char *)(D_001F2840 + 0xDB) = 0; + QW(0x29C + 4 * QW(0x2B0)) ^= 0x1000; + QW(0x2B0) = -1; + } + } + + s4 = -1; + if (QW(0x28) != 0 && *(short *)(D_001A63A8 + 4) == 0) { + s4 = func_002956E8(); + if (s4 != -1) { + s3 = func_002958B8(); + if (s3 != -1) { + bit = (D_001A79F0 + QW(0x23C)) ? 0x100 : 0; + if (bit == s3) { + func_002EEED0(QW(0x288 + 4 * s4), QW(0x238), 0, QW(0x240), 0); + QW(0x2B4 + 4 * s4) = QW(0x240); + } else { + char *tbl = D_0014B540; + int a2v; + if (s3 & 0x100) { + idx = s3 ^ 0x100; + a3 = *(int *)(tbl + 0x15D0 + idx * 8); + a2v = *(int *)(tbl + 0x15D4 + idx * 8); + } else { + a3 = *(int *)(tbl + 0x14F0 + s3 * 8); + a2v = *(int *)(tbl + 0x14F4 + s3 * 8); + } + func_002B7B18(QW(0x288 + 4 * s4), a3 + *(int *)(tbl + 0x36C), a2v); + QW(0x2B4 + 4 * s4) = (int)(a2v << 11) >> 4; + *(unsigned char *)(D_001F2840 + 0xDB) = 1; + } + QW(0x29C + 4 * s4) = s3 | 0x1000; + QW(0x2B0) = s4; + } + } + } + + s0 = func_00295828(s7); + if (QW(0x230) != QW(0x234) && *(short *)(D_001A63A8 + 4) == 0 && s0 == -1 && s4 == s0) + func_00295A80(); + return 0; +} diff --git a/nonmatching/functional/func_002DDEC8.c b/nonmatching/functional/func_002DDEC8.c new file mode 100644 index 000000000..9c81c412e --- /dev/null +++ b/nonmatching/functional/func_002DDEC8.c @@ -0,0 +1,68 @@ +/* functional: high. Cheat-code entry: records the pressed pad button, and once 20 are in, searches the 147-entry code table; the near-miss draft below is behaviourally identical to retail, only the compiled loop shape differs. */ +/* M12, 2026-10-26: complete C, not linked. -O2 -G8. 532/520 bytes (us boot, 28 places). + * The top level (if/else on the pad mode, the bit chain, the five unlock ranges) matches retail. + * Residual: the code search loops. Retail does no strength reduction in the inner loop (mult + * j*i and sll j,1 every pass, test at the top after j++), compares table[0] with codes[(u8)i] + * before it, and keeps the outer bound 147 in a register (slt), so the bound is likely a + * variable. A goto inner loop gets the mult but still walks the table pointer. + */ +#include "common.h" +typedef union { u8 b[0x200]; u32 u[0x80]; u64 d[0x40]; } Pad; +extern char D_00138180[]; +extern int D_001ABCA0 __attribute__((sda)); +extern short D_001B2028[]; +extern u8 D_00261C08[]; +extern u8 D_001A7B30[] MACRO_ADDR; +extern u8 D_001A7AF8[] MACRO_ADDR; +extern u8 D_001395B8[] MACRO_ADDR; +extern u8 D_001A7A60[] MACRO_ADDR; +extern int D_001A8CEC MACRO_ADDR; +extern void func_002AD7B8(int); +extern int func_002E5E58(int, unsigned int, void *); +extern void func_002B0A18(int, int); + +/* Records the pressed button into the cheat buffer and, once it is full, unlocks the code it spells. */ +void func_002DDEC8(void) { + u32 buttons; + int value, i, j, match, found; + if ((((Pad *)D_00138180)->d[0x1C0 / 8] & 0xF) == 6) { + buttons = ((Pad *)D_00138180)->u[0x1C4 / 4]; + if (!(buttons & 0xF0A0)) return; + if (D_001ABCA0 >= 20) return; + if (buttons & 0x1000) value = 0; + else if (buttons & 0x4000) value = 1; + else if (buttons & 0x8000) value = 2; + else if (buttons & 0x2000) value = 3; + else value = (buttons & 0x80) ? 4 : 5; + D_001B2028[D_001ABCA0] = value; + D_001ABCA0++; + if (D_001ABCA0 != 20) return; + found = -1; + for (i = 2; i < 147; i++) { + match = 1; + if (D_001B2028[0] == D_00261C08[(u8)i]) { + for (j = 1; j < 20; j++) { + if (D_001B2028[j] != D_00261C08[(u8)(j * i + i)]) { match = 0; break; } + } + } else match = 0; + if (match) { found = i - 2; break; } + } + if (found == -1) return; + if (found < 56) { + D_001A7B30[found] = 1; + D_001A7AF8[found] = 1; + } else if ((unsigned)(found - 56) < 18) { + func_002AD7B8(found - 55); + } else if ((unsigned)(found - 74) < 6) { + D_001395B8[found - 74] = 1; + } else if ((unsigned)(found - 80) < 30) { + if (!D_001A7A60[found - 80]) { + D_001A7A60[found - 80] = 1; + func_002E5E58(1, 0, 0); + func_002B0A18(0x1233, -1); + D_001A8CEC = 1; + } + } + } else + D_001ABCA0 = 0; +} diff --git a/nonmatching/functional/func_002E35E0.c b/nonmatching/functional/func_002E35E0.c new file mode 100644 index 000000000..74afb4fb8 --- /dev/null +++ b/nonmatching/functional/func_002E35E0.c @@ -0,0 +1,61 @@ +/* functional: high. Same calls, frame writes and order as retail; the retail loop is a branch chain on the counter, written here as a switch. Case 0 and 1 are identical (retail's case 0 also stores a dead value to frame[2] that func_00283B98's result overwrites). */ +typedef float f32; + +extern f32 D_001ABDD4 __attribute__((sda)); +extern short *D_001B20C0; +extern int D_001B20D0[4]; +extern int D_001B2120[4]; +extern int D_001B20F0[]; +extern int D_001A72E0; + +extern void func_002E37B0(void); +extern void func_00283388(void *); +extern void func_00282C88(void *); +extern f32 func_00283B98(f32, f32); +extern void func_00283430(void *, void *); +extern void func_002E3940(int); +extern void func_002E3820(void); +extern void func_002FC560(int, long); + +void func_002E35E0(void) +{ + f32 frame[4]; + int i; + + i = 0; + func_002E37B0(); + D_001B20C0[2] = 0; + func_00283388(D_001B20F0); + func_00282C88(frame); + if (D_001B20C0[3] > 0) { + do { + f32 b = 0.0f; + switch (i) { + case 0: + case 1: + frame[1] = 0.0f; + break; + case 2: + b = -0.15f; + frame[1] = -0.075f; + break; + case 3: + frame[0] = -0.06f; + b = 0.125f; + frame[1] = 0.05f; + break; + } + frame[2] = func_00283B98(D_001ABDD4, b); + func_00283430(D_001B20F0, frame); + D_001B2120[0] = D_001B20D0[0]; + D_001B2120[1] = D_001B20D0[1]; + D_001B2120[2] = D_001B20D0[2]; + D_001B2120[3] = D_001B20D0[3]; + func_002E3940(i); + i++; + } while (i < D_001B20C0[3]); + } + func_002E3820(); + func_002FC560(0x47, 0x5360B); + func_002FC560(0x4E, 0x1000000 | (D_001A72E0 >> 13)); +} diff --git a/nonmatching/functional/func_002E3998.c b/nonmatching/functional/func_002E3998.c new file mode 100644 index 000000000..203ad5805 --- /dev/null +++ b/nonmatching/functional/func_002E3998.c @@ -0,0 +1,65 @@ +/* functional: high. Written from the assembly; same calls, arguments and order (fcheck SAME on 4 captured calls). buf is an alloca of (count & 0x1FFFFFFF) rounded up to 16 bytes, filled by func_002E4388. */ +typedef unsigned char u8; + +extern int D_001ABDE0; /* $gp - 0x3210: the buffer toggle, 0 or 1 */ +extern int D_001ABDD8[2]; /* $gp - 0x3218: the two buffers */ +extern char *D_001B22A8 __attribute__((sda)); + +extern void func_002E4388(void *, int, void *); +extern void func_0029CF58(int, int, int); +extern void func_0029CF78(void); +extern int func_002E3E54(int, int, int, int); +extern void func_002E40C8(int, int, int, int); + +#define REC(o, i) ((char *)(o) + 0x20 + (i) * 32) + +void func_002E3998(char *obj) +{ + int count = *(int *)obj; + unsigned int size = (((unsigned int)count << 3) >> 3) + 0xF; + u8 *buf; + int i; + + buf = (u8 *)__builtin_alloca((size >> 4) << 4); + if (count == 0) + return; + + func_002E4388(obj + 0x10, count, buf); + *(int *)(D_001B22A8 + 0) = 0x30000007; + *(int *)(D_001B22A8 + 4) = 0x139370; + *(int *)(D_001B22A8 + 8) = 0; + *(int *)(D_001B22A8 + 12) = 0x50000007; + D_001B22A8 = D_001B22A8 + 0x10; + D_001ABDE0 = 1 - D_001ABDE0; + if (buf[0] == 1) + func_0029CF58(*(int *)REC(obj, 0), (short)*(unsigned short *)(REC(obj, 0) + 0xE) >> 4, D_001ABDD8[D_001ABDE0]); + + if (*(int *)obj <= 0) + return; + i = 0; + do { + int chk; + + if (buf[i] == 1) + func_0029CF78(); + D_001ABDE0 = 1 - D_001ABDE0; + if (i + 1 < *(int *)obj) { + if (buf[i + 1] == 1) + func_0029CF58(*(int *)REC(obj, i + 1), (short)*(unsigned short *)(REC(obj, i + 1) + 0xE) >> 4, + D_001ABDD8[D_001ABDE0]); + chk = buf[i]; + } else { + chk = buf[i]; + } + if (chk == 1) { + char *r = REC(obj, i); + int t = D_001ABDD8[1 - D_001ABDE0]; + short h24 = *(short *)(r + 4), h26 = *(short *)(r + 6), h28 = *(short *)(r + 8); + short h2a = *(short *)(r + 0xA), h2c = *(short *)(r + 0xC); + + if (func_002E3E54(t + h28, 0x70002000, h24, h2c) == 0) + func_002E40C8(h26, t + h2c, t + h2a, 0x70002000); + } + i++; + } while (i < *(int *)obj); +} diff --git a/nonmatching/functional/func_002E3BD8.c b/nonmatching/functional/func_002E3BD8.c new file mode 100644 index 000000000..da62abab5 --- /dev/null +++ b/nonmatching/functional/func_002E3BD8.c @@ -0,0 +1,63 @@ +/* functional: medium. Same calls, arguments, writes and order as retail; the frame is a variable-length array sized as retail does (count masked to 29 bits, rounded up to 16), so only its stack placement differs. */ +typedef short s16; +typedef unsigned char u8; + +extern int *D_001B22A8; +extern int D_001ABDD8[2] __attribute__((sda)); +extern int D_001ABDE0 __attribute__((sda)); +extern int D_001A72E0; + +extern void func_002E4388(void *, int, void *); +extern int func_002E3E54(int, int, int, int); +extern void func_002E3FA0(int, int, int, int); +extern void func_0029CF58(int, int, int); +extern void func_0029CF78(void); +extern void func_002FC560(int, long); + +#define REC(o, k) ((char *)(o) + ((k) << 5)) +#define H(o, k, off) (*(s16 *)(REC(o, k) + (off))) +#define W20(o, k) (*(int *)(REC(o, k) + 0x20)) + +void func_002E3BD8(char *object) +{ + int count = *(int *)object; + unsigned n = (unsigned)count & 0x1FFFFFFF; + u8 frame[(n + 15) >> 4 << 4]; + int s0, s3; + + if (count == 0) + return; + func_002E4388(object + 0x10, count, frame); + D_001B22A8[0] = 0x30000007; + D_001B22A8[1] = 0x139300; + D_001B22A8[2] = 0; + D_001B22A8[3] = 0x50000007; + D_001B22A8 = D_001B22A8 + 4; + D_001ABDE0 = 1 - D_001ABDE0; + if (frame[0] == 1) + func_0029CF58(W20(object, 0), H(object, 0, 0x2E) >> 4, D_001ABDD8[D_001ABDE0]); + if (*(int *)object > 0) { + s0 = 0; + do { + if (frame[s0] == 1) + func_0029CF78(); + D_001ABDE0 = 1 - D_001ABDE0; + s3 = s0 + 1; + if (s3 < *(int *)object) { + if (frame[s3] == 1) + func_0029CF58(W20(object, s3), H(object, s3, 0x2E) >> 4, D_001ABDD8[D_001ABDE0]); + } + if (frame[s0] == 1) { + int base = D_001ABDD8[1 - D_001ABDE0]; + int a = base + H(object, s0, 0x2C); + int b = base + H(object, s0, 0x2A); + if (func_002E3E54(base + H(object, s0, 0x28), 0x70002000, + H(object, s0, 0x24), H(object, s0, 0x2C)) == 0) + func_002E3FA0(H(object, s0, 0x26), a, b, 0x70002000); + } + s0 = s3; + } while (s0 < *(int *)object); + } + func_002FC560(0x47, 0x3180B); + func_002FC560(0x4E, (long)(D_001A72E0 >> 13) | 0x1000000 | 0x100000000L); +} diff --git a/nonmatching/functional/func_002E48E8.c b/nonmatching/functional/func_002E48E8.c new file mode 100644 index 000000000..032941ff6 --- /dev/null +++ b/nonmatching/functional/func_002E48E8.c @@ -0,0 +1,46 @@ +/* functional: high. Clears the 0x16C0-byte record table at 0x188660 (header qwords, two words at +0x40/+0x44, then the first word and flag byte of each 0x70-byte record), then configures the same engine-side slots in the same call order; retail clears with sq, codegen only. */ +typedef unsigned char u8; +extern u8 D_00188660[]; +extern int D_001A7B98 __attribute__((sda)); +extern void func_00131DE8(int); +extern void func_00132938(int); +extern void func_00132968(int, int); +extern void func_001329B0(int, int, int); +extern void func_00132888(int, int); +extern void func_002E4860(void); +extern void func_001328C0(int, int *); +void func_002E48E8(void) { + int cfg[5]; + u8 *p; + int i; + for (i = 3; i >= 0; i--) { + ((long long *)D_00188660)[i * 2] = 0; + ((long long *)D_00188660)[i * 2 + 1] = 0; + } + *(int *)(D_00188660 + 0x40) = 0; + *(int *)(D_00188660 + 0x44) = 0; + p = D_00188660; + do { + *(int *)(p + 0x70) = 0; + p[0x74] = 0; + p += 0x70; + } while (p < D_00188660 + 0x16C0); + func_00131DE8(2); + func_00132938(D_001A7B98 == 0); + func_00132968(0, 1); + func_001329B0(1, 0x18, 0x2F); + func_001329B0(2, 0x18, 0x2F); + func_001329B0(4, 0x18, 0x2F); + func_001329B0(5, 0x18, 0x2F); + func_001329B0(6, 0x18, 0x2F); + func_002E4860(); + func_00132888(0, *(int *)(D_00188660 + 0x48)); + func_00132888(1, *(int *)(D_00188660 + 0x4C)); + func_00132888(2, *(int *)(D_00188660 + 0x50)); + func_00132888(3, *(int *)(D_00188660 + 0x54)); + func_00132888(4, *(int *)(D_00188660 + 0x58)); + func_00132888(5, *(int *)(D_00188660 + 0x5C)); + func_00132888(6, *(int *)(D_00188660 + 0x50)); + cfg[0] = 6; cfg[1] = 0x3B; cfg[2] = 0x6666; cfg[3] = 0x8000; cfg[4] = 0x8000; + func_001328C0(0, cfg); +} diff --git a/nonmatching/functional/func_002E4AC8.c b/nonmatching/functional/func_002E4AC8.c new file mode 100644 index 000000000..d54101029 --- /dev/null +++ b/nonmatching/functional/func_002E4AC8.c @@ -0,0 +1,396 @@ +/* functional: medium. Transcribed from retail branch by branch (52 voice entries of 0x70 bytes from 0x188660, three scratch arrays of 52 words); the call arguments are the retail ones. Risk: a misread offset in the long loops, not the structure. */ +extern char D_00188660[]; +extern char D_00189F70[]; +extern char D_001B5340[]; +extern float D_0018A150; +extern int D_001B5600; +extern int D_001A8BB0; +extern int D_001A8C88; +extern int D_001A8B80; +extern int D_001B1688; +extern short D_00152C20; + +extern void func_00132028(void); +extern void func_00133710(int, int, int, int, int); +extern void func_00133788(int, int, int, int); +extern void func_002E4478(void *); +extern float func_00282DF0(void *); +extern void func_00282CF0(void *, void *, void *); +extern void func_00282CC0(void *, void *, void *); +extern float func_00283CE0(int); +extern void func_00282D30(void *, void *, float); +extern void func_00132888(int, int); +extern void func_00282A60(void *, int, int); +extern void func_00283098(void *, void *); +extern void func_00282F20(void *, void *, float); +extern float func_00282DB0(void *, void *); +extern int func_002E46C0(void *, void *); +extern int func_00283150(void *, int); +extern int func_002E4528(void *, void *); +extern int func_002E4708(void *, void *); +extern int func_00283CF0(float); +extern int func_00133988(int); +extern void func_00132A70(int); +extern void func_00132B58(int, int, int); +extern void func_00132BC0(int, int, int, int, int, int, int, int); +extern void func_00132A10(int, int, int, int, int, int, int, int); +extern void func_002B7578(void); +extern void func_00133230(void); +extern void func_00133220(void); + +#define I(p, o) (*(int *)((char *)(p) + (o))) +#define U8(p, o) (*(unsigned char *)((char *)(p) + (o))) +#define H(p, o) (*(short *)((char *)(p) + (o))) +#define UH(p, o) (*(unsigned short *)((char *)(p) + (o))) +#define FL(p, o) (*(float *)((char *)(p) + (o))) + +static void copy16(void *dst, void *src) +{ + ((long *)dst)[0] = ((long *)src)[0]; + ((long *)dst)[1] = ((long *)src)[1]; +} + +int func_002E4AC8(void) +{ + char *B = D_00188660; + char *tab = D_00188660 + 0xB4; /* 0x188714: a 36-byte row per voice at +0x70*i */ + float f22 = 0.0f; + float f20, f0, f4; + int flagA = D_001B5600; + int i, k, n, s0, a3, s3, s1, s4v, s7v; + int cnt; + unsigned char spbuf[16] __attribute__((aligned(16))); + unsigned char vec[16] __attribute__((aligned(16))); + unsigned char tmp[16] __attribute__((aligned(16))); + unsigned char qs[16] __attribute__((aligned(16))); + int arrA[52], arrB[52], arrC[52]; + char *E; + + if (flagA != 0) + f22 = D_0018A150; + func_00132028(); + + { + int a0 = U8(B, 0x6B); + if (a0 & 8) + func_00133710(2, 0, 0, 0, 0); + else if (a0 & 0x13) + func_00133710(2, U8(B, 0x68), I(B, 0x64), U8(B, 0x69), U8(B, 0x6A)); + else if (a0 & 4) + func_00133788(2, I(B, 0x64), 0xC, 3); + } + U8(B, 0x6B) = 0; + + if (D_001A8BB0 == 0 || D_001A8BB0 == 2) { + if (D_001A8C88 == 0) + func_002E4478(spbuf); + } + ((long *)vec)[0] = 0; + ((long *)vec)[1] = 0; + + /* ring of four quads at B[0..0x3F], index at B+0x40 */ + k = (I(B, 0x40) + 1) % 4; + I(B, 0x40) = k; + copy16(B + k * 16, D_001B5340); + + s4v = 0; + f0 = func_00282DF0(D_00189F70); + a3 = I(B, 0x40); + s0 = (a3 + 3) % 4; + f20 = f0; + if (s0 != a3) { + do { + func_00282CF0(tmp, B + a3 * 16, B + s0 * 16); + f0 = func_00282DF0(tmp); + if (!(f0 < f20 + 1.0f)) + break; + func_00282CC0(vec, vec, tmp); + s4v++; + a3 = s0; + s0 = (a3 + 3) % 4; + } while (s0 != I(B, 0x40)); + } + if (s4v >= 2) { + f0 = func_00283CE0(s4v); + func_00282D30(vec, vec, 1.0f / f0); + } + + { + int P1 = D_001A8B80; + int mode = D_001A8BB0; + f4 = (float)I(B, 0x48); + if (P1 == 0 && H(B, 0x17B8) <= 0) { + if (mode == 2 || (mode == 6 && (D_00152C20 == 3 || D_00152C20 == 0))) { + func_00132888(1, 0); + func_00132888(0, I(B, 0x48) / 2); + func_00132888(3, I(B, 0x54) / 2); + } else { + func_00132888(1, flagA ? (I(B, 0x4C) * 3) / 5 : I(B, 0x4C)); + func_00132888(0, I(B, 0x48)); + func_00132888(3, I(B, 0x54)); + } + } else { + short c; + f0 = 1.0f; + if (P1 != 0) { + if (H(B, 0x17B8) == 0) { + if (P1 == 2) { + H(B, 0x17B8) = 2; + } else { + H(B, 0x17BA) = 30; + H(B, 0x17B8) = 1; + } + } + } else if (H(B, 0x17B8) == 2) { + H(B, 0x17BA) = 30; + H(B, 0x17B8) = 3; + } + c = UH(B, 0x17BA) - 1; + UH(B, 0x17BA) = c; + switch (H(B, 0x17B8)) { + case 2: + f0 = 4.0f; + break; + case 1: + f0 = ((1.0f - (float)c * 0.033333335f) * 3.0f) + 1.0f; + if (!(c > 0)) + H(B, 0x17B8) = 2; + break; + case 3: + f0 = (float)c * 0.10000001f + 1.0f; + if (!(c > 0)) + H(B, 0x17B8) = 0; + break; + default: + break; + } + f4 = f4 / f0; + s1 = (int)f4; + func_00132888(0, s1); + func_00132888(3, s1); + } + } + + func_00132888(2, I(B, 0x50)); + func_00132888(5, I(B, 0x5C)); + func_00132888(6, I(B, 0x50)); + func_00282A60(arrA, 0, 0xD0); + func_00282A60(arrC, 0, 0xD0); + func_00282A60(arrB, 0, 0xD0); + + /* pass 1: classify every voice */ + for (i = 0; i < 52; i++) { + int st, obj, a0, cap; + char *va; + E = B + 0x70 * i; + st = U8(E, 0x74); + if (st != 7) { + if (I(E, 0x70) == 0 || I(E, 0x70) == -1) + continue; + } + obj = I(E, 0x88); + a0 = 0; + if (obj != 0) { + int t = U8(obj, 0x20); + if (t == 0xFE || t == 0xFD) + a0 = 1; + } + if (a0 != 0) + I(E, 0x88) = 0; + st = U8(E, 0x74); + if (st == 4) { + arrA[i] = 0x20; + continue; + } + if (st != 6 && a0 != 0 && U8(I(E, 0x78), 0x18) != 0) { + arrA[i] = 0x20; + continue; + } + if (D_001A8BB0 != 0 && D_001A8BB0 != 2 && D_001A8BB0 != 6 && U8(E, 0x74) != 7) { + arrA[i] = 0x10; + continue; + } + va = B + 0x90 + 0x70 * i; + obj = I(E, 0x88); + if (obj == 0 || (U8(E, 0x75) & 8)) { + ((long *)tmp)[0] = 0; + ((long *)tmp)[1] = 0; + } else if (U8(E, 0x75) & 0x40) { + func_00283098(qs, E + 0xA0); + func_00282CC0(qs, qs, (char *)obj + 0x10); + func_00282CF0(tmp, qs, va); + copy16(va, qs); + } else { + float x = FL(E, 0x98); + FL(E, 0x98) = x - 1.0f; + func_00282CF0(tmp, (char *)obj + 0x10, va); + copy16(va, (char *)I(E, 0x88) + 0x10); + FL(E, 0x98) = FL(E, 0x98) + 1.0f; + } + func_00282CF0(tmp, tmp, vec); + func_00282CF0(qs, D_001B5340, va); + func_00282F20(qs, qs, 1.0f); + arrB[i] = 0; + *(float *)&arrB[i] = func_00282DB0(qs, tmp); + if (U8(E, 0x75) & 0x10) { + cap = 0x400; + arrC[i] = H(E, 0x80); + } else { + cap = func_002E46C0(E + 0x70, va); + arrC[i] = (H(E, 0x80) * cap) / 1024; + if (!(U8(I(E, 0x78), 0x19) & 2)) + arrA[i] |= 8; + } + if (flagA != 0) { + if (!(U8(I(E, 0x78), 0x19) & 4)) { + if (f22 < FL(E, 0x98)) + arrC[i] = arrC[i] / 2; + } + } + arrA[i] |= 1; + if (cap < 0x20 && arrC[i] < 0x20) { + if (U8(E, 0x75) & 4) { + arrA[i] = 0x20; + continue; + } + } + if (U8(E, 0x75) & 1) + continue; + { + int old = arrA[i]; + arrA[i] = old | 2; + if (!(U8(E, 0x75) & 0x20) && !(U8(I(E, 0x78), 0x19) & 0x10)) + arrA[i] = old | 6; + } + } + + /* pass 2: loudness scaling for voices flagged 8 */ + for (i = 0; i < 52; i++) { + int sc; + E = B + 0x70 * i; + if (!(arrA[i] & 8)) + continue; + if (U8(E, 0x74) == 7) { + if (!(D_001A8BB0 == 0 || D_001A8BB0 == 2)) + continue; + if (D_001A8C88 != 0) + continue; + if (D_001B1688 != I(B, 0x1738)) { + for (k = 0; k < 6; k++) + func_002E4478(B + 0x1740 + 16 * k); + I(B, 0x1738) = D_001B1688; + } + s3 = 0; + s0 = 0; + n = 0; + do { + int r = func_002E4528(E + 0x70, B + 0x1740 + 16 * n); + int j; + tab[0x70 * i + s0] = r; + for (j = 1; j <= 5; j++) + tab[0x70 * i + s0 + j] = tab[0x70 * i + s0]; + if (tab[0x70 * i + s0] != 0) + s3 += 6; + n++; + s0 += 6; + } while (s0 < 0x24); + sc = s3; + } else { + if ((D_001A8BB0 == 0 || D_001A8BB0 == 2) && D_001A8C88 == 0) { + int cond; + int r; + UH(E, 0x82) = (UH(E, 0x82) + 1) % 36; + if (U8(E, 0x75) & 4) + cond = (i ^ D_001B1688) & 1; + else + cond = ((D_001B1688 ^ i) & 3) == 0; + if (cond) { + r = func_002E4528(E + 0x70, spbuf); + tab[0x70 * i + UH(E, 0x82)] = r; + } else { + tab[0x70 * i + UH(E, 0x82)] = tab[0x70 * i + (UH(E, 0x82) + 35) % 36]; + } + } + sc = func_00283150(tab + 0x70 * i, 0x24); + } + if (sc >= 0x24) { + arrC[i] = 0; + } else if (sc >= 0x13) { + arrC[i] = ((0x24 - sc) * arrC[i]) / 0x12; + } + } + + /* pass 3: start, update or stop the voices */ + for (i = 0; i < 52; i++) { + int a0 = arrA[i]; + int s2; + E = B + 0x70 * i; + if (a0 == 0) + continue; + s2 = I(E, 0x70); + I(E, 0x70) = -1; + if (a0 & 0x20) { + if (U8(E, 0x74) == 7) { + U8(E, 0x74) = 0; + I(E, 0x88) = 0; + I(E, 0x8C) = 0; + } else { + func_00132A70(s2); + U8(E, 0x74) = 6; + func_00132B58(s2, 0x2E60E8, (int)(B + 0x70 * i + 0x70)); + } + continue; + } + if (a0 & 0x10) { + func_00132B58(s2, 0x2E60E8, (int)(B + 0x70 * i + 0x70)); + continue; + } + { + int s4 = I(E, 0x84); + s1 = s4 ? 0x11 : 1; + s7v = 0; + s3 = 0; + if (a0 & 2) { + s1 |= 6; + s7v = func_002E4708(B + 0x70 * i + 0x70, B + 0x70 * i + 0x90); + } + if (arrA[i] & 4) { + float m = (U8(E, 0x75) & 2) ? 500.0f : 300.0f; + s1 |= 8; + s3 = func_00133988(func_00283CF0(*(float *)&arrB[i] * m)); + } + if (flagA != 0 && !(U8(I(E, 0x78), 0x19) & 8)) { + s1 |= 8; + s3 -= 0x5F4; + } + if (U8(E, 0x74) != 7) { + func_00132BC0(s2, s1, arrC[i], s7v, s3, s4, 0x2E60E8, (int)(B + 0x70 * i + 0x70)); + } else { + int o = I(E, 0x78); + int x; + if (o == 0) { + I(E, 0x88) = 0; + I(E, 0x8C) = 0; + U8(E, 0x74) = 0; + } else { + x = I(B, 0x17A0 + 4 * I(o, 0x1C)); + if (x == -1 || x == 0) { + I(E, 0x88) = 0; + I(E, 0x8C) = 0; + U8(E, 0x74) = 0; + } else { + U8(E, 0x74) = 1; + func_00132A10(x, H(o, 0x1A), arrC[i], s7v, s3, s4, 0x2E60A0, (int)(B + 0x70 * i + 0x70)); + } + } + } + } + } + + func_002B7578(); + func_00133230(); + func_00132028(); + func_00133220(); + I(B, 0x6C) = 0; + return 0; +} diff --git a/nonmatching/functional/func_002E5AD0.c b/nonmatching/functional/func_002E5AD0.c new file mode 100644 index 000000000..b3b2eb405 --- /dev/null +++ b/nonmatching/functional/func_002E5AD0.c @@ -0,0 +1,105 @@ +/* functional: medium. fcheck SAME on all captured calls; the draft below is the near miss, differing from retail only in scheduling and register allocation. */ +/* func_002E5AD0 - 552 bytes. Worker attempt, 27 October 2026 (draft below). + + Not at handoff quality (476 B vs 552). Logic and field offsets decoded + (draft.c); needs a new file src/game/002E10B8-qcopy.c with cflags `-O2 -G0 + -ffunction-sections -frac2-qcopy -frac2-qcopy-barrier`. + Problem: retail recomputes `mult a0,s0,a0` (index*0x70, constant 0x70 + reloaded) in each basic block/branch arm; mine CSEs it once into a2 across + the whole function, and the first condition (`sltiu v1,b18,1` + `xori`) is + shaped differently. Pool = D_00188660, entries 0x70 apart, fields start + 0x70 after the entry base. + Second pass: tried 3-way if/else-if chain (src / vec / neither), pool as + array of 0x70-byte PoolEntry indexed [i+1], xori/sltiu variants for the + first test; size stays 476 vs 552. Retail ends with a single `return index` + epilogue (addiu s0,-1 / daddu v0,s0) and recomputes `mult` per basic block; + mine CSEs the multiply. */ + +#include "include_asm.h" +#include "common.h" + + +/* One 0x70-byte record of the pool at D_00188660. The first record of the pool is a header; the + entries start at the second one. */ +typedef struct { + int f70; + u8 state; + u8 flags; + u8 pad06[2]; + char *owner; + u16 id; + short f7e; + short arg; + short f82; + int f84; + int f88; + int f8c; + float f90[4]; + float fa0[4]; + float fb0; + u8 pad_b4[0x2C]; +} PoolEntry2E5AD0; + +typedef struct { + u8 pad00[0x10]; + int start; + int end; + u8 b18; + u8 pad19; + u16 id; +} Source2E5AD0; + +extern PoolEntry2E5AD0 D_00188660[]; + +extern int func_002E5A50(int); +extern void func_00282C88(void *); +extern int func_002E46C0(void *, void *); +extern int func_002A77E0(int); + +#define SLOT(i) (&D_00188660[(i) + 1]) + +int func_002E5AD0(Source2E5AD0 *obj, int flags, char *src, char *vec, int arg) +{ + int index; + int distance; + int end; + int none = obj->b18 == 0; + + if ((flags & 4) ? none : none ^ 1) + return -1; + index = func_002E5A50((int)src); + if (index >= 0x34) + return -1; + SLOT(index)->owner = (char *)obj; + SLOT(index)->fb0 = -1.0f; + SLOT(index)->id = obj->id; + SLOT(index)->f7e = -1; + SLOT(index)->arg = arg; + SLOT(index)->f88 = 0; + SLOT(index)->f8c = 0; + qzero(SLOT(index)->fa0); + if (src != 0) { + qcopy(SLOT(index)->f90, src + 0x10); + SLOT(index)->f90[2] += 1.0f; + } else if (vec != 0) { + qcopy(SLOT(index)->f90, vec); + } else { + flags |= 0x11; + func_00282C88(SLOT(index)->f90); + } + if (flags & 0x10) + distance = arg; + else + distance = func_002E46C0((u8 *)SLOT(index) - 0x20, SLOT(index)); + if (distance < 0x20) + return -1; + SLOT(index)->flags = flags; + SLOT(index)->state = 7; + SLOT(index)->f82 = 0; + end = obj->end; + if (end != obj->start) + end = func_002A77E0(end - obj->start) + obj->start; + SLOT(index)->f70 = -1; + SLOT(index)->f84 = end; + return index; +} diff --git a/nonmatching/functional/func_002E6178.c b/nonmatching/functional/func_002E6178.c new file mode 100644 index 000000000..7945bce65 --- /dev/null +++ b/nonmatching/functional/func_002E6178.c @@ -0,0 +1,84 @@ +/* functional: medium. One captured call. Spawns the hero equipment pieces from the equipment bits (same calls and writes in the same order); near-miss draft of the retail code, differing only in scheduling and register allocation. */ +#include "common.h" + +typedef struct { + char pad00[0x30]; + unsigned char alpha; + char pad31; + unsigned short drawMask; + unsigned short flags; + char pad36[0x63 - 0x36]; + unsigned char mode; + char pad64[4]; + char *state; +} EquipPiece; + +extern EquipPiece *func_0029EFD0(int); +extern void func_002E6120(void); +extern EquipPiece *D_00152C00; +extern unsigned int D_001A7AF0 MACRO_ADDR; +extern short D_001A8C28[], D_001A8C30[], D_001A8C38[], D_001A8C40[]; +extern short D_001A8C48[], D_001A8C50[], D_001A8C58[]; +extern EquipPiece *D_00214920[]; + +/* Spawns the hero's equipment pieces from the equipment bits and flags them. */ +void func_002E6178(EquipPiece *base) +{ + EquipPiece **parts; + EquipPiece **p; + EquipPiece *piece; + char *state; + unsigned int equipment; + int chassis, partA, partB, partC, partD, partE, partF, count; + + if (!base) + base = D_00152C00; + func_002E6120(); + count = 5; + base->alpha = 255; + parts = D_00214920; + equipment = D_001A7AF0; + chassis = (equipment >> 2) & 3; + partA = (equipment >> 4) & 1; + partB = (equipment >> 6) & 1; + partC = (equipment >> 7) & 3; + partD = (equipment >> 9) & 1; + partE = (equipment >> 10) & 3; + partF = (equipment >> 14) & 3; + parts[0] = func_0029EFD0(D_001A8C28[chassis]); + parts[1] = func_0029EFD0(D_001A8C28[chassis]); + parts[1]->flags |= 0x8000; + parts[2] = func_0029EFD0(D_001A8C30[partA]); + parts[3] = func_0029EFD0(D_001A8C38[partB]); + parts[4] = func_0029EFD0(D_001A8C38[partB]); + parts[4]->flags |= 0x8000; + if (D_001A8C40[partC]) { + parts[5] = func_0029EFD0(D_001A8C40[partC]); + parts[6] = func_0029EFD0(D_001A8C40[partC]); + count = 7; + parts[6]->flags |= 0x8000; + } + parts[count++] = func_0029EFD0(D_001A8C48[partD]); + piece = parts[count++] = func_0029EFD0(D_001A8C48[partD]); + piece->flags |= 0x8000; + parts[count++] = func_0029EFD0(D_001A8C50[partE]); + piece = parts[count++] = func_0029EFD0(D_001A8C50[partE]); + piece->flags |= 0x8000; + if ((D_001A7AF0 >> 12) & 1) + parts[count++] = func_0029EFD0(0x10E2); + if ((D_001A7AF0 >> 13) & 1) + parts[count++] = func_0029EFD0(0x10E4); + if (partF) + parts[count] = func_0029EFD0(D_001A8C58[partF]); + for (p = parts; *p; p++) { + if ((*p)->mode) + (*p)->mode = 8; + (*p)->flags |= 0x124; + (*p)->drawMask = 0x1FF; + state = (*p)->state; + *(char **)(state + 8) = state + 0x20; + } + base->flags |= 0x20; + state = base->state; + *(char **)(state + 8) = state + 0x20; +} diff --git a/nonmatching/functional/func_002E64A0.c b/nonmatching/functional/func_002E64A0.c new file mode 100644 index 000000000..0e97ab02c --- /dev/null +++ b/nonmatching/functional/func_002E64A0.c @@ -0,0 +1,158 @@ +/* functional: high. Hand-written from the assembly (not byte-matched: 25 calls, two loops); same reads, writes and calls in order; fcheck SAME on 4 calls. The third argument to func_00282E80 is passed as retail does. */ +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef int Quad __attribute__((mode(TI))); + +extern void *D_00214920[]; +extern char D_00214970[]; +extern char D_00214978[]; +extern int D_001A8BB0; +extern int D_001A79F0; +extern int D_001A9BC0; +extern float D_001ACF94; +extern float D_001ACF98; +extern char D_001ACF9C[]; +extern char D_001ACF28[], D_001ACF38[], D_001ACF48[], D_001ACF58[], D_001ACF68[], D_001ACF78[], D_001ACF88[]; +extern char D_00199E20[]; +extern char D_00199EA0[]; + +extern void func_002E6178(void); +extern float func_002A89A8(void *, int, float); +extern void func_002B41A8(char *, void *, int, int, void *, void *, void *, void *, void *, void *, void *); +extern void func_00282D60(void *, void *, float); +extern int func_00283DB8(void *, float); +extern float func_00282E80(void *, void *, void *); +extern int func_002761F8(void *, void *, int, int, int); +extern void func_00282CF0(void *, void *, void *); +extern void func_00282F20(void *, void *, float); +extern void func_00282CC0(void *, void *, void *); +extern void func_002AA548(void *, void *, float, float, float, float); +extern void func_0029FC20(char *, int, void *); +extern void func_0029FCC0(char *, int, void *); +extern void func_00283098(void *, void *, void *); +extern void func_002AB6D8(void *, void *); +extern void func_002A10E8(void *); +extern void func_002AD6F0(void *, void *, void *, void *); +extern void func_002B44C0(char *, void *, void *); + +void func_002E64A0(char *mob) +{ + float v20[4] __attribute__((aligned(16))); + float v30[4] __attribute__((aligned(16))); + float v40[12] __attribute__((aligned(16))); + float v80[4] __attribute__((aligned(16))); + float v90[4] __attribute__((aligned(16))); + float vA0[4] __attribute__((aligned(16))); + void *best = 0; + int i, t, s0; + char *e; + + if (D_00214920[0] == 0) { + func_002E6178(); + if (D_001A8BB0 == 0) { + float d = func_002A89A8(mob + 0x10, 0, 0.5f); + if (d != 0.0f) + *(float *)(mob + 0x18) = d + D_001ACF94; + D_001ACF98 = *(float *)(mob + 0x18); + } + func_002B41A8(mob, D_00214970, 0, 0, D_001ACF58, D_001ACF68, D_001ACF28, D_001ACF88, + D_001ACF78, D_001ACF38, D_001ACF48); + } + + if (D_001A8BB0 == 0) { + func_00282D60(v20, mob, 0.0009765625f); + v20[3] = v20[3] + 1.0f; + if (func_00283DB8(v20, 50.0f) == -1) + return; + if (D_001A9BC0 == 0) { + char *s1 = D_00199EA0; + char *s5 = mob + 0x10; + float dist = func_00282E80(s5, s1, v40); + s0 = 0; + if (dist < 6.0f) { + if (*(u8 *)(mob + 0xBC) != 0) { + *(Quad *)v20 = *(Quad *)(s1 + 0x60); + v20[2] = v20[2] + 2.0f; + if (func_002761F8(s1 + 0x60, v20, 0, *(int *)(s1 + 0x2210), 0) == 0) { + func_00282CF0(v30, s5, s1); + *(int *)&v30[2] = 0; + func_00282F20(v30, v30, 0.4f); + func_00282CC0(v20, v20, v30); + s0 = func_002761F8(s1 + 0x60, v20, 0, *(int *)(s1 + 0x2210), 0) != 0; + } else { + s0 = 1; + } + } else { + *(Quad *)v20 = *(Quad *)(s1 + 0x60); + v20[2] = v20[2] + 2.0f; + s0 = func_002761F8(s1 + 0x60, v20, 0, *(int *)(s1 + 0x2210), 0) != 0; + } + } + *(u8 *)(mob + 0xBC) = s0; + if (s0) + func_002AA548(D_00214978, D_001ACF9C, D_001ACF98 + 0.75f, 0.0005555555690079927f, 0.0005555555690079927f, 0.01666666753590107f); + else + func_002AA548(D_00214978, D_001ACF9C, D_001ACF98, 0.0005555555690079927f, 0.0005555555690079927f, 0.01666666753590107f); + } + } + func_0029FC20(mob, 1, v40); + func_0029FCC0(mob, 1, v20); + + if (D_00214920[0] != 0) { + i = 0; + do { + e = (char *)D_00214920[i]; + if (*(u16 *)(mob + 0x34) & 1) + *(u16 *)(e + 0x34) = *(u16 *)(e + 0x34) | 0x41; + else + *(u16 *)(e + 0x34) = *(u16 *)(e + 0x34) & 0xFFBE; + e = (char *)D_00214920[i]; + *(Quad *)v30 = *(Quad *)(e + 0x10); + *(Quad *)v80 = *(Quad *)(e + 0xF0); + *(Quad *)(e + 0x10) = *(Quad *)(mob + 0x10); + for (t = 0; t < 3; t++) { + e = (char *)D_00214920[i]; + *(float *)(e + 0xC0 + t * 16) = v40[t * 4]; + *(float *)(e + 0xC4 + t * 16) = v40[t * 4 + 1]; + *(float *)(e + 0xC8 + t * 16) = v40[t * 4 + 2]; + } + if (D_001A8BB0 == 2 && D_001A79F0 == 5) { + e = (char *)D_00214920[i]; + *(Quad *)vA0 = 0; + vA0[0] = -0.33719998598098755f; + vA0[1] = -0.5364000201225281f; + vA0[2] = -1.79489004611969f; + func_00283098(vA0, vA0, e + 0xC0); + func_00282CC0(v90, v20, vA0); + *(Quad *)(e + 0x10) = *(Quad *)v90; + } + e = (char *)D_00214920[i]; + func_002AB6D8(e + 0xC0, e + 0xF0); + func_002A10E8(e); + e = (char *)D_00214920[i]; + { + char *obj = *(char **)(e + 0x68); + func_00282CF0(v90, e + 0x10, v30); + func_002AD6F0(obj + 0x20, v90, v80, (char *)D_00214920[i] + 0xF0); + } + if (*(int *)(D_00199E20 + 0x340) >= 2) { + if (*(char **)(D_00199E20 + 0x33C) == (char *)D_00214920[i]) + best = D_00199EA0; + } + if (D_001A8BB0 == 0) { + if (*(int *)(mob + 0x98) != 0) { + e = (char *)D_00214920[i]; + *(int *)(e + 0x98) = *(int *)(*(char **)(e + 0x24) + 0x10); + } + } else { + e = (char *)D_00214920[i]; + *(int *)(e + 0x98) = 0; + *(int *)(mob + 0x98) = 0; + } + i++; + } while (D_00214920[i] != 0); + } + if (D_001A8BB0 == 0) + func_002B44C0(mob, D_00214970, best); +} diff --git a/nonmatching/functional/func_002E6A60.c b/nonmatching/functional/func_002E6A60.c new file mode 100644 index 000000000..0cb805929 --- /dev/null +++ b/nonmatching/functional/func_002E6A60.c @@ -0,0 +1,40 @@ +/* functional: medium. Same calls with the same arguments, the same nine negated floats stored to the same addresses, the 16-byte copy done as four word copies, returns the byte at record+0xC; only FPU register use and store order differ. */ +extern int D_001B8840[]; +extern float D_001B90F0; +extern int D_001B5340[]; +extern float D_001B5200[]; + +extern void func_0027AB40(void); +extern void func_00129178(void *); +extern void func_001292C0(void *, void *, float); +extern void func_00129368(void *, void *, float); +extern void func_00129218(void *, void *, float); + +int func_002E6A60(void) +{ + char *rec = (char *)D_001B8840[0x68 / 4] + (D_001B8840[0x38 / 4] << 5); + unsigned char b = *(unsigned char *)(rec + 0xC); + float m[16]; + float *out = D_001B5200; + + D_001B90F0 = *(float *)(rec + 0x1C); + func_0027AB40(); + D_001B5340[0] = *(int *)(rec + 0); + D_001B5340[1] = *(int *)(rec + 4); + D_001B5340[2] = *(int *)(rec + 8); + D_001B5340[3] = *(int *)(rec + 12); + func_00129178(m); + func_001292C0(m, m, *(float *)(rec + 0x10)); + func_00129368(m, m, *(float *)(rec + 0x14)); + func_00129218(m, m, *(float *)(rec + 0x18)); + out[0xDC] = -m[8]; + out[0xDD] = -m[9]; + out[0xDE] = -m[10]; + out[0xE0] = -m[0]; + out[0xE1] = -m[1]; + out[0xE2] = -m[2]; + out[0xE4] = m[4]; + out[0xE5] = m[5]; + out[0xE6] = m[6]; + return b; +} diff --git a/nonmatching/functional/func_002E76C0.c b/nonmatching/functional/func_002E76C0.c new file mode 100644 index 000000000..1f430172b --- /dev/null +++ b/nonmatching/functional/func_002E76C0.c @@ -0,0 +1,120 @@ +/* functional: medium. Hand-decoded from the assembly (not attempted as a match): the same calls in the same order with the same arguments, and the same stores into the 0x50-byte record at sp+0x80 that func_002ED2B8 reads. The record's fields are named by offset only; the unwritten words (0xB0, 0xB8, 0xC8) are left as they are. Argument types of the vector helpers are guessed from other callers. */ +extern void func_00283130(void *dst, long src); +extern void func_00282D60(void *dst, void *src, float scale); +extern void func_00282CA8(void *dst, void *a, void *b); +extern void func_00283958(void *src, void *dst); +extern void func_00283738(void *dst, void *a, void *b); +extern void func_00283098(void *dst, void *a, void *b); +extern void func_00282CC0(void *dst, void *a, void *b); +extern void func_00282D30(void *dst, void *src, float angle); +extern void func_00282C88(void *dst); +extern int func_00283CF0(float); +extern int func_00283D38(int, int, float); +extern void func_002E7570(void *src, void *matrix, int count, unsigned int colour, float from, float to, float radius); +extern void func_002ED368(void *); +extern void func_002ED2B8(void *, void *); +extern float func_002A7878(float, float); +extern int func_002A7820(int, int); +extern float func_002A7940(void); +extern float D_001AC090[]; /* 16-byte entries, the float at +0xC is the scale */ +extern int D_001B2150[]; +extern int D_001B8874; + +typedef struct { + float v[4]; /* 0x80 */ + int q[4]; /* 0x90 */ + float f0; /* 0xA0 */ + float f4; /* 0xA4 */ + float f8; /* 0xA8 */ + int fc; /* 0xAC */ + int pad10; /* 0xB0 */ + int a14; /* 0xB4 */ + int pad18; /* 0xB8 */ + int a1c; /* 0xBC */ + int a20; /* 0xC0 */ + int a24; /* 0xC4 */ + int pad28; /* 0xC8 */ + int a2c; /* 0xCC */ +} Rec2E76C0; + +void func_002E76C0(unsigned char *obj, int idx, int flag) +{ + int m0[4]; + int m1[4]; + int m2[4]; + int d[4], e[4], f[4]; + Rec2E76C0 r; + float *tab; + float sc, a5, c04, c06; + float p, q2, r2, s2, t2, u2, w2; + int c1, c2, col0, col1, v; + int i; + + func_00283130(d, *(long *)(*(int *)(obj + 0x58) + 0x10)); + func_00283130(e, *(long *)(*(int *)(obj + 0x5C) + 0x10)); + func_00282D60(d, d, (1.0f - *(float *)(obj + 0x44)) * 3.0517578125e-05f); + func_00282D60(e, e, *(float *)(obj + 0x44) * 3.0517578125e-05f); + func_00282CA8(f, d, e); + func_00283958(f, m2); + func_00283738(m2, obj + 0xC0, m2); + tab = &D_001AC090[idx * 4]; + func_00283098(m0, tab, m2); + func_00282CC0(m0, m0, obj + 0x10); + + sc = (float)obj[0x95] * (tab[3] * 0.0078125f); + a5 = sc * 5.0f; + c04 = 0.039999999f; + c06 = 0.059999999f; + p = sc * c04 + c04; /* f27 */ + q2 = sc * c06 + c06; /* f24 */ + c1 = func_00283CF0(a5 + 25.0f); + c2 = func_00283CF0(a5 + 15.0f); + { + float k75 = 0.074999996f; /* f28 */ + float x23 = sc * k75; + float x22 = sc * 0.375f; + float x21 = sc * 0.099999994f; + float x1 = sc * 0.024999998f; + float x0 = sc * 0.024999991f; + x22 = x22 + 0.375f; + x21 = x21 + 0.099999994f; + s2 = x1 + 0.024999998f; /* f26 */ + r2 = x0 + 0.175f; /* f25 */ + col0 = func_00283D38(0x40204080, 0x40205080, sc); + x23 = x23 + k75; + col1 = func_00283D38(-1, -1, sc); + func_00282D30(m1, m2, -q2); + func_002E7570(m0, m1, c1, col0, x22, x21, x23); + if (idx == 0) { + func_00282D30(m1, m2, -p); + func_002E7570(m0, m1, c2, col1, r2, s2, k75); + } + } + func_002ED368(obj); + r.fc = func_00283D38(0x2F7F, 0x7F7F, func_002A7878(0.0f, 1.0f)); + if (flag != 0) + r.a14 = func_002A7820(0x18, 0x28); + else + r.a14 = func_002A7820(0x1F, 0x3F); + r.f8 = func_002A7878(1.0f, 2.0f); + for (i = 0; i < 4; i++) + ((int *)r.v)[i] = m0[i]; + r.v[0] += func_002A7878(-0.025f, 0.025f); + r.v[1] += func_002A7878(-0.025f, 0.025f); + r.v[2] += func_002A7878(-0.025f, 0.025f); + func_00282C88(r.q); + for (i = 0; i < 4; i++) + r.q[i] = D_001B2150[i]; + r.f4 = func_002A7940(); + r.f0 = func_002A7878(90.0f, 180.0f) * 0.00029088825f; + v = func_002A7820(0x46, 0x5A); + r.a20 = v; + r.a2c = 5; + r.a1c = 1; + r.a24 = v; + if (flag == 0) { + int x = D_001B8874; + r.a1c = (((x % 2) + 1) << 1) | 1; + } + func_002ED2B8(obj, &r); +} diff --git a/nonmatching/functional/func_002E8368.c b/nonmatching/functional/func_002E8368.c new file mode 100644 index 000000000..68d91e041 --- /dev/null +++ b/nonmatching/functional/func_002E8368.c @@ -0,0 +1,285 @@ +/* functional: medium (fcheck covers 4 calls; the phase-end branch is probably not exercised). Hand-decoded from the assembly (not attempted as a match): per-frame update of the intro/transition controller at 0x1B8840; the same reads, writes and calls in the same order with the same arguments. Field names are offsets only. Returns 1 once when the last phase ends and the flag at 0x1A7284 is set. */ +#define W(p, o) (*(int *)((char *)(p) + (o))) +#define H(p, o) (*(short *)((char *)(p) + (o))) +#define B(p, o) (*(unsigned char *)((char *)(p) + (o))) +#define F(p, o) (*(float *)((char *)(p) + (o))) + +extern void func_002F5220(void); +extern void func_002F52B8(void); +extern void func_00133220(void); +extern void func_00133230(void); +extern int func_00132028(void); +extern void func_001329F0(int, int, int, int, int, int, int, int); +extern void func_0029F338(void *); +extern void func_0029F390(void *); +extern void func_0029FC20(void *, int, void *); +extern void func_002A0F48(void *); +extern void func_002A10E8(void *); +extern void func_002EC1C0(void); +extern void func_002ED260(int); +extern void func_00132AA0(void); +extern int func_001163B0(void); +extern void func_00132858(int); +extern void func_00132818(void); +extern void func_002E80B0(void); +extern void func_00282A60(void *, int, int); +extern void func_00294070(int); +extern void func_002E6A60(void); +extern void func_0027AB40(void); +extern void func_00282D30(void *, void *, float); +extern void func_00282CC0(void *, void *, void *); +extern float func_00283CE0(int); +extern void func_002E64A0(int); + +extern float D_001B15A0; +extern char D_001B8840[]; /* controller state */ +extern char D_00152C00[]; /* mode block: +0x64 index, +0x68 phase */ +extern int D_001B2130[]; /* [0] +0, [1] +4, [3] +0xC */ +extern int D_001B2148[]; /* null-terminated entity list */ +extern int D_00214920[]; /* null-terminated entity list */ +extern int D_001ABEB0; +extern int D_001A7280; +extern int D_001A7284; +extern int D_001B22B0; +extern int D_001BAEC0[]; +extern int D_001886D0[]; +extern char D_00261F30[]; /* 16-byte entries */ +extern char D_001B20D0[]; +extern char D_001AC0C0[]; +extern char D_001AC0D0[]; +extern char D_001AC120[]; +extern float D_001AC150[]; +extern float D_001AC168[]; +extern float D_001ABDD4; +extern float D_001ABF10; +extern char D_001B9040[]; /* view: +0xB0 float */ + +int func_002E8368(void) +{ + char *S = D_001B8840; + char *g = D_00152C00; + float sp0[4], sp10[4], sp20[12]; + float f20; + int i, fp; + + func_002F5220(); + { + float v = D_001B15A0 - 0.25f; + W(S, 0x38) = W(S, 0x38) + 1; + W(S, 0x34) = W(S, 0x34) + 1; + D_001B15A0 = v; + if (v < 0.0f) + D_001B15A0 = 0.0f; + } + if (W(S, 0x34) == 1) { + int n; + func_00133220(); + n = W(g, 0x64); + func_001329F0(D_001B2130[n], 0, 0x400, 0, 0, 0, 0, (int)D_001886D0 + n * 0x70); + func_00133230(); + func_00132028(); + } + if (W(S, 0x34) < H(S, 0x40)) { + goto running; + } + /* the phase has ended */ + for (i = 0; i < H(S, 0x44); i++) { + void *p = (void *)W(S, 0x18C + i * 4); + if (p != 0) { + void *q = (void *)W(p, 0x24); + B(q, 0xC) = B(q, 0xC) - 1; + q = (void *)W(p, 0x24); + W(q, 0x48 + B(q, 0xC) * 4) = 0; + func_0029F338(p); + } + } + if (D_001ABEB0 != 0) { + func_002EC1C0(); + D_001ABEB0 = 0; + } + func_002ED260(0x240450); + if (D_001A7280 < 2) + W(g, 0x68) = 0; + if (W(g, 0x68) < 2) { + if (W(g, 0x64) != 2) { + func_00132AA0(); + while (func_00132028() != 0) + ; + } + if (W(g, 0x68) == 0) { + int r = func_001163B0() >> 16; + W(g, 0x64) = (W(g, 0x64) + (r % 3) + 1) & 3; + } else { + W(g, 0x64) = 4; + func_00133220(); + func_00132858(D_001B2130[0]); + func_00132858(D_001B2130[1]); + func_00132858(D_001B2130[3]); + func_00133230(); + while (func_00132028() != 0) + ; + func_00132818(); + D_001A7284 = 0; + func_002E80B0(); + } + W(g, 0x68) = W(g, 0x68) + 1; + { + int *dst = (int *)D_001AC0D0, *src = (int *)D_001AC0C0; + dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; + } + func_00282A60(S, 0, 0x1D0); + { + int a2 = D_001B22B0; + int a1 = D_001BAEC0[6]; + int *a2p, *a3; + int *t0; + int t1; + W(S, 0x6C) = D_001BAEC0[3] + a2; + W(S, 0x70) = D_001BAEC0[4] + a2; + a2p = (int *)(a1 + 0x50 + W(g, 0x64) * 4); + a1 = a1 + W((void *)a1, 4); + a3 = (int *)(a1 + *a2p); + if (a3[1] != 0) { + t0 = (int *)(S + 0x74); + t1 = 0; + do { + int v0 = a3[0]; + t1++; + a3 += 2; + *t0 = a1 + *a2p + (v0 + 0x800); + t0++; + if (!(t1 < 0x46)) + break; + } while (a3[1] != 0); + } + } + func_00294070(0); + goto after; + } else { + if (D_001A7284 == 0) + return 0; + D_001A7284 = 0; + return 1; + } + +running: + if (!(W(S, 0x38) < 0x60)) { + W(S, 0x3C) = W(S, 0x3C) + 1; + func_00294070(W(S, 0x3C)); + } +after: + func_002E6A60(); + { + float t = D_001AC168[W(g, 0x64)]; + if (F(D_001B9040, 0xB0) < t) + F(D_001B9040, 0xB0) = t; + } + func_0027AB40(); + f20 = 1.0f; + if (W(g, 0x64) == 4) { + f20 = (float)(H(S, 0x40) - W(S, 0x34)) / (float)H(S, 0x40); + func_00282D30(sp0, D_001AC120, f20); + func_00282CC0(D_001AC0D0, D_001AC0D0, sp0); + } + func_00282D30(D_001B20D0, D_00261F30 + W(g, 0x64) * 16, (float)(W(S, 0x34) - 0x78) * (f20 * 20.0f)); + D_001ABDD4 = D_001AC150[W(g, 0x64)]; + for (i = 0; i < H(S, 0x44); i = fp) { + char *o; + int s1, s2; + float r; + char *s0m; + s1 = W(S, 0x38); + o = (char *)W(S, 0x18C + i * 4); + fp = i + 1; + r = func_00283CE0(s1 & 1); + s1 = s1 >> 1; + s2 = s1 + 1; + s0m = (char *)W(o, 0x68); + F(o, 0x44) = r * 0.5f; + func_00282D30(sp0, s0m + s1 * 16, 1.0f - r * 0.5f); + func_00282D30(sp10, s0m + s2 * 16, F(o, 0x44)); + func_00282CC0(o + 0x10, sp0, sp10); + { + char *d = (char *)W(o, 0x24); + B(o, 0x42) = B(d, 0xC) - 1; + B(o, 0x40) = s1; + B(o, 0x41) = s2; + B(o, 0x43) = B(d, 0xC) - 1; + } + func_0029F390(o); + H(o, 0x32) = 0x1FF; + if (H(o, 0xAA) == 0xD54) { + if (D_001B2148[0] != 0) { + func_0029FC20(o, 0, sp20); + B(o, 0x63) = 0; + H(o, 0x32) = 0; + if (D_001B2148[0] != 0) { + int a3 = 0; + do { + char *e = (char *)D_001B2148[a3]; + int k; + int *q; + B(e, 0x63) = 0; + e = (char *)D_001B2148[a3]; + H(e, 0x32) = 0x1FF; + e = (char *)D_001B2148[a3]; + q = (int *)(o + 0x10); + ((int *)(e + 0x10))[0] = q[0]; ((int *)(e + 0x10))[1] = q[1]; + ((int *)(e + 0x10))[2] = q[2]; ((int *)(e + 0x10))[3] = q[3]; + for (k = 0; k < 3; k++) { + e = (char *)D_001B2148[a3]; + F(e, 0xC0 + k * 16) = sp20[k * 4]; + F(e, 0xC4 + k * 16) = sp20[k * 4 + 1]; + F(e, 0xC8 + k * 16) = sp20[k * 4 + 2]; + } + func_002A10E8((void *)D_001B2148[a3]); + a3 = a3 + 1; + } while (D_001B2148[a3] != 0); + } + } else { + func_002E64A0(W(S, 0x54)); + } + B(o, 0x95) = 0xE8; + } + B(o, 0x62) = 0xFF; + B(o, 0x61) = 0xFF; + func_002A0F48(o); + { + char *d = (char *)W(o, 0x24); + B(o, 0x42) = B(d, 0xC) - 1; + B(o, 0x43) = B(d, 0xC) - 1; + if (W(g, 0x64) == 4) { + int a0 = H(S, 0x40); + int v1 = W(S, 0x34); + if (a0 - 0x28 < v1) { + float f1 = (float)(a0 - v1 - 4) * 0.027777778f; + if (f1 < 0.0f) + f1 = 0.0f; + F(o, 0x2C) = F(d, 0x24) * f1; + D_001ABF10 = f1; + if (D_001B2148[0] != 0) { + char *e = (char *)D_001B2148[0]; + F(e, 0x2C) = F((char *)W(e, 0x24), 0x24) * f1; + } + B(o, 0x95) = (int)(f1 * 232.0f); + if (H(o, 0xAA) == 0xD54) { + if (D_001B2148[0] == 0) { + int *p; + H(g, 0x2C) = 1; + p = D_00214920; + if (*p != 0) { + do { + char *e = (char *)*p; + p++; + F(e, 0x2C) = F((char *)W(e, 0x24), 0x24) * f1; + } while (*p != 0); + } + } + } + } + } + } + } + func_002F52B8(); + return 0; +} diff --git a/nonmatching/functional/func_002ECB70.c b/nonmatching/functional/func_002ECB70.c new file mode 100644 index 000000000..a7291c0d0 --- /dev/null +++ b/nonmatching/functional/func_002ECB70.c @@ -0,0 +1,131 @@ +/* functional: medium. Decoded from the assembly: a 43-point fan is transformed (or eased from its previous frame's points by the G2/60 ratio), then 38 four-vertex quads are copied from the point tables into one packet and sent to func_00280B90. Same calls, arguments and order as retail; locals and loops are clearer. Helper names are the catalogue's; their float/vector roles are guessed from use. */ +typedef struct { float x, y, z, w; } V4; +typedef struct { + V4 pos[4]; + unsigned int col[4]; + float uv[4][2]; + long long q70, q78, q80, q88; +} Pkt; + +extern long func_0027C878(int); +extern void func_0029FC20(void *, int, float *, unsigned int); +extern float func_00282C48(float); +extern float func_00282C10(float); +extern float func_00283CE0(int); +extern void func_00282948(int *); +extern void func_002830C0(float *, float *, float *); +extern void func_00282CF0(float *, float *, float *); +extern void func_00282F20(float, float *, float *); +extern float func_00282DB0(float *, float *); +extern void func_00282D30(float, float *, float *); +extern void func_00280B90(Pkt *, int, int); + +extern int D_001A8BB0; +extern float D_001B5200[]; +extern short D_00152C20; +extern float D_001B5340[]; +extern float D_002620A0[]; +extern float D_002625B0[]; +extern float D_00214CE0[]; +extern float D_00214E38[]; +extern float D_00214A30[]; +extern short D_00262350[]; +extern int D_001ABF00 __attribute__((sda)); +extern int D_001ABF04 __attribute__((sda)); +extern int D_001ABF08 __attribute__((sda)); + +void func_002ECB70(float *obj) +{ + Pkt pkt; + float mtx[16]; + float v1[4], v0[4], v2[4]; + float *mtxp; + long ret; + unsigned int col; + int s2 = 0; + int i, j; + + ret = func_0027C878(0x15); + col = (D_001A8BB0 == 2) ? 0x28A5A281u : 0x3CA5A281u; + pkt.q88 = 0x8000000044LL; + pkt.q80 = 0xFF9000000260LL; + pkt.q78 = ret; + pkt.q70 = 0; + pkt.col[0] = col; + pkt.col[1] = col; + pkt.col[2] = col; + pkt.col[3] = col; + func_0029FC20(obj, 0, mtx, col); + + if (D_001A8BB0 == 0 || D_001A8BB0 == 2) { + if (func_00282C48(D_001B5200[0x140 / 4] - obj[4]) < 16.0f) { + if (func_00282C48(D_001B5200[0x144 / 4] - obj[5]) < 16.0f) + s2 = 1; + } + } else { + s2 = 1; + } + if (D_001A8BB0 == 6 && D_00152C20 == 2) + s2 = 0; + + if (s2 == 0 && D_001ABF00 != 1) { + if (D_001ABF04 == 1) { + D_001ABF04 = 0; + for (i = 0; i < 43; i++) { + D_00214E38[i * 2] = D_00214CE0[i * 2]; + D_00214E38[i * 2 + 1] = D_00214CE0[i * 2 + 1]; + func_002830C0(D_00214A30 + i * 4, D_002620A0 + i * 4, mtx); + } + } else { + for (i = 0; i < 43; i++) + func_002830C0(D_00214A30 + i * 4, D_002620A0 + i * 4, mtx); + } + D_001ABF08 = 0x3C; + } else { + float t; + + mtxp = mtx; + D_001ABF04 = 1; + func_00282948(&D_001ABF08); + t = func_00283CE0(D_001ABF08); + t = t / func_00283CE0(0x3C); + for (i = 0; i < 43; i++) { + float d, r, f4, x, y; + + func_002830C0(D_00214A30 + i * 4, D_002620A0 + i * 4, mtxp); + func_00282CF0(v2, D_00214A30 + i * 4, D_001B5340); + func_00282F20(1.0f, v2, v2); + func_002830C0(v1, D_002625B0 + i * 4, mtxp); + func_00282F20(0.1f, v1, v1); + d = func_00282DB0(v1, v2); + func_00282D30(d + d, v0, v1); + func_00282CF0(v0, v2, v0); + func_00282F20(1.0f, v0, v0); + v0[2] = v0[2] + 1.0f; + r = func_00282C10(v0[2] + v0[2]); + f4 = r + r; + x = v0[0] / f4 + 0.5f; + y = v0[1] / f4 + 0.5f; + if (D_001ABF00 == 1 || D_001ABF08 == 0) { + D_00214CE0[i * 2] = x; + D_00214CE0[i * 2 + 1] = y; + } else { + D_00214CE0[i * 2] = x + (D_00214E38[i * 2] - x) * t; + D_00214CE0[i * 2 + 1] = y + (D_00214E38[i * 2 + 1] - y) * t; + } + } + if (D_001ABF00 == 1) + D_001ABF00 = 2; + } + + for (i = 0; i < 38; i++) { + for (j = 0; j < 4; j++) { + int idx = D_00262350[i * 8 + j * 2]; + + pkt.pos[j] = *(V4 *)(D_00214A30 + idx * 4); + pkt.uv[j][0] = D_00214CE0[idx * 2]; + pkt.uv[j][1] = D_00214CE0[idx * 2 + 1]; + } + func_00280B90(&pkt, 0, 0); + } +} diff --git a/nonmatching/functional/func_002F0FA0.c b/nonmatching/functional/func_002F0FA0.c new file mode 100644 index 000000000..72a6701e1 --- /dev/null +++ b/nonmatching/functional/func_002F0FA0.c @@ -0,0 +1,27 @@ +/* functional: high. Clears the flags of every record of the 0x20-byte record list; same reads/writes as retail, only register allocation differs. */ +extern int D_001B21B4; +extern int D_001B21B8; +extern int D_001B21C4; +extern int D_001B21E0; +extern int D_001B21E4; +extern int D_001BAED4; +extern int D_001B21E8 __attribute__((sda)); + +void func_002F0FA0(void) +{ + char *p = (char *)D_001B21B4; + int v = D_001BAED4; + + D_001B21C4 = v; + D_001B21E0 = 0; + D_001B21E4 = 0; + D_001B21E8 = 0; + if (p == (char *)D_001B21B8) + return; + do { + *(unsigned char *)(p + 0x1F) = 0xFF; + *(unsigned short *)(p + 0x16) = 0; + *(unsigned short *)(p + 0x14) = 0xFFFF; + p += 0x20; + } while (p != (char *)D_001B21B8); +} diff --git a/nonmatching/functional/func_002FC280.c b/nonmatching/functional/func_002FC280.c new file mode 100644 index 000000000..a671b6c19 --- /dev/null +++ b/nonmatching/functional/func_002FC280.c @@ -0,0 +1,77 @@ +/* functional: high. Same reads, writes and calls as retail; only instruction order of two loads differs. */ +/* func_002FC280 - retained near miss, USA v1.01. + * + * Profile: -O2 -G8 -ffunction-sections (the two viewport scratch words are read and + * written through $gp, so the body needs the -G8 sibling). + * Last verdict: 28 places, 316/316 bytes, `boot: instruction at +0x40 differs`. + * Everything from the packet body to the epilogue is exact, including the fail path's + * `sw $0,-0x1AD0($gp); jal; nop`, the annulled `bnel` store and the four separate + * packet-pointer reloads. + * + * Remaining difference: in the prologue the D_001B22A8 reload (two instructions, + * `lui;lw`) and the `D_001B22A0[index]` load are in the opposite order. Retail: + * lw $a1,0($v1) (table entry) + * lui $a2,%hi(D_001B22A8); lw $a2,%lo($a2) + * this draft: + * lui $a2,...; lw $a2,... then lw $a1,0($v1) + * Tried: the subtraction written first, a `base` local read before the pointer, a + * `flags` local for the read-modify-write, and `used = table; used = ptr - used`. + * All keep the volatile pointer load first. Reopen with a source form that makes the + * table load schedule ahead of the volatile small-data load. + * + * Notes for requalification: + * - D_001B22A8 must be `unsigned char * volatile` for the four packet stores to reload + * it (retail reloads every time); the non-volatile form keeps one copy in $a3. + * - D_001AD520 must be volatile for the fail path's store to land before the call + * instead of in its delay slot. + * - D_001B22B4 is read absolutely at entry and through $gp later, hence the measured + * second name D_001B22B4. + */ +extern volatile int D_001AD520 __attribute__((sda)); +extern int D_001B22B8 __attribute__((sda)); +extern int D_001B22B4; +extern int D_001B22B0; +extern int D_001B22A0[]; +extern unsigned char * volatile D_001B22A8; +extern int func_00126F38(int); +extern void func_0011AEA0(int); +extern void func_00127220(void *, int); +extern void func_002FC3C0(int); +extern void func_002FC220(void); +extern void func_002F0FA0(void); + +/* Waits for the display-list buffer to free up and opens the next packet. */ +void func_002FC280(void) +{ + int flags; + int used; + int ready = 0; + char *packet; + + flags = D_001AD520 | 0x1F; + used = D_001B22A0[D_001B22B4]; + used = (int)D_001B22A8 - used; + D_001AD520 = flags; + if (used > D_001B22B0) { + if (used < D_001B22B8) + D_001B22B8 = used; + ready = 1; + } + if (!ready || D_001B22B4 == 0) { + *(int *)(D_001B22A8 + 0) = 0x70000000; + *(int *)(D_001B22A8 + 4) = 0; + *(int *)(D_001B22A8 + 8) = 0; + *(int *)(D_001B22A8 + 12) = 0; + packet = (char *)func_00126F38(1); + *(int *)packet |= 0xC0; + func_0011AEA0(0); + func_00127220(packet, D_001B22A0[D_001B22B4]); + if (ready) { + func_002FC3C0(1); + func_002FC220(); + } + } else { + D_001AD520 = 0; + func_002F0FA0(); + } +} diff --git a/nonmatching/functional/func_00336F20.c b/nonmatching/functional/func_00336F20.c new file mode 100644 index 000000000..86e592776 --- /dev/null +++ b/nonmatching/functional/func_00336F20.c @@ -0,0 +1,70 @@ +/* functional: medium. Written from the assembly; the two jump-table switches are folded into one predicate (kind is 0xC, 0xE, 0x11, 0x12 or 0x35); same reads, writes and calls in the same order. */ +typedef unsigned char u8; +typedef struct { int w0; u8 b4; u8 pad[0x67]; int w6C; u8 pad2[0x70]; } Rec336F20; /* 0xE0 bytes */ + +extern u8 D_00139568[]; +extern Rec336F20 D_00239BA0[]; +extern int D_001ADAF8[] __attribute__((sda)); +extern int func_00287F60(int); +extern int func_00288D58(void); + +static int isKind(int k) +{ + switch (k) { + case 0xC: case 0xE: case 0x11: case 0x12: case 0x35: + return 1; + default: + return 0; + } +} + +void func_00336F20(int kind, int *o1, int *o2, int *o3) +{ + Rec336F20 *rec; + int *tbl; + int n; + int cond; + + *o1 = 0x55F0C070; + *o2 = 0x55F0C070; + + tbl = isKind(kind) ? D_001ADAF8 + 4 : D_001ADAF8; + rec = &D_00239BA0[D_00139568[kind]]; + *o3 = tbl[rec->b4]; + if (kind == 0x10) { + rec = &D_00239BA0[D_00139568[0x10]]; + if (rec->b4 >= 2) + *o3 = D_001ADAF8[rec->b4 - 2]; + } + + if (D_00239BA0[D_00139568[kind]].w0 == 0) + return; + if (func_00287F60(kind) <= 0) + return; + + if (func_00288D58() != 0) { + cond = 0; + if (D_00239BA0[D_00139568[kind]].w0 != 0) { + n = func_00287F60(kind); + if (n >= 2) + cond = 1; + else if (n == 1) + cond = isKind(kind); + } + if (cond) { + rec = &D_00239BA0[D_00139568[kind]]; + if (rec->w0 == 0) + return; + if (rec->b4 == 2 || (rec->b4 == 1 && isKind(kind))) { + *o1 = 0x60664088; + *o2 = 0x606600D8; + } + return; + } + } + rec = &D_00239BA0[D_00139568[kind]]; + if (rec->b4 == 0 && rec->w6C > 0) { + *o1 = 0x6049C1FF; + *o2 = 0x60001EFF; + } +} diff --git a/nonmatching/functional/func_00337C10.c b/nonmatching/functional/func_00337C10.c new file mode 100644 index 000000000..67b2d758e --- /dev/null +++ b/nonmatching/functional/func_00337C10.c @@ -0,0 +1,71 @@ +/* functional: high. Same stores and calls in retail order except retail's dead store of 0 ahead of the later store to the same word, which this compiler removes; it has no observable effect. */ +#include "include_asm.h" +extern int *func_00336DA8(int, int); +extern int func_00336E40(void *); +extern int *func_00335E20(void *); +extern void func_00335E68(void *, int); +extern void func_003361E0(void *, void *, void *); +extern void func_00336390(void *, int, int); +extern void func_003363C0(void *, float); +extern void func_00115484(void *, int, int); +extern char D_001ADB40[]; +extern char D_001ADB50[]; +extern char *D_001A8CFC; + +void func_00337C10(char *obj, void *arg) +{ + int *p; + char *a; + char *b; + + obj[0xA8] = 0; + *(int *)(obj + 0x1AC) = 0; + *(int *)(obj + 0x1A8) = 0; + *(int *)(obj + 0x1C8) = 0; + *(int *)(obj + 0x1CC) = 0; + *(int *)(obj + 0x1D4) = 0x28; + *(int *)(obj + 0x1C4) = 1; + *(int *)(obj + 0x1B0) = 1; + func_00115484(obj + 0x1D8, 0, 0x78); + if (arg != 0) { + p = func_00336DA8(0x10, func_00336E40(arg)); + *(float *)(p + 0) = 255.0f; + *(float *)(p + 1) = 108.0f; + p[2] = 0; + p[3] = 0; + *(int **)(obj + 0) = p; + p = func_00336DA8(0x10, func_00336E40(arg)); + p[0] = 0; + p[1] = 0; + p[0] = 0x60442D00; + p[2] = 0; + p[3] = 0; + *(int **)(obj + 8) = p; + p = func_00336DA8(0x10, func_00336E40(arg)); + *(int **)(obj + 4) = p; + p[0] = 0; + p[1] = 0; + p[2] = 0; + p[3] = 0; + p = func_00336DA8(0x10, func_00336E40(arg)); + *(int **)(obj + 12) = p; + p[0] = 0; + p[1] = 0; + p[2] = 0; + p[3] = 0; + } + a = obj + 0x10; + b = obj + 0x5C; + func_003361E0(a, D_001ADB40, arg); + func_00336390(a, (int)(D_001A8CFC + 0x8710), 0x57); + func_003363C0(a, 0.02f); + func_00335E68(a, 1); + *func_00335E20(a) = 0x55F0C070; + func_003361E0(b, D_001ADB50, arg); + func_00336390(b, (int)(D_001A8CFC + 0x8710), 0x58); + func_00335E68(b, 1); + *func_00335E20(b) = 0x55F0C070; + **(int **)(obj + 12) = 0x80F0F0F0; + *(int *)(obj + 0x1B8) = 0; + **(int **)(obj + 4) = 0; +} diff --git a/nonmatching/functional/func_003380B0.c b/nonmatching/functional/func_003380B0.c new file mode 100644 index 000000000..ec0a2c787 --- /dev/null +++ b/nonmatching/functional/func_003380B0.c @@ -0,0 +1,80 @@ +/* functional: high. Transcribed branch by branch; the formatting call gets the same register arguments as retail in each branch. */ +extern int func_00115DA8(char *, const char *, ...); +extern void func_00280190(unsigned short *, unsigned long, char *, int); +extern void func_00280200(unsigned short *, unsigned long, char *, int); +extern void func_00280270(unsigned short *, unsigned long, char *, int); +extern void func_0027EB88(int, int, int, int, int, int); +extern void func_003384C0(void *, int, int, float, float, float, float); + +extern char D_001ADB58[]; +extern char D_001ADB68[]; +extern char D_001ADB78[]; +extern char D_001ADB90[]; +extern char D_001ADBB0[]; +extern char D_001ADBB8[]; +extern char D_001ADBC8[]; +extern int D_001A7344; + +static void draw_text(int mode, unsigned short *r, unsigned long style, char *buf, int len) +{ + if (mode == 0) + func_00280200(r, style, buf, len); + else if (mode == 1) + func_00280190(r, style, buf, len); + else if (mode == 2) + func_00280270(r, style, buf, len); +} + +void func_003380B0(void *p, unsigned short *r, unsigned long style, int text, int mode, int center, + int len, int t3, int a9, int a10, int a11) +{ + char buf[0x300]; + int saved = (short)r[9]; + int x, h, hx, hy, x0, x1, y0, y1; + + r[9] |= 3; + + if (a9 == 0) { + func_00115DA8(buf, D_001ADBC8, text, text, mode, center, a10, t3); + } else if (a10 == 0) { + if (t3 != 0) + func_00115DA8(buf, D_001ADBB0, text, 1, a9, center, a10, t3); + else + func_00115DA8(buf, D_001ADBB8, text, 1, 1, a9, a10, t3); + } else if (a11 == 0) { + if (t3 != 0) + func_00115DA8(buf, D_001ADB78, text, 1, a9, a10, a10, t3); + else + func_00115DA8(buf, D_001ADB90, text, 1, 1, a9, a10, t3); + } else { + if (t3 != 0) + func_00115DA8(buf, D_001ADB58, text, 1, a9, 1, a10, t3); + else + func_00115DA8(buf, D_001ADB68, text, 1, 1, a9, 1, a10); + } + + r[9] |= 4; + draw_text(mode, r, style, buf, len); + + if (center != 0) { + h = (short)r[7]; + x = (D_001A7344 - h) >> 1; + r[1] = x + r[7]; + r[5] = x + (h >> 1); + r[0] = x; + } + draw_text(mode, r, style, buf, len); + + hx = ((short)r[6] >> 1) + 10; + hy = ((short)r[7] >> 1) + 5; + x1 = (short)r[4] + hx; + y1 = (short)r[5] + hy; + y0 = (short)r[5] - hy; + x0 = (short)r[4] - hx; + r[9] &= 0xFFFB; + func_0027EB88(y0, y1, x0, x1, 0x60, 0x60442D00); + func_003384C0(p, 0x60, 0x55F0C070, (float)y0, (float)y1, (float)x0, (float)x1); + + draw_text(mode, r, style, buf, len); + r[9] = saved; +} diff --git a/nonmatching/functional/func_003384C0.c b/nonmatching/functional/func_003384C0.c new file mode 100644 index 000000000..6c6f4b921 --- /dev/null +++ b/nonmatching/functional/func_003384C0.c @@ -0,0 +1,36 @@ +/* functional: high. Near miss (604 vs 600 bytes: scheduling and one address form only); same reads, writes and calls in order; float constants taken from the retail words. */ +extern void func_0027AB40(void); +extern void func_00300A00(void *, int, float, float, float, float, float); +extern int func_00335E08(void); +extern int D_001A8CFC; +extern float D_001B9040[]; + +void func_003384C0(char *obj, int hi, int lo, float a, float b, float c, float d) +{ + float saved, k, t, c2, d2; + int flags; + + if (D_001A8CFC == 0) + return; + saved = D_001B9040[44]; + D_001B9040[44] = 0.62f; + k = 1.0f; + func_0027AB40(); + t = (b - a - 26.0f) * 0.008928571827709675f; + t = t * 0.3110000193119049f + 0.03750000149011612f; + if (func_00335E08() == 1) + k = 0.9f; + flags = (hi << 24) | lo; + func_00300A00(*(void **)(obj + 0x220C), flags, c, a, -1.0f, k, t); + func_00300A00(*(void **)(obj + 0x220C), flags, d, a, 1.0f, k, t); + t = (d - c - 150.0f) * 0.003267973894253373f; + c2 = c + 16.0f; + d2 = d - 16.0f; + t = t * 0.43300002813339233f + 0.10199999809265137f; + func_00300A00(*(void **)(obj + 0x2210), flags, c2, a, -1.0f, 1.0f, t); + func_00300A00(*(void **)(obj + 0x2210), flags, d2, a, 1.0f, 1.0f, t); + func_00300A00(*(void **)(obj + 0x2210), flags, c2, b, -1.0f, -1.0f, t); + func_00300A00(*(void **)(obj + 0x2210), flags, d2, b, 1.0f, -1.0f, t); + func_0027AB40(); + D_001B9040[44] = saved; +} diff --git a/nonmatching/functional/func_0033C590.c b/nonmatching/functional/func_0033C590.c new file mode 100644 index 000000000..199683b9d --- /dev/null +++ b/nonmatching/functional/func_0033C590.c @@ -0,0 +1,50 @@ +/* functional: high (fcheck covers 2 calls only). From the near miss (one redundant instruction off retail): same reads, writes and calls in the same order; options toggled at 0x1A7BA4/A8/B1 and mode mod 3 at 0x1A7BAC. */ +typedef unsigned char u8; +typedef int s32; + +extern void func_0033AE28(char *); +extern void func_0033B018(char *, float, float); +extern void func_002E5E58(int, int, int); +extern void func_0027AB40(void); + +int func_0033C590(u8 *a0, s32 a1) +{ + u8 *p = a0 + 8; + unsigned int *sfx = (unsigned int *)0x1A7BA4; + unsigned int *music = (unsigned int *)0x1A7BA8; + int *mode = (int *)0x1A7BAC; + unsigned char *paused = (unsigned char *)0x1A7BB1; + float *q; + int v; + + func_0033AE28(p); + q = *(float **)(a0 + 0x2AC); + func_0033B018(p, q[0], q[1]); + if (a1 & 0x1000) { + func_002E5E58(3, 0, 0); + v = *(int *)(a0 + 0x2A8); + *(int *)(a0 + 0x2A8) = (v + 3) % 4; + } else if (a1 & 0x4000) { + func_002E5E58(3, 0, 0); + v = *(int *)(a0 + 0x2A8); + *(int *)(a0 + 0x2A8) = (v + 1) % 4; + } else if (a1 & 0x40) { + func_002E5E58(4, 0, 0); + switch (*(int *)(a0 + 0x2A8)) { + case 0: + *sfx = *sfx < 1; + break; + case 1: + *music = *music < 1; + break; + case 2: + *mode = (*mode + 1) % 3; + break; + case 3: + *paused = *paused < 1; + func_0027AB40(); + break; + } + } + return (a1 >> 6) & 1; +} diff --git a/nonmatching/functional/func_0033D028.c b/nonmatching/functional/func_0033D028.c new file mode 100644 index 000000000..6e87d80bb --- /dev/null +++ b/nonmatching/functional/func_0033D028.c @@ -0,0 +1,72 @@ +/* functional: high. Written from the assembly; same reads, writes and calls in the same order. The two float constants at D_001ADE58/D_001ADE60 are $gp-relative in retail (-G8 unit), here plain globals; no byte match attempted beyond that (needs a -G8 split of the unit). */ +extern void func_0033AE28(char *); +extern void func_0033B018(char *, float, float); +extern void func_00335E38(char *, float, float, float, float); +extern void func_00336B98(char *, int); +extern int func_002E5E58(int, unsigned int, void *); +extern void func_002E4860(void); +extern void func_00132938(int); +extern unsigned int D_00138340; +extern int D_001A7B98; +extern int D_001A7B9C; +extern int D_001A7BA0; +extern float D_001ADE58; +extern float D_001ADE5C; +extern float D_001ADE60; +extern float D_001ADE64; + +int func_0033D028(char *a0, int a1) +{ + char *p = a0 + 8; + float *q; + float zero = 0.0f; + int v; + + func_0033AE28(p); + q = *(float **)(a0 + 0x33C); + func_0033B018(p, q[0], q[1]); + q = *(float **)(a0 + 0x33C); + func_00335E38(a0 + 0x2AC, q[0] + D_001ADE58, q[1] + D_001ADE5C, zero, zero); + q = *(float **)(a0 + 0x33C); + func_00335E38(a0 + 0x2F4, q[0] + D_001ADE60, q[1] + D_001ADE64, zero, zero); + if (a1 & 0x1000) { + func_002E5E58(3, 0, 0); + *(int *)(a0 + 0x2A8) = (*(int *)(a0 + 0x2A8) + 2) % 3; + } else if (a1 & 0x4000) { + func_002E5E58(3, 0, 0); + *(int *)(a0 + 0x2A8) = (*(int *)(a0 + 0x2A8) + 1) % 3; + } else { + v = D_00138340; + if (v & 0x8000) { + int sel = *(int *)(a0 + 0x2A8); + if (sel == 0) { + v = D_001A7BA0 - 3; + D_001A7BA0 = v > -1 ? v : 0; + } else if (sel == 1) { + v = D_001A7B9C - 3; + D_001A7B9C = v > -1 ? v : 0; + } + } else if (v & 0x2000) { + int sel = *(int *)(a0 + 0x2A8); + if (sel == 0) { + v = D_001A7BA0 + 3; + D_001A7BA0 = v < 0x401 ? v : 0x400; + } else if (sel == 1) { + v = D_001A7B9C + 3; + D_001A7B9C = v < 0x401 ? v : 0x400; + } + } else if (a1 & 0x40) { + if (*(int *)(a0 + 0x2A8) == 2) { + func_002E5E58(4, 0, 0); + D_001A7B98 = D_001A7B98 == 0; + func_00132938(D_001A7B98 ^ 1); + } else { + func_002E5E58(5, 0, 0); + } + } + } + func_00336B98(a0 + 0x2F4, D_001A7B9C); + func_00336B98(a0 + 0x2AC, D_001A7BA0); + func_002E4860(); + return (a1 >> 6) & 1; +} diff --git a/nonmatching/functional/func_0033EA60.c b/nonmatching/functional/func_0033EA60.c new file mode 100644 index 000000000..89b96ba04 --- /dev/null +++ b/nonmatching/functional/func_0033EA60.c @@ -0,0 +1,59 @@ +/* functional: high (fcheck covers 1 call only). Reconstruction from a 560/564-byte near miss; same calls in order with the same arguments; globals addressed absolutely instead of via $gp. */ + +extern void func_0033AE28(char *); +extern void func_0033B018(float **, float, float); +extern void func_00335E38(unsigned char *, float, float, float, float); +extern char *func_002DF1F8(char *, int); +extern int func_00288FB8(int); +extern void func_00336A38(int, unsigned int); +extern void func_0033EC98(char *); +extern int func_002D6F38(int); +extern char D_001C4FA0[]; +extern int D_00258D70[][2]; +extern char D_001ADC88[];extern float D_001ADF28; +extern float D_001ADF2C; +extern float D_001ADF30; +extern float D_001ADF34; +extern float D_001ADF38; +extern float D_001ADF3C; +extern float D_001ADF50; +extern float D_001ADF54; +extern float D_001ADF58; +extern float D_001ADF5C; +extern float D_001ADF48; +extern float D_001ADF4C; +extern float D_001ADF40; +extern float D_001ADF44; + +int func_0033EA60(char *object, int flags) +{ + char *first = object + 8; + char *label = object + 0x578; + float *position; + int id; + func_0033AE28(first); + position = *(float **)(object + 0x2A8); + func_0033B018((float **)first, position[0], position[1]); position = *(float **)(object + 0x2A8); + func_00335E38((unsigned char *)object + 0x2E8, D_001ADF28 + position[0], D_001ADF2C + position[1], 0.0f, 0.0f); + position = *(float **)(object + 0x2A8); + func_00335E38((unsigned char *)object + 0x330, D_001ADF30 + position[0], D_001ADF34 + position[1], 0.0f, 0.0f); + position = *(float **)(object + 0x2A8); + func_00335E38((unsigned char *)object + 0x378, D_001ADF38 + position[0], D_001ADF3C + position[1], 0.0f, 0.0f); + position = *(float **)(object + 0x2A8); + func_00335E38((unsigned char *)object + 0x2AC, D_001ADF50 + position[0], D_001ADF54 + position[1], 0.0f, 0.0f); + position = *(float **)(object + 0x2A8); + func_00335E38((unsigned char *)object + 0x3C0, D_001ADF58 + position[0], D_001ADF5C + position[1], 0.0f, 0.0f); + position = *(float **)(object + 0x2A8); + func_00335E38((unsigned char *)object + 0x578, D_001ADF48 + position[0], D_001ADF4C + position[1], 0.0f, 0.0f); + position = *(float **)(object + 0x2A8); + func_00335E38((unsigned char *)object + 0x530, D_001ADF40 + position[0], D_001ADF44 + position[1], 0.0f, 0.0f); + func_002DF1F8(object + 0x5C0, *(int *)(D_001C4FA0 + 0x230)); + id = D_00258D70[*(int *)(D_001C4FA0 + 0x230)][1]; + if (id >= 0) + func_00336A38((int)label, func_00288FB8(id)); + else + func_00336A38((int)label, (unsigned int)D_001ADC88); + func_0033EC98(object); + func_002D6F38(0); + return (flags >> 6) & 1; +} diff --git a/nonmatching/functional/func_0033FB98.c b/nonmatching/functional/func_0033FB98.c new file mode 100644 index 000000000..9680d28b8 --- /dev/null +++ b/nonmatching/functional/func_0033FB98.c @@ -0,0 +1,143 @@ +/* functional: medium. Written from the assembly (no draft existed); same calls with the same arguments in the same order and the same stores. Local names follow the stack slots. */ +typedef struct { float x, y; } Vec2; +typedef struct { int *entries; } Table18D4; + +extern Vec2 D_001AE048[6]; /* 8-byte pairs at 0x1AE048, 50, 58, 60, 68, 70 */ +extern Table18D4 *D_001B18D4; +extern char *D_001A8CFC; +extern unsigned char D_00139568[]; +extern int D_00139868[]; +extern int D_00138180[]; +extern char D_00239BA0[]; /* records, 0xE0 bytes each */ + +extern void func_00115484(void *, int, int); +extern void func_00335E38(char *, float, float, float, float); +extern void func_00335E68(void *, int); +extern void func_00336920(void *, int, int); +extern char *func_0034E5B8(char *); +extern void func_00336F20(int, int *, int *, int *); +extern int *func_00335E20(void *); +extern void func_00336498(void *, int, int); +extern void func_00336B90(void *, int); +extern void func_00336B98(void *, int); +extern int func_002E5E58(int, unsigned int, void *); +extern void func_003409C8(char *, int); +extern void func_00340B70(char *, int); +extern int func_002A9588(int, int, int, int, int); +extern void func_003363C0(void *, float); + +#define REC(id) (D_00239BA0 + D_00139568[id] * 0xE0) + +static void place(char *obj, int off, Vec2 *v) +{ + float *p = *(float **)(obj + 0x80C); + + func_00335E38(obj + off, v->x + p[0], v->y + p[1], 0.0f, 0.0f); +} + +int func_0033FB98(char *obj, int buttons) +{ + Vec2 zero0, zero1, zero2, zero3; /* sp + 0, 0x10, 0x20, 0x30 */ + Vec2 v40, v50, v60, v70, v80, v90; + int a0v, a4v, a8v; + int i; + char *s3; + + func_00115484(&zero0, 0, 8); + func_00115484(&zero1, 0, 8); + func_00115484(&zero2, 0, 8); + func_00115484(&zero3, 0, 8); + v40 = D_001AE048[0]; + v50 = D_001AE048[1]; + v60 = D_001AE048[2]; + v70 = D_001AE048[3]; + v80 = D_001AE048[4]; + place(obj, 0x00, &zero0); + place(obj, 0x4C, &zero3); + place(obj, 0xE4, &zero1); + place(obj, 0x130, &zero2); + place(obj, 0x828, &v40); + place(obj, 0x8B8, &v60); + place(obj, 0x900, &v50); + place(obj, 0x948, &v70); + place(obj, 0x990, &v80); + v90 = D_001AE048[5]; + place(obj, 0x2A8, &v90); + place(obj, 0x2F4, &v90); + place(obj, 0x5A0, &v90); + + if (D_001B18D4 != 0) { + s3 = obj + 0x5EC; + for (i = 0; i < 8; i++, s3 += 0x3C) { + char *ent = (char *)D_001B18D4->entries + i * 0x1C; + char *item; + char *rec; + int *r; + + if (*(int *)ent == 0) + func_00335E68(s3, 0); + else { + func_00335E68(s3, 1); + func_00336920(s3, *(int *)((char *)D_001B18D4->entries + i * 0x1C), 0); + } + func_00335E68(func_0034E5B8(D_001A8CFC + 0x36F28) + i * 0x48, 0); + a0v = 0; + a4v = 0; + a8v = 0; + func_00336F20(*(int *)((char *)D_001B18D4->entries + i * 0x1C + 0x18), &a0v, &a4v, &a8v); + r = func_00335E20(s3); + *r = (*r & 0xFF000000) | (a8v & 0xFFFFFF); + item = func_0034E5B8(D_001A8CFC + 0x36F28) + i * 0x48; + func_00335E68(item, 1); + func_00336498(item, a0v, a4v); + func_00336B90(item, 0x64); + func_00336B98(item, 0x64); + + rec = REC(*(int *)((char *)D_001B18D4->entries + i * 0x1C + 0x18)); + if (*(int *)rec == 0) + continue; + if (*(int *)(rec + 0x6C) == 0 && *(unsigned char *)(rec + 4) == 0) + continue; + func_00336B90(item, *(int *)(rec + 0x6C)); + r = func_00335E20(s3); + *r = (*r & 0xFF000000) | (a8v & 0xFFFFFF); + { + int id = *(int *)((char *)D_001B18D4->entries + i * 0x1C + 0x18); + + if (*(int *)(REC(id) + 0x6C) > 0) + func_00336B98(item, D_00139868[id] >> 5); + } + } + } + + if (buttons & 4) { + int v; + + func_002E5E58(3, 0, 0); + v = *(int *)(obj + 0x7CC) - 1; + *(int *)(obj + 0x7CC) = (-1 < v) ? v : 7; + } else if (buttons & 8) { + int v; + + func_002E5E58(3, 0, 0); + v = *(int *)(obj + 0x7CC) + 1; + *(int *)(obj + 0x7CC) = (7 < v) ? 0 : v; + } else { + int sel = *(int *)(obj + 0x81C); + + if (sel == 0) + func_003409C8(obj, buttons); + else if (sel == 1) + func_00340B70(obj, buttons); + } + if (D_00138180[0x1C4 / 4] & 0xC) + func_002A9588(0, 0, 1, 0, 1); + { + char *s1 = obj + 0x5A0; + int *r = func_00335E20(s1); + + *r = func_002A9588(0x60442D00, 0x70FFFEED, 0x14, 0, 0); + func_003363C0(s1, (float)*(int *)(obj + 0x7CC)); + } + return 0; +} diff --git a/nonmatching/functional/func_00341030.c b/nonmatching/functional/func_00341030.c new file mode 100644 index 000000000..5d77a0864 --- /dev/null +++ b/nonmatching/functional/func_00341030.c @@ -0,0 +1,59 @@ +/* functional: medium. Starts from the near-miss draft; same reads, writes and calls in retail order; byte differences are register allocation only. */ +/* func_00341030 (380 bytes, boot; unit src/game/0033F030.c, -O2 -G0). + 26 October 2026 (M32): 368/380. Grid menu input; calls, fields and the movz wrap of + the increments agree. Retail's decrements keep the new value in $v1 for the store and + test a $v0 copy (daddu $v0,$v1,$zero; bgez) - one extra move each - which none of + --x, x--, x = x - 1, a local or a ternary reproduces; with `if (++x >= n) x = 0` + the size is 380 but the increments lose the movz. */ +typedef struct { + short unk0; + short unk2; + short unk4; + short id; + short unk8; +} MenuCell_00341030; + +typedef struct { + char pad000[0x1B0]; + float *origin; + char pad1B4[0x50]; + int column; + int row; + int columns; + int rows; + int selected; +} Menu_00341030; + +extern void func_00115484(void *, int, int); +extern void func_00335E38(void *, float, float, float, float); +extern void func_002E5E58(int, int, int); + +/* Grid menu input: draws the menu at its origin, moves the cursor with the four + direction bits (wrapping, with a click) and selects the cell's id. */ +int func_00341030(Menu_00341030 *menu, int pad, MenuCell_00341030 *cells) +{ + float offset[2]; + + func_00115484(offset, 0, 8); + if (cells != 0) { + menu->selected = 0; + func_00335E38(menu, offset[0] + menu->origin[0], offset[1] + menu->origin[1], 0.0f, 0.0f); + if (pad & 0x1000) { + func_002E5E58(3, 0, 0); + if (--menu->row < 0) + menu->row = menu->rows - 1; + } else if (pad & 0x4000) { + func_002E5E58(3, 0, 0); + menu->row = menu->row + 1 < menu->rows ? menu->row + 1 : 0; + } else if (pad & 0x8000) { + func_002E5E58(3, 0, 0); + if (--menu->column < 0) + menu->column = menu->columns - 1; + } else if (pad & 0x2000) { + func_002E5E58(3, 0, 0); + menu->column = menu->column + 1 < menu->columns ? menu->column + 1 : 0; + } + menu->selected = cells[menu->row * menu->columns + menu->column].id; + } + return 0; +} diff --git a/nonmatching/functional/func_00342978.c b/nonmatching/functional/func_00342978.c new file mode 100644 index 000000000..422c9534d --- /dev/null +++ b/nonmatching/functional/func_00342978.c @@ -0,0 +1,87 @@ +/* functional: low. Same as the near miss draft (calls, arguments, order); only one captured state exists and it covers few branches: not every path was exercised. */ +/* func_00342978 - retained C reconstruction, not an exact match. + * Profile: -O2 -G8 -ffunction-sections. + * Measured in src/game/00342978-g8.c: 576/584 bytes. + * US: 0/28 local places; PAL: 0/28 local places; US2: 0/28 local places. + * No strict linked build was run because no local placement matched. + * Scroll, state and loop-bound GPR allocation and loop fill differ. Guarded bound captures and separate value lifetimes did not close it. Reopen with allocator evidence. + */ + +#include "common.h" + +extern int func_002E5E58(int, unsigned int, void *); +extern int D_001AE1CC __attribute__((sda)); + +int func_00342978(void *owner, int flags) +{ + char *p = owner; + int index; + int scroll; + int state; + int limit; + int final_state; + *(int *)(p + 0x94) += 1; + switch (*(int *)(p + 0x18)) { + case 0: + if ((flags & 0x1000) && *(int *)(p + 0x14) > 0) { + func_002E5E58(3, 0, 0); + *(int *)(p + 0x1C) = 1; + *(int *)(p + 0x18) = 1; + break; + } + if ((flags & 0x4000) && *(int *)(p + 0x14) < *(int *)(p + 0x10) - 1) { + func_002E5E58(3, 0, 0); + *(int *)(p + 0x18) = 1; + *(int *)(p + 0x1C) = 0; + } + break; + case 1: + *(int *)(p + 0x18) = 2; + *(int *)(p + 0x90) = 0; + case 2: + scroll = *(int *)(p + 0x90); + if (*(int *)(p + 0x1C) == 0) scroll -= 4; + else scroll += 4; + *(int *)(p + 0x90) = scroll; + if ((float)scroll >= *(float *)(p + 0xC) || (float)scroll <= -*(float *)(p + 0xC)) { + if (*(int *)(p + 0x1C) == 0) index = ++*(int *)(p + 0x14); + else index = --*(int *)(p + 0x14); + *(int *)(p + 0x14) = index; + if (index < 0) *(int *)(p + 0x14) = 0; + if (*(int *)(p + 0x14) > *(int *)(p + 0x10) - 1) + *(int *)(p + 0x14) = *(int *)(p + 0x10) - 1; + state = flags ? *(int *)(p + 0x18) : 0; + *(int *)(p + 0x18) = state; + final_state = 0; + if (*(int *)(p + 0x14) != 0) + final_state = *(int *)(p + 0x14) == *(int *)(p + 0x10) - 1 ? 0 : state; + *(int *)(p + 0x18) = final_state; + *(int *)(p + 0x90) = 0; + } + break; + } + *(int *)(p + 0x20) = 0; + index = *(int *)(p + 0x14); + if (index > 0) { + limit = -D_001AE1CC; + if (limit < 0) { + do { + index--; + *(int *)(p + 0x20) -= 1; + } while (index > 0 && *(int *)(p + 0x20) > limit); + } + } + *(int *)(p + 0x24) = 0; + index = *(int *)(p + 0x14); + state = *(int *)(p + 0x10) - 1; + if (index < state) { + limit = D_001AE1CC; + if (limit > 0) { + do { + index++; + *(int *)(p + 0x24) += 1; + } while (index < state && *(int *)(p + 0x24) < limit); + } + } + return 0; +} \ No newline at end of file diff --git a/nonmatching/functional/func_003458F8.c b/nonmatching/functional/func_003458F8.c new file mode 100644 index 000000000..507a81da9 --- /dev/null +++ b/nonmatching/functional/func_003458F8.c @@ -0,0 +1,71 @@ +/* functional: medium. Only one captured call, so the pad branches are read from the assembly, not exercised. Menu cursor handler: pad bits 0x1000 up, 0x4000 down, 0x8000 left, 0x2000 right (each plays sound 3 and moves the column at +0x48C / row at +0x490 with the retail wrap rules), 0x40 select; then looks up the 10-byte entry at 0x259CE0 for (col + row*3), stores its id at +0x494 and, when the id is flagged, copies a short from the 0xE0-byte table into the gp-relative word; written as the same branches and writes in the same order, only codegen differs. */ +extern void func_002E5E58(int a, int b, int c); +extern char D_00259CE0[]; +extern unsigned char D_001A7AF8[]; +extern unsigned char D_00139568[]; +extern char D_00239BA0[]; +extern int D_001F2880; +extern int D_001ADB10 __attribute__((sda)); + +void func_003458F8(char *self, int buttons) { + int x, y, id; + int *px = (int *)(self + 0x48C); + int *py = (int *)(self + 0x490); + if (buttons & 0x1000) { + func_002E5E58(3, 0, 0); + y = *py - 1; + *py = y; + if (y < 0) { + *py = 0; + *(int *)(self + 0x498) = 1; + } + } else if (buttons & 0x4000) { + func_002E5E58(3, 0, 0); + y = *py; + if (y == 0) { + if (*px == 0) { + *py = 1; + *px = 1; + } else { + *py = 1; + } + } else if (y == 1) { + *(int *)(self + 0x498) = 3; + x = *px; + *py = 0; + *px = x - 1; + } + } else if (buttons & 0x8000) { + func_002E5E58(3, 0, 0); + x = *px - 1; + *px = x; + if (*py == 0) { + *px = (x > -1) ? x : 2; + } else { + *px = (x > 0) ? x : 2; + } + } else if (buttons & 0x2000) { + func_002E5E58(3, 0, 0); + x = *px + 1; + *px = x; + if (*py == 0) { + *px = (x > 2) ? 0 : x; + } else { + *px = (x > 2) ? 1 : x; + } + } else if (buttons & 0x40) { + int sel = *(short *)(D_00259CE0 + (*px + *py * 3) * 10 + 6); + if (sel != 0 && D_001A7AF8[sel] != 0) { + func_002E5E58(4, 0, 0); + D_001F2880 = sel; + *(int *)(self + 0x494) = sel; + return; + } + func_002E5E58(5, 0, 0); + } + id = *(short *)(D_00259CE0 + (*px + *py * 3) * 10 + 6); + *(int *)(self + 0x494) = id; + if (id != 0 && D_001A7AF8[id] != 0) { + D_001ADB10 = *(short *)(D_00239BA0 + D_00139568[id] * 0xE0 + 6); + } +} diff --git a/nonmatching/functional/func_00345F70.c b/nonmatching/functional/func_00345F70.c new file mode 100644 index 000000000..81f8f0455 --- /dev/null +++ b/nonmatching/functional/func_00345F70.c @@ -0,0 +1,70 @@ +/* functional: medium. Same reads, writes and calls in retail order, checked on one captured state (one mode only); the offset table is read through one array declaration where retail reads some entries gp-relative and some absolute, which reaches the same words. */ +#include "include_asm.h" + +extern int func_002A9588(unsigned int, unsigned int, int, int, int); +extern int *func_00335E20(void *); +extern void func_00335E38(void *, float, float, float, float); +extern void func_003458F8(void *, int); +extern void func_00345B78(void *, int); +extern void func_00345D58(void *, int); +extern int D_00138344; +extern int D_001ADB10; +extern float D_001AE2A8[2]; +extern float D_001AE3F0[]; + +#define OFS(base, o) ((float *)((char *)(base) + (o))) + +int func_00345F70(char *obj, int buttons, int *out) +{ + float tmp[2]; + int mode; + int *p; + float *pos; + + *out = *(int *)(obj + 0x498); + *(int *)(obj + 0x494) = 0; + mode = *(int *)(obj + 0x498); + D_001ADB10 = -1; + tmp[0] = D_001AE2A8[0]; + tmp[1] = D_001AE2A8[1]; + if (mode == 1) { + *out = mode; + func_00345D58(obj, buttons); + } else if (mode < 2) { + if (mode == 0) { + *out = 0; + func_003458F8(obj, buttons); + } + } else if (mode == 3) { + *out = mode; + func_00345B78(obj, buttons); + } + if (D_00138344 & 0xF000) + func_002A9588(0, 0, 1, 0, 1); + p = func_00335E20(obj + 0x17C); + *p = func_002A9588(0x60442D00, 0x70FFFEED, 0x14, 0, 0); + + pos = *(float **)(obj + 0x400); + func_00335E38(obj, D_001AE3F0[0] + pos[0], D_001AE3F0[1] + pos[1], 0.0f, 0.0f); + pos = *(float **)(obj + 0x400); + func_00335E38(obj + 0x4C, D_001AE3F0[8] + pos[0], D_001AE3F0[9] + pos[1], 0.0f, 0.0f); + pos = *(float **)(obj + 0x400); + func_00335E38(obj + 0x98, D_001AE3F0[2] + pos[0], D_001AE3F0[3] + pos[1], 0.0f, 0.0f); + pos = *(float **)(obj + 0x400); + func_00335E38(obj + 0xE4, D_001AE3F0[4] + pos[0], D_001AE3F0[5] + pos[1], 0.0f, 0.0f); + pos = *(float **)(obj + 0x400); + func_00335E38(obj + 0x130, D_001AE3F0[6] + pos[0], D_001AE3F0[7] + pos[1], 0.0f, 0.0f); + pos = *(float **)(obj + 0x400); + func_00335E38(obj + 0x1C8, tmp[0] + pos[0], tmp[1] + pos[1], 0.0f, 0.0f); + pos = *(float **)(obj + 0x400); + func_00335E38(obj + 0x2E0, D_001AE3F0[12] + pos[0], D_001AE3F0[13] + pos[1], 0.0f, 0.0f); + pos = *(float **)(obj + 0x400); + func_00335E38(obj + 0x298, D_001AE3F0[10] + pos[0], D_001AE3F0[11] + pos[1], 0.0f, 0.0f); + pos = *(float **)(obj + 0x400); + func_00335E38(obj + 0x328, D_001AE3F0[14] + pos[0], D_001AE3F0[15] + pos[1], 0.0f, 0.0f); + pos = *(float **)(obj + 0x400); + func_00335E38(obj + 0x370, D_001AE3F0[16] + pos[0], D_001AE3F0[17] + pos[1], 0.0f, 0.0f); + pos = *(float **)(obj + 0x400); + func_00335E38(obj + 0x3B8, D_001AE3F0[18] + pos[0], D_001AE3F0[19] + pos[1], 0.0f, 0.0f); + return 0; +} diff --git a/nonmatching/functional/func_00346C60.c b/nonmatching/functional/func_00346C60.c new file mode 100644 index 000000000..4ce40821e --- /dev/null +++ b/nonmatching/functional/func_00346C60.c @@ -0,0 +1,142 @@ +/* functional: medium (fcheck covers 1 call only; the button branches were not all exercised). Hand-decoded from the assembly (not attempted as a match): a 6x4 menu grid cursor (rows at +0x500, columns at +0x4FC), the same calls in the same order with the same arguments; the 8-byte copies from 0x1AE508.. are word copies here. The zero-initialised and copied pairs are positions (x, y) placed with func_00335E38. */ +extern void func_002E5E58(int, int, int); +extern void func_00115484(void *, int, int); +extern int func_002A9588(int, int, int, int, int); +extern int *func_00335E20(void *); +extern void func_00335E38(void *, float, float, float, float); +extern char *func_00288FB8(int); +extern int func_00115DA8(char *, char *, ...); + +extern float D_001AE508[2]; +extern float D_001AE510[2]; +extern float D_001AE518[2]; +extern float D_001AE520[2]; +extern char D_001AE528[]; +extern char D_001AE530[]; +extern char D_001AE538[]; +extern char D_001ADF98[]; +extern char D_001AE548[]; +extern char D_001ADBC8[]; +extern int D_001ADB10; +extern int D_001F2880; +extern unsigned char D_001A7AF8[]; +extern unsigned char D_001A7B30[]; +extern unsigned char D_00139568[]; +extern int D_00139688[]; + +typedef struct { + unsigned char pad0[4]; + unsigned char flag4; + unsigned char pad5; + short s6; + int w8; + unsigned char padC[0x88 - 0xC]; + unsigned short h88; + unsigned char pad8a[4]; + unsigned short h8e; + unsigned char pad90[0xE0 - 0x90]; +} Rec346C60; +extern Rec346C60 D_00239BA0[]; +extern unsigned char D_00259F58[]; + +int func_00346C60(unsigned char *obj, int buttons) +{ + float a[2], b[2], c[2], d[2], e[2], f[2], g[2], h[2]; + int old, fp, ret = -1; + int *res; + + old = *(int *)(obj + 0x4FC) + *(int *)(obj + 0x500) * 4; + if (buttons & 0x1000) { + int r; + func_002E5E58(3, 0, 0); + r = *(int *)(obj + 0x500) - 1; + *(int *)(obj + 0x500) = (-1 < r) ? r : 5; + } else if (buttons & 0x4000) { + int r; + func_002E5E58(3, 0, 0); + r = *(int *)(obj + 0x500) + 1; + *(int *)(obj + 0x500) = (5 < r) ? 0 : r; + } else if (buttons & 0x8000) { + int r; + func_002E5E58(3, 0, 0); + r = *(int *)(obj + 0x4FC) - 1; + *(int *)(obj + 0x4FC) = (-1 < r) ? r : 3; + } else if (buttons & 0x2000) { + int r; + func_002E5E58(3, 0, 0); + r = *(int *)(obj + 0x4FC) + 1; + *(int *)(obj + 0x4FC) = (3 < r) ? 0 : r; + } + fp = *(int *)(obj + 0x4FC) + *(int *)(obj + 0x500) * 4; + func_00115484(a, 0, 8); + func_00115484(b, 0, 8); + func_00115484(c, 0, 8); + func_00115484(d, 0, 8); + e[0] = D_001AE508[0]; e[1] = D_001AE508[1]; + func_00115484(f, 0, 8); + g[0] = D_001AE510[0]; g[1] = D_001AE510[1]; + h[0] = D_001AE518[0]; h[1] = D_001AE518[1]; + { + float k[2]; + k[0] = D_001AE520[0]; k[1] = D_001AE520[1]; + if (old != fp) + func_002A9588(0, 0, 1, 0, 1); + res = func_00335E20(obj + 0xE4); + *res = func_002A9588(0x60442D00, 0x70FFFEED, 0x14, 0, 0); + func_00335E38(obj + 0, a[0] + (*(float **)(obj + 0x498))[0], a[1] + (*(float **)(obj + 0x498))[1], 0.0f, 0.0f); + func_00335E38(obj + 0x4C, b[0] + (*(float **)(obj + 0x498))[0], b[1] + (*(float **)(obj + 0x498))[1], 0.0f, 0.0f); + func_00335E38(obj + 0x130, c[0] + (*(float **)(obj + 0x498))[0], c[1] + (*(float **)(obj + 0x498))[1], 0.0f, 0.0f); + func_00335E38(obj + 0x98, d[0] + (*(float **)(obj + 0x498))[0], d[1] + (*(float **)(obj + 0x498))[1], 0.0f, 0.0f); + func_00335E38(obj + 0xE4, e[0] + (*(float **)(obj + 0x498))[0] + (float)*(int *)(obj + 0x4FC) * 53.5f, + e[1] + (*(float **)(obj + 0x498))[1] + (float)*(int *)(obj + 0x500) * 47.0f, 0.0f, 0.0f); + func_00335E38(obj + 0x17C, f[0] + (*(float **)(obj + 0x498))[0], f[1] + (*(float **)(obj + 0x498))[1], 0.0f, 0.0f); + func_00335E38(obj + 0x210, g[0] + (*(float **)(obj + 0x498))[0], g[1] + (*(float **)(obj + 0x498))[1], 0.0f, 0.0f); + D_001ADB10 = -1; + if (fp >= 0) { + int s5 = *(short *)(D_00259F58 + fp * 10 + 6); + unsigned char *s3 = D_00139568 + s5; + if (D_00239BA0[*s3].flag4 != 0) { + D_001A7AF8[s5] = 1; + D_001A7B30[s5] = 1; + } + if (D_001A7AF8[s5] != 0) { + char *s0 = (char *)obj + 0x4B8; + char *s1 = (char *)obj + 0x4A0; + int val, val2, n; + D_001ADB10 = D_00239BA0[*s3].s6; + func_00115DA8(s0, func_00288FB8(D_00239BA0[*s3].w8)); + val = D_00139688[s5]; + val2 = D_00239BA0[*s3].h8e; + if (D_00239BA0[*s3].h88 != 0) { + n = func_00115DA8(s1, D_001AE528, func_00288FB8(0x2DC9)); + s1 += n; + if (val < 1000) { + n = func_00115DA8(s1, D_001AE530, val); + } else { + n = func_00115DA8(s1, D_001AE538, val / 1000, val % 1000); + } + s1 += n; + if (val2 < 1000) + func_00115DA8(s1, D_001ADF98, val2); + else + func_00115DA8(s1, D_001AE548, val2 / 1000, val2 % 1000); + } else { + func_00115DA8(s1, D_001ADBC8, func_00288FB8(0x2DB9)); + } + if (buttons & 0x40) { + func_002E5E58(4, 0, 0); + ret = fp; + D_001F2880 = s5; + } + } else { + obj[0x4A0] = 0; + obj[0x4B8] = 0; + if (buttons & 0x40) + func_002E5E58(5, 0, 0); + } + } + func_00335E38(obj + 0x258, h[0] + (*(float **)(obj + 0x498))[0], h[1] + (*(float **)(obj + 0x498))[1], 0.0f, 0.0f); + func_00335E38(obj + 0x2A0, k[0] + (*(float **)(obj + 0x498))[0], k[1] + (*(float **)(obj + 0x498))[1], 0.0f, 0.0f); + } + return ret; +} diff --git a/nonmatching/functional/func_00347F80.c b/nonmatching/functional/func_00347F80.c new file mode 100644 index 000000000..4a8b296ca --- /dev/null +++ b/nonmatching/functional/func_00347F80.c @@ -0,0 +1,37 @@ +/* functional: high. Near-miss draft that differs from retail in register allocation and scheduling only; fcheck SAME on all captured calls. */ +/* USA trial: 220 bytes versus 224 expected, with 0 of 28 placements matching. */ +extern int func_002E5E58(int, int, void *); + +int func_00347F80(char *object, int flags) +{ + int direction = 0; + int index; + int last; + int original; + void **entries; + + if (flags & 0x1000) { + direction = -1; + } else if (flags & 0x4000) { + direction = 1; + } else if (flags & 0x40) { + if (*(void **)(object + 0x5C + *(int *)(object + 0x50) * 4) != 0) + return 1; + } + if (direction != 0) { + original = *(int *)(object + 0x50); + func_002E5E58(3, 0, 0); + last = *(int *)(object + 0xB0) - 1; + entries = (void **)(object + 0x5C); + index = *(int *)(object + 0x50); + do { + index += direction; + if (last < index) + index = 0; + if (index < 0) + index = last; + *(int *)(object + 0x50) = index; + } while (entries[index] == 0 && index != original); + } + return 0; +} diff --git a/nonmatching/functional/func_003489E8.c b/nonmatching/functional/func_003489E8.c new file mode 100644 index 000000000..e575fb10e --- /dev/null +++ b/nonmatching/functional/func_003489E8.c @@ -0,0 +1,115 @@ +/* functional: medium. Menu list update: sets the visibility of the seven sub-widgets from the mode flag at +0x44C, positions five widgets from a constant table plus the origin at +0x434, lays out the item rows, then moves the selection on the pad's up/down bits (skipping empty entries), highlights it and returns the selected index. Field names are unknown; stores, calls and branches follow retail. */ +typedef unsigned char u8; + +extern float D_001AE5EC; +extern float D_001AE760[10]; + +extern void func_00335E68(u8 *w, int on); +extern void func_003363C0(u8 *w, float f); +extern float *func_00335E10(u8 *w); +extern unsigned int *func_00335E20(u8 *w); +extern void func_00335F88(u8 *w, float f); +extern void func_002E5E58(int a, int b, int c); +extern void func_002A9588(int a, int b, int c, int d, int e); + +int func_003489E8(u8 *m, int pad) +{ + u8 *w98 = m + 0x98; + u8 *w4C = m + 0x4C; + u8 *w130 = m + 0x130; + u8 *w17C = m + 0x17C; + u8 *w1C8 = m + 0x1C8; + u8 *wE4 = m + 0xE4; + u8 *a, *b, *c, *d; + float f; + int pressed, step, i, n, idx, v, start, lim; + float *p; + float **org = (float **)(m + 0x434); + + func_00335E68(m, *(int *)(m + 0x44C) == 0); + func_00335E68(w98, *(int *)(m + 0x44C) == 0); + func_00335E68(w4C, *(int *)(m + 0x44C) == 0); + func_00335E68(wE4, *(int *)(m + 0x44C) == 0); + func_00335E68(w130, *(int *)(m + 0x44C) == 0); + func_00335E68(w17C, (*(int *)(m + 0x44C) ^ 1) == 0); + func_00335E68(w1C8, (*(int *)(m + 0x44C) ^ 1) == 0); + func_003363C0(w17C, D_001AE5EC); + func_003363C0(w1C8, D_001AE5EC); + + if (*(int *)(m + 0x44C) == 0 && *(int *)(m + 0x448) != 0) { + f = 1.0f; + func_003363C0(m, f); + func_003363C0(w98, f); + func_003363C0(w4C, f); + func_003363C0(w130, f); + (*org)[1] = 170.0f; + } else { + f = 0.0f; + func_003363C0(m, f); + func_003363C0(w98, f); + func_003363C0(w4C, f); + func_003363C0(w130, f); + (*org)[1] = 190.0f; + } + + a = w130; b = w4C; c = w98; d = wE4; + pressed = pad & 0x1000; + + p = func_00335E10(m); + p[0] = D_001AE760[0] + (*org)[0]; + p[1] = D_001AE760[1] + (*org)[1]; + p = func_00335E10(a); + p[0] = D_001AE760[2] + (*org)[0]; + p[1] = D_001AE760[3] + (*org)[1]; + p = func_00335E10(b); + p[0] = D_001AE760[4] + (*org)[0]; + p[1] = D_001AE760[5] + (*org)[1]; + p = func_00335E10(c); + p[0] = D_001AE760[6] + (*org)[0]; + p[1] = D_001AE760[7] + (*org)[1]; + p = func_00335E10(d); + p[0] = D_001AE760[8] + (*org)[0]; + p[1] = D_001AE760[9] + (*org)[1]; + + n = *(int *)(m + 0x444); + for (i = 0; i < *(int *)(m + 0x444); i++) { + u8 *row = m + 0x218 + i * 0x48; + float *src; + p = func_00335E10(row); + src = (float *)(*(int *)(m + 0x414) + i * 12); + p[0] = src[0] + (*org)[0]; + src = (float *)(*(int *)(m + 0x414) + i * 12); + p[1] = src[1] + (*org)[1]; + func_00335F88(row, ((float *)(*(int *)(m + 0x414) + i * 12))[2]); + if (*(int *)(m + 0x418 + i * 4) != 0) + *func_00335E20(row) = 0x80F0F0F0; + else + *func_00335E20(row) = 0x80808080; + } + + step = 0; + if (pressed) { + func_002E5E58(3, 0, 0); + step = -1; + } else if (pad & 0x4000) { + func_002E5E58(3, 0, 0); + step = 1; + } + if (step != 0) { + start = *(int *)(m + 0x43C); + func_002A9588(0, 0, 1, 0, 1); + lim = n - 1; + do { + v = *(int *)(m + 0x43C) + step; + if (lim < v) + v = 0; + idx = (v > -1) ? v : lim; + *(int *)(m + 0x43C) = idx; + if (*(int *)(m + 0x418 + idx * 4) != 0) + break; + } while (start != idx); + } + func_003363C0(wE4, (float)*(int *)(m + 0x43C)); + *func_00335E20(m + *(int *)(m + 0x43C) * 0x48 + 0x218) = 0x70FFFEED; + return *(int *)(m + 0x43C); +} diff --git a/nonmatching/functional/func_0034B2C8.c b/nonmatching/functional/func_0034B2C8.c new file mode 100644 index 000000000..27b85aa3c --- /dev/null +++ b/nonmatching/functional/func_0034B2C8.c @@ -0,0 +1,424 @@ +/* functional: medium. Near-miss draft written from m2c and the assembly (see check below). */ +/* Near miss / handoff (Opus big14, 2026-10-26): func_0034B2C8, 6,048 bytes, boot + 27 level + * places, the ammo/bolt HUD setup (one function: one prologue, one jr $ra). Written from m2c + * and the assembly; no twin found. + * + * Profile: default -O2 -G0 -ffunction-sections in its own unit (src/game/0034B2C8.c). + * Last verdict: tools/check.py -> 5988 bytes, expected 6048, 0/28. Opcodes match through the + * first 356 instructions with the same registers; 100 opcode hunks / about 160 instructions. + * The frame is 0x150 against 0x160 (retail spills 37 pointer slots, this spills 35). + * + * Measured so far: + * - Writing every widget as an inline self + N expression reproduces retail's shape: gcse + * hoists the second-half addresses into the block after the arg check and makes the + * copies retail spills (sw 0x9C <- 0x14 and so on), and the f26/f23 copies of 1.0/0.0. + * - The *func_00335E20(w) colour stores after the arg check go through one s32 local + * (retail keeps it in $s3 across the calls); the first half uses literals. + * - Colour words passed as float bits: denormal values stay as int-in-GPR + mtc1 (v0 form), + * normal single uses fold to li.s ($at form), both as retail. Normal values used twice + * (0x60442D00, 0xF0F0B0, 0x55F0C070) come out li.s/hoisted (union helper) or in a callee- + * saved GPR (pointer-cast helper, this draft); retail loads them via $v0 into $f20 right + * before the first use. Neither spelling found; the double-use form is the main open item. + * - Open as well: the loop's mult temporary (retail builds i*0x4C+0x358 in $s0 itself), + * self+0xE94 lands in $s2 here (spilled in retail), and the spill-slot order of the + * second-half pointers (it follows pseudo numbers, so it should follow once the + * expression set matches). + * + * closer3 (2026-10-26), the double-use colour: retail needs one SFmode pseudo set once from + * an SImode constant (lui/ori $v0, mtc1 $v0,$f20) and read twice. In this cc1 every + * spelling loses that pseudo: a union read `u.f` expands straight into $f13 and cse's + * fold_rtx SUBREG case (gen_lowpart_if_possible of the known SImode constant) turns it into + * a const_double (li.s); `f32 c = u.f` keeps a pseudo but cse folds it to a hoisted li.s; a + * pointer pun (local or inline, `*(s32 *)&c = X`) is purged to an SImode pseudo that lives in + * a saved GPR with an mtc1 per use. (Why denormals escape the fold is not checked yet.) + * Look at whether retail's cse folds a SUBREG of a constant into SFmode at all (patch 0048 + * brings 2.95.2's cse.c), before more spellings. The giv-recombination change + * (no_recombine_givs.py) does not change this function. + */ +#include "common.h" + +typedef union { + s32 i; + f32 f; +} HudBits_34B2C8; + +extern char D_001AE668[]; +extern char D_001AE678[]; +extern char D_001AE688[]; +extern char D_001AE690[]; +extern char D_001AE6A0[]; +extern char D_001AE6B0[]; +extern char D_001AE6C0[]; +extern char D_001AE6D0[]; +extern char D_001AE6E0[]; +extern char D_001ADFE0[]; +extern char D_001ADFF0[]; +extern char D_001AE000[]; +extern char D_001AE010[]; +extern char D_001AE020[]; +extern s32 D_001A7B90; +extern s32 D_001A79F8; + +extern void func_003361E0(char *o, char *text, s32 arg); +extern void func_00336390(char *o, char *font, s32 n); +extern void func_00335E38(char *o, f32 x, f32 y, f32 z, f32 w); +extern void func_00335F88(char *o, f32 x, f32 y, f32 z, f32 w); +extern s32 *func_00335E20(char *o); +extern s32 *func_00335E18(char *o); +extern s32 *func_00335E10(char *o); +extern void func_00115484(void *dst, s32 c, s32 n); +extern void func_003369A8(char *o, char *text, s32 arg); +extern void func_00336A38(char *o, char *buf); +extern void func_00336A30(char *o, s32 n); +extern void func_00336710(char *o, char *text, s32 arg); +extern void func_00336920(char *o, s32 id, s32 n); +extern s32 func_00336E40(s32 arg); +extern f32 *func_00336DA8(s32 n, s32 a); +extern void func_00335F20(char *o, f32 *v); +extern void func_00335E68(char *o, s32 n); +extern void func_00115DA8(char *buf, char *fmt, s32 n); +extern f32 func_00283B98(f32 a, f32 b); +extern f32 func_00283180(f32 a); +extern f32 func_00283198(f32 a); +extern void func_00335EB0(char *o, s32 *v); +extern void func_00349D58(char *o, char *font, s32 arg); +extern void func_0034AFE8(char *o, f32 x, f32 y); +extern void func_003494D0(char *o, s32 arg); +extern void func_00349628(char *o, s32 n); +extern void func_003495D8(f32 weight, f32 x, f32 y, f32 z, f32 w, char *o, s32 index); +extern void func_00349610(char *o, s32 n, f32 a, f32 b); +extern void func_00349590(char *o, void *v); +extern void func_00349670(char *o, s32 v); +extern void func_003495C0(char *o, char *v); +extern void func_00349630(char *o, s32 v); +extern void func_00349678(char *o, f32 v); +extern s32 func_003495C8(char *o, s32 v); +extern void func_00349970(char *o, s32 arg); +extern void func_00349A60(char *o, s32 n, void *v); +extern void func_00349AA8(char *o, s32 n, s32 v); +extern void func_00349A70(f32 x, f32 y, f32 z, f32 w, char *o, s32 row, s32 group); +extern void func_00349B18(char *o, f32 v); +extern void func_0034CA90(char *o, s32 mode); +extern s32 func_002B0D70(void); + +static inline f32 hud_bits_34B2C8(s32 i) +{ + f32 f; + + *(s32 *)&f = i; + return f; +} + +/* The ammo/bolt HUD setup: initialises the HUD's text, sprite and fade widgets. */ +void func_0034B2C8(char *self, char *font, s32 arg) +{ + char buf[16]; + s32 i; + s32 color; + f32 *v; + + *(s32 *)(self + 0x157C) = 0; + *(s32 *)(self + 0x1580) = 0; + func_003361E0(self, D_001AE668, arg); + func_00336390(self, font + 0x8710, 1); + func_00335E38(self, 10.0f, 10.0f, 0.0f, 0.0f); + func_003361E0(self + 0x4C, D_001AE678, arg); + func_00335E38(self + 0x4C, 10.0f, 10.0f, 0.0f, 0.0f); + func_00336390(self + 0x4C, font + 0x8710, 2); + func_003361E0(self + 0x98, D_001AE688, arg); + func_00335E38(self + 0x98, 10.0f, 18.5f, 0.0f, 0.0f); + func_00336390(self + 0x98, font + 0x8710, 5); + func_00115484(self + 0xE4, 0, 0x18); + func_003369A8(self + 0x100, D_001AE690, arg); + func_00335F88(self + 0x100, 0.9f, 0.0f, 0.0f, 0.0f); + func_00336A38(self + 0x100, self + 0xE4); + func_00335E38(self + 0x100, 64.0f, 15.0f, 0.0f, 0.0f); + func_00336A30(self + 0x100, 0); + func_00336710(self + 0x148, D_001AE6A0, arg); + func_00335F88(self + 0x148, 32.0f, 32.0f, 0.0f, 0.0f); + func_00335E38(self + 0x148, 20.0f, 15.0f, 0.0f, 0.0f); + *func_00335E20(self + 0x148) = 0x60F0F0B0; + func_003361E0(self + 0x184, D_001AE6B0, arg); + func_00335E38(self + 0x184, 498.0f, 10.0f, 0.0f, 0.0f); + func_00336390(self + 0x184, font + 0x8710, 3); + func_003361E0(self + 0x1D0, D_001AE6C0, arg); + func_00335E38(self + 0x1D0, 498.0f, 10.0f, 0.0f, 0.0f); + func_00336390(self + 0x1D0, font + 0x8710, 4); + func_00115484(self + 0x21C, 0, 0x18); + func_003369A8(self + 0x238, D_001AE6D0, arg); + func_00335F88(self + 0x238, 0.9f, 0.0f, 0.0f, 0.0f); + func_00336A38(self + 0x238, self + 0x21C); + func_00335E38(self + 0x238, 440.0f, 15.0f, 0.0f, 0.0f); + func_00336A30(self + 0x238, 2); + func_00336710(self + 0x280, D_001AE6E0, arg); + func_00335F88(self + 0x280, 32.0f, 32.0f, 0.0f, 0.0f); + func_00335E38(self + 0x280, 457.0f, 15.0f, 0.0f, 0.0f); + func_00336920(self + 0x280, 0x7567, 0); + + if (arg != 0) { + v = func_00336DA8(0x10, func_00336E40(arg)); + *(f32 **)(self + 0x7E4) = v; + v[0] = 0.0f; + v[1] = 0.0f; + v[2] = 0.0f; + v[3] = 0.0f; + } + + func_003361E0(self + 0x2C0, D_001ADFE0, arg); + func_00336390(self + 0x2C0, font + 0x8710, 7); + func_00335F88(self + 0x2C0, 1.0f, 1.0f, 0.0f, 0.0f); + func_00335E38(self + 0x2C0, 125.0f, 208.0f, 0.0f, 0.0f); + color = 0x442D00; + *func_00335E20(self + 0x2C0) = color; + func_00335F20(self + 0x2C0, *(f32 **)(self + 0x7E4)); + func_003361E0(self + 0x30C, D_001ADFF0, arg); + func_00336390(self + 0x30C, font + 0x8710, 6); + func_00335F88(self + 0x30C, 1.0f, 1.0f, 0.0f, 0.0f); + func_00335E38(self + 0x30C, 125.0f, 208.0f, 0.0f, 0.0f); + color = 0xF0C070; + *func_00335E20(self + 0x30C) = color; + func_00335F20(self + 0x30C, *(f32 **)(self + 0x7E4)); + func_003361E0(self + 0x5B8, D_001AE000, arg); + func_00336390(self + 0x5B8, font + 0x8710, 0x10); + func_00335E38(self + 0x5B8, 125.0f, 208.0f, 0.0f, 0.0f); + color = 0xFFFEED; + *func_00335E20(self + 0x5B8) = color; + func_00335E68(self + 0x5B8, 0); + + for (i = 0; i < 8; i++) { + char *w; + f32 angle; + f32 x; + + func_00115DA8(buf, D_001AE010, i); + w = self + (i * 0x4C + 0x358); + func_003361E0(w, buf, arg); + func_00336390(w, font + 0x8710, i + 8); + func_00335F88(w, 1.0f, 1.0f, 0.0f, 0.0f); + func_00335E38(w, 125.0f, 208.0f, 0.0f, 0.0f); + func_00335F20(w, *(f32 **)(self + 0x7E4)); + color = 0x442D00; + *func_00335E20(w) = color; + func_00115DA8(buf, D_001AE020, i); + w = self + (i * 0x3C + 0x604); + func_00336710(w, buf, arg); + func_00335E68(w, 0); + angle = func_00283B98((f32)i * 2.0f * 0.39269909f - 3.14159274f, 1.57079637f); + x = (s32)(func_00283180(angle) * 82.14f) + 109; + func_00335E38(w, x, (s32)(func_00283198(angle) * 76.442f) + 189, 0.0f, 0.0f); + func_00335F88(w, 32.0f, 32.0f, 0.0f, 0.0f); + color = 0xF0F0B0; + *func_00335E20(w) = color; + } + for (i = 1; i < 8; i++) { + func_00335EB0(self + 0x358 + i * 0x4C, func_00335E18(self + 0x358)); + func_00335EB0(self + 0x604 + i * 0x3C, func_00335E18(self + 0x604)); + } + func_00335EB0(self + 0x2C0, func_00335E18(self + 0x358)); + + func_00349D58(self + 0x7E8, font, arg); + func_0034AFE8(self + 0x7E8, 147.0f, 10.0f); + *(s32 *)(self + 0x7F4) = 0; + + func_003494D0(self + 0xBEC, arg); + func_00349628(self + 0xBEC, 3); + func_003495D8(0.0f, hud_bits_34B2C8(0x442D00), 0.0f, 0.0f, 0.0f, self + 0xBEC, 0); + func_003495D8(0.3f, hud_bits_34B2C8(0x60442D00), 0.0f, 0.0f, 0.0f, self + 0xBEC, 1); + func_003495D8(1.0f, hud_bits_34B2C8(0x60442D00), 0.0f, 0.0f, 0.0f, self + 0xBEC, 2); + func_00349610(self + 0xBEC, 1, 0.0f, 2.0f); + func_00349590(self + 0xBEC, func_00335E20(self + 0x358)); + func_00349670(self + 0xBEC, 0); + func_003495C0(self + 0xBEC, self + 0x1448); + func_00349630(self + 0xBEC, -1); + func_00349678(self + 0xBEC, D_001A7B90 != 0 ? 0.14f : 0.116666675f); + func_003495C8(self + 0xBEC, 0); + + func_003494D0(self + 0xC74, arg); + func_00349628(self + 0xC74, 3); + func_003495D8(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, self + 0xC74, 0); + func_003495D8(0.3f, 1.0f, 1.0f, 0.0f, 0.0f, self + 0xC74, 1); + func_003495D8(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, self + 0xC74, 2); + func_00349590(self + 0xC74, func_00335E18(self + 0x358)); + func_00349670(self + 0xC74, 0); + func_003495C0(self + 0xC74, self + 0x1444); + func_00349630(self + 0xC74, -1); + func_00349678(self + 0xC74, D_001A7B90 != 0 ? 0.14f : 0.116666675f); + func_003495C8(self + 0xC74, 0); + + func_003494D0(self + 0xE0C, arg); + func_00349628(self + 0xE0C, 3); + func_003495D8(0.0f, hud_bits_34B2C8(0xF0F0B0), 0.0f, 0.0f, 0.0f, self + 0xE0C, 0); + func_003495D8(0.5f, hud_bits_34B2C8(0xF0F0B0), 0.0f, 0.0f, 0.0f, self + 0xE0C, 1); + func_00349610(self + 0xE0C, 0, 0.0f, 0.0f); + func_003495D8(1.0f, hud_bits_34B2C8(0x60F0F0B0), 0.0f, 0.0f, 0.0f, self + 0xE0C, 2); + func_00349610(self + 0xE0C, 1, 0.0f, 0.0f); + func_00349590(self + 0xE0C, func_00335E20(self + 0x604)); + func_00349670(self + 0xE0C, 0); + func_003495C0(self + 0xE0C, self + 0x1448); + func_00349630(self + 0xE0C, -1); + func_00349678(self + 0xE0C, D_001A7B90 != 0 ? 0.14f : 0.116666675f); + func_003495C8(self + 0xE0C, 0); + + func_003494D0(self + 0xCFC, arg); + func_00349628(self + 0xCFC, 3); + func_003495D8(0.0f, hud_bits_34B2C8(0xF0C070), 0.0f, 0.0f, 0.0f, self + 0xCFC, 0); + func_003495D8(0.3f, hud_bits_34B2C8(0x55F0C070), 0.0f, 0.0f, 0.0f, self + 0xCFC, 1); + func_003495D8(1.0f, hud_bits_34B2C8(0x55F0C070), 0.0f, 0.0f, 0.0f, self + 0xCFC, 2); + func_00349610(self + 0xCFC, 1, 0.0f, 3.0f); + func_00349590(self + 0xCFC, func_00335E20(self + 0x30C)); + func_00349670(self + 0xCFC, 0); + func_003495C0(self + 0xCFC, self + 0x1448); + func_00349630(self + 0xCFC, -1); + func_00349678(self + 0xCFC, D_001A7B90 != 0 ? 0.14f : 0.116666675f); + func_003495C8(self + 0xCFC, 0); + + func_003494D0(self + 0xD84, arg); + func_00349628(self + 0xD84, 3); + func_003495D8(0.0f, 1.2f, 1.2f, 0.0f, 0.0f, self + 0xD84, 0); + func_003495D8(0.3f, 1.0f, 1.0f, 0.0f, 0.0f, self + 0xD84, 1); + func_003495D8(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, self + 0xD84, 2); + func_00349590(self + 0xD84, func_00335E18(self + 0x30C)); + func_00349670(self + 0xD84, 0); + func_003495C0(self + 0xD84, self + 0x1444); + func_00349630(self + 0xD84, -1); + func_00349678(self + 0xD84, D_001A7B90 != 0 ? 0.14f : 0.116666675f); + func_003495C8(self + 0xD84, 0); + + func_003494D0(self + 0xE94, arg); + func_00349628(self + 0xE94, 3); + func_003495D8(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, self + 0xE94, 0); + func_003495D8(0.01f, 1.0f, 1.0f, 0.0f, 0.0f, self + 0xE94, 1); + func_003495D8(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, self + 0xE94, 2); + func_00349590(self + 0xE94, *(f32 **)(self + 0x7E4)); + func_00349670(self + 0xE94, 0); + func_003495C0(self + 0xE94, self + 0x1444); + func_00349630(self + 0xE94, -1); + func_00349678(self + 0xE94, D_001A7B90 != 0 ? 0.14f : 0.116666675f); + func_003495C8(self + 0xE94, 0); + + func_00349970(self + 0xF1C, arg); + func_00349A60(self + 0xF1C, 0, func_00335E20(self)); + func_00349AA8(self + 0xF1C, 0, 0x442D00); + func_00349AA8(self + 0xF1C, 1, 0x60442D00); + *(f32 *)(self + 0xF1C + 0xC) = 2.0f; + *(f32 *)(self + 0xF1C + 0x8) = 0.0f; + func_00349A60(self + 0xF1C, 1, func_00335E18(self)); + func_00349A70(0.0f, 0.0f, 0.0f, 0.0f, self + 0xF1C, 1, 0); + func_00349A70(1.0f, 1.0f, 0.0f, 0.0f, self + 0xF1C, 1, 1); + func_00349A60(self + 0xF1C, 2, func_00335E10(self)); + func_00349A70(50.0f, 25.0f, 0.0f, 0.0f, self + 0xF1C, 2, 0); + func_00349A70(10.0f, 10.0f, 0.0f, 0.0f, self + 0xF1C, 2, 1); + + func_00349970(self + 0xFB0, arg); + func_00349A60(self + 0xFB0, 0, func_00335E20(self + 0x4C)); + func_00349AA8(self + 0xFB0, 0, 0xF0C070); + func_00349AA8(self + 0xFB0, 1, 0x55F0C070); + *(f32 *)(self + 0xF1C + 0xC) = 3.0f; + *(f32 *)(self + 0xF1C + 0x8) = 0.0f; + func_00349A60(self + 0xFB0, 1, func_00335E18(self + 0x4C)); + func_00349A70(1.2f, 1.2f, 0.0f, 0.0f, self + 0xFB0, 1, 0); + func_00349A70(1.0f, 1.0f, 0.0f, 0.0f, self + 0xFB0, 1, 1); + func_00349A60(self + 0xFB0, 2, func_00335E10(self + 0x4C)); + func_00349A70(0.0f, 0.0f, 0.0f, 0.0f, self + 0xFB0, 2, 0); + func_00349A70(10.0f, 10.0f, 0.0f, 0.0f, self + 0xFB0, 2, 1); + + func_00349970(self + 0x1044, arg); + func_00349A60(self + 0x1044, 0, func_00335E20(self + 0x100)); + func_00349AA8(self + 0x1044, 0, 0xF0F0F0); + func_00349AA8(self + 0x1044, 1, 0x80F0F0F0); + *(f32 *)(self + 0x1044 + 0xC) = 3.0f; + *(f32 *)(self + 0x1044 + 0x8) = 0.0f; + + func_003494D0(self + 0x10D8, arg); + func_00349628(self + 0x10D8, 3); + func_003495D8(0.0f, hud_bits_34B2C8(0x1EFF), 0.0f, 0.0f, 0.0f, self + 0x10D8, 0); + func_003495D8(0.5f, hud_bits_34B2C8(0x1EFF), 0.0f, 0.0f, 0.0f, self + 0x10D8, 1); + func_003495D8(1.0f, hud_bits_34B2C8(0x80001EFF), 0.0f, 0.0f, 0.0f, self + 0x10D8, 2); + func_00349590(self + 0x10D8, func_00335E20(self + 0x98)); + func_00349670(self + 0x10D8, 0); + func_003495C0(self + 0x10D8, self + 0x1448); + func_00349630(self + 0x10D8, 1); + func_00349678(self + 0x10D8, 0.06666f); + + func_00349970(self + 0x1160, arg); + func_00349A60(self + 0x1160, 0, func_00335E20(self + 0x148)); + func_00349AA8(self + 0x1160, 0, 0xF0F0B0); + func_00349AA8(self + 0x1160, 1, 0x60F0F0B0); + *(f32 *)(self + 0x1160 + 0xC) = 3.0f; + *(f32 *)(self + 0x1160 + 0x8) = 0.0f; + func_00349B18(self + 0x1160, 0.06666f); + + func_00349970(self + 0x11F4, arg); + func_00349A60(self + 0x11F4, 0, func_00335E20(self + 0x184)); + func_00349AA8(self + 0x11F4, 0, 0x442D00); + func_00349AA8(self + 0x11F4, 1, 0x60442D00); + *(f32 *)(self + 0x11F4 + 0xC) = 2.0f; + *(f32 *)(self + 0x11F4 + 0x8) = 0.0f; + func_00349A60(self + 0x11F4, 1, func_00335E18(self + 0x184)); + func_00349A70(0.0f, 0.0f, 0.0f, 0.0f, self + 0x11F4, 1, 0); + func_00349A70(1.0f, 1.0f, 0.0f, 0.0f, self + 0x11F4, 1, 1); + func_00349A60(self + 0x11F4, 2, func_00335E10(self + 0x184)); + func_00349A70(458.0f, 25.0f, 0.0f, 0.0f, self + 0x11F4, 2, 0); + func_00349A70(498.0f, 10.0f, 0.0f, 0.0f, self + 0x11F4, 2, 1); + + func_00349970(self + 0x1288, arg); + func_00349A60(self + 0x1288, 0, func_00335E20(self + 0x1D0)); + func_00349AA8(self + 0x1288, 0, 0xF0C070); + func_00349AA8(self + 0x1288, 1, 0x55F0C070); + *(f32 *)(self + 0x1288 + 0xC) = 3.0f; + *(f32 *)(self + 0x1288 + 0x8) = 0.0f; + func_00349A60(self + 0x1288, 1, func_00335E18(self + 0x1D0)); + func_00349A70(1.2f, 1.2f, 0.0f, 0.0f, self + 0x1288, 1, 0); + func_00349A70(1.0f, 1.0f, 0.0f, 0.0f, self + 0x1288, 1, 1); + *(f32 *)(self + 0x1288 + 0x4) = 3.0f; + *(f32 *)(self + 0x1288) = 0.0f; + func_00349A60(self + 0x1288, 2, func_00335E10(self + 0x1D0)); + func_00349A70(514.0f, 4.0f, 0.0f, 0.0f, self + 0x1288, 2, 0); + func_00349A70(498.0f, 10.0f, 0.0f, 0.0f, self + 0x1288, 2, 1); + + func_00349970(self + 0x131C, arg); + func_00349A60(self + 0x131C, 0, func_00335E20(self + 0x238)); + func_00349AA8(self + 0x131C, 0, 0xF0F0F0); + func_00349AA8(self + 0x131C, 1, 0x80F0F0F0); + *(f32 *)(self + 0x131C + 0xC) = 3.0f; + *(f32 *)(self + 0x131C + 0x8) = 0.0f; + + func_00349970(self + 0x13B0, arg); + func_00349A60(self + 0x13B0, 0, func_00335E20(self + 0x280)); + func_00349AA8(self + 0x13B0, 0, 0xF0F0B0); + func_00349AA8(self + 0x13B0, 1, 0x60F0F0B0); + *(f32 *)(self + 0x13B0 + 0xC) = 3.0f; + *(f32 *)(self + 0x13B0 + 0x8) = 0.0f; + + func_003494D0(self + 0x144C, arg); + func_00349628(self + 0x144C, 2); + func_00349590(self + 0x144C, func_00335E20(self)); + func_003495C0(self + 0x144C, self + 0x1448); + func_00349678(self + 0x144C, 0.02f); + func_003495D8(0.0f, hud_bits_34B2C8(0x70202080), 0.0f, 0.0f, 0.0f, self + 0x144C, 0); + func_003495D8(1.0f, hud_bits_34B2C8(0x70242335), 0.0f, 0.0f, 0.0f, self + 0x144C, 1); + func_00349670(self + 0x144C, 2); + func_00349630(self + 0x144C, 1); + func_003495C8(self + 0x144C, 0); + + func_003494D0(self + 0x14D4, arg); + func_00349628(self + 0x14D4, 2); + func_00349590(self + 0x14D4, func_00335E20(self + 0x4C)); + func_003495C0(self + 0x14D4, self + 0x1448); + func_00349678(self + 0x14D4, 0.02f); + func_003495D8(0.0f, hud_bits_34B2C8(0x606060C0), 0.0f, 0.0f, 0.0f, self + 0x14D4, 0); + func_003495D8(1.0f, hud_bits_34B2C8(0x60424162), 0.0f, 0.0f, 0.0f, self + 0x14D4, 1); + func_00349670(self + 0x14D4, 2); + func_00349630(self + 0x14D4, 1); + func_003495C8(self + 0x14D4, 0); + + *(s32 **)(self + 0x2BC) = &D_001A79F8; + *(s32 *)(self + 0x1564) = 0; + *(s32 *)(self + 0x1568) = -1; + *(s32 *)(self + 0x156C) = 0; + *(s32 *)(self + 0x1570) = -1; + *(s32 *)(self + 0x1574) = 0; + *(s32 *)(self + 0x1578) = -1; + func_0034CA90(self, 0); + *(s32 *)(self + 0x1588) = func_002B0D70(); +} diff --git a/nonmatching/functional/func_0034E070.c b/nonmatching/functional/func_0034E070.c new file mode 100644 index 000000000..90c423094 --- /dev/null +++ b/nonmatching/functional/func_0034E070.c @@ -0,0 +1,55 @@ +/* functional: high. Same calls and arguments in the same order as retail; only the register allocation differs. */ +#include "common.h" +extern int D_001A8CFC[] __attribute__((sda)); +extern char D_001AE710[]; +extern char D_001AE720[]; + +extern int func_00336D50(int, int); +extern void func_00336408(void *, int, int, void *, void *); +extern void func_00336CC8(void *, int); +extern void func_00336B90(void *, int); +extern void func_00336B98(void *, int); +extern void func_00336498(void *, int, int); +extern void func_003364B0(void *, int, int); +extern void func_00336CE0(void *, int); +extern void func_00336CC0(void *, int); +extern void func_0034B2C8(void *, int, int); +extern void func_00336710(void *, void *, void *); +extern int *func_00335E20(void *); +extern void *func_003367B8(void *); +extern void func_00337C10(void *, void *); + +void func_0034E070(void *object, int a1, void *a2) +{ + char *base = (char *)object; + char *record = base; + void *colour; + + *(int *)(base + 0x220C) = func_00336D50(D_001A8CFC[0] + 0x8710, 0x57); + *(int *)(base + 0x2210) = func_00336D50(D_001A8CFC[0] + 0x8710, 0x58); + { + char *end = base + 0x750; + do { + func_00336408(record, 0x20, 1, D_001AE710, a2); + func_00336CC8(record, 5); + func_00336B90(record, 0x64); + func_00336B98(record, 0); + func_00336498(record, 0x8049C1FF, 0x80001EFF); + func_003364B0(record, 0x50F0C070, 0x50F0C070); + func_00336CE0(record, 0x80000000); + func_00336CC0(record, 1); + record += 0x48; + } while (record < end); + } + func_0034B2C8(base + 0x7A0, a1, a2); + func_00336710(base + 0x1D30, D_001AE720, a2); + func_00335E20(base + 0x1D30); + colour = func_003367B8(base + 0x1D30); + *(float *)colour = 2155905152.0f; + colour = func_003367B8(base + 0x1D30); + *(float *)colour = 60008.0f; + *(int *)((char *)colour + 4) = 0; + *(int *)(base + 0x798) = (int)a2; + func_00337C10(base + 0x1D6C, a2); + func_00337C10(base + 0x1FBC, a2); +} From d348384ef11023d04454213b27f3f2ef0b9710fc Mon Sep 17 00:00:00 2001 From: Flavius Date: Tue, 27 Oct 2026 19:10:37 -0600 Subject: [PATCH 5/9] fix(fcheck): place sda variables and ref_ names as retail has them A variable the candidate defines under a retail name is at that address even when its reference names the section (a static, an initialised small-data global), and a $gp-relative addend to a local symbol counts from the object's gp0. D_XXXXXXXX_sda and SDATA's __gp aliases are the variable they name. A name nothing else places, a near miss's ref_LNN_XXXXXXXX_kk, takes the value retail's bytes give it under the candidate's relocations, aligned by instruction kind. --- tests/test_fcheck.py | 39 +++++++ tools/fcheck.py | 250 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 282 insertions(+), 7 deletions(-) diff --git a/tests/test_fcheck.py b/tests/test_fcheck.py index 9d050d59b..9a878e322 100644 --- a/tests/test_fcheck.py +++ b/tests/test_fcheck.py @@ -2,6 +2,7 @@ import struct import sys import unittest +from unittest import mock from pathlib import Path ROOT = Path(__file__).resolve().parent.parent @@ -124,6 +125,44 @@ def test_hi_pairs_with_the_next_lo_in_object_order(self): with self.assertRaises(fcheck.Link): placer(bad, named=lambda n: 0x0023FFF8) + def test_named_variables_in_writable_sections_go_to_their_address(self): + # An initialised sda global or a static is reached through its section's symbol, its + # $gp-relative offset less the object's gp0 (.reginfo): still its named address. + gp0 = 0x4000 + text = words(LW_GP | ((0 - gp0) & 0xFFFF), LW_GP | ((4 - gp0) & 0xFFFF), LUI_V0, ADDIU_V0 | 4, JR_RA, 0) + reginfo = words(0, 0, 0, 0, 0, gp0) + obj = {"sections": [section(""), section(".text.func_00200000", text, flags=6), + section(".sdata", words(0, 0), flags=3), section(".reginfo", reginfo, flags=2)], + "symbols": [sym("", 0), sym("", 2, kind=fcheck.STT_SECTION), sym("func_00200000", 1), + sym("D_001A748C", 2, 0, kind=1, size=4), sym("D_001A7498", 2, 4, kind=1, size=4)], + "relocations": {1: [(0, elf.R_MIPS_GPREL16, 1), (4, elf.R_MIPS_GPREL16, 1), + (8, elf.R_MIPS_HI16, 1), (12, elf.R_MIPS_LO16, 1)]}} + names = {"D_001A748C": 0x001A748C, "D_001A7498": 0x001A7498} + p, image = placer(obj, named=names.get, writable=lambda s: 0x00238C80) + self.assertEqual(word(image, 0) & 0xFFFF, (0x001A748C - GP) & 0xFFFF) + self.assertEqual(word(image, 4) & 0xFFFF, (0x001A7498 - GP) & 0xFFFF) + self.assertEqual(word(image, 8) & 0xFFFF, 0x001A) + self.assertEqual(word(image, 12) & 0xFFFF, 0x7498) + self.assertNotIn(2, p.where) # the section itself was never needed + self.assertEqual(fcheck.sda_alias("D_001A7C04_sda"), "D_001A7C04") + self.assertEqual(fcheck.sda_alias("D_001A748C__gp"), "D_001A748C") + self.assertIsNone(fcheck.sda_alias("D_001A748C")) + + def test_ref_values_read_from_retail_by_alignment(self): + # The near miss has an extra instruction; its %hi/%lo of ref_..._00 (plus 0x3FFF) and + # its call of ref_..._01 line up with retail's, which give the values. + ADDIU_SP, OR_V0 = 0x27BDFFF0, 0x00431025 + mine = words(ADDIU_SP, LUI_V0, OR_V0, ADDIU_V0 | 0x3FFF, JAL, 0, JR_RA, 0) + retail = words(ADDIU_SP, LUI_V0 | 0x0035, ADDIU_V0 | 0x5FBF, JAL | (0x00123450 >> 2), 0, JR_RA, 0) + obj = {"sections": [section(""), section(".text.func_00200000", mine, flags=6)], + "symbols": [sym("", 0), sym("func_00200000", 1, size=len(mine)), + sym("ref_L00_00200000_00", 0, kind=0), sym("ref_L00_00200000_01", 0, kind=0)], + "relocations": {1: [(4, elf.R_MIPS_HI16, 2), (12, elf.R_MIPS_LO16, 2), (16, elf.R_MIPS_26, 3)]}} + with mock.patch.object(fcheck, "boot_functions", lambda: {"func_00200000": (0x00200000, len(retail))}), \ + mock.patch.object(fcheck, "retail_bytes", lambda a, n: retail[:n]): + values = fcheck.aligned_values(obj, "func_00200000") + self.assertEqual(values, {"ref_L00_00200000_00": 0x00351FC0, "ref_L00_00200000_01": 0x00123450}) + def test_missing_function(self): obj = {"sections": [section("")], "symbols": [sym("", 0)], "relocations": {}} with self.assertRaises(fcheck.Link): diff --git a/tools/fcheck.py b/tools/fcheck.py index 2442d4bde..5904d453b 100644 --- a/tools/fcheck.py +++ b/tools/fcheck.py @@ -36,6 +36,13 @@ gets its section in the kernel area, which the note says; - a read-only section (.rodata: strings, constants, switch jump tables): copied after NAME with its own relocations resolved, so a jump table's entries go to the placed code; +- a variable the candidate defines in a writable section under a name with a retail address + (D_XXXXXXXX, an sda one among them, even when the reference names the section, as a static's + or an initialised small-data global's does): that address. An sda alias D_XXXXXXXX_sda or + D_XXXXXXXX__gp is D_XXXXXXXX; +- a name none of these places (a near miss's ref_LNN_XXXXXXXX_kk, the kk-th external of a + function): the value retail's bytes give it under the candidate's relocations, its + instructions aligned to retail's by kind (the note names each value so read); - a $gp-relative reference (-G8 units, R_MIPS_GPREL16/LITERAL): the target must be within $gp's 64 KB window (retail's $gp, 0x1AEFF0). A read-only constant is used where retail has the same bytes there (the unit's own placement, else core.lit / .lit); anything else out of @@ -283,6 +290,178 @@ def unit_values(unit: Path, name: str) -> tuple[dict[str, int], dict[str, int]]: return own, {k: next(iter(v)) for k, v in others.items() if len(v) == 1} +def sda_alias(name: str) -> str | None: + """The variable an sda alias names: D_XXXXXXXX_sda (a second, $gp-relative declaration of + D_XXXXXXXX), or SDATA()'s assembler name D_XXXXXXXX__gp; None for another name.""" + m = re.fullmatch(r"(\w+?)(?:_sda|__gp)", name) + return m.group(1) if m else None + + +# Opcodes whose 16-bit immediate is an address's low half on a base register: addiu, the +# loads and stores (lq/sq, ldl/ldr, lwc1/swc1, ld/sd and the rest). +ADDRESS_OPS = {0x09, 0x1A, 0x1B, 0x1E, 0x1F, *range(0x20, 0x30), 0x31, 0x35, 0x37, 0x39, 0x3D, 0x3F} +LOADS = {0x1A, 0x1B, 0x1E, *range(0x20, 0x28), 0x37} + + +def retail_references(ref: bytes, address: int) -> list[tuple[str, int]]: + """The addresses retail's code at ADDRESS forms, in order: ("call", target) for a jal or j, + ("data", address) for a %lo-style use of a lui's register or a $gp-based access.""" + sext = build.sext + hi: dict[int, int] = {} + out = [] + for at in range(0, len(ref) - 3, 4): + w = struct.unpack_from("> 26, (w >> 21) & 31, (w >> 16) & 31, (w >> 11) & 31 + if op in (2, 3): + out.append(("call", ((address + at + 4) & 0xF0000000) | ((w & 0x3FFFFFF) << 2))) + if op == 3: + hi.pop(31, None) + continue + if op == 0x0F: + hi[rt] = (w & 0xFFFF) << 16 + continue + if op in ADDRESS_OPS and (rs in hi or rs == 28): + out.append(("data", ((GP if rs == 28 else hi[rs]) + sext(w & 0xFFFF)) & 0xFFFFFFFF)) + # What the instruction writes no longer holds a lui's value. + if op in (0, 0x1C): + hi.pop(rd, None) + elif op in LOADS or 0x08 <= op <= 0x0E or (op == 0x11 and rs in (0, 2)): + hi.pop(rt, None) + return out + + +def signature(w: int) -> tuple[int, int]: + """An instruction's kind, without its registers and immediate: the opcode, and the function + field of SPECIAL/MMI ones, the rt field of REGIMM's.""" + op = w >> 26 + return op, (w & 0x3F if op in (0, 0x1C) else (w >> 16) & 0x1F if op == 1 else 0) + + +def aligned_by_position(obj: dict, index: int, start: int, end: int, address: int, ref: bytes) -> dict[str, int]: + """aligned_values' first pass: the candidate's instructions aligned to retail's by kind (the + longest common runs); a relocated one whose retail counterpart is the same instruction (a + jump, a lui with the %lo instruction based on the register it loaded, a $gp-based access) + gives its symbol a value, kept when every such use agrees.""" + import difflib + data = bytes(obj["sections"][index]["data"]) + word = lambda blob, at: struct.unpack_from("> 26 != 0x0F: + continue + rl = word(ref, at_retail[lo]) + if (rl >> 21) & 31 != (r >> 16) & 31: + continue + v = (((r & 0xFFFF) << 16) + sext(rl & 0xFFFF) + - (((w & 0xFFFF) << 16) + sext(word(data, lo) & 0xFFFF))) + elif kind == elf.R_MIPS_GPREL16: + if (r >> 21) & 31 != 28: + continue + v = GP + sext(r & 0xFFFF) - sext(w & 0xFFFF) + else: + continue + seen.setdefault(n, set()).add(v & 0xFFFFFFFF) + return {n: next(iter(v)) for n, v in seen.items() if len(v) == 1} + + +def aligned_values(obj: dict, name: str, known=lambda n: None) -> dict[str, int]: + """Values of the symbols NAME's candidate section refers to that nothing else places (a near + miss's `ref_LNN_XXXXXXXX_kk`, the kk-th external of a function: a value of each program, which + no matched function of the boot may give), read from retail's bytes as tools/build.py reads + them under the relocations, for a candidate that is not byte-identical: its references, in + order, are aligned to the addresses retail's code forms (retail_references), the symbols KNOWN + or the first pass (aligned_by_position) gives a value anchoring the alignment, and a symbol + takes the value every use aligned to retail agrees on.""" + sym = next((s for s in obj["symbols"] if s["name"] == name and s["type"] == STT_FUNC and 0 < s["section"] < 0xFF00), None) + if sym is None or name not in boot_functions(): + return {} + address, size = boot_functions()[name] + index = sym["section"] + data = bytes(obj["sections"][index]["data"]) + rel = obj["relocations"].get(index, []) + word = lambda at: struct.unpack_from(" int: + (ka, n, addend), (kb, r) = a, b + if ka != kb: + return -1 + if n is None or value.get(n) is None: + return 1 # free: a section's or an unknown symbol's use + return 3 if (value[n] + addend) & 0xFFFFFFFF == r else -1 + + # The best alignment (most agreeing anchors), by dynamic programming. + M, N = len(mine), len(theirs) + best = [[0] * (N + 1) for _ in range(M + 1)] + for i in range(M - 1, -1, -1): + for j in range(N - 1, -1, -1): + s = score(mine[i], theirs[j]) + best[i][j] = max(best[i + 1][j], best[i][j + 1], best[i + 1][j + 1] + s if s > 0 else 0) + seen: dict[str, set[int]] = {} + i = j = 0 + while i < M and j < N: + s = score(mine[i], theirs[j]) + if s > 0 and best[i][j] == best[i + 1][j + 1] + s: + kind, n, addend = mine[i] + if n and value.get(n) is None: + seen.setdefault(n, set()).add((theirs[j][1] - addend) & 0xFFFFFFFF) + i, j = i + 1, j + 1 + elif best[i][j] == best[i + 1][j]: + i += 1 + else: + j += 1 + out = {n: v for n, v in first.items() if known(n) is None} + out.update({n: next(iter(v)) for n, v in seen.items() if len(v) == 1}) + return out + + # --- Placing --------------------------------------------------------------------------------- def gp_literal(data: bytes) -> int | None: @@ -324,6 +503,10 @@ def __init__(self, obj: dict, name: str, named, function, writable, literal, bas and 0 < s["section"] < 0xFF00), None) if sym is None: raise Link(f"LINK {name} is not defined by the candidate") + # The gp the assembler assumed (.reginfo's last word): a $gp-relative reference to a local + # symbol holds its offset less it (S + A + GP0 - GP, as the linker reads it). + reginfo = next((s for s in obj["sections"] if s["name"] == ".reginfo" and len(s["data"]) >= 24), None) + self.gp0 = struct.unpack_from(" int: self.notes.append(f"{sec['name']} has no retail placement: placed fresh in the kernel area") return self.copy(index) + def target(self, sym: dict, addend: int) -> int: + """Where a reference to SYM plus ADDEND goes. Into a writable section of the candidate, a + variable it defines there with a retail address (a D_ name, an sda one among them) is + at that address, whatever the section's own placement: the reference may name the + section (a static, or an initialised small-data global) and not the variable.""" + index = sym["section"] + if sym["type"] == STT_SECTION and 0 < index < 0xFF00: + sec = self.obj["sections"][index] + if sec.get("flags", 0) & SHF_WRITE or sec["type"] == elf.SHT_NOBITS: + for s in self.obj["symbols"]: + if (s["section"] == index and s["type"] != STT_SECTION and s["name"] + and s["value"] <= addend < s["value"] + max(s.get("size", 0), 1)): + v = self.named(s["name"]) + if v is not None: + return v + addend - s["value"] + return self.value(sym) + addend + def value(self, sym: dict) -> int: if sym["type"] == STT_SECTION: return self.section(sym["section"]) @@ -376,6 +576,9 @@ def value(self, sym: dict) -> int: return self.section(sym["section"]) + sym["value"] if sym["section"] == SHN_ABS: return sym["value"] + if n.startswith("ref_"): + raise Link(f"LINK {n} has no retail address: no matched function gives it, and its uses do not " + "line up with retail's (name it D_XXXXXXXX)") raise Link(f"LINK {n or '?'} has no retail address") def relocate(self) -> None: @@ -392,6 +595,14 @@ def apply(self, index: int) -> None: word = lambda at: struct.unpack_from(" None: "is outside $gp's window: give the data a retail D_ address") put(at, (w & 0xFFFF0000) | (d & 0xFFFF)) continue - v = self.value(sym) if kind == elf.R_MIPS_32: - put(at, w + v) + put(at, self.target(sym, w)) elif kind == elf.R_MIPS_26: - target = v + ((w & 0x3FFFFFF) << 2) + target = self.target(sym, (w & 0x3FFFFFF) << 2) if (target ^ (self.base + off + at)) & 0xF0000000: raise Link("LINK a jump out of its 256 MB region") put(at, (w & 0xFC000000) | ((target >> 2) & 0x3FFFFFF)) @@ -417,9 +627,11 @@ def apply(self, index: int) -> None: if lo is None: raise Link(f"LINK a %hi at +{at:#x} without its %lo") ahl = ((w & 0xFFFF) << 16) + sext(word(lo) & 0xFFFF) - put(at, (w & 0xFFFF0000) | (((v + ahl + 0x8000) >> 16) & 0xFFFF)) + put(at, (w & 0xFFFF0000) | (((self.target(sym, ahl) + 0x8000) >> 16) & 0xFFFF)) elif kind == elf.R_MIPS_LO16: - put(at, (w & 0xFFFF0000) | ((v + sext(w & 0xFFFF)) & 0xFFFF)) + lo = sext(w & 0xFFFF) + ahl = ahl_of_lo.get(at, lo) + put(at, (w & 0xFFFF0000) | ((self.target(sym, ahl) - ahl + lo) & 0xFFFF)) else: raise Link(f"LINK relocation type {kind} not handled") @@ -428,6 +640,8 @@ def gp_target(self, sym: dict, addend: int, ins: int) -> int: is not reachable from where it is copied: the same bytes in retail's window are used.""" index = sym["section"] sec = self.obj["sections"][index] if 0 < index < 0xFF00 else None + if sym["type"] == STT_SECTION or (sym.get("bind", 1) == 0 and sec is not None): + addend += self.gp0 if (sym["type"] == STT_SECTION and sec is not None and not sec["name"].startswith(".text") and not sec.get("flags", 0) & SHF_WRITE and sec["type"] != elf.SHT_NOBITS): width = WIDTH.get(ins >> 26, 4) @@ -436,7 +650,7 @@ def gp_target(self, sym: dict, addend: int, ins: int) -> int: if a is None: raise Link(f"LINK the constant {data.hex()} in {sec['name']} (read $gp-relative) is nowhere in retail's $gp window") return a - return self.value(sym) + addend + return self.target(sym, addend) def segments(self) -> list[tuple[int, bytes]]: return [(self.base, bytes(self.image))] @@ -510,7 +724,11 @@ def build_candidate(name: str, candidate: Path | None, out: Path, includes: list table = boot_functions() derived = {k: int(v, 16) for k, v in placed().get("values", {}).items()} + aligned: dict[str, int] | None = None + from_bytes: list[str] = [] + def named(n: str) -> int | None: + nonlocal aligned for d in (own,): if n in d: return d[n] & 0xFFFFFFFF @@ -520,7 +738,22 @@ def named(n: str) -> int | None: return derived[n] if n in others: return others[n] & 0xFFFFFFFF - return build.address_in_name(n) + v = build.address_in_name(n) + if v is not None: + return v + if sda_alias(n): + v = named(sda_alias(n)) + if v is not None: + return v + # Else (a near miss's ref_ name): the value retail's own bytes give it. + if aligned is None: + aligned = {} # (no recursion through named while aligning) + aligned = aligned_values(obj, name, named) + if n in aligned: + if n not in from_bytes: + from_bytes.append(n) + return aligned[n] + return None def function(n: str) -> int | None: return table[n][0] if n in table else named(n) @@ -540,6 +773,9 @@ def literal(data: bytes, sec: str, offset: int) -> int | None: p = Placer(obj, name, named, function, writable, literal) p.relocate() + if from_bytes: + p.notes.append("read from retail's bytes by alignment: " + ", ".join( + f"{n}={aligned[n]:#010x}" for n in from_bytes)) segs = p.segments() files = [] for i, (a, data) in enumerate(segs): From b9b2ca9daec7d83277c70304be3892d5d65474fc Mon Sep 17 00:00:00 2001 From: Flavius Date: Tue, 27 Oct 2026 19:20:04 -0600 Subject: [PATCH 6/9] feat(nonmatching): functional C for four sound and pad functions, workarounds removed func_00132498, func_001325E0, func_002B85F8 and func_00300AA8 are SAME at the gate now that it compares only a call's arguments; func_00300AA8's gp-relative globals are at their real addresses. The committed drafts drop what worked around the old gate: hardcoded addresses become named globals (func_001334B8, func_00133580, func_00132C48, func_002B88F0), constants held in locals and leftover-register arguments go (func_002B68C8, func_00132498), the frame-size helper goes (func_002B85F8), the program's end is its ref_ name again (func_00290B48), and func_00128FD0 passes the 32-bit tag its callee stores instead of a 64-bit word built on an unset $s4. --- nonmatching/functional.tsv | 4 + nonmatching/functional/func_00128FD0.c | 6 +- nonmatching/functional/func_00132498.c | 48 +++++++ nonmatching/functional/func_001325E0.c | 78 +++++++++++ nonmatching/functional/func_00132C48.c | 20 +-- nonmatching/functional/func_001334B8.c | 9 +- nonmatching/functional/func_00133580.c | 10 +- nonmatching/functional/func_00290B48.c | 8 +- nonmatching/functional/func_002B68C8.c | 12 +- nonmatching/functional/func_002B85F8.c | 79 +++++++++++ nonmatching/functional/func_002B88F0.c | 5 +- nonmatching/functional/func_00300AA8.c | 179 +++++++++++++++++++++++++ 12 files changed, 427 insertions(+), 31 deletions(-) create mode 100644 nonmatching/functional/func_00132498.c create mode 100644 nonmatching/functional/func_001325E0.c create mode 100644 nonmatching/functional/func_002B85F8.c create mode 100644 nonmatching/functional/func_00300AA8.c diff --git a/nonmatching/functional.tsv b/nonmatching/functional.tsv index 8142240c9..5f76ccd7b 100644 --- a/nonmatching/functional.tsv +++ b/nonmatching/functional.tsv @@ -6,6 +6,8 @@ func_00115FC0 nonmatching/functional/func_00115FC0.c func_00119BF8 nonmatching/functional/func_00119BF8.c func_00128FD0 nonmatching/functional/func_00128FD0.c func_00132028 nonmatching/functional/func_00132028.c +func_00132498 nonmatching/functional/func_00132498.c +func_001325E0 nonmatching/functional/func_001325E0.c func_00132C48 nonmatching/functional/func_00132C48.c func_00133108 nonmatching/functional/func_00133108.c func_001334B8 nonmatching/functional/func_001334B8.c @@ -56,6 +58,7 @@ func_002B6828 nonmatching/functional/func_002B6828.c func_002B68C8 nonmatching/functional/func_002B68C8.c func_002B6E78 nonmatching/functional/func_002B6E78.c func_002B7578 nonmatching/functional/func_002B7578.c +func_002B85F8 nonmatching/functional/func_002B85F8.c func_002B88F0 nonmatching/functional/func_002B88F0.c func_002BA0E0 nonmatching/functional/func_002BA0E0.c func_002C9CF8 nonmatching/functional/func_002C9CF8.c @@ -80,6 +83,7 @@ func_002E8368 nonmatching/functional/func_002E8368.c func_002ECB70 nonmatching/functional/func_002ECB70.c func_002F0FA0 nonmatching/functional/func_002F0FA0.c func_002FC280 nonmatching/functional/func_002FC280.c +func_00300AA8 nonmatching/functional/func_00300AA8.c func_00336F20 nonmatching/functional/func_00336F20.c func_00337C10 nonmatching/functional/func_00337C10.c func_003380B0 nonmatching/functional/func_003380B0.c diff --git a/nonmatching/functional/func_00128FD0.c b/nonmatching/functional/func_00128FD0.c index 437ebf20d..0f8faf75b 100644 --- a/nonmatching/functional/func_00128FD0.c +++ b/nonmatching/functional/func_00128FD0.c @@ -1,6 +1,6 @@ -/* functional: low. Hand-written from the assembly. Retail also reads a 64-bit register ($s4) it never sets, keeping that register's upper 32 bits when it builds the 0x0103400B tag word for func_001286C8; C cannot read the caller's $s4, so the upper half is passed as 0. The fifth argument (in $t0) is the source of the first copy. The frame is zeroed because retail passes the buffer on with its unwritten gap bytes holding whatever was on the stack (0 in every captured call); zeroing makes that deterministic. */ +/* functional: medium. Hand-written from the assembly. Retail builds the tag word for func_001286C8 as bitfields of an uninitialised 64-bit local (in $s4): its low 32 bits are always 0x0103400B and its upper 32 bits are whatever the caller left in $s4. func_001286C8 stores only the low word of that argument (sw $a1) and then reuses $a1, so the honest form passes the 32-bit word 0x0103400B and nothing of $s4. The fifth argument (in $t0) is the source of the first copy. The frame is zeroed because retail passes the buffer on with its unwritten gap bytes holding whatever was on the stack (0 in every captured call); zeroing makes that deterministic. */ extern void func_001153D4(void *, void *, int); -extern int func_001286C8(int, long, void *, void *); +extern int func_001286C8(int, unsigned int, void *, void *); int func_00128FD0(int a0, int a1, int a2, int a3, void *a4) { @@ -14,5 +14,5 @@ int func_00128FD0(int a0, int a1, int a2, int a3, void *a4) func_001153D4(base + 8, a4, a3); ((int *)base)[0] = a1; func_001153D4(base + a3 + 8, (void *)a2, a1); - return func_001286C8(a0, 0x0103400BL, base + 0x30, base); + return func_001286C8(a0, 0x0103400Bu, base + 0x30, base); } diff --git a/nonmatching/functional/func_00132498.c b/nonmatching/functional/func_00132498.c new file mode 100644 index 000000000..cfe119340 --- /dev/null +++ b/nonmatching/functional/func_00132498.c @@ -0,0 +1,48 @@ +/* functional: high. snd_BankLoadAsync. Same reads, writes and calls in retail order; the $gp-relative globals are reached absolutely here (same addresses), which changes no behaviour. The two messages have no conversions, and func_00133580 takes one argument: what retail leaves in $a1-$a3 at those calls is not passed. */ +extern int D_001A74C8; +extern int D_001A7488; +extern int D_001A74F8; +extern int D_001A71C0; +extern int D_001A74D0; +extern long D_001A74D8; +extern int D_001A71C4; +extern int D_001A7180; +extern char D_001A7140[]; +extern char D_001A7578[]; +extern char D_001A7600[]; +extern char D_001A7630[]; + +extern void func_00115D48(char *msg); +extern int func_00133580(int a); +extern int func_0011D810(char *p); +extern void func_00132028(void); +extern void func_0011AEA0(int a); +extern int func_0011D620(char *client, int rpc, int mode, void *send_buf, int send_size, void *recv_buf, int recv_size, int a7, int a8); + +void func_00132498(int a0, int a1, int a2, long a3) +{ + D_001A7488 = 0; + if (D_001A74C8 != 0) { + if (D_001A74F8 == 0) + func_00115D48(D_001A7600); + return; + } + if (func_00133580(1) == 1) { + if (D_001A74F8 == 0) + func_00115D48(D_001A7630); + return; + } + D_001A71C0 = a0; + D_001A71C4 = a1; + D_001A7180 = -1; + D_001A74D0 = a2; + D_001A74D8 = a3; + while (func_0011D810(D_001A7140) != 0) { + if (D_001A74F8 == 0) + func_00115D48(D_001A7578); + func_00132028(); + func_0011AEA0(0); + } + D_001A74C8 = 1; + func_0011D620(D_001A7140, 3, 1, &D_001A71C0, 8, &D_001A7180, 4, 0, 0); +} diff --git a/nonmatching/functional/func_001325E0.c b/nonmatching/functional/func_001325E0.c new file mode 100644 index 000000000..31497bdb3 --- /dev/null +++ b/nonmatching/functional/func_001325E0.c @@ -0,0 +1,78 @@ +/* functional: high. Same reads, writes and calls in the same order as retail; only instruction scheduling (store order at the loop entry) and register use differ. */ +/* from src/core/00131780-g8.c: the catalogue row is eight bytes too long. + * + * func_001325E0's row (0x001325E0, 244 bytes) opens with + * + * addiu $sp,$sp,0x20 ; nop + * + * that belongs to no function: a linker remnant of the 989snd unit, the kind + * rac1-decomp splits off as core_text/func_0012E1B8, right before its + * cmd-0x57 wrapper func_0012E1C8 (rac1-decomp src/core/989snd.c), which is + * the function this row is. The remnant is dead-stripped code, not part of + * the function, and no C emits a stack-pointer increase at function entry, so + * the row as it stands cannot be matched from C. A tools/levels.py split (the + * row starts at 0x001325E8, 236 bytes) unblocks it. + * + * The C below is the function itself: 228 bytes against retail's 236. What + * agrees is the whole shape - the 0x20 frame, the $s0 and $ra saves, the + * branch out of the "pending" path, the four stores into D_001A71C0, + * D_001A74D0, D_001A7180 and D_001A74D8, the -1 sentinel materialised as + * `lui 0xFFFF` + `ori 0xFFFF`, the wait loop (the busy flag loaded into the + * back edge's delay slot, the pump and the message under it, then + * func_0011D810), the ninth argument of func_0011D620 on the stack at + * 0x0($sp), and the epilogue. What is left is two instructions of scheduling: + * retail keeps the four stores (and three `lui $at`) before the branch into + * the loop, this cc1 sinks the last store into the branch's delay slot and + * reuses $at, and the two stores after the loop come out in the other order. + * + * Unit: the -g8 sibling (the game compiler at -O2 -G8 -ffunction-sections); + * the small globals need __attribute__((sda)) to come out $gp-relative. + * GP is 0x001AEFF0: -0x7B28 is D_001A74C8, -0x7B68 is D_001A7488, -0x7AF8 + * is D_001A74F8 and -0x7B20 is D_001A74D0. + * + * Verified: compiled with the unit's own flags and compared with retail at + * 0x001325E8: 228 of 236 bytes, the first difference at +0x1c (the $gp + * offsets, which the linker fills in). + */ + +extern int D_001A74C8 __attribute__((sda)); /* gp - 0x7B28 */ +extern int D_001A7488 __attribute__((sda)); /* gp - 0x7B68 */ +extern int D_001A74F8 __attribute__((sda)); /* gp - 0x7AF8 */ +extern int D_001A74D0; +extern int D_001A71C0; +extern unsigned int D_001A7180; +extern long long D_001A74D8; +extern int D_001A7140; +extern char D_001A76F8[]; +extern char D_001A7578[]; + +extern void func_00115D48(char *); +extern void func_00132028(void); +extern void func_0011AEA0(int); +extern int func_0011D810(int); +extern void func_0011D620(int, int, int, int, int, int, int, int, int); + +void func_001325E0(int arg0, int arg1, long long arg2) { + D_001A7488 = 0; + if (D_001A74C8 != 0) { + if (D_001A74F8 != 0) { + return; + } + func_00115D48(D_001A76F8); + return; + } else { + D_001A71C0 = arg0; + D_001A74D0 = arg1; + D_001A7180 = 0xFFFFFFFFu; + D_001A74D8 = arg2; + while (func_0011D810((int)&D_001A7140) != 0) { + if (D_001A74F8 == 0) { + func_00115D48(D_001A7578); + } + func_00132028(); + func_0011AEA0(0); + } + D_001A74C8 = 1; + func_0011D620((int)&D_001A7140, 0x57, 1, (int)&D_001A71C0, 4, (int)&D_001A7180, 4, 0, 0); + } +} diff --git a/nonmatching/functional/func_00132C48.c b/nonmatching/functional/func_00132C48.c index 193c94037..2dc576577 100644 --- a/nonmatching/functional/func_00132C48.c +++ b/nonmatching/functional/func_00132C48.c @@ -1,9 +1,13 @@ -/* functional: medium. snd_SendCommandSync written from the retail assembly: same reads, writes and calls in the same order; the staging copy and waits sit in static helpers, and the syscall argument registers that retail leaves unset are only matched for the checked calls. */ +/* functional: medium. snd_SendCommandSync written from the retail assembly: same reads, writes and calls in the same order (stage the payload in D_001A70C0, wait for the sound server, send the command over RPC into the D_001A7080 reply, drain, return its second word); the staging copy and waits sit in static helpers. */ extern int D_001A7480 __attribute__((sda)); extern int D_001A74F8 __attribute__((sda)); extern int D_001A74C0 __attribute__((sda)); extern int D_001A74C4 __attribute__((sda)); extern int *D_001A74A0[] __attribute__((sda)); +extern char D_001A7040[]; +extern int D_001A7080[]; +extern unsigned char D_001A70C0[]; +extern char D_001A7578[]; extern void func_00132028(void); extern void func_0011AEA0(int); extern void func_001322B8(void *, int); @@ -15,7 +19,7 @@ extern void func_00133108(void *); static void snd_stage_and_pump(int size, unsigned char *payload) { int i; - unsigned char *dst = (unsigned char *)0x1A70C0; + unsigned char *dst = D_001A70C0; for (i = 0; i < size; i++) { unsigned char b = payload[i]; @@ -25,10 +29,10 @@ static void snd_stage_and_pump(int size, unsigned char *payload) { func_00132028(); func_0011AEA0(0); } - func_001322B8((void *)0x1A7080, 1); - while (func_0011D810((void *)0x1A7040) != 0) { + func_001322B8(D_001A7080, 1); + while (func_0011D810(D_001A7040) != 0) { if (D_001A74F8 == 0) { - func_00115D48((void *)0x1A7578); + func_00115D48(D_001A7578); } func_00132028(); func_0011AEA0(0); @@ -46,12 +50,12 @@ int func_00132C48(int cmd, int size, unsigned char *payload) { snd_stage_and_pump(size, payload); if (size != 0) { - func_0011D620((void *)0x1A7040, cmd, 1, (void *)0x1A70C0, size, (void *)0x1A7080, 0xC, 0, 0); + func_0011D620(D_001A7040, cmd, 1, D_001A70C0, size, D_001A7080, 0xC, 0, 0); } else { - func_0011D620((void *)0x1A7040, cmd, 1, (void *)0, 0, (void *)0x1A7080, 0xC, 0, 0); + func_0011D620(D_001A7040, cmd, 1, (void *)0, 0, D_001A7080, 0xC, 0, 0); } snd_drain(); - ret = *(int *)0x1A7084; + ret = D_001A7080[1]; entry = D_001A74A0[D_001A74C0]; if (*entry != 0 && D_001A74C4 == 0) { func_00133108(entry); diff --git a/nonmatching/functional/func_001334B8.c b/nonmatching/functional/func_001334B8.c index ab9185a13..a7fe1f76e 100644 --- a/nonmatching/functional/func_001334B8.c +++ b/nonmatching/functional/func_001334B8.c @@ -2,6 +2,9 @@ extern int D_001A748C __attribute__((sda)); extern int D_001A7494 __attribute__((sda)); extern int D_001A7498 __attribute__((sda)); +extern int D_001A7100; +extern int D_001A7110; +extern char D_001A713F; extern void func_0011B3D0(void *, void *); extern int func_001253A8(void); extern int func_00133580(int); @@ -16,9 +19,9 @@ int func_001334B8(int arg0, int arg1, int arg2) { if (func_00133580(1) == 1) { return 0; } - *(int *)0x1A7100 = 1; - *(int *)0x1A7110 = 0; - func_0011B3D0((void *)0x1A7100, (void *)0x1A713F); + D_001A7100 = 1; + D_001A7110 = 0; + func_0011B3D0(&D_001A7100, &D_001A713F); payload[0] = arg0; payload[1] = arg1; payload[2] = arg2; diff --git a/nonmatching/functional/func_00133580.c b/nonmatching/functional/func_00133580.c index ec6e41c23..7c619241e 100644 --- a/nonmatching/functional/func_00133580.c +++ b/nonmatching/functional/func_00133580.c @@ -1,4 +1,6 @@ -/* functional: medium. Same flow as retail (flag clear: tail into func_00124B88(arg0); else poll func_0011B500 until D_001A7100 reads non-zero, pumping func_00132028). Not byte-identical: register allocation differs, so the dead $v1 the callee's system call sees may differ. */ +/* functional: medium. Same flow as retail (flag clear: tail into func_00124B88(arg0); else poll func_0011B500 until D_001A7100 reads non-zero, pumping func_00132028, publishing the status to D_001A7498). Not byte-identical: register allocation differs. */ +extern int D_001A748C __attribute__((sda)); +extern int D_001A7498 __attribute__((sda)); extern unsigned int D_001A7100; extern char D_001A713F; extern void func_0011B500(void *, void *); @@ -8,12 +10,12 @@ extern int func_00132028(void); int func_00133580(int arg0) { int busy; - if (*(int *)0x1A748C == 0) + if (D_001A748C == 0) return func_00124B88(arg0); func_0011B500(&D_001A7100, &D_001A713F); busy = D_001A7100 < 1; - *(int *)0x1A7498 = busy; + D_001A7498 = busy; if (busy == 1) return 0; if (arg0 == 1) @@ -22,7 +24,7 @@ int func_00133580(int arg0) { func_00132028(); func_0011B500(&D_001A7100, &D_001A713F); busy = D_001A7100 < 1; - *(int *)0x1A7498 = busy; + D_001A7498 = busy; } while (busy == 0); return 0; } diff --git a/nonmatching/functional/func_00290B48.c b/nonmatching/functional/func_00290B48.c index f9c692d6b..d7d8fc428 100644 --- a/nonmatching/functional/func_00290B48.c +++ b/nonmatching/functional/func_00290B48.c @@ -1,7 +1,9 @@ -/* functional: medium. fcheck SAME on all captured calls; same stores of the same values as retail, only allocation and store order differ. The two program-end references are the constants 0x351FC0 and 0x2000000 read off the retail words (boot build). */ +/* functional: medium. Same stores of the same values as retail, only allocation and store order differ. The program's end and the top of memory are the function's two externals ref_L00_00290B48_00/_01 (in the boot 0x351FC0 and 0x2000000), as in the near miss. */ extern void *func_00115484(void *, int, int); extern char D_001BAEC0[]; extern int D_001B22B0; +extern char ref_L00_00290B48_00[]; +extern char ref_L00_00290B48_01[]; void func_00290B48(void) { @@ -10,8 +12,8 @@ void func_00290B48(void) unsigned int top, block1, block2, block3, block4, block5; func_00115484(state, 0, 0x9C); - top = 0x2000000u - 0x8000; - start = (0x351FC0u + 0x3FFF) & ~0x3FFF; + top = (unsigned int)ref_L00_00290B48_01 - 0x8000; + start = ((unsigned int)ref_L00_00290B48_00 + 0x3FFF) & ~0x3FFF; block1 = top - 0x40000; block2 = block1 - 0x64000; first = start + D_001B22B0; diff --git a/nonmatching/functional/func_002B68C8.c b/nonmatching/functional/func_002B68C8.c index 3bc2ace0f..ade418a74 100644 --- a/nonmatching/functional/func_002B68C8.c +++ b/nonmatching/functional/func_002B68C8.c @@ -1,4 +1,4 @@ -/* functional: high. Same stores and calls in retail order; retail issues the +0x24 store in the delay slot of the func_002B66F0 call, here it is a plain store before the call. $v1 holds 0x20 and $a0 holds -1 at the call, as in retail, because the callee chain reads them. */ +/* functional: high. Same stores and calls in retail order; retail issues the +0x24 store in the delay slot of the func_002B66F0 call, here it is a plain store before the call. func_002B66F0 takes no arguments: it sets every register it passes on. */ #include "include_asm.h" extern void func_0026FEB8(void *); @@ -10,15 +10,11 @@ extern void func_002B6938(void); void func_002B68C8(void) { char *state; - int minus1; - int flags; func_0026FEB8(D_001AA198); state = D_001A63A8; - minus1 = -1; - flags = 0x20; - *(short *)(state + 0x3C) = minus1; - state[0x40] = flags; + *(short *)(state + 0x3C) = -1; + state[0x40] = 0x20; state[0x41] = 0; state[0x42] = 0; state[0x43] = 0; @@ -28,7 +24,7 @@ void func_002B68C8(void) *(short *)(state + 0x72) = 0; *(int *)(state + 0x8C) = 0; *(short *)(state + 0x96) = 0; - *(int *)(state + 0x24) = minus1; + *(int *)(state + 0x24) = -1; func_002B66F0(); func_002B6938(); } diff --git a/nonmatching/functional/func_002B85F8.c b/nonmatching/functional/func_002B85F8.c new file mode 100644 index 000000000..3e7d176d9 --- /dev/null +++ b/nonmatching/functional/func_002B85F8.c @@ -0,0 +1,79 @@ +/* functional: medium. Same state machine, calls and stores as retail by reading the assembly; byte layout differs. The 4-byte copies read the stack buffer past what the callee wrote, as retail does (dead stack content) */ +typedef unsigned char u8; +extern void func_002B8888(u8 *); +extern char D_00138180[]; +extern int func_00128C18(int); +extern int func_00128B28(int, void *); +extern int func_00128F48(int, void *); +extern int func_00128A48(int, void *); +extern void func_00128FD0(int, int, void *, int, void *); +extern void func_002B88F0(void *, void *, int); +extern int D_001A7428[]; + +void func_002B85F8(void *base) +{ + unsigned char *p = base; + unsigned char b[0x40]; + int mode = *(int *)(p + 0x1B0); + int r; + int n; + int i; + + *(int *)(p + 0x1A0) = 0; + *(int *)(p + 0x1AC) = *(int *)(p + 0x348); + *(int *)(p + 0x1B0) = 0; + *(int *)(p + 0x1BC) = mode; + r = func_00128C18(*(int *)(p + 0x194)); + *(int *)(p + 0x19C) = r; + + if (r != 1) { + func_002B8888(p); + *(int *)(p + 0x198) = 0; + p[0x189] = 0; + } else { + int st = *(int *)(p + 0x198); + if (st == 1) { + int s1 = func_00128A48(*(int *)(p + 0x194), b); + short h = 0; + if (p[0x184] & 2) + h = p[0x189]; + if (p[0x184] & 1) + h = (short)(h << 1) | (p[0x188] & 1); + func_00128FD0(*(int *)(p + 0x194), 1, p + 0x184, 2, &h); + func_002B88F0(p, b, s1); + p[0x189] = 0; + } else if (st < 2) { + if (st == 0) { + n = func_00128B28(*(int *)(p + 0x194), b); + *(int *)(p + 0x1DC) = (*(int *)b == -1) ? 0x79 : 0; + if (n < 5) { + for (i = 0; i < n; i++) + p[0x180 + i] = b[i]; + *(int *)(p + 0x198) = 1; + } else { + n = 0; + *(int *)(p + 0x198) = 2; + } + for (i = n; i < 4; i++) + p[0x180 + i] = b[i]; + n = func_00128F48(*(int *)(p + 0x194), b); + if (n < 5) { + for (i = 0; i < n; i++) + p[0x184 + i] = b[i]; + } else { + n = 0; + } + for (i = n; i < 4; i++) + p[0x184 + i] = b[i]; + func_002B8888(p); + } + p[0x189] = 0; + } else { + if (st == 2) + func_002B8888(p); + p[0x189] = 0; + } + } + p[0x188] = 0; + D_001A7428[0] = *(int *)(D_00138180 + 0x19C) == 0; +} diff --git a/nonmatching/functional/func_002B88F0.c b/nonmatching/functional/func_002B88F0.c index 90f61ffd4..a700007a3 100644 --- a/nonmatching/functional/func_002B88F0.c +++ b/nonmatching/functional/func_002B88F0.c @@ -2,6 +2,7 @@ extern float func_00283CE0(int); extern float func_00282E20(float *); extern float func_00283248(float, float); +extern unsigned char D_001A7A44; extern float func_00283C80(float, float); void func_002B88F0(char *p, unsigned char *raw, int type) { @@ -46,7 +47,7 @@ void func_002B88F0(char *p, unsigned char *raw, int type) { f[0x100 / 4 + i] = func_00283CE0(raw[2 + i]) * 0.003921569f; } } - if (*(unsigned char *)0x1A7A44 != 0) { + if (D_001A7A44 != 0) { f[0x108 / 4] = -f[0x108 / 4]; f[0x100 / 4] = -f[0x100 / 4]; v = w[0x1A0 / 4]; @@ -96,7 +97,7 @@ void func_002B88F0(char *p, unsigned char *raw, int type) { w[0x1A8 / 4] = a1; w[0x1C0 / 4] = t0; w[0x348 / 4] = t0; - if (*(unsigned char *)0x1A7A44 != 0) { + if (D_001A7A44 != 0) { f[0x148 / 4] = -f[0x148 / 4]; f[0x140 / 4] = -f[0x140 / 4]; if (t0 & 0x8000) { diff --git a/nonmatching/functional/func_00300AA8.c b/nonmatching/functional/func_00300AA8.c new file mode 100644 index 000000000..3ef2a4634 --- /dev/null +++ b/nonmatching/functional/func_00300AA8.c @@ -0,0 +1,179 @@ +/* functional: medium. From the near miss (1188 vs 1200 bytes, frame size differs only): same stores and calls in order. Its externals are named by their retail addresses; the projection floats and the GIF tag retail reaches $gp-relative are D_001AD870 (gp - 0x1780) and D_001AD830 (gp - 0x17C0). */ +/* + * Retained handwritten near miss; historical evidence only. + * No linked-image equality or complete retail semantics is established. + * Compiler: game EE-GCC 2.9-ee-991111b (project profile). + * Profile: -O2 -G0 -ffunction-sections (reported default game profile). + * Reported final C / retail: 1188 / 1200 bytes; size equality is not a match. + * us: 0/28 placements; 1188/1200 bytes. + * us2: 0/28 placements; 1188/1200 bytes. + * pal: 0/28 placements; 1188/1200 bytes. + * Residual: Renderer allocation/frame mismatch; real scratchpad addresses, native header, upload rows and output packets retained. + * Reopening condition: Reopen only after independent source, data-layout, ABI or compiler evidence specifically explains the residual above; then test that single hypothesis. Do not repeat tied source forms or flag sweeps. + * Where listed, tools/check.py measurements predate retention; no compilation was rerun for this copy. + */ + +/* Nonmatching manual reconstruction, 2026-10-23. + * 00300AA8: 1188 bytes versus retail 1200; frame 0xB0 (retail 0xA0). + * Profile: default -O2 -G0 -ffunction-sections. + * All carrying US/US2/PAL checks returned zero matches and exit 1; + * catalogue absence is recorded separately and never counted as success. + * Complete texture upload and GIF payload construction. DMA queue qualifiers are the existing declaration from00300618-g8.c, not a new barrier. The frame/allocation differs. + * Reopen only with new measured source/type/scheduling evidence. No artificial + * barriers, register pins, generated decompiler C or omitted behavior were used. + */ + +typedef unsigned char u8; +typedef int s32; +typedef unsigned int u32; +typedef int ParticleWord __attribute__((mode(TI))); +typedef union { ParticleWord word; float f[4]; } ParticleVector; +typedef struct { float x,y,z,w; } V4_F213135aa; +extern void func_00282CC0(void *, void *, void *); +extern void func_00282CF0(void *, void *, void *); +extern void func_00282D30(void *, void *, float); + +typedef unsigned long long NativeCompactU64; +typedef union { + s32 field00[4]; + struct { + NativeCompactU64 field00; + short field08; + short field0A; + unsigned short field0C; + unsigned short field0E; + } compact; +} NativeCompactRow16; +typedef struct { + char pad00[4]; + short field04; + char pad06[2]; + short field08; + short field0A; + char pad0C[0x14]; + u32 field20; +} NativeSpawnHeader; +extern void func_00300748(NativeSpawnHeader *, char *, float); +extern void func_00283388(float *); +/* Partial native draw header through the packed-vertex tint field. */ +typedef struct { + short vertexCount, quadCount, boneCount, textureCount, frameCount, boneStride; + float scale; + void *vertices; + u8 *quads; + NativeCompactRow16 *textures; + u8 *texels; + void *bones; + int packed; + u32 tint; +} NativeDrawHeaderView; +typedef struct { + u8 *source; + unsigned short reserved04, oldPage; + u8 *secondSource; + u8 width, height; + unsigned short newPage; +} TextureUpload; +typedef struct { NativeCompactU64 color, coordinate, position, reserved18; } ScratchVertex; +extern int D_001A72D0 __attribute__((sda)); +extern int D_001A7350[2]; +extern u8 D_001A7BB1 __attribute__((sda)); +extern int D_001B15FC; +extern TextureUpload D_001B9340[64]; +extern float D_001AD870[16]; +extern float D_001B90F0[2], D_001B9240[2]; +extern ParticleWord D_001AD830; +/* The queue retains the qualifiers of its existing renderer declaration. */ +extern volatile unsigned int * volatile D_001B22A8; +extern void func_00300910(void); +extern void func_00282D30(void *, void *, float); +extern void func_00283788(void *, void *, void *); +extern void func_0029D258(void *, void *, int, int); +extern void func_0029D398(void *, void *, int, u32, int); + +/* Upload referenced textures, transform the native vertices and submit each quad. */ +void func_00300AA8(char *object, float x, float y, int flags, float frame, float *transform) +{ + NativeDrawHeaderView *header=(NativeDrawHeaderView *)object; + float matrix[16] __attribute__((aligned(16))); + NativeCompactRow16 *texture; + TextureUpload *upload; + ScratchVertex *scratch=(ScratchVertex *)0x70000000; + u8 *quad; + volatile u32 *packet; + NativeCompactU64 *payload; + NativeCompactU64 command; + int index, oldCursor, page, width, height, shift, queued, vertex; + if (!(flags >> 24)) return; + for (index=0; indextextureCount; ++index) { + texture=&header->textures[index]; + oldCursor=D_001A72D0; + page=oldCursor+0x400; + width=(short)texture->compact.field0C; + height=(short)texture->compact.field0E; + shift=width-6; + if (shift<0) shift=0; + D_001A72D0=page+(1U<<((width+height)&31)); + queued=D_001B15FC; + texture->compact.field00=(NativeCompactU64)(long)(page>>8) + | (NativeCompactU64)(1U<<(shift&31))<<14 + | (NativeCompactU64)((long)((NativeCompactU64)texture->compact.field0C<<48)>>22) + | 0x01300000UL + | (NativeCompactU64)((long)((NativeCompactU64)texture->compact.field0E<<48)>>18) + | (NativeCompactU64)(long)(oldCursor>>8)<<37 + | 0x400000000ULL | 0x8000000000000000ULL; + if (queued<64) { + upload=&D_001B9340[queued]; + upload->oldPage=(u32)oldCursor>>8; + upload->reserved04=0; + upload->source=header->texels+(unsigned short)texture->compact.field0A*16; + D_001B15FC=queued+1; + upload->secondSource=header->texels+(unsigned short)texture->compact.field08*16; + upload->width=texture->compact.field0C; + upload->newPage=(u32)page>>8; + upload->height=texture->compact.field0E; + } + } + func_00300910(); + func_00282D30(transform+12,transform+12,166.66600036621094f); + D_001AD870[0]=D_001B9240[0]/D_001B90F0[0]; + D_001AD870[9]=-(D_001B9240[1]/D_001B90F0[1]); + if (D_001A7BB1) D_001AD870[9]*=0.7500000596046448f; + func_00283788(matrix,D_001AD870,transform); + matrix[13]+=(float)D_001A7350[1]+y*16.0f; + matrix[12]+=(float)D_001A7350[0]+x*16.0f; + /* PS2 scratchpad identity matrix and expanded skinning records. */ + func_00283388((float *)0x70001fc0); + if (header->frameCount) func_00300748((NativeSpawnHeader *)header,(char *)0x70002000,frame); + if (!header->packed) func_0029D258(header->vertices,matrix,header->vertexCount,flags); + else func_0029D398(header->vertices,matrix,header->vertexCount,header->tint,flags); + command=0x80000004cc007ffbULL; + quad=header->quads; + for (index=0; indexquadCount; ++index,quad+=4) { + if (*(int *)quad>>8 == -1) { + if ((short)quad[0]textureCount) command=header->textures[quad[0]].compact.field00; + else command=0x80000004cc007ffbULL; + --index; + } else { + D_001B22A8[0]=0x10000008; + D_001B22A8[1]=0; + D_001B22A8[2]=0; + D_001B22A8[3]=0x50000008; + packet=D_001B22A8; + *(ParticleWord *)(packet+4)=D_001AD830; + D_001B22A8=packet+8; + *(NativeCompactU64 *)(packet+8)=command; + *(NativeCompactU64 *)(packet+10)=0x5c; + payload=(NativeCompactU64 *)(packet+12); + vertex=quad[0]; + payload[0]=scratch[vertex].color; payload[1]=scratch[vertex].coordinate; payload[2]=scratch[vertex].position; + vertex=quad[1]; + payload[3]=scratch[vertex].color; payload[4]=scratch[vertex].coordinate; payload[5]=scratch[vertex].position; + vertex=quad[3]; + payload[6]=scratch[vertex].color; payload[7]=scratch[vertex].coordinate; payload[8]=scratch[vertex].position; + vertex=quad[2]; + payload[9]=scratch[vertex].color; payload[10]=scratch[vertex].coordinate; payload[11]=scratch[vertex].position; + D_001B22A8+=0x1c; + } + } +} From a5d30909818fc4efd67a2282cadc08c7c5032bc8 Mon Sep 17 00:00:00 2001 From: Flavius Date: Tue, 27 Oct 2026 20:20:07 -0600 Subject: [PATCH 7/9] feat(fcheck): check functions at their places in level 00 A level00 capture script plays New Game into level 00 (Aranos) to frame 7000: through the intro, then running with the left stick, jumping, the wrench, the pause menu, weapons and the map, in four phases. A function with a place in level 00's program is captured there, the CRC guard keeping the boot's code at the address before the load out; a resident boot function (below 0x1A7C80) at its boot address. check groups a function's states by the program they are of and builds it once for each: in a level its symbols resolve at the level's addresses (its catalogue places, the level build's placed.json, the boot's resident values, boot addresses the level moved read from the level's copies of boot code, a boot function's own copy first), against the level's bytes. build takes --level, capture lists may give states per phase, and check --retail runs retail's own bytes as the candidate, the harness's self-test. --- tests/test_fcheck.py | 135 +++++++++ tools/fcheck.py | 655 +++++++++++++++++++++++++++++++++---------- 2 files changed, 641 insertions(+), 149 deletions(-) diff --git a/tests/test_fcheck.py b/tests/test_fcheck.py index 9a878e322..482501432 100644 --- a/tests/test_fcheck.py +++ b/tests/test_fcheck.py @@ -209,11 +209,146 @@ def test_returns(self): self.assertEqual(fcheck.returns("func_1", "extern int func_2(void);\nu16 func_1(void) {\n}"), "int") def test_scripts_stop_before_a_level_runs(self): + # The boot's scripts end before level 00 runs; a level's script says which level it plays. self.assertLessEqual(fcheck.SCRIPTS["newgame"]["frames"], 1650) + self.assertNotIn("level", fcheck.SCRIPTS["menu"]) + self.assertNotIn("level", fcheck.SCRIPTS["newgame"]) for s in fcheck.SCRIPTS.values(): self.assertEqual(s["phases"], sorted(s["phases"])) self.assertTrue(all(int(p.split(":")[0]) < s["frames"] for p in s["presses"])) +class LevelTests(unittest.TestCase): + """Level placement: a function's place in level 00's program, resident boot functions below + LEVEL_BASE, the boot build's values in a level, and which program a state is of.""" + BOOT_FUNCS = {"func_00150000": (0x00150000, 64), # resident: below LEVEL_BASE + "func_00200000": (0x00200000, 32), # replaced by the level; copy at 0x2A0100 + "func_00210000": (0x00210000, 16)} # replaced, no copy in level 00 + CATALOGUE = {"func_00200000": {0: 0x002A0100, 3: 0x002B0100, -1: 32}, + "func_L00_002A0200": {0: 0x002A0200, -1: 8}, + "func_L03_002B0300": {3: 0x002B0300, -1: 8}} + + def setUp(self): + self.BOOT_FUNCS, self.CATALOGUE = dict(self.BOOT_FUNCS), dict(self.CATALOGUE) # a test may add to its own + fcheck.level_functions.cache_clear() + self.patches = [mock.patch.object(fcheck, "boot_functions", lambda: self.BOOT_FUNCS), + mock.patch.object(fcheck, "catalogue", lambda: self.CATALOGUE)] + for p in self.patches: + p.start() + + def tearDown(self): + for p in self.patches: + p.stop() + fcheck.level_functions.cache_clear() + + def test_level_functions(self): + prog = fcheck.Program(0) + self.assertEqual(prog.functions(), {"func_00150000": (0x00150000, 64), "func_00200000": (0x002A0100, 32), + "func_L00_002A0200": (0x002A0200, 8)}) + self.assertTrue(prog.own("func_00200000") and prog.own("func_L00_002A0200")) + self.assertFalse(prog.own("func_00150000") or prog.own("func_L03_002B0300") or prog.own("func_00210000")) + self.assertTrue(fcheck.BOOT_PROGRAM.own("func_00200000")) + self.assertFalse(fcheck.BOOT_PROGRAM.own("func_L00_002A0200")) + + def test_states_and_capture_lines(self): + level = fcheck.Program(0) + self.assertEqual(fcheck.script_program("level00"), level) + self.assertEqual(fcheck.script_program("menu"), fcheck.BOOT_PROGRAM) + self.assertEqual(fcheck.state_program("func_00200000", Path("states/level00/func_00200000.0.snap")), level) + self.assertEqual(fcheck.state_program("func_00200000", Path("states/menu/func_00200000.0.snap")), fcheck.BOOT_PROGRAM) + # a resident boot function called in the level is the boot's code + self.assertEqual(fcheck.state_program("func_00150000", Path("states/level00/func_00150000.0.snap")), fcheck.BOOT_PROGRAM) + code = bytes(range(32)) + with mock.patch.object(fcheck.Program, "retail", lambda self, a, n: code[:n] if a == 0x002A0100 else bytes(n)): + line = fcheck.capture_line("func_00200000", level, 1).split() + self.assertEqual(line, ["002a0100", "32", f"{fcheck.zlib.crc32(code):08x}", "func_00200000", "1"]) + self.assertEqual(fcheck.capture_line("func_00150000", level).split()[:2], ["00150000", "64"]) + self.assertIsNone(fcheck.capture_line("func_00210000", level)) + self.assertIsNone(fcheck.capture_line("func_L03_002B0300", level)) + + def test_level_scripts(self): + s = fcheck.SCRIPTS["level00"] + self.assertEqual(s["level"], 0) + self.assertGreaterEqual(s["phases"][0], 1500) # level 00 is loaded by then + self.assertTrue(all(int(p.split(":")[0]) < s["frames"] for p in s["sticks"])) + + def test_copy_map(self): + # func_00200000's level copy is its code with the data it addresses moved: lui/addiu of + # 0x00263640 and a lw at 0x00263690 in the boot, of 0x0028C080 and 0x0028C0D0 in the level. + LUI_V1, LW_V1 = 0x3C030000, 0x8C620000 # lui $v1, 0; lw $v0, 0($v1) + def code(a, b): + return words(LUI_V0 | (a + 0x8000) >> 16, ADDIU_V0 | (a & 0xFFFF), LUI_V1 | (b + 0x8000) >> 16, + LW_V1 | (b & 0xFFFF), JR_RA, 0, 0, 0) + boot, level = code(0x00263640, 0x00263690), code(0x0028C080, 0x0028C0D0) + fcheck.copy_map.cache_clear() + with mock.patch.object(fcheck, "retail_bytes", lambda a, n: boot[:n] if a == 0x00200000 else bytes(n)), \ + mock.patch.object(fcheck.Program, "retail", lambda self, a, n: level[:n] if a == 0x002A0100 else bytes(n)): + self.assertEqual(fcheck.copy_map(0), {0x00263640: 0x0028C080, 0x00263690: 0x0028C0D0}) + self.assertEqual(fcheck.level_address(0, 0x00263640), 0x0028C080) + self.assertEqual(fcheck.level_address(0, 0x00263660), 0x0028C0A0) # between two pairs that agree + self.assertIsNone(fcheck.level_address(0, 0x002636A0)) # past the last: not inferred + fcheck.copy_map.cache_clear() + + def test_own_copy_pairs_win(self): + # func_00200000's level copy stores the address of the level's own variant of a callback + # (0x002A0400) where the boot's stores func_00210000: the copy's own pairing gives it, + # though the catalogue would not (func_00210000 has no place in level 00). + boot = words(LUI_V0 | 0x0021, ADDIU_V0, JR_RA, 0, 0, 0, 0, 0) + level = words(LUI_V0 | 0x002A, ADDIU_V0 | 0x0400, JR_RA, 0, 0, 0, 0, 0) + fcheck.copy_pairs.cache_clear() + fcheck.copy_map.cache_clear() + import build + placement = build.Placement("00_aranos_tutorial", {"segments": [], "sections": []}) + with mock.patch.object(fcheck, "retail_bytes", lambda a, n: boot[:n] if a == 0x00200000 else bytes(n)), \ + mock.patch.object(fcheck.Program, "retail", lambda self, a, n: level[:n] if a == 0x002A0100 else bytes(n)), \ + mock.patch.object(fcheck, "program_placement", lambda level: placement), \ + mock.patch.object(fcheck, "program_placed", lambda key: {"values": {}}), \ + mock.patch.object(fcheck, "placed", lambda: {"values": {}}), \ + mock.patch.object(fcheck, "aligned_values", lambda *a: {}): + self.assertEqual(fcheck.copy_pairs("func_00200000", 0), [(("data", 0x00210000), 0x002A0400)]) + r = fcheck.Resolver(fcheck.Program(0), "func_00200000", {"symbols": [], "sections": []}, {}, {}) + self.assertEqual(r.named("func_00210000"), 0x002A0400) + self.assertEqual(r.function("func_00210000"), 0x002A0400) + # another function's candidate has no such pairing: func_00210000 is not in level 00 + r = fcheck.Resolver(fcheck.Program(0), "func_L00_002A0200", {"symbols": [], "sections": []}, {}, {}) + self.assertIsNone(r.named("func_00210000")) + fcheck.copy_pairs.cache_clear() + fcheck.copy_map.cache_clear() + + def test_resolver_in_a_level(self): + import build + prog = fcheck.Program(0) + placement = build.Placement("00_aranos_tutorial", {"segments": [], "sections": []}) + level_values = {"D_001B0960": "0x001b2000", "F1234_FUN_x": "0x002a0100"} + boot_values = {"D_00140000_x": "0x00140000", # resident data: kept + "r1_helper": "0x00200000", # a replaced boot function: its copy + "r1_gone": "0x00210000", # replaced, no copy: not known in the level + "D_001B0960": "0x001b0960"} # the level's own value wins + with mock.patch.object(fcheck, "program_placement", lambda level: placement), \ + mock.patch.object(fcheck, "program_placed", lambda key: {"values": level_values}), \ + mock.patch.object(fcheck, "placed", lambda: {"values": boot_values}), \ + mock.patch.object(fcheck, "aligned_values", lambda *a: {}), \ + mock.patch.object(fcheck, "level_address", lambda level, a: 0x0028C080 if a == 0x00263640 else None): + r = fcheck.Resolver(prog, "func_L00_002A0200", {"symbols": [], "sections": []}, {}, {}) + self.assertEqual(r.named("func_00200000"), 0x002A0100) + self.assertEqual(r.named("func_00150000"), 0x00150000) + self.assertEqual(r.named("D_001B0960"), 0x001B2000) + self.assertEqual(r.named("D_00140000_x"), 0x00140000) + self.assertEqual(r.named("r1_helper"), 0x002A0100) + self.assertIsNone(r.named("r1_gone")) + self.assertEqual(r.named("D_L00_001B4000"), 0x001B4000) + self.assertIsNone(r.named("D_L03_001B4000")) # another level's data + self.assertIsNone(r.named("D_001B5000")) # a boot address the level replaces + self.assertEqual(r.named("D_00160000"), 0x00160000) # resident boot data + self.assertIsNone(r.named("func_00210000")) # a boot function not in the level + self.assertEqual(r.named("D_00263640"), 0x0028C080) # boot data the level's copies moved + # a name stating a boot function's address that the boot table names otherwise + self.BOOT_FUNCS["func_002001F8"] = (0x00200200, 8) + self.CATALOGUE["func_002001F8"] = {0: 0x002A0300, -1: 8} + fcheck.level_functions.cache_clear() + r = fcheck.Resolver(prog, "func_L00_002A0200", {"symbols": [], "sections": []}, {}, {}) + self.assertEqual(r.named("func_00200200"), 0x002A0300) + + if __name__ == "__main__": unittest.main() diff --git a/tools/fcheck.py b/tools/fcheck.py index 5904d453b..b826991ae 100644 --- a/tools/fcheck.py +++ b/tools/fcheck.py @@ -4,9 +4,9 @@ need not match a byte, only leave the same memory, result and kept registers as retail does from real calls of the game (OpenRAC's docs/port/BACKFILL.md, "The gate"). - python3 tools/fcheck.py check NAME [CANDIDATE.c] [--ulps N] [--work DIR] - python3 tools/fcheck.py build NAME [CANDIDATE.c] --out DIR - python3 tools/fcheck.py capture LIST [--script menu|newgame|all] [--frames N] [--force] + python3 tools/fcheck.py check NAME [CANDIDATE.c] [--ulps N] [--work DIR] [--retail] + python3 tools/fcheck.py build NAME [CANDIDATE.c] --out DIR [--level N] + python3 tools/fcheck.py capture LIST [--script menu|newgame|level00|all] [--frames N] [--force] python3 tools/fcheck.py source NAME python3 tools/fcheck.py scripts @@ -21,13 +21,34 @@ accesses and system calls are compared (each run on a fresh machine with the capture run's hooks table and disc, so a hooked library function answers as it did in the capture). Exit status: 0 SAME, 1 differs, 2 the candidate did not build or place, 3 no states, 4 every state -skipped. - -Placement. Each relocation is resolved against retail's addresses: +skipped. `--retail` is the harness's self-test: retail's own bytes of NAME, moved to 0x000A0000, +are the candidate (CANDIDATE, or src/'s C, gives only the return type); a DIFFERS on it is the +harness's or the states', never the C's. + +Programs. A state is of the program the run had loaded where NAME was called: a level's run +(level00) takes a function with a place in that level's program there (config/levels/ +functions.tsv: func_LNN_ names, and boot and shared functions with a NN: place), so its states +are of the level; every other state is of the boot (a boot run's, or a resident boot function's +in a level's run: the boot's memory below 0x1A7C80, which a level's program does not replace). +`check` builds NAME once for each program its states are of, against that program's addresses, +and the verdict is the worst: DIFFERS on any, else did not build, else SAME. In a level, retail +is the level's program (baserom/levels/NN_*/, its sections as tools/build.py's reference) above +0x1A7C80 and the boot's below. + +Placement. Each relocation is resolved against retail's addresses in the program: - a named symbol: the value NAME's own C needs to read as retail's bytes (tools/build.py's - derivation, when NAME is matched), the boot's function table, the values the boot build - derived (build/boot/placed.json), those other matched functions of the unit need, or the - address its name states; + derivation, when NAME is matched); in a level, for a boot function's own copy there, what + its boot address is in the copy (the copy is the same code: its references pair in order + with the boot's, copy_pairs; the level may use its own variant of a callback where the + catalogue also places a copy of the boot's); the program's function table (a level's: its + places, and the resident boot functions below 0x1A7C80); the values the program's build + derived (build/boot/placed.json, build/NN_*/placed.json from `tools/build.py level NN`); in a + level the boot build's values that hold there (below 0x1A7C80, or moved as below); those + other matched functions of the unit need; or the address its name states where the program + has it (in a level: D_LNN_ of that level and boot addresses below 0x1A7C80; a boot address + above it is moved to where the level's copies of boot code have it, copy_map: a boot + function's to its place in the level, else to what the copies call there, data to the + paired address, or inside a structure the distance pairs on both sides agree on); - another function's section of the unit: that function's retail address; a static helper with none is placed after NAME; - a writable section of the unit (.data, .sdata, .bss, .sbss): where retail has it, as this @@ -45,23 +66,30 @@ instructions aligned to retail's by kind (the note names each value so read); - a $gp-relative reference (-G8 units, R_MIPS_GPREL16/LITERAL): the target must be within $gp's 64 KB window (retail's $gp, 0x1AEFF0). A read-only constant is used where retail has - the same bytes there (the unit's own placement, else core.lit / .lit); anything else out of - range stops the build ("LINK ..."). + the same bytes there (the unit's own placement, else core.lit / .lit, in a level its lit at + 0x1A7C80); anything else out of range stops the build ("LINK ..."). `capture` plays the US v1.01 disc (baserom/SCUS_972.68.iso) headless in the interpreter (`openrac-boot --capture`, no window) and writes call states of the listed functions (names, -one a line) into build/fcheck/states/