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>
113 lines
4.0 KiB
C
113 lines
4.0 KiB
C
#include "mod_pad_transform.h"
|
|
#include "sio.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define PAD_TYPE_ALL (PSX_MOD_PAD_TYPE_BIT(PSX_MOD_PAD_DIGITAL) | \
|
|
PSX_MOD_PAD_TYPE_BIT(PSX_MOD_PAD_DUALSHOCK) | \
|
|
PSX_MOD_PAD_TYPE_BIT(PSX_MOD_PAD_JOGCON) | \
|
|
PSX_MOD_PAD_TYPE_BIT(PSX_MOD_PAD_NEGCON))
|
|
#define BAD_LOG_INTERVAL 600u
|
|
|
|
static PSXModPadTransform s_xf[PSX_MAX_PLAYERS];
|
|
static uint8_t s_active[PSX_MAX_PLAYERS];
|
|
static uint8_t s_release[PSX_MAX_PLAYERS];
|
|
static uint32_t s_bad_count[PSX_MAX_PLAYERS];
|
|
|
|
int psx_mod_set_pad_transform(uint32_t player, const PSXModPadTransform *xf) {
|
|
if (player >= PSX_MAX_PLAYERS) return 0;
|
|
if (!xf) {
|
|
if (s_active[player]) s_release[player] = 1;
|
|
s_active[player] = 0;
|
|
memset(&s_xf[player], 0, sizeof s_xf[player]);
|
|
return 1;
|
|
}
|
|
if (xf->struct_size != sizeof *xf || !xf->transform ||
|
|
!xf->allowed_types || (xf->allowed_types & ~PAD_TYPE_ALL) ||
|
|
xf->initial_type > PSX_MOD_PAD_NEGCON ||
|
|
!(xf->allowed_types & PSX_MOD_PAD_TYPE_BIT(xf->initial_type))) {
|
|
fprintf(stderr,
|
|
"psxrecomp: mod pad transform (player %u) rejected: struct_size %u "
|
|
"(expected %u), callback %s, allowed_types 0x%x, initial_type %u\n",
|
|
(unsigned)player, (unsigned)xf->struct_size, (unsigned)sizeof *xf,
|
|
xf->transform ? "set" : "NULL", (unsigned)xf->allowed_types,
|
|
(unsigned)xf->initial_type);
|
|
return 0;
|
|
}
|
|
s_xf[player] = *xf;
|
|
s_active[player] = 1;
|
|
s_release[player] = 0;
|
|
s_bad_count[player] = 0;
|
|
return 1;
|
|
}
|
|
|
|
void mod_pad_transform_reset(void) {
|
|
for (uint32_t i = 0; i < PSX_MAX_PLAYERS; i++) {
|
|
if (s_active[i]) s_release[i] = 1;
|
|
s_active[i] = 0;
|
|
}
|
|
memset(s_xf, 0, sizeof s_xf);
|
|
memset(s_bad_count, 0, sizeof s_bad_count);
|
|
}
|
|
|
|
int mod_pad_transform_initial_type(uint32_t player) {
|
|
if (player >= PSX_MAX_PLAYERS || !s_active[player]) return -1;
|
|
return (int)s_xf[player].initial_type;
|
|
}
|
|
|
|
static void pass_through(const PSXModPadFrame *f, PSXModPadOutput *o) {
|
|
memset(o, 0, sizeof *o);
|
|
o->struct_size = sizeof *o;
|
|
o->buttons = f->buttons;
|
|
o->type = f->type;
|
|
o->lx = f->lx; o->ly = f->ly; o->rx = f->rx; o->ry = f->ry;
|
|
}
|
|
|
|
static void neutral(uint32_t type, PSXModPadOutput *o) {
|
|
memset(o, 0, sizeof *o);
|
|
o->struct_size = sizeof *o;
|
|
o->buttons = 0xffff;
|
|
o->type = type;
|
|
o->lx = o->ly = o->rx = o->ry = 0x80;
|
|
}
|
|
|
|
static int output_valid(uint32_t allowed, const PSXModPadOutput *o) {
|
|
return o->struct_size == sizeof *o && o->buttons <= 0xffff &&
|
|
o->type <= PSX_MOD_PAD_NEGCON &&
|
|
(allowed & PSX_MOD_PAD_TYPE_BIT(o->type)) &&
|
|
o->lx <= 255 && o->ly <= 255 && o->rx <= 255 && o->ry <= 255 &&
|
|
o->negcon_i <= 255 && o->negcon_ii <= 255 && o->negcon_l <= 255;
|
|
}
|
|
|
|
int mod_pad_transform_run(uint32_t player, const PSXModPadFrame *frame,
|
|
const PSXModPadOutput *stock, PSXModPadOutput *out) {
|
|
if (!frame || !out || player >= PSX_MAX_PLAYERS) return 0;
|
|
if (s_release[player] && !s_active[player]) {
|
|
s_release[player] = 0;
|
|
neutral(frame->type, out);
|
|
return 1;
|
|
}
|
|
if (!s_active[player]) return 0;
|
|
PSXModPadOutput o, base;
|
|
if (stock) base = *stock;
|
|
else pass_through(frame, &base);
|
|
o = base;
|
|
if (!s_xf[player].transform(frame, &o)) {
|
|
*out = base;
|
|
return 1;
|
|
}
|
|
if (!output_valid(s_xf[player].allowed_types, &o)) {
|
|
if (s_bad_count[player]++ % BAD_LOG_INTERVAL == 0)
|
|
fprintf(stderr,
|
|
"psxrecomp: mod pad transform (player %u) returned an invalid "
|
|
"frame (struct_size %u, type %u, buttons 0x%x); delivering "
|
|
"neutral (%u so far)\n",
|
|
(unsigned)player, (unsigned)o.struct_size, (unsigned)o.type,
|
|
(unsigned)o.buttons, (unsigned)s_bad_count[player]);
|
|
neutral(frame->type, out);
|
|
return 1;
|
|
}
|
|
*out = o;
|
|
return 1;
|
|
}
|