diff --git a/src/main/java/com/dostalgia/UploadResource.java b/src/main/java/com/dostalgia/UploadResource.java index 44da2ac..1b14b28 100644 --- a/src/main/java/com/dostalgia/UploadResource.java +++ b/src/main/java/com/dostalgia/UploadResource.java @@ -106,11 +106,13 @@ public class UploadResource { // Create setup bundle only if the setup executable is itself DOS-compatible. // Some games (e.g. Fallout) ship a Windows SETUP.EXE even though the main // game is DOS — that setup would fail with "This program cannot run in DOS mode". + boolean hasSetupBundle = false; if (setupExe != null) { String setupPlatform = detectPlatform(Path.of(setupExe)); if (!"windows".equals(setupPlatform)) { Path setupBundlePath = svc.getGamesDir().resolve(gameId + ".setup.jsdos"); createBundle(extractDir, setupExe, setupBundlePath); + hasSetupBundle = true; } } @@ -118,7 +120,9 @@ public class UploadResource { Game game = new Game(gameId, title); game.setBundleFile(bundleFile); game.setPlatform(platform); - game.setSetupExe(setupExe != null ? extractDir.relativize(Path.of(setupExe)).toString() : null); + if (hasSetupBundle) { + game.setSetupExe(extractDir.relativize(Path.of(setupExe)).toString()); + } game.setReady(true); svc.save(game); @@ -291,24 +295,26 @@ public class UploadResource { */ static boolean isLikelySelfExtractor(Path exePath) { try (var fis = Files.newInputStream(exePath)) { - byte[] buf = new byte[4096]; + byte[] buf = new byte[32768]; // 32KB to cover most PKSFX stubs int read = fis.readNBytes(buf, 0, buf.length); if (read < 0x40) return false; // Must start with MZ if (buf[0] != 0x4D || buf[1] != 0x5A) return false; - // Scan the MS-DOS stub area (after the header) for PKWARE signatures - // PKSFX/PKZIP stubs contain strings like "PKZIP", "PK", "PKSFX" - // in the first few KB of the executable + // Scan for PKWARE text signatures in the MS-DOS stub area String content = new String(buf, 0, read, java.nio.charset.StandardCharsets.ISO_8859_1); if (content.contains("PKZIP") || content.contains("PKSFX")) return true; - // Check for the typical PKWARE "PK\x03\x04" local file header signature - // which appears in append-to-exe style self-extractors - for (int i = 0; i < read - 4; i++) { - if (buf[i] == 0x50 && buf[i+1] == 0x4B && buf[i+2] == 0x03 && buf[i+3] == 0x04) - return true; - if (buf[i] == 0x50 && buf[i+1] == 0x4B && buf[i+2] == 0x05 && buf[i+3] == 0x06) - return true; + // Check for PKWARE zip signatures in appended zip data. + // Only check at reasonable offsets (past the MZ header + stub) + // to avoid false positives on code bytes that happen to be 0x50,0x4B. + for (int i = 0x40; i < read - 4; i++) { + int p = buf[i] & 0xFF; + // PK\x03\x04 = local file header, PK\x01\x02 = central dir + if (p == 0x50 && (buf[i+1] & 0xFF) == 0x4B) { + int sig = (buf[i+2] & 0xFF) | ((buf[i+3] & 0xFF) << 8); + if (sig == 0x0403 || sig == 0x0201 || sig == 0x0605) + return true; + } } return false; } catch (IOException e) { @@ -379,7 +385,7 @@ public class UploadResource { "debug", // Debugger "uninst", "uninstall", "uninstaller", // Uninstallers // Compression / archive tools (self-extractors) - "pksfx", "pkzip", "pkunzip", // PKWARE + "pksfx", "pkzip", "pkunzip", "sfx", "makesfx", // PKWARE "unzip", "zip", "zip2exe", // Info-ZIP "arj", "arj32", // ARJ "lha", "lha32", "lzh", // LHA/LHarc