love live io

This commit is contained in:
Hay1tsme
2025-06-07 15:09:37 -04:00
parent cf22a6542b
commit 0cbb535d81
19 changed files with 264 additions and 109 deletions
+9
View File
@@ -26,3 +26,12 @@ void k92x0249_config_load(struct k92x0249_config *cfg, const wchar_t *filename)
wcstombs_s(NULL, cfg->serial, sizeof(cfg->serial), wserial, sizeof(cfg->serial) - 1);
}
void k9101258_config_load(struct usbio_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
wchar_t wserial[17];
cfg->enabled = GetPrivateProfileIntW(L"usbio", L"enable", 1, filename);
}
+2
View File
@@ -5,6 +5,8 @@
#include "board/io4.h"
#include "board/k92x0249.h"
#include "board/k9101258.h"
void io4_config_load(struct io4_config *cfg, const wchar_t *filename);
void k92x0249_config_load(struct k92x0249_config *cfg, const wchar_t *filename);
void k9101258_config_load(struct usbio_config *cfg, const wchar_t *filename);
+36 -7
View File
@@ -21,14 +21,12 @@
// USB IO, A/B/C variants
static const uint8_t base_bfr[64] = {
0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // 6: gamebtns, 7: door sw, 4: game/opt btns
0xFF, 0xFF, 0xFF, 0x00, 0x35, 0x00, 0x00, 0x00, // 12 - coin count, 13 - coin jam (64)
//0xF0, 0xCA, 0x79, 0xE0, 0x08, 0xE3, 0x70, 0xE2,
0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, // 6: gamebtns, 7: door sw, 4: game/opt btns
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, // 12 - coin count, 13 - coin jam (64)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 - 31 possibly analog in?
//0x7C, 0xEB, 0x3C, 0xDB, 0x84, 0xDE, 0xF0, 0xD9,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 38/39 system buttons?
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reader stuff?
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // Reader stuff?
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
@@ -41,8 +39,16 @@ static const wchar_t usbio_path[] = L"$usbio";
static HANDLE usbio_fd;
static void *usbio_ops_ctx;
static struct usbio_ops ops = {
.digital_in = NULL,
.digital_out = NULL,
.analog_in = NULL,
.analog_out = NULL,
.reader_poll = NULL,
.reader_led_out = NULL,
};
HRESULT k9101258_hook_init()
HRESULT k9101258_hook_init(const struct usbio_ops *ioops, void *ctx)
{
HRESULT hr;
@@ -52,6 +58,9 @@ HRESULT k9101258_hook_init()
return hr;
}
ops.digital_in = ioops->digital_in;
usbio_ops_ctx = ctx;
// only idVendor, idProduct and bcdDevice are actually read it seems
struct _USB_DEVICE_DESCRIPTOR desc = {
.bLength = sizeof(USB_DEVICE_DESCRIPTOR),
@@ -92,7 +101,27 @@ HRESULT k9101258_hook_init()
static void read_pipe(void *ctx, uint8_t *bfr, uint32_t bfr_len, uint32_t *len_tx)
{
memcpy_s(bfr, bfr_len, base_bfr, sizeof(base_bfr));
*len_tx = 64;
*len_tx = sizeof(base_bfr);
uint32_t coinct = 0;
uint8_t buttons[4] = {0};
uint16_t analog[8] = {0};
uint8_t card_id[16] = {0};
if (ops.digital_in != NULL) {
ops.digital_in(usbio_ops_ctx, buttons, &coinct);
memcpy_s(bfr + 4, bfr_len - 4, buttons, sizeof(buttons));
bfr[12] = coinct % 255;
}
if (ops.analog_in != NULL) {
ops.analog_in(usbio_ops_ctx, analog);
}
if (ops.reader_poll != NULL) {
ops.reader_poll(usbio_ops_ctx, card_id);
memcpy_s(bfr + 48, bfr_len - 48, card_id, sizeof(card_id));
}
}
static void write_pipe(void *ctx, uint8_t *bfr, uint32_t bfr_len, uint32_t *len_tx)
+8 -10
View File
@@ -4,21 +4,19 @@
#include <stdbool.h>
#pragma pack(push,1)
struct usbio_config {
bool enabled;
};
// needs to handle digital and analog reads and writes, coin stuff, and
// having an ad-on device (usually a card reader) and maybe an SD card??
// Mayhaps move this to a seperate thing in the event a second USB IO
// providor is needed?
struct usbio_ops {
void (*digital_in)(void *ctx, uint32_t *out);
void (*digital_in)(void *ctx, uint8_t out[4], uint32_t *coin_ct);
void (*digital_out)(void *ctx); // TODO: digital outputs
void (*analog_in)(void *ctx, uint16_t out[8]);
bool (*is_coin_pressed)(void *ctx);
void (*reader_poll)(void *ctx); // TODO: reader struct?
void (*reader_led_out)(void *ctx); // TODO: reader struct?
void (*analog_out)(void *ctx); // TODO: confirm we even have analog out lol
void (*reader_poll)(void *ctx, uint8_t card_id[16]);
void (*reader_led_out)(void *ctx, uint32_t rgb);
};
#pragma pack(pop)
HRESULT k9101258_hook_init();
HRESULT k9101258_hook_init(const struct usbio_ops *ioops, void *ctx);
+1
View File
@@ -33,4 +33,5 @@ void ll3_hook_config_load(
ll3_dll_config_load(&cfg->dll, filename);
gfx_config_load(&cfg->gfx, filename);
printer_config_load(&cfg->printer, filename);
k9101258_config_load(&cfg->usbio, filename);
}
+1
View File
@@ -14,6 +14,7 @@ struct ll3_hook_config {
struct ll3_dll_config dll;
struct gfx_config gfx;
struct printer_config printer;
struct usbio_config usbio;
};
void ll3_hook_config_load(
+6 -5
View File
@@ -14,6 +14,7 @@
#include "ll3hook/config.h"
#include "ll3hook/ll3-dll.h"
#include "ll3hook/usbio.h"
#include "platform/platform.h"
@@ -43,11 +44,6 @@ static DWORD CALLBACK ll3_pre_startup(void)
printer_hook_init(&ll3_hook_cfg.printer, 0, ll3_hook_mod);
hr = k9101258_hook_init();
if (FAILED(hr)) {
goto fail;
}
/* Initialize emulation hooks */
hr = platform_hook_init(
@@ -65,6 +61,11 @@ static DWORD CALLBACK ll3_pre_startup(void)
goto fail;
}
hr = ll3_usbio_init(&ll3_hook_cfg.usbio);
if (FAILED(hr)) {
goto fail;
}
/* Initialize debug helpers */
spike_hook_init(L".\\taitools.ini");
+2 -5
View File
@@ -13,11 +13,8 @@ const struct dll_bind_sym ll3_dll_syms[] = {
.sym = "ll3_io_init",
.off = offsetof(struct ll3_dll, init),
}, {
.sym = "ll3_io_read_coin_counter",
.off = offsetof(struct ll3_dll, read_coin_counter),
}, {
.sym = "ll3_io_get_btns",
.off = offsetof(struct ll3_dll, get_btns),
.sym = "ll3_io_poll",
.off = offsetof(struct ll3_dll, poll),
},
};
+1 -2
View File
@@ -7,8 +7,7 @@
struct ll3_dll {
uint16_t api_version;
HRESULT (*init)(void);
void (*read_coin_counter)(uint16_t *coins, uint16_t *services);
void (*get_btns)(uint8_t *btn, uint8_t *stick);
void (*poll)(uint16_t *game_btn, uint8_t *sys_btn, uint32_t *coinct);
};
struct ll3_dll_config {
+1 -2
View File
@@ -3,5 +3,4 @@ LIBRARY ll3hook
EXPORTS
ll3_io_get_api_version
ll3_io_init
ll3_io_read_coin_counter
ll3_io_get_btns
ll3_io_poll
+2
View File
@@ -22,5 +22,7 @@ shared_library(
'dllmain.c',
'll3-dll.c',
'll3-dll.h',
'usbio.c',
'usbio.h',
],
)
+90
View File
@@ -0,0 +1,90 @@
#include <assert.h>
#include <stdint.h>
#include "ll3hook/usbio.h"
#include "ll3hook/ll3-dll.h"
#include "ll3io/ll3io.h"
#include "util/dprintf.h"
static void get_buttons(void *ctx, uint8_t out[4], uint32_t *coin_ct);
HRESULT ll3_usbio_init(const struct usbio_config *cfg)
{
if (!cfg->enabled) {
return S_FALSE;
}
ll3_dll.init();
struct usbio_ops ops = {
.digital_in = get_buttons,
.digital_out = NULL,
.analog_in = NULL,
.analog_out = NULL,
.reader_poll = NULL,
.reader_led_out = NULL,
};
return k9101258_hook_init(&ops, NULL);
}
static void get_buttons(void *ctx, uint8_t out[4], uint32_t *coin_ct)
{
uint16_t gamebtn = 0;
uint8_t sysbtn = 0;
ll3_dll.poll(&gamebtn, &sysbtn, coin_ct);
// Door switches
out[3] |= 0xFC;
if (sysbtn & LL3_SYSBTN_TEST) {
out[0] |= 0x20;
}
if (sysbtn & LL3_SYSBTN_SERVICE) {
out[0] |= 0x40;
}
if (sysbtn & LL3_SYSBTN_SELECT) {
out[0] |= 0x10;
}
if (sysbtn & LL3_SYSBTN_ENTER) {
out[2] |= 0x80;
}
if (sysbtn & LL3_SYSBTN_COIN) {
out[0] |= 0x80;
}
if (gamebtn & LL3_BTN1) {
out[2] |= 0x01;
}
if (gamebtn & LL3_BTN2) {
out[2] |= 0x02;
}
if (gamebtn & LL3_BTN3) {
out[2] |= 0x04;
}
if (gamebtn & LL3_BTN4) {
out[2] |= 0x08;
}
if (gamebtn & LL3_BTN5) {
out[2] |= 0x10;
}
if (gamebtn & LL3_BTN6) {
out[2] |= 0x20;
}
if (gamebtn & LL3_BTN7) {
out[2] |= 0x40;
}
if (gamebtn & LL3_BTN8) {
out[0] |= 0x01;
}
if (gamebtn & LL3_BTN9) {
out[0] |= 0x02;
}
if (gamebtn & LL3_BTN_CANCEL) {
out[0] |= 0x04;
}
if (gamebtn & LL3_BTN_DECIDE) {
out[0] |= 0x08;
}
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include <windows.h>
#include "board/k9101258.h"
HRESULT ll3_usbio_init(const struct usbio_config *cfg);
+17 -19
View File
@@ -8,23 +8,21 @@
void ll3_io_config_load(struct ll3_input_config *cfg, const wchar_t *filename)
{
cfg->test = GetPrivateProfileIntW(L"jvs", L"test", VK_HOME, filename);
cfg->service = GetPrivateProfileIntW(L"jvs", L"service", VK_DELETE, filename);
cfg->coin = GetPrivateProfileIntW(L"jvs", L"coin", VK_INSERT, filename);
cfg->test = GetPrivateProfileIntW(L"usbio", L"test", VK_HOME, filename);
cfg->service = GetPrivateProfileIntW(L"usbio", L"service", VK_DELETE, filename);
cfg->select = GetPrivateProfileIntW(L"usbio", L"select", VK_END, filename);
cfg->enter = GetPrivateProfileIntW(L"usbio", L"enter", VK_RSHIFT, filename);
cfg->coin = GetPrivateProfileIntW(L"usbio", L"coin", VK_INSERT, filename);
cfg->is_xinput = GetPrivateProfileIntW(L"deck", L"deck", 0, filename);
cfg->xinput_player = GetPrivateProfileIntW(L"deck", L"controller_num", 0, filename);
cfg->btn_l = GetPrivateProfileIntW(L"deck", L"left_button", 'C', filename);
cfg->btn_r = GetPrivateProfileIntW(L"deck", L"right_button", 'N', filename);
cfg->stick_l_up = GetPrivateProfileIntW(L"deck", L"left_stick_up", 'W', filename);
cfg->stick_l_right = GetPrivateProfileIntW(L"deck", L"left_stick_right", 'D', filename);
cfg->stick_l_down = GetPrivateProfileIntW(L"deck", L"left_stick_down", 'S', filename);
cfg->stick_l_left = GetPrivateProfileIntW(L"deck", L"left_stick_left", 'A', filename);
cfg->stick_r_up = GetPrivateProfileIntW(L"deck", L"right_stick_up", 'I', filename);
cfg->stick_r_right = GetPrivateProfileIntW(L"deck", L"right_stick_right", 'L', filename);
cfg->stick_r_down = GetPrivateProfileIntW(L"deck", L"right_stick_down", 'K', filename);
cfg->stick_r_left = GetPrivateProfileIntW(L"deck", L"right_stick_left", 'J', filename);
}
cfg->btn1 = GetPrivateProfileIntW(L"usbio", L"rhythm_1", '1', filename);
cfg->btn2 = GetPrivateProfileIntW(L"usbio", L"rhythm_2", '2', filename);
cfg->btn3 = GetPrivateProfileIntW(L"usbio", L"rhythm_3", '3', filename);
cfg->btn4 = GetPrivateProfileIntW(L"usbio", L"rhythm_4", '4', filename);
cfg->btn5 = GetPrivateProfileIntW(L"usbio", L"rhythm_5", '5', filename);
cfg->btn6 = GetPrivateProfileIntW(L"usbio", L"rhythm_6", '6', filename);
cfg->btn7 = GetPrivateProfileIntW(L"usbio", L"rhythm_7", '7', filename);
cfg->btn8 = GetPrivateProfileIntW(L"usbio", L"rhythm_8", '8', filename);
cfg->btn9 = GetPrivateProfileIntW(L"usbio", L"rhythm_9", '9', filename);
cfg->btn_decide = GetPrivateProfileIntW(L"usbio", L"decide", '-', filename);
cfg->btn_cancel = GetPrivateProfileIntW(L"usbio", L"cancel", '=', filename);
}
+13 -14
View File
@@ -5,23 +5,22 @@
#pragma pack(push, 1)
struct ll3_input_config {
uint8_t is_xinput;
uint8_t xinput_player;
uint8_t test;
uint8_t service;
uint8_t select;
uint8_t enter;
uint8_t coin;
uint8_t btn_l;
uint8_t btn_r;
uint8_t stick_l_up;
uint8_t stick_l_right;
uint8_t stick_l_down;
uint8_t stick_l_left;
uint8_t stick_r_up;
uint8_t stick_r_right;
uint8_t stick_r_down;
uint8_t stick_r_left;
uint8_t btn1;
uint8_t btn2;
uint8_t btn3;
uint8_t btn4;
uint8_t btn5;
uint8_t btn6;
uint8_t btn7;
uint8_t btn8;
uint8_t btn9;
uint8_t btn_decide;
uint8_t btn_cancel;
};
#pragma pack(pop)
+50 -17
View File
@@ -12,8 +12,7 @@
static bool ll3_io_coin = false;
static bool ll3_io_service = false;
static uint16_t ll3_coin_ct = 0;
static uint16_t ll3_service_ct = 0;
static uint32_t ll3_coin_ct = 0;
static struct ll3_input_config cfg;
uint16_t ll3_io_get_api_version(void)
@@ -28,20 +27,10 @@ HRESULT ll3_io_init(void)
return S_OK;
}
void ll3_io_get_btns(uint8_t *btn, uint8_t *stick)
{
if (GetAsyncKeyState(cfg.test) & 0x8000) {
*btn |= ll3_BTN_TEST;
}
if (GetAsyncKeyState(cfg.service) & 0x8000) {
*btn |= ll3_BTN_SERVICE;
}
}
void ll3_io_read_coin_counter(uint16_t *coins, uint16_t *services)
void ll3_io_poll(uint16_t *game_btn, uint8_t *sys_btn, uint32_t *coinct)
{
if (GetAsyncKeyState(cfg.coin) & 0x8000) {
*sys_btn |= LL3_SYSBTN_COIN;
if (!ll3_io_coin) {
ll3_io_coin = true;
ll3_coin_ct++;
@@ -51,14 +40,58 @@ void ll3_io_read_coin_counter(uint16_t *coins, uint16_t *services)
}
if (GetAsyncKeyState(cfg.service) & 0x8000) {
*sys_btn |= LL3_SYSBTN_SERVICE;
if (!ll3_io_service) {
ll3_io_service = true;
ll3_service_ct++;
ll3_coin_ct++;
}
} else {
ll3_io_service = false;
}
*coins = ll3_coin_ct;
*services = ll3_service_ct;
*coinct = ll3_coin_ct;
if (GetAsyncKeyState(cfg.test) & 0x8000) {
*sys_btn |= LL3_SYSBTN_TEST;
}
if (GetAsyncKeyState(cfg.enter) & 0x8000) {
*sys_btn |= LL3_SYSBTN_ENTER;
}
if (GetAsyncKeyState(cfg.select) & 0x8000) {
*sys_btn |= LL3_SYSBTN_SELECT;
}
if (GetAsyncKeyState(cfg.btn1) & 0x8000) {
*game_btn |= LL3_BTN1;
}
if (GetAsyncKeyState(cfg.btn2) & 0x8000) {
*game_btn |= LL3_BTN2;
}
if (GetAsyncKeyState(cfg.btn3) & 0x8000) {
*game_btn |= LL3_BTN3;
}
if (GetAsyncKeyState(cfg.btn4) & 0x8000) {
*game_btn |= LL3_BTN4;
}
if (GetAsyncKeyState(cfg.btn5) & 0x8000) {
*game_btn |= LL3_BTN5;
}
if (GetAsyncKeyState(cfg.btn6) & 0x8000) {
*game_btn |= LL3_BTN6;
}
if (GetAsyncKeyState(cfg.btn7) & 0x8000) {
*game_btn |= LL3_BTN7;
}
if (GetAsyncKeyState(cfg.btn8) & 0x8000) {
*game_btn |= LL3_BTN8;
}
if (GetAsyncKeyState(cfg.btn9) & 0x8000) {
*game_btn |= LL3_BTN9;
}
if (GetAsyncKeyState(cfg.btn_cancel) & 0x8000) {
*game_btn |= LL3_BTN_CANCEL;
}
if (GetAsyncKeyState(cfg.btn_decide) & 0x8000) {
*game_btn |= LL3_BTN_DECIDE;
}
}
+17 -27
View File
@@ -5,22 +5,25 @@
#include <stdint.h>
enum {
ll3_BTN_TEST = 0x01,
ll3_BTN_SERVICE = 0x02,
ll3_BTN_COIN = 0x03,
ll3_BTN_LEFT = 0x04,
ll3_BTN_RIGHT = 0x05,
LL3_SYSBTN_TEST = 0x01,
LL3_SYSBTN_SERVICE = 0x02,
LL3_SYSBTN_COIN = 0x04,
LL3_SYSBTN_ENTER = 0x08,
LL3_SYSBTN_SELECT = 0x10,
};
enum {
ll3_STICK_L_UP = 0x01,
ll3_STICK_L_RIGHT = 0x02,
ll3_STICK_L_DOWN = 0x03,
ll3_STICK_L_LEFT = 0x04,
ll3_STICK_R_UP = 0x05,
ll3_STICK_R_RIGHT = 0x06,
ll3_STICK_R_DOWN = 0x07,
ll3_STICK_R_LEFT = 0x08,
LL3_BTN1 = 0x0001,
LL3_BTN2 = 0x0002,
LL3_BTN3 = 0x0004,
LL3_BTN4 = 0x0008,
LL3_BTN5 = 0x0010,
LL3_BTN6 = 0x0020,
LL3_BTN7 = 0x0040,
LL3_BTN8 = 0x0080,
LL3_BTN9 = 0x0100,
LL3_BTN_DECIDE = 0x0200,
LL3_BTN_CANCEL = 0x0400,
};
/* Get the version of the Theatrhythm IO API that this DLL supports. This
@@ -41,17 +44,4 @@ uint16_t ll3_io_get_api_version(void);
HRESULT ll3_io_init(void);
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
ll3_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into
a left hand side set of inputs and a right hand side set of inputs: the bit
mappings are the same in both cases.
All buttons are active-high, even though some buttons' electrical signals
on a real cabinet are active-low.
Minimum API version: 0x0100 */
void ll3_io_get_btns(uint8_t *btn, uint8_t *stick);
void ll3_io_read_coin_counter(uint16_t *coins, uint16_t *services);
void ll3_io_poll(uint16_t *game_btn, uint8_t *sys_btn, uint32_t *coinct);
+1 -1
View File
@@ -42,7 +42,7 @@ HRESULT ri_init(uint8_t dev_num, ri_mouse_cb_t in_mouse_cb, ri_kb_cb_t in_kb_cb,
DWORD err = 0;
PRAWINPUTDEVICELIST ri_devs = NULL;
uint32_t num_final_devs = 0;
PRI_DEVICE final_dev_list;
PRI_DEVICE final_dev_list = NULL;
mouse_cb = in_mouse_cb;
kb_cb = in_kb_cb;
+1
View File
@@ -1,6 +1,7 @@
#include <windows.h>
#include <assert.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>