adds example libaio ios

This commit is contained in:
Will Xyen
2025-06-19 03:30:20 -07:00
parent 2a71845df7
commit a00e3b82c7
19 changed files with 1261 additions and 0 deletions

View File

@@ -160,6 +160,7 @@ include src/main/iidxio-async/Module.mk
include src/main/iidxio-bio2/Module.mk
include src/main/iidxio-ezusb/Module.mk
include src/main/iidxio-ezusb2/Module.mk
include src/main/iidxio-lightning/Module.mk
include src/main/iidxio/Module.mk
include src/main/iidxiotest/Module.mk
include src/main/imgui/Module.mk
@@ -195,6 +196,8 @@ include src/main/sdvxhook2-cn/Module.mk
include src/main/sdvxhook2/Module.mk
include src/main/sdvxio-bio2/Module.mk
include src/main/sdvxio-kfca/Module.mk
include src/main/sdvxio-tbs/Module.mk
include src/main/sdvxio-valk/Module.mk
include src/main/sdvxio/Module.mk
include src/main/security/Module.mk
include src/main/unicorntail/Module.mk
@@ -490,6 +493,7 @@ $(zipdir)/iidx-hwio-x64.zip: \
build/bin/indep-64/iidxio-bio2.dll \
build/bin/indep-64/iidxio-ezusb.dll \
build/bin/indep-64/iidxio-ezusb2.dll \
build/bin/indep-64/iidxio-lightning.dll \
dist/iidx/iidxio-bio2.conf \
build/bin/indep-64/vigem-iidxio.exe \
dist/iidx/vigem-iidxio.conf \
@@ -644,6 +648,8 @@ $(zipdir)/sdvx-hwio-x64.zip: \
build/bin/indep-64/eamio-icca.dll \
build/bin/indep-64/sdvxio-kfca.dll \
build/bin/indep-64/sdvxio-bio2.dll \
build/bin/indep-64/sdvxio-tbs.dll \
build/bin/indep-64/sdvxio-valk.dll \
build/bin/indep-64/vigem-sdvxio.exe \
| $(zipdir)/
$(V)echo ... $@

View File

@@ -0,0 +1,12 @@
dlls += iidxio-lightning
ldflags_iidxio-lightning := \
-lsetupapi \
libs_iidxio-lightning := \
cconfig \
util \
src_iidxio-lightning := \
iidxio.c \
load_aio.c \

View File

@@ -0,0 +1,48 @@
#pragma once
#include <stdint.h>
struct AIO_NODE;
struct AIO_NMGR;
struct AIO_SCI;
#pragma pack(push, 1)
struct AIO_IOB2_BI2X_TDJ__DEVSTATUS {
uint8_t unk1[4];
uint8_t b_test;
uint8_t b_service;
uint8_t b_coinmech;
uint8_t unk2;
uint8_t b_start[2];
uint8_t b_vefx;
uint8_t b_effect;
uint8_t b_headphone[2];
uint8_t unk3[6]; // 0x10-0x13
uint8_t a_turntable[2];
uint8_t unk4;
uint8_t unk6[4];
uint8_t b_p1[7];
uint8_t b_p2[7];
uint8_t unk7[7];
uint8_t unk8[0x9A]; // 0x30-0xC9
};
#pragma pack(pop)
_Static_assert(
sizeof(struct AIO_IOB2_BI2X_TDJ__DEVSTATUS) == 202,
"bi2x_device_status is the wrong size");
typedef void (*t_aioNodeCtl_UpdateDevicesStatus)(void);
typedef unsigned int (*t_aioNodeCtl_Destroy)(struct AIO_NODE *node);
typedef unsigned int (*t_aioNodeMgr_Destroy)(struct AIO_NMGR *mgr);
typedef unsigned int (*t_aioSci_Destroy)(struct AIO_SCI *sci);
typedef struct AIO_SCI* (*t_aioIob2Bi2x_OpenSciUsbCdc)(uint8_t unk);
typedef struct AIO_NMGR* (*t_aioNMgrIob2_Create)(struct AIO_SCI *port, unsigned int delay);
typedef struct AIO_NODE* (*t_aioIob2Bi2xTDJ_Create)(struct AIO_NMGR *node_mgr, uint8_t param);
typedef void (*t_aioIob2Bi2xTDJ_IoReset)(struct AIO_NODE *AIO_IOB2_BI2X_TDJ_this, int value);
typedef void (*t_AIO_IOB2_BI2X_TDJ__GetDeviceStatus)(struct AIO_NODE *AIO_IOB2_BI2X_TDJ_this, struct AIO_IOB2_BI2X_TDJ__DEVSTATUS *status);
typedef void (*t_AIO_IOB2_BI2X_TDJ__SetTurnTableResist)(struct AIO_NODE *AIO_IOB2_BI2X_TDJ_this, int side, uint8_t resist);

View File

