Mod: Quick Gadget Select - #7
Conversation
The slingshot-suppression half of the feature never ran. It was a psx_mod_write_code_word() in an activation callback, and activation callbacks fire at main.cpp:12196 -- before SDL_Init and before the run loop, so the game's own EXE load overwrites 0x80063B6C long afterwards. The address is inside the boot image (load_address 0x80010000, text_size 0xA5000), so the write was dead every launch and the stock ammo cycle kept claiming the same second button press the row opens on. Express it as a declarative [[patch]] instead. Those are applied at first dispatch of the entry PC, guarded by expected bytes, and reapplied after a savestate restore -- none of which the plugin path does. The second plugin id is gone; the feature is one vblank plugin plus one patch. Also in this change: - Survive savestate and rewind. Plugin statics are not in a save state, so a restore under an open row left the snapshot describing a timeline that no longer exists, and cancelling wrote those stale bytes into the restored save. Every write now records what it left in guest RAM and each frame re-checks it; a mismatch abandons the row without writing. - Place the row against the real right edge under widescreen via psx_mod_widescreen_x_margin() rather than a fixed 4:3 316. - Stop leaking the icon texture page into the game's next frame: save it from GPUSTAT bits 0..10 and put it back, and clear the texture window first so a leftover window cannot wrap the icon UVs. Document why the drawing offset is deliberately inherited rather than set. - Swap the right-stick X/Y address names, which were the wrong way round against the DualShock report order, and say in the feature description that the stick commit needs an analog pad. - Use psx_mod_read_half() instead of reimplementing it. - Record what the addresses are and what 0x08018F7C decodes to. The test now asserts the planned main_exe write -- target, address, expected and replacement bytes -- and that nothing is patched while the feature is off. A plugin-count assertion passed against the original manifest while the patch did nothing; this one fails against it. Verified: gcc -Wall -Wextra clean on the plugin; ape_preloaded_mods_test passes on this tree and fails with "wrong Quick Gadget Select plugin plan" when the manifest is reverted to the original. Expected bytes D2C24380 read from SCUS_944.23 at file offset 2048 + 0x53B6C. Not verified: no in-game run. The row's widescreen placement and the GPU state handling are reasoned from the GP0/GPUSTAT contract, not observed on screen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Measured on a running guest, at the title screen and inside the attract demo's playable phase: the pad shadow reads 41 5A FF FF 00 00. The 0x41 is the controller reply id for a DIGITAL pad, which carries one halfword of buttons and no axes at all -- so the two bytes this code was reading as right X / right Y are outside the reply and read 0x00. 0x00 is not neutral. ape_axis_is_active() measures against the 0x80 centre, so 0x00 is a full deflection and the right stick reported as permanently active. Any player whose pad enumerates as digital would have had the row commit on the very frame it opened, making the feature unusable rather than merely losing the stick shortcut. The previous commit's comment claimed the opposite -- that the check "stays false" on a D-Pad controller -- which was backwards. Check the reply id first and fail closed: no DualShock, no axis read. Verified: 41 5A FF FF 00 00 read live over the debug server at 0x800B87A0 on this machine with no gamepad attached, sampled at the title screen and while transition phase was 0x03. The 0x80-centred analog path is NOT verified -- it needs a real DualShock, which this machine does not have. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The row was pinned to x=316, i.e. 320 minus a 4px margin. Ape Escape does not run at 320. Measured over the debug server on a live guest, it moves through three display modes -- 256x240 early in boot, 640x480, then 384x240 for gameplay -- so in game the row stopped 68 pixels short of the right edge, with visible dead space beside it. A hardcoded 384 would be no better: it would be wrong in the other two modes. Take the width from psx_mod_display_width() at draw time instead, and skip the row entirely while the geometry reports zero rather than drawing it somewhere guessed. In the gameplay mode the right edge is now 380 rather than 316. Requires the psx_mod_display_width() addition to psxrecomp; the width the presenter uses depends on the GP1(06h) horizontal range, which is write-only and unreachable from a plugin, so this cannot be derived locally. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Temporary pin onto the branch, not master. Quick Gadget Select needs psx_mod_display_width() to right-align its row to the picture the guest is actually scanning out, and that API does not exist in any released framework commit yet. Ape Escape moves through 256x240, 640x480 and 384x240, so no constant is correct in every mode; the width has to be asked for at draw time. The visible width depends on the GP1(06h) horizontal range, which is write-only, so a plugin cannot derive it and the framework has to hand it over. Repoint this at master once mstan/psxrecomp feat/mod-display-width lands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thanks for this — the feature works and the approach is right. I've pushed some commits to your branch rather than sending you a list of review notes; here's what changed and why. The main thing: the slingshot patch never applied. Verified across four builds by reading the site on a live guest:
That's a framework footgun rather than a mistake on your part — it fails completely silently, with no log line and no diagnostic. I've filed it upstream. Right-stick check on a digital pad. The axis bytes only exist in a DualShock reply. On a digital pad they read Row placement. Also in there:
Could you sanity-check it on your save? Particularly the open/cycle/commit feel, and the right-stick commit — no pad here presents as analog, so that path has still never actually run. |
This is an attempt at recreating the Quick Gadget Switching feature from AE2 JP and AE3 in Ape Escape 1
Repeated presses on an equipped face button opens the menu and cycles over all unlocked gadgets, using the gadget with the right stick or pressing any other face button commits the change.
It's still very much imperfect, but usable.
Replay.2026-08-06.16-21-38.mp4