diff --git a/src/main/java/com/dostalgia/UploadResource.java b/src/main/java/com/dostalgia/UploadResource.java index 8b202b1..6c157c9 100644 --- a/src/main/java/com/dostalgia/UploadResource.java +++ b/src/main/java/com/dostalgia/UploadResource.java @@ -87,6 +87,15 @@ public class UploadResource { LOG.warning("Failed to fix cue references: " + e.getMessage()); } + // Patch game config files with hardcoded absolute paths + // Some games (Fallout, etc.) have FALLOUT.CFG with C:\dir\ paths + // that break after flattening moves files to root. + try { + fixAbsoluteConfigPaths(extractDir); + } catch (Exception e) { + LOG.warning("Failed to patch config paths: " + e.getMessage()); + } + // Find main executable String mainExe = findMainExe(extractDir); @@ -471,6 +480,74 @@ public class UploadResource { } } + /** Known game config files that may have hardcoded absolute DOS paths. + * Key = filename pattern (lowercase), Value = list of path prefixes to + * replace with ".\\" (relative to the game root). */ + private static final java.util.Map> CONFIG_PATH_FIXES = + java.util.Map.of( + "fallout.cfg", java.util.List.of("c:\\fallout1\\", "c:\\fallout2\\", "c:\\fallout\\") + ); + + /** + * Patch known game config files that contain hardcoded absolute DOS paths + * like {@code master_dat=C:\fallout1\master.dat}. After flattening moves + * everything to the bundle root, these paths break. Rewrites them to + * relative {@code .\master.dat} instead. + */ + private static void fixAbsoluteConfigPaths(Path extractDir) throws IOException { + try (var walk = Files.walk(extractDir)) { + walk.filter(Files::isRegularFile).forEach(f -> { + String name = f.getFileName().toString().toLowerCase(); + var prefixes = CONFIG_PATH_FIXES.get(name); + if (prefixes == null) return; + try { + byte[] data = Files.readAllBytes(f); + byte[] original = data.clone(); + for (String prefix : prefixes) { + // Replace both "C:\dir\" and "c:\dir\" patterns + byte[] pBytes = prefix.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1); + byte[] pSlash = prefix.replace('\\', '/') + .getBytes(java.nio.charset.StandardCharsets.ISO_8859_1); + byte[] rel = ".\\".getBytes(java.nio.charset.StandardCharsets.ISO_8859_1); + byte[] relSlash = "./" + .getBytes(java.nio.charset.StandardCharsets.ISO_8859_1); + data = replaceBytes(data, pBytes, rel); + data = replaceBytes(data, pSlash, relSlash); + } + if (!java.util.Arrays.equals(data, original)) { + Files.write(f, data); + LOG.info("Patched absolute paths in " + f.getFileName()); + } + } catch (IOException e) { + LOG.warning("Failed to patch " + f.getFileName() + ": " + e.getMessage()); + } + }); + } + } + + /** Simple byte-level search-and-replace (preserves CRLF, encoding, etc.). */ + private static byte[] replaceBytes(byte[] src, byte[] search, byte[] replacement) { + if (search.length == 0) return src; + java.util.ArrayList result = new java.util.ArrayList<>(src.length); + int i = 0; + while (i <= src.length - search.length) { + boolean match = true; + for (int j = 0; j < search.length; j++) { + if (src[i + j] != search[j]) { match = false; break; } + } + if (match) { + for (byte b : replacement) result.add(b); + i += search.length; + } else { + result.add(src[i++]); + } + } + while (i < src.length) result.add(src[i++]); + byte[] out = new byte[result.size()]; + for (int k = 0; k < out.length; k++) out[k] = result.get(k); + return out; + } + /** Find mountable CD image files in the extracted game directory. * Returns relative paths (with forward slashes) sorted alphabetically. */ private List findCdImages(Path dir) throws IOException {