@@ -0,0 +1,18 @@
LIBRARY iidxio
EXPORTS
iidx_io_ep1_send
iidx_io_ep1_set_deck_lights
iidx_io_ep1_set_panel_lights
iidx_io_ep1_set_top_lamps
iidx_io_ep1_set_top_neons
iidx_io_ep2_get_keys
iidx_io_ep2_get_panel
iidx_io_ep2_get_sys
iidx_io_ep2_get_slider
iidx_io_ep2_get_turntable
iidx_io_ep2_recv
iidx_io_ep3_write_16seg
iidx_io_fini
iidx_io_init
iidx_io_set_loggers

View File

@@ -0,0 +1,205 @@
#include <windows.h>
#define LOG_MODULE "iidxio-lightning"
#include <stdatomic.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "bemanitools/glue.h"
#include "bemanitools/iidxio.h"
#include "util/log.h"
#include "load_aio.h"
static atomic_bool running;
static atomic_bool processing_io;
static struct AIO_IOB2_BI2X_TDJ__DEVSTATUS pin_cur;
static struct bi2x_ctx *bi2x_ctx;
static bool _bio2_iidx_io_poll(struct AIO_IOB2_BI2X_TDJ__DEVSTATUS *pin)
{
if (!running) {
return false;
}
processing_io = true;
poll_bi2x(bi2x_ctx, pin);
processing_io = false;
return true;
}
void iidx_io_set_loggers(
log_formatter_t misc,
log_formatter_t info,
log_formatter_t warning,
log_formatter_t fatal)
{
log_to_external(misc, info, warning, fatal);
}
bool iidx_io_init(
thread_create_t thread_create,
thread_join_t thread_join,
thread_destroy_t thread_destroy)
{
init_aio_handles();
bi2x_ctx = setup_bi2x();
if (!bi2x_ctx) {
log_warning("Unable to start BI2X?");
return false;
}
running = true;
return running;
}
void iidx_io_fini(void)
{
running = false;
while (processing_io) {
// avoid banging
Sleep(1);
}
close_bi2x(bi2x_ctx);
bi2x_ctx = NULL;
}
void iidx_io_ep1_set_deck_lights(uint16_t deck_lights)
{
return;
}
void iidx_io_ep1_set_panel_lights(uint8_t panel_lights)
{
return;
}
void iidx_io_ep1_set_top_lamps(uint8_t top_lamps)
{
return;
}
void iidx_io_ep1_set_top_neons(bool top_neons)
{
return;
}
bool iidx_io_ep1_send(void)
{
return true;
}
bool iidx_io_ep2_recv(void)
{
if (!_bio2_iidx_io_poll(&pin_cur)) {
return false;
}
return true;
}
uint8_t iidx_io_ep2_get_turntable(uint8_t player_no)
{
switch (player_no) {
case 0:
return pin_cur.a_turntable[0];
case 1:
return pin_cur.a_turntable[1];
default:
return 0;
}
}
uint8_t iidx_io_ep2_get_slider(uint8_t slider_no)
{
return 0;
}
uint8_t iidx_io_ep2_get_sys(void)
{
uint8_t state;
state = 0;
if (pin_cur.b_test) {
state |= (1 << IIDX_IO_SYS_TEST);
}
if (pin_cur.b_service) {
state |= (1 << IIDX_IO_SYS_SERVICE);
}
if (pin_cur.b_coinmech) {
state |= (1 << IIDX_IO_SYS_COIN);
}
return state;
}
uint8_t iidx_io_ep2_get_panel(void)
{
uint8_t state;
state = 0;
if (pin_cur.b_start[0]) {
state |= (1 << IIDX_IO_PANEL_P1_START);
}
if (pin_cur.b_start[1]) {
state |= (1 << IIDX_IO_PANEL_P2_START);
}
if (pin_cur.b_vefx) {
state |= (1 << IIDX_IO_PANEL_VEFX);
}
if (pin_cur.b_effect) {
state |= (1 << IIDX_IO_PANEL_EFFECT);
}
return state;
}
uint16_t iidx_io_ep2_get_keys(void)
{
uint16_t state;
state = 0;
state |= ((pin_cur.b_p1[0] > 0) << IIDX_IO_KEY_P1_1);
state |= ((pin_cur.b_p1[1] > 0) << IIDX_IO_KEY_P1_2);
state |= ((pin_cur.b_p1[2] > 0) << IIDX_IO_KEY_P1_3);
state |= ((pin_cur.b_p1[3] > 0) << IIDX_IO_KEY_P1_4);
state |= ((pin_cur.b_p1[4] > 0) << IIDX_IO_KEY_P1_5);
state |= ((pin_cur.b_p1[5] > 0) << IIDX_IO_KEY_P1_6);
state |= ((pin_cur.b_p1[6] > 0) << IIDX_IO_KEY_P1_7);
state |= ((pin_cur.b_p2[0] > 0) << IIDX_IO_KEY_P2_1);
state |= ((pin_cur.b_p2[1] > 0) << IIDX_IO_KEY_P2_2);
state |= ((pin_cur.b_p2[2] > 0) << IIDX_IO_KEY_P2_3);
state |= ((pin_cur.b_p2[3] > 0) << IIDX_IO_KEY_P2_4);
state |= ((pin_cur.b_p2[4] > 0) << IIDX_IO_KEY_P2_5);
state |= ((pin_cur.b_p2[5] > 0) << IIDX_IO_KEY_P2_6);
state |= ((pin_cur.b_p2[6] > 0) << IIDX_IO_KEY_P2_7);
return state;
}
bool iidx_io_ep3_write_16seg(const char *text)
{
return true;
}

