The RcoRenderer applied translate/scale to the shared GraphicsContext on
every render, so repeated repaints (menu animations fire many) compounded
the scale and pushed the buttons off-screen. Reset the transform each
render. Also decompress zlib/RLZ RCO images (previously returned raw).
Add the 11 RCO animation record types (MoveTo/Recolour/Resize/Fade/
FireEvent/...), an AnimFactory, and populate the resource animtable. Anim
play() applies the final state immediately so the menu lays out correctly.
Also add setInterval overloads (5+ params), setFocus(VsmxBaseObject), and
animation offsets (animX/Y/Z) to position objects, fixing the dispatch
warnings that were silently stalling the menu.
The master clock was anchored once to the first stream's audio PTS, but
every stream starts its own PTS timeline (~90000). Subsequent streams were
paced against the stale anchor, so their targets were all in the past and
the video free-ran to catch up (the reported speed-up / poor fps). Reset
the clock anchor when each stream starts.
The menu script passes intervals in milliseconds, but the Timer treated
them as seconds, so wait_then_call(1100, open_mm) waited 1100 seconds and
the menu never opened. setInterval also only fired once instead of
repeating, so the button 'arrive' animation never advanced. Both are fixed,
with a single scheduler thread serializing callbacks.
RCO menu objects are authored in the PSP movie player's 854x480 FWVGA
framebuffer, not the 480x272 LCD. The renderer was laying everything out in
the wrong coordinate space, which is why no menu items were visible.
PlaybackEngine now runs one daemon decode thread that routes packets into
bounded queues, while the video/audio presenter threads only consume and
pace. This removes the demuxer contention that caused stutter, bursts, and
the speed-up/catch-up behavior.
Press S (or media next) during playback to skip the current playlist and
advance the menu's intro chain quickly.
Video and audio threads both pull from the same engine, racing on the
MPS demuxer file pointer and feeding corrupted H.264 data to FFmpeg (which
segfaults). Synchronize the pull methods and wait for the audio thread
before closing the engine.
RCO object positions are screen-center with +Y up, but the renderer drew
them as top-left, pushing menu items off-screen. Convert to top-left in
the 480x272 design space before drawing.
Also use precise parkNanos pacing and reuse per-frame ARGB/WritableImage
buffers to remove the 1.4MB allocations per video frame.
Pausing stops the audio line and blocks both playback threads; the A/V
clock is re-anchored on resume so video and audio continue exactly where
they left off without fast-forwarding.
The PSP logical screen stays 480x272 for the RCO menu and VSMX scripts,
but the initial window now opens at 2x so it is visible on high-DPI
screens. Users can still resize freely.
Video now scales to fill the window while keeping its 16:9 display ratio
in both windowed and fullscreen modes. The RCO menu is scaled to fill the
window at its native 480x272 aspect ratio.
The audio thread writes decoded PCM to a SourceDataLine and acts as the
master A/V clock; video frames are now paced against that shared clock so
audio and video stay in sync.
Windowed mode still stretches the anamorphic frame to fill 480x272 like
the PSP. In fullscreen the video scales to fit the screen while keeping
its 16:9 display ratio, letterboxing as needed.
Window, menu canvas and video surface are now 480x272 (the PSP screen
size). Video is stretched to fill the screen like the PSP player, rather
than aspect-preserving letterboxing.
- Rejoin whitespace-split program args so ISO paths with spaces work
(Gradle --args splits on spaces, which caused a silent load failure
and a blank white window).
- Black scene background and black video canvas fill; scale video to
fit and center it instead of drawing top-left at native size.
- Playback completion now invokes MoviePlayer.onPlayListEnd so the menu
advances past the anti-piracy warning instead of freezing.
- RcoRenderer implements Display: draws the live RCO object tree onto a
Canvas, decodes PNG textures, handles focus and resource changes
(thread-safe via Platform.runLater).
- PageObject now implements root/children semantics and open/close/display
so opening a page adds its objects to the renderer.
- Object getImage() returns the ImageObject so the UI can decode PNGs.
- PlayerApp wires menu + video: loads the RCO, runs the VSMX script,
executes onAutoPlay, plays video via PlaybackEngine with YUV->ARGB
blitting and PTS pacing, and routes arrow/Enter keys to the controller.
- Runs end-to-end on Batman Begins (window, menu load, playback).
- VideoDecoder.Frame and PlaybackEngine.VideoFrame now carry U and V
chroma planes plus their strides, in addition to luma.
- YuvConverter converts YUVJ420P frames to packed 0xAARRGGBB pixels for
the renderer, with unit tests.
- Playlist.streamForPlayListNumber maps the 1-based playListNumber to the
actual stream entry; stream names are not sequential across discs
(The Dark Knight: 00001/00003/00004; The Day After Tomorrow has
duplicate 00002 names).
- CLI menu playback now resolves the correct .MPS file for every region.
- ui module: JavaFX via org.openjfx.javafxplugin + PlayerApp smoke test
(window verified on this display).
- Validated menus + playback on 4 discs: Batman Begins (USA), Jackass
(Australia), The Dark Knight (Europe), The Day After Tomorrow (Japan).
RCO IMG entries hold PNG images (magic 89 50 4E 47), decodable directly
by ImageIO/JavaFX. RcoFile now reads pImgData and stores raw image
bytes in ImageObject; the CLI reports the first image's magic.
- Type system: BaseType (+Int/Float/Unknown) and BaseReferenceType
(+Event/Image/Text/Ref/Object/Model) for typed record fields.
- Object model: BaseObject/BasePositionObject and all 25 object classes
(Page, Plane, Button, UButton, Text, Model, Icon, Group, Action,
XMenu/XMList/XList/XItem, MList/MItem, LList/LItem, IList/IItem,
Progress, Scroll, Spin, ItemSpin, Edit, Clock) with reflection-based
field deserialization.
- ObjectFactory + RcoContext materialize 89 objects and 81 images from
the RCO; RcoFile resolves references and wires entry.obj/vsmxObject.
- Resource builds the live pagetable/animtable/soundtable/texturetable
into the VSMX tree; Controller tracks input focus.
- Validated on Batman Begins: onAutoPlay still drives playback
(stream 00003) with the object tree populated.
The CLI 'rco' command now runs the full pipeline headlessly: parse RCO,
execute the VSMX script, invoke onAutoPlay, and decode the selected
stream via PlaybackEngine. Batman Begins autoplays playlist 3 (the
anti-piracy warning) and decodes 23 frames from stream 00003.
- NativeFunctionFactory resolves script method calls to native Java
methods via reflection; VsmxNativeFunction wraps them for the VM.
- Native objects: Controller, MoviePlayer, Resource, Math, Timer,
GlobalVariables, decoupled from the UI via a MenuHost interface.
- BaseNativeObject.callCallback invokes script callbacks like onAutoPlay.
- CLI 'rco' now wires the full menu and executes onAutoPlay headlessly.
- Validated against Batman Begins: onAutoPlay computes stream IDs and
issues play(playList=3, chapter=1, video=0xE000, audio=0xBD00,
audioFlag=1, subtitle=-1, subtitleFlag=0) — the anti-piracy warning
clip with English video/audio.
- VsmxInterpreter: stack machine executing all 74 VSMX opcodes
(arithmetic, comparisons, bitwise, jumps, calls, arrays, objects).
- Object model: VsmxBaseObject/Undefined/Null/Boolean/Number/String/
Object/Array/Function/Reference/LocalVarReference/Method/CallState/
NativeObject, plus BaseNativeObject for future native objects.
- CLI 'rco' now runs the menu script headlessly and prints the 172
resulting global variables (state, chapter/language config, focus).
- Validated against Batman Begins: script runs with zero unimplemented-
opcode warnings and defines all menu functions (on_cancel, mm_on_push,
trans_to_play, play_movie_beginning, ...).
- Lzr: RLZ decompressor ported from JPCSP (for compressed RCO data).
- RcoFile: parses RCO header, entry tree (labels, VSMX blob, image/
sound/font/object/animation tables, text), and event name table.
- RcoEntrySizes: fixed binary sizes of the 26 object and 11 animation
record types, needed to locate entries with entrySize=0.
- Vsmx: parses VSMX bytecode (code groups + string tables) and
disassembles to human-readable opcodes; VsmxCode holds the opcode
table.
- CLI 'rco' command dumps events, entry tree, and VSMX disassembly.
- Validated against Batman Begins EN/FR menus: full scene graph
(pages/planes/buttons/groups) and 5585-bytecode scripts decoded.
- AnnexBSplitter incrementally splits the Annex-B stream into access
units, holding partial slices across packet feeds.
- VideoDecoder exposes feedAccessUnit and the decoded frame rate.
- PlaybackEngine demuxes and decodes in lockstep, tagging video frames
and audio chunks with 90 kHz PTS (audio from PES headers, video from
PES anchors interpolated by decoder frame rate).
- CLI 'play' command runs the engine headlessly and reports A/V drift.
- Validated against Batman Begins: 95 frames in 4.0s, max drift -78.6 ms
(video trails audio at stream start), avg frame duration 3833.6 ticks.
- Tests for AnnexBSplitter.
- MpsDemuxer parses MPEG-PS packs (0x1BA) into video (0x1E0-0x1EF) and
audio (0x1BD) PES packets with PTS/DTS at the 90kHz clock.
- VideoDecoder decodes Annex-B H.264 via JavaCPP FFmpeg 8.1.2, splitting
the stream into access units (one slice NAL per packet) so each send
yields a frame.
- CLI 'decode' command dumps frame metadata (720x480 YUVJ420P).
- Validated against Batman Begins: 72 frames decoded.
- Parse PS packets, PES headers and PTS/DTS timestamps
- 'mps' CLI command samples packets from a stream
- Validated on Batman Begins 00001.MPS (Annex-B H.264 + 2-channel ATRAC3+)
- Add little-endian readers to UmdIsoFile
- Playlist parser (streams + chapter markers) ported from JPCSP
- dump command now lists streams, durations and chapter markers
- Unit test for chapter/timestamp mapping