From 99d2e513345f02a6a3f7cd81a58e544f3fc35869 Mon Sep 17 00:00:00 2001 From: MrPolo Date: Tue, 28 Jul 2026 18:27:32 -0700 Subject: [PATCH] Added objdiff support --- README.md | 47 ++++++++++++++++++++++++++- objdiff.json | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 objdiff.json diff --git a/README.md b/README.md index 588de5c..5110a86 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,16 @@ brew install \ ## Diffing a Function -This repo uses [asm-differ](https://github.com/simonlindholm/asm-differ) (`tools/asm-differ`, a submodule) to compare your current C output against the target assembly for a function. +Both diffing tools below compare your current build's object files against a known-good baseline (`expected/build`), so the general workflow is: + +1. `make setup` - only needed once, or after pulling changes that touch `splat.yaml`/the base ROM. Regenerates `asm/`, `bin/`, and `assets/` from `dmpj.d64`. +2. `make` - rebuilds the ROM from current source. If it succeeds, `md5sum -c checksum.md5` prints `build/dmpj.d64: OK`, confirming a bit-exact match against the original ROM regardless of which functions are matched yet (unmatched functions still compile to identical bytes via their `GLOBAL_ASM` stubs). +3. `cp -r build expected/build` - snapshot that known-good build as the baseline. Only needs to be redone after step 1/2 change what "known-good" looks like (e.g. after `make setup`, or after pulling other people's matches). +4. Diff a specific function with `asm-differ` or `objdiff` (below) to see if your in-progress C matches. + +### Using asm-differ + +[asm-differ](https://github.com/simonlindholm/asm-differ) (`tools/asm-differ`, a submodule), is a tool used by default on decomp.me, to compare your current C output against the target assembly for a function. Before diffing for the first time, snapshot a known-good build as the baseline to diff against: @@ -117,6 +126,42 @@ python3 tools/asm-differ/diff.py -mo func_80007D64 Run `python3 tools/asm-differ/diff.py --help` for the full list of options. +### Using objdiff + +[objdiff](https://github.com/encounter/objdiff) is an alternative diffing tool also used on decomp.me with a GUI, live rebuild-on-save, and a function/object browser - also the same diffing engine decomp.me uses under the hood, so a scratch that matches in the browser will match here too. The project's `objdiff.json` (repo root) is already configured with one unit per `c`-type source file. + +Install it (either works): + +```sh +# prebuilt binary - see https://github.com/encounter/objdiff/releases +# or, with Rust installed: +cargo install --locked --git https://github.com/encounter/objdiff.git objdiff-gui objdiff-cli +``` + +Like `asm-differ`, it needs a known-good baseline to diff against: + +```sh +make +cp -r build expected/build +``` + +Then launch the GUI, pointing it at the repo root (it won't auto-detect the current directory): + +```sh +objdiff -p /path/to/PaintStudioDecomp +``` + +- The sidebar lists every unit from `objdiff.json` (one per `c`-type source file, e.g. `src/gameboot.c`). +- Click a unit to open its diff view: target assembly on the left, your current build on the right, function by function. Matching lines are unhighlighted; mismatches are highlighted, similar to `asm-differ`'s output. +- Each unit also shows a match percentage in the sidebar so you can see progress at a glance. +- With the window focused, save the source file you're working on and objdiff rebuilds and re-diffs automatically (via `watch_patterns` in `objdiff.json`) - no need to switch back to a terminal. + +To check overall progress without the GUI, use the CLI instead: + +```sh +objdiff-cli report generate +``` + ## Using decomp.me Functions can also be matched entirely in the browser using [decomp.me](https://decomp.me/), which doesn't require a local build environment. This is useful for getting help from others, or for working without a full local setup. diff --git a/objdiff.json b/objdiff.json new file mode 100644 index 0000000..1ae9c83 --- /dev/null +++ b/objdiff.json @@ -0,0 +1,89 @@ +{ + "custom_make": "make", + "build_base": true, + "build_target": false, + "watch_patterns": [ + "*.c", + "*.h", + "*.s", + "Makefile", + "splat.yaml" + ], + "ignore_patterns": [ + "build/**/*" + ], + "units": [ + { + "name": "src/gameboot.c", + "target_path": "expected/build/src/gameboot.c.o", + "base_path": "build/src/gameboot.c.o", + "metadata": { + "source_path": "src/gameboot.c" + } + }, + { + "name": "src/main/787C8.c", + "target_path": "expected/build/src/main/787C8.c.o", + "base_path": "build/src/main/787C8.c.o", + "metadata": { + "source_path": "src/main/787C8.c" + } + }, + { + "name": "src/capture/11C2D8.c", + "target_path": "expected/build/src/capture/11C2D8.c.o", + "base_path": "build/src/capture/11C2D8.c.o", + "metadata": { + "source_path": "src/capture/11C2D8.c" + } + }, + { + "name": "src/leo/lib/leotestunit.c", + "target_path": "expected/build/src/leo/lib/leotestunit.c.o", + "base_path": "build/src/leo/lib/leotestunit.c.o", + "metadata": { + "source_path": "src/leo/lib/leotestunit.c" + } + }, + { + "name": "src/libultra/io/piacs.c", + "target_path": "expected/build/src/libultra/io/piacs.c.o", + "base_path": "build/src/libultra/io/piacs.c.o", + "metadata": { + "source_path": "src/libultra/io/piacs.c" + } + }, + { + "name": "src/libultra/io/vitbl.c", + "target_path": "expected/build/src/libultra/io/vitbl.c.o", + "base_path": "build/src/libultra/io/vitbl.c.o", + "metadata": { + "source_path": "src/libultra/io/vitbl.c" + } + }, + { + "name": "src/libultra/gu/scale.c", + "target_path": "expected/build/src/libultra/gu/scale.c.o", + "base_path": "build/src/libultra/gu/scale.c.o", + "metadata": { + "source_path": "src/libultra/gu/scale.c" + } + }, + { + "name": "src/overlays/camera/27BA20.c", + "target_path": "expected/build/src/overlays/camera/27BA20.c.o", + "base_path": "build/src/overlays/camera/27BA20.c.o", + "metadata": { + "source_path": "src/overlays/camera/27BA20.c" + } + }, + { + "name": "src/overlays/saveload/saveload.c", + "target_path": "expected/build/src/overlays/saveload/saveload.c.o", + "base_path": "build/src/overlays/saveload/saveload.c.o", + "metadata": { + "source_path": "src/overlays/saveload/saveload.c" + } + } + ] +} \ No newline at end of file