View File

@@ -0,0 +1,161 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#define LOG_MODULE "load_aio"
#include "util/log.h"
#include "load_aio.h"
#define DECLARE_MODULE_FUNCTION(NAME) t_ ## NAME NAME
struct aio_module {
// "libaio.dll"
DECLARE_MODULE_FUNCTION(aioNodeCtl_UpdateDevicesStatus);
DECLARE_MODULE_FUNCTION(aioNodeCtl_Destroy);
DECLARE_MODULE_FUNCTION(aioNodeMgr_Destroy);
DECLARE_MODULE_FUNCTION(aioSci_Destroy);
// "libaio-iob.dll"
DECLARE_MODULE_FUNCTION(aioIob2Bi2x_OpenSciUsbCdc);
DECLARE_MODULE_FUNCTION(aioNMgrIob2_Create);
// "libaio-iob2_video.dll"
DECLARE_MODULE_FUNCTION(aioIob2Bi2xTDJ_Create);
// technically the game uses the C++ one, but this one doesn't incur a penalty unlike the C version of AIO_IOB2_BI2X_TDJ__GetDeviceStatus
DECLARE_MODULE_FUNCTION(aioIob2Bi2xTDJ_IoReset);
DECLARE_MODULE_FUNCTION(AIO_IOB2_BI2X_TDJ__GetDeviceStatus);
DECLARE_MODULE_FUNCTION(AIO_IOB2_BI2X_TDJ__SetTurnTableResist);
};
void warn_module(const char *function_name) {
log_warning("Could not load %s!", function_name);
}
#define LOAD_MODULE_FUNCTION(TARGET, HANDLE, NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, #NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
#define LOAD_MODULE_FUNCTION_EX(TARGET, HANDLE, NAME, REAL_NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, REAL_NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
static struct aio_module module_handles;
void init_aio_handles(void) {
HMODULE h_aio = LoadLibrary("libaio.dll");
HMODULE h_aio_iob = LoadLibrary("libaio-iob.dll");
HMODULE h_aio_iob2_video = LoadLibrary("libaio-iob2_video.dll");
if (!h_aio) {
log_warning("Could not load libaio.dll!");
return;
}
if (!h_aio_iob) {
log_warning("Could not load libaio-iob.dll!");
return;
}
if (!h_aio_iob2_video) {
log_warning("Could not load libaio-iob2_video.dll!");
return;
}
// "libaio.dll"
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_UpdateDevicesStatus);
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_Destroy);
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeMgr_Destroy);
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioSci_Destroy);
// "libaio-iob.dll"
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioIob2Bi2x_OpenSciUsbCdc);
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioNMgrIob2_Create);
// "libaio-iob2_video.dll"
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xTDJ_Create);
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xTDJ_IoReset);
LOAD_MODULE_FUNCTION_EX(module_handles, h_aio_iob2_video, AIO_IOB2_BI2X_TDJ__GetDeviceStatus, "?GetDeviceStatus@AIO_IOB2_BI2X_TDJ@@QEBAXAEAUDEVSTATUS@1@@Z");
LOAD_MODULE_FUNCTION_EX(module_handles, h_aio_iob2_video, AIO_IOB2_BI2X_TDJ__SetTurnTableResist, "?SetTurnTableResist@AIO_IOB2_BI2X_TDJ@@QEAAXIE@Z");
log_misc("Done loading all libaio functions required.");
}
struct AIO_NMGR_VTABLE {
void *_dtor;
void *AddNodeCtl;
void *RemoveNodeCtl;
void *PacketCreate;
void *PacketSend;
void *QueryPktRecvTimeout;
void *PacketRecv;
void *_GetCommStatus;
void *_Reconnect;
void *_unk;
void (*BeginManage)(struct AIO_NMGR *);
};
struct AIO_NMGR {
struct AIO_NMGR_VTABLE *vtbl;
uint8_t data[0x9F0];
};
struct bi2x_ctx {
struct AIO_SCI *sci_usb;
struct AIO_NMGR *iob2_mgr;
struct AIO_NODE *iob_bi2x_node;
};
struct bi2x_ctx *setup_bi2x(void) {
struct bi2x_ctx *ctx = malloc(sizeof(struct bi2x_ctx));
ctx->sci_usb = module_handles.aioIob2Bi2x_OpenSciUsbCdc(0);
if (!ctx->sci_usb) {
log_warning("Unable to aioIob2Bi2x_OpenSciUsbCdc");
free(ctx);
return NULL;
}
ctx->iob2_mgr = module_handles.aioNMgrIob2_Create(ctx->sci_usb, 0);
if (!ctx->iob2_mgr) {
log_warning("Unable to aioNMgrIob2_Create");
free(ctx);
return NULL;
}
ctx->iob_bi2x_node = module_handles.aioIob2Bi2xTDJ_Create(ctx->iob2_mgr, 1u);
if (!ctx->iob_bi2x_node) {
log_warning("Unable to aioIob2Bi2xTDJ_Create?");
free(ctx);
return NULL;
}
ctx->iob2_mgr->vtbl->BeginManage(ctx->iob2_mgr);
log_misc("BeginManage iob2_mgr");
// never actually called
// module_handles.aioIob2Bi2xTDJ_IoReset(ctx->iob_bi2x_node, 0x11);
// TODO: set these
// module_handles.AIO_IOB2_BI2X_TDJ__SetTurnTableResist(ctx->iob_bi2x_node, 0, 127);
// module_handles.AIO_IOB2_BI2X_TDJ__SetTurnTableResist(ctx->iob_bi2x_node, 1, 127);
return ctx;
}
void close_bi2x(struct bi2x_ctx *ctx) {
if (ctx->iob_bi2x_node) {
module_handles.aioNodeCtl_Destroy(ctx->iob_bi2x_node);
}
if (ctx->iob2_mgr) {
module_handles.aioNodeMgr_Destroy(ctx->iob2_mgr);
}
if (ctx->sci_usb) {
module_handles.aioSci_Destroy(ctx->sci_usb);
}
}
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_TDJ__DEVSTATUS *status) {
module_handles.aioNodeCtl_UpdateDevicesStatus();
// technically we should do some checking here
module_handles.AIO_IOB2_BI2X_TDJ__GetDeviceStatus(ctx->iob_bi2x_node, status);
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "aio.h"
void init_aio_handles(void);
struct bi2x_ctx;
struct bi2x_ctx *setup_bi2x(void);
void close_bi2x(struct bi2x_ctx *ctx);
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_TDJ__DEVSTATUS *status);

