diff --git a/Module.mk b/Module.mk index 99892a0..16b8a38 100644 --- a/Module.mk +++ b/Module.mk @@ -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 ... $@ diff --git a/src/main/iidxio-lightning/Module.mk b/src/main/iidxio-lightning/Module.mk new file mode 100644 index 0000000..f5de3eb --- /dev/null +++ b/src/main/iidxio-lightning/Module.mk @@ -0,0 +1,12 @@ +dlls += iidxio-lightning + +ldflags_iidxio-lightning := \ + -lsetupapi \ + +libs_iidxio-lightning := \ + cconfig \ + util \ + +src_iidxio-lightning := \ + iidxio.c \ + load_aio.c \ diff --git a/src/main/iidxio-lightning/aio.h b/src/main/iidxio-lightning/aio.h new file mode 100644 index 0000000..0a5ab65 --- /dev/null +++ b/src/main/iidxio-lightning/aio.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +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); diff --git a/src/main/iidxio-lightning/iidxio-lightning.def b/src/main/iidxio-lightning/iidxio-lightning.def new file mode 100644 index 0000000..403900d --- /dev/null +++ b/src/main/iidxio-lightning/iidxio-lightning.def @@ -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 diff --git a/src/main/iidxio-lightning/iidxio.c b/src/main/iidxio-lightning/iidxio.c new file mode 100644 index 0000000..50d9355 --- /dev/null +++ b/src/main/iidxio-lightning/iidxio.c @@ -0,0 +1,205 @@ +#include + +#define LOG_MODULE "iidxio-lightning" + +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/main/iidxio-lightning/load_aio.c b/src/main/iidxio-lightning/load_aio.c new file mode 100644 index 0000000..6aa14ab --- /dev/null +++ b/src/main/iidxio-lightning/load_aio.c @@ -0,0 +1,161 @@ +#include + +#include +#include + +#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); +} diff --git a/src/main/iidxio-lightning/load_aio.h b/src/main/iidxio-lightning/load_aio.h new file mode 100644 index 0000000..4bb414c --- /dev/null +++ b/src/main/iidxio-lightning/load_aio.h @@ -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); diff --git a/src/main/sdvxio-tbs/Module.mk b/src/main/sdvxio-tbs/Module.mk new file mode 100644 index 0000000..3f9570e --- /dev/null +++ b/src/main/sdvxio-tbs/Module.mk @@ -0,0 +1,12 @@ +dlls += sdvxio-tbs + +ldflags_sdvxio-tbs := \ + -lsetupapi \ + +libs_sdvxio-tbs := \ + cconfig \ + util \ + +src_sdvxio-tbs := \ + sdvxio.c \ + load_aio.c \ diff --git a/src/main/sdvxio-tbs/aio.h b/src/main/sdvxio-tbs/aio.h new file mode 100644 index 0000000..983ddc0 --- /dev/null +++ b/src/main/sdvxio-tbs/aio.h @@ -0,0 +1,72 @@ +#pragma once + +#include + +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); diff --git a/src/main/sdvxio-tbs/load_aio.c b/src/main/sdvxio-tbs/load_aio.c new file mode 100644 index 0000000..4973cd6 --- /dev/null +++ b/src/main/sdvxio-tbs/load_aio.c @@ -0,0 +1,156 @@ +#include + +#include +#include + +#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)); +} diff --git a/src/main/sdvxio-tbs/load_aio.h b/src/main/sdvxio-tbs/load_aio.h new file mode 100644 index 0000000..81e4d3a --- /dev/null +++ b/src/main/sdvxio-tbs/load_aio.h @@ -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); diff --git a/src/main/sdvxio-tbs/sdvxio-tbs.def b/src/main/sdvxio-tbs/sdvxio-tbs.def new file mode 100644 index 0000000..8d6e632 --- /dev/null +++ b/src/main/sdvxio-tbs/sdvxio-tbs.def @@ -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 diff --git a/src/main/sdvxio-tbs/sdvxio.c b/src/main/sdvxio-tbs/sdvxio.c new file mode 100644 index 0000000..92f1e57 --- /dev/null +++ b/src/main/sdvxio-tbs/sdvxio.c @@ -0,0 +1,173 @@ +#include + +#define LOG_MODULE "iidxio-lightning" + +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/main/sdvxio-valk/Module.mk b/src/main/sdvxio-valk/Module.mk new file mode 100644 index 0000000..56bc80e --- /dev/null +++ b/src/main/sdvxio-valk/Module.mk @@ -0,0 +1,12 @@ +dlls += sdvxio-valk + +ldflags_sdvxio-valk := \ + -lsetupapi \ + +libs_sdvxio-valk := \ + cconfig \ + util \ + +src_sdvxio-valk := \ + sdvxio.c \ + load_aio.c \ diff --git a/src/main/sdvxio-valk/aio.h b/src/main/sdvxio-valk/aio.h new file mode 100644 index 0000000..9f5bd99 --- /dev/null +++ b/src/main/sdvxio-valk/aio.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +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); diff --git a/src/main/sdvxio-valk/load_aio.c b/src/main/sdvxio-valk/load_aio.c new file mode 100644 index 0000000..2db1549 --- /dev/null +++ b/src/main/sdvxio-valk/load_aio.c @@ -0,0 +1,155 @@ +#include + +#include +#include + +#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)); +} diff --git a/src/main/sdvxio-valk/load_aio.h b/src/main/sdvxio-valk/load_aio.h new file mode 100644 index 0000000..81e4d3a --- /dev/null +++ b/src/main/sdvxio-valk/load_aio.h @@ -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); diff --git a/src/main/sdvxio-valk/sdvxio-valk.def b/src/main/sdvxio-valk/sdvxio-valk.def new file mode 100644 index 0000000..fe617ac --- /dev/null +++ b/src/main/sdvxio-valk/sdvxio-valk.def @@ -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 diff --git a/src/main/sdvxio-valk/sdvxio.c b/src/main/sdvxio-valk/sdvxio.c new file mode 100644 index 0000000..f322731 --- /dev/null +++ b/src/main/sdvxio-valk/sdvxio.c @@ -0,0 +1,137 @@ +#include + +#define LOG_MODULE "iidxio-lightning" + +#include +#include +#include +#include +#include + +#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; +}