Open reverse-engineering threads
The live list of this project's unsolved mysteries. Every row is a reverse-engineering question about Legaia's runtime that is still contested, with the concrete next step that would close it. If you are looking for a way to contribute, this page is the invitation.
How to use these three pages
Before starting a hunt, look for it here. If the question is not on this page, it is probably already answered or already disproved - and checking is cheaper than re-deriving.
| Page | Holds | Read it when |
|---|---|---|
| This page | Live hunts: open, partial, mostly resolved | You are picking up work, or want to know whether a question is still contested. |
| Settled threads | Answered questions, each carrying an evidence grade | You need the answer to something, or you are about to build on a claim and want to know how firmly it is pinned. |
| Do not re-walk | Falsified hypotheses, reasoning intact | A reading of the bytes looks obvious and you want to check nobody has already spent a week disproving it. |
A falsified row is kept forever with its reasoning intact, because the reasoning is the deliverable - "the world-map slot-4 bodies are coastline wireframes" is a very plausible reading of those bytes, and knowing why it is wrong is worth more than the row it occupies.
Nothing on any of the three pages counts ports, tests or coverage. Each row carries a one-line current answer; byte-level detail lives on the linked page, and the full working notes live in docs/reference/open-rev-eng-threads.md. A thread whose answer is complete moves to the settled register - which is where most of this page's former residents now live.
What an evidence grade means
Grades live on the settled threads page. They are explained here because the grade is the first thing to check when a settled fact is load-bearing for live work.
Every settled row is graded by what its own stated evidence actually rests on. Where a row cites more than one kind, it is graded by the weakest load-bearing claim - the one that breaks the conclusion if it is wrong.
| Grade | The row cites |
|---|---|
| disassembly | Instructions, addresses, opcode encodings, branch or store sequences. The strongest grade. |
| capture | A runtime capture, save state, probe, firehose, or disc-derived oracle. |
| decompiled-C | Ghidra's C output, a FUN_x(...) call signature, a Ghidra label, or a claim about store order / store count / a boolean operator with no instruction behind it. |
| inference | Reasoning from surrounding facts, corpus absence, or analogy, with no direct evidence cited. |
decompiled-C is the re-audit bucket, not a wrong-answer bucket. It marks a claim nobody has confirmed against instructions. Most are probably right; the point is that none has been checked, and every claim falsified in the last audit wave would have graded decompiled-C. The C is a rendering: dropped register arguments, || printed as nested ifs, reordered or omitted stores, and hand-written annotations read as fact have each already put a wrong statement on these pages.
Status conventions, and how a row is laid out
| Status | Meaning |
|---|---|
| open | Active hunt. A concrete next step exists; the row names it. |
| partial | The main result is pinned; a residual sub-question remains. |
| mostly resolved | The mechanism is pinned; one leg is unconfirmed. |
Many rows qualify the status in parentheses - partial (transcode closed), open (narrowed) - naming how far it got. Read the parenthetical.
Each area below opens with a table of one-line rows. A thread whose write-up outgrows a table cell keeps its one-liner in the table and links to a section immediately after that table via details ↓; the full analysis - every address, capture and falsification - lives in that section, under its own Status: line.
Recently corrected
Rows the last audit wave overturned. They are listed here rather than filed silently into the settled page, because a claim that was wrong once is the cheapest place to look for a claim that is still wrong.
- Debug flag
_DAT_8007B8C2had its branch sense backwards, and one arm was named for the wrong loader. Every site reads the flag withlhand takes the zero arm to the debug-station host trap, the non-zero arm to the index resolver. - The VA-aliasing corollary was too narrow. It read as an
0x801Fxxxxproblem;801e23ecis a settled casualty in the0x801Eband, and its aliased reading had silently dropped all three initiative modifier terms. - The op-
0x2F"seven byte-identical dumps" shorthand was compressing. The capture-derived dumps agree with each other; the static 0897 dump is a strict subset of them, not a twin. FUN_8001EBEC's second pose-copy arm loads seven words, so its range ends at+0x15C, not+0x158.- A committed claim can quote a dump statistic the dump no longer reports. Re-extraction makes dumps longer, and a caveat written against the old one keeps suppressing work -
0x8005BA38was recorded "not a function" and is a completeRotTransPers. The class is on do not re-walk § measurement readings. - A battle
0xB5was read in the wrong id space. Spell0xB5is Lapis Wave; formation0xB5is Cort. The wrong reading survived because Cort casts Lapis Wave - the two spaces agreeing on the answer is what hid the error. - Op-
0x35sub-op 9 was recorded as "Queue". It is a start behind an asset-load barrier, and it is what a cutscene changes music with - so a scene-corpus BGM sweep, which only runs prescripts, could not see the difference. - "Retail shots rarely roll the camera" was false, and so was the row that replaced it. Retail authors a non-zero op-
0x45slot-2 roll in eight scenes; the two linear censuses that had measured it were each measuring their own decode gate.
Two rows a prior audit flagged as the highest-risk decompiled-C claims - the narration-roller op's operand decode and the item-add OOB store-order claim - have both now been re-derived from the disassembly and confirmed. Both live on the settled register with the instruction evidence cited.
Field / locomotion
| Thread | Status | Current answer | Detail |
|---|---|---|---|
| Region story-flag gate families (record-header C1/C2 gates) | partial - structure settled; play order for the un-walked dungeons owed | The structure is resolved and settled: every region's C1/C2 gate families - the partition-2 record-header flag lists the spawn evaluator FUN_8003BDE0 checks - are decoded across the chapter-2/3 regions and the Rim Elm variants, each family's exact lists pinned by census anchor tests (see the settled register). What remains is a play-order confirmation for the dungeons the capture corpus never walked. details ↓ |
script-vm |
Region story-flag gate families - the residual
Status: structure resolved and settled; residual = play-order confirmation for the dungeons the capture corpus never walked.
The full structure (Sebucus spokes, Rim Elm opening/revisit/final bands, Uru Mais, Nivora Ravine, Karisto castle depth, Conkram, and the 0x7/0xF variant-discriminator pattern) lives on the settled threads page. The families for the dungeons the capture corpus never walked (taiku/doman/rayman, station, dohaty/retock, the Karisto spokes) are proven as structure, but their in-game play order is not yet confirmed against a live capture. ropeway/ropeway2/jiji are the only spokes walked organically, and Nivora's 0x370 has one live organic SET confirming its play order. The generic C1/C2 seeder already drives every family, so one dungeon-walk capture per region would close the residual.
Battle / rendering
| Thread | Status | Current answer | Detail |
|---|---|---|---|
| The battle-intro enemy-name banner - which placement record raises it | mostly resolved - chrome, seat and frame law captured; only the record identity is owed | The chrome, the seat and the frame arithmetic are captured on the same banner surface mid-fight. What is owed is only which placement record the transient intro instance raises. details ↓ | battle |
The battle-intro enemy-name banner
Status: mostly resolved - the chrome, the seat and the frame arithmetic are captured. What is owed is only which placement record the intro instance raises.
The thread opened as "chrome and seat unknown, blocked on a live frame", on the premise that no manifest state catches the surface. That premise was wrong about the surface rather than about the intro: a corner-sweep of the whole save library finds the battle's full-width top message banner live in two states - the mid-battle Seru "captured!" banner, and the level-up banner.
Both draw the same thing, and it is the frame the thread described: the widget table's class-0 9-slice window, tile-set 0, sub-palette 2 - 4×4 corners and 24×4 / 4×24 edges cut from one 32×32 patch at texels (160, 0), content pen (16, 12), frame origin (8, 4), interior 20 tall, top and bottom edges tiled 24 wide from x = 12 with the last tile clipped. Every placement record whose kind byte is 0x03 / 0x04 / 0x44 frames itself that way - see the widget-class table.
One sub-claim in the old row is falsified by those frames: there is no blue interior. Retail draws the border sprites and the glyph run and nothing else - no fill primitive of any kind under the window, so the scene shows through. The blue-marbled 32×32 patch that made "gold border over a blue interior" a natural reading is the fill the framed menu windows use.
Residual: the intro instance is transient, so which top-seated 0x0303 record it raises - the candidates park at (16, -24) and live at (16, 14), and the runtime overwrites the disc width with the measured enemy name - is still capture-owed. Two operating notes apply to any run: PCSX-Redux probes do not exit on their own, and pgrep -f matches the caller's own command line, so a probe runbook has to kill on a timeout it owns.
Recently closed in this area: the +0x0E kind-pair mapping and the element-badge palette selector both fell out of the widget-class table, and the status-element badge sheet 0x18..=0x20 is pinned cell by cell. Before them, the ground grid's depth-cue far colour and the battle-intro tile shatter's side-face shade page closed by capture. All on the settled register.
Audio / BGM
No open threads. The most recent one - op-0x35 sub-op 0xA, the "unhalt-pause toggle" - resolved as the track-swap commit and moved to the settled register.
Title / boot / overlays
No open threads. The last one - PROT 0968 identity, the one slot-B cluster entry without a residency capture - closed by capture: a PCSX-Redux state taken at the first command menu of the evolved-Cort fight shows the loader-B tracker 0x8007BC4C reading 0x49 and entry 968 100 % byte-resident at 0x801F69D8 over its own 0xA28 extent, with the field-side ladder states bracketing the page-in to the battle load. It is the Cort battle-stage overlay; see the settled register.
When to add a row
A thread belongs here when:
- There is something specific that would close it - a probe to run, a dump to read, a function to port. "Generally understand X better" is not closable; skip.
- The next step is non-obvious from the code or git log. If
grepwould surface it, no row needed. - The detail lives elsewhere (a docs page, a Ghidra dump). The row is the pointer, not the analysis.
When a thread closes, rewrite the row to a resolved / falsified line if the path was instructive enough to warrant a do-not-re-walk marker; otherwise delete the row. Rotating the page is part of using it.