Fix playListNumber to stream-name mapping; add JavaFX ui module

- 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).
This commit is contained in:
2026-09-15 23:04:24 -04:00
parent fd41390642
commit b1896cd71b
4 changed files with 68 additions and 2 deletions
@@ -436,6 +436,8 @@ public final class Main {
VsmxInterpreter interpreter = new VsmxInterpreter();
interpreter.setVsmx(vsmx);
final Playlist playlist = Playlist.parse(iso);
final List<String> playCalls = new java.util.ArrayList<>();
MenuHost menuHost = new MenuHost() {
@Override
@@ -443,7 +445,8 @@ public final class Main {
int audioFlag, int subtitleNumber, int subtitleFlag) {
String call = String.format("play(playList=%d, chapter=%d, video=%d, audio=%d, audioFlag=%d, subtitle=%d, subtitleFlag=%d)",
playListNumber, chapterNumber, videoNumber, audioNumber, audioFlag, subtitleNumber, subtitleFlag);
String streamName = String.format("%05d", playListNumber);
Playlist.Stream stream = playlist.streamForPlayListNumber(playListNumber);
String streamName = stream == null ? String.format("%05d", playListNumber) : stream.name();
try (PlaybackEngine engine = PlaybackEngine.open(iso, streamName, 0)) {
int frames = 0;
PlaybackEngine.VideoFrame frame;
@@ -27,6 +27,16 @@ public final class Playlist {
return streams;
}
/** Returns the stream with the given 1-based playListNumber, or null. */
public Stream streamForPlayListNumber(int playListNumber) {
for (Stream stream : streams) {
if (stream.playListNumber() == playListNumber) {
return stream;
}
}
return null;
}
/** A single MPS stream (playlist item). */
public record Stream(
String name,
+15 -1
View File
@@ -1,5 +1,6 @@
plugins {
`java-library`
application
id("org.openjfx.javafxplugin") version "0.1.0"
}
java {
@@ -7,3 +8,16 @@ java {
languageVersion = JavaLanguageVersion.of(21)
}
}
javafx {
version = "21.0.5"
modules("javafx.controls")
}
application {
mainClass = "dev.quoije.umd.ui.PlayerApp"
}
dependencies {
implementation(project(":core"))
}
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package dev.quoije.umd.ui;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.List;
/** JavaFX application entry point for the UMD player. */
public final class PlayerApp extends Application {
@Override
public void start(Stage stage) {
List<String> params = getParameters().getRaw();
String imagePath = params.isEmpty() ? null : params.get(0);
StackPane root = new StackPane(new Label("useless-umd-player\n" + (imagePath == null ? "(no iso)" : imagePath)));
stage.setTitle("useless-umd-player");
stage.setScene(new Scene(root, 854, 480));
stage.show();
// Temporary smoke test: auto-close after 2 seconds.
new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException ignored) {
}
Platform.exit();
}, "smoke-close").start();
}
public static void main(String[] args) {
launch(args);
}
}