mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
82bc90dff2
A digital presentation folds the sticks onto the D-pad and centres them, so a transform on a digital player (the default for many titles) never saw the stick. The transform decides what the guest sees, so for a digital stage-1 pad with no controller source its frame is now the host pad: real sticks and buttons without the stick->D-pad fold, type still DIGITAL (new PadExtHooks.host_pad, capture_pad_slot_view). The output is pre-filled with the stock pad and a decline delivers it, so pass-through stays the stock folded digital bytes (mod_pad_transform_run takes the stock pad). Analog pads, sources and guarded frames are unchanged. Tests: pad_external_input_test (digital player: frame has the real lx and unfolded buttons, decline and pass-through deliver the stock digital bytes, no host-pad query for analog / guarded / source ports), mod_pad_transform_test (decline returns the stock pad, output pre-filled from it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
5.2 KiB
C
125 lines
5.2 KiB
C
/* psx_mod_set_pad_transform registration rules and per-frame validation. */
|
|
#include "mod_pad_transform.h"
|
|
#include "sio.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define CHECK(expr) do { if (!(expr)) { \
|
|
fprintf(stderr, "FAIL line %d: %s\n", __LINE__, #expr); return 1; } } while (0)
|
|
|
|
static int mode, calls;
|
|
static PSXModPadFrame seen;
|
|
|
|
static int xf(const PSXModPadFrame *f, PSXModPadOutput *o) {
|
|
calls++;
|
|
seen = *f;
|
|
if (mode == 1) return 0; /* decline: pass through */
|
|
o->type = PSX_MOD_PAD_NEGCON;
|
|
o->buttons = f->buttons | 0x0400u; /* drop L1 */
|
|
o->negcon_i = f->host_rt;
|
|
o->negcon_ii = f->host_lt;
|
|
if (mode == 2) o->negcon_i = 256; /* out of range */
|
|
if (mode == 3) o->type = PSX_MOD_PAD_JOGCON; /* not allowed */
|
|
if (mode == 4) o->struct_size = 4;
|
|
return 1;
|
|
}
|
|
|
|
static PSXModPadFrame frame(void) {
|
|
PSXModPadFrame f;
|
|
memset(&f, 0, sizeof f);
|
|
f.struct_size = sizeof f;
|
|
f.buttons = 0xfbffu; /* L1 held */
|
|
f.lx = 0x40; f.ly = 0x80; f.rx = 0x90; f.ry = 0x70;
|
|
f.type = PSX_MOD_PAD_DUALSHOCK;
|
|
f.host_flags = 7; f.host_lt = 30; f.host_rt = 200;
|
|
return f;
|
|
}
|
|
|
|
int main(void) {
|
|
PSXModPadTransform t;
|
|
PSXModPadFrame f = frame();
|
|
PSXModPadOutput o;
|
|
memset(&t, 0, sizeof t);
|
|
t.struct_size = sizeof t;
|
|
t.allowed_types = PSX_MOD_PAD_TYPE_BIT(PSX_MOD_PAD_DUALSHOCK) |
|
|
PSX_MOD_PAD_TYPE_BIT(PSX_MOD_PAD_NEGCON);
|
|
t.initial_type = PSX_MOD_PAD_DUALSHOCK;
|
|
t.transform = xf;
|
|
|
|
/* Nothing registered: no stage, nothing owed. */
|
|
CHECK(mod_pad_transform_run(0, &f, NULL, &o) == 0);
|
|
CHECK(mod_pad_transform_initial_type(0) == -1);
|
|
|
|
/* Registration validation. */
|
|
CHECK(!psx_mod_set_pad_transform(PSX_MAX_PLAYERS, &t));
|
|
{ PSXModPadTransform b = t; b.struct_size = 8; CHECK(!psx_mod_set_pad_transform(0, &b)); }
|
|
{ PSXModPadTransform b = t; b.transform = NULL; CHECK(!psx_mod_set_pad_transform(0, &b)); }
|
|
{ PSXModPadTransform b = t; b.allowed_types = 0; CHECK(!psx_mod_set_pad_transform(0, &b)); }
|
|
{ PSXModPadTransform b = t; b.allowed_types |= 1u << 4; CHECK(!psx_mod_set_pad_transform(0, &b)); }
|
|
{ PSXModPadTransform b = t; b.initial_type = PSX_MOD_PAD_JOGCON; CHECK(!psx_mod_set_pad_transform(0, &b)); }
|
|
CHECK(mod_pad_transform_run(0, &f, NULL, &o) == 0);
|
|
CHECK(psx_mod_set_pad_transform(0, &t));
|
|
CHECK(mod_pad_transform_initial_type(0) == PSX_MOD_PAD_DUALSHOCK);
|
|
CHECK(mod_pad_transform_initial_type(1) == -1);
|
|
|
|
/* Applied output; the frame carries pad + host extras. */
|
|
CHECK(mod_pad_transform_run(0, &f, NULL, &o) == 1);
|
|
CHECK(seen.player == 0 && seen.host_rt == 200 && seen.type == PSX_MOD_PAD_DUALSHOCK);
|
|
CHECK(o.type == PSX_MOD_PAD_NEGCON && o.buttons == 0xffffu &&
|
|
o.negcon_i == 200 && o.negcon_ii == 30 && o.negcon_l == 0);
|
|
CHECK(o.lx == 0x40 && o.rx == 0x90); /* pre-filled pass-through */
|
|
|
|
/* Decline -> exact pass-through. */
|
|
mode = 1;
|
|
CHECK(mod_pad_transform_run(0, &f, NULL, &o) == 1);
|
|
CHECK(o.type == PSX_MOD_PAD_DUALSHOCK && o.buttons == 0xfbffu &&
|
|
o.lx == 0x40 && o.ry == 0x70 && o.negcon_i == 0);
|
|
|
|
/* Decline with a stock pad -> that stock pad, not the frame (a digital
|
|
* player's frame is its host pad; the stock pad keeps the fold). */
|
|
{
|
|
PSXModPadOutput stock;
|
|
memset(&stock, 0, sizeof stock);
|
|
stock.struct_size = sizeof stock;
|
|
stock.buttons = 0xff7fu; stock.type = PSX_MOD_PAD_DIGITAL;
|
|
stock.lx = stock.ly = stock.rx = stock.ry = 0x80;
|
|
CHECK(mod_pad_transform_run(0, &f, &stock, &o) == 1);
|
|
CHECK(memcmp(&o, &stock, sizeof o) == 0);
|
|
mode = 0; /* applied: pre-filled from the stock pad */
|
|
CHECK(mod_pad_transform_run(0, &f, &stock, &o) == 1);
|
|
CHECK(o.type == PSX_MOD_PAD_NEGCON && o.lx == 0x80 && o.negcon_i == 200);
|
|
mode = 1;
|
|
}
|
|
|
|
/* Invalid output (range, disallowed type, struct size) -> neutral of the
|
|
* frame's type. */
|
|
for (mode = 2; mode <= 4; mode++) {
|
|
CHECK(mod_pad_transform_run(0, &f, NULL, &o) == 1);
|
|
CHECK(o.type == PSX_MOD_PAD_DUALSHOCK && o.buttons == 0xffffu &&
|
|
o.lx == 0x80 && o.ry == 0x80 && o.negcon_i == 0);
|
|
}
|
|
|
|
/* Detach: one neutral release frame, then nothing; the callback is not
|
|
* called for the release. */
|
|
mode = 0;
|
|
CHECK(psx_mod_set_pad_transform(0, NULL));
|
|
calls = 0;
|
|
CHECK(mod_pad_transform_run(0, &f, NULL, &o) == 1);
|
|
CHECK(calls == 0 && o.buttons == 0xffffu && o.type == PSX_MOD_PAD_DUALSHOCK &&
|
|
o.lx == 0x80);
|
|
CHECK(mod_pad_transform_run(0, &f, NULL, &o) == 0);
|
|
CHECK(mod_pad_transform_initial_type(0) == -1);
|
|
|
|
/* Session reset: same release for every registered player. */
|
|
CHECK(psx_mod_set_pad_transform(0, &t) && psx_mod_set_pad_transform(1, &t));
|
|
mod_pad_transform_reset();
|
|
CHECK(mod_pad_transform_run(0, &f, NULL, &o) == 1 && o.buttons == 0xffffu);
|
|
CHECK(mod_pad_transform_run(1, &f, NULL, &o) == 1 && o.buttons == 0xffffu);
|
|
CHECK(mod_pad_transform_run(0, &f, NULL, &o) == 0 && mod_pad_transform_run(1, &f, NULL, &o) == 0);
|
|
/* A reset with nothing registered owes nothing. */
|
|
mod_pad_transform_reset();
|
|
CHECK(mod_pad_transform_run(0, &f, NULL, &o) == 0);
|
|
fprintf(stderr, "mod_pad_transform: passed\n");
|
|
return 0;
|
|
}
|