From e484551b4ee1f653029243d38f0167e5e0a66a9a Mon Sep 17 00:00:00 2001 From: Christopher Schnick Date: Thu, 23 Nov 2023 12:36:26 +0100 Subject: [PATCH] Fix ck2 compressed save game corruption on write --- canonical_version | 2 +- .../pdxu/io/parser/TextFormatParser.java | 8 ++-- .../pdxu/io/savegame/SavegameStructure.java | 2 +- .../io/savegame/ZipSavegameStructure.java | 43 +++++++++++-------- version | 2 +- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/canonical_version b/canonical_version index 3a37f70b..e02b2e80 100644 --- a/canonical_version +++ b/canonical_version @@ -1 +1 @@ -2.13.7 \ No newline at end of file +2.13.8 \ No newline at end of file diff --git a/pdxu-io/src/main/java/com/crschnick/pdxu/io/parser/TextFormatParser.java b/pdxu-io/src/main/java/com/crschnick/pdxu/io/parser/TextFormatParser.java index 5e9843ec..b092f444 100644 --- a/pdxu-io/src/main/java/com/crschnick/pdxu/io/parser/TextFormatParser.java +++ b/pdxu-io/src/main/java/com/crschnick/pdxu/io/parser/TextFormatParser.java @@ -115,12 +115,12 @@ public final synchronized ArrayNode parse(Path file) throws IOException, ParseEx return parse(file.getFileName().toString(), Files.readAllBytes(file), 0, false); } - public final synchronized ArrayNode parse(String name, Path file, boolean strict) throws IOException, ParseException { - return parse(name, Files.readAllBytes(file), 0, strict); + public final synchronized ArrayNode parse(String displayName, Path file, boolean strict) throws IOException, ParseException { + return parse(displayName, Files.readAllBytes(file), 0, strict); } - public final synchronized ArrayNode parse(String name, byte[] input, int start) throws ParseException { - return parse(name, input, start, false); + public final synchronized ArrayNode parse(String displayName, byte[] input, int start) throws ParseException { + return parse(displayName, input, start, false); } public final synchronized ArrayNode parse(String name, byte[] input, int start, boolean strict) throws ParseException { diff --git a/pdxu-io/src/main/java/com/crschnick/pdxu/io/savegame/SavegameStructure.java b/pdxu-io/src/main/java/com/crschnick/pdxu/io/savegame/SavegameStructure.java index 5ef97124..304a3532 100644 --- a/pdxu-io/src/main/java/com/crschnick/pdxu/io/savegame/SavegameStructure.java +++ b/pdxu-io/src/main/java/com/crschnick/pdxu/io/savegame/SavegameStructure.java @@ -63,7 +63,7 @@ public void writeData(OutputStream out, ArrayNode node) throws IOException { "CK2txt".getBytes(), SavegameType.CK2, Set.of(new ZipSavegameStructure.SavegamePart("meta", "meta"), - new ZipSavegameStructure.SavegamePart("gamestate", "*"))) { + new ZipSavegameStructure.SavegamePart("*", "gamestate"))) { @Override public void writeData(OutputStream out, ArrayNode node) throws IOException { diff --git a/pdxu-io/src/main/java/com/crschnick/pdxu/io/savegame/ZipSavegameStructure.java b/pdxu-io/src/main/java/com/crschnick/pdxu/io/savegame/ZipSavegameStructure.java index a2fa5ef3..97b1314c 100644 --- a/pdxu-io/src/main/java/com/crschnick/pdxu/io/savegame/ZipSavegameStructure.java +++ b/pdxu-io/src/main/java/com/crschnick/pdxu/io/savegame/ZipSavegameStructure.java @@ -8,10 +8,7 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -34,16 +31,16 @@ public static byte[] getFirstHeader(byte[] input, int maxLength) { private final Set parts; private final String[] ignored; - public ZipSavegameStructure(byte[] header,SavegameType type, Set parts, String... ignored) { + public ZipSavegameStructure(byte[] header,SavegameType type, Set parts, String... ignoredFiles) { this.header = header; this.type = type; this.parts = parts; - this.ignored = ignored; + this.ignored = ignoredFiles; } protected SavegameParseResult parseInput(byte[] input, int offset) { var wildcard = parts.stream() - .filter(p -> p.identifier().equals("*")) + .filter(p -> p.fileName().equals("*")) .findAny(); try { @@ -51,15 +48,15 @@ protected SavegameParseResult parseInput(byte[] input, int offset) { Map nodes = new LinkedHashMap<>(); ZipEntry entry; while ((entry = zipIn.getNextEntry()) != null) { - ZipEntry finalEntry = entry; + ZipEntry finalZipEntry = entry; // Skip ignored entries - if (Arrays.stream(ignored).anyMatch(s -> s.equals(finalEntry.getName()))) { + if (Arrays.stream(ignored).anyMatch(s -> s.equals(finalZipEntry.getName()))) { continue; } var part = parts.stream() - .filter(p -> p.identifier().equals(finalEntry.getName())) + .filter(p -> p.fileName().equals(finalZipEntry.getName())) .findAny().or(() -> wildcard); // Ignore unknown entry @@ -72,16 +69,16 @@ protected SavegameParseResult parseInput(byte[] input, int offset) { return new SavegameParseResult.Invalid("File " + part.get().identifier() + " has an invalid header"); } - var node = type.getParser().parse(part.get().name, bytes, header != null ? header.length + 1 : 0); + var node = type.getParser().parse(part.get().identifier(), bytes, header != null ? header.length + 1 : 0); if (node.size() == 0) { - return new SavegameParseResult.Invalid("File " + entry.getName() + " is empty"); + return new SavegameParseResult.Invalid("File " + part.get().identifier() + " is empty"); } - nodes.put(part.get().name(), node); + nodes.put(part.get().identifier(), node); } var missingParts = parts.stream() - .map(SavegamePart::name) + .map(SavegamePart::identifier) .filter(s -> !nodes.containsKey(s)) .toList(); if (missingParts.size() > 0) { @@ -98,15 +95,27 @@ protected SavegameParseResult parseInput(byte[] input, int offset) { @Override public void write(Path out, SavegameContent content) throws IOException { try (var fs = FileSystems.newFileSystem(out, Map.of("create", true))) { + Optional wildcardPart; + try (var list = Files.list(fs.getPath("/"))) { + wildcardPart = list.map(path -> path.getFileName().toString()).filter(p -> content.entrySet().stream().noneMatch(e -> p.equals(e.getKey()))) + .map(s -> new SavegamePart(s, "gamestate")) + .findAny(); + } + for (var e : content.entrySet()) { var usedPart = parts.stream() - .filter(part -> part.name().equals(e.getKey())) + .filter(part -> part.fileName().equals(e.getKey())) .findAny(); + + if (usedPart.isEmpty() && wildcardPart.isPresent() && wildcardPart.get().identifier().equals(e.getKey())) { + usedPart = wildcardPart; + } + if (usedPart.isEmpty()) { continue; } - var path = fs.getPath(usedPart.get().identifier()); + var path = fs.getPath(usedPart.get().fileName()); try (var partOut = Files.newOutputStream(path)) { if (header != null) { partOut.write(header); @@ -128,6 +137,6 @@ public SavegameType getType() { return type; } - public record SavegamePart(String name, String identifier) { + public record SavegamePart(String fileName, String identifier) { } } diff --git a/version b/version index 3a37f70b..708ee156 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.13.7 \ No newline at end of file +2.13.8-SNAPSHOT \ No newline at end of file