View File

@@ -0,0 +1,12 @@
dlls += sdvxio-tbs
ldflags_sdvxio-tbs := \
-lsetupapi \
libs_sdvxio-tbs := \
cconfig \
util \
src_sdvxio-tbs := \
sdvxio.c \
load_aio.c \

72
src/main/sdvxio-tbs/aio.h Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
#include <stdint.h>
struct AIO_NODE;
struct AIO_NMGR;
struct AIO_SCI;
struct INPUT
{
char DevIoCounter;
char bExIoAErr;
char bExIoBErr;
char bPcPowerOn;
char bPcPowerCheck;
char CoinCount;
char bTest;
char bService;
char bCoinSw;
char bCoinJam;
char bHPDetect;
uint16_t StickX;
uint16_t StickY;
char bStickBtn;
char bTrigger1;
char bTrigger2;
char bButton0;
char bButton1;
char bButton2;
char bButton3;
};
struct INPUTDATA
{
char Data[10];
};
struct OUTPUTDATA
{
char Data[8];
};
struct IORESETDATA
{
char Data[4];
};
struct AIO_IOB2_BI2X_TBS__DEVSTATUS {
char InputCounter;
char OutputCounter;
char TapeLedCounter;
struct INPUT Input;
struct INPUTDATA InputData;
struct OUTPUTDATA OutputData;
struct IORESETDATA IoResetData;
};
_Static_assert(
sizeof(struct AIO_IOB2_BI2X_TBS__DEVSTATUS) == 50,
"bi2x_device_status is the wrong size");
typedef void (*t_aioNodeCtl_UpdateDevicesStatus)(void);
typedef unsigned int (*t_aioNodeCtl_Destroy)(struct AIO_NODE *node);
typedef unsigned int (*t_aioNodeMgr_Destroy)(struct AIO_NMGR *mgr);
typedef unsigned int (*t_aioSci_Destroy)(struct AIO_SCI *sci);
typedef struct AIO_SCI* (*t_aioIob2Bi2x_OpenSciUsbCdc)(uint8_t unk);
typedef struct AIO_NMGR* (*t_aioNMgrIob2_Create)(struct AIO_SCI *port, unsigned int delay);
typedef struct AIO_NODE* (*t_aioIob2Bi2xTBS_Create)(struct AIO_NMGR *node_mgr, uint8_t param);
typedef void (*t_aioIob2Bi2xTBS_IoReset)(struct AIO_NODE *i_pNodeCtl, int value);
typedef void (*t_aioIob2Bi2xTBS_GetDeviceStatus)(struct AIO_NODE *i_pNodeCtl, struct AIO_IOB2_BI2X_TBS__DEVSTATUS *status, int size);

View File

