feat: keep and auto-mount CD images via imgmount instead of stripping
Build & Deploy / build-and-deploy (push) Successful in 1m17s

- Removed .iso, .cue, .img, .ccd from SKIP_EXT (they're now kept in bundles)
- Added CD_EXT set and findCdImages() method to detect mountable images
- buildDosboxConf() now generates imgmount D:/E:/… commands for each CD image
- Only .nrg, .mdf, .mds, .sub, .dmg are still stripped (unsupported formats)

Games with CD images will now work out of the box — DOSBox presents
them as CD-ROM drives and the game can find its disc.
This commit is contained in:
Hermes Agent
2026-06-02 16:11:24 +02:00
parent 1362e6034a
commit 7b7cc56bc1
@@ -380,13 +380,37 @@ 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(
".img", ".iso", ".nrg", ".mdf", ".mds", // disk images ".nrg", ".mdf", ".mds", // disk images DOSBox can't mount natively
".ccd", ".sub", ".cue", // CloneCD / CUE sheets ".sub", // subchannel data (accompanies .ccd — CCD itself is kept)
".dmg" // Mac disk images ".dmg" // Mac disk images
); );
/** File extensions that DOSBox can mount as CD-ROM via imgmount. */
private static final java.util.Set<String> CD_EXT = java.util.Set.of(
".iso", ".cue", ".img", ".ccd"
);
/** Find mountable CD image files in the extracted game directory.
* Returns relative paths (with forward slashes) sorted alphabetically. */
private List<String> findCdImages(Path dir) throws IOException {
List<String> cds = new ArrayList<>();
try (var walk = Files.walk(dir)) {
walk.filter(Files::isRegularFile).forEach(f -> {
String name = f.getFileName().toString().toLowerCase();
int dot = name.lastIndexOf('.');
if (dot >= 0 && CD_EXT.contains(name.substring(dot))) {
cds.add(dir.relativize(f).toString().replace('\\', '/'));
}
});
}
cds.sort(String::compareTo);
return cds;
}
/** /**
* Executable filenames (lowercase, stem only, no extension) that should NEVER * Executable filenames (lowercase, stem only, no extension) that should NEVER
* be selected as the main game executable. These are DOS extenders, DPMI hosts, * be selected as the main game executable. These are DOS extenders, DPMI hosts,
@@ -415,7 +439,9 @@ public class UploadResource {
Path jsdos = extractDir.resolve(".jsdos"); Path jsdos = extractDir.resolve(".jsdos");
Files.createDirectories(jsdos); Files.createDirectories(jsdos);
String relExe = extractDir.relativize(Path.of(exePath)).toString(); String relExe = extractDir.relativize(Path.of(exePath)).toString();
Files.writeString(jsdos.resolve("dosbox.conf"), buildDosboxConf(relExe)); // Find CD images for DOSBox imgmount (mountable via D:, E:, …)
List<String> cdImages = findCdImages(extractDir);
Files.writeString(jsdos.resolve("dosbox.conf"), buildDosboxConf(relExe, cdImages));
Files.write(jsdos.resolve("jsdos.json"), buildJsdosJson(relExe)); Files.write(jsdos.resolve("jsdos.json"), buildJsdosJson(relExe));
// ZIP it up directly from extractDir (no temp copy) // ZIP it up directly from extractDir (no temp copy)
@@ -524,8 +550,8 @@ 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) { private String buildDosboxConf(String relExe, List<String> cdImages) {
// 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);
String dir = parts[0]; String dir = parts[0];
@@ -558,6 +584,12 @@ 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
char driveLetter = 'D';
for (String img : cdImages) {
sb.append("imgmount ").append(driveLetter).append(" \"").append(img).append("\" -t cdrom\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");