Revert "feat: add Sierra game bundle patcher and API endpoint"
Build & Deploy / build-and-deploy (push) Successful in 20s

This reverts commit 5abc066178.
This commit is contained in:
Hermes Agent
2026-06-01 13:35:19 +02:00
parent 5abc066178
commit 1362e6034a
2 changed files with 0 additions and 166 deletions
@@ -131,23 +131,6 @@ public class GameResource {
} }
} }
/**
* Patch a Sierra game bundle to fix common file layout issues
* (INTERP.ERR paths, RESOURCE.CFG directories).
*/
@POST
@Path("/{id}/patch-sierra")
public Response patchSierra(@PathParam("id") String id) {
try {
var patched = svc.patchSierraGame(id);
return Response.ok(Map.of("patched", patched)).build();
} catch (NoSuchFileException e) {
return Response.status(404).entity(Map.of("error", "Game not found")).build();
} catch (Exception e) {
return Response.serverError().entity(Map.of("error", e.getMessage())).build();
}
}
@GET @GET
@Path("/{id}/download") @Path("/{id}/download")
@Produces("application/zip") @Produces("application/zip")
@@ -322,154 +322,5 @@ public class GameService {
} }
} }
/**
* Patch a .jsdos bundle for Sierra games that have file layout issues:
* - INTERP.ERR(C) in a subdirectory instead of root (causes "Can't find" errors)
* - RESOURCE.CFG directory paths pointing to wrong locations
* Returns the list of files that were modified.
*/
public List<String> patchSierraGame(String id) throws IOException {
Game game = load(id);
Path bundlePath = gamesDir.resolve(game.getBundleFile());
if (!Files.exists(bundlePath)) {
throw new NoSuchFileException("Bundle not found: " + game.getBundleFile());
}
Path tmpPath = bundlePath.resolveSibling(bundlePath.getFileName() + ".patchtmp");
List<String> patched = new ArrayList<>();
try (var zis = new java.util.zip.ZipInputStream(Files.newInputStream(bundlePath));
var zos = new java.util.zip.ZipOutputStream(Files.newOutputStream(tmpPath))) {
// First pass: read all entries to find INTERP files in subdirs
// We can't buffer the whole zip, so we do the copy in one pass
// and apply known fixes on the fly.
// Read the RESOURCE.CFG for Sierra directory detection
// We do this by pre-scanning the zip (streaming approach)
java.util.zip.ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
String name = entry.getName();
zos.putNextEntry(new java.util.zip.ZipEntry(name));
if (name.equals("RESOURCE.CFG")) {
// Read, patch, write
byte[] content = zis.readAllBytes();
String text = new String(content, java.nio.charset.StandardCharsets.ISO_8859_1);
String original = text;
// Fix directory paths pointing to a subdir when resources are at root
text = text.replaceAll("(?m)^directory\\s*=\\\\?.+", "directory = .");
text = text.replaceAll("(?m)^audio\\s*=\\s*[A-Za-z]:\\\\?.+", "audio = .");
text = text.replaceAll("(?m)^sync\\s*=\\s*[A-Za-z]:\\\\?.+", "sync = .");
text = text.replaceAll("(?m)^movieDir\\s*=\\s*[A-Za-z]:\\\\?.+", "movieDir = .\\\\SEQ");
text = text.replaceAll("(?m)^patchDir\\s*=\\s*[A-Za-z]:\\\\?.+", "patchDir = .");
if (!text.equals(original)) {
patched.add("RESOURCE.CFG (fixed paths)");
zos.write(text.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1));
} else {
zos.write(content);
}
} else {
// For all other files, check if we need to add INTERP.ERR(C) copies
zis.transferTo(zos);
}
zos.closeEntry();
zis.closeEntry();
}
// Second pass: scan for INTERP files in subdirectories
// We need to re-read the zip for this. Use a clean approach.
}
// If RESOURCE.CFG was not patched, check for INTERP.ERR and add it
boolean needsInterpFix = false;
String interpSource = null;
// Re-scan the original bundle for INTERP files in subdirs
try (var zis = new java.util.zip.ZipInputStream(Files.newInputStream(bundlePath))) {
java.util.zip.ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
String name = entry.getName();
if (!name.startsWith(".jsdos/") && name.contains("/") &&
(name.endsWith("/INTERP.ERR") || name.endsWith("/INTERP.ERRC"))) {
// Found an INTERP file in a subdirectory
String rootName = name.substring(name.lastIndexOf('/') + 1);
interpSource = name;
break;
}
zis.closeEntry();
}
}
if (interpSource != null) {
// Check if INTERP file already exists at root in the patched bundle
boolean hasRootInterp = false;
try (var zis = new java.util.zip.ZipInputStream(Files.newInputStream(tmpPath))) {
java.util.zip.ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
String name = entry.getName();
if (name.equals("INTERP.ERR") || name.equals("INTERP.ERRC")) {
hasRootInterp = true;
break;
}
zis.closeEntry();
}
}
if (!hasRootInterp) {
// Read the INTERP file from original bundle and append it
byte[] interpData = null;
try (var zis = new java.util.zip.ZipInputStream(Files.newInputStream(bundlePath))) {
java.util.zip.ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (entry.getName().equals(interpSource)) {
interpData = zis.readAllBytes();
break;
}
zis.closeEntry();
}
}
if (interpData != null) {
// Append to the patched bundle
String rootName = interpSource.substring(interpSource.lastIndexOf('/') + 1);
try (var zis2 = new java.util.zip.ZipInputStream(Files.newInputStream(tmpPath));
var tmp2 = new java.util.zip.ZipOutputStream(Files.newOutputStream(
tmpPath.resolveSibling(bundlePath.getFileName() + ".patchtmp2")))) {
java.util.zip.ZipEntry e;
while ((e = zis2.getNextEntry()) != null) {
tmp2.putNextEntry(new java.util.zip.ZipEntry(e.getName()));
zis2.transferTo(tmp2);
tmp2.closeEntry();
zis2.closeEntry();
}
// Add INTERP file at root
tmp2.putNextEntry(new java.util.zip.ZipEntry(rootName));
tmp2.write(interpData);
tmp2.closeEntry();
zis2.close();
tmp2.close();
}
Files.delete(tmpPath);
Path tmp2 = tmpPath.resolveSibling(bundlePath.getFileName() + ".patchtmp2");
Files.move(tmp2, tmpPath);
patched.add(rootName + " (copied from " + interpSource + ")");
}
}
}
// If nothing was patched, clean up and return empty
if (patched.isEmpty()) {
Files.deleteIfExists(tmpPath);
return patched;
}
Files.move(tmpPath, bundlePath, StandardCopyOption.REPLACE_EXISTING);
return patched;
}
public Path getGamesDir() { return gamesDir; } public Path getGamesDir() { return gamesDir; }
} }