Fix ZIP entry order: write .jsdos/ config first, then game files sorted
Build & Deploy / build-and-deploy (push) Successful in 40s
Build & Deploy / build-and-deploy (push) Successful in 40s
js-dos reads ZIP entries sequentially and needs .jsdos/dosbox.conf and .jsdos/jsdos.json first to configure DOSBox. Java's Files.walk() uses filesystem readdir order which put them at the very end of the ZIP (after the 328MB CloneCD image), causing extraction failures. Now: 1. First pass: walk .jsdos/ directory and write config files first 2. Second pass: walk everything else (excluding .jsdos/) in sorted order 3. All directory ancestors are still added as explicit entries 4. .sorted() ensures deterministic, alphabetical entry order
This commit is contained in:
@@ -290,27 +290,52 @@ public class UploadResource {
|
|||||||
// ZIP it up directly from extractDir (no temp copy)
|
// ZIP it up directly from extractDir (no temp copy)
|
||||||
try (var zos = new java.util.zip.ZipOutputStream(Files.newOutputStream(bundlePath))) {
|
try (var zos = new java.util.zip.ZipOutputStream(Files.newOutputStream(bundlePath))) {
|
||||||
var addedDirs = new java.util.HashSet<String>();
|
var addedDirs = new java.util.HashSet<String>();
|
||||||
Files.walk(extractDir).filter(Files::isRegularFile).forEach(f -> {
|
|
||||||
try {
|
// Helper: write a directory entry and all its ancestors if not yet added
|
||||||
String entryName = extractDir.relativize(f).toString().replace('\\', '/');
|
// (Emscripten FS requires every directory in the chain as explicit entries)
|
||||||
// Ensure ALL ancestor directories have explicit entries
|
java.util.function.Consumer<String> ensureDir = entryName -> {
|
||||||
// (Emscripten FS requires every directory in the chain)
|
|
||||||
int idx = entryName.lastIndexOf('/');
|
int idx = entryName.lastIndexOf('/');
|
||||||
while (idx >= 0) {
|
while (idx >= 0) {
|
||||||
String parent = entryName.substring(0, idx + 1);
|
String parent = entryName.substring(0, idx + 1);
|
||||||
if (addedDirs.add(parent)) {
|
if (addedDirs.add(parent)) {
|
||||||
|
try {
|
||||||
zos.putNextEntry(new java.util.zip.ZipEntry(parent));
|
zos.putNextEntry(new java.util.zip.ZipEntry(parent));
|
||||||
zos.closeEntry();
|
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<Path> writeFile = f -> {
|
||||||
|
try {
|
||||||
|
String entryName = extractDir.relativize(f).toString().replace('\\', '/');
|
||||||
|
ensureDir.accept(entryName);
|
||||||
zos.putNextEntry(new java.util.zip.ZipEntry(entryName));
|
zos.putNextEntry(new java.util.zip.ZipEntry(entryName));
|
||||||
Files.copy(f, zos);
|
Files.copy(f, zos);
|
||||||
zos.closeEntry();
|
zos.closeEntry();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException(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
|
// Clean up .jsdos config files from the extract dir
|
||||||
|
|||||||
Reference in New Issue
Block a user