@@ -0,0 +1,156 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#define LOG_MODULE "load_aio"
#include "util/log.h"
#include "load_aio.h"
#define DECLARE_MODULE_FUNCTION(NAME) t_ ## NAME NAME
struct aio_module {
// "libaio.dll"
DECLARE_MODULE_FUNCTION(aioNodeCtl_UpdateDevicesStatus);
DECLARE_MODULE_FUNCTION(aioNodeCtl_Destroy);
DECLARE_MODULE_FUNCTION(aioNodeMgr_Destroy);
DECLARE_MODULE_FUNCTION(aioSci_Destroy);
// "libaio-iob.dll"
DECLARE_MODULE_FUNCTION(aioIob2Bi2x_OpenSciUsbCdc);
DECLARE_MODULE_FUNCTION(aioNMgrIob2_Create);
// "libaio-iob2_video.dll"
DECLARE_MODULE_FUNCTION(aioIob2Bi2xTBS_Create);
// technically the game uses the C++ one, but this one doesn't incur a penalty unlike the C version of aioIob2Bi2xTBS_GetDeviceStatus
DECLARE_MODULE_FUNCTION(aioIob2Bi2xTBS_IoReset);
DECLARE_MODULE_FUNCTION(aioIob2Bi2xTBS_GetDeviceStatus);
};
void warn_module(const char *function_name) {
log_warning("Could not load %s!", function_name);
}
#define LOAD_MODULE_FUNCTION(TARGET, HANDLE, NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, #NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
#define LOAD_MODULE_FUNCTION_EX(TARGET, HANDLE, NAME, REAL_NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, REAL_NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
static struct aio_module module_handles;
void init_aio_handles(void) {
HMODULE h_aio = LoadLibrary("libaio.dll");
HMODULE h_aio_iob = LoadLibrary("libaio-iob.dll");
HMODULE h_aio_iob2_video = LoadLibrary("libaio-iob2_video.dll");
if (!h_aio) {
log_warning("Could not load libaio.dll!");
return;
}
if (!h_aio_iob) {
log_warning("Could not load libaio-iob.dll!");
return;
}
if (!h_aio_iob2_video) {
log_warning("Could not load libaio-iob2_video.dll!");
return;
}
// "libaio.dll"
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_UpdateDevicesStatus);
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_Destroy);
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeMgr_Destroy);
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioSci_Destroy);
// "libaio-iob.dll"
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioIob2Bi2x_OpenSciUsbCdc);
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioNMgrIob2_Create);
// "libaio-iob2_video.dll"
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xTBS_Create);
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xTBS_IoReset);
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xTBS_GetDeviceStatus);
// LOAD_MODULE_FUNCTION_EX(module_handles, h_aio_iob2_video, aioIob2Bi2xTBS_GetDeviceStatus, "?GetDeviceStatus@AIO_IOB2_BI2X_TDJ@@QEBAXAEAUDEVSTATUS@1@@Z");
log_misc("Done loading all libaio functions required.");
}
struct AIO_NMGR_VTABLE {
void *_dtor;
void *AddNodeCtl;
void *RemoveNodeCtl;
void *PacketCreate;
void *PacketSend;
void *QueryPktRecvTimeout;
void *PacketRecv;
void *_GetCommStatus;
void *_Reconnect;
void *_unk;
void (*BeginManage)(struct AIO_NMGR *);
};
struct AIO_NMGR {
struct AIO_NMGR_VTABLE *vtbl;
uint8_t data[0x9F0];
};
struct bi2x_ctx {
struct AIO_SCI *sci_usb;
struct AIO_NMGR *iob2_mgr;
struct AIO_NODE *iob_bi2x_node;
};
struct bi2x_ctx *setup_bi2x(void) {
struct bi2x_ctx *ctx = malloc(sizeof(struct bi2x_ctx));
ctx->sci_usb = module_handles.aioIob2Bi2x_OpenSciUsbCdc(0);
if (!ctx->sci_usb) {
log_warning("Unable to aioIob2Bi2x_OpenSciUsbCdc");
free(ctx);
return NULL;
}
ctx->iob2_mgr = module_handles.aioNMgrIob2_Create(ctx->sci_usb, 0);
if (!ctx->iob2_mgr) {
log_warning("Unable to aioNMgrIob2_Create");
free(ctx);
return NULL;
}
ctx->iob_bi2x_node = module_handles.aioIob2Bi2xTBS_Create(ctx->iob2_mgr, 1u);
if (!ctx->iob_bi2x_node) {
log_warning("Unable to aioIob2Bi2xTBS_Create?");
free(ctx);
return NULL;
}
ctx->iob2_mgr->vtbl->BeginManage(ctx->iob2_mgr);
log_misc("BeginManage iob2_mgr");
// never actually called
// module_handles.aioIob2Bi2xTBS_IoReset(ctx->iob_bi2x_node, 0x11);
return ctx;
}
void close_bi2x(struct bi2x_ctx *ctx) {
if (ctx->iob_bi2x_node) {
module_handles.aioNodeCtl_Destroy(ctx->iob_bi2x_node);
}
if (ctx->iob2_mgr) {
module_handles.aioNodeMgr_Destroy(ctx->iob2_mgr);
}
if (ctx->sci_usb) {
module_handles.aioSci_Destroy(ctx->sci_usb);
}
}
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_TBS__DEVSTATUS *status) {
module_handles.aioNodeCtl_UpdateDevicesStatus();
// technically we should do some checking here
module_handles.aioIob2Bi2xTBS_GetDeviceStatus(ctx->iob_bi2x_node, status, sizeof(struct AIO_IOB2_BI2X_TBS__DEVSTATUS));
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "aio.h"
void init_aio_handles(void);
struct bi2x_ctx;
struct bi2x_ctx *setup_bi2x(void);
void close_bi2x(struct bi2x_ctx *ctx);
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_TBS__DEVSTATUS *status);

