fix: full dosbox.conf with usescancodes=true + auto WASD for Doom
Build & Deploy / build-and-deploy (push) Successful in 53s
Build & Deploy / build-and-deploy (push) Successful in 53s
Root cause: Dosbox.conf was missing the [sdl] section with 'usescancodes=true' — without it DOSBox doesn't translate keyboard scan codes and WASD keys don't register as game input. Fix: - Generate a complete dosbox.conf matching js-dos v8 standards (all sections: sdl, dosbox, cpu, mixer, render, midi, sblaster, gus, speaker, joystick, serial, dos, ipx, autoexec) - Key setting: usescancodes=true + mapperfile=mapper-jsdos.map - Uses svga_s3 machine type with SB16 audio (same as dos.zone) Bonus: Auto-detect Doom-engine games (DOOM.WAD / DOOM2.WAD) and inject a DEFAULT.CFG with WASD controls: W=forward, S=backward, A=strafe-left, D=strafe-right Arrows=turn, Ctrl=fire, Space=use, Alt=strafe, Shift=run Mouse enabled for looking Note: Existing uploaded games still have the old dosbox.conf. They need to be re-uploaded to get the fix.
This commit is contained in:
@@ -83,11 +83,13 @@ public class UploadResource {
|
|||||||
if ("sockdrive".equals(bundleType)) {
|
if ("sockdrive".equals(bundleType)) {
|
||||||
// Copy files loose
|
// Copy files loose
|
||||||
copyDir(extractDir, gameDir);
|
copyDir(extractDir, gameDir);
|
||||||
// Write dosbox.conf
|
// Auto-inject WASD config for Doom-engine games
|
||||||
|
injectDoomConfig(extractDir, gameDir);
|
||||||
|
// Write dosbox.conf (full config, not just autoexec)
|
||||||
Path jsdos = gameDir.resolve(".jsdos");
|
Path jsdos = gameDir.resolve(".jsdos");
|
||||||
Files.createDirectories(jsdos);
|
Files.createDirectories(jsdos);
|
||||||
String relExe = Path.of(mainExe).getFileName().toString();
|
String relExe = Path.of(mainExe).getFileName().toString();
|
||||||
String conf = "[autoexec]\n@echo off\nmount c .\nc:\ncls\n" + relExe + "\n";
|
String conf = buildDosboxConf(relExe);
|
||||||
Files.writeString(jsdos.resolve("dosbox.conf"), conf);
|
Files.writeString(jsdos.resolve("dosbox.conf"), conf);
|
||||||
bundleFile = ".jsdos/dosbox.conf";
|
bundleFile = ".jsdos/dosbox.conf";
|
||||||
} else {
|
} else {
|
||||||
@@ -207,12 +209,14 @@ public class UploadResource {
|
|||||||
Path tmpBundle = Files.createTempDirectory("dostalgia-bundle-");
|
Path tmpBundle = Files.createTempDirectory("dostalgia-bundle-");
|
||||||
try {
|
try {
|
||||||
copyDir(extractDir, tmpBundle);
|
copyDir(extractDir, tmpBundle);
|
||||||
|
// Auto-inject WASD config for Doom-engine games
|
||||||
|
injectDoomConfig(extractDir, tmpBundle);
|
||||||
|
|
||||||
// Create .jsdos config
|
// Create .jsdos config (full config, not just autoexec)
|
||||||
Path jsdos = tmpBundle.resolve(".jsdos");
|
Path jsdos = tmpBundle.resolve(".jsdos");
|
||||||
Files.createDirectories(jsdos);
|
Files.createDirectories(jsdos);
|
||||||
String relExe = Path.of(mainExe).getFileName().toString();
|
String relExe = Path.of(mainExe).getFileName().toString();
|
||||||
String conf = "[autoexec]\n@echo off\nmount c .\nc:\ncls\n" + relExe + "\n";
|
String conf = buildDosboxConf(relExe);
|
||||||
Files.writeString(jsdos.resolve("dosbox.conf"), conf);
|
Files.writeString(jsdos.resolve("dosbox.conf"), conf);
|
||||||
|
|
||||||
// ZIP it up
|
// ZIP it up
|
||||||
@@ -235,6 +239,176 @@ public class UploadResource {
|
|||||||
|
|
||||||
// ─── File Utilities ──────────────────────────────────────────
|
// ─── File Utilities ──────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a complete dosbox.conf with all sections needed for proper
|
||||||
|
* keyboard and hardware emulation in js-dos v8.
|
||||||
|
*/
|
||||||
|
private String buildDosboxConf(String relExe) {
|
||||||
|
return "[sdl]\n"
|
||||||
|
+ "autolock=true\n"
|
||||||
|
+ "fullscreen=false\n"
|
||||||
|
+ "fulldouble=false\n"
|
||||||
|
+ "fullresolution=original\n"
|
||||||
|
+ "windowresolution=original\n"
|
||||||
|
+ "output=surface\n"
|
||||||
|
+ "sensitivity=100\n"
|
||||||
|
+ "waitonerror=true\n"
|
||||||
|
+ "priority=higher,normal\n"
|
||||||
|
+ "mapperfile=mapper-jsdos.map\n"
|
||||||
|
+ "usescancodes=true\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[dosbox]\n"
|
||||||
|
+ "machine=svga_s3\n"
|
||||||
|
+ "memsize=16\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[cpu]\n"
|
||||||
|
+ "core=auto\n"
|
||||||
|
+ "cputype=auto\n"
|
||||||
|
+ "cycles=auto\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[mixer]\n"
|
||||||
|
+ "nosound=false\n"
|
||||||
|
+ "rate=44100\n"
|
||||||
|
+ "blocksize=1024\n"
|
||||||
|
+ "prebuffer=20\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[render]\n"
|
||||||
|
+ "frameskip=0\n"
|
||||||
|
+ "aspect=false\n"
|
||||||
|
+ "scaler=none\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[midi]\n"
|
||||||
|
+ "mpu401=intelligent\n"
|
||||||
|
+ "mididevice=default\n"
|
||||||
|
+ "midiconfig=\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[sblaster]\n"
|
||||||
|
+ "sbtype=sb16\n"
|
||||||
|
+ "sbbase=220\n"
|
||||||
|
+ "irq=7\n"
|
||||||
|
+ "dma=1\n"
|
||||||
|
+ "hdma=5\n"
|
||||||
|
+ "sbmixer=true\n"
|
||||||
|
+ "oplmode=auto\n"
|
||||||
|
+ "oplemu=default\n"
|
||||||
|
+ "oplrate=44100\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[gus]\n"
|
||||||
|
+ "gus=false\n"
|
||||||
|
+ "gusrate=44100\n"
|
||||||
|
+ "gusbase=240\n"
|
||||||
|
+ "gusirq=5\n"
|
||||||
|
+ "gusdma=3\n"
|
||||||
|
+ "ultradir=C:\\ULTRASND\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[speaker]\n"
|
||||||
|
+ "pcspeaker=true\n"
|
||||||
|
+ "pcrate=44100\n"
|
||||||
|
+ "tandy=auto\n"
|
||||||
|
+ "tandyrate=44100\n"
|
||||||
|
+ "disney=true\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[joystick]\n"
|
||||||
|
+ "joysticktype=auto\n"
|
||||||
|
+ "timed=true\n"
|
||||||
|
+ "autofire=false\n"
|
||||||
|
+ "swap34=false\n"
|
||||||
|
+ "buttonwrap=false\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[serial]\n"
|
||||||
|
+ "serial1=dummy\n"
|
||||||
|
+ "serial2=dummy\n"
|
||||||
|
+ "serial3=disabled\n"
|
||||||
|
+ "serial4=disabled\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[dos]\n"
|
||||||
|
+ "xms=true\n"
|
||||||
|
+ "ems=true\n"
|
||||||
|
+ "umb=true\n"
|
||||||
|
+ "keyboardlayout=auto\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[ipx]\n"
|
||||||
|
+ "ipx=true\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "[autoexec]\n"
|
||||||
|
+ "@echo off\n"
|
||||||
|
+ "mount c .\n"
|
||||||
|
+ "c:\n"
|
||||||
|
+ "cls\n"
|
||||||
|
+ relExe + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect Doom-engine games and inject a DEFAULT.CFG with WASD controls.
|
||||||
|
* Checks for DOOM.WAD or DOOM2.WAD in the source, then writes the
|
||||||
|
* config to the destination if it doesn't already have one.
|
||||||
|
*/
|
||||||
|
private void injectDoomConfig(Path srcDir, Path dstDir) throws IOException {
|
||||||
|
boolean hasDoom = false;
|
||||||
|
try (var stream = Files.list(srcDir)) {
|
||||||
|
for (Path f : (Iterable<Path>) stream::iterator) {
|
||||||
|
String name = f.getFileName().toString().toUpperCase();
|
||||||
|
if (name.equals("DOOM.WAD") || name.equals("DOOM2.WAD")) {
|
||||||
|
hasDoom = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasDoom) return;
|
||||||
|
// Don't overwrite an existing config
|
||||||
|
Path cfg = dstDir.resolve("DEFAULT.CFG");
|
||||||
|
if (Files.exists(cfg)) return;
|
||||||
|
Files.write(cfg, buildDoomDefaultCfg());
|
||||||
|
LOG.info("Doom detected — injected DEFAULT.CFG with WASD controls");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a DEFAULT.CFG (Doom engine config) with WASD controls.
|
||||||
|
* Only called when a DOOM.WAD or DOOM2.WAD is detected in the upload.
|
||||||
|
*
|
||||||
|
* Key bindings:
|
||||||
|
* W=forward, S=backward, A=strafe left, D=strafe right
|
||||||
|
* Arrows=turn, Ctrl=fire, Space=use, Alt=strafe, Shift=run
|
||||||
|
* Mouse enabled for looking
|
||||||
|
*/
|
||||||
|
private byte[] buildDoomDefaultCfg() {
|
||||||
|
String cfg = ""
|
||||||
|
+ "mouse_sensitivity\t\t5\n"
|
||||||
|
+ "sfx_volume\t\t8\n"
|
||||||
|
+ "music_volume\t\t8\n"
|
||||||
|
+ "show_messages\t\t1\n"
|
||||||
|
+ "key_right\t\t77\n" // Right arrow — turn right
|
||||||
|
+ "key_left\t\t75\n" // Left arrow — turn left
|
||||||
|
+ "key_up\t\t17\n" // W — forward
|
||||||
|
+ "key_down\t\t31\n" // S — backward
|
||||||
|
+ "key_strafeleft\t\t30\n" // A — strafe left
|
||||||
|
+ "key_straferight\t\t32\n" // D — strafe right
|
||||||
|
+ "key_fire\t\t29\n" // Ctrl — fire
|
||||||
|
+ "key_use\t\t57\n" // Space — use/open
|
||||||
|
+ "key_strafe\t\t56\n" // Alt — strafe (hold)
|
||||||
|
+ "key_speed\t\t54\n" // Shift — run
|
||||||
|
+ "use_mouse\t\t1\n"
|
||||||
|
+ "mouseb_fire\t\t0\n"
|
||||||
|
+ "mouseb_strafe\t\t1\n"
|
||||||
|
+ "mouseb_forward\t\t2\n"
|
||||||
|
+ "use_joystick\t\t0\n"
|
||||||
|
+ "joyb_fire\t\t0\n"
|
||||||
|
+ "joyb_strafe\t\t1\n"
|
||||||
|
+ "joyb_use\t\t3\n"
|
||||||
|
+ "joyb_speed\t\t2\n"
|
||||||
|
+ "screenblocks\t\t10\n"
|
||||||
|
+ "detaillevel\t\t0\n"
|
||||||
|
+ "showmessages\t\t1\n"
|
||||||
|
+ "comport\t\t1\n"
|
||||||
|
+ "snd_channels\t\t3\n"
|
||||||
|
+ "snd_musicdevice\t\t3\n"
|
||||||
|
+ "snd_sfxdevice\t\t3\n"
|
||||||
|
+ "snd_sbport\t\t544\n"
|
||||||
|
+ "snd_sbirq\t\t7\n"
|
||||||
|
+ "snd_sbdma\t\t1\n";
|
||||||
|
return cfg.getBytes(java.nio.charset.StandardCharsets.US_ASCII);
|
||||||
|
}
|
||||||
|
|
||||||
private double dirSizeMB(Path dir) throws IOException {
|
private double dirSizeMB(Path dir) throws IOException {
|
||||||
long[] total = {0};
|
long[] total = {0};
|
||||||
Files.walkFileTree(dir, new SimpleFileVisitor<>() {
|
Files.walkFileTree(dir, new SimpleFileVisitor<>() {
|
||||||
|
|||||||
Reference in New Issue
Block a user