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

212 lines
9.3 KiB
Markdown

# OpenRoller PSP port
The first hardware target is a PSP-1004 (PSP-1000/Fat). The runtime must fit
the original model's memory limits and must not depend on the extra application
memory available on later PSP models.
## Display contract
The arcade game uses a 720x1280 portrait canvas. OpenRoller keeps those logical
coordinates on PSP and rotates the final scene into the physical 480x272 panel:
- scale: 3/8;
- resulting image: 270x480;
- physical border: one pixel on each long side;
- `SELECT`: swap clockwise/counter-clockwise tate orientation;
- `START`: pause/resume once a stage is loaded.
This transform must remain above both the gameplay renderer and the song-select
renderer. Content code should not contain PSP screen coordinates.
Gameplay geometry is submitted to the native PSP Geometry Engine as
`GU_TRANSFORM_3D` vertices. The 720x1280 portrait projection is preserved in
tate mode by swapping its horizontal/vertical projection extents and rolling
the camera basis by 90 degrees. The GU performs perspective division,
near/far clipping, and depth testing; CPU projection is not part of the runtime
renderer except for the original screen-relative directional-note arrow and
lower-right helper overlays.
## Memory rules for PSP-1000
- Use RGB565 double framebuffers and a 16-bit depth buffer. Together they use
0xcc000 bytes of VRAM, leaving about 1.2 MiB of VRAM for resident textures.
- Never load a whole song into RAM. Audio is decoded from a bounded streaming
ring and the decoded sample count is the gameplay clock.
- Keep only the current jacket resident; replace it when selection moves.
- Convert DDS/MTX textures on the host to RGB565, RGBA4444, or indexed T8 data.
- Parse arcade formats on the host. PSP consumes little-endian, aligned binary
arrays and never instantiates the reverse-engineering object graph.
- Generate/copy only the visible rail window and visible notes each frame.
- Background meshes require culling and a fixed resident-memory budget.
## Build
The local machine does not need a permanent PSPDEV installation. The build
script uses the official PSPDEV Docker image:
```sh
tools/build_psp.sh
```
The result is `psp/EBOOT.PBP`. Copy the runtime directory to:
```text
ms0:/PSP/GAME/OpenRoller/
```
When `catalog.orpc` is present next to `EBOOT.PBP`, the runtime opens the
portrait song-select screen and lazily loads the selected jacket, chart and
audio stream. It renders the real track, camera, notes, draw window, four-corner color
background, and the stage-authored `.tumo` objects. Object translation, scale,
rotation, color and visibility channels are evaluated on PSP; one-level parent
composition, wireframe selection, the fixed 250 ms visibility fade and sorted
alpha rendering follow the recovered `game471.exe` path.
Particle repeat/lifetime, spawn shape, direction, velocity and opacity are now
evaluated against the authored timeline and current BPM. The sprite resource
lookup is not ported yet, so particle instances use a small geometric marker.
Visualizer type, color and timeline are also carried by `.orps`; the current GE
renderer maps those types to rings, rays, bars and scanlines, but still uses a
deterministic pulse rather than the original audio analyser/FFT path. TUMO
geometry is position/color only at this stage; its material textures remain the
next background fidelity layer.
Without a catalog, the legacy `stage.orps`/`audio.mp3` pair still works. Without
either format, the executable falls back to the synthetic smoke scene.
The host preparation script applies both per-difficulty volumes from
`stage_param.dat`, pre-mixes the authored BGM and SHOT WAVs with a
latency-compensated limiter, and emits one CBR 128 kbit/s, 44.1 kHz stereo MP3
per chart. The PSP runtime decodes that stream incrementally through one handle
of the native MP3 module and submits it through one SRC channel. This is
deliberately single-stream: simultaneous MP3 handles stutter on PSP-1000 while
the 3D stage is active. Reactive SHOT muting is therefore temporarily disabled
in the PSP build. The number of samples accepted by audio output is the gameplay
clock; rendering framerate does not advance song time. `SE_ARRANGE`, `TAP_SE1` and
`TAP_SE2` are host-converted to small 44.1 kHz PCM effects mixed by the same
thread.
The host-side chart normalizer is built with the regular desktop CMake build:
```sh
cmake -S . -B build
cmake --build build --target openroller-psp-pack
build/openroller-psp-pack GC/data/stage/ac_example_easy.dat stage.orps
```
The version-4 `.orps` file is a little-endian, 16-byte-aligned snapshot containing the
track, notes, camera, draw distance, background colors, pre-triangulated TUMO
vertices, stage-object records, flattened animation keys, particles,
visualizer keys and BPM changes. Each note also carries the effective runtime
type, appearance/end times, directional vector, per-note early/late/miss/mute
windows, colour and effect selector produced by the recovered
`BuildTimingDataSub` path. It deliberately
retains unknown note fields so discoveries made later in the reverse do not
invalidate imported charts. For `ac_10pt8tion_easy.dat` the full package is
about 493 KiB; the 36 source models contribute about 356 KiB of position-only
vertices and are submitted to GU directly from the loaded package.
For a ready-to-copy Memory Stick directory using the default test chart:
```sh
tools/prepare_psp_demo.sh
```
This creates `dist/PSP/GAME/OpenRoller/`. Copy that `OpenRoller` directory to
`ms0:/PSP/GAME/`. Runtime controls are:
For the 30-song library build used by the PSP-1004 port:
```sh
tools/prepare_psp_library.sh
```
This produces 97 difficulty packages for `10pt8ion`, `Oshama Scramble!`,
`Bonetrousle`, `Shadow`, `Planet connection`, `Departure`, `Journey`,
`SPACE ARCADIAN`, `Altale`, `Analysis Division`, `Thrash Beat`, `Agent Angels`,
`FLOWER`, `ADRENA`, and `7 days a week`, plus `Satisfiction` and fourteen
VOCALOID tracks: `MikuMiku`, `The Disappearance of Hatsune Miku`,
`World's End Dancehall`, `Rolling Girl`, `Uraomote Lovers`,
`Unknown Mother-Goose`, `Redial`, `Tell Your World`, `Umiyuri`,
`Karakuri Pierrot`, `Dappo Rock`, `ECHO`, `Vampire`, and
`PaIII.SENSATION`. Audio is CBR 128 kbit/s MP3 and jackets are 128x128
RGBA4444. Only the selected song's resources are resident.
The song selector has a continuously scrolling public-test notice. Its build
identifier is compiled as `tsuki@kagebaito-yyyymmdd-hhmmss` by
`tools/build_psp.sh`; each release build therefore exposes its exact timestamp
on screen.
Song-select controls are:
- D-pad up/down: previous/next song;
- `L` / `R`: move five songs;
- `CIRCLE`: enter difficulty selection, then start the selected chart;
- D-pad in difficulty selection: choose an available mode;
- `CROSS`: back (or exit from the song list);
- `SELECT`: switch tate side.
The physical D-pad is rotated into the portrait coordinate system. In the
default clockwise orientation physical left/up/right/down becomes logical
down/left/up/right; counter-clockwise tate uses the inverse mapping.
Gameplay controls are:
- D-pad or `CROSS` / `CIRCLE` / `TRIANGLE` / `SQUARE`: tap;
- `SELECT`: switch tate side;
- `START`: open the pause menu;
- `L` / `R`: seek backward/forward five seconds;
- `START` + `SELECT`: exit (the PSP `HOME` menu also remains available).
The pause menu contains `CONTINUE`, `RESTART`, and `BACK TO MENU`; `CIRCLE`
confirms and `CROSS` cancels. At chart end the current song is unloaded and the
runtime returns to song select automatically. The Linux runtime performs the
same transition after the authored track duration and reopens its selector.
Every newly pressed D-pad or face-button bit is retained as an independent
input, so simultaneous buttons can judge CRITICAL and start DUAL HOLD.
Releases terminate HOLD/DUAL HOLD, while alternating/repeated presses keep
SCRATCH and BEAT active. The recovered `game471.exe` judge routine uses
25% / 50% / 100% of the full early or late input-window width for GREAT / COOL /
GOOD, with `GreatMinTime = 32` ms as the lower GREAT bound. Windows come from
the chart timing lists or the per-difficulty `system.cfg` override, including
the recovered FLICK extension. Long-note ranks use the original held-percentage
thresholds and short-tail correction. SLIDE HOLD and MERRY GO ROUND rendering
data is packaged, but their final input state machines remain unresolved in
the desktop reverse as well.
Notes fade in/out on authored beat-relative times and are submitted after the
rail without depth testing. HOLD/SCRATCH/BEAT/SLIDE/DUAL bodies and matching end
markers are generated as PSP GE geometry; directional arrows are fixed in
screen orientation using the camera at the note timestamp, and the control
helper stays in the lower-right HUD slot. The original note/effect atlases are
not yet converted for PSP, so these marks remain geometric rather than using
the arcade textures.
## Runtime package
```text
OpenRoller/
EBOOT.PBP
catalog.orpc
songs/
<song-id>/
easy.orps
normal.orps
hard.orps
extra.orps # when present
easy_bgm.mp3
normal_bgm.mp3
...
jacket.orpj
sounds/
adlib.pcm
tap1.pcm
tap2.pcm
```
`catalog.orpc` is a fixed-record index generated from the original
`stage_param.dat`; it binds display title, artist, BPM, duration, genre,
difficulty ratings and availability to a stable song key. The menu uses that
same key to resolve the chart package, per-difficulty mix and jacket paths.