diff --git a/build.gradle b/build.gradle index 8300d4a..98b43e3 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,7 @@ tasks.register('verifyCoreVersion') { def submodule = file('vendor/Rex-EMoolator') doLast { - if (usingExternal.get()) { + if (usingExternal) { logger.lifecycle('verifyCoreVersion: pominięte (-PemulatorPath, kopia robocza)') return } diff --git a/gradle.properties b/gradle.properties index 6eb05f5..78dbcb6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Wersja :core, na którą przypięty jest submoduł vendor/Rex-EMoolator. # Zadanie verifyCoreVersion pilnuje, żeby ta wartość zgadzała się z `git describe` # w submodule — inaczej łatwo zbumpować jedno i zapomnieć o drugim. -coreVersion=v0.4.3 +coreVersion=v0.5.1 org.gradle.jvmargs=-Xmx2g org.gradle.parallel=true diff --git a/src/main/java/pl/genschu/rexcatalog/Main.java b/src/main/java/pl/genschu/rexcatalog/Main.java index 28897c0..45c15f0 100644 --- a/src/main/java/pl/genschu/rexcatalog/Main.java +++ b/src/main/java/pl/genschu/rexcatalog/Main.java @@ -101,7 +101,7 @@ public final class Main { try (Database db = Database.open(dataDir); Statement st = db.connection().createStatement(); ResultSet rs = st.executeQuery(""" - SELECT c.display_name, c.container, c.status, c.file_count, + SELECT c.display_name, c.container, c.fs_type, c.status, c.file_count, g.root_path, COALESCE(g.engine_override, g.engine_detected) AS engine, MAX(CASE WHEN d.field = 'edition' THEN d.value END) AS edition, @@ -114,12 +114,13 @@ public final class Main { ORDER BY family, edition, c.display_name, g.root_path """)) { - System.out.printf("%-40s %-10s %-10s %-13s %-22s %s%n", - "KOPIA", "STATUS", "KORZEŃ", "SILNIK", "RODZINA", "WYDANIE"); + System.out.printf("%-40s %-8s %-10s %-10s %-13s %-22s %s%n", + "KOPIA", "FS", "STATUS", "KORZEŃ", "SILNIK", "RODZINA", "WYDANIE"); while (rs.next()) { String root = rs.getString("root_path"); - System.out.printf("%-40s %-10s %-10s %-13s %-22s %s%n", + System.out.printf("%-40s %-8s %-10s %-10s %-13s %-22s %s%n", truncate(rs.getString("display_name"), 40), + nvl(rs.getString("fs_type")), rs.getString("status"), root == null ? "—" : (root.isEmpty() ? "/" : root + "/"), nvl(rs.getString("engine")), diff --git a/src/main/java/pl/genschu/rexcatalog/db/Database.java b/src/main/java/pl/genschu/rexcatalog/db/Database.java index 58cb5d1..c82b177 100644 --- a/src/main/java/pl/genschu/rexcatalog/db/Database.java +++ b/src/main/java/pl/genschu/rexcatalog/db/Database.java @@ -46,6 +46,29 @@ public final class Database implements AutoCloseable { st.executeUpdate(ddl); } } + migrate(); + } + + /** + * Dokłada kolumny, których {@code CREATE TABLE IF NOT EXISTS} nie doda do już + * istniejącej tabeli. Tylko {@code ADD COLUMN} — bez przebudowy tabel, bo na + * {@code copy} wiszą klucze obce z {@code ON DELETE CASCADE}. + */ + private void migrate() throws SQLException { + addColumnIfMissing("copy", "fs_type", "TEXT"); + } + + private void addColumnIfMissing(String table, String column, String type) throws SQLException { + try (Statement st = connection.createStatement()) { + try (java.sql.ResultSet rs = st.executeQuery("PRAGMA table_info(" + table + ")")) { + while (rs.next()) { + if (column.equalsIgnoreCase(rs.getString("name"))) { + return; + } + } + } + st.executeUpdate("ALTER TABLE " + table + " ADD COLUMN " + column + " " + type); + } } @Override @@ -98,6 +121,9 @@ public final class Database implements AutoCloseable { // Kopia to nośnik, nie gra: jedna płyta bywa dwupackiem z dwoma drzewami // gier, więc przypisanie do wydania wisi przy game_root, nie tutaj. // status mówi, czy zawartość w ogóle dało się odczytać. + // container to rodzaj artefaktu (plik .iso / .zip / katalog na dysku), + // fs_type to system plików wykryty przez :core po zawartości — to nie to samo: + // Wojna Trojańska.iso jest plikiem .iso, ale w środku ma UDF, nie ISO9660. """ CREATE TABLE IF NOT EXISTS copy ( id INTEGER PRIMARY KEY, @@ -105,6 +131,7 @@ public final class Database implements AutoCloseable { status TEXT NOT NULL DEFAULT 'ok' CHECK (status IN ('ok', 'installer', 'unreadable')), status_note TEXT, + fs_type TEXT, path TEXT NOT NULL UNIQUE, display_name TEXT, size INTEGER, diff --git a/src/main/java/pl/genschu/rexcatalog/ingest/Ingestor.java b/src/main/java/pl/genschu/rexcatalog/ingest/Ingestor.java index a6dabf9..ef93cb4 100644 --- a/src/main/java/pl/genschu/rexcatalog/ingest/Ingestor.java +++ b/src/main/java/pl/genschu/rexcatalog/ingest/Ingestor.java @@ -1,6 +1,8 @@ package pl.genschu.rexcatalog.ingest; import pl.genschu.bloomooemulator.engine.filesystem.AssetSourceDispatcher; +import pl.genschu.bloomooemulator.engine.filesystem.FileSystemDetector; +import pl.genschu.bloomooemulator.engine.filesystem.FileSystemType; import pl.genschu.bloomooemulator.engine.filesystem.IFileSystem; import pl.genschu.rexcatalog.db.Database; @@ -25,7 +27,8 @@ import java.util.Map; * Indeksuje pojedynczą kopię (ISO / ZIP / rozpakowany katalog) do bazy katalogu. * *
Nic nie rozpakowuje na dysk — czyta przez {@link IFileSystem} z {@code :core},
- * czyli tę samą implementację ISO9660/Joliet, której używa emulator przy uruchamianiu gry.
+ * czyli te same implementacje ISO9660/Joliet i UDF, których używa emulator
+ * przy uruchamianiu gry.
* Kopia, której nie da się odczytać, i tak trafia do bazy — ze statusem i powodem,
* bo „mam ten obraz, ale go nie otwieram" to też fakt wart skatalogowania.
*/
@@ -54,14 +57,16 @@ public final class Ingestor {
Map