Files
openroller/docs/re_gc_android_runtime.md

201 lines
9.8 KiB
Markdown

# Groove Coaster Android Runtime Reverse
This is a clean-room interoperability record for the offline Android package
`gc2offlinev4.xapk`. It documents facts used to validate the GC stage loader
and Vectorail runtime. Addresses below are ELF virtual addresses in
`lib/arm64-v8a/libtune.so`; the Ghidra project uses an additional `0x100000`
image-base offset.
## Provenance
```text
XAPK c6c394f7a1cc65331edc98aac94014668f9d5277ce17262fecf9aeeb1a2a3003
APK 62c9e739dc9b8c1bcbcb4b5234d78f154b20d1a930329b9c21a3c9236b92a0dc
libtune 689b2e4c0bc4479a3f309944b5070796878c66c0dfdbd283c11ec0bbeeea9efa
```
Stage and audio ZIPs use the password returned by
`mtxc::ObbFile::getZipPassword`: `eiprblFFv69R83J5`.
The Android `ac_10pt8tion_{easy,normal,hard}{,_ext}.dat` payloads are binary
identical to the corresponding files in `GC/data/stage`. For example both
copies of `ac_10pt8tion_easy.dat` have SHA-256
`86a75c86b91bbbe25cae78bcb51d4f7842b3fc4946e4b091f2ec5ba084497765`.
The Android executable is therefore a valid independent specification for the
arcade files consumed by Vectorail.
## Stage Selection And Containers
`TuneAppMain::LoadStageData` (`0x9da24`) loads six chart ids per song. The
first three are serialized mobile ids. The other three are synthesized by
prefixing those ids with `ac_`. `GameScene::makeFilenameStageDat` (`0xc4bac`)
then appends `.dat` or `_ext.dat`.
The sampled offline package contains one-note `placeholder_bgm` mobile charts,
while its `ac_` charts contain the complete arcade route, camera, notes and
background scene. `GameScene::LoadStageData` (`0xc607c`) performs this order:
1. Load the selected main DAT from `<stage-pack>.zip`.
2. Call `TuneGameData::LoadGameData` (`0x933a0`).
3. Call `TuneGameData::LoadBGData` (`0x93aa4`) on the same bytes.
4. Load `_ext.dat` only for an extra/arcade difficulty when arrange mode is off.
5. Build runtime data and load stage resources.
For every selected `ac_` chart, `LoadExtData` starts at byte 6 of the matching
`_ext.dat`, replaces the four timing lists, reads another array of 99-byte
notes, and links each ext note to a same-time main note. If both effective
types are FLICK, the main runtime type becomes `0x10` (dual flick).
The corpus contains 2927 valid sidecars with 18965 ext notes. Every sidecar
parses with the layout described by `docs/stage_ext.pat`, and all ext note
records are FLICK entries. One unusual main chart is itself named
`SW_marianne_hard_ext.dat`, so its sidecar is
`SW_marianne_hard_ext_ext.dat`. File discovery must check whether removing the
suffix names an existing main chart instead of excluding every `_ext.dat`.
## Parsed Stage Sections
The parser now follows every section consumed by Android `LoadGameData` and
`LoadBGData`:
| Header | Runtime data |
| ---: | --- |
| 0 | stage config, BPM and four timing tables |
| 1 | route draw-distance keys |
| 2 | route points |
| 3 | 99-byte note records |
| 4 | 59-byte camera records |
| 5 | 44-byte `TuneBGEffectData` / FlowItem keys |
| 6 | 12-byte visualizer keys |
| 7 | background texture names and 20-byte image keys |
| 8 | background color keys |
| 9 | model/shader names and animated stage objects |
After the variable-length object stream, Android records the current read
position and treats it as the base of four relative extension offsets. The
third extension contains signed object parent indices. Header word 11 points
inside this table on current charts; it is not the object-stream end. The
parser now derives the base from the consumed object stream exactly as Android
does, which also fixes the small mobile DAT revision.
## Timing, Route And Long Elements
`TuneTimingData::GetTime` selects the latest timing key at or before the note.
Mode 1 is absolute milliseconds, mode 3 is next-note spacing, and other modes
multiply the authored value by `60000 / BPM`.
`TuneGameData::GetWayPosition` (`0x953bc`) linearly interpolates route points
by timestamp and clamps before/after the route. `WaySplitCheck` (`0x95c4c`)
clips the polyline to the requested time range, emits samples at a fixed world
distance, carries the unused distance across authored segments, and stores the
true interpolated timestamp for each sample.
Android spacing is `0.20` for HOLD/SLIDE/DUAL, `0.15` for SCRATCH and `0.40`
for BEAT. Arcade `game471.exe` uses `0.55` for BEAT; Vectorail intentionally
keeps the arcade value when playing arcade DATs.
`LoadTuneMarkDataOne` (`0x967d8`) confirms the wire-field translation and
compatibility remaps. Runtime types 7/8 become 1, 11 becomes 10, 12/14 become
9, and 13 becomes 4. Marker effects are forced to 35 for type 10, 32 for type
9, 37 for type 15 and 11 for raw type 13. Duration types are 3, 4, 5, 10 and
15. MERRY type 6 expands to `count` targets separated by the authored beat
spacing.
## Camera
`TuneGameData::GetCameraData` (`0x95588`) independently confirms the seven
anchor modes, the two interpolation paths and the orthographic/perspective
blend documented in `re_gc_camera.md`. Android uses right-handed OpenGL
look-at/projection matrices. The arcade executable uses the corresponding D3D
left-handed path; Vectorail converts the evaluated eye/target/up state to its
OpenGL renderer and uses the arcade fixed 75 degree FOV.
No camera wire fields are discarded, including non-finite values in the three
`ac_comet_*` intros.
## Background Runtime
`DrawBGColor` (`0xb3fc8`) selects the active color key, optionally cross-fades
to the next key, and applies BPM-derived HSV brightness modulation when its
rhythm-reactive flag is set. `DrawBGImage` (`0xb49b0`) supports tiled atlas
images, a built-in centered image and external texture entries.
`DrawBGVisualizer` (`0xb6118`) selects timed visualizer keys and calls the
8572-byte procedural `DrawVisualizer` path. The conventional FFT routine at
`0x104ec0` is not used to drive this stage visualizer.
The 44-byte table previously called particles is `TuneBGEffectData`.
`GameScene::ExecFlowItem` (`0xc0610`) uses it to spawn and update authored
flowing background elements. Shape 1 spawns a random screen point, shape 2 a
screen-space grid and shape 3 an eight-point ring. Repeat and lifetime are
scaled by the active beat before points are unprojected to route depth. It is
separate from hit-effect particles.
## Audio
Stage audio is stored as encrypted `*_bgm.ogg.zip` and `*_shot.ogg.zip` files.
`MtxSoundBuffer::LoadData` (`0x10a614`) rewrites `.m4a` resource names to
`.ogg`. The sampled Android 10pt8tion pair is Vorbis, 44.1 kHz stereo, and both
streams are 129.621859 seconds. The corresponding arcade BGM WAV is PCM s16le,
44.1 kHz stereo, with the same duration.
`GameScene::ExecGameStage` (`0xaf8ac`) maintains a 60 Hz logical count, reads
`MtxSoundSource::GetPlaySecTime` (`0x10af3c`) from BGM, and replaces the logical
count when they differ by more than two frames. BGM and SHOT are prepared and
started together. If either source is no longer playing, both are stopped,
repositioned and restarted; SHOT is also periodically aligned to the BGM time.
Vectorail now derives gameplay time from consumed BGM source bytes instead of
wall time. Its two SDL streams start together, retain their source PCM and are
cleared/requeued at the BGM position when their source clocks differ by more
than two frames. Corpus verification found 114 of 1674 unique arcade pairs
with different PCM frame counts; exhausted SHOT tails remain silent when the
BGM position is already beyond the shorter stream.
## Verification Status
`opencoaster-stage-verify` currently validates all 2970 main DATs recursively,
including the nested `stage/sound/ac_dontfight_ex.dat` and the chart whose
actual name ends in `_ext`. It
compares the retained wire model to the runtime route, all camera fields, note
type/effect/color/timing/distance translation and clip dimensions. It also
validates resolved BGM/SHOT containers and PCM formats.
The final aggregate result is `2970/2970` main charts, 2927 loaded sidecars,
18965 ext notes and 18948 linked dual-flicks. Section coverage is 16869 FlowItem
keys, 54152 visualizer keys, 79 background image keys, 417451 background color
keys and 1171999 stage objects. Audio resolution succeeds for 2918 charts;
2768 have paired BGM/SHOT and 150 intentionally resolve only BGM. Gameplay
coverage includes 193 MERRY records expanded to 676 timed targets and 6364
SLIDE HOLD records.
Implemented in the player:
- route timing and draw windows;
- camera modes, interpolation and projection;
- note heads, directional overlays, long paths, BEAT samples and MERRY layout;
- ext timing tables and dual-flick pairing, including both direction vectors;
- stage model transforms, visibility, colors and one-level parents;
- exact background color cross-fade and BPM brightness;
- timed background image selection and external DDS loading;
- timed visualizer execution for types 1 through 7;
- FlowItem spawning, screen layouts, route-depth unprojection and motion;
- MERRY target timing and desktop SLIDE HOLD duration judging;
- BGM/SHOT playback, hit SE, source-position game clock and pair resync.
Remaining asset/input fidelity gaps:
- the seven visualizer types execute as GLSL procedural equivalents; the
original Android vertex generators have not been copied constant-for-constant;
- FlowItem timing/layout/motion executes, but original texture selectors
34/35 are represented by colored billboards;
- mode-3 background loading is implemented, but `stage_back10` is referenced
by the corpus and absent from the supplied game dump; built-in modes 1/2
still need their original atlas resources;
- dual flick and directional SLIDE HOLD use desktop multi-input/hold semantics
rather than Android touch lines;
- Android touch-line gesture semantics (the desktop player maps controls to
keyboard/gamepad inputs).
This distinction is intentional: a green parser/runtime translation result
does not claim pixel-identical procedural meshes or unavailable textures.