You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 576df88
Browse filesBrowse the repository at this point in the historyBrowse files
feat(ppsspp): patched PPSSPP v1.20.4, its build, and the headless container (#4)
* feat(ppsspp): patched PPSSPP v1.20.4, its build, and the headless container
Five patches against v1.20.4 (GPL-2.0-or-later): savestate commands that answer when done,
the ARM JIT VFPU memcheck offset, and backports of the log to every connection, the stepping
reason and game.speed.get/set. ppsspp/build.sh builds it on macOS or Linux; the Docker image
runs the same script.
ppsspp-debug takes stop reasons and cpu.breakpoint.hit where PPSSPP sends them, speaks
game.speed.get/set, accepts a new stop as the answer to resume(), and DockerEmulator
captures the container's display.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* ci: no image build; anyone can build it locally
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: README.md
+6-4Lines changed: 6 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,6 +18,7 @@ release ULES01213), and for porting monsters to it from Monster Hunter Portable
18
18
|`packages/`| Python libraries: the debugger client, the file formats, the game (`mhfu`, which also holds the address map every language is generated from), the porter |
19
19
|`framework/`| The PRX plugin and its Lua host |
20
20
|`apps/`| The studio (monster and map editor), the HUD, the Blender extension |
21
+
|`ppsspp/`| PPSSPP patched for automation, its build, and a headless container to run it in |
21
22
|`examples/`| Example mods |
22
23
|`docs/`| Guides and format references |
23
24
@@ -33,13 +34,14 @@ uv run pytest
33
34
34
35
Tests that read game files take them from `MHFU_DATA` and `MHP3RD_DATA`, each a directory of
35
36
extracted `DATA.BIN` files, and skip when the variable is unset; tests against a running PPSSPP
36
-
take `PPSSPP_BINARY` and `PPSSPP_GAME` the same way. Nothing from the games is committed here:
37
-
no ISO, no extracted files, no memory dumps.
37
+
take `PPSSPP_BINARY` and `PPSSPP_GAME` the same way (`ppsspp/build.sh` builds the patched one).
38
+
Nothing from the games is committed here: no ISO, no extracted files, no memory dumps.
38
39
39
40
## Licence
40
41
41
42
MIT, except the file-format library and everything that bundles it, which are GPL-3.0-or-later
42
-
because they contain code derived from [mhff](https://github.com/svanheulen/mhff). Every file's
43
-
licence is machine-readable ([REUSE](https://reuse.software/); check with `reuse lint`).
43
+
because they contain code derived from [mhff](https://github.com/svanheulen/mhff), and the
44
+
PPSSPP patches, which are GPL-2.0-or-later like PPSSPP. Every file's licence is machine-readable
45
+
([REUSE](https://reuse.software/); check with `reuse lint`).
44
46
45
47
Not affiliated with or endorsed by Capcom. Monster Hunter is a trademark of Capcom Co., Ltd.
Copy file name to clipboardExpand all lines: packages/ppsspp-debug/README.md
+16-7Lines changed: 16 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -36,8 +36,9 @@ PPSSPP opens the debugger at startup only with `RemoteDebuggerOnStartup = True`
36
36
| PPSSPP (1.20.4) | The client |
37
37
|---|---|
38
38
| Sends its log only to the newest debugger connection, and stops it for all when any connection closes | Log streams get a connection of their own, opened when the stream starts |
39
-
| Does not say why the CPU stopped | Matches a breakpoint stop by its pc; counts as a watchpoint hit any stop this client did not ask for |
40
-
| Answers `cpu.stepping` and `cpu.resume` with a broadcast, not a reply; pausing a paused CPU answers nothing |`pause()` and `resume()` wait for the broadcast, and are fine to call twice |
39
+
| Does not say why the CPU stopped | Matches a breakpoint stop by its pc, and counts as a watchpoint hit any stop this client did not ask for; uses the reason where PPSSPP gives one |
40
+
| Reports a hit of a breakpoint that does not stop only as a log line | Parses it; takes `cpu.breakpoint.hit` events instead from a PPSSPP that sends them |
41
+
| Answers `cpu.stepping` and `cpu.resume` with a broadcast, not a reply; pausing a paused CPU answers nothing |`pause()` and `resume()` wait for the broadcast, and are fine to call twice; `resume()` also takes a new stop as its answer |
41
42
| Stops and restarts the CPU for every memory request while the game runs (about 10 ms each) |`paused()` makes each request about 0.3 ms; one `read()` of a whole struct costs one stop |
42
43
| Keeps breakpoints armed after the script that set them has died |`breakpoint()` and `watchpoint()` remove theirs when the block ends, however it ends, and let the game run if a hit arrived that nobody took |
43
44
| Reports errors as events, and an unknown event as an error | Raises `DebuggerError`, or `Unsupported` for an event this PPSSPP does not have |
@@ -58,14 +59,16 @@ with LocalEmulator("/path/to/PPSSPPSDL", "/path/to/game.iso") as emu:
58
59
ppsspp.wait_for_game()
59
60
```
60
61
61
-
`DockerEmulator(container)` drives PPSSPP in the modkit's headless container through its
62
-
`ppsspp-ctl`, and relaunches it when it dies right after reporting ready. A `state=` launch
63
-
skips the cold boot, so PRX plugins do not load.
62
+
`DockerEmulator()` drives PPSSPP in the modkit's headless container
63
+
([`ppsspp/`](../../ppsspp)) through its `ppsspp-ctl`, relaunches it when it dies right after
64
+
reporting ready, and captures its display with `screenshot()`. A `state=` launch skips the cold
65
+
boot, so PRX plugins do not load.
64
66
65
67
## Patched PPSSPP
66
68
67
-
`save_state()`, `load_state()` and `speed()` need the PPSSPP patches that ship with the modkit;
68
-
stock PPSSPP raises `Unsupported`.
69
+
The modkit's PPSSPP build ([`ppsspp/`](../../ppsspp)) fixes the first two rows of the table.
70
+
`save_state()` and `load_state()` need it; `speed()` and `set_speed()` need it or a PPSSPP newer
71
+
than 1.20.4. Stock 1.20.4 raises `Unsupported` for them.
69
72
70
73
`screenshot()` depends on the GPU backend: PPSSPP 1.20.4 with Vulkan answers "Could not
71
74
download output" whether the game runs or is stopped.
@@ -75,9 +78,15 @@ download output" whether the game runs or is stopped.
75
78
`ppsspp_debug.testing.FakePPSSPP` is a debugger server with PPSSPP's quirks, for testing code
76
79
that uses this client without an emulator.
77
80
81
+
`FakePPSSPP(patched=True)` behaves like the modkit's build, `FakePPSSPP(hit_events=True)` like a
82
+
PPSSPP that sends `cpu.breakpoint.hit`.
83
+
78
84
The live tests run against a real PPSSPP when `PPSSPP_BINARY` and `PPSSPP_GAME` are set, and
79
85
print what they measure with `-s`:
80
86
81
87
```bash
82
88
PPSSPP_BINARY=/path/to/PPSSPPSDL PPSSPP_GAME=/path/to/game.iso uv run pytest packages/ppsspp-debug -s
83
89
```
90
+
91
+
`PPSSPP_CONTAINER=ppsspp` in place of `PPSSPP_BINARY` runs them in the modkit's container, with
0 commit comments