monorepo: one repository for the decoders, the port and the corpus

Merges the Godot port into the reverse-engineering repository, preserving both
histories -- 1019 commits of corpus plus the port's 31, brought in by subtree
merge and then moved into place so git can follow each file across the rename.

The reason is not tidiness. The two-repo split forced the exporter to depend on
the decoders by pinned revision, and that created a whole class of failure that
now disappears: a sha reachable only from a topic branch, orphaned by a
squash-merge, breaking a fresh checkout silently at build time. It also forced a
live read-only mount of one agent's working tree into another's container, which
is why a contract file could move mid-iteration. With a path dependency, a
decoder change and the exporter change it requires land in the same commit or
not at all.

Canary stays separate: it is a fork tracking upstream.

New structure for the long term:

  docs/game/     how the game is NAVIGATED -- menus, modals, prompts, alerts,
                 and in-game flight. Written so nobody rediscovers it. Mostly
                 open questions on purpose; the in-game tutorials are the
                 resource for the flight half.
  docs/port/MODDING.md
                 modding as a constraint on the exporter TODAY, not a later
                 feature: one logical asset in one file (the disc splits nearly
                 everything, and resolving that is the exporter's job), names a
                 person recognises, PNG/OGG/OGV/JSON only, base-and-overrides so
                 re-exporting is always safe, provenance in every file.
  data/base + data/mods
                 generated tree and drop-in overrides, both gitignored
  exchange/      transient inter-agent files, deliberately outside history
  docs/agents/   the team protocol

Both the README and the navigation doc lead with the correction that cost the
most: the oracle is the real game under Xenia Canary. Reborn's renderer is a
hypothesis under test, it has been wrong, and treating it as ground truth
propagated into three documents and both agents before a human caught it.

Scripted modding stays possible without being built: no screen name is hardcoded
in GDScript and there is no native code in port/, which is what Godot Mod Loader
needs to be able to substitute behaviour later.
This commit is contained in:
MechaCat02
2026-08-29 11:34:46 +02:00
parent f44ebced59
commit 9fbb352ef0
45 changed files with 293 additions and 1523 deletions

12
authored/README.md Normal file
View File

@@ -0,0 +1,12 @@
# Authored decisions
Everything here is a decision **we** made, not something the disc said. It is
hand-written, committed, and survives a re-export — unlike `export/`, which is
regenerated wholesale and must never be hand-edited.
Every entry carries a `why`. When the RE agent decodes the real answer, **delete
the entry** and let the exporter emit it; that deletion is the measure of
progress.
See `docs/FORMAT.md` for the schemas and `docs/BLOCKED.md` for which HANDOFF
question each placeholder is standing in for.

55
authored/flow.json Normal file
View File

@@ -0,0 +1,55 @@
{
"format": "sylpheed.flow/1",
"_": [
"The boot sequence. AUTHORED, and it has to be: HANDOFF Q6 closed this with a",
"negative -- the order is in none of the four places it could have been. It is",
"not in config.ini's empty [SYSTEM], not in the movie manifest (which carries",
"assets, not transitions), not in a persistent GamePart field (the requested id",
"lives only as a stack argument in flight), and `GP_ADVERTISE_DEMO` has zero",
"xrefs of any kind. A transition is a call with a name argument, chosen by code.",
"",
"So this file REPRODUCES AN OBSERVATION. The sequence below is what the RE",
"agent watched the game do, not what any file on the disc says it does. Nothing",
"here may be presented as decoded."
],
"boot": [
{
"screen": "publisher_logo",
"why": "The SQUARE ENIX wordmark is the first thing the boot shows -- RE agent, 2026-08-29. Entry 10 of the pair; 13 is its region twin and the port shows one, not both."
},
{
"screen": "developer_logos",
"why": "GAME ARTS / SETA / studio anima, after the publisher wordmark. HANDOFF Q2."
},
{
"video": "ADV",
"why": "HANDOFF Q9, DECODED from the movie manifest: ADVERTISE_MOVIE -> ADV.wmv, and the boot intro and the attract movie are the SAME asset -- there is no separate boot slot. Its POSITION here (after the developer logos, before the title) is measured, not decoded: it is the order the RE agent watched the game boot in.",
"skippable": true,
"skippable_why": "HANDOFF Q9: one (A) press skips a movie -- measured, title reached at 57 s against a 193 s baseline."
},
{
"screen": "title",
"why": "HANDOFF Q2/Q6: the boot reaches the title after the intro movie. The port holds here -- nothing takes the title's place until P5 gives it somewhere to go."
}
],
"dwell": {
"_": [
"DELIBERATELY EMPTY. Each screen's dwell is its own keyframe group -- the",
"publisher wordmark reaches its hold at t=235 (3.92 s) and the developer",
"logos at t=190 (3.17 s), both read from the disc. Holding beyond that would",
"be a number nobody has measured, so the sequencer holds for zero extra time",
"and the pacing is the disc's own.",
"",
"When a capture times the real boot, the extra hold per screen goes here."
]
},
"screens": {
"_": [
"What each button does. NOT FILLED IN -- that is P5. HANDOFF Q4 measured the",
"destination screens and the RE agent later decoded that a transition is a",
"lookup by NAME, giving a candidate vocabulary (TITLE_SCREEN, TITLE_MENU,",
"LOADING, DIFFICULTY, EXTRA_MENU, TUTORIAL_MENU). Those are the right `goto`",
"targets when this is written, marked as the name match they are."
]
}
}

View File

@@ -0,0 +1,94 @@
{
"format": "sylpheed.screen_names/1",
"_": [
"Which GP_TITLE pak entry is which screen. AUTHORED: the disc does not name its",
"builds, so every name here is a decision. The identifications come from",
"HANDOFF Q2 (ui-title-build-map.md), which measured them against framebuffer",
"captures of the running game; the exporter stamps the name into the screen",
"file with name_source: \"authored\" so a reader can tell a recovered name from",
"an invented one.",
"",
"KEYED BY PAK ENTRY INDEX, not by the enumeration ordinal. It used to be the",
"ordinal; widening the enumeration to reach the splash renumbers ordinals, and",
"a name that moves when the enumeration rule changes is not a name. The entry",
"was always described here as the stronger locator -- now it is the only",
"stable one.",
"",
"Delete an entry here the day the RE agent decodes a name field."
],
"archives": {
"dat/GP_TITLE.pak": {
"2": {
"name": "press_start",
"why": "HANDOFF Q2: builds 2/3 are the PRESS (A) BUTTON plate -- a build of its own, composited over the title and faded in a beat later. English of the EN/JP pair. Measured against a live capture."
},
"3": {
"name": "press_start_jp",
"why": "HANDOFF Q2: the Japanese twin of build 2. Out of scope for this milestone; named so it is not mistaken for a screen we need."
},
"4": {
"name": "title",
"why": "HANDOFF Q2: build 4 is the English title art. Measured against a live capture."
},
"5": {
"name": "main_menu",
"why": "HANDOFF Q2: builds 5/8 are the five-button main menu; 5 is English. Measured against a live capture. (An earlier reading called 8 a submenu and was withdrawn -- 8 is the Japanese main menu.)"
},
"6": {
"name": "extras",
"why": "HANDOFF Q2: builds 6/9 are the EXTRAS submenu, the only submenu inside this archive. Measured against a fresh EXTRAS capture."
},
"7": {
"name": "title_jp",
"why": "HANDOFF Q2: the Japanese twin of build 4."
},
"8": {
"name": "main_menu_jp",
"why": "HANDOFF Q2: the Japanese twin of build 5."
},
"9": {
"name": "extras_jp",
"why": "HANDOFF Q2: the Japanese twin of build 6."
},
"10": {
"name": "publisher_logo",
"why": "The SQUARE ENIX PUBLISHER wordmark -- the FIRST thing the boot sequence shows, before the developer logos. Measured by the RE agent 2026-08-29, render grid at docs/re/captures/title-builds/splash-both-halves-rendered.png. Entries 10/13 are region twins distinguished by the trademark glyph; 10 carries the (TM)."
},
"13": {
"name": "publisher_logo_r",
"why": "The region twin of entry 10, carrying (R) where 10 carries (TM). Named so it is not mistaken for a second screen the boot path needs."
},
"11": {
"name": "developer_logos",
"why": "The GAME ARTS / SETA / studio anima logos -- the developer splash, shown after the publisher wordmark. HANDOFF Q2 and the RE agent's 2026-08-29 render grid; draws 7/7 elements."
},
"14": {
"name": "developer_logos_r",
"why": "The region twin of entry 11, as 13 is to 10."
}
}
},
"unnamed": {
"dat/GP_TITLE.pak": "Entries 0/1 and 12/15 are plates never seen running -- not in the boot path, not on any title-side screen, not in the attract loop (HANDOFF Q2). They export under their entry index rather than a name we would be inventing."
},
"also_export": {
"dat/GP_TITLE.pak": {
"10": {
"name": "publisher_logo",
"why": "LOCATED BY ENTRY INDEX, not by a rule. These four bundles declare their sprites directly and have no .rat layout child, so `is_build` cannot see them -- and the RE agent established that NO content rule can: design size fails (every extra composable bundle sampled is 1280x720, the same as every screen) and element count fails (fragments run 2..15 elements in GP_OPTIONS/GP_SAVE_LOAD while these are 3 and 7 -- the ranges overlap). Safe here and not in general: in GP_TITLE the widened set adds exactly these four and all four are real screens, zero fragments."
},
"11": {
"name": "developer_logos",
"why": "As entry 10: located by index because no content rule distinguishes a splash from a fragment. 7 elements, all drawn."
},
"13": {
"name": "publisher_logo_r",
"why": "As entry 10, region twin."
},
"14": {
"name": "developer_logos_r",
"why": "As entry 11, region twin."
}
}
}
}

59
authored/timing.json Normal file
View File

@@ -0,0 +1,59 @@
{
"format": "sylpheed.timing/1",
"keyframe_units_per_second": 60,
"why": [
"HANDOFF Q1. The disc says a keyframe is at `t=30`; it does not say what a",
"`t` is. The unit was MEASURED off the running game, not decoded: a declared",
"15-unit fade lands on round(255*k/15) for all seven of its samples with k",
"stepping 2,4,6,8,10,12,14 on seven consecutive submitted frames -- so 2",
"units per rendered frame -- and the idle title presents at 28.3-28.8 fps,",
"a 30 Hz game, giving 60 units per second. A second line agrees: the",
"transition quad is declared black for 12 units, and a capture measured the",
"pure-black plateau at 0.17-0.23 s, where 12/60 = 0.20 s.",
"",
"Expressed as units-per-second rather than seconds-per-unit so the value is",
"exact rather than a repeating decimal a reader has to recognise.",
"",
"DELETE THIS FILE when a field on the disc is found that states the unit.",
"Nothing here is on the disc."
],
"kind": "measured",
"source": "/reborn docs/port/HANDOFF.md Q1, docs/re/ui-keyframe-time-unit.md",
"ramp": "linear",
"ramp_why": [
"Also HANDOFF Q1, and part of the same measurement: the fade lands on the",
"linear value at every one of the seven sampled frames, so there is no ease."
],
"exit_ramp_seconds": 0.4,
"exit_ramp_why": [
"HANDOFF Q7 + the RE agent's 2026-08-29 answer. MEASURED, not on the disc.",
"",
"Every element of a screen ends on exactly ONE untimed keyframe, so there is",
"exactly one unknown duration per screen -- the ramp INTO that final keyframe.",
"This is that duration. ~0.4 s, which is 24 units at 60 units/s.",
"",
"The alternative readings were tested and refuted. It is not a black quad laid",
"over a frozen screen: under that model a black rect scales every region by the",
"same 1-alpha, so the button-region / background-region brightness RATIO would",
"be constant through the fade. Measured on the RE agent's filmstrip it falls",
"6.495 -> 5.574 -> 3.105 -> 2.125 -> 1.935, a 3.4x monotonic drop. The screen",
"itself plays out: pteff00.prm ramps to opaque black while the button labels,",
"ptmsg, pteff10 and pteff12 all ramp to transparent, and ptframe1/2 hold.",
"",
"REACH, quoted from the RE agent rather than smoothed over: the filmstrip is",
"downsampled and the button region contains some background, so this pins the",
"DIRECTION, not 0.4 s to +/-0.05 s, and it is one transition pair. Treat the",
"number as approximate and the model as established."
],
"exit_ramp_units": 24,
"dwell_seconds": null,
"dwell_why": [
"NOT SET, and not needed. A screen's dwell is its OWN keyframe group: the",
"publisher wordmark reaches its hold at t=235 (3.92 s) and the developer logos",
"at t=190 (3.17 s), both read from the disc. Adding a hold on top of that would",
"be inventing a number nobody measured, so the sequencer holds for zero extra",
"time and the pacing you see is the disc's own.",
"",
"If a capture ever times the real boot, this is where that number goes."
]
}