View File

@@ -0,0 +1,14 @@
LIBRARY sdvxio
EXPORTS
sdvx_io_fini
sdvx_io_get_input_gpio
sdvx_io_get_input_gpio_sys
sdvx_io_get_spinner_pos
sdvx_io_init
sdvx_io_read_input
sdvx_io_set_gpio_lights
sdvx_io_set_loggers
sdvx_io_set_pwm_light
sdvx_io_write_output
sdvx_io_set_amp_volume

View File

@@ -0,0 +1,173 @@
#include <windows.h>
#define LOG_MODULE "iidxio-lightning"
#include <stdatomic.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "bemanitools/glue.h"
#include "bemanitools/sdvxio.h"
#include "util/log.h"
#include "load_aio.h"
static atomic_bool running;
static atomic_bool processing_io;
static struct AIO_IOB2_BI2X_TBS__DEVSTATUS pin_cur;
static struct bi2x_ctx *bi2x_ctx;
static bool _bio2_iidx_io_poll(struct AIO_IOB2_BI2X_TBS__DEVSTATUS *pin)
{
if (!running) {
return false;
}
processing_io = true;
poll_bi2x(bi2x_ctx, pin);
processing_io = false;
return true;
}
void sdvx_io_set_loggers(
log_formatter_t misc,
log_formatter_t info,
log_formatter_t warning,
log_formatter_t fatal)
{
log_to_external(misc, info, warning, fatal);
}
bool sdvx_io_init(
thread_create_t thread_create,
thread_join_t thread_join,
thread_destroy_t thread_destroy)
{
init_aio_handles();
bi2x_ctx = setup_bi2x();
if (!bi2x_ctx) {
log_warning("Unable to start BI2X?");
return false;
}
running = true;
return running;
}
void sdvx_io_fini(void)
{
running = false;
while (processing_io) {
// avoid banging
Sleep(1);
}
close_bi2x(bi2x_ctx);
bi2x_ctx = NULL;
}
void sdvx_io_set_gpio_lights(uint32_t gpio_lights)
{
// wow ignore
return;
}
void sdvx_io_set_pwm_light(uint8_t light_no, uint8_t intensity)
{
// wow ignore
return;
}
bool sdvx_io_write_output(void)
{
return true;
}
bool sdvx_io_read_input(void)
{
if (!_bio2_iidx_io_poll(&pin_cur)) {
return false;
}
return true;
}
uint16_t sdvx_io_get_spinner_pos(uint8_t spinner_no)
{
// spinner_no
int32_t raw_value = 0;
if (spinner_no == 0) {
raw_value = pin_cur.Input.StickX;
}
if (spinner_no == 1) {
raw_value = pin_cur.Input.StickY;
}
int32_t value = raw_value - 32767;
float converted = value / 32767.0f;
converted *= 2.0;
if (converted > 1.0f) {
converted = 1.0f;
} else if (converted < -1.0f) {
converted = -1.0f;
}
int32_t final = - 32767 * converted;
return (uint16_t)final/64;
}
static uint8_t shift_pin(uint16_t value, uint8_t pin)
{
if (value) {
return (1 << pin);
}
return 0;
}
uint8_t sdvx_io_get_input_gpio_sys(void)
{
return 0;
}
uint16_t sdvx_io_get_input_gpio(uint8_t gpio_bank)
{
if (gpio_bank > 2) {
return 0;
}
uint16_t data = 0;
if (gpio_bank == 0) {
data |= shift_pin(pin_cur.Input.bStickBtn, SDVX_IO_IN_GPIO_0_START);
data |= shift_pin(pin_cur.Input.bButton0, SDVX_IO_IN_GPIO_0_A);
data |= shift_pin(pin_cur.Input.bButton1, SDVX_IO_IN_GPIO_0_B);
data |= shift_pin(pin_cur.Input.bButton2, SDVX_IO_IN_GPIO_0_C);
} else if (gpio_bank == 1) {
data |= shift_pin(pin_cur.Input.bButton3, SDVX_IO_IN_GPIO_1_D);
data |= shift_pin(pin_cur.Input.bTrigger1, SDVX_IO_IN_GPIO_1_FX_L);
data |= shift_pin(pin_cur.Input.bTrigger2, SDVX_IO_IN_GPIO_1_FX_R);
}
return data;
}
bool sdvx_io_set_amp_volume(
uint8_t primary, uint8_t headphone, uint8_t subwoofer)
{
return true;
}

View File

@@ -0,0 +1,12 @@
dlls += sdvxio-valk
ldflags_sdvxio-valk := \
-lsetupapi \
libs_sdvxio-valk := \
cconfig \
util \
src_sdvxio-valk := \
sdvxio.c \
load_aio.c \

View File

