Skip to content

Add server-side ROM patching endpoint#3225

Draft
gantoine wants to merge 3 commits intomasterfrom
claude/server-side-rom-patching-EaQck
Draft

Add server-side ROM patching endpoint#3225
gantoine wants to merge 3 commits intomasterfrom
claude/server-side-rom-patching-EaQck

Conversation

@gantoine
Copy link
Copy Markdown
Member

@gantoine gantoine commented Apr 6, 2026

Summary

  • Adds POST /api/roms/{id}/patch endpoint that applies patch files to ROM files server-side using RomPatcher.js
  • Enables third-party apps (mobile clients, handhelds, desktop tools) to request ROM patching using games + patches already in the library, without needing client-side JavaScript
  • Supports all 9 patch formats: IPS, UPS, BPS, PPF, RUP, APS, BDF, PMSR, VCDIFF

How it works

  1. A Node.js helper script (backend/utils/patcher.js) loads RomPatcher.js from the frontend's node_modules (already installed in the Docker image)
  2. The FastAPI endpoint looks up ROM and patch files by their RomFile IDs, validates paths via fs_rom_handler.validate_path(), then invokes the patcher via asyncio.create_subprocess_exec
  3. The patched ROM is streamed back as a file download; temp files are cleaned up via BackgroundTask

API usage

# Apply patch (file_id=42) to ROM (file_id=10)
curl -X POST /api/roms/10/patch \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"patch_file_id": 42}' \
  --output patched.bin

# With custom output filename
curl -X POST /api/roms/10/patch \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"patch_file_id": 42, "output_file_name": "My Patched ROM"}' \
  --output patched.bin

Test plan

  • Verify the endpoint applies IPS/BPS/UPS patches correctly against known-good ROM + patch combinations
  • Verify error handling for missing files, unsupported formats, and invalid patches
  • Verify temp file cleanup after successful and failed requests
  • Verify authentication (roms.read scope required)
  • Test with third-party client using client tokens

https://claude.ai/code/session_01HS6ZvAiBjmLPVB3gGw8eEt

Introduces POST /api/roms/{id}/patch that applies patch files to ROM files
server-side, enabling third-party apps to request ROM patching using games
and patches already in the library without needing client-side JS support.

- backend/utils/patcher.js: Node.js helper that loads RomPatcher.js and
  applies a patch file to a ROM, writing the result to an output path
- backend/endpoints/roms/patch.py: FastAPI endpoint that looks up ROM and
  patch files by ID, invokes the Node.js patcher via subprocess, and
  streams the patched ROM back as a download
- Supports all 9 patch formats: IPS, UPS, BPS, PPF, RUP, APS, BDF, PMSR, VCDIFF
- Requires roms.read scope for authentication
- Temp files are cleaned up via Starlette BackgroundTask after response

https://claude.ai/code/session_01HS6ZvAiBjmLPVB3gGw8eEt
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Test Results (postgresql)

1 243 tests  ±0   1 243 ✅ ±0   3m 48s ⏱️ -14s
    1 suites ±0       0 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 1ee4682. ± Comparison against base commit 3388b34.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Test Results (mariadb)

1 243 tests  ±0   1 243 ✅ ±0   3m 53s ⏱️ -1s
    1 suites ±0       0 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 1ee4682. ± Comparison against base commit 3388b34.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
16128 10936 68% 0% 🟢

New Files

File Coverage Status
backend/endpoints/roms/patch.py 45% 🟢
backend/utils/rom_patcher/init.py 100% 🟢
backend/utils/rom_patcher/patcher.py 35% 🟢
TOTAL 60% 🟢

Modified Files

File Coverage Status
backend/endpoints/roms/_init_.py 62% 🟢
TOTAL 62% 🟢

updated for commit: 1ee4682 by action🐍

gantoine and others added 2 commits April 12, 2026 19:51
- Rewrite frontend/src/views/Patcher.vue around library ROM/patch pickers
  (v-autocomplete with debounced search) and call POST /api/roms/{id}/patch
  with responseType: blob; download and/or re-upload the patched output.
- Drop the rom-patcher npm dep and vite-plugin-static-copy from the frontend;
  remove the now-unused web worker, type decls, and viteStaticCopy plugin.
- Move the Node patcher into a self-contained npm project at
  backend/utils/rom_patcher/ (dir uses underscore so it's a valid Python
  package). Patcher.js loads RomPatcher.js from its sibling node_modules.
- Extract the heavy subprocess logic into backend/utils/rom_patcher/patcher.py
  (apply_patch, PatcherError, SUPPORTED_PATCH_EXTENSIONS); slim patch.py to
  HTTP/DB plumbing and fix a stale .parent.parent path.
- Wire up Docker: dev Dockerfile installs the new npm project; prod
  Dockerfile adds a backend-node-build stage and copies rom-patcher-js into
  the production image; install nodejs at runtime.
- Add new patcher i18n keys to en_US (other locales fall back).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants