diff --git a/src/main/java/com/dostalgia/UploadResource.java b/src/main/java/com/dostalgia/UploadResource.java index b1d569b..72905f6 100644 --- a/src/main/java/com/dostalgia/UploadResource.java +++ b/src/main/java/com/dostalgia/UploadResource.java @@ -290,27 +290,52 @@ public class UploadResource { // ZIP it up directly from extractDir (no temp copy) try (var zos = new java.util.zip.ZipOutputStream(Files.newOutputStream(bundlePath))) { var addedDirs = new java.util.HashSet(); - Files.walk(extractDir).filter(Files::isRegularFile).forEach(f -> { - try { - String entryName = extractDir.relativize(f).toString().replace('\\', '/'); - // Ensure ALL ancestor directories have explicit entries - // (Emscripten FS requires every directory in the chain) - int idx = entryName.lastIndexOf('/'); - while (idx >= 0) { - String parent = entryName.substring(0, idx + 1); - if (addedDirs.add(parent)) { + + // Helper: write a directory entry and all its ancestors if not yet added + // (Emscripten FS requires every directory in the chain as explicit entries) + java.util.function.Consumer ensureDir = entryName -> { + int idx = entryName.lastIndexOf('/'); + while (idx >= 0) { + String parent = entryName.substring(0, idx + 1); + if (addedDirs.add(parent)) { + try { zos.putNextEntry(new java.util.zip.ZipEntry(parent)); zos.closeEntry(); + } catch (IOException e) { + throw new UncheckedIOException(e); } - idx = entryName.lastIndexOf('/', idx - 1); } + idx = entryName.lastIndexOf('/', idx - 1); + } + }; + + // Helper: write a single file entry with its directory chain + java.util.function.Consumer writeFile = f -> { + try { + String entryName = extractDir.relativize(f).toString().replace('\\', '/'); + ensureDir.accept(entryName); zos.putNextEntry(new java.util.zip.ZipEntry(entryName)); Files.copy(f, zos); zos.closeEntry(); } catch (IOException e) { throw new UncheckedIOException(e); } - }); + }; + + // ── First pass: .jsdos config files (must come first in ZIP) ── + try (var walk = Files.walk(jsdos)) { + walk.filter(Files::isRegularFile) + .sorted() // deterministic order + .forEach(writeFile); + } + + // ── Second pass: all other game files ── + try (var walk = Files.walk(extractDir)) { + walk.filter(Files::isRegularFile) + .filter(f -> !f.startsWith(jsdos)) + .sorted() // deterministic order + .forEach(writeFile); + } } // Clean up .jsdos config files from the extract dir