@@ -0,0 +1,27 @@
#pragma once
#include <stdint.h>
struct AIO_NODE;
struct AIO_NMGR;
struct AIO_SCI;
struct AIO_IOB2_BI2X_UFC__DEVSTATUS {
char buffer[414];
};
_Static_assert(
sizeof(struct AIO_IOB2_BI2X_UFC__DEVSTATUS) == 414,
"bi2x_device_status is the wrong size");
typedef void (*t_aioNodeCtl_UpdateDevicesStatus)(void);
typedef unsigned int (*t_aioNodeCtl_Destroy)(struct AIO_NODE *node);
typedef unsigned int (*t_aioNodeMgr_Destroy)(struct AIO_NMGR *mgr);
typedef unsigned int (*t_aioSci_Destroy)(struct AIO_SCI *sci);
typedef struct AIO_SCI* (*t_aioIob2Bi2x_OpenSciUsbCdc)(uint8_t unk);
typedef struct AIO_NMGR* (*t_aioNMgrIob2_Create)(struct AIO_SCI *port, unsigned int delay);
typedef struct AIO_NODE* (*t_aioIob2Bi2xUFC_Create)(struct AIO_NMGR *node_mgr, uint8_t param);
typedef void (*t_aioIob2Bi2xUFC_IoReset)(struct AIO_NODE *i_pNodeCtl, int value);
typedef void (*t_aioIob2Bi2xUFC_GetDeviceStatus)(struct AIO_NODE *i_pNodeCtl, struct AIO_IOB2_BI2X_UFC__DEVSTATUS *status, int size);

View File

