swdc: add ffb and led emulation

This commit is contained in:
2024-09-30 20:23:28 +02:00
parent c06bb408e7
commit 2251585ef0
24 changed files with 872 additions and 146 deletions

View File

@ -13,6 +13,43 @@
#include "platform/config.h"
#include "platform/platform.h"
void led15070_config_load(struct led15070_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
wchar_t tmpstr[16];
cfg->enable = GetPrivateProfileIntW(L"led15070", L"enable", 1, filename);
cfg->port_no = GetPrivateProfileIntW(L"led15070", L"portNo", 0, filename);
cfg->fw_ver = GetPrivateProfileIntW(L"led15070", L"fwVer", 0x90, filename);
/* TODO: Unknown, no firmware file available */
cfg->fw_sum = GetPrivateProfileIntW(L"led15070", L"fwSum", 0xdead, filename);
GetPrivateProfileStringW(
L"led15070",
L"boardNumber",
L"15070-02",
tmpstr,
_countof(tmpstr),
filename);
size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number));
for (int i = n; i < sizeof(cfg->board_number); i++)
{
cfg->board_number[i] = ' ';
}
GetPrivateProfileStringW(
L"led15070",
L"eepromPath",
L"DEVICE",
cfg->eeprom_path,
_countof(cfg->eeprom_path),
filename);
}
void swdc_dll_config_load(
struct swdc_dll_config *cfg,
const wchar_t *filename)
@ -43,6 +80,7 @@ void swdc_hook_config_load(
dvd_config_load(&cfg->dvd, filename);
io4_config_load(&cfg->io4, filename);
vfd_config_load(&cfg->vfd, filename);
led15070_config_load(&cfg->led15070, filename);
}
void zinput_config_load(struct zinput_config *cfg, const wchar_t *filename)

View File

@ -4,6 +4,7 @@
#include <stddef.h>
#include "board/config.h"
#include "board/led15070.h"
#include "hooklib/dvd.h"
@ -17,7 +18,9 @@ struct swdc_hook_config {
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct ffb_config ffb;
struct vfd_config vfd;
struct led15070_config led15070;
struct swdc_dll_config dll;
struct zinput_config zinput;
};

View File

@ -31,6 +31,7 @@
#include "swdchook/config.h"
#include "swdchook/swdc-dll.h"
#include "swdchook/io4.h"
#include "swdchook/ffb.h"
#include "platform/platform.h"
@ -91,6 +92,19 @@ static DWORD CALLBACK swdc_pre_startup(void)
goto fail;
}
hr = swdc_ffb_hook_init(&swdc_hook_cfg.ffb, 1);
if (FAILED(hr)) {
goto fail;
}
hr = led15070_hook_init(&swdc_hook_cfg.led15070, swdc_dll.led_init,
swdc_dll.led_set_fet_output, NULL, swdc_dll.led_gs_update, 2, 1);
if (FAILED(hr)) {
goto fail;
}
/* Hook external DLL APIs */
zinput_hook_init(&swdc_hook_cfg.zinput);

59
swdchook/ffb.c Normal file
View File

@ -0,0 +1,59 @@
#include <windows.h>
#include <xinput.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/ffb.h"
#include "swdchook/swdc-dll.h"
#include "util/dprintf.h"
static void swdc_ffb_toggle(bool active);
static void swdc_ffb_constant_force(uint8_t direction, uint8_t force);
static void swdc_ffb_rumble(uint8_t force, uint8_t period);
static void swdc_ffb_damper(uint8_t force);
static const struct ffb_ops swdc_ffb_ops = {
.toggle = swdc_ffb_toggle,
.constant_force = swdc_ffb_constant_force,
.rumble = swdc_ffb_rumble,
.damper = swdc_ffb_damper
};
HRESULT swdc_ffb_hook_init(const struct ffb_config *cfg, unsigned int port_no)
{
HRESULT hr;
assert(swdc_dll.init != NULL);
hr = ffb_hook_init(cfg, &swdc_ffb_ops, port_no);
if (FAILED(hr)) {
return hr;
}
return swdc_dll.ffb_init();
}
static void swdc_ffb_toggle(bool active)
{
swdc_dll.ffb_toggle(active);
}
static void swdc_ffb_constant_force(uint8_t direction, uint8_t force)
{
swdc_dll.ffb_constant_force(direction, force);
}
static void swdc_ffb_rumble(uint8_t force, uint8_t period)
{
swdc_dll.ffb_rumble(force, period);
}
static void swdc_ffb_damper(uint8_t force)
{
swdc_dll.ffb_damper(force);
}

7
swdchook/ffb.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "board/ffb.h"
HRESULT swdc_ffb_hook_init(const struct ffb_config *cfg, unsigned int port_no);

View File

