Files
tsuki 831d96e562 Initial public source release
Split reusable rendering and format support into vectorail-core and vectorail-gc.
2026-08-02 17:05:27 +02:00

5.6 KiB

Groove Coaster Wai Wai Party Switch reverse notes

Status: work in progress. These notes describe the base title 0100EB500D92E000; update and DLC content have not yet been merged into the analysis corpus.

Executable and engine lineage

The program NCA contains an AArch64 NSO (main). Converting it to ELF and importing it into Ghidra exposes a stripped executable with relocations, RTTI, and exception unwind data.

This is the same custom GC engine lineage as the arcade and Android builds, not a Unity rewrite. Direct evidence in main includes:

  • RTTI name 13CTuneGameData;
  • assertion/source paths under D:/project/GC/svn/latest/Program/Main/Src/GCMain/Tune/Functions/;
  • TuneGameData.cpp, TuneGameManager.cpp, and TuneGameManager_Draw.cpp source names;
  • the assertion label LoadStageData;
  • stage paths stage/data_gz/%s.dat.gz, %s_ext.dat.gz, and %s_clip.dat.gz.

Initial function mapping in the base Switch executable (Ghidra image addresses):

001456a0  CTuneGameData constructor
001457f0  CTuneGameData destructor
00145f60  CTuneGameData deleting destructor
0014a3d0  CTuneGameData::GetWayPosition
0014a560  CTuneGameData::GetCameraData
00134af0  asynchronous stage .dat/.ext/.clip loader
00155b50  TuneGameManager::LoadStageData owner/assert site

The names after the destructors are cross-matched against the symbol-bearing Android ARM64 build and then checked from their decompiled behavior.

RomFS inventory

The base RomFS contains 8,976 files (about 1.6 GiB). Important families are:

3421  .tumo       scene models
2136  .gz         compressed stage data
1790  .opus       audio, normally named *.wav.opus
1052  .bntx       Switch textures
 104  .tusc
  88  .bnvib
  85  .dat        boot/catalog tables
  69  .efcb2
  65  .uvb
  57  .rvb
  57  .mtx
  19  .bnsh       shaders

Gameplay audio names explicitly distinguish tap, slide-hold, scratch, critical, beat, and adlib hit effects. This makes the Switch assets useful for validating the note-type-to-sound mapping even where the arcade catalog uses numeric IDs.

Stage data compatibility

Each chart still consists of the familiar three files, now gzip-compressed:

stage/data_gz/<chart>.dat.gz
stage/data_gz/<chart>_ext.dat.gz
stage/data_gz/<chart>_clip.dat.gz

After gzip decompression the normal .dat is accepted directly by OpenRoller's arcade StageDat and StagePattern parsers. For example, sw_adr_hard_1.dat decodes as:

track points:       240
notes:              529
camera keys:         67
draw-distance keys:  21
background models:    8
background objects: 199

The parser also recovers all expected note types, authored camera modes, particle/visualizer keys, colors, and object animation. This is strong structural confirmation that the arcade field interpretations are not merely heuristics.

The Switch _clip.dat can be much larger than the main chart because it contains baked per-frame object visibility/animation data, just like the arcade clip stream.

All 712 main chart files in the extracted base-game RomFS parse successfully with the current OpenRoller stage parser. A chart can be staged and launched directly by ID:

tools/run_switch_stage.sh /path/to/decoded/romfs sw_adr_hard_1

List the available chart IDs with:

tools/run_switch_stage.sh /path/to/decoded/romfs --list

The launcher decompresses the chart, extension, and clip streams into build/nsw_runtime, links the common model directory, converts the matching Nintendo Switch OPUS stream with vgmstream-cli, and starts the Linux player. Set VGMSTREAM_CLI=/path/to/vgmstream-cli if it is not on PATH or in the repository build directory.

This currently provides chart, camera, note, clip, model geometry, and BGM loading. The .bntx texture/material pipeline and the newer Switch song catalog are not implemented yet, so a successfully running stage is not yet a pixel-identical Switch presentation and there is no Switch-native song menu.

The BGM resolver finds a shipped audio stream for 711 of the 712 chart files, including charts whose internal authoring-time BGM name differs from the release filename. The sole exception is sw_shoukon_hard_1: it is absent from the base stage_param.dat catalog and the base RomFS contains no matching BGM, so this orphan chart can only be launched silently from that data set.

boot/stage_param.dat is a newer catalog revision. The current arcade catalog parser does not consume it completely and must not be treated as compatible until its extra fields are mapped from the Switch loader.

Camera cross-check

The Switch CTuneGameData::GetCameraData stores key timestamps in a separate array and uses a 0x44-byte runtime camera payload rather than Android's interleaved 0x48-byte record. Despite that storage change, its behavior is the same:

  • the active camera key is selected by timestamp;
  • fMode 1 evaluates both endpoint cameras, rebuilds endpoint up vectors, then interpolates the complete camera states;
  • fMode 2 interpolates raw key fields before evaluation;
  • aMode has the same seven branches 0..6;
  • projection type and projection blend are separate results;
  • the mode-2 hold branch samples the state one millisecond before the key.

This independently validates the camera evaluator currently documented in re_gc_camera.md and implemented in the Linux player.

The projection/FOV policy remains platform-specific. The Android build calls SwitchAspectValue(60.0, 68.5, 75.0) and selects the value for its 3:2, 16:9, or iPhone-X layout. The arcade executable instead establishes a normal 75-degree gameplay FOV. Therefore Android's aspect presets must not replace the arcade target behavior in the Linux player.