feat: patch hardcoded absolute DOS paths in game configs during upload
Build & Deploy / build-and-deploy (push) Successful in 1m41s

Adds fixAbsoluteConfigPaths() which runs after flattening and cue fixing
during game upload. Detects known config files (FALLOUT.CFG, etc.) with
hardcoded C:\dir\ paths and rewrites them to .\ relative paths, so games
like Fallout can find their DAT files after flattening moves everything
to the bundle root.

Uses byte-level replacement to preserve original CRLF line endings and
encoding. Extensible via CONFIG_PATH_FIXES map — add new entries for
other games with the same problem.
This commit is contained in:
David Alvarez
2026-06-08 10:16:33 +02:00
parent 9fceeb4b78
commit 22a9a55af8
@@ -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<String, java.util.List<String>> 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<Byte> 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<String> findCdImages(Path dir) throws IOException {