# 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/ / 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.