@ -15,10 +15,12 @@ static HRESULT init_mmf(void);
static void swdc_set_gamebtns(uint16_t value);
static HRESULT swdc_io4_poll(void *ctx, struct io4_state *state);
static HRESULT swdc_io4_write_gpio(uint8_t* payload, size_t len);
static uint16_t coins;
static const struct io4_ops swdc_io4_ops = {
.poll = swdc_io4_poll,
.poll = swdc_io4_poll,
.write_gpio = swdc_io4_write_gpio
};
HRESULT swdc_io4_hook_init(const struct io4_config *cfg) {
@ -172,3 +174,34 @@ static HRESULT swdc_io4_poll(void *ctx, struct io4_state *state) {
return S_OK;
}
static HRESULT swdc_io4_write_gpio(uint8_t* payload, size_t len)
{
// Just fast fail if there aren't enough bytes in the payload
if (len < 3)
return S_OK;
// This command is used for lights in SWDC, but it only contains button lights,
// and only in the first 3 bytes of the payload; everything else is padding to
// make the payload 62 bytes. The rest of the cabinet lights and the side button
// lights are handled separately, by the 15070 lights controller.
uint32_t lights_data = (uint32_t) ((uint8_t)(payload[0]) << 24 |
(uint8_t)(payload[1]) << 16 |
(uint8_t)(payload[2]) << 8);
// Since Sega uses an odd ordering for the first part of the bitfield,
// let's normalize the data and just send over bytes for the receiver
// to interpret as ON/OFF values.
uint8_t rgb_out[6] = {
lights_data & SWDC_IO_LED_START ? 0xFF : 0x00,
lights_data & SWDC_IO_LED_VIEW_CHANGE ? 0xFF : 0x00,
lights_data & SWDC_IO_LED_UP ? 0xFF : 0x00,
lights_data & SWDC_IO_LED_DOWN ? 0xFF : 0x00,
lights_data & SWDC_IO_LED_RIGHT ? 0xFF : 0x00,
lights_data & SWDC_IO_LED_LEFT ? 0xFF : 0x00,
};
swdc_dll.led_set_leds(rgb_out);
return S_OK;
}

View File

@ -28,5 +28,7 @@ shared_library(
'io4.h',
'zinput.c',
'zinput.h',
'ffb.c',
'ffb.h'
],
)

View File

@ -21,6 +21,33 @@ const struct dll_bind_sym swdc_dll_syms[] = {
}, {
.sym = "swdc_io_get_analogs",
.off = offsetof(struct swdc_dll, get_analogs),
}, {
.sym = "swdc_io_led_init",
.off = offsetof(struct swdc_dll, led_init),
}, {
.sym = "swdc_io_led_set_fet_output",
.off = offsetof(struct swdc_dll, led_set_fet_output),
}, {
.sym = "swdc_io_led_gs_update",
.off = offsetof(struct swdc_dll, led_gs_update),
}, {
.sym = "swdc_io_led_set_leds",
.off = offsetof(struct swdc_dll, led_set_leds),
}, {
.sym = "swdc_io_ffb_init",
.off = offsetof(struct swdc_dll, ffb_init),
}, {
.sym = "swdc_io_ffb_toggle",
.off = offsetof(struct swdc_dll, ffb_toggle),
}, {
.sym = "swdc_io_ffb_constant_force",
.off = offsetof(struct swdc_dll, ffb_constant_force),
}, {
.sym = "swdc_io_ffb_rumble",
.off = offsetof(struct swdc_dll, ffb_rumble),
}, {
.sym = "swdc_io_ffb_damper",
.off = offsetof(struct swdc_dll, ffb_damper),
}
};

View File

@ -10,6 +10,15 @@ struct swdc_dll {
void (*get_opbtns)(uint8_t *opbtn);
void (*get_gamebtns)(uint16_t *gamebtn);
void (*get_analogs)(struct swdc_io_analog_state *out);
HRESULT (*led_init)(void);
void (*led_set_fet_output)(const uint8_t *rgb);
void (*led_gs_update)(const uint8_t *rgb);
void (*led_set_leds)(const uint8_t *rgb);
HRESULT (*ffb_init)(void);
void (*ffb_toggle)(bool active);
void (*ffb_constant_force)(uint8_t direction, uint8_t force);
void (*ffb_rumble)(uint8_t period, uint8_t force);
void (*ffb_damper)(uint8_t force);
};
struct swdc_dll_config {

View File

@ -16,3 +16,12 @@ EXPORTS
swdc_io_get_opbtns
swdc_io_get_gamebtns
swdc_io_get_analogs
swdc_io_led_init
swdc_io_led_set_fet_output
swdc_io_led_gs_update
swdc_io_led_set_leds
swdc_io_ffb_init
swdc_io_ffb_toggle
swdc_io_ffb_constant_force
swdc_io_ffb_rumble
swdc_io_ffb_damper