forked from tsuki/openroller
Add portable Windows build and update GC runtime
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user