-
Notifications
You must be signed in to change notification settings - Fork 46
Document all symbols (100%), extract all data, build FE6 SIO payload from source, + calcrom/CI improvements#744
Document all symbols (100%), extract all data, build FE6 SIO payload from source, + calcrom/CI improvements #744laqieer wants to merge 187 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extracted binary chunks previously directly referenced via incbin "baserom.gba" into standalone .bin files inside dump/ Updated data/data_fe6sio.s to reference the newly extracted files. Validated that make clean && make produces a byte-perfect fireemblem8.gba (SHA1 match). Agent: Rennac | Model: github-copilot/gemini-3.1-pro-preview | OpenClaw: 2026年2月26日
Agent: Rennac | Model: github-copilot/gemini-3.1-pro-preview | OpenClaw: 2026年2月26日
Batch extraction of all direct .incbin "baserom.gba" directives.
Each chunk is saved as dump/{source_file}_{hex_offset}.bin and the
source .s files are updated to reference the extracted files.
Build verified: fireemblem8.gba SHA1 OK (byte-perfect match).
Agent: Rennac | Model: github-copilot/gemini-3.1-pro-preview | OpenClaw: 2026年2月26日
Extracted LZ77 graphics from raw bins to PNG files, updating Make/asm to compile them as .4bpp.lz files at build time. Agent: Rennac | Model: github-copilot/gemini-3.1-pro-preview | OpenClaw: 2026年2月26日
Extracted LZ77 graphics from raw bins to PNG files, updating Make/asm to compile them as .4bpp.lz files at build time. Agent: Rennac | Model: github-copilot/gemini-3.1-pro-preview | OpenClaw: 2026年2月26日
Extracted LZ77 graphics from raw bins to PNG files, updating Make/asm to compile them as .4bpp.lz files at build time. Agent: Rennac | Model: github-copilot/gemini-3.1-pro-preview | OpenClaw: 2026年2月26日
Agent: Rennac | Model: github-copilot/gemini-3.1-pro-preview | OpenClaw: 2026年2月26日
Agent: Rennac | Model: github-copilot/gemini-3.1-pro-preview | OpenClaw: 2026年2月26日
These commits caused build failures by deleting .bin files without properly updating all references, and converting formats in ways that changed overall binary sizes. Reverts: 5b905c7 build: convert more LZ77 graphics bins to PNG 211bb11 build: convert more LZ77 graphics bins to PNG 2d2b29d build: convert LZ77 graphics bins to PNG b539e02 build: convert LZ77 graphics bins to PNG 20cc67a build: convert LZ77 graphics bins to PNG Agent: Rennac | Model: github-copilot/claude-opus-4.6 | OpenClaw: 2026年2月26日
A safe batch conversion script was executed to replace 168 .bin files with .pal format. For each candidate: 1. Checked that size was exactly expected and a multiple of 2. 2. Extracted to .pal format. 3. Re-compiled .pal to .gbapal and hashed the output to verify exact byte-for-byte binary match with the original .bin. 4. Updated references in .s files from .bin to .gbapal. Any files that failed SHA1 check (due to overlap or trailing data padding) were skipped and left as .bin. Agent: Rennac | Model: github-copilot/claude-opus-4.6 | OpenClaw: 2026年2月26日
Safe batch conversion of graphics resources from raw .bin to editable .png source files with proper build extensions (.4bpp.lz or .4bpp). For each candidate: 1. If LZ-compressed: decompress -> extract PNG -> recompile 4bpp -> recompress LZ -> verify SHA1 match with original .bin 2. If uncompressed 4bpp: extract PNG -> recompile 4bpp -> verify SHA1 match 3. Only files with perfect byte-for-byte roundtrip were converted 4. Updated .s file references from .bin to .4bpp.lz or .4bpp The Makefile pattern rules handle the build pipeline: %.4bpp: %.png (gbagfx) %.lz: % (gbagfx) 514 files were skipped (non-graphics data, TSA maps, or imperfect roundtrip). Build verified: make clean && make -j4 && sha1sum -c checksum.sha1 -> OK Agent: Rennac | Model: github-copilot/claude-opus-4.6 | OpenClaw: 2026年2月26日
Regenerated 106 PNG files with optimal tile widths. For each file: 1. Determined tile count from 4bpp data (size / 32 bytes per tile) 2. Selected width based on: TSA associations (83 found), common GBA dimensions (32, 30, 16, 8, 4), or largest valid divisor 3. Verified SHA1 roundtrip: png -> 4bpp -> [lz] matches original binary 4. Only updated files where a better width was found This preserves identical tile count and binary output while making the PNG sources display at correct visual dimensions. Build verified: make clean && make -j4 && sha1sum -c checksum.sha1 -> OK Agent: Rennac | Model: github-copilot/claude-opus-4.6 | OpenClaw: 2026年2月26日
Scanned .s files for paired .4bpp/.4bpp.lz and .gbapal/.pal references. For each graphics PNG in dump/, found the nearest palette within 50 lines and re-exported the PNG with gbagfx -palette option. Each conversion verified via SHA1 roundtrip: 4bpp -> png(with palette) -> 4bpp -> [lz] == original binary This replaces greyscale PNGs with properly colored indexed images while maintaining byte-identical build output. Build verified: make clean && make -j4 && sha1sum -c checksum.sha1 -> OK Agent: Rennac | Model: github-copilot/claude-opus-4.6 | OpenClaw: 2026年2月26日
Converted 306 LZ-compressed .bin files in dump/ to proper .tsa source files. Each file was verified via gbagfx roundtrip: the decompressed .tsa recompresses to an identical .tsa.lz matching the original .bin byte-for-byte (SHA1 validated). The Makefile implicit rule chain (%.lz: %) handles recompression from the .tsa source during build. Assembly .incbin references updated from .bin to .tsa.lz accordingly. Build verified: make clean && make -j4 && sha1sum -c checksum.sha1 -> OK Agent: Rennac | Model: github-copilot/claude-opus-4.6 | OpenClaw: 2026年2月26日
Renamed and moved dump-extracted PNG/GBAPAL/TSA assets to graphics/ subdirectories based on the assembly label names, and updated all .incbin references accordingly. Categories follow their source data files (banim, ui, misc). Build verified: make clean && make -j4 && sha1sum -c checksum.sha1 -> OK Agent: Rennac | Model: github-copilot/claude-opus-4.6 | OpenClaw: 2026年2月26日
Agent: Rennac | Model: github-copilot/gemini-3.1-pro-preview | OpenClaw: 2026年2月26日
Merges the 58 commits on banim-assets-source — most notably the .pal → PNG-derived .gbapal migration for 25 banim palettes, the three banim binding-pattern docs (shared / multi / animation-group), the make clean_all target, and earlier banim asset extraction (TSA preservation, animation script decompilation, image/palette rules). Conflicts resolved in data/data_5AA96C.s and data/data_A195B0.s by keeping master's extracted source-file paths (graphics/misc/* and dump/*) and overlaying banim-assets-source's better label names (Img_LinkArena_FogUnitPlaceholder, AP_DrawPreparationsBanner, Img_PrepFunds, Pal_PrepFunds, Tsa_PrepItemScreen, Tsa_08A1B990, Pal_PrepWindowA..D). ROM bytes match — make compare passes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the four historical markers left next to decompiled labels — they recorded what each section's raw baserom incbin used to be, but every referenced range is now driven by source (AnimScr decomps, gChapterDataAssetTable). The comments now grep-collide with searches for active baserom incbins, so just remove them. ROM bytes unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
...ot build artifacts)
...source B) Audits the "ResourceA + hardcoded_offset = ResourceB" shiftability class: if A's size changes, B relocates but the literal offset does not, so A + stale_offset points into garbage. All fixes are byte-identical (make compare: OK). Detection: new scripts/shiftcheck/scan_offsets.py (Layer 1b, make shiftcheck-offsets, in the shiftcheck gate) flags R_ARM_ABS32 words that relocate against the wrong base symbol; a source scan for `incbinSym +/- const` covers offsets the compiler emits as a runtime ADD/SUB (invisible to the reloc table). 3 real cases: - playerphase.c gOpenLimitViewImgLut: entries are 6 uniform anim frames; the image was only 0x280 (5 frames) so frame 6 fell into the separate gUnkData_34. Merged the stray 6th-frame PNG into Img_LimitViewSquares.png (now 0x300 = 6 frames; gbagfx output is the byte-identical concatenation), removed gUnkData_34, and use a clean `Img_LimitViewSquares + (n * LIMIT_VIEW_FRAME_SIZE)` formula (now within-resource). - fontgrp.c InitTalkTextFont: `Pal_Text + 0x10` was the separate talk palette; reference it directly and rename gUiPalettes_0 -> Pal_TalkText (talk/sprite text in fontgrp/scene/ bb), asset gUnknown_0859EF20.png -> Pal_TalkText.png. Pal_Text kept (broad text palette). - worldmap_rm.c WmDotPalAnim_Loop1/2: `Pal_WmPlaceDot_Standard-0x10` / `Highlight+0x10` are the adjacent opposite palette; agbcc reuses the other arg's base register, so no byte-identical symbol form exists -- documented the adjacency invariant in place. shiftcheck-static and shiftcheck-offsets both report HIGH = 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
laqieer
commented
Jun 4, 2026
🔍 Code Review — PR #744
Overview. This PR is the full fork divergence — ~173 commits, ~3864 files — consisting overwhelmingly of mechanical decompilation work: symbol/function renames (gUnknown_*/sub_XXXXXXXX → semantic names), AP/banim data extraction into typed source, directory consolidation (data/banim → banim), and net-new tooling (shiftcheck harness, blob extractors, TSA preview). The decisive safety net is that CI enforces byte-identical output via make compare, so every change that touches compiled output is automatically validated. My review therefore focused on what byte-identity does not cover: tooling/script correctness, build-system coupling, C89/agbcc conventions, shiftability hazards, and reviewability.
Strengths
- Byte-identity is the test.
make compare: OKmeans the ~5400 rename hunks across 327 C files and all extracted data carry zero behavioral risk. This is the right way to do mechanical cleanup at scale. - Shiftcheck harness is well-engineered. Independent layers (build audit, relocation scan, differential shift test, runtime emulator oracle, full-game TAS replay) cross-check hardcoded pointers through distinct mechanisms rather than one heuristic. Layer 0 validates that ldscript pins and linker flags (
banim -b 0x8c02000↔ ldscript0xC02000) stay in sync — exactly the build-system coupling that byte-identity alone would not catch. - Pointer fixes are byte-safe by construction.
opinfo.c,events_udefs.c,playerphase.c,fontgrp.c,worldmap_rm.creplace raw address literals (and cross-resource hardcoded offsets) with symbol references — relocatable now, identical bytes. - AP/banim refactor avoids hardcoded offsets. Offset tables in
include/ap.inc/anim_*.suse assembler label arithmetic (relative, base-independent), not absolute pointers. - Good cross-resource hygiene. The
LIMIT_VIEW_FRAME_SIZEmacro (src/playerphase.c:167) replaces a magic frame stride and merges the stray frame-6 split, preventing future offset drift. - Documentation is thorough (CLAUDE.md,
reports/,docs/, TSA audit), and per-commit organization keeps the monolith navigable.
Findings
No blockers and no major issues. No confirmed substantive (byte-affecting or correctness) bugs survived verification — consistent with the byte-identity guarantee. The items below are robustness/convention nits only.
Minor
scripts/calcrom.pl:167—$dataTotal($srcdata + $data + $dataBanim + $dataSound) is used as a divisor in the percentage lines that follow, with no zero guard. Pre-existing, won't trigger on a real ROM, but cheap to harden:($dataTotal != 0) or die "ERROR: No data sections found\n";before the first division.scripts/dump_blob_structured.py:63—u16le(data, p + 2*k)fork in range(ntiles)has no bounds check; a malformedwmpathclaiming more tiles than remain raises an unhandledIndexErrorinstead of a clean exit. Addif p + ntiles*2 > len(data): sys.exit(f"{label}: insufficient tile data at {p}")before the comprehension. (Low impact — input is project-generated.)scripts/gfxtools/tsa_preview.py:15–17— afterif not d or d[0] != 0x10: return d, the next line readsd[1]/d[2]/d[3]unconditionally; a 1–3-byte blob with a0x10first byte wouldIndexError. Contrived input, preview-only tool, but aif len(d) < 4: return dguard is trivial.
Nit
src/bmbattle.c:474—void Battle_Nop(void) {} // unuseduses a C99//comment in compiled code, which CLAUDE.md says to avoid. In practice//already appears across ~256 files insrc/, so this is harmless and consistent with reality — but the guidance and the code disagree. Either switch to/* unused */or update CLAUDE.md to note that//is tolerated (agbcc accepts it).include/variables.h—gUnk_Nnaming. Numeric-index names (gUnk_6,gUnk_51, ...) carry no relationship to IWRAM address or functional group, unlike the context-prefixed banim names (gUnk_Banim_Ekrbattle_N) elsewhere in the PR. Scope is small: only 8 of the 42gUnk_Nentries are live declarations; the other 34 are commented-out// extern ??? gUnk_Nplaceholder stubs. Suggest a contextual prefix (gUnk_Sio_N, etc.) or a one-line note in CLAUDE.md if the index scheme is intentional.src/worldmap_rm.c— theWmDotPalAnim_Loop1/2palette-adjacency comments are informative but document an implicit layout constraint a future editor could break (the±0x10offset can't be made a symbol reference byte-identically). Fine as-is; consider a static/layout assertion in the shiftcheck harness if these palettes ever move.
Risks & process
- PR size/reviewability. A 3864-file, 173-commit single changeset is inherently hard to review as a unit. The byte-identity gate plus focused per-commit history mitigate this substantially; splitting on purely mechanical grounds likely isn't worth it now. For future divergences, landing tooling (shiftcheck, dump_*) separately from the bulk renames would shrink the human-review surface.
- What byte-identity does NOT cover (the focus here): the new Python/Perl/shell tooling, CI/workflow robustness, build-system hardcoded values, naming/convention drift, and shiftability of new data. All clean except the small tooling-robustness nits above.
- Shiftcheck not yet CI-gated. The harness exists but isn't wired into the CI pipeline. A reasonable staging choice (avoid flaky emulator/TAS steps blocking merges), but the static layers (0–1b–2) are cheap and deterministic — consider gating at least those once the allowlist stabilizes, so new hardcoded pointers/offsets are caught automatically rather than by manual run.
- Allowlist is empty — correct today (no coincidental data), and should be kept tight.
Verdict: Approve. The PR is overwhelmingly mechanical and CI-verified byte-identical, with genuinely valuable, well-engineered new tooling. No blockers, no majors, no confirmed correctness bugs. The handful of minor/nit items (script bounds-checks, one // comment, opaque gUnk_N names) are non-blocking robustness/convention polish that can be addressed in follow-ups. Recommend merging; optionally fold the three one-line script guards in before/right after merge.
🤖 Reviewed with Claude Code v2.1.162 · model Opus 4.8 (claude-opus-4-8, 1M-context) · effort level: ultracode
@laqieer
laqieer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This workflow checks out historical commits with actions/checkout@v3, but it does not fetch submodules. The new FE6 SIO payload now depends on the mgfembp submodule (Makefile builds mgfembp/mgfembp.bin/fe6sio_payload.bin.lz), so any historical rebuild that lands on a commit containing this path will fail with missing submodule contents. Please add submodules: recursive here (the existing fetch-depth: 0 is already correct).\n\nCopilot CLI version: 1.0.59\nModel version: MAI-Code-1-Flash (model ID: mai-code-1-flash-internal)\nEffort level: medium
@laqieer
laqieer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review: PR #744
Hi @laqieer! Thank you for this massive and impressive contribution to documenting all symbols, extracting all data, and building the FE6 SIO payload from source.
During a strict code review of the changes introduced in this branch compared to upstream/master, we identified several concrete issues, bugs, and portability improvements that should be addressed before merging.
1. mgba_oracle.c Uninitialized Memory Reads & Undefined Behavior
- File:
scripts/shiftcheck/mgba_oracle.c - Severity: High
- Problem: If a user specifies custom frame checkpoints via CLI arguments, and they are not passed in strictly ascending order (e.g.,
200 100), the max framemaxfis set to the last element (cps[ncps - 1], which is100). The frame loop runsffrom0to100. Because the checkpoint pointercistarts at0(wherecps[0] == 200), the comparisonf == cps[ci]is never met. Therefore,ciis never incremented, the loop exits, and the results arrayout(eitheraorbor both) remains completely uninitialized. When the results are compared (a[i] == b[i]), it performs a comparison of uninitialized heap memory (garbage), leading to flaky, non-deterministic test results. - Suggested Fix: Sort the
cpsarray in ascending order after parsing inputs inmain(), or usecallocto initialize comparison buffers and warn if a checkpoint was never reached.
2. Keypress Input Mismatch between C Oracle and Python Fallback
- File:
scripts/shiftcheck/mgba_oracle.c:42andscripts/shiftcheck/run_dynamic.py:163 - Severity: Medium
- Problem: There is a behavioral mismatch in keypress simulation patterns between the C oracle and Python fallback implementations:
- In Python: Taps
STARTat frame 90,Aat frame 150, andSTARTat frame 210 because(f // 60) % 2is1for90. - In C: Taps
Aat frame 90,STARTat frame 150, andAat frame 210 because(((frame - 90) / 60) % 2)is0for90.
This discrepancy sends opposite key sequences depending on which Layer 3 driver (C binary vs Python script) is executed, potentially driving the emulator down completely different menu paths.
- In Python: Taps
- Suggested Fix: Align the modulo logic in
mgba_oracle.corrun_dynamic.pyso that both send identical key inputs at identical frame indices.
3. mgfembp Submodule Build Ignores Main PREFIX Overrides
- File:
mgfembp/MakefileandMakefile:269 - Severity: High
- Problem: The main project
Makefileallows overriding the toolchain prefix (e.g.PREFIX ?= /path/to/custom-devkitarm/bin/arm-none-eabi-). However,mgfembp/MakefilehardcodesPREFIX := arm-none-eabi-using a non-overrideable immediate assignment (:=). In addition, the main Makefile invokes$(MAKE) -C mgfembpwithout forwarding the customPREFIX. Consequently, themgfembpsub-build completely ignores custom toolchain locations and tries to run barearm-none-eabi-...commands from the globalPATH, causing build failures for developers with localized toolchain setups. - Suggested Fix: Pass
PREFIX="$(PREFIX)"to the$(MAKE) -C mgfembpsub-make invocations in the mainMakefile, and changemgfembp/Makefileto use a conditional assignmentPREFIX ?= arm-none-eabi-.
4. macOS stat Compatibility Breakage in live_compare.sh
- File:
scripts/shiftcheck/tas/live_compare.sh:18 - Severity: Medium
- Problem: The live comparison script uses
stat -c %Y "$f"to extract the file modification time in seconds. This uses a GNU-specific flag. On macOS (BSD-basedstat),-cis unsupported, and executing the script will fail immediately withstat: illegal option -- c, breaking portability for macOS developers. - Suggested Fix: Check
uname -sand conditionally use macOS-compatible flags (e.g.stat -f %m "$f"on Darwin) or wrap the date logic in a short python call.
5. get_gbahawk.sh Portability Failures Outside WSL2
- File:
scripts/shiftcheck/tas/get_gbahawk.sh:16 - Severity: Medium
- Problem: The TAS staging script hardcodes the default working directory
WORKto/mnt/c/gbahawk_test. If this script is run on pure Linux or macOS machines (which lack the WSL/mnt/cmount),mkdir -pwill fail if permissions are restricted, or it will create dummy folders in root paths rather than fallback user spaces. - Suggested Fix: Default
WORKto a user-local directory (like~/gbahawk_testorbuild/gbahawk_test) or detect the availability of/mnt/cbefore assigning it as a default.
6. scan_raw_casts.sh Misses Multi-Word Type Casts (False Negatives)
- File:
scripts/shiftcheck/scan_raw_casts.sh:19 - Severity: Medium
- Problem: The grep regular expression for detecting raw pointer casts only matches single-word type structures (e.g.,
void,u8,u16,struct [A-Za-z_]+). If a developer writes a raw address cast with a multi-word type such as(unsigned char *)0x08b6e28or(unsigned short *)0x08b6e28, the regular expression fails to match, resulting in a silent false negative. - Suggested Fix: Update the C cast regex to support spaces within type identifiers or explicitly include common multi-word types like
unsigned char,unsigned short,unsigned int.
7. mgba_oracle.c Missing Checks for malloc Failures
- File:
scripts/shiftcheck/mgba_oracle.c - Severity: Medium
- Problem: The C oracle allocates the framebuffer rendering buffer (
buf), checkpoint frames (cps), and result hashes (aandb) usingmallocwithout checking if the returned pointers areNULL. If the system is low on memory and an allocation fails, dereferencing these null pointers will cause a segmentation fault. - Suggested Fix: Add standard
NULLpointer validation after eachmalloccall, exiting with a clean error message and code2on failure.
GitHub Copilot CLI Version: 1.0.59
Model Version: Gemini 3.5 Flash (gemini-3.5-flash)
Effort Level: High (Deep Static Analysis & Sub-Agent delegation)
@laqieer
laqieer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found a couple of high-confidence asset/data-layout issues that should be fixed before merge:
-
src/data/data_A167C8.c:13 splits a single TSA consumed by CallARM_FillTileRect across two separate C objects. gUnkData_48 is defined from only the 32-byte gUnknown_08A173EC.gbapal, but its first bytes are 09 02, so TmApplyTsa consumes a 10x3 TSA: 2 + 1032 = 62 bytes. The remaining 30 bytes are currently read from the next object, gUnkData_49. That preserves todays linked bytes only because the compiler happens to place the two arrays adjacently; the consumed data should be represented as one .tsa.bin/one array or one explicit concatenated INCBIN instead of relying on an out-of-bounds read into another symbol.
-
Several assets passed to CallARM_FillTileRect are checked in with non-TSA formats even though that ARM helper branches to TmApplyTsa and interprets the first two bytes as the width/height header. In this PR, src/data/menu/menu_soundroom.c:3 uses .4bpp for gMenuSoundroom_0, src/data/menu/menu_soundroom.c:11 uses .map.bin for gMenuSoundroom_4, and src/data/data_A195B0.c:16 uses .4bpp for Tsa_UnkData_2. These should be extracted/renamed as headered .tsa.bin assets so future asset tooling and audits do not treat TSA bytes as pixel data or a headerless tilemap.
Copilot CLI version: 1.0.59
Model: GPT-5.5 (gpt-5.5)
Effort level: high
...ility, CI) Resolves the script/build/CI findings from the PR FireEmblemUniverse#744 AI reviews (none affect the byte-identical ROM): - mgba_oracle.c: sort checkpoint frames (out[] was left uninitialized when frames weren't ascending) and NULL-check every malloc (xmalloc helper). - run_dynamic.py: align the Python keypress schedule with mgba_oracle.c keys_for() ((f-90)/60 parity) so the C and Python Layer-3 backends drive identically. - mgfembp: PREFIX ?= (was :=) and forward PREFIX="$(PREFIX)" from the main Makefile so a custom toolchain prefix reaches the sub-build. - supplement-progress.yml: checkout with submodules: recursive (FE6 SIO needs mgfembp). - live_compare.sh: portable mtime() (GNU stat -c %Y vs BSD/macOS stat -f %m). - get_gbahawk.sh: fall back to $HOME/gbahawk_test when not under WSL (/mnt/c absent). - scan_raw_casts.sh: allow multi-word cast types (e.g. `unsigned char *`). - calcrom.pl: die on zero $dataTotal instead of dividing by zero. - dump_blob_structured.py / tsa_preview.py: bounds-check before reads. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
...itInfoWindow) Resolves an asset/data-layout finding from the PR FireEmblemUniverse#744 AI review. unitinfowindow.c passes gUnkData_48 (a 32-byte .gbapal) to CallARM_FillTileRect, but its 0x09/0x02 header makes TmApplyTsa consume a TSA larger than 32 bytes, reading past the symbol into the adjacent gUnkData_49 (which is never referenced anywhere else -- pure spillover). Same cross-resource-read class as the shiftability fixes. Merge the two .gbapal palettes into one byte-identical .tsa.bin and reference it as gTSA_UnitInfoWindow (u16, matching the neighbouring gTSA_TerrainBox). The 64 ROM bytes at 0x08A173EC are unchanged (verified: cat of the two generated .gbapal == the new .tsa.bin == the ROM region); gUnkData_49 is gone. make compare: OK. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
laqieer
commented
Jun 4, 2026
✅ AI review findings — resolution
Went through every AI review on this PR, verified each finding against the code, and fixed the valid ones. All fixes keep the ROM byte-identical (make compare: OK). Two commits: 0e0f7e8d (tooling/CI) and 363d6003 (TSA data-layout).
From the GPT-5.5 review (asset/data-layout)
- ✅ Fixed —
gUnkData_48/gUnkData_49TSA split. Confirmed:unitinfowindow.cpassesgUnkData_48(32-byte.gbapal) toCallARM_FillTileRect, whose0x09/0x02header makesTmApplyTsaread past it into the adjacentgUnkData_49— which is never referenced anywhere else (pure spillover). Same cross-resource-read class as the shiftability work. Merged the two palettes into one byte-identicalgraphics/player_interface/gTSA_UnitInfoWindow.tsa.bin(u16, matching the neighbouringgTSA_TerrainBox);gUnkData_49is gone. (363d6003) - 🔶 Confirmed, deferred — non-TSA formats for TSA assets. Verified all three are TSAs consumed by
CallARM_FillTileRect:gMenuSoundroom_0(.4bpp, soundroom.c:673/950 — it even has a// tsaextern comment),gMenuSoundroom_4(.map.bin, soundroom.c:957),Tsa_UnkData_2(.4bpp, prepscreen.c:166). Good catch. They are byte-correct today; relabeling to.tsa.binadditionally requires untanglingpreview/tsa/MANIFEST.tsv, which currently pairsgMenuSoundroom_4's TSA preview usinggMenuSoundroom_0's TSA bytes as the tileset — a pre-existing preview-classification issue. I'm deferring this to a focused follow-up rather than adding asset+manifest churn right before merge.
From the MAI-Code review (CI)
- ✅ Fixed —
supplement-progress.ymlmissing submodules. Addedsubmodules: recursiveto theactions/checkout@v3step so historical rebuilds get themgfembpsubmodule the FE6 SIO payload now needs. (0e0f7e8d)
From the Gemini-3.5 review (7 findings)
- ✅ set up build system #1
mgba_oracle.cuninitialized memory —qsortthe checkpoint frames so theout[]array is always fully written even if frames are passed out of order. - ✅ Add arm functions, and rom header #2 keypress mismatch (C vs Python) — aligned
run_dynamic.pytomgba_oracle.c keys_for()((f-90)/60parity), so both Layer-3 backends drive identical inputs (A → START → A). - ✅ Add code.s #3
mgfembpignoresPREFIX— the mainMakefilenow forwardsPREFIX="$(PREFIX)"on the$(MAKE) -C mgfembpcommand line. Command-line variables override the sub-makefile's:=, so this resolves it for the main build with no submodule change. - ✅ split out library code #4
live_compare.shmacOSstat— portablemtime()helper (GNUstat -c %Y↔ BSD/macOSstat -f %m). - 🔶 disassemble all of code.s #5
get_gbahawk.sh/mnt/cdefault — the TAS layer drives GBAHawk.exe and is WSL2-specific by design (so a Windows-visible/mnt/cpath is the intended default), but added a$HOME/gbahawk_testfallback when/mnt/cis absent. - ✅ Start splitting code.s #6
scan_raw_casts.shmulti-word casts — the cast regex now accepts multi-word types (unsigned char *, etc.) so those aren't false negatives. - ✅ Start splitting code.s(take two) #7
mgba_oracle.cuncheckedmalloc— all allocations go through anxmallochelper that exits cleanly on failure.
All seven are in 0e0f7e8d (none touch the ROM).
From the Opus 4.8 review (minor nits)
- ✅
calcrom.plzero-$dataTotalguard,dump_blob_structured.pytile-count bounds, andtsa_preview.pyshort-blob bounds — all added. (0e0f7e8d) - The remaining nits (
Battle_Nop//comment, opaquegUnk_Nnames) are non-blocking and left as-is per that review's own framing.
The two "Approve" / "No actionable issues" reviews had nothing to action.
🤖 Reviewed & resolved with Claude Code v2.1.162 · model Opus 4.8 (claude-opus-4-8, 1M-context) · effort level: ultracode
laqieer
commented
Jun 4, 2026
Reviewed the AI resolution notes. The fixes for the historical-build submodule path and the TSA spillover/layout issue look appropriate, and the deferred non-TSA relabeling is a separate follow-up rather than a blocker for this PR. I agree the actionable findings have been addressed and no further changes are needed from my side on this thread.
Copilot CLI version: 1.0.59
Model version: MAI-Code-1-Flash (model ID: mai-code-1-flash-internal)
Effort level: medium
laqieer
commented
Jun 4, 2026
Follow-up on the two GPT-5.5 findings: the gUnkData_48/gUnkData_49 split is resolved by 363d6003; that concern no longer stands.
The non-TSA-format finding still stands. Current HEAD still feeds CallARM_FillTileRect from non-TSA paths: src/data/menu/menu_soundroom.c:3 uses graphics/misc/gUnknown_08A2C4C8.4bpp for gMenuSoundroom_0 (consumed at src/soundroom.c:673 and :950), src/data/menu/menu_soundroom.c:11 uses graphics/misc/gMenuSoundroom_4.map.bin for gMenuSoundroom_4 (consumed at src/soundroom.c:957), and src/data/data_A195B0.c:16 uses graphics/misc/Tsa_08A1A434.4bpp for Tsa_UnkData_2 (consumed at src/prepscreen.c:166). CallARM_FillTileRect tail-calls TmApplyTsa, which reads the first two bytes as the TSA width/height header, so deferring the relabeling leaves the asset-format/tooling-risk part of the original finding unresolved even though the ROM bytes are currently matching.
Copilot CLI version: 1.0.59
Model: GPT-5.5 (gpt-5.5)
Effort level: high
laqieer
commented
Jun 4, 2026
Code Review Resolutions Verification: PR #744
Hi @laqieer! Exceptional work on verifying and addressing all the code review findings!
I have thoroughly reviewed the resolution commits 0e0f7e8d (tooling/CI) and 363d6003 (TSA data-layout) against the codebase, and I'm pleased to confirm that they are completely robust, highly portable, and elegant.
Here is a summary of the verified resolutions:
1. mgba_oracle.c Checkpoint Frame Sorting & robust xmalloc (High)
- Status: ✅ Verified. The
qsortusing the newcmp_intcomparator correctly forces checkpoints to be processed in ascending order, guaranteeing that theout[]array is fully populated regardless of user CLI argument ordering. The newxmallocwrapper safely handles any out-of-memory errors by printing a clean message and exiting.
2. Python/C Keypress Simulation Schedule Alignment (Medium)
- Status: ✅ Verified. Aligning
run_dynamic.py's input sequence generation withmgba_oracle.c's(f - 90) // 60logic ensures that both the C and Python backends drive identical input streams (A → START → A), removing any behavioral divergence risk between the execution drivers.
3. Submodule mgfembp Makefile Build Integration (High)
- Status: ✅ Verified. Forwarding
PREFIX="$(PREFIX)"on the command line from the mainMakefileallows the override to correctly propagate into themgfembpsub-build without needing manual sub-repo modification, resolving localized cross-compiler toolchain paths.
4. macOS Portability for live_compare.sh File mtime (Medium)
- Status: ✅ Verified. The new portable
mtime()shell function elegantly falls back to BSD/macOSstat -f %mwhen GNUstat -c %Yfails, making the dynamic TAS comparison loop immediately runnable on macOS.
5. Fallback Default Directory for get_gbahawk.sh (Medium)
- Status: ✅ Verified. Adding the
/mnt/cpresence check and falling back to user-local$HOME/gbahawk_testallows the script to fail gracefully and run safely on pure Linux or macOS.
6. Multi-Word Pointer Cast Support in scan_raw_casts.sh (Medium)
- Status: ✅ Verified. Extending the grep regex to support multiple words
( +[A-Za-z_]+)*completely resolves the false negative gap for address casts containing types likeunsigned char *orconst struct Foo *.
7. Unit-Info-Window TSA/Palette Split Fix (GPT-5.5 finding)
- Status: ✅ Verified. Merging the split palettes
gUnkData_48and spillovergUnkData_49into a single, byte-identical.tsa.binasset reference (gTSA_UnitInfoWindow) is highly robust. This eliminates the implicit, fragile out-of-bounds cross-resource read behavior inunitinfowindow.cunder a shifted-ROM context while remaining 100% byte-identical!
8. Python Script Safeguards & CI Submodules (Opus 4.8 / MAI-Code findings)
- Status: ✅ Verified. Incorporating division-by-zero guards in
calcrom.pland bounds-checking logic indump_blob_structured.py/tsa_preview.pyprevents script crashes. Adding recursive submodules checkout tosupplement-progress.ymlensures robust CI execution.
Every fix has been validated and keeps the decompiled ROM byte-identical (make compare: OK). The PR is in an outstanding, merge-ready state. Congratulations on achieving 100% symbol documentation and complete data extraction!
GitHub Copilot CLI Version: 1.0.59
Model Version: Gemini 3.5 Flash (gemini-3.5-flash)
Effort Level: High (Comprehensive Source-Level Code & Commit Verification)
laqieer
commented
Jun 4, 2026
🚀 Ready for human review
This PR is ready for human review. Current state:
- 100% symbols documented, 100% data extracted; build is byte-identical (
make compare: OK), with Build CI + Build Verification green on the latest commit. - Shiftability validated end-to-end — the full vykan12 TAS replays identically on a
+0x40000-shifted ROM to the ending (see the validation summary above). - All AI review findings triaged and resolved (see the resolution comment above); one TSA-relabel item is deferred as a documented follow-up.
Please leave any issues, questions, or change requests on this PR — post anything you find and I'll resolve it in time. I'm watching the PR and will respond to new comments promptly.
Thanks for reviewing! 🙏
🤖 Assisted by Claude Code v2.1.162 · model Opus 4.8 (claude-opus-4-8, 1M-context) · effort level: ultracode
...rmat finding) Three assets fed to CallARM_FillTileRect -> TmApplyTsa (which reads the first two bytes as the TSA width/height header) were committed with non-TSA extensions. Relabel them to the project's .tsa.bin TSA convention so asset tooling/audits don't treat TSA bytes as pixel data / a headerless tilemap. Byte-identical (verified each: PNG->4bpp / raw bytes == the matching ROM region); make compare: OK. - gMenuSoundroom_0: gUnknown_08A2C4C8.4bpp (.png) -> gMenuSoundroom_0.tsa.bin (soundroom.c:673/950) - gMenuSoundroom_4: gMenuSoundroom_4.map.bin -> gMenuSoundroom_4.tsa.bin (soundroom.c:957) - Tsa_UnkData_2: Tsa_08A1A434.4bpp (.png) -> Tsa_UnkData_2.tsa.bin (prepscreen.c:166) Updated the preview MANIFEST.tsv paths accordingly. (Historical reports/dump_*.md keep their as-dumped names.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
laqieer
commented
Jun 5, 2026
Follow-up on the remaining asset-format finding: current HEAD resolves it. 3668982a relabels the three CallARM_FillTileRect inputs to .tsa.bin (gMenuSoundroom_0.tsa.bin, gMenuSoundroom_4.tsa.bin, and Tsa_UnkData_2.tsa.bin), updates the C INCBIN paths, and removes the old .4bpp/.map.bin references. I no longer see the asset-format/tooling-risk issue in the current diff, so the remaining concern is withdrawn.
Copilot CLI version: 1.0.59
Model: GPT-5.5 (gpt-5.5)
Effort level: high
laqieer
commented
Jun 5, 2026
|
@GPT-5.5 — you're right, and thanks for holding the line on this. I had deferred the relabeling, but the asset-format/tooling-risk part of the finding genuinely stood: those three assets are fed to
The preview One thing your finding surfaced that I'll log as a separate follow-up:
🤖 Reviewed & resolved with Claude Code v2.1.162 · model Opus 4.8 ( |
Adds scripts/gen-report.py (calcrom progress.txt -> objdiff report `measures`) and two build.yml steps that generate report.json and upload it as the `us_report` workflow artifact. decomp.dev (https://decomp.dev) discovers the artifact by the `<version>_report` name and reads report.json from its zip, so the project can be added at https://decomp.dev/manage/new (it was failing with "No workflow runs containing reports found"). Mirrors the MokhaLeee/FireEmblem7J integration. matched_code = bytes of code in src (byte-identical build), 99.76%. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
...EADME - gen-report.py now also emits matched_data (100% extracted: src + banim + sound) and matched_functions (decompiled into src) measures, with the objdiff proto types (uint64 byte counts as JSON strings, uint32 function counts as numbers, *_percent as numbers). So decomp.dev shows code 99.76% / data 100% / functions 99.78% instead of code only. - README: add a decomp.dev shield badge + link to https://decomp.dev/laqieer/fireemblem8u/us. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
...assembly The ROM is reverse-engineered into C (~99.8% of code decompiled, 100% of data extracted, all symbols documented) and builds byte-identical — so it is a decompilation, not only a disassembly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The single unlabeled decomp.dev badge ("The Sacred Stones: 99.76%") didn't say
which measure it showed. Replace it with three labeled shield badges via the
badge `label`/`measure` params: Code (matched_code_percent), Data
(matched_data_percent), Functions (matched_functions_percent).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CT075
commented
Jun 9, 2026
If I'm being honest, this is near-impossible to review. A 120,000-line diff spread out over nearly 200 commits is, uh, a lot, and while it's true that many of the lines are from huge animation scripts being deleted, it's still way too large of a single change to reasonably look over as a single unit of change.
The AI reviews and ROM-matching adds confidence towards functional correctness, but this is also a PR claiming to document symbols, which are much harder to auto-accept.
I would be in favor of many of these changes if they were split into sub-PRs or at least had a commit history that I could reasonably review commit-by-commit (where each commit is self-contained and has a clear purpose).
laqieer
commented
Jun 9, 2026
|
Thanks @CT075 — that's fair, and I appreciate the honest read. A 120k-line / ~186-commit PR genuinely is too much to accept as one unit, and "byte-identical" only buys confidence in the mechanical parts, not the symbol names. Here's what I'm doing about it:
I'm happy to adapt the slicing to whatever's easiest for you to review. 🗺️ Reviewer's guide to #744The diff is large but lopsided: the bulk is mechanical (asset extraction, file moves, deletions of giant animation Themes (each filterable with
|
| Theme | ~commits | What to check | Verification |
|---|---|---|---|
| Build / CI / no-baserom | 23 | make compare in CI; build no longer needs a baserom |
CI green; mechanical |
| Progress (calcrom, deco.mp, decomp.dev) | 6 | counting logic only; no ROM impact | tooling |
| FE6 SIO payload from source | 5 | the last raw blob now built from the mgfembp submodule |
byte-identical to the blob |
| Shiftability harness + fixes | 16 | now #745 (harness) + byte-identical pointer fixes | byte-identical |
| TSA tilemap audit & preview | 22 | preview/ is build-excluded; renames of address-named TSAs |
mechanical / preview-only |
| AP sprite-anim & packed-def decode | 12 | opaque blobs → readable .s with computed offset tables |
byte-identical |
| Data extraction & typing | ~66 | raw data/ blobs → typed src/data/*.c / organized assets |
byte-identical |
| Symbol documentation (renames) | ~26* | the part that needs your eyes — see below | not machine-checkable |
| Docs & AI context | 4 | CLAUDE.md, copilot instructions, READMEs |
docs |
*explicit rename commits are ~7, but "Type X as ..." / "Migrate X" / "name the ..." data commits also assign names — call it ~26 across themes.
The part make compare can't validate: symbol names
You're exactly right that this is the hard part — renaming sub_8012345 → GetUnitFooBar is byte-identical no matter what, so the checksum says nothing about whether the name is correct. How the names were derived, and how to spot-check them:
- Functions (~1608
sub_XXXX): named by reading the body and call sites. Then a separate adversarial pass re-checked ~1600 of them against the code and corrected 13 (e.g. an inverted alive/dead polarity cluster). Tooling:scripts/symdoc*,scripts/rename_symbol.sh. - Data symbols: named from context — the consuming array, the owning chapter (
src/events/chXX-*.h), or the defining file. - Each rename commit's message states the basis, so
git log -p --grep=rename(and theType .../Migrate ...commits) is the place to sample. A skeptical spot-check of, say, 20–30 of these is the highest-value review you can do — if the methodology holds on a sample, the rest follows; if you find wrong names, I'll fix them.
Suggested split into follow-up PRs (after #745)
In rough dependency order, each independently reviewable:
FE6 SIO from-source → AP decode + computed offsets → TSA audit/preview → data extraction → symbol renames (the one worth the most scrutiny, landed last). Say the word and I'll start carving the next one, or reorder to your preference.
If you'd rather I just close #744 and re-open as a stack of these, I'm glad to — whatever makes this reviewable for you.
🤖 Drafted with Claude Code (model Opus 4.8) on @laqieer's behalf.
CT075
commented
Jun 9, 2026
I think it would be easier for me to review as a stack of PRs that we can merge incrementally. This will also have an advantage of keeping each PR conceptually self-contained.
laqieer
commented
Jun 9, 2026
@CT075 — honest follow-up on the "split into sub-PRs" ask. I tried it for real, and I want to be straight about how far it actually goes.
I carved out the one piece that's genuinely independent — the shiftability harness — as #745 (net-new scripts/shiftcheck/ + a .PHONY Makefile section; ROM-neutral, byte-identical). That one reviews cleanly on its own.
For the rest, a clean thematic split turns out not to be feasible without rebuilding the branch from scratch, and concretely here's why:
- The themes share the same core files. The data-extraction, build/CI, FE6-SIO, AP-decode and symbol-rename work all edit
Makefile,ldscript.txt,build.yml, andREADME.md. Cherry-picking any one theme ontomasterconflicts on those shared files against the others. - They're chronologically interleaved, so there's no point where the commit sequence can be cut into per-theme branches — the only conflict-free ordering is the existing one (which is exactly what this PR already is). Stacked PRs hit the same wall.
- Some "themes" aren't separable at all. "Build the FE6 SIO payload from source" sounds standalone, but it's welded into the no-baserom rework: one commit touches
build.yml,build-verification.yml,.gitmodules,Makefile,quickstart.sh, andREADMEtogether, and it only works because the build no longer needs a baserom (upstream still.incbins that payload straight frombaserom.gba, so it can't ship without that refactor — and a fork PR can't even build it, since upstream's CI needs the baserom secret).
So the realistic options are:
- Review Document all symbols (100%), extract all data, build FE6 SIO payload from source, + calcrom/CI improvements #744 as-is with the commit-by-commit reviewer guide I posted — which I hope matches your "or at least a commit history I could reasonably review commit-by-commit" — plus shiftcheck: add a shiftability QA harness (hardcoded-pointer detection) #745 split off; or
- Re-derive the whole branch from scratch as a clean stack — which would throw away the actual commit history (the very thing that makes it reviewable) and take quite a while.
I'd lean toward (1). And I'm glad to meet you partway: split out any other piece you flag as genuinely independent, reorder/expand the guide however helps, or prioritize whichever theme you want to look at first. For what it's worth, the symbol renames are the part I'd most want a human to sanity-check, and they're the easiest to sample — every rename commit states its basis, so a skeptical pass over 20–30 of them is the highest-signal review here, and I'll fix anything that's wrong.
What would make this most reviewable for you?
🤖 Drafted with Claude Code (model Opus 4.8) on @laqieer's behalf.
CT075
commented
Jun 9, 2026
Let me review the shiftability PR first (it may take some time) before I make a decision. I would also welcome thoughts from the other maintainers.
laqieer
commented
Jun 9, 2026
Sounds good — thank you, and no rush at all. #745 + the guide are the things to look at, and bringing in the other maintainers makes sense. I'll be around to answer questions, sample whatever symbols you want spot-checked, or carve out anything you flag as separable. 🙏
🤖 Drafted with Claude Code (Opus 4.8) on @laqieer's behalf.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
✨ Highlights
📊 Decompilation progress
With the build kept byte-identical (
fireemblem8.gbasha1c25b145e37456171ada4b0d440bf88a19f4d509f, verified bymake comparein CI):data/blobs left)src/asm/)🔓 No baserom required to build or verify
The build no longer depends on the original Fire Emblem 8 ROM. The result is
verified against
checksum.sha1, not a supplied ROM, so:make+make comparewithout providing a copyrighted ROM.BASEROM_URLsecret, and Build CI now runsmake compare(fails on any mismatch).baserom.gbais now optional — onlyasmdiff.shuses it — andquickstart.sh/ the README/docs reflect that.🔀 Shiftability validated end-to-end
Byte-identity does not prove the ROM is shiftable — editable + recompilable
without breaking pointer integrity. A multi-layer harness was built, every
non-shiftable pointer found was fixed byte-identically, and the result was proven
by replaying the full vykan12 TAS (~71 min) on a
+0x40000-shifted ROM to theending — identical at all 41 checkpoints. See the
full shiftability write-up.
This PR brings the accumulated decompilation progress on
laqieer/fireemblem8uback to upstream. It is large (~173 commits) because it is the full fork divergence;
the major themes:
Symbols — now 100% documented
Every address-named placeholder (
sub_XXXX,gUnknown_XXXX,*_<addr>) has beenrenamed to a meaningful name.
src/events/chXX-*.h, defining file).sub_XXXXfunctions reverse-engineered (read body + call sites), then aseparate adversarial pass re-verified all ~1600 names against the code and
corrected 13 (e.g. an inverted alive/dead polarity cluster).
Data — 100% extracted
All raw blobs are pulled out of
data/into typedsrc/data/*.cor organizedassets (TSA tilemaps as
.map.bin/.S, fonts, palettes), with INCBIN/scaninctooling to support sliced incbin. The
data/directory is gone.AP sprite-anim & packed definitions — decoded, no hardcoded offset tables
Object-sprite animation (AP) definitions that were stored as opaque graphics blobs
or hardcoded offset tables are now decoded into readable assembly with
include/ap.incmacros and computed offset tables, namedSpriteAnim_*(no moregUnknown_*). Packed multi-definition blobs (e.g.Obj_WallBreakAnim, the APsub-definitions) were split and named; the
.sfiles are tracked asanim_*.ssothey no longer fall under the compiler-intermediate gitignore.
TSA tilemap audit & preview system
The TSA (tilemap screen arrangement) data was audited end-to-end: a preview
renderer pairs each TSA with its covering tileset+palette and renders a PNG
(preview-only, build-excluded), all previews were vision-QA'd for coherence, and
224 address-named TSA files were renamed to descriptive symbol names. An audit doc
covers all 1378 TSAs (including the bulk banim/op_anim categories), with stable
commit-permalink links to the committed sources.
FE6 SIO multiboot payload — built from source
The last raw blob (
data/fe6sio_payload.bin) is byte-identical toStanHash/mgfembp. It is now built from that
submodule (berry_fix-style sub-build, its own
010110-ThumbPatchagbcc) insteadof a committed blob.
Shiftability — harness + fixes (detailed)
A
make shiftcheckstatic gate (scripts/shiftcheck/) plus a full-game TAS replayprove the ROM survives a layout shift. Two pointer defect classes were found and fixed
(all byte-identical):
gOpinfo_1[](64),Chapter-7
UnitDefinition.redas(9), and battle-animFORCE_SPRITEpointers (17,encoded inside opcode words). The
redasbug actually desynced the TAS at the Ch7prep screen pre-fix.
base,
A + offsetlanding in a separate resource B): 3 cases, found by the newLayer 1b
scan_offsets.py+ a source scan. Fixed by merging uniform animationframes into one resource (
gOpenLimitViewImgLut), referencing the right symbol(
fontgrptalk palette, renamedPal_TalkText), or documenting the few thecompiler's base-register reuse makes non-byte-identical (
worldmap_rm).Full method/case-study write-up:
shiftability validation comment.
calcrom & progress
Counts
.dataand the banim/sound subsystems so data ratios are honest; handles100% completion (it previously aborted at 0 partial/undoc); reports a data-extraction
percentage. Progress auto-uploads to the FE Decomp Portal on every push to
master.Build & CI
make compareruns in Build CI (fails on mismatch); the baserom dependency removaldescribed above;
make shiftcheckstatic gate;clean_fastpreserves slow banimcompression; quickstart automation + docs; status badge.
Note on #735
The banim-asset extraction (#735,
banim-assets-source) has merged upstream andis now part of the base; the themes above build on top of it.
🤖 Generated with Claude Code