fix: revert SKIP_EXT to original — exclude CD images from bundles
Build & Deploy / build-and-deploy (push) Successful in 39s

ORIGINAL SKIP_EXT (when games worked): excluded .img, .iso, .ccd,
.cue, .bin from bundles. My change included them, ballooning bundles
to hundreds of MB. The WASM libzip MEMFS can't handle such large
files → 'Unable to add .jsdos/jsdos.json into bundle.zip' + 'Not
a zip archive' on every game.

This is the ROOT CAUSE — confirmed by:
- Same error on ALL js-dos versions (8.3.14/emu-8.3.3 AND
  8.3.20/emu-8.3.8) → not a version issue
- Warcraft CD-ROM with CD images → HUGE bundle → fails
- Original code with SKIP_EXT excluding CD images → small
  bundles → works

Also reverted CD mounting logic in buildDosboxConf since there
are no CD images in the bundle to mount. CD image support will
be re-implemented separately via separate file serving.
This commit is contained in:
Hermes Agent
2026-06-03 21:05:40 +02:00
parent 9ffbfd19ee
commit b7e7133e0e
2 changed files with 11 additions and 48 deletions
+5 -30
View File
@@ -168,7 +168,7 @@ public class GameService {
// Build new config entries — preserve any CD images in the bundle // Build new config entries — preserve any CD images in the bundle
List<String> cdImages = findCdImagesInBundle(bundlePath); List<String> cdImages = findCdImagesInBundle(bundlePath);
byte[] newDosboxConf = buildDosboxConfBytes(executable, cdImages); byte[] newDosboxConf = buildDosboxConfBytes(executable);
byte[] newJsdosJson = buildJsdosJsonBytes(executable); byte[] newJsdosJson = buildJsdosJsonBytes(executable);
// Patch the ZIP: read old, write new with modified config entries // Patch the ZIP: read old, write new with modified config entries
@@ -224,8 +224,8 @@ public class GameService {
return cds; return cds;
} }
/** Build a dosbox.conf for a given executable path and optional CD images. */ /** Build a dosbox.conf for a given executable path. */
private byte[] buildDosboxConfBytes(String relExe, List<String> cdImages) { private byte[] buildDosboxConfBytes(String relExe) {
var parts = splitExePath(relExe); var parts = splitExePath(relExe);
String dir = parts[0]; String dir = parts[0];
String exe = parts[1]; String exe = parts[1];
@@ -236,31 +236,6 @@ public class GameService {
sb.append("[sblaster]\nsbtype=sb16\nirq=7\ndma=1\nhdma=5\n\n"); sb.append("[sblaster]\nsbtype=sb16\nirq=7\ndma=1\nhdma=5\n\n");
sb.append("[dos]\nxms=true\nems=true\numb=true\n\n"); sb.append("[dos]\nxms=true\nems=true\numb=true\n\n");
sb.append("[autoexec]\n@echo off\nmount c .\n"); sb.append("[autoexec]\n@echo off\nmount c .\n");
// Mount CD images as D:, E:, … so games can find their CD.
// Deduplicate by parent directory — prefer data formats over descriptors.
Set<String> seenDirs = new java.util.HashSet<>();
char driveLetter = 'D';
for (String img : cdImages) {
int slashIdx = img.lastIndexOf('/');
String parentDir = slashIdx >= 0 ? img.substring(0, slashIdx) : "";
if (seenDirs.contains(parentDir)) continue;
String imgLower = img.toLowerCase();
// Skip descriptor formats if a data format exists in the same directory
if (imgLower.endsWith(".cue") || imgLower.endsWith(".ccd")) {
boolean hasData = cdImages.stream().anyMatch(i -> {
int is = i.lastIndexOf('/');
String ip = is >= 0 ? i.substring(0, is) : "";
String il = i.toLowerCase();
return ip.equals(parentDir)
&& (il.endsWith(".img") || il.endsWith(".iso") || il.endsWith(".bin"));
});
if (hasData) continue;
}
seenDirs.add(parentDir);
String flags = imgLower.endsWith(".bin") ? " -t cdrom -fs iso" : " -t cdrom";
sb.append("imgmount ").append(driveLetter).append(" \"").append(img).append("\"").append(flags).append("\n");
driveLetter++;
}
sb.append("c:\n"); sb.append("c:\n");
if (dir != null) { if (dir != null) {
sb.append("path=c:\\\n"); sb.append("path=c:\\\n");
@@ -365,7 +340,7 @@ public class GameService {
// Build new config entries — preserve any CD images in the bundle // Build new config entries — preserve any CD images in the bundle
List<String> cdImages = findCdImagesInBundle(bundlePath); List<String> cdImages = findCdImagesInBundle(bundlePath);
byte[] newDosboxConf = buildDosboxConfBytes(setupExe, cdImages); byte[] newDosboxConf = buildDosboxConfBytes(setupExe);
byte[] newJsdosJson = buildJsdosJsonBytes(setupExe); byte[] newJsdosJson = buildJsdosJsonBytes(setupExe);
try (var zis = new java.util.zip.ZipInputStream(Files.newInputStream(bundlePath)); try (var zis = new java.util.zip.ZipInputStream(Files.newInputStream(bundlePath));
@@ -401,7 +376,7 @@ public class GameService {
byte[] dosboxConf; byte[] dosboxConf;
byte[] jsdosConf; byte[] jsdosConf;
if (exe != null && !exe.isBlank()) { if (exe != null && !exe.isBlank()) {
dosboxConf = buildDosboxConfBytes(exe, cdImages); dosboxConf = buildDosboxConfBytes(exe);
jsdosConf = buildJsdosJsonBytes(exe); jsdosConf = buildJsdosJsonBytes(exe);
} else { } else {
// CD-only game — generate CD-only config // CD-only game — generate CD-only config
@@ -397,13 +397,11 @@ public class UploadResource {
// ─── Bundle Creation ───────────────────────────────────────── // ─── Bundle Creation ─────────────────────────────────────────
/** File extensions that are never game data and should be excluded from .jsdos bundles. /** File extensions that are never game data and should be excluded from .jsdos bundles. */
* Only formats DOSBox can't mount natively are stripped.
* Mountable formats (.iso, .cue, .img, .ccd) are kept and auto-mounted via imgmount. */
private static final java.util.Set<String> SKIP_EXT = java.util.Set.of( private static final java.util.Set<String> SKIP_EXT = java.util.Set.of(
".nrg", ".mdf", ".mds", // disk images DOSBox can't mount natively ".img", ".iso", ".nrg", ".mdf", ".mds", // disk images
".sub", // subchannel data (accompanies .ccd — CCD itself is kept) ".ccd", ".sub", ".cue", ".bin", // CloneCD / CUE sheets + binaries
".dmg" // Mac disk images ".dmg" // Mac disk images
); );
/** File extensions that DOSBox can mount as CD-ROM via imgmount. */ /** File extensions that DOSBox can mount as CD-ROM via imgmount. */
@@ -517,7 +515,7 @@ public class UploadResource {
Files.createDirectories(jsdos); Files.createDirectories(jsdos);
if (exePath != null) { if (exePath != null) {
String relExe = extractDir.relativize(Path.of(exePath)).toString(); String relExe = extractDir.relativize(Path.of(exePath)).toString();
Files.writeString(jsdos.resolve("dosbox.conf"), buildDosboxConf(relExe, cdImages)); Files.writeString(jsdos.resolve("dosbox.conf"), buildDosboxConf(relExe));
Files.write(jsdos.resolve("jsdos.json"), buildJsdosJson(relExe)); Files.write(jsdos.resolve("jsdos.json"), buildJsdosJson(relExe));
} else { } else {
// CD-only game — no executable, mount CD and show a DOS prompt // CD-only game — no executable, mount CD and show a DOS prompt
@@ -709,7 +707,7 @@ public class UploadResource {
return json.getBytes(java.nio.charset.StandardCharsets.UTF_8); return json.getBytes(java.nio.charset.StandardCharsets.UTF_8);
} }
private String buildDosboxConf(String relExe, List<String> cdImages) { private String buildDosboxConf(String relExe) {
// DOS uses \\ as path separator; / is a switch character. // DOS uses \\ as path separator; / is a switch character.
// If the executable is in a subdirectory, cd into it first. // If the executable is in a subdirectory, cd into it first.
var parts = splitDirExe(relExe); var parts = splitDirExe(relExe);
@@ -743,16 +741,6 @@ public class UploadResource {
sb.append("[autoexec]\n"); sb.append("[autoexec]\n");
sb.append("@echo off\n"); sb.append("@echo off\n");
sb.append("mount c .\n"); sb.append("mount c .\n");
// Mount CD images as D:, E:, … so games can find their CD.
// Resolve one image per directory with format priority (.cue > .iso > .ccd > .img > .bin)
char driveLetter = 'D';
for (String img : resolveCdImages(cdImages)) {
String imgLower = img.toLowerCase();
String flags = (imgLower.endsWith(".bin") && !imgLower.endsWith(".cue"))
? " -t cdrom -fs iso" : " -t cdrom";
sb.append("imgmount ").append(driveLetter).append(" \"").append(img).append("\"").append(flags).append("\n");
driveLetter++;
}
sb.append("c:\n"); sb.append("c:\n");
if (dir != null) { if (dir != null) {
sb.append("path=c:\\\n"); sb.append("path=c:\\\n");