201 lines
9.8 KiB
Markdown
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.
|