Parse PLAYLIST.UMD: streams, chapters and timestamps

- 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
This commit is contained in:
2026-09-15 21:03:37 -04:00
parent 797b0507bd
commit b09e23208a
5 changed files with 242 additions and 0 deletions
@@ -3,6 +3,7 @@ package dev.quoije.umd.cli;
import dev.quoije.umd.iso.UmdIsoFile;
import dev.quoije.umd.iso.UmdIsoReader;
import dev.quoije.umd.playlist.Playlist;
import dev.quoije.umd.sfo.Sfo;
import java.io.IOException;
@@ -65,6 +66,8 @@ public final class Main {
printSfo(iso);
System.out.println();
printTree(iso);
System.out.println();
printPlaylist(iso);
}
return 0;
}
@@ -120,6 +123,34 @@ public final class Main {
}
}
private static void printPlaylist(UmdIsoReader iso) {
try {
Playlist playlist = Playlist.parse(iso);
System.out.println("Playlist (" + playlist.streams().size() + " streams):");
for (Playlist.Stream stream : playlist.streams()) {
System.out.printf(" %s %dx%d %s .. %s (%d chapters)%n",
stream.name(), stream.width(), stream.height(),
formatTimestamp(stream.firstTimestamp()),
formatTimestamp(stream.lastTimestamp()),
stream.markers().size());
for (Playlist.Marker marker : stream.markers()) {
System.out.printf(" %s @ %s%n", marker.name(), formatTimestamp(marker.timestamp()));
}
}
} catch (Exception e) {
System.out.println("Playlist: error: " + e.getMessage());
}
}
private static String formatTimestamp(long timestamp) {
long seconds = timestamp / 90000; // 90 kHz MPEG clock
long hundredth = (timestamp - seconds * 90000) * 100 / 90000;
long hours = seconds / 3600;
long minutes = (seconds % 3600) / 60;
long secs = seconds % 60;
return String.format("%02d:%02d:%02d.%02d", hours, minutes, secs, hundredth);
}
private static byte[] readFile(UmdIsoReader iso, String path) throws IOException {
if (!iso.hasFile(path)) {
return null;