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 ─────────────────────────────────────────
/** 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(
".img", ".iso", ".nrg", ".mdf", ".mds", // disk images
".ccd", ".sub", ".cue", // CloneCD / CUE sheets
".nrg", ".mdf", ".mds", // disk images DOSBox can't mount natively
".sub", // subchannel data (accompanies .ccd — CCD itself is kept)
".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
* 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");
Files.createDirectories(jsdos);
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));
// 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);
}
private String buildDosboxConf(String relExe) {
// DOS uses \ as path separator; / is a switch character.
private String buildDosboxConf(String relExe, List<String> cdImages) {
// DOS uses \\ as path separator; / is a switch character.
// If the executable is in a subdirectory, cd into it first.
var parts = splitDirExe(relExe);
String dir = parts[0];
@@ -558,6 +584,12 @@ public class UploadResource {
sb.append("[autoexec]\n");
sb.append("@echo off\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");
if (dir != null) {
sb.append("path=c:\\\n");