# 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.