@@ -0,0 +1,155 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#define LOG_MODULE "load_aio"
#include "util/log.h"
#include "load_aio.h"
#define DECLARE_MODULE_FUNCTION(NAME) t_ ## NAME NAME
struct aio_module {
// "libaio.dll"
DECLARE_MODULE_FUNCTION(aioNodeCtl_UpdateDevicesStatus);
DECLARE_MODULE_FUNCTION(aioNodeCtl_Destroy);
DECLARE_MODULE_FUNCTION(aioNodeMgr_Destroy);
DECLARE_MODULE_FUNCTION(aioSci_Destroy);
// "libaio-iob.dll"
DECLARE_MODULE_FUNCTION(aioIob2Bi2x_OpenSciUsbCdc);
DECLARE_MODULE_FUNCTION(aioNMgrIob2_Create);
// "libaio-iob2_video.dll"
DECLARE_MODULE_FUNCTION(aioIob2Bi2xUFC_Create);
// technically the game uses the C++ one, but this one doesn't incur an extra function call unlike the C version
DECLARE_MODULE_FUNCTION(aioIob2Bi2xUFC_IoReset);
DECLARE_MODULE_FUNCTION(aioIob2Bi2xUFC_GetDeviceStatus);
};
void warn_module(const char *function_name) {
log_warning("Could not load %s!", function_name);
}
#define LOAD_MODULE_FUNCTION(TARGET, HANDLE, NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, #NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
#define LOAD_MODULE_FUNCTION_EX(TARGET, HANDLE, NAME, REAL_NAME) do {TARGET.NAME = (t_ ## NAME)GetProcAddress(HANDLE, REAL_NAME); if (TARGET.NAME == NULL) {warn_module(#NAME); return;}} while(0)
static struct aio_module module_handles;
void init_aio_handles(void) {
HMODULE h_aio = LoadLibrary("libaio.dll");
HMODULE h_aio_iob = LoadLibrary("libaio-iob.dll");
HMODULE h_aio_iob2_video = LoadLibrary("libaio-iob2_video.dll");
if (!h_aio) {
log_warning("Could not load libaio.dll!");
return;
}
if (!h_aio_iob) {
log_warning("Could not load libaio-iob.dll!");
return;
}
if (!h_aio_iob2_video) {
log_warning("Could not load libaio-iob2_video.dll!");
return;
}
// "libaio.dll"
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_UpdateDevicesStatus);
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeCtl_Destroy);
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioNodeMgr_Destroy);
LOAD_MODULE_FUNCTION(module_handles, h_aio, aioSci_Destroy);
// "libaio-iob.dll"
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioIob2Bi2x_OpenSciUsbCdc);
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob, aioNMgrIob2_Create);
// "libaio-iob2_video.dll"
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xUFC_Create);
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xUFC_IoReset);
LOAD_MODULE_FUNCTION(module_handles, h_aio_iob2_video, aioIob2Bi2xUFC_GetDeviceStatus);
log_misc("Done loading all libaio functions required.");
}
struct AIO_NMGR_VTABLE {
void *_dtor;
void *AddNodeCtl;
void *RemoveNodeCtl;
void *PacketCreate;
void *PacketSend;
void *QueryPktRecvTimeout;
void *PacketRecv;
void *_GetCommStatus;
void *_Reconnect;
void *_unk;
void (*BeginManage)(struct AIO_NMGR *);
};
struct AIO_NMGR {
struct AIO_NMGR_VTABLE *vtbl;
uint8_t data[0x9F0];
};
struct bi2x_ctx {
struct AIO_SCI *sci_usb;
struct AIO_NMGR *iob2_mgr;
struct AIO_NODE *iob_bi2x_node;
};
struct bi2x_ctx *setup_bi2x(void) {
struct bi2x_ctx *ctx = malloc(sizeof(struct bi2x_ctx));
ctx->sci_usb = module_handles.aioIob2Bi2x_OpenSciUsbCdc(0);
if (!ctx->sci_usb) {
log_warning("Unable to aioIob2Bi2x_OpenSciUsbCdc");
free(ctx);
return NULL;
}
ctx->iob2_mgr = module_handles.aioNMgrIob2_Create(ctx->sci_usb, 0);
if (!ctx->iob2_mgr) {
log_warning("Unable to aioNMgrIob2_Create");
free(ctx);
return NULL;
}
ctx->iob_bi2x_node = module_handles.aioIob2Bi2xUFC_Create(ctx->iob2_mgr, 1u);
if (!ctx->iob_bi2x_node) {
log_warning("Unable to aioIob2Bi2xUFC_Create?");
free(ctx);
return NULL;
}
ctx->iob2_mgr->vtbl->BeginManage(ctx->iob2_mgr);
log_misc("BeginManage iob2_mgr");
// never actually called
// module_handles.aioIob2Bi2xUFC_IoReset(ctx->iob_bi2x_node, 0x11);
return ctx;
}
void close_bi2x(struct bi2x_ctx *ctx) {
if (ctx->iob_bi2x_node) {
module_handles.aioNodeCtl_Destroy(ctx->iob_bi2x_node);
}
if (ctx->iob2_mgr) {
module_handles.aioNodeMgr_Destroy(ctx->iob2_mgr);
}
if (ctx->sci_usb) {
module_handles.aioSci_Destroy(ctx->sci_usb);
}
}
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_UFC__DEVSTATUS *status) {
module_handles.aioNodeCtl_UpdateDevicesStatus();
// technically we should do some checking here
module_handles.aioIob2Bi2xUFC_GetDeviceStatus(ctx->iob_bi2x_node, status, sizeof(struct AIO_IOB2_BI2X_UFC__DEVSTATUS));
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "aio.h"
void init_aio_handles(void);
struct bi2x_ctx;
struct bi2x_ctx *setup_bi2x(void);
void close_bi2x(struct bi2x_ctx *ctx);
void poll_bi2x(struct bi2x_ctx *ctx, struct AIO_IOB2_BI2X_TBS__DEVSTATUS *status);

View File

@@ -0,0 +1,14 @@
LIBRARY sdvxio-valk
EXPORTS
sdvx_io_fini
sdvx_io_get_input_gpio
sdvx_io_get_input_gpio_sys
sdvx_io_get_spinner_pos
sdvx_io_init
sdvx_io_read_input
sdvx_io_set_gpio_lights
sdvx_io_set_loggers
sdvx_io_set_pwm_light
sdvx_io_write_output
sdvx_io_set_amp_volume

View File

@@ -0,0 +1,137 @@
#include <windows.h>
#define LOG_MODULE "iidxio-lightning"
#include <stdatomic.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "bemanitools/glue.h"
#include "bemanitools/sdvxio.h"
#include "util/log.h"
#include "load_aio.h"
static atomic_bool running;
static atomic_bool processing_io;
static struct AIO_IOB2_BI2X_UFC__DEVSTATUS pin_cur;
static struct bi2x_ctx *bi2x_ctx;
static bool _bio2_iidx_io_poll(struct AIO_IOB2_BI2X_UFC__DEVSTATUS *pin)
{
if (!running) {
return false;
}
processing_io = true;
poll_bi2x(bi2x_ctx, pin);
processing_io = false;
return true;
}
void sdvx_io_set_loggers(
log_formatter_t misc,
log_formatter_t info,
log_formatter_t warning,
log_formatter_t fatal)
{
log_to_external(misc, info, warning, fatal);
}
bool sdvx_io_init(
thread_create_t thread_create,
thread_join_t thread_join,
thread_destroy_t thread_destroy)
{
init_aio_handles();
bi2x_ctx = setup_bi2x();
if (!bi2x_ctx) {
log_warning("Unable to start BI2X?");
return false;
}
running = true;
return running;
}
void sdvx_io_fini(void)
{
running = false;
while (processing_io) {
// avoid banging
Sleep(1);
}
close_bi2x(bi2x_ctx);
bi2x_ctx = NULL;
}
void sdvx_io_set_gpio_lights(uint32_t gpio_lights)
{
// wow ignore
return;
}
void sdvx_io_set_pwm_light(uint8_t light_no, uint8_t intensity)
{
// wow ignore
return;
}
bool sdvx_io_write_output(void)
{
return true;
}
bool sdvx_io_read_input(void)
{
if (!_bio2_iidx_io_poll(&pin_cur)) {
return false;
}
return true;
}
uint16_t sdvx_io_get_spinner_pos(uint8_t spinner_no)
{
// todo: implement
return 0;
}
static uint8_t shift_pin(uint16_t value, uint8_t pin)
{
if (value) {
return (1 << pin);
}
return 0;
}
uint8_t sdvx_io_get_input_gpio_sys(void)
{
// todo: implement
return 0;
}
uint16_t sdvx_io_get_input_gpio(uint8_t gpio_bank)
{
// todo: implement
return 0;
}
bool sdvx_io_set_amp_volume(
uint8_t primary, uint8_t headphone, uint8_t subwoofer)
{
return true;
}