233 lines
9.4 KiB
Markdown
233 lines
9.4 KiB
Markdown
# Menu reverse notes: game471.exe as source of truth
|
|
|
|
This work deliberately does not derive menu layout or behavior from web
|
|
screenshots. The authoritative inputs are `GC/game_patched.exe` and the
|
|
matching files in `GC/data/2d_boost` and `GC/data/task_cfg`.
|
|
|
|
## Task flow
|
|
|
|
The global state machine at `FUN_00651440` constructs these tasks in order:
|
|
|
|
| state | task | constructor | scheduler id |
|
|
|---|---|---:|---:|
|
|
| `0x10` | `CSelectMusicTask` | `FUN_005aa900` | `0x260` |
|
|
| `0x11` | `CDifficultyTask` | `FUN_005bc750` | `0x261` |
|
|
| `0x12` | `CGameMainTask` | gameplay constructor | — |
|
|
|
|
This proves that selecting a song and selecting its difficulty are separate
|
|
screens. OpenRoller's earlier combined selector was structurally wrong.
|
|
|
|
The main vtables recovered from RTTI are:
|
|
|
|
- `CSelectMusicTask::vftable` at `0x006fbcdc`;
|
|
- `CDifficultyTask::vftable` at `0x006fb898`.
|
|
|
|
Their render callbacks are `FUN_005aca40` and `FUN_005be2d0` respectively.
|
|
|
|
## Scene files chosen by the executable
|
|
|
|
`FUN_00446a10` chooses the language-specific select-music task config. The
|
|
English configs resolve to:
|
|
|
|
```text
|
|
data/task_cfg/selectmusic_eng.cfg
|
|
data/2d_boost/selectmusic2_eng.rvb
|
|
data/2d_boost/selectmusic2_eng.mtx
|
|
|
|
data/task_cfg/difficulty_eng.cfg
|
|
data/2d_boost/selectmode2_eng.rvb
|
|
data/2d_boost/selectmode2_eng.mtx
|
|
```
|
|
|
|
The `.rvb` MOVI header identifies both scenes as `720x1280 @ 60 fps`. Its two
|
|
dimension fields are stored height-first. This ordering is confirmed by the
|
|
root placements recovered from `TIME`: `imc_title=(360,112)`,
|
|
`imc_focus=(341,695)`, and `imc_navi=(238,1136)`. The UI is authored directly
|
|
for the cabinet's portrait display.
|
|
|
|
The RVB top-level child layout is now parsed by `gc::ParseRvbScene`. For the
|
|
English music scene it is:
|
|
|
|
```text
|
|
PREP @ 0x000033 size 0x0056cf (455 action/path bindings)
|
|
REPO @ 0x005702 size 0x0017b0
|
|
DEFN @ 0x006eb2 size 0x03046e
|
|
EXPG @ 0x037320 size 0x000054
|
|
TIME @ 0x037374 size 0x00177a
|
|
```
|
|
|
|
`PREP` exposes the real animation hierarchy, including
|
|
`/imc_focus/imc_fd_jacket_anim`, the four nodes below `/imc_focus/imc_diff`,
|
|
the eight `/imc_sort/imc_sort*` nodes, `tg_decision`, and the focus in/out
|
|
actions. The companion difficulty scene exposes its own `/imc_slmode`
|
|
hierarchy.
|
|
|
|
Use the local probe to inspect all bindings:
|
|
|
|
```sh
|
|
./build/openroller-rvb-probe GC/data/2d_boost/selectmusic2_eng.rvb
|
|
./build/openroller-rvb-probe GC/data/2d_boost/selectmode2_eng.rvb
|
|
```
|
|
|
|
The probe also accepts exact MovieClip states, for example:
|
|
|
|
```sh
|
|
./build/openroller-rvb-probe --state /=jf_slmusic_start \
|
|
--state /imc_focus=jf_focus_start \
|
|
GC/data/2d_boost/selectmusic2_eng.rvb
|
|
```
|
|
|
|
## Recovered RVB/MTX runtime
|
|
|
|
Every animation object is a recursive node with a four-byte tag, total size,
|
|
local-data size, local data, and child nodes. `DEFN` supplies named `MOVC` and
|
|
`SHAP` definitions; `TIME` contains labeled `FRAM` records. Frames are display
|
|
list deltas rather than complete scenes:
|
|
|
|
- `PLC3` creates/replaces a definition at a depth or updates its transform;
|
|
- `RMOV` removes a depth;
|
|
- `TRN2` carries the 2D affine transform;
|
|
- `COLT` carries RGBA multiplication, including authored visibility fades;
|
|
- `ASRC` contains the exported `play();`, `stop();`, and target actions.
|
|
|
|
`gc::BuildRvbSnapshot` accumulates those deltas through a selected label. Its
|
|
snapshot state can either advance a `play()` entry to the following `stop()`
|
|
or request a relative animation frame globally or per MovieClip path. Per-path
|
|
evaluation is required here because the selected transition must advance while
|
|
unrelated child loops remain at their authored steady frames.
|
|
|
|
MTX starts with `MTX\0`; each payload is a DDS whose first dword was replaced
|
|
by the container. Restoring `DDS ` yields a standard DDS. RVB `ImageN` maps to
|
|
MTX texture `N-1`; all 115 music resources and all 210 difficulty resources
|
|
match their declared dimensions.
|
|
|
|
## Coordinates recovered from render code
|
|
|
|
The select-music render callback walks exactly twelve neighboring song slots.
|
|
The associated catalog offsets stored at `0x006e0708` are:
|
|
|
|
```text
|
|
-5 -4 -3 -2 -1 0 0 1 2 3 4 5
|
|
```
|
|
|
|
`FUN_00447170` supplies the stable slot geometry. Its twelve vertical pairs
|
|
are:
|
|
|
|
```text
|
|
175/189 228/242 281/295 334/348 387/401 440/454
|
|
701/715 754/768 807/821 860/874 913/927 966/980
|
|
```
|
|
|
|
The fixed components in `FUN_005aca40` include positions `(39,497)`,
|
|
`(251,467)`, `(262,500)`, `(262,522)`, and `(330,594)`. These are floats read
|
|
directly from the executable's `.rdata`, not measurements from a screenshot.
|
|
|
|
`CDifficultyTask` passes sprite centres to `FUN_005b33a0`, which subtracts half
|
|
of the scaled source rectangle. The selected song fragments therefore resolve
|
|
to these destination rectangles:
|
|
|
|
```text
|
|
jacket source (1,1,196,196) -> (105,167,98,98)
|
|
title source (0,197,374,34) -> (209,178,374,34)
|
|
source source (0,232,374,24) -> (220,214,374,24)
|
|
artist source (198,180,314,16) -> (220,239,314,16)
|
|
```
|
|
|
|
The difficulty callback centers variable-length groups using the executable's
|
|
actual formulas and spacings:
|
|
|
|
- `(7 - count) * 0.5 * 68`, with the row anchored at `426 + 16`;
|
|
- `(9 - count) * 0.5 * 52`, with the row anchored at `433 + 16`.
|
|
|
|
`FUN_00447170` also supplies the transition timing used by the executable. Song
|
|
slot changes interpolate linearly over `0.125` seconds. The list enters/exits
|
|
over `0.375` seconds and shifts by one viewport width using exponent `4` easing.
|
|
The row plates retain their full dimensions; their opacity and the title scale
|
|
use the slot values `0.7`, `0.8`, `0.9`, and `1.0`. The desktop selector now
|
|
combines these executable-owned values with frame-based RVB entry, exit, sort,
|
|
focus, difficulty-change, and decision states. Confirmation is a distinct
|
|
`jf_decision`/`jf_mode_decision` phase before the task exit timeline; collapsing
|
|
both phases is why an immediate screen switch does not resemble the cabinet.
|
|
|
|
## Common and navigator layers
|
|
|
|
The select task is not visually self-contained. Two additional original
|
|
movies are composed with it:
|
|
|
|
```text
|
|
data/2d_boost/common_eng.rvb/.mtx
|
|
data/2d_boost/navigator/navi_001_yume.rvb/.mtx
|
|
```
|
|
|
|
`common_eng` supplies the network/player icons and the three bottom controller
|
|
prompts. The select-music controller uses the `jf_ctrl_3` layout and the
|
|
`jf_ctrl_tx02`, `jf_ctrl_tx05`, and `jf_ctrl_tx08` label states (`Select`,
|
|
`Change song order`, `Confirm`).
|
|
|
|
The navigator is itself a 720x1280 MovieClip scene, not a single positioned
|
|
DDS. Its recovered opening state draws the bottom backing plate at
|
|
`(0,1000)..(720,1232)`, Yume at `(416,830)..(720,1280)`, and a separate mouth
|
|
layer. Rendering only `navigator/001_yume/base.dds` was therefore structurally
|
|
incorrect.
|
|
|
|
## Dynamically linked carousel rows
|
|
|
|
The twelve list rows do not live in the root select-music timeline. The task
|
|
creates twelve instances of each exported linkage symbol below and attaches
|
|
them to `imc_scroll_dds`:
|
|
|
|
```text
|
|
EXPG UNIQUE_71 -> mc_music_link
|
|
EXPG UNIQUE_74 -> mc_index_link
|
|
```
|
|
|
|
`gc::BuildRvbSymbolSnapshot` resolves those EXPG symbols back to their DEFN
|
|
MovieClips. `mc_music_link` contains the 520x34 row plate and its three score
|
|
cells. `FUN_00447170` places it at x=8 and the twelve y positions listed above;
|
|
`FUN_00447620` supplies row opacity (`0,.7,.8,.9,1,0,0,1,.9,.8,.7,0`), not a
|
|
geometric scale. The title atlas fragments do use the same values as scale.
|
|
|
|
`mc_index_link` is the corresponding 356x36 category plate. Entries returned
|
|
by `FUN_005aa7c0` below 50000 select `mc_music_link`; pseudo entries at or above
|
|
50000 select `mc_index_link` and therefore consume a normal carousel slot.
|
|
For the Genre sort, `FUN_005b40f0` loads
|
|
`data/2d_boost/menu/s_j[_eng].dds`. `FUN_005b3fc0` selects one of its 256x32
|
|
rows and `FUN_005aca40` draws it at the index clip position plus `(53,2)`:
|
|
with the static x=88 row position this gives label x=141. The English rows are
|
|
beginner, Anime & Pops, VOCALOID, Touhou, Rhythm Game, Game, Variety, Original.
|
|
|
|
## Sort-tab indirection
|
|
|
|
The integer stored in `DAT_007f3134` is not the left-to-right tab number. The
|
|
eight internal sort kinds map through the executable byte table `34621857`;
|
|
internal kind 0 (Genre and `s_j_eng.dds`) consequently drives visual frame
|
|
`jf_sort3_ini`. The visual tab order remains New, Monthly Theme Music, Genre,
|
|
Difficulty, Score Average, Title, Favorite, At random. Each `imc_sortN` child
|
|
also receives its independent `jf_sortN_on/off` availability frame. The small
|
|
40x18 NEW marker at `(44,181)` is a separate root child present in both
|
|
`jf_sort3_ini` and the stable `jf_sort3` frame; it is not part of `imc_sort1`.
|
|
|
|
## Executable-owned menu background
|
|
|
|
The background is a separate 3D task, not part of any RVB and not a guessed
|
|
flat colour. `FUN_00577fb0` first emits a full-screen four-vertex strip with
|
|
the exact D3D colours:
|
|
|
|
```text
|
|
top: ARGB FF30309B
|
|
bottom: ARGB FFE57386
|
|
```
|
|
|
|
It then draws two locally loaded TUMO resources. The loader table begins at
|
|
the literal `data/model/menu_obj_05.tumo`; its second 0x40-byte entry is
|
|
`data/model/obj_sphere06.tumo`. The latter is the 288-segment wire sphere seen
|
|
behind the list. Its recovered camera is LH, FOV 60 degrees, aspect 720/1280,
|
|
near/far 0.1/1000, looking along +Z. The sphere is translated to z=100, scaled
|
|
by 5, rotated equally about XYZ at `time*0.125`, and given the small authored
|
|
two-frequency vertical drift.
|
|
|
|
Finally `FUN_005b73a0` draws `data/2d_boost/menu/balloon.dds` as a 12x4 grid
|
|
of 64px cells at y=1000. This reconstructs source rectangle `(0,0,768,256)`;
|
|
the last 48 pixels are clipped by the 720px cabinet viewport. This is the
|
|
dark controller backing visible behind the common HUD and navigator.
|