For pure DOS executables, the e_lfanew field at MZ header offset 0x3C
is uninitialized garbage. Duke Nukem 2's EXE had bytes that form a
negative int32 (-1878619136), which was used as an array index causing
ArrayIndexOutOfBoundsException.
Now checks peOffset < 0 and returns 'dos' (no Windows signature found).
Many classic DOS games (Blood, DOOM, Duke Nukem 3D, Quake) use
DOS/4GW or DOS/32A — 32-bit DOS extenders with Linear Executable
(LE) format. My code was detecting LE as 'windows', causing these
games to be incorrectly flagged as unplayable.
Only PE (Portable Executable / Win32) and NE (New Executable /
Windows 3.x) are actually Windows.
Previously when no IGDB matches were found, the button next to the
search box changed from 'Search' to 'Upload' after the first search.
Since the text is editable and users may want to refine their query,
this button should always trigger a search.
Upload is still available via:
- The 'Upload anyway as "..."' button (shown after failed search)
- The 'Use filename: ...' button at the bottom
- Move platform detection before setup bundle creation
- Only create setup bundle when platform != 'windows'
- Also hide the Setup button in GameDetail for Windows games
- Windows setup executables (INSTALL.EXE) can't run in DOSBox anyway
js-dos v8's C extraction code (jsdos-libzip.c) iterates ZIP entries
linearly and calls mkdir() on each directory entry. If a child
directory appears before its parent, mkdir() fails with perror()
and exit(1) — fatal crash with 'No such file or directory'.
Previous approach wrote directory entries interleaved with files,
deepest-first (from the file path right-to-left). Now:
Phase 1: Walk entire file tree to discover ALL directories
Phase 2: Write ALL dir entries sorted (shallowest first via TreeSet
natural ordering — '/' sorts before any letter)
Phase 3: Write .jsdos/ config files
Phase 4: Write game files (parents already exist from Phase 2)
This guarantees that mkdir("NAS50TH/TRACKS/") runs before
mkdir("NAS50TH/TRACKS/WILKES/"), which runs before open().
Many DOS game ZIPs include CloneCD backups (.ccd/.img/.sub) or
other disk images that are not needed for gameplay — the game data
is already extracted in the working directory. These can be 300MB+
and cause WASM memory exhaustion in js-dos.
Added a SKIP_EXT set that filters out known disk image extensions
during game file pass. The .jsdos config pass is unaffected.
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
Previously only the immediate parent directory was added as a ZIP
entry. For deeply nested paths like NAS50TH/TRACKS/WILKES/WILKES.DAT,
only NAS50TH/TRACKS/WILKES/ was created — but NAS50TH/TRACKS/ and
NAS50TH/ were skipped (they're only parents of other directories,
never the immediate parent of a file).
Emscripten's FS.createDirectory() fails if parent doesn't exist,
so intermediate directories must be in the ZIP too.
Now walks up the full ancestor chain with a while loop.
js-dos uses Emscripten's virtual filesystem which requires explicit
ZIP directory entries (names ending with '/') to create directories.
Java's ZipOutputStream only writes file entries, so subdirectories
like NAS50TH/TRACKS/WILKES/ were never created — causing Emscripten
'No such file or directory' errors for any path inside them.
- createBundle: before writing each file entry, add its parent
directory as an explicit ZIP entry if not already added
- Affects all games with subdirectory structures (NASCAR Racing 2,
Gus Goes to Cyberopolis, etc.)
- Use a HashSet to track already-added directories (no duplicates)
- Game.java: add 'platform' field ('dos'/'windows') + 'isPlayable' helper
- UploadResource.java: detect EXE type by reading PE/NE header during upload
- GameResource.java: allow PATCH to update platform
- GameCard.svelte: show 🪟 Win badge for Windows games (blue style)
- GameDetail.svelte: show platform badge, disable Play for Windows with
'Unplayable' button; add platform dropdown to edit form
- Play.svelte: show blocking overlay for Windows games with 'Try anyway'
option for users who want to attempt it
- Fix flattenSingleDir: flatten when exactly 1 root directory exists,
even if other metadata files (file_id.diz, *.nfo) are present at root
- Fix buildDosboxConf/buildJsdosJson: add 'cd <dir>' before executable
when it lives in a subdirectory, since DOS treats '/' as switch char
(previously 'mastori2/ORION2.EXE' was parsed as command 'mastori2'
with switch '/ORION2.EXE')
- Remove SockdriveResource.java entirely
- Remove bundleType field from Game.java (was 'standard'/'sockdrive')
- Simplify GameResource.java download endpoint
- Clean up upload resource