Compare commits
25 Commits
2024-12-23
...
develop
Author | SHA1 | Date |
---|---|---|
![]() |
a6126bf290 | |
![]() |
015097972a | |
![]() |
67eda7458b | |
![]() |
b37e1105d0 | |
![]() |
9a6c4939c2 | |
![]() |
39711a994a | |
![]() |
61f95c3f2e | |
![]() |
70c3e2fe0f | |
![]() |
369fe28687 | |
![]() |
3371f3f437 | |
![]() |
a57542c2d2 | |
![]() |
27116a7a41 | |
![]() |
e850346b79 | |
![]() |
4d0ef54279 | |
![]() |
b8af67377c | |
![]() |
4cb76dd1ee | |
![]() |
efe01d92a6 | |
![]() |
004a2f6dcd | |
![]() |
a1611afffc | |
![]() |
1d63ab24d3 | |
![]() |
2f54183636 | |
![]() |
402bf0f247 | |
![]() |
4c20deb60a | |
![]() |
96ee1afc2f | |
![]() |
0c28765bdd |
|
@ -21,3 +21,6 @@ subprojects/capnhook
|
|||
|
||||
# For enabling debug logging on local builds
|
||||
MesonLocalOptions.mk
|
||||
|
||||
# Some meson cache thing
|
||||
.meson-subproject-wrap-hash.txt
|
||||
|
|
30
Package.mk
30
Package.mk
|
@ -5,7 +5,7 @@ $(BUILD_DIR_ZIP)/chuni.zip:
|
|||
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_32)/chunihook/chunihook.dll \
|
||||
$(DIST_DIR)/chuni/segatools.ini \
|
||||
$(DIST_DIR)/chuni/start.bat \
|
||||
$(DIST_DIR)/chuni/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/chuni
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -20,7 +20,7 @@ $(BUILD_DIR_ZIP)/cxb.zip:
|
|||
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_32)/cxbhook/cxbhook.dll \
|
||||
$(DIST_DIR)/cxb/segatools.ini \
|
||||
$(DIST_DIR)/cxb/start.bat \
|
||||
$(DIST_DIR)/cxb/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/cxb
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -35,7 +35,7 @@ $(BUILD_DIR_ZIP)/diva.zip:
|
|||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/divahook/divahook.dll \
|
||||
$(DIST_DIR)/diva/segatools.ini \
|
||||
$(DIST_DIR)/diva/start.bat \
|
||||
$(DIST_DIR)/diva/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/diva
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -50,7 +50,7 @@ $(BUILD_DIR_ZIP)/carol.zip:
|
|||
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_32)/carolhook/carolhook.dll \
|
||||
$(DIST_DIR)/carol/segatools.ini \
|
||||
$(DIST_DIR)/carol/start.bat \
|
||||
$(DIST_DIR)/carol/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/carol
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -65,7 +65,7 @@ $(BUILD_DIR_ZIP)/idz.zip:
|
|||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/idzhook/idzhook.dll \
|
||||
$(DIST_DIR)/idz/segatools.ini \
|
||||
$(DIST_DIR)/idz/start.bat \
|
||||
$(DIST_DIR)/idz/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/idz
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -80,7 +80,7 @@ $(BUILD_DIR_ZIP)/fgo.zip:
|
|||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/fgohook/fgohook.dll \
|
||||
$(DIST_DIR)/fgo/segatools.ini \
|
||||
$(DIST_DIR)/fgo/start.bat \
|
||||
$(DIST_DIR)/fgo/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/fgo
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -96,7 +96,7 @@ $(BUILD_DIR_ZIP)/idac.zip:
|
|||
$(BUILD_DIR_64)/idachook/idachook.dll \
|
||||
$(DIST_DIR)/idac/segatools.ini \
|
||||
$(DIST_DIR)/idac/config_hook.json \
|
||||
$(DIST_DIR)/idac/start.bat \
|
||||
$(DIST_DIR)/idac/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/idac
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -112,7 +112,7 @@ $(BUILD_DIR_ZIP)/swdc.zip:
|
|||
$(BUILD_DIR_64)/swdchook/swdchook.dll \
|
||||
$(DIST_DIR)/swdc/segatools.ini \
|
||||
$(DIST_DIR)/swdc/config_hook.json \
|
||||
$(DIST_DIR)/swdc/start.bat \
|
||||
$(DIST_DIR)/swdc/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/swdc
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -127,7 +127,7 @@ $(BUILD_DIR_ZIP)/mercury.zip:
|
|||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/mercuryhook/mercuryhook.dll \
|
||||
$(DIST_DIR)/mercury/segatools.ini \
|
||||
$(DIST_DIR)/mercury/start.bat \
|
||||
$(DIST_DIR)/mercury/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/mercury
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -141,7 +141,7 @@ $(BUILD_DIR_ZIP)/chusan.zip:
|
|||
$(V)mkdir -p $(BUILD_DIR_ZIP)/chusan/DEVICE
|
||||
$(V)cp $(DIST_DIR)/chusan/segatools.ini \
|
||||
$(DIST_DIR)/chusan/config_hook.json \
|
||||
$(DIST_DIR)/chusan/start.bat \
|
||||
$(DIST_DIR)/chusan/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/chusan
|
||||
$(V)cp $(BUILD_DIR_32)/chusanhook/chusanhook.dll \
|
||||
$(BUILD_DIR_ZIP)/chusan/chusanhook_x86.dll
|
||||
|
@ -164,7 +164,7 @@ $(BUILD_DIR_ZIP)/mu3.zip:
|
|||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/mu3hook/mu3hook.dll \
|
||||
$(DIST_DIR)/mu3/segatools.ini \
|
||||
$(DIST_DIR)/mu3/start.bat \
|
||||
$(DIST_DIR)/mu3/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/mu3
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -179,7 +179,7 @@ $(BUILD_DIR_ZIP)/mai2.zip:
|
|||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/mai2hook/mai2hook.dll \
|
||||
$(DIST_DIR)/mai2/segatools.ini \
|
||||
$(DIST_DIR)/mai2/start.bat \
|
||||
$(DIST_DIR)/mai2/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/mai2
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -195,7 +195,7 @@ $(BUILD_DIR_ZIP)/cm.zip:
|
|||
$(BUILD_DIR_64)/cmhook/cmhook.dll \
|
||||
$(DIST_DIR)/cm/config_hook.json \
|
||||
$(DIST_DIR)/cm/segatools.ini \
|
||||
$(DIST_DIR)/cm/start.bat \
|
||||
$(DIST_DIR)/cm/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/cm
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -211,7 +211,7 @@ $(BUILD_DIR_ZIP)/tokyo.zip:
|
|||
$(BUILD_DIR_64)/tokyohook/tokyohook.dll \
|
||||
$(DIST_DIR)/tokyo/config_hook.json \
|
||||
$(DIST_DIR)/tokyo/segatools.ini \
|
||||
$(DIST_DIR)/tokyo/start.bat \
|
||||
$(DIST_DIR)/tokyo/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/tokyo
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
|
@ -224,7 +224,7 @@ $(BUILD_DIR_ZIP)/kemono.zip:
|
|||
$(V)mkdir -p $(BUILD_DIR_ZIP)/kemono
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/kemono/DEVICE
|
||||
$(V)cp $(DIST_DIR)/kemono/segatools.ini \
|
||||
$(DIST_DIR)/kemono/start.bat \
|
||||
$(DIST_DIR)/kemono/launch.bat \
|
||||
$(BUILD_DIR_ZIP)/kemono
|
||||
$(V)cp $(BUILD_DIR_32)/kemonohook/kemonohook.dll \
|
||||
$(BUILD_DIR_ZIP)/kemono/kemonohook_x86.dll
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include <windows.h>
|
||||
#include <ntstatus.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
|
|
@ -75,6 +75,15 @@ void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
|
|||
cfg->port_no = GetPrivateProfileIntW(L"aime", L"portNo", 0, filename);
|
||||
cfg->high_baudrate = GetPrivateProfileIntW(L"aime", L"highBaud", 1, filename);
|
||||
cfg->gen = GetPrivateProfileIntW(L"aime", L"gen", 0, filename);
|
||||
cfg->proxy_flag = GetPrivateProfileIntW(L"aime", L"proxyFlag", 2, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"aime",
|
||||
L"authdataPath",
|
||||
L"DEVICE\\authdata.bin",
|
||||
cfg->authdata_path,
|
||||
_countof(cfg->authdata_path),
|
||||
filename);
|
||||
}
|
||||
|
||||
void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
|
||||
|
|
113
board/led15070.c
113
board/led15070.c
|
@ -103,19 +103,30 @@ HRESULT led15070_hook_init(
|
|||
io_led_set_fet_output_t _led_set_fet_output,
|
||||
io_led_dc_update_t _led_dc_update,
|
||||
io_led_gs_update_t _led_gs_update,
|
||||
unsigned int first_port,
|
||||
unsigned int num_boards)
|
||||
unsigned int port_no[2])
|
||||
{
|
||||
unsigned int num_boards = 0;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(_led_init != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
if (cfg->port_no != 0) {
|
||||
first_port = cfg->port_no;
|
||||
for (int i = 0; i < led15070_nboards; i++)
|
||||
{
|
||||
if (cfg->port_no[i] != 0) {
|
||||
port_no[i] = cfg->port_no[i];
|
||||
}
|
||||
|
||||
if (port_no[i] != 0) {
|
||||
num_boards++;
|
||||
}
|
||||
}
|
||||
|
||||
assert(num_boards != 0);
|
||||
|
||||
led_init = _led_init;
|
||||
led_set_fet_output = _led_set_fet_output;
|
||||
led_dc_update = _led_dc_update;
|
||||
|
@ -131,10 +142,7 @@ HRESULT led15070_hook_init(
|
|||
|
||||
InitializeCriticalSection(&v->lock);
|
||||
|
||||
// TODO: IMPROVE!
|
||||
first_port = i == 1 ? first_port + 2 : first_port;
|
||||
|
||||
uart_init(&v->boarduart, first_port);
|
||||
uart_init(&v->boarduart, port_no[i]);
|
||||
v->boarduart.baud.BaudRate = 115200;
|
||||
v->boarduart.written.bytes = v->written_bytes;
|
||||
v->boarduart.written.nbytes = sizeof(v->written_bytes);
|
||||
|
@ -238,7 +246,7 @@ static HRESULT led15070_handle_irp_locked(int board, struct irp *irp)
|
|||
}
|
||||
|
||||
for (;;) {
|
||||
#if 0
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("TX Buffer:\n");
|
||||
dump_iobuf(&boarduart->written);
|
||||
#endif
|
||||
|
@ -257,7 +265,7 @@ static HRESULT led15070_handle_irp_locked(int board, struct irp *irp)
|
|||
return hr;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("Deframe Buffer:\n");
|
||||
dump_iobuf(&req_iobuf);
|
||||
#endif
|
||||
|
@ -385,7 +393,9 @@ static HRESULT led15070_req_reset(int board, const struct led15070_req_any *req)
|
|||
|
||||
static HRESULT led15070_req_set_input(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set input (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
|
@ -408,9 +418,10 @@ static HRESULT led15070_req_set_input(int board, const struct led15070_req_any *
|
|||
static HRESULT led15070_req_set_normal_12bit(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
uint8_t idx = req->payload[0];
|
||||
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set LED - Normal 12bit (board %u, index %u)\n",
|
||||
board, idx);
|
||||
#endif
|
||||
|
||||
// TODO: Data for this command. Seen with Carol
|
||||
|
||||
|
@ -435,9 +446,10 @@ static HRESULT led15070_req_set_normal_12bit(int board, const struct led15070_re
|
|||
static HRESULT led15070_req_set_normal_8bit(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
uint8_t idx = req->payload[0];
|
||||
|
||||
// dprintf("LED 15070: Set LED - Normal 8bit (board %u, index %u)\n",
|
||||
// board, idx);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set LED - Normal 8bit (board %u, index %u)\n",
|
||||
board, idx);
|
||||
#endif
|
||||
|
||||
led15070_per_board_vars[board].gs[idx][0] = req->payload[1]; // R
|
||||
led15070_per_board_vars[board].gs[idx][1] = req->payload[2]; // G
|
||||
|
@ -468,8 +480,10 @@ static HRESULT led15070_req_set_multi_flash_8bit(int board, const struct led1507
|
|||
uint8_t idx_skip = req->payload[2];
|
||||
|
||||
// TODO: useful?
|
||||
// dprintf("LED 15070: Set LED - Multi flash 8bit (board %u, start %u, end %u, skip %u)\n",
|
||||
// board, idx_start, idx_end, idx_skip);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set LED - Multi flash 8bit (board %u, start %u, end %u, skip %u)\n",
|
||||
board, idx_start, idx_end, idx_skip);
|
||||
#endif
|
||||
|
||||
if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) {
|
||||
idx_start += idx_skip;
|
||||
|
@ -508,9 +522,10 @@ static HRESULT led15070_req_set_multi_fade_8bit(int board, const struct led15070
|
|||
uint8_t idx_start = req->payload[0];
|
||||
uint8_t idx_end = req->payload[1];
|
||||
uint8_t idx_skip = req->payload[2];
|
||||
|
||||
// dprintf("LED 15070: Set LED - Multi fade 8bit (board %u, start %u, end %u, skip %u)\n",
|
||||
// board, idx_start, idx_end, idx_skip);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set LED - Multi fade 8bit (board %u, start %u, end %u, skip %u)\n",
|
||||
board, idx_start, idx_end, idx_skip);
|
||||
#endif
|
||||
|
||||
if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) {
|
||||
idx_start += idx_skip;
|
||||
|
@ -545,7 +560,9 @@ static HRESULT led15070_req_set_multi_fade_8bit(int board, const struct led15070
|
|||
|
||||
static HRESULT led15070_req_set_palette_7_normal_led(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set palette - 7 Normal LED (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
|
@ -567,7 +584,9 @@ static HRESULT led15070_req_set_palette_7_normal_led(int board, const struct led
|
|||
|
||||
static HRESULT led15070_req_set_palette_6_flash_led(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set palette - 6 Flash LED (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
|
@ -589,7 +608,9 @@ static HRESULT led15070_req_set_palette_6_flash_led(int board, const struct led1
|
|||
|
||||
static HRESULT led15070_req_set_15dc_out(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set 15DC out (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
|
@ -611,7 +632,9 @@ static HRESULT led15070_req_set_15dc_out(int board, const struct led15070_req_an
|
|||
|
||||
static HRESULT led15070_req_set_15gs_out(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set 15GS out (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
|
@ -633,7 +656,9 @@ static HRESULT led15070_req_set_15gs_out(int board, const struct led15070_req_an
|
|||
|
||||
static HRESULT led15070_req_set_psc_max(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set PSC max (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
|
@ -655,14 +680,16 @@ static HRESULT led15070_req_set_psc_max(int board, const struct led15070_req_any
|
|||
|
||||
static HRESULT led15070_req_set_fet_output(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set FET output (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
led15070_per_board_vars[board].fet[0] = req->payload[0]; // R or FET0 intensity
|
||||
led15070_per_board_vars[board].fet[1] = req->payload[1]; // G or FET1 intensity
|
||||
led15070_per_board_vars[board].fet[2] = req->payload[2]; // B or FET2 intensity
|
||||
|
||||
if (led_set_fet_output)
|
||||
led_set_fet_output((const uint8_t*)led15070_per_board_vars[board].fet);
|
||||
led_set_fet_output(board, (const uint8_t*)led15070_per_board_vars[board].fet);
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
|
@ -685,8 +712,9 @@ static HRESULT led15070_req_set_fet_output(int board, const struct led15070_req_
|
|||
static HRESULT led15070_req_set_gs_palette(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
uint8_t idx = req->payload[0];
|
||||
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set GS palette (board %u, index %u)\n", board, idx);
|
||||
#endif
|
||||
|
||||
led15070_per_board_vars[board].gs_palette[idx][0] = req->payload[1]; // R
|
||||
led15070_per_board_vars[board].gs_palette[idx][1] = req->payload[2]; // G
|
||||
|
@ -712,10 +740,12 @@ static HRESULT led15070_req_set_gs_palette(int board, const struct led15070_req_
|
|||
|
||||
static HRESULT led15070_req_dc_update(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
// dprintf("LED 15070: DC update (board %u)\n", board);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: DC update (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (led_dc_update)
|
||||
led_dc_update((const uint8_t*)led15070_per_board_vars[board].dc);
|
||||
led_dc_update(board, (const uint8_t*)led15070_per_board_vars[board].dc);
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
|
@ -737,10 +767,12 @@ static HRESULT led15070_req_dc_update(int board, const struct led15070_req_any *
|
|||
|
||||
static HRESULT led15070_req_gs_update(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
// dprintf("LED 15070: GS update (board %u)\n", board);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: GS update (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (led_gs_update)
|
||||
led_gs_update((const uint8_t*)led15070_per_board_vars[board].gs);
|
||||
led_gs_update(board, (const uint8_t*)led15070_per_board_vars[board].gs);
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
|
@ -762,7 +794,9 @@ static HRESULT led15070_req_gs_update(int board, const struct led15070_req_any *
|
|||
|
||||
static HRESULT led15070_req_rotate(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Rotate (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
if (!led15070_per_board_vars[board].enable_response)
|
||||
return S_OK;
|
||||
|
@ -787,9 +821,10 @@ static HRESULT led15070_req_set_dc_data(int board, const struct led15070_req_any
|
|||
uint8_t idx_start = req->payload[0];
|
||||
uint8_t idx_end = req->payload[1];
|
||||
uint8_t idx_skip = req->payload[2];
|
||||
|
||||
// dprintf("LED 15070: Set DC data (board %u, start %u, end %u, skip %u)\n",
|
||||
// board, idx_start, idx_end, idx_skip);
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Set DC data (board %u, start %u, end %u, skip %u)\n",
|
||||
board, idx_start, idx_end, idx_skip);
|
||||
#endif
|
||||
|
||||
if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) {
|
||||
idx_start += idx_skip;
|
||||
|
@ -829,9 +864,10 @@ static HRESULT led15070_req_eeprom_write(int board, const struct led15070_req_an
|
|||
|
||||
uint8_t addr = req->payload[0];
|
||||
uint8_t data = req->payload[1];
|
||||
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: EEPROM write (board %u, address %02x, data %02x)\n",
|
||||
board, addr, data);
|
||||
#endif
|
||||
|
||||
if (addr > 0x07) {
|
||||
dprintf("LED 15070: Error -- Invalid EEPROM write address %02x\n",
|
||||
|
@ -919,8 +955,9 @@ static HRESULT led15070_req_eeprom_read(int board, const struct led15070_req_any
|
|||
|
||||
uint8_t addr = req->payload[0];
|
||||
uint8_t data = 0;
|
||||
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: EEPROM read (board %u, address %02x)\n", board, addr);
|
||||
#endif
|
||||
|
||||
if (addr > 0x07) {
|
||||
dprintf("LED 15070: Error -- Invalid EEPROM read address %02x\n",
|
||||
|
@ -1002,7 +1039,9 @@ static HRESULT led15070_req_eeprom_read(int board, const struct led15070_req_any
|
|||
|
||||
static HRESULT led15070_req_ack_on(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Acknowledge commands ON (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
led15070_per_board_vars[board].enable_response = true;
|
||||
|
||||
|
@ -1023,7 +1062,9 @@ static HRESULT led15070_req_ack_on(int board, const struct led15070_req_any *req
|
|||
|
||||
static HRESULT led15070_req_ack_off(int board, const struct led15070_req_any *req)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Acknowledge commands OFF (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
led15070_per_board_vars[board].enable_response = false;
|
||||
|
||||
|
@ -1044,7 +1085,9 @@ static HRESULT led15070_req_ack_off(int board, const struct led15070_req_any *re
|
|||
|
||||
static HRESULT led15070_req_board_info(int board)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Get board info (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
struct led15070_resp_board_info resp;
|
||||
|
||||
|
@ -1067,7 +1110,9 @@ static HRESULT led15070_req_board_info(int board)
|
|||
|
||||
static HRESULT led15070_req_board_status(int board)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Get board status (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
struct led15070_resp_any resp;
|
||||
|
||||
|
@ -1091,7 +1136,9 @@ static HRESULT led15070_req_board_status(int board)
|
|||
|
||||
static HRESULT led15070_req_fw_sum(int board)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Get firmware checksum (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
struct led15070_resp_any resp;
|
||||
|
||||
|
@ -1113,7 +1160,9 @@ static HRESULT led15070_req_fw_sum(int board)
|
|||
|
||||
static HRESULT led15070_req_protocol_ver(int board)
|
||||
{
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Get protocol version (board %u)\n", board);
|
||||
#endif
|
||||
|
||||
struct led15070_resp_any resp;
|
||||
|
||||
|
@ -1187,7 +1236,7 @@ static HRESULT led15070_eeprom_open(int board, wchar_t *path, HANDLE *handle)
|
|||
HRESULT hr;
|
||||
BOOL ok;
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Opening EEPROM file '%S' handle (board %u)\n", path, board);
|
||||
#endif
|
||||
|
||||
|
@ -1233,7 +1282,7 @@ static HRESULT led15070_eeprom_close(int board, wchar_t *path, HANDLE *handle)
|
|||
HRESULT hr;
|
||||
BOOL ok;
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_LED15070)
|
||||
dprintf("LED 15070: Closing EEPROM file '%S' handle (board %u)\n", path, board);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
struct led15070_config {
|
||||
bool enable;
|
||||
unsigned int port_no;
|
||||
unsigned int port_no[2];
|
||||
char board_number[8];
|
||||
uint8_t fw_ver;
|
||||
uint16_t fw_sum;
|
||||
|
@ -15,9 +15,9 @@ struct led15070_config {
|
|||
};
|
||||
|
||||
typedef HRESULT (*io_led_init_t)(void);
|
||||
typedef void (*io_led_set_fet_output_t)(const uint8_t *rgb);
|
||||
typedef void (*io_led_dc_update_t)(const uint8_t *rgb);
|
||||
typedef void (*io_led_gs_update_t)(const uint8_t *rgb);
|
||||
typedef void (*io_led_set_fet_output_t)(uint8_t board, const uint8_t *rgb);
|
||||
typedef void (*io_led_dc_update_t)(uint8_t board, const uint8_t *rgb);
|
||||
typedef void (*io_led_gs_update_t)(uint8_t board, const uint8_t *rgb);
|
||||
|
||||
HRESULT led15070_hook_init(
|
||||
const struct led15070_config *cfg,
|
||||
|
@ -25,5 +25,4 @@ HRESULT led15070_hook_init(
|
|||
io_led_set_fet_output_t _led_set_fet_output,
|
||||
io_led_dc_update_t _led_dc_update,
|
||||
io_led_gs_update_t _led_gs_update,
|
||||
unsigned int first_port,
|
||||
unsigned int num_boards);
|
||||
unsigned int port_no[2]);
|
||||
|
|
|
@ -107,9 +107,13 @@ static uint8_t led15093_host_adr = 1;
|
|||
static io_led_init_t led_init;
|
||||
static io_led_set_leds_t set_leds;
|
||||
|
||||
HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led_init,
|
||||
io_led_set_leds_t _set_leds, unsigned int first_port, unsigned int num_boards, uint8_t board_adr, uint8_t host_adr)
|
||||
HRESULT led15093_hook_init(
|
||||
const struct led15093_config *cfg,
|
||||
io_led_init_t _led_init,
|
||||
io_led_set_leds_t _set_leds,
|
||||
unsigned int port_no[2])
|
||||
{
|
||||
unsigned int num_boards = 0;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(_led_init != NULL);
|
||||
|
@ -119,14 +123,24 @@ HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led
|
|||
return S_FALSE;
|
||||
}
|
||||
|
||||
if (cfg->port_no != 0) {
|
||||
first_port = cfg->port_no;
|
||||
for (int i = 0; i < led15093_nboards; i++)
|
||||
{
|
||||
if (cfg->port_no[i] != 0) {
|
||||
port_no[i] = cfg->port_no[i];
|
||||
}
|
||||
|
||||
if (port_no[i] != 0) {
|
||||
num_boards++;
|
||||
}
|
||||
}
|
||||
|
||||
assert(num_boards != 0);
|
||||
|
||||
led15093_board_adr = num_boards;
|
||||
led15093_host_adr = num_boards == 2 ? 1 : 2;
|
||||
|
||||
led_init = _led_init;
|
||||
set_leds = _set_leds;
|
||||
led15093_board_adr = board_adr;
|
||||
led15093_host_adr = host_adr;
|
||||
|
||||
memcpy(led15093_board_num, cfg->board_number, sizeof(led15093_board_num));
|
||||
memcpy(led15093_chip_num, cfg->chip_number, sizeof(led15093_chip_num));
|
||||
|
@ -140,7 +154,7 @@ HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led
|
|||
|
||||
InitializeCriticalSection(&vb->lock);
|
||||
|
||||
uart_init(&vb->boarduart, first_port + i);
|
||||
uart_init(&vb->boarduart, port_no[i]);
|
||||
if (cfg->high_baudrate) {
|
||||
vb->boarduart.baud.BaudRate = 460800;
|
||||
} else {
|
||||
|
@ -209,7 +223,6 @@ static HRESULT led15093_handle_irp_locked(int board, struct irp *irp)
|
|||
_led15093_per_board_vars *v = &led15093_per_board_vars[board];
|
||||
struct uart *boarduart = &led15093_per_board_vars[board].boarduart;
|
||||
|
||||
/*
|
||||
if (irp->op == IRP_OP_OPEN) {
|
||||
// Unfortunately the LED board UART gets opened and closed repeatedly
|
||||
|
||||
|
@ -236,30 +249,6 @@ static HRESULT led15093_handle_irp_locked(int board, struct irp *irp)
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (irp->op == IRP_OP_OPEN) {
|
||||
dprintf("LED 15093: Starting backend DLL\n");
|
||||
// int res = led_init();
|
||||
hr = led_init();
|
||||
|
||||
/*
|
||||
if (res != 0) {
|
||||
dprintf("LED 15093: Backend error, LED board disconnected: "
|
||||
"%d\n",
|
||||
res);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
*/
|
||||
if (FAILED(hr)) {
|
||||
dprintf("LED 15093: Backend error, LED board disconnected: "
|
||||
"%x\n",
|
||||
(int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(boarduart, irp);
|
||||
|
||||
|
@ -688,16 +677,6 @@ static HRESULT led15093_req_set_imm_led(int board, const struct led15093_req_set
|
|||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
/*
|
||||
if (board == 0) {
|
||||
dprintf("board %d: red: %d, green: %d, blue: %d\n", board, req->data[0x96], req->data[0x97], req->data[0x98]);
|
||||
}
|
||||
else if (board == 1)
|
||||
{
|
||||
dprintf("board %d: red: %d, green: %d, blue: %d\n", board, req->data[0xb4], req->data[0xb5], req->data[0xb6]);
|
||||
}
|
||||
*/
|
||||
|
||||
// Return the current LED data, remove const qualifier
|
||||
set_leds(board, (uint8_t *) req->data);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
struct led15093_config {
|
||||
bool enable;
|
||||
bool high_baudrate;
|
||||
unsigned int port_no;
|
||||
unsigned int port_no[2];
|
||||
char board_number[8];
|
||||
char chip_number[5];
|
||||
char boot_chip_number[5];
|
||||
|
@ -20,5 +20,5 @@ typedef HRESULT (*io_led_init_t)(void);
|
|||
typedef void (*io_led_set_leds_t)(uint8_t board, uint8_t *rgb);
|
||||
|
||||
HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led_init,
|
||||
io_led_set_leds_t _set_leds, unsigned int first_port, unsigned int num_boards, uint8_t board_adr, uint8_t host_adr);
|
||||
io_led_set_leds_t _set_leds, unsigned int port_no[2]);
|
||||
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
#pragma pack(push, 1)
|
||||
|
||||
enum {
|
||||
SG_NFC_CMD_GET_FW_VERSION = 0x30,
|
||||
SG_NFC_CMD_GET_HW_VERSION = 0x32,
|
||||
SG_NFC_CMD_RADIO_ON = 0x40,
|
||||
SG_NFC_CMD_RADIO_OFF = 0x41,
|
||||
SG_NFC_CMD_POLL = 0x42,
|
||||
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x50,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_A = 0x51,
|
||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x54,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_B = 0x55,
|
||||
SG_NFC_CMD_TO_UPDATE_MODE = 0x60,
|
||||
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
|
||||
SG_NFC_CMD_RESET = 0x62,
|
||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||
SG_NFC_CMD_GET_FW_VERSION = 0x30,
|
||||
SG_NFC_CMD_GET_HW_VERSION = 0x32,
|
||||
SG_NFC_CMD_RADIO_ON = 0x40,
|
||||
SG_NFC_CMD_RADIO_OFF = 0x41,
|
||||
SG_NFC_CMD_POLL = 0x42,
|
||||
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x50,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_AIME = 0x51,
|
||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x54,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_BANA = 0x55,
|
||||
SG_NFC_CMD_TO_UPDATE_MODE = 0x60,
|
||||
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
|
||||
SG_NFC_CMD_RESET = 0x62,
|
||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||
};
|
||||
|
||||
struct sg_nfc_res_get_fw_version {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -16,6 +17,7 @@
|
|||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
#include "util/slurp.h"
|
||||
|
||||
static HRESULT sg_nfc_dispatch(
|
||||
void *ctx,
|
||||
|
@ -87,6 +89,8 @@ void sg_nfc_init(
|
|||
uint8_t addr,
|
||||
const struct sg_nfc_ops *ops,
|
||||
unsigned int gen,
|
||||
unsigned int proxy_flag,
|
||||
const wchar_t* authdata_path,
|
||||
void *ops_ctx)
|
||||
{
|
||||
assert(nfc != NULL);
|
||||
|
@ -96,6 +100,8 @@ void sg_nfc_init(
|
|||
nfc->ops_ctx = ops_ctx;
|
||||
nfc->addr = addr;
|
||||
nfc->gen = gen;
|
||||
nfc->proxy_flag = proxy_flag;
|
||||
nfc->authdata_path = authdata_path;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
|
@ -189,8 +195,8 @@ static HRESULT sg_nfc_dispatch(
|
|||
&req->felica_encap,
|
||||
&res->felica_encap);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_A:
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_B:
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_AIME:
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_BANA:
|
||||
case SG_NFC_CMD_SEND_HEX_DATA:
|
||||
return sg_nfc_cmd_send_hex_data(nfc, &req->simple, &res->simple);
|
||||
|
||||
|
@ -382,18 +388,62 @@ static HRESULT sg_nfc_cmd_mifare_read_block(
|
|||
|
||||
sg_nfc_dprintf(nfc, "Read uid %08x block %i\n", uid, req->payload.block_no);
|
||||
|
||||
if (req->payload.block_no > 3) {
|
||||
if (req->payload.block_no > 14) {
|
||||
sg_nfc_dprintf(nfc, "MIFARE block number out of range\n");
|
||||
|
||||
return E_FAIL;
|
||||
} else if (req->payload.block_no >= 5){ // emoney auth encrypted
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
char* auth;
|
||||
long size = wslurp(nfc->authdata_path, &auth, false);
|
||||
if (size < 0){
|
||||
sg_nfc_dprintf(nfc, "Failed to read %ls: %lx!\n", nfc->authdata_path, GetLastError());
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
if (req->payload.block_no == 6){
|
||||
offset = 16;
|
||||
} else if (req->payload.block_no == 8){
|
||||
offset = 32;
|
||||
} else if (req->payload.block_no == 9){
|
||||
offset = 48;
|
||||
} else if (req->payload.block_no == 10){
|
||||
offset = 64;
|
||||
} else if (req->payload.block_no == 12){
|
||||
offset = 82;
|
||||
} else if (req->payload.block_no == 13){
|
||||
offset = 98;
|
||||
} else if (req->payload.block_no == 14){
|
||||
offset = 114;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16 && offset + i < size; i++){
|
||||
res->block[i] = auth[offset + i];
|
||||
}
|
||||
|
||||
free(auth);
|
||||
|
||||
} else if (req->payload.block_no == 4){ // emoney auth plain
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
res->block[0] = 0x54; // header
|
||||
res->block[1] = 0x43;
|
||||
res->block[2] = nfc->proxy_flag; // 2 or 3 depending on game (useProxy in env.json)
|
||||
res->block[3] = 0x01; // unknown flag
|
||||
|
||||
} else { // read all other blocks normally
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
memcpy( res->block,
|
||||
nfc->mifare.sectors[0].blocks[req->payload.block_no].bytes,
|
||||
sizeof(res->block));
|
||||
}
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
memcpy( res->block,
|
||||
nfc->mifare.sectors[0].blocks[req->payload.block_no].bytes,
|
||||
sizeof(res->block));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,10 @@ struct sg_nfc {
|
|||
void *ops_ctx;
|
||||
uint8_t addr;
|
||||
unsigned int gen;
|
||||
unsigned int proxy_flag;
|
||||
struct felica felica;
|
||||
struct mifare mifare;
|
||||
const wchar_t* authdata_path;
|
||||
};
|
||||
|
||||
void sg_nfc_init(
|
||||
|
@ -32,6 +34,8 @@ void sg_nfc_init(
|
|||
uint8_t addr,
|
||||
const struct sg_nfc_ops *ops,
|
||||
unsigned int gen,
|
||||
unsigned int proxy_flag,
|
||||
const wchar_t* authdata_path,
|
||||
void *ops_ctx);
|
||||
|
||||
void sg_nfc_transact(
|
||||
|
|
|
@ -81,7 +81,7 @@ HRESULT sg_reader_hook_init(
|
|||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, gen, NULL);
|
||||
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, gen, cfg->proxy_flag, cfg->authdata_path, NULL);
|
||||
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, gen, NULL);
|
||||
|
||||
InitializeCriticalSection(&sg_reader_lock);
|
||||
|
|
|
@ -12,6 +12,8 @@ struct aime_config {
|
|||
unsigned int port_no;
|
||||
bool high_baudrate;
|
||||
unsigned int gen;
|
||||
unsigned int proxy_flag;
|
||||
wchar_t authdata_path[MAX_PATH];
|
||||
};
|
||||
|
||||
HRESULT sg_reader_hook_init(
|
||||
|
|
|
@ -74,8 +74,6 @@ static DWORD CALLBACK carol_pre_startup(void)
|
|||
HMODULE dbghelp;
|
||||
|
||||
dprintf("--- Begin carol_pre_startup ---\n");
|
||||
if ( !SetProcessDPIAware() )
|
||||
dprintf("Failed to set process DPI awareness level!\n");
|
||||
|
||||
/* Pin the D3D shader compiler. This makes startup much faster. */
|
||||
|
||||
|
|
|
@ -196,7 +196,7 @@ static void touch_scan_auto(const bool is_pressed, const uint16_t mouse_x, const
|
|||
|
||||
flg = resp.touches[0].x1 != last_x1 || resp.touches[0].x2 != last_x2 || resp.touches[0].y1 != last_y1 || resp.touches[0].y2 != last_y2;
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
if (flg)
|
||||
dprintf("Touch: Mouse down! x %02X %02X y: %02X %02X\n", resp.touches[0].x1, resp.touches[0].x2, resp.touches[0].y1, resp.touches[0].y2);
|
||||
#endif
|
||||
|
|
|
@ -56,7 +56,8 @@ void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
|
|||
memset(cfg->boot_chip_number, ' ', sizeof(cfg->boot_chip_number));
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"led15093", L"enable", 1, filename);
|
||||
cfg->port_no = 0;
|
||||
cfg->port_no[0] = GetPrivateProfileIntW(L"led15093", L"portNo1", 0, filename);
|
||||
cfg->port_no[1] = GetPrivateProfileIntW(L"led15093", L"portNo2", 0, filename);
|
||||
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
|
||||
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0x90, filename);
|
||||
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xadf7, filename);
|
||||
|
|
|
@ -114,8 +114,9 @@ static DWORD CALLBACK chuni_pre_startup(void)
|
|||
{
|
||||
dprintf("IO DLL doesn't support led_init/led_set_leds, cannot start LED15093 hook\n");
|
||||
} else {
|
||||
unsigned int led_port_no[2] = {10, 11};
|
||||
hr = led15093_hook_init(&chuni_hook_cfg.led15093,
|
||||
chuni_dll.led_init, chuni_dll.led_set_leds, 10, 2, 2, 1);
|
||||
chuni_dll.led_init, chuni_dll.led_set_leds, led_port_no);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
|
|
@ -174,6 +174,20 @@ HRESULT chuni_io_led_init(void)
|
|||
}
|
||||
|
||||
void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb)
|
||||
{
|
||||
{
|
||||
#if 0
|
||||
if (board == 0) {
|
||||
dprintf("CHUNI LED: Left Air 1: red: %d, green: %d, blue: %d\n", rgb[0x96], rgb[0x97], rgb[0x98]);
|
||||
dprintf("CHUNI LED: Left Air 2: red: %d, green: %d, blue: %d\n", rgb[0x99], rgb[0x9A], rgb[0x9B]);
|
||||
dprintf("CHUNI LED: Left Air 3: red: %d, green: %d, blue: %d\n", rgb[0x9C], rgb[0x9D], rgb[0x9E]);
|
||||
}
|
||||
else if (board == 1)
|
||||
{
|
||||
dprintf("CHUNI LED: Right Air 1: red: %d, green: %d, blue: %d\n", rgb[0xB4], rgb[0xB5], rgb[0xB6]);
|
||||
dprintf("CHUNI LED: Right Air 2: red: %d, green: %d, blue: %d\n", rgb[0xB7], rgb[0xB8], rgb[0xB9]);
|
||||
dprintf("CHUNI LED: Right Air 3: red: %d, green: %d, blue: %d\n", rgb[0xBA], rgb[0xBB], rgb[0xBC]);
|
||||
}
|
||||
#endif
|
||||
|
||||
led_output_update(board, rgb);
|
||||
}
|
||||
|
|
|
@ -96,7 +96,8 @@ void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
|
|||
memset(cfg->boot_chip_number, ' ', sizeof(cfg->boot_chip_number));
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"led15093", L"enable", 1, filename);
|
||||
cfg->port_no = 0;
|
||||
cfg->port_no[0] = GetPrivateProfileIntW(L"led15093", L"portNo1", 0, filename);
|
||||
cfg->port_no[1] = GetPrivateProfileIntW(L"led15093", L"portNo2", 0, filename);
|
||||
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
|
||||
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0x90, filename);
|
||||
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xadf7, filename);
|
||||
|
|
|
@ -22,27 +22,23 @@
|
|||
COM4: 837-15396 "Gen 3" Aime Reader
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "amex/amex.h"
|
||||
#include <windows.h>
|
||||
|
||||
#include "board/sg-reader.h"
|
||||
#include "board/vfd.h"
|
||||
|
||||
#include "chuniio/chuniio.h"
|
||||
#include "chusanhook/config.h"
|
||||
#include "chusanhook/io4.h"
|
||||
#include "chusanhook/slider.h"
|
||||
|
||||
#include "chuniio/chuniio.h"
|
||||
|
||||
#include "hook/process.h"
|
||||
|
||||
#include "gfxhook/d3d9.h"
|
||||
#include "gfxhook/gfx.h"
|
||||
|
||||
#include "hook/process.h"
|
||||
|
||||
#include "hooklib/serial.h"
|
||||
#include "hooklib/spike.h"
|
||||
|
||||
|
@ -55,8 +51,7 @@ static HMODULE chusan_hook_mod;
|
|||
static process_entry_t chusan_startup;
|
||||
static struct chusan_hook_config chusan_hook_cfg;
|
||||
|
||||
static DWORD CALLBACK chusan_pre_startup(void)
|
||||
{
|
||||
static DWORD CALLBACK chusan_pre_startup(void) {
|
||||
HMODULE d3dc;
|
||||
HMODULE dbghelp;
|
||||
HRESULT hr;
|
||||
|
@ -88,7 +83,7 @@ static DWORD CALLBACK chusan_pre_startup(void)
|
|||
chusan_hook_config_load(&chusan_hook_cfg, get_config_path());
|
||||
|
||||
/* Hook Win32 APIs */
|
||||
|
||||
|
||||
dvd_hook_init(&chusan_hook_cfg.dvd, chusan_hook_mod);
|
||||
gfx_hook_init(&chusan_hook_cfg.gfx);
|
||||
gfx_d3d9_hook_init(&chusan_hook_cfg.gfx, chusan_hook_mod);
|
||||
|
@ -96,6 +91,16 @@ static DWORD CALLBACK chusan_pre_startup(void)
|
|||
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
struct dipsw_config new_dipsw_config[8] = {
|
||||
{L"Delivery Server", L"Server", L"Client"},
|
||||
{L"Monitor Type", L"60FPS", L"120FPS"},
|
||||
{L"Cabinet Type", L"CVT", L"SP"},
|
||||
};
|
||||
|
||||
// Set the system dip switch configuration
|
||||
memcpy(chusan_hook_cfg.platform.system.dipsw_config, new_dipsw_config,
|
||||
sizeof(new_dipsw_config));
|
||||
|
||||
hr = platform_hook_init(
|
||||
&chusan_hook_cfg.platform,
|
||||
"SDHD",
|
||||
|
@ -124,27 +129,7 @@ static DWORD CALLBACK chusan_pre_startup(void)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
bool *dipsw = &chusan_hook_cfg.platform.system.dipsw[0];
|
||||
bool is_cvt = dipsw[2];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
dprintf("DipSw: NetInstall: %s\n", dipsw[0] ? "Server" : "Client");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
dprintf("DipSw: Monitor Type: %dFPS\n", dipsw[1] ? 60 : 120);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
dprintf("DipSw: Cab Type: %s\n", is_cvt ? "CVT" : "SP");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int first_port = is_cvt ? 2 : 20;
|
||||
bool is_cvt = chusan_hook_cfg.platform.system.dipsw[2];
|
||||
|
||||
if (!is_cvt) {
|
||||
hr = vfd_hook_init(&chusan_hook_cfg.vfd, 2);
|
||||
|
@ -154,19 +139,31 @@ static DWORD CALLBACK chusan_pre_startup(void)
|
|||
}
|
||||
}
|
||||
|
||||
if ( chuni_dll.led_init == NULL || chuni_dll.led_set_leds == NULL )
|
||||
{
|
||||
dprintf("IO DLL doesn't support led_init/led_set_leds, cannot start LED15093 hook\n");
|
||||
unsigned int led_port_no[2];
|
||||
|
||||
if (is_cvt) {
|
||||
led_port_no[0] = 2;
|
||||
led_port_no[1] = 3;
|
||||
} else {
|
||||
hr = led15093_hook_init(&chusan_hook_cfg.led15093,
|
||||
chuni_dll.led_init, chuni_dll.led_set_leds, first_port, 2, 2, 1);
|
||||
led_port_no[0] = 20;
|
||||
led_port_no[1] = 21;
|
||||
}
|
||||
|
||||
if (chuni_dll.led_init == NULL || chuni_dll.led_set_leds == NULL) {
|
||||
dprintf(
|
||||
"IO DLL doesn't support led_init/led_set_leds, cannot start "
|
||||
"LED15093 hook\n");
|
||||
} else {
|
||||
hr = led15093_hook_init(&chusan_hook_cfg.led15093, chuni_dll.led_init,
|
||||
chuni_dll.led_set_leds, led_port_no);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&chusan_hook_cfg.aime, 4, is_cvt ? 2: 3, chusan_hook_mod);
|
||||
hr = sg_reader_hook_init(&chusan_hook_cfg.aime, 4, is_cvt ? 2 : 3,
|
||||
chusan_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
@ -186,8 +183,7 @@ fail:
|
|||
ExitProcess(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
|
||||
{
|
||||
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) {
|
||||
HRESULT hr;
|
||||
|
||||
if (cause != DLL_PROCESS_ATTACH) {
|
||||
|
@ -199,7 +195,7 @@ BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
|
|||
hr = process_hijack_startup(chusan_pre_startup, &chusan_startup);
|
||||
|
||||
if (!SUCCEEDED(hr)) {
|
||||
dprintf("Failed to hijack process startup: %x\n", (int) hr);
|
||||
dprintf("Failed to hijack process startup: %x\n", (int)hr);
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
|
|
|
@ -63,6 +63,14 @@ static DWORD CALLBACK cm_pre_startup(void)
|
|||
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
struct dipsw_config new_dipsw_config[8] = {
|
||||
{L"Delivery Server", L"Client", L"Server"},
|
||||
};
|
||||
|
||||
// Set the system dip switch configuration
|
||||
memcpy(cm_hook_cfg.platform.system.dipsw_config, new_dipsw_config,
|
||||
sizeof(new_dipsw_config));
|
||||
|
||||
hr = platform_hook_init(
|
||||
&cm_hook_cfg.platform,
|
||||
"SDED",
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
pushd %~dp0
|
||||
|
||||
taskkill /f /im aimeReaderHost.exe > nul 2>&1
|
||||
|
||||
start /min inject -d -k carolhook.dll aimeReaderHost.exe -p 10
|
||||
|
||||
inject -d -k carolhook.dll carol_nu.exe
|
||||
|
||||
taskkill /f /im aimeReaderHost.exe > nul 2>&1
|
||||
|
||||
echo.
|
|
@ -72,6 +72,8 @@ windowed=1
|
|||
framed=1
|
||||
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
||||
monitor=0
|
||||
; Enable DPI awareness for the game process, preventing Windows from stretching the game window if a DPI scaling higher than 100% is used
|
||||
dpiAware=1
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Custom IO settings
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
pushd %~dp0
|
||||
|
||||
start /min inject -d -k chunihook.dll aimeReaderHost.exe -p 12
|
||||
|
||||
inject -d -k chunihook.dll chuniApp.exe
|
||||
|
||||
taskkill /f /im aimeReaderHost.exe > nul 2>&1
|
||||
|
||||
echo.
|
|
@ -64,6 +64,8 @@ windowed=1
|
|||
framed=1
|
||||
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
||||
monitor=0
|
||||
; Enable DPI awareness for the game process, preventing Windows from stretching the game window if a DPI scaling higher than 100% is used
|
||||
dpiAware=1
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; LED settings
|
||||
|
|
|
@ -90,6 +90,8 @@ windowed=1
|
|||
framed=0
|
||||
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
||||
monitor=0
|
||||
; Enable DPI awareness for the game process, preventing Windows from stretching the game window if a DPI scaling higher than 100% is used
|
||||
dpiAware=1
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; LED settings
|
||||
|
|
|
@ -60,7 +60,7 @@ enable=1
|
|||
subnet=192.168.150.0
|
||||
billingCa=../DEVICE/ca.crt
|
||||
billingPub=../DEVICE/billing.pub
|
||||
billingType=2
|
||||
billingType=0
|
||||
|
||||
[eeprom]
|
||||
; See above
|
||||
|
@ -83,6 +83,8 @@ windowed=1
|
|||
framed=1
|
||||
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
||||
monitor=0
|
||||
; Enable DPI awareness for the game process, preventing Windows from stretching the game window if a DPI scaling higher than 100% is used
|
||||
dpiAware=1
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Custom IO settings
|
||||
|
|
|
@ -69,6 +69,9 @@ enable=1
|
|||
windowed=1
|
||||
; Add a frame to the game window if running windowed.
|
||||
framed=0
|
||||
; Enable DPI awareness for the game process, preventing Windows from stretching the game window if a DPI scaling higher than 100% is used
|
||||
dpiAware=1
|
||||
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Custom IO settings
|
||||
|
|
|
@ -41,7 +41,7 @@ portNo=17
|
|||
; 837-15093-06 LED board emulation setting.
|
||||
enable=1
|
||||
; COM port number for the LED board. Has to be the same as the FTDI port.
|
||||
portNo=17
|
||||
portNo1=17
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Network settings
|
||||
|
@ -92,6 +92,8 @@ enable=1
|
|||
windowed=0
|
||||
; Add a frame to the game window if running windowed.
|
||||
framed=0
|
||||
; Enable DPI awareness for the game process, preventing Windows from stretching the game window if a DPI scaling higher than 100% is used
|
||||
dpiAware=1
|
||||
|
||||
[touch]
|
||||
; WinTouch emulation setting.
|
||||
|
@ -156,8 +158,11 @@ coin=0x72
|
|||
; : : AIME. : :
|
||||
; '·:..............................................:·'
|
||||
;
|
||||
; Only XInput is currently supported.
|
||||
|
||||
; Select the input mode. "xinput" for controller, "keyboard" for keyboard.
|
||||
mode=xinput
|
||||
|
||||
[xinput]
|
||||
; XInput bindings
|
||||
;
|
||||
; Left Stick Joystick
|
||||
|
@ -166,3 +171,27 @@ coin=0x72
|
|||
; Left Shoulder Switch Target
|
||||
; A/B Attack
|
||||
; X/Y Noble Phantasm
|
||||
|
||||
; Configure deadzones for the left thumbsticks.
|
||||
; The default value for the left stick is 7849, max value is 32767.
|
||||
stickDeadzone=7849
|
||||
|
||||
[keyboard]
|
||||
; Keyboard bindings:
|
||||
|
||||
; Stick controls (default: WASD)
|
||||
up=0x57
|
||||
left=0x41
|
||||
down=0x53
|
||||
right=0x44
|
||||
|
||||
; Attack (default: Space)
|
||||
attack=0x20
|
||||
; Dash (default: LSHIFT)
|
||||
dash=0xa0
|
||||
; Change Target (default: J)
|
||||
target=0x4A
|
||||
; Re-center camera (default: K)
|
||||
camera=0x4B
|
||||
; Noble Phantasm (default: L)
|
||||
np=0x4C
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
pushd %~dp0
|
||||
|
||||
start /min "AM Daemon" inject -d -k idzhook.dll amdaemon.exe -c configDHCP_Final_Common.json configDHCP_Final_JP.json configDHCP_Final_JP_ST1.json configDHCP_Final_JP_ST2.json configDHCP_Final_EX.json configDHCP_Final_EX_ST1.json configDHCP_Final_EX_ST2.json
|
||||
start "AM Daemon" /min inject -d -k idzhook.dll amdaemon.exe -c configDHCP_Final_Common.json configDHCP_Final_JP.json configDHCP_Final_JP_ST1.json configDHCP_Final_JP_ST2.json configDHCP_Final_EX.json configDHCP_Final_EX_ST1.json configDHCP_Final_EX_ST2.json
|
||||
|
||||
rem Set dipsw1=0 and uncomment the ServerBox for in store battle?
|
||||
rem inject -k idzhook.dll ServerBoxD8_Nu_x64.exe
|
|
@ -97,6 +97,9 @@ windowed=0
|
|||
framed=1
|
||||
; Select the monitor to run the game on. (Fullscreen only, 0=primary screen)
|
||||
monitor=0
|
||||
; Enable DPI awareness for the game process, preventing Windows from stretching the game window if a DPI scaling higher than 100% is used
|
||||
dpiAware=1
|
||||
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Custom IO settings
|
||||
|
|
|
@ -69,6 +69,15 @@ freeplay=0
|
|||
; this to 1 on exactly one machine and set this to 0 on all others.
|
||||
dipsw1=1
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; LED settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[led15070]
|
||||
; Enable emulation of the 837-15070-04 controlled lights, which handle the
|
||||
; cabinet and button LEDs.
|
||||
enable=1
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Misc. hook settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
@ -126,11 +135,22 @@ coin=0x72
|
|||
; Uncomment and complete the following sequence of settings to configure a
|
||||
; custom keybinding.
|
||||
[button]
|
||||
;1p_btn1=0x53
|
||||
;1p_btn2=0x53
|
||||
;1p_btn3=0x53
|
||||
enable=1
|
||||
;p1Btn1=0x53
|
||||
;p1Btn2=0x53
|
||||
;p1Btn3=0x53
|
||||
; ... etc ...
|
||||
;2p_btn1=0x53
|
||||
;2p_btn2=0x53
|
||||
;2p_btn3=0x53
|
||||
;p2Btn1=0x53
|
||||
;p2Btn2=0x53
|
||||
;p2Btn3=0x53
|
||||
; ... etc ...
|
||||
|
||||
[touch]
|
||||
p1Enable=1
|
||||
;p1DebugInput=0
|
||||
p2Enable=1
|
||||
;p2DebugInput=0
|
||||
;p1TouchA1=0x53
|
||||
;p1TouchA2=0x53
|
||||
; ... etc ...
|
||||
;p1TouchE8=0x53
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
@echo off
|
||||
pushd %~dp0
|
||||
|
||||
taskkill /f /im amdaemon.exe > nul 2>&1
|
||||
|
||||
REM USA
|
||||
start "AM Daemon" /min inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_usa.json
|
||||
REM start "AM Daemon" /min inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_usa.json
|
||||
|
||||
REM JP
|
||||
start "AM Daemon" /min inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_jpn.json
|
|
@ -5,7 +5,7 @@
|
|||
[vfs]
|
||||
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
|
||||
amfs=
|
||||
; Insert the path to the game Option directory here (contains Axxx directories)
|
||||
; Insert the path to the game Option directory here (unused in Mercury)
|
||||
option=
|
||||
; Create an empty directory somewhere and insert the path here.
|
||||
; This directory may be shared between multiple SEGA games.
|
||||
|
@ -76,6 +76,9 @@ dipsw1=1
|
|||
[gfx]
|
||||
; Enables the graphics hook.
|
||||
enable=1
|
||||
; Enable DPI awareness for the game process, preventing Windows from stretching the game window if a DPI scaling higher than 100% is used
|
||||
dpiAware=1
|
||||
|
||||
|
||||
; Hooks related to the touch boards
|
||||
[touch]
|
||||
|
|
|
@ -72,6 +72,9 @@ dipsw1=1
|
|||
[gfx]
|
||||
; Enables the graphics hook.
|
||||
enable=1
|
||||
; Enable DPI awareness for the game process, preventing Windows from stretching the game window if a DPI scaling higher than 100% is used
|
||||
dpiAware=1
|
||||
|
||||
|
||||
[unity]
|
||||
; Enable Unity hook. This will allow you to run custom .NET code before the game
|
||||
|
|
|
@ -44,7 +44,7 @@ option=../../option
|
|||
1. In the `[dns]` section, set `default=` to your computer's hostname or LAN IP. Do not put
|
||||
`127.0.0.1` here, the game specifically checks for and rejects loopback addresses. This setting
|
||||
controls the address of the network services server
|
||||
1. Right click `start.bat` in `app/bin` and run it as Administrator. I think you need to run it as
|
||||
1. Right click `launch.bat` in `app/bin` and run it as Administrator. I think you need to run it as
|
||||
admin at least once, but once you have done that you can run the game as a regular user
|
||||
1. A sequence of several start-up screens will be displayed. You should also see a bunch of debug
|
||||
output in a command line window; if you're seeing hex dumps here then that's a good sign. There
|
||||
|
|
|
@ -11,7 +11,7 @@ The default file path for config file is `./segatools.ini`.
|
|||
|
||||
You can modify environment variable `SEGATOOLS_CONFIG_PATH` to another path.
|
||||
|
||||
For example, You can have another `start.bat` with following code in it,
|
||||
For example, You can have another `launch.bat` with following code in it,
|
||||
Then you can copy `segatools.ini` to `another_config.ini` but with different dns host in it
|
||||
```bat
|
||||
set SEGATOOLS_CONFIG_PATH=.\another_config.ini
|
||||
|
@ -101,6 +101,18 @@ emulates an IC card in its proximity. A variety of different IC cards can be
|
|||
emulated; the exact choice of card that is emulated depends on the presence or
|
||||
absence of the configured card ID files.
|
||||
|
||||
### `proxyFlag`
|
||||
|
||||
Default: `2`
|
||||
|
||||
The "proxy flag" of the emulated Thinca authentication card. This should be 2 if no proxy is used, and 3 if it is. Invalid values will break Thinca authentication card reading. This information can be obtained by checking for the presence of "use_proxy: true" `tfps-res-pro\env.json`.
|
||||
|
||||
### `authdataPath`
|
||||
|
||||
Default: `DEVICE\authdata.bin`
|
||||
|
||||
Path to the binary file containing data for a Thinca authentication card (see `emoney.txt`)
|
||||
|
||||
## `[vfd]`
|
||||
|
||||
Controls emulation of the VFD GP1232A02A FUTABA assembly.
|
||||
|
@ -342,6 +354,38 @@ Nu chassis DIP switch settings:
|
|||
- `111`: 1920x1080
|
||||
- Switch 8: Game-specific. Not used in any shipping game.
|
||||
|
||||
## `[gfx]`
|
||||
|
||||
### `enable`
|
||||
|
||||
Default: `1`
|
||||
|
||||
Enables graphic hooks.
|
||||
|
||||
### `windowed`
|
||||
|
||||
Default: `0`
|
||||
|
||||
Force the game to run windowed.
|
||||
|
||||
### `framed`
|
||||
|
||||
Default: `0`
|
||||
|
||||
Add a frame to the game window if running windowed.
|
||||
|
||||
### `monitor`
|
||||
|
||||
Default: `0`
|
||||
|
||||
Select the monitor to run the game on. (Fullscreen only, 0 = primary screen)
|
||||
|
||||
### `dpiAware`
|
||||
|
||||
Default: `1`
|
||||
|
||||
Sets the game to be DPI-aware. This prevents Windows automatically scaling the game window by your desktop's scaling factor, which may cause blurry graphics.
|
||||
|
||||
## `[hwmon]`
|
||||
|
||||
Configure stub implementation of the platform hardware monitor driver. The
|
||||
|
@ -420,18 +464,22 @@ Default: (Varies depending on game)
|
|||
|
||||
Override the game's four-character platform code (e.g. `AAV2` for Nu 2). This
|
||||
is actually supposed to be a separate three-character `platformId` and
|
||||
integer `modelType` setting, but they are combined here for convenience. Valid
|
||||
values include:
|
||||
integer `modelType` setting, but they are combined here for convenience.
|
||||
|
||||
- `AAV0`: Nu 1 (Project DIVA)
|
||||
- `AAV1`: Nu 1.1 (Chunithm)
|
||||
- `AAV2`: Nu 2 (Initial D Zero)
|
||||
- `AAW0`: NuSX 1
|
||||
- `AAW1`: NuSX 1.1
|
||||
- `ACA0`: ALLS UX
|
||||
- `ACA1`: ALLS HX
|
||||
- `ACA2`: ALLS UX (without dedicated GPU)
|
||||
- `ACA4`: ALLS MX
|
||||
`platformId` is one of the following:
|
||||
- `AAV`: Nu 1/1.1/2
|
||||
- `AAW`: NuSX 1/1.1
|
||||
- `ACA`: ALLS UX/HX/MX
|
||||
|
||||
`modelType` is one of the following:
|
||||
- `1`: Server (SV)
|
||||
- `2`: Satalite (ST)
|
||||
- `3`: Live (LV)
|
||||
- `4`: Terminal (TN)
|
||||
|
||||
It's safe to assume that every game you'll be playing with these tools will be a Satalite.
|
||||
Some games care, others don't. Some even change how they run based on this value (Wonderland Wars
|
||||
will boot into terminal mode if `modelType` is 4).
|
||||
|
||||
### `region`
|
||||
|
||||
|
@ -468,15 +516,9 @@ the billing transactions.
|
|||
|
||||
Default: `1`
|
||||
|
||||
Set the billing "type" for the keychip. The type determins what kind of revenue share,
|
||||
if any, the game maker has with SEGA. Some games may be picky and require types other
|
||||
then 1 (ex. crossbeats REV. requires billing type 2), so this option is provided if this
|
||||
is an issue. Billing types are:
|
||||
|
||||
- 0: No billing?
|
||||
- 1: Billing type A
|
||||
- 2: Billing type B1
|
||||
- 3: Billing type B2
|
||||
Sets the billing type, a single bit value that flags the cabinet as a rental if set to `1`, or `0` otherwise.
|
||||
Certian games that were only sold officially as full purchases (that is, non-rentals) must have this value set to 0.
|
||||
NOTE: Crossbeats erroniously displays Billing modes A/B1/B2, but this value comes from the `modelType` and NOT this value!
|
||||
|
||||
### `systemFlag`
|
||||
|
||||
|
@ -607,15 +649,18 @@ Configure the location of the "Option" data mount point. This mount point is
|
|||
optional (hence the name, probably) and contains directories which contain
|
||||
minor over-the-air content updates.
|
||||
|
||||
## `[openssl]`
|
||||
## `[epay]`
|
||||
|
||||
Enable or disable the application of the OpenSSL patch for Intel Gen 10 or newer CPUs.
|
||||
Configure Thinca Payment (E-Money) emulation and hooks.
|
||||
|
||||
### `enable`
|
||||
|
||||
Default: `1`
|
||||
|
||||
Enable the patch automatically sets the OPENSSL_ia32cap variable in the user's environment
|
||||
table if the CPU meets the requirements. This setting must be enabled on PCs with
|
||||
Intel Gen 10 or newer CPUs to ensure the game runs properly, but it's ok to leave enabled
|
||||
on other CPUs since the patch will not run if the CPU requirements are not met.
|
||||
Enables the Thinca emulation. This will allow you to enable E-Money on compatible servers.
|
||||
|
||||
### `hook`
|
||||
|
||||
Default: `0`
|
||||
|
||||
Enables hooking of respective Thinca DLL functions to emulate the existence of E-Money. This cannot be used with a real E-Money server.
|
|
@ -0,0 +1,213 @@
|
|||
# E-Money Authentication Procedure
|
||||
by Haruka Akechi
|
||||
|
||||
### SETTING UP
|
||||
|
||||
1) Obtain the 64 byte long authentication card encryption key. `amdaemon.exe` holds the secrets.
|
||||
|
||||
2) Inside the `emoney\` folder, install the python modules and launch the generator script:
|
||||
|
||||
```shell
|
||||
python -m pip install -r requirements.txt
|
||||
python authcardgen.py --key <ENTER YOUR KEY HERE>
|
||||
```
|
||||
|
||||
```
|
||||
Usage: authcardgen.py [OPTIONS]
|
||||
|
||||
Options:
|
||||
--cardid TEXT Card ID (64 hex characters)
|
||||
--key TEXT Key (128 hex characters, required) [required]
|
||||
--store-card-id TEXT Store Card ID (padded to 16 bytes)
|
||||
--merchant-code TEXT Merchant Code (padded to 20 bytes)
|
||||
--store-branch-id TEXT Store Branch ID (padded to 12 bytes)
|
||||
--passphrase TEXT Passphrase, used for the pfx password (padded to 16 bytes)
|
||||
--output TEXT Output filename
|
||||
--help Show this message and exit.
|
||||
```
|
||||
|
||||
3) Place the generated `authcard.bin` in your `DEVICE\` folder.
|
||||
|
||||
4) Check `tfps-res-pro\env.json` for your game. If it contains a `"use_proxy": true` statement, add to segatools.ini:
|
||||
|
||||
```ini
|
||||
[aime]
|
||||
proxy_flag=3
|
||||
```
|
||||
|
||||
5) Replace the two URLs in `tfps-res-pro\resource.xml` to your servers'. This is to ensure the Host header will match the certificate's.
|
||||
|
||||
6) Where amdaemon.exe is located, there should be a `ca.pem`. Replace this file with either [this](https://curl.se/ca/cacert.pem) for the most common CA's (including Let's Encrypt), or whatever CA the server is using (your server will provide this).
|
||||
|
||||
7) Run your game and enter the test menu, and navigate to E-Money Settings.
|
||||
|
||||
8) Select "Terminal Authentication"
|
||||
|
||||
9) Hold your key for scanning a card (default: RETURN)
|
||||
|
||||
10) If your shop name shows up, everything was done succesfully. Otherwise, check the VFD.
|
||||
|
||||
### TECHNICAL INFO
|
||||
|
||||
For debugging anything e-money related, I highly recommend setting "emoney.log.level" to 4 in your game's amdaemon config.json. This should create a `<amfs>\emoney_log\thincapayment.log`.
|
||||
|
||||
When terminal authentication is started from the test menu, the game will check for an Aime reader of at least generation 3. If that is not fulfilled, the VFD will display "unsupported card reader" and abort. If the card reader is good, the VFD will prompt for a card to be touched, and the reader will start scanning for a MIFARE card, which from this point we call "Thinca Authentication Card". This card contains one unencrypted block (3) which contains:
|
||||
|
||||
[0] = 0x54 // T
|
||||
|
||||
[1] = 0x43 // C
|
||||
|
||||
[2] = proxy_type
|
||||
|
||||
[3] = 0x01
|
||||
|
||||
Afterwards, a number of encrypted blocks are read, namely the blocks 5,6,8,9,10,12,13 and 14. These blocks together form a 130 byte long binary blob that contains the authentication data.
|
||||
|
||||
This data is encrypted as following:
|
||||
|
||||
Given a fixed 0x40 byte long encryption key and a fixed 0x20 byte long static "card ID", both of which can be found in amdaemon:
|
||||
XOR every byte of the encryption key with 0x1C.
|
||||
Calculate a 0x20 bytes long HMAC-SHA-256 of the card ID with the XOR'ed encryption key as the key.
|
||||
Calculate the needed IV by XORing the lower 0x10 bytes of the HMAC with the upper 0x10 bytes of the HMAC:
|
||||
|
||||
```
|
||||
byte[] iv = new byte[16];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
iv[i] = (byte) (hmac[i + 16] ^ hmac[i]);
|
||||
}
|
||||
```
|
||||
|
||||
With this IV, and the HMAC as the key, finally encrypt the data with AES/CBC/PKCS5Padding.
|
||||
|
||||
Now what is actually stored on such a card? This:
|
||||
|
||||
```
|
||||
+---------------+---------------+-----------------+------------+----------+
|
||||
| Store Card ID | Merchant Code | Store Branch ID | Passphrase | NULL |
|
||||
+---------------+---------------+-----------------+------------+----------+
|
||||
| 0x10 bytes | 0x14 bytes | 0xC bytes | 0x10 bytes | 0x1 byte |
|
||||
| char* | char* | uint128_t | char* | NULL |
|
||||
+---------------+---------------+-----------------+------------+----------+
|
||||
```
|
||||
|
||||
Only two things really matter here. The Store Branch ID must be non-zero, otherwise amdaemon will reject it, and the passphrase, which is the PFX key password (passphrase during authcard creation) for the certificate returned in the network response (see below).
|
||||
|
||||
Technically with the Store Card ID you could bind different auth cards to different users, but for home usage, it really doesn't matter.
|
||||
|
||||
That's the Thinca Authentication Card out of the way, so continue on to:
|
||||
|
||||
### NETWORK
|
||||
|
||||
First, a regular HTTP(S) connection will be made to the URL specified in tfps-res-pro\env.json, tasms.root_endpoint.
|
||||
|
||||
Request Data:
|
||||
`{"modelName":"ACA","serialNumber":"ACAE01A9999","merchantCode":"NOTSEGA","storeBranchNumber":11111,"storeCardId":"FAKESTORE"}`
|
||||
|
||||
Note that the serialNumber here actually isn't the keychip ID, but rather the PCBID. The three other values are read from the Thinca Authentication Card.
|
||||
|
||||
Response Headers:
|
||||
`x-certificate-md5: <md5 of the certificate string>`
|
||||
|
||||
Response Data:
|
||||
```
|
||||
{
|
||||
"certificate": "<base 64 encoded pfx string, with the authentication card's passphrase being the pfx password>",
|
||||
"initSettings": {
|
||||
"endpoints": {
|
||||
"terminals": {
|
||||
"uri": "https://localhost/emoney/terminals"
|
||||
},
|
||||
"statuses": {
|
||||
"uri": "https://localhost/emoney/statuses"
|
||||
},
|
||||
"sales": {
|
||||
"uri": "https://localhost/emoney/sales"
|
||||
},
|
||||
"counters": {
|
||||
"uri": "https://localhost/emoney/counters"
|
||||
}
|
||||
},
|
||||
"intervals": {
|
||||
"checkSetting": 60,
|
||||
"sendStatus": 60
|
||||
},
|
||||
"settigsType": "AmusementTerminalSettings", // sic
|
||||
"status": "1", // a string
|
||||
"terminalId": "536453645364536453645364536453", // must be exactly 30 characters
|
||||
"version": "2024-01-01T01:01:01", // a timestamp
|
||||
"availableElectronicMoney": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
5,
|
||||
6,
|
||||
8,
|
||||
9,
|
||||
91, // aimepay
|
||||
101 // "cash" ???
|
||||
],
|
||||
"cashAvailability": true,
|
||||
"productCode": 1337
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next up, we will connect to the TLAM service to get the URL for the TCAP service. Everything from this point on requires not only HTTPS, but also client certificate validation (which is the certificate returned from the previous request). Technically you don't need to validate it, but you must accept a client certificate or the client HTTPS library will not be happy.
|
||||
|
||||
The client certificate itself must be signed with the same key than the server's HTTPS certificate.
|
||||
|
||||
The client AND server certificate must have it's CA included in the "ca.pem" file in amdaemon's directory. You can freely replace this file with https://curl.se/ca/cacert.pem to allow Let's Encrypt and whatever else.
|
||||
|
||||
At this point, "ThincaPayment::setClientCertificate(). ErrCode 203" means that the downloaded file couldn't be found, or the certificate password is wrong.
|
||||
|
||||
[Warn ] TCAP communicate error 06514086 means the ca.pem has no entry for the given server CA.
|
||||
|
||||
TLAM:
|
||||
The TLAM url comes from tftp-res-pro\resource.xml, commonPrimaryUri.
|
||||
|
||||
<commonPrimaryUri>/initauth.jsp
|
||||
|
||||
Request Data:
|
||||
<none>
|
||||
Response Headers:
|
||||
`Content-Type: application/x-tlam`
|
||||
Response Data:
|
||||
`SERV=https://localhost/emoney/tcap`
|
||||
|
||||
TCAP:
|
||||
Here be dragons.
|
||||
|
||||
Is it this?? https://en.wikipedia.org/wiki/Transaction_Capabilities_Application_Part
|
||||
Maybe thincatcapclient.dll holds the secrets?
|
||||
|
||||
Request Data:
|
||||
```
|
||||
02 05 01 00 ba 00 00 00 21 00 00 00 00 00 25 00 ....º... !.....%.
|
||||
9f 00 01 00 00 07 47 65 6e 65 72 69 63 06 43 4c ......Ge neric.CL
|
||||
49 45 4e 54 00 02 00 00 07 47 65 6e 65 72 69 63 IENT.... .Generic
|
||||
06 53 54 41 54 55 53 00 03 00 00 07 47 65 6e 65 .STATUS. ....Gene
|
||||
72 69 63 06 4f 50 54 49 4f 4e 00 04 00 00 06 46 ric.OPTI ON.....F
|
||||
65 6c 69 43 61 03 52 2f 57 00 05 00 00 07 47 65 eliCa.R/ W.....Ge
|
||||
6e 65 72 69 63 09 52 2f 57 5f 45 56 45 4e 54 00 neric.R/ W_EVENT.
|
||||
06 00 00 07 47 65 6e 65 72 69 63 0a 52 2f 57 5f ....Gene ric.R/W_
|
||||
53 54 41 54 55 53 00 07 00 00 07 47 65 6e 65 72 STATUS.. ...Gener
|
||||
69 63 0a 52 2f 57 5f 4f 50 54 49 4f 4e 00 08 00 ic.R/W_O PTION...
|
||||
00 07 47 65 6e 65 72 69 63 06 4e 46 43 5f 52 57 ..Generi c.NFC_RW
|
||||
00 00 00 26 00 03 02 05 00 00 00 00 22 00 00 ...&.... ...."..
|
||||
```
|
||||
|
||||
and
|
||||
```
|
||||
02 05 03 00 17 00 00 00 21 00 11 34 34 20 30 30 ........ !..44 00
|
||||
20 30 31 20 32 31 20 30 30 20 30 30 01 21 0 0 00
|
||||
```
|
||||
|
||||
Response Headers:
|
||||
```
|
||||
Content-Type: application/x-tcap
|
||||
Transfer-Encoding: chunked
|
||||
```
|
||||
Response Data:
|
||||
????
|
||||
|
||||
To be continued ...
|
|
@ -65,7 +65,7 @@ felicaGen=0
|
|||
and fill it with 20 digits, for example `01234567891234567890`. Make sure to
|
||||
save the file.
|
||||
|
||||
1. Right click `start.bat` in `package` and run it as Administrator. After the
|
||||
1. Right click `launch.bat` in `package` and run it as Administrator. After the
|
||||
first run you may be able to run the game as a normal user.
|
||||
1. Once you're at the title screen, press 2 or 3 a few times to add some
|
||||
credits, then _hold_ the Enter key for a few seconds to scan a card and start
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
import hmac
|
||||
import hashlib
|
||||
import click
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import pad, unpad
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class CardEncryptor:
|
||||
def __init__(
|
||||
self,
|
||||
cardid_hex,
|
||||
key_hex,
|
||||
store_card_id_str="FAKESTORE",
|
||||
merchant_code_str="NOTSEGA",
|
||||
store_branch_id_str="11111",
|
||||
passphrase_str="573",
|
||||
output_file="authdata.bin",
|
||||
):
|
||||
self.cardid = bytes.fromhex(cardid_hex)
|
||||
self.key = bytearray.fromhex(key_hex)
|
||||
self.output_file = output_file
|
||||
|
||||
if len(self.cardid) != 0x20:
|
||||
raise ValueError("Card ID must be 32 bytes (64 hex characters)")
|
||||
if len(self.key) != 0x40:
|
||||
raise ValueError("Key must be 64 bytes (128 hex characters)")
|
||||
|
||||
# XOR the key with 0x1C as in original Java
|
||||
for i in range(len(self.key)):
|
||||
self.key[i] ^= 0x1C
|
||||
|
||||
self.store_card_id = self._str_to_bytes(store_card_id_str, 0x10)
|
||||
self.merchant_code = self._str_to_bytes(merchant_code_str, 0x14)
|
||||
self.store_branch_id = self._str_to_bytes(store_branch_id_str, 0x0C)
|
||||
self.passphrase = self._str_to_bytes(passphrase_str, 0x10)
|
||||
|
||||
# Construct full data payload
|
||||
self.data = (
|
||||
self.store_card_id
|
||||
+ self.merchant_code
|
||||
+ self.store_branch_id
|
||||
+ self.passphrase
|
||||
+ bytes([0x00]) # +1 null terminator / padding byte
|
||||
)
|
||||
|
||||
def _str_to_bytes(self, s, length):
|
||||
b = bytearray(length)
|
||||
b[: len(s)] = s.encode()
|
||||
return bytes(b)
|
||||
|
||||
def _bytes_to_str(self, b):
|
||||
return b.decode()
|
||||
|
||||
def _bytes_to_hex(self, b):
|
||||
return b.hex().upper()
|
||||
|
||||
def _calculate_hmac_sha256(self, key, data, length):
|
||||
h = hmac.new(key, data, hashlib.sha256)
|
||||
return h.digest()[:length]
|
||||
|
||||
def _aes_cbc_encrypt(self, key, data, iv):
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
return cipher.encrypt(pad(data, AES.block_size))
|
||||
|
||||
def _aes_cbc_decrypt(self, key, data, iv):
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
return unpad(cipher.decrypt(data), AES.block_size)
|
||||
|
||||
def run(self):
|
||||
print("Card ID:\t\t", self._bytes_to_hex(self.cardid))
|
||||
print("Store Card ID:\t\t", self._bytes_to_str(self.store_card_id))
|
||||
print("Merchant Code:\t\t", self._bytes_to_str(self.merchant_code))
|
||||
print("Store Branch ID:\t", self._bytes_to_str(self.store_branch_id))
|
||||
print("Passphrase:\t\t", self._bytes_to_str(self.passphrase))
|
||||
|
||||
hmac_output = self._calculate_hmac_sha256(self.key, self.cardid, 0x20)
|
||||
# print("HMAC:\t\t", self._bytes_to_hex(hmac_output))
|
||||
|
||||
iv = bytes([hmac_output[i + 16] ^ hmac_output[i] for i in range(16)])
|
||||
# print("IV:\t\t", self._bytes_to_hex(iv))
|
||||
|
||||
encrypted = self._aes_cbc_encrypt(hmac_output, self.data, iv)
|
||||
# print("ENCRYPTED:\t", self._bytes_to_hex(encrypted))
|
||||
Path(self.output_file).write_bytes(encrypted)
|
||||
|
||||
decrypted = self._aes_cbc_decrypt(hmac_output, encrypted, iv)
|
||||
# print("DECRYPTED:\t", self._bytes_to_hex(decrypted))
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
"--cardid",
|
||||
default="0102030401020304010203040102030401020304010203040102030401020304",
|
||||
help="Card ID (64 hex characters)",
|
||||
)
|
||||
@click.option(
|
||||
"--key",
|
||||
required=True,
|
||||
help="Key (128 hex characters, required)",
|
||||
)
|
||||
@click.option(
|
||||
"--store-card-id", default="FAKESTORE", help="Store Card ID (padded to 16 bytes)"
|
||||
)
|
||||
@click.option(
|
||||
"--merchant-code", default="NOTSEGA", help="Merchant Code (padded to 20 bytes)"
|
||||
)
|
||||
@click.option(
|
||||
"--store-branch-id", default="11111", help="Store Branch ID (padded to 12 bytes)"
|
||||
)
|
||||
@click.option("--passphrase", default="573", help="Passphrase, used for the pfx password (padded to 16 bytes)")
|
||||
@click.option("--output", default="authdata.bin", help="Output filename")
|
||||
def cli(cardid, key, store_card_id, merchant_code, store_branch_id, passphrase, output):
|
||||
if len(key) != 128 or not all(c in "0123456789abcdefABCDEF" for c in key):
|
||||
raise click.BadParameter("The key must be a 128-character hexadecimal string.")
|
||||
|
||||
encryptor = CardEncryptor(
|
||||
cardid,
|
||||
key,
|
||||
store_card_id_str=store_card_id,
|
||||
merchant_code_str=merchant_code,
|
||||
store_branch_id_str=store_branch_id,
|
||||
passphrase_str=passphrase,
|
||||
output_file=output,
|
||||
)
|
||||
encryptor.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
|
@ -0,0 +1,2 @@
|
|||
click
|
||||
pycryptodome
|
|
@ -48,7 +48,8 @@ void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
|
|||
memset(cfg->boot_chip_number, ' ', sizeof(cfg->boot_chip_number));
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"led15093", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"led15093", L"portNo", 0, filename);
|
||||
cfg->port_no[0] = GetPrivateProfileIntW(L"led15093", L"portNo1", 0, filename);
|
||||
cfg->port_no[1] = GetPrivateProfileIntW(L"led15093", L"portNo2", 0, filename);
|
||||
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
|
||||
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0xA0, filename);
|
||||
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xAA53, filename);
|
||||
|
|
|
@ -82,8 +82,10 @@ static DWORD CALLBACK fgo_pre_startup(void)
|
|||
/* Hook external DLL APIs */
|
||||
|
||||
printer_hook_init(&fgo_hook_cfg.printer, 4, fgo_hook_mod);
|
||||
dll_hook_push(fgo_hook_mod, L"C330Ausb.dll");
|
||||
dll_hook_push(fgo_hook_mod, L"C330AFWDLusb.dll");
|
||||
if (fgo_hook_cfg.printer.enable) {
|
||||
dll_hook_push(fgo_hook_mod, L"C330Ausb.dll");
|
||||
dll_hook_push(fgo_hook_mod, L"C330AFWDLusb.dll");
|
||||
}
|
||||
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
|
@ -133,8 +135,9 @@ static DWORD CALLBACK fgo_pre_startup(void)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
unsigned int led_port_no[2] = {17, 0};
|
||||
hr = led15093_hook_init(&fgo_hook_cfg.led15093,
|
||||
fgo_dll.led_init, fgo_dll.led_set_leds, 17, 1, 1, 2);
|
||||
fgo_dll.led_init, fgo_dll.led_set_leds, led_port_no);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
|
|
@ -87,7 +87,7 @@ static HRESULT fgo_io4_poll(void *ctx, struct io4_state *state)
|
|||
state->buttons[0] |= 1 << 1;
|
||||
}
|
||||
|
||||
if (gamebtn & FGO_IO_GAMEBTN_NOBLE_PHANTASHM) {
|
||||
if (gamebtn & FGO_IO_GAMEBTN_NOBLE_PHANTASM) {
|
||||
state->buttons[0] |= 1 << 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fgoio/fgoio.h"
|
||||
|
||||
struct fgo_io_backend {
|
||||
void (*get_gamebtns)(uint8_t *gamebtn);
|
||||
void (*get_analogs)(int16_t *x, int16_t *y);
|
||||
};
|
|
@ -1,11 +1,38 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "fgoio/config.h"
|
||||
|
||||
#include <xinput.h>
|
||||
|
||||
|
||||
void fgo_kb_config_load(
|
||||
struct fgo_kb_config *cfg,
|
||||
const wchar_t *filename) {
|
||||
|
||||
cfg->vk_attack = GetPrivateProfileIntW(L"keyboard", L"attack", ' ', filename);
|
||||
cfg->vk_dash = GetPrivateProfileIntW(L"keyboard", L"dash", VK_LSHIFT, filename);
|
||||
cfg->vk_target = GetPrivateProfileIntW(L"keyboard", L"target", 'J', filename);
|
||||
cfg->vk_camera = GetPrivateProfileIntW(L"keyboard", L"camera", 'K', filename);
|
||||
cfg->vk_np = GetPrivateProfileIntW(L"keyboard", L"np", 'L', filename);
|
||||
|
||||
// Standard WASD
|
||||
cfg->vk_right = GetPrivateProfileIntW(L"keyboard", L"right", 'D', filename);
|
||||
cfg->vk_left = GetPrivateProfileIntW(L"keyboard", L"left", 'A', filename);
|
||||
cfg->vk_down = GetPrivateProfileIntW(L"keyboard", L"down", 'S', filename);
|
||||
cfg->vk_up = GetPrivateProfileIntW(L"keyboard", L"up", 'W', filename);
|
||||
}
|
||||
|
||||
void fgo_xi_config_load(
|
||||
struct fgo_xi_config *cfg,
|
||||
const wchar_t *filename) {
|
||||
|
||||
cfg->stick_deadzone = GetPrivateProfileIntW(L"xinput", L"stickDeadzone", XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, filename);
|
||||
|
||||
}
|
||||
|
||||
void fgo_io_config_load(
|
||||
struct fgo_io_config *cfg,
|
||||
|
@ -17,4 +44,15 @@ void fgo_io_config_load(
|
|||
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", VK_F1, filename);
|
||||
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", VK_F2, filename);
|
||||
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", VK_F3, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"io4",
|
||||
L"mode",
|
||||
L"xinput",
|
||||
cfg->mode,
|
||||
_countof(cfg->mode),
|
||||
filename);
|
||||
|
||||
fgo_xi_config_load(&cfg->xi, filename);
|
||||
fgo_kb_config_load(&cfg->kb, filename);
|
||||
}
|
||||
|
|
|
@ -5,12 +5,34 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct fgo_kb_config {
|
||||
uint8_t vk_np;
|
||||
uint8_t vk_target;
|
||||
uint8_t vk_dash;
|
||||
uint8_t vk_attack;
|
||||
uint8_t vk_camera;
|
||||
uint8_t vk_right;
|
||||
uint8_t vk_left;
|
||||
uint8_t vk_down;
|
||||
uint8_t vk_up;
|
||||
};
|
||||
|
||||
struct fgo_xi_config {
|
||||
uint16_t stick_deadzone;
|
||||
};
|
||||
|
||||
struct fgo_io_config {
|
||||
uint8_t vk_test;
|
||||
uint8_t vk_service;
|
||||
uint8_t vk_coin;
|
||||
|
||||
wchar_t mode[12];
|
||||
struct fgo_kb_config kb;
|
||||
struct fgo_xi_config xi;
|
||||
};
|
||||
|
||||
void fgo_kb_config_load(struct fgo_kb_config *cfg, const wchar_t *filename);
|
||||
void fgo_xi_config_load(struct fgo_xi_config *cfg, const wchar_t *filename);
|
||||
void fgo_io_config_load(
|
||||
struct fgo_io_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
|
123
fgoio/fgoio.c
123
fgoio/fgoio.c
|
@ -2,37 +2,51 @@
|
|||
#include <xinput.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fgoio/fgoio.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "keyboard.h"
|
||||
#include "xi.h"
|
||||
#include "fgoio/config.h"
|
||||
#include "util/dprintf.h"
|
||||
#include "util/env.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static uint8_t fgo_opbtn;
|
||||
static uint8_t fgo_gamebtn;
|
||||
static int16_t fgo_stick_x;
|
||||
static int16_t fgo_stick_y;
|
||||
static struct fgo_io_config fgo_io_cfg;
|
||||
static const struct fgo_io_backend* fgo_io_backend;
|
||||
static bool fgo_io_coin;
|
||||
|
||||
uint16_t fgo_io_get_api_version(void)
|
||||
{
|
||||
uint16_t fgo_io_get_api_version(void) {
|
||||
return 0x0100;
|
||||
}
|
||||
|
||||
HRESULT fgo_io_init(void)
|
||||
{
|
||||
HRESULT fgo_io_init(void) {
|
||||
fgo_io_config_load(&fgo_io_cfg, get_config_path());
|
||||
|
||||
return S_OK;
|
||||
HRESULT hr;
|
||||
|
||||
if (wstr_ieq(fgo_io_cfg.mode, L"keyboard")) {
|
||||
hr = fgo_kb_init(&fgo_io_cfg.kb, &fgo_io_backend);
|
||||
} else if (wstr_ieq(fgo_io_cfg.mode, L"xinput")) {
|
||||
hr = fgo_xi_init(&fgo_io_cfg.xi, &fgo_io_backend);
|
||||
} else {
|
||||
hr = E_INVALIDARG;
|
||||
dprintf("FGO IO: Invalid IO mode \"%S\", use keyboard or xinput\n",
|
||||
fgo_io_cfg.mode);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT fgo_io_poll(void)
|
||||
{
|
||||
XINPUT_STATE xi;
|
||||
WORD xb;
|
||||
HRESULT fgo_io_poll(void) {
|
||||
assert(fgo_io_backend != NULL);
|
||||
|
||||
fgo_opbtn = 0;
|
||||
fgo_gamebtn = 0;
|
||||
|
@ -56,97 +70,34 @@ HRESULT fgo_io_poll(void)
|
|||
fgo_io_coin = false;
|
||||
}
|
||||
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
XInputGetState(0, &xi);
|
||||
xb = xi.Gamepad.wButtons;
|
||||
|
||||
if (xi.Gamepad.bLeftTrigger > 64) {
|
||||
fgo_gamebtn |= FGO_IO_GAMEBTN_SPEED_UP;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_LEFT_SHOULDER) {
|
||||
fgo_gamebtn |= FGO_IO_GAMEBTN_TARGET;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_A || xb & XINPUT_GAMEPAD_B) {
|
||||
fgo_gamebtn |= FGO_IO_GAMEBTN_ATTACK;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_Y || xb & XINPUT_GAMEPAD_X) {
|
||||
fgo_gamebtn |= FGO_IO_GAMEBTN_NOBLE_PHANTASHM;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_LEFT_THUMB) {
|
||||
fgo_gamebtn |= FGO_IO_GAMEBTN_CAMERA;
|
||||
}
|
||||
|
||||
float LX = xi.Gamepad.sThumbLX;
|
||||
float LY = xi.Gamepad.sThumbLY;
|
||||
|
||||
// determine how far the controller is pushed
|
||||
float magnitude = sqrt(LX*LX + LY*LY);
|
||||
|
||||
// determine the direction the controller is pushed
|
||||
float normalizedLX = LX / magnitude;
|
||||
float normalizedLY = LY / magnitude;
|
||||
|
||||
float normalizedMagnitude = 0;
|
||||
|
||||
// check if the controller is outside a circular dead zone
|
||||
if (magnitude > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
|
||||
{
|
||||
// clip the magnitude at its expected maximum value
|
||||
if (magnitude > 32767) magnitude = 32767;
|
||||
|
||||
// adjust magnitude relative to the end of the dead zone
|
||||
magnitude -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
|
||||
|
||||
// optionally normalize the magnitude with respect to its expected range
|
||||
// giving a magnitude value of 0.0 to 1.0
|
||||
normalizedMagnitude = magnitude / (32767 - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
|
||||
} else // if the controller is in the deadzone zero out the magnitude
|
||||
{
|
||||
magnitude = 0.0;
|
||||
normalizedMagnitude = 0.0;
|
||||
}
|
||||
|
||||
fgo_stick_x = (int16_t)(normalizedLX * normalizedMagnitude * 32767);
|
||||
fgo_stick_y = (int16_t)(normalizedLY * normalizedMagnitude * 32767);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void fgo_io_get_opbtns(uint8_t *opbtn)
|
||||
{
|
||||
void fgo_io_get_opbtns(uint8_t* opbtn) {
|
||||
if (opbtn != NULL) {
|
||||
*opbtn = fgo_opbtn;
|
||||
}
|
||||
}
|
||||
|
||||
void fgo_io_get_gamebtns(uint8_t *btn)
|
||||
{
|
||||
if (btn != NULL) {
|
||||
*btn = fgo_gamebtn;
|
||||
}
|
||||
void fgo_io_get_gamebtns(uint8_t* btn) {
|
||||
assert(fgo_io_backend != NULL);
|
||||
assert(btn != NULL);
|
||||
|
||||
fgo_io_backend->get_gamebtns(btn);
|
||||
}
|
||||
|
||||
void fgo_io_get_analogs(int16_t *stick_x, int16_t *stick_y)
|
||||
{
|
||||
if (stick_x != NULL) {
|
||||
*stick_x = fgo_stick_x;
|
||||
}
|
||||
void fgo_io_get_analogs(int16_t* stick_x, int16_t* stick_y) {
|
||||
assert(fgo_io_backend != NULL);
|
||||
assert(stick_x != NULL);
|
||||
assert(stick_y != NULL);
|
||||
|
||||
if (stick_y != NULL) {
|
||||
*stick_y = fgo_stick_y;
|
||||
}
|
||||
fgo_io_backend->get_analogs(stick_x, stick_y);
|
||||
}
|
||||
|
||||
HRESULT fgo_io_led_init(void)
|
||||
{
|
||||
HRESULT fgo_io_led_init(void) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void fgo_io_led_set_colors(uint8_t board, uint8_t *rgb)
|
||||
{
|
||||
void fgo_io_led_set_colors(uint8_t board, uint8_t* rgb) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ enum {
|
|||
FGO_IO_GAMEBTN_SPEED_UP = 0x01,
|
||||
FGO_IO_GAMEBTN_TARGET = 0x02,
|
||||
FGO_IO_GAMEBTN_ATTACK = 0x04,
|
||||
FGO_IO_GAMEBTN_NOBLE_PHANTASHM = 0x08,
|
||||
FGO_IO_GAMEBTN_NOBLE_PHANTASM = 0x08,
|
||||
FGO_IO_GAMEBTN_CAMERA = 0x10,
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "fgoio/backend.h"
|
||||
#include "fgoio/config.h"
|
||||
#include "fgoio/fgoio.h"
|
||||
#include "fgoio/keyboard.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static void fgo_kb_get_gamebtns(uint8_t* gamebtn_out);
|
||||
static void fgo_kb_get_analogs(int16_t* x, int16_t* y);
|
||||
|
||||
static const struct fgo_io_backend fgo_kb_backend = {
|
||||
.get_gamebtns = fgo_kb_get_gamebtns,
|
||||
.get_analogs = fgo_kb_get_analogs
|
||||
};
|
||||
|
||||
static struct fgo_kb_config config;
|
||||
|
||||
HRESULT fgo_kb_init(const struct fgo_kb_config* cfg, const struct fgo_io_backend** backend) {
|
||||
assert(cfg != NULL);
|
||||
assert(backend != NULL);
|
||||
|
||||
dprintf("Keyboard: Using keyboard input\n");
|
||||
*backend = &fgo_kb_backend;
|
||||
config = *cfg;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void fgo_kb_get_gamebtns(uint8_t* gamebtn_out) {
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
uint8_t gamebtn = 0;
|
||||
|
||||
if (GetAsyncKeyState(config.vk_np) & 0x8000) {
|
||||
gamebtn |= FGO_IO_GAMEBTN_NOBLE_PHANTASM;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(config.vk_target) & 0x8000) {
|
||||
gamebtn |= FGO_IO_GAMEBTN_TARGET;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(config.vk_dash) & 0x8000) {
|
||||
gamebtn |= FGO_IO_GAMEBTN_SPEED_UP;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(config.vk_attack) & 0x8000) {
|
||||
gamebtn |= FGO_IO_GAMEBTN_ATTACK;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(config.vk_camera) & 0x8000) {
|
||||
gamebtn |= FGO_IO_GAMEBTN_CAMERA;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
||||
|
||||
static void fgo_kb_get_analogs(int16_t* x, int16_t* y) {
|
||||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
|
||||
if (GetAsyncKeyState(config.vk_left) & 0x8000) {
|
||||
*x = SHRT_MIN + 1;
|
||||
} else if (GetAsyncKeyState(config.vk_right) & 0x8000) {
|
||||
*x = SHRT_MAX - 1;
|
||||
} else {
|
||||
*x = 0;
|
||||
}
|
||||
if (GetAsyncKeyState(config.vk_down) & 0x8000) {
|
||||
*y = SHRT_MIN + 1;
|
||||
} else if (GetAsyncKeyState(config.vk_up) & 0x8000) {
|
||||
*y = SHRT_MAX - 1;
|
||||
} else {
|
||||
*y = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "fgoio/backend.h"
|
||||
#include "fgoio/config.h"
|
||||
|
||||
HRESULT fgo_kb_init(const struct fgo_kb_config *cfg, const struct fgo_io_backend **backend);
|
|
@ -11,5 +11,10 @@ fgoio_lib = static_library(
|
|||
'fgoio.h',
|
||||
'config.c',
|
||||
'config.h',
|
||||
'backend.h',
|
||||
'keyboard.c',
|
||||
'keyboard.h',
|
||||
'xi.c',
|
||||
'xi.h',
|
||||
],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
#include <windows.h>
|
||||
#include <xinput.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fgoio/backend.h"
|
||||
#include "fgoio/config.h"
|
||||
#include "fgoio/fgoio.h"
|
||||
#include "fgoio/xi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static void fgo_xi_get_gamebtns(uint8_t* gamebtn_out);
|
||||
static void fgo_xi_get_analogs(int16_t* x, int16_t* y);
|
||||
static HRESULT fgo_xi_config_apply(const struct fgo_xi_config* cfg);
|
||||
|
||||
static const struct fgo_io_backend fgo_xi_backend = {
|
||||
.get_gamebtns = fgo_xi_get_gamebtns,
|
||||
.get_analogs = fgo_xi_get_analogs
|
||||
};
|
||||
|
||||
static float fgo_xi_stick_deadzone;
|
||||
|
||||
const uint16_t max_stick_value = 32767;
|
||||
|
||||
HRESULT fgo_xi_init(const struct fgo_xi_config* cfg, const struct fgo_io_backend** backend) {
|
||||
assert(cfg != NULL);
|
||||
assert(backend != NULL);
|
||||
|
||||
HRESULT hr = fgo_xi_config_apply(cfg);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("XInput: Using XInput controller\n");
|
||||
*backend = &fgo_xi_backend;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT fgo_xi_config_apply(const struct fgo_xi_config* cfg) {
|
||||
/* Deadzone check */
|
||||
if (cfg->stick_deadzone > 32767 || cfg->stick_deadzone < 0) {
|
||||
dprintf("XInput: Stick deadzone is too large or negative\n");
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
dprintf("XInput: --- Begin configuration ---\n");
|
||||
dprintf("XInput: Left Deadzone . . . . : %i\n", cfg->stick_deadzone);
|
||||
dprintf("XInput: --- End configuration ---\n");
|
||||
|
||||
fgo_xi_stick_deadzone = cfg->stick_deadzone;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void fgo_xi_get_gamebtns(uint8_t* gamebtn_out) {
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
XINPUT_STATE xi;
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
XInputGetState(0, &xi);
|
||||
|
||||
uint8_t gamebtn = 0;
|
||||
WORD xb = xi.Gamepad.wButtons;
|
||||
|
||||
if (xi.Gamepad.bLeftTrigger > 64) {
|
||||
gamebtn |= FGO_IO_GAMEBTN_SPEED_UP;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_LEFT_SHOULDER) {
|
||||
gamebtn |= FGO_IO_GAMEBTN_TARGET;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_A || xb & XINPUT_GAMEPAD_B) {
|
||||
gamebtn |= FGO_IO_GAMEBTN_ATTACK;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_Y || xb & XINPUT_GAMEPAD_X) {
|
||||
gamebtn |= FGO_IO_GAMEBTN_NOBLE_PHANTASM;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_LEFT_THUMB) {
|
||||
gamebtn |= FGO_IO_GAMEBTN_CAMERA;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
||||
|
||||
static void fgo_xi_get_analogs(int16_t* x, int16_t* y) {
|
||||
|
||||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
|
||||
XINPUT_STATE xi;
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
XInputGetState(0, &xi);
|
||||
|
||||
float LX = xi.Gamepad.sThumbLX;
|
||||
float LY = xi.Gamepad.sThumbLY;
|
||||
|
||||
// determine how far the controller is pushed
|
||||
float magnitude = sqrt(LX * LX + LY * LY);
|
||||
|
||||
// determine the direction the controller is pushed
|
||||
float normalizedLX = LX / magnitude;
|
||||
float normalizedLY = LY / magnitude;
|
||||
|
||||
float normalizedMagnitude = 0;
|
||||
|
||||
// check if the controller is outside a circular dead zone
|
||||
if (magnitude > fgo_xi_stick_deadzone) {
|
||||
// clip the magnitude at its expected maximum value
|
||||
if (magnitude > 32767) magnitude = 32767;
|
||||
|
||||
// adjust magnitude relative to the end of the dead zone
|
||||
magnitude -= fgo_xi_stick_deadzone;
|
||||
|
||||
// optionally normalize the magnitude with respect to its expected range
|
||||
// giving a magnitude value of 0.0 to 1.0
|
||||
normalizedMagnitude = magnitude / (32767 - fgo_xi_stick_deadzone);
|
||||
} else // if the controller is in the deadzone zero out the magnitude
|
||||
{
|
||||
normalizedMagnitude = 0;
|
||||
}
|
||||
|
||||
*x = (int16_t) (normalizedLX * normalizedMagnitude * 32767);
|
||||
*y = (int16_t) (normalizedLY * normalizedMagnitude * 32767);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
/* Can't call this xinput.h or it will conflict with <xinput.h> */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "fgoio/backend.h"
|
||||
#include "fgoio/config.h"
|
||||
|
||||
HRESULT fgo_xi_init(const struct fgo_xi_config *cfg, const struct fgo_io_backend **backend);
|
|
@ -14,4 +14,5 @@ void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename)
|
|||
cfg->windowed = GetPrivateProfileIntW(L"gfx", L"windowed", 0, filename);
|
||||
cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename);
|
||||
cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename);
|
||||
cfg->dpiAware = GetPrivateProfileIntW(L"gfx", L"dpiAware", 1, filename);
|
||||
}
|
||||
|
|
|
@ -67,6 +67,14 @@ void gfx_hook_init(const struct gfx_config *cfg)
|
|||
return;
|
||||
}
|
||||
|
||||
if (cfg->dpiAware) {
|
||||
if (SetProcessDPIAware()) {
|
||||
dprintf("Gfx: Game process set to DPI aware.\n");
|
||||
} else {
|
||||
dprintf("Gfx: Failed to set process DPI aware\n");
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&gfx_config, cfg, sizeof(*cfg));
|
||||
hook_table_apply(NULL, "user32.dll", gfx_hooks, _countof(gfx_hooks));
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ struct gfx_config {
|
|||
bool windowed;
|
||||
bool framed;
|
||||
int monitor;
|
||||
bool dpiAware;
|
||||
};
|
||||
|
||||
void gfx_hook_init(const struct gfx_config *cfg);
|
||||
|
|
|
@ -14,6 +14,7 @@ static HCURSOR (*next_SetCursor)(HCURSOR hCursor);
|
|||
static BOOL my_SetCursorPos(int x, int y);
|
||||
static BOOL my_SetPhysicalCursorPos(int x, int y);
|
||||
static int my_ShowCursor(BOOL bShow);
|
||||
static int cursor_track = -1; // If no mouse is connected, this starts as -1
|
||||
|
||||
static const struct hook_symbol cursor_syms[] = {
|
||||
{
|
||||
|
@ -45,7 +46,7 @@ void cursor_hook_init()
|
|||
|
||||
static BOOL my_SetCursorPos(int x, int y)
|
||||
{
|
||||
dprintf("my_SetCursorPos Hit! x %d y %d\n", x, y);
|
||||
// dprintf("my_SetCursorPos Hit! x %d y %d\n", x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -58,7 +59,12 @@ static BOOL my_SetPhysicalCursorPos(int x, int y)
|
|||
static int my_ShowCursor(BOOL bShow)
|
||||
{
|
||||
dprintf("my_ShowCursor Hit!\n");
|
||||
return 0;
|
||||
if (bShow) {
|
||||
cursor_track++;
|
||||
} else {
|
||||
cursor_track--;
|
||||
}
|
||||
return cursor_track;
|
||||
}
|
||||
|
||||
static HCURSOR my_SetCursor(HCURSOR hCursor)
|
||||
|
|
|
@ -217,20 +217,24 @@ static void dns_hook_init(void)
|
|||
dns_hook_initted = true;
|
||||
InitializeCriticalSection(&dns_hook_lock);
|
||||
|
||||
dns_hook_apply_hooks(NULL);
|
||||
}
|
||||
|
||||
void dns_hook_apply_hooks(HMODULE mod){
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
mod,
|
||||
"dnsapi.dll",
|
||||
dns_hook_syms_dnsapi,
|
||||
_countof(dns_hook_syms_dnsapi));
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
mod,
|
||||
"ws2_32.dll",
|
||||
dns_hook_syms_ws2,
|
||||
_countof(dns_hook_syms_ws2));
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
mod,
|
||||
"winhttp.dll",
|
||||
dns_hook_syms_winhttp,
|
||||
_countof(dns_hook_syms_winhttp));
|
||||
|
|
|
@ -8,3 +8,4 @@ void port_hook_init(unsigned short _startup_port, unsigned short _billing_port,
|
|||
// if to_src is NULL, all lookups for from_src will fail
|
||||
HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src);
|
||||
|
||||
void dns_hook_apply_hooks(HMODULE mod);
|
||||
|
|
|
@ -4,6 +4,7 @@ hooklib_lib = static_library(
|
|||
implicit_include_directories : false,
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
ws2_32_lib
|
||||
],
|
||||
sources : [
|
||||
'cursor.c',
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "hooklib/path.h"
|
||||
|
||||
#include <util/dprintf.h>
|
||||
|
||||
/* Helpers */
|
||||
|
||||
static void path_hook_init(void);
|
||||
|
@ -533,6 +535,12 @@ static BOOL path_transform_w(wchar_t **out, const wchar_t *src)
|
|||
goto end;
|
||||
}
|
||||
|
||||
#if LOG_VFS
|
||||
if (!wcsstr(src, L"AppUser")) {
|
||||
dprintf("Path: %ls -> %ls\n", src, dest);
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -308,7 +308,7 @@ static void reg_hook_init(void)
|
|||
|
||||
reg_hook_initted = true;
|
||||
InitializeCriticalSection(®_hook_lock);
|
||||
dprintf("Reg hook init\n");
|
||||
dprintf("Reg: hook init.\n");
|
||||
|
||||
reg_hook_insert_hooks(NULL);
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ void led15070_config_load(struct led15070_config *cfg, const wchar_t *filename)
|
|||
wchar_t tmpstr[16];
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"led15070", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"led15070", L"portNo", 0, filename);
|
||||
cfg->port_no[0] = GetPrivateProfileIntW(L"led15070", L"portNo1", 0, filename);
|
||||
cfg->port_no[1] = GetPrivateProfileIntW(L"led15070", L"portNo2", 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", 0x0000, filename);
|
||||
|
|
|
@ -60,6 +60,18 @@ static DWORD CALLBACK idac_pre_startup(void)
|
|||
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
struct dipsw_config new_dipsw_config[8] = {
|
||||
{L"Delivery Server", L"Server", L"Client"},
|
||||
{L"Cabinet Type", L"SWDC CVT", L"DZero CVT"},
|
||||
{L"Single Seat", L"ON", L"OFF"},
|
||||
{L"Seat Setting 1", L"ON", L"OFF"},
|
||||
{L"Seat Setting 2", L"ON", L"OFF"},
|
||||
};
|
||||
|
||||
// Set the system dip switch configuration
|
||||
memcpy(idac_hook_cfg.platform.system.dipsw_config, new_dipsw_config,
|
||||
sizeof(new_dipsw_config));
|
||||
|
||||
hr = platform_hook_init(
|
||||
&idac_hook_cfg.platform,
|
||||
"SDGT",
|
||||
|
@ -70,13 +82,21 @@ static DWORD CALLBACK idac_pre_startup(void)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
bool *dipsw = &idac_hook_cfg.platform.system.dipsw[0];
|
||||
bool is_swdc_cvt = dipsw[1];
|
||||
|
||||
if (!dipsw[2]) {
|
||||
// the next two bit are the seat number most significant bit first
|
||||
dprintf("System: Seat Number: %d\n", ((dipsw[4] << 1) | dipsw[3]) + 1);
|
||||
}
|
||||
|
||||
hr = idac_dll_init(&idac_hook_cfg.dll, idac_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&idac_hook_cfg.aime, 3, 3, idac_hook_mod);
|
||||
hr = sg_reader_hook_init(&idac_hook_cfg.aime, 3, is_swdc_cvt ? 3 : 2, idac_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
@ -94,8 +114,9 @@ static DWORD CALLBACK idac_pre_startup(void)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
unsigned int led_port_no[2] = {2, 0};
|
||||
hr = led15070_hook_init(&idac_hook_cfg.led15070, idac_dll.led_init,
|
||||
idac_dll.led_set_fet_output, NULL, idac_dll.led_gs_update, 2, 1);
|
||||
idac_dll.led_set_fet_output, NULL, idac_dll.led_gs_update, led_port_no);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
|
|
@ -12,9 +12,9 @@ struct idac_dll {
|
|||
void (*get_shifter)(uint8_t *gear);
|
||||
void (*get_analogs)(struct idac_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);
|
||||
void (*led_set_fet_output)(uint8_t board, const uint8_t *rgb);
|
||||
void (*led_gs_update)(uint8_t board, const uint8_t *rgb);
|
||||
void (*led_set_leds)(uint8_t board, const uint8_t *rgb);
|
||||
HRESULT (*ffb_init)(void);
|
||||
void (*ffb_toggle)(bool active);
|
||||
void (*ffb_constant_force)(uint8_t direction, uint8_t force);
|
||||
|
|
|
@ -159,7 +159,7 @@ static HRESULT idac_io4_write_gpio(uint8_t* payload, size_t len)
|
|||
lights_data & IDAC_IO_LED_LEFT ? 0xFF : 0x00,
|
||||
};
|
||||
|
||||
idac_dll.led_set_leds(rgb_out);
|
||||
idac_dll.led_set_leds(0, rgb_out);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ HRESULT idac_io_led_init(void)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
void idac_io_led_set_fet_output(const uint8_t *rgb)
|
||||
void idac_io_led_set_fet_output(uint8_t board, const uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
dprintf("IDAC LED: LEFT SEAT LED: %02X\n", rgb[0]);
|
||||
|
@ -137,7 +137,7 @@ void idac_io_led_set_fet_output(const uint8_t *rgb)
|
|||
return;
|
||||
}
|
||||
|
||||
void idac_io_led_gs_update(const uint8_t *rgb)
|
||||
void idac_io_led_gs_update(uint8_t board, const uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
for (int i = 0; i < 9; i++) {
|
||||
|
@ -149,7 +149,7 @@ void idac_io_led_gs_update(const uint8_t *rgb)
|
|||
return;
|
||||
}
|
||||
|
||||
void idac_io_led_set_leds(const uint8_t *rgb)
|
||||
void idac_io_led_set_leds(uint8_t board, const uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
dprintf("IDAC LED: START: %02X\n", rgb[0]);
|
||||
|
|
|
@ -127,7 +127,7 @@ HRESULT idac_io_led_init(void);
|
|||
|
||||
Minimum API version: 0x0101 */
|
||||
|
||||
void idac_io_led_set_fet_output(const uint8_t *rgb);
|
||||
void idac_io_led_set_fet_output(uint8_t board, const uint8_t *rgb);
|
||||
|
||||
/* Update the RGB LEDs. rgb is a pointer to an array up to 32 * 4 = 128 bytes.
|
||||
|
||||
|
@ -144,7 +144,7 @@ void idac_io_led_set_fet_output(const uint8_t *rgb);
|
|||
|
||||
Minimum API version: 0x0101 */
|
||||
|
||||
void idac_io_led_gs_update(const uint8_t *rgb);
|
||||
void idac_io_led_gs_update(uint8_t board, const uint8_t *rgb);
|
||||
|
||||
/* Update the cabinet button LEDs. rgb is a pointer to an array up to 6 bytes.
|
||||
|
||||
|
@ -160,7 +160,7 @@ void idac_io_led_gs_update(const uint8_t *rgb);
|
|||
|
||||
Minimum API version: 0x0101 */
|
||||
|
||||
void idac_io_led_set_leds(const uint8_t *rgb);
|
||||
void idac_io_led_set_leds(uint8_t board, const uint8_t *rgb);
|
||||
|
||||
/* Initialize FFB emulation. This function will be called before any
|
||||
other idac_io_ffb_*() function calls.
|
||||
|
|
|
@ -27,7 +27,8 @@ void led15070_config_load(struct led15070_config *cfg, const wchar_t *filename)
|
|||
wchar_t tmpstr[16];
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"led15070", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"led15070", L"portNo", 0, filename);
|
||||
cfg->port_no[0] = GetPrivateProfileIntW(L"led15070", L"portNo1", 0, filename);
|
||||
cfg->port_no[1] = GetPrivateProfileIntW(L"led15070", L"portNo2", 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", 0x0000, filename);
|
||||
|
|
|
@ -128,8 +128,9 @@ static DWORD CALLBACK idz_pre_startup(void)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
unsigned int led_port_no[2] = {11, 0};
|
||||
hr = led15070_hook_init(&idz_hook_cfg.led15070, idz_dll.led_init,
|
||||
idz_dll.led_set_fet_output, NULL, idz_dll.led_gs_update, 11, 1);
|
||||
idz_dll.led_set_fet_output, NULL, idz_dll.led_gs_update, led_port_no);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
|
|
@ -12,9 +12,9 @@ struct idz_dll {
|
|||
void (*jvs_read_shifter)(uint8_t *gear);
|
||||
void (*jvs_read_coin_counter)(uint16_t *total);
|
||||
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);
|
||||
void (*led_set_fet_output)(uint8_t board, const uint8_t *rgb);
|
||||
void (*led_gs_update)(uint8_t board, const uint8_t *rgb);
|
||||
void (*led_set_leds)(uint8_t board, const uint8_t *rgb);
|
||||
HRESULT (*ffb_init)(void);
|
||||
void (*ffb_toggle)(bool active);
|
||||
void (*ffb_constant_force)(uint8_t direction, uint8_t force);
|
||||
|
|
|
@ -192,5 +192,5 @@ static void idz_jvs_write_gpio(void *ctx, uint32_t state)
|
|||
state & IDZ_IO_LED_LEFT ? 0xFF : 0x00,
|
||||
};
|
||||
|
||||
idz_dll.led_set_leds(rgb_out);
|
||||
idz_dll.led_set_leds(0, rgb_out);
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ HRESULT idz_io_led_init(void)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
void idz_io_led_set_fet_output(const uint8_t *rgb)
|
||||
void idz_io_led_set_fet_output(uint8_t board, const uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
dprintf("IDZ LED: LEFT SEAT LED: %02X\n", rgb[0]);
|
||||
|
@ -140,7 +140,7 @@ void idz_io_led_set_fet_output(const uint8_t *rgb)
|
|||
return;
|
||||
}
|
||||
|
||||
void idz_io_led_gs_update(const uint8_t *rgb)
|
||||
void idz_io_led_gs_update(uint8_t board, const uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
for (int i = 0; i < 9; i++) {
|
||||
|
@ -152,7 +152,7 @@ void idz_io_led_gs_update(const uint8_t *rgb)
|
|||
return;
|
||||
}
|
||||
|
||||
void idz_io_led_set_leds(const uint8_t *rgb)
|
||||
void idz_io_led_set_leds(uint8_t board, const uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
dprintf("IDZ LED: START: %02X\n", rgb[0]);
|
||||
|
|
|
@ -138,7 +138,7 @@ HRESULT idz_io_led_init(void);
|
|||
|
||||
Minimum API version: 0x0101 */
|
||||
|
||||
void idz_io_led_set_fet_output(const uint8_t *rgb);
|
||||
void idz_io_led_set_fet_output(uint8_t board, const uint8_t *rgb);
|
||||
|
||||
/* Update the RGB LEDs. rgb is a pointer to an array up to 32 * 4 = 128 bytes.
|
||||
|
||||
|
@ -155,7 +155,7 @@ void idz_io_led_set_fet_output(const uint8_t *rgb);
|
|||
|
||||
Minimum API version: 0x0101 */
|
||||
|
||||
void idz_io_led_gs_update(const uint8_t *rgb);
|
||||
void idz_io_led_gs_update(uint8_t board, const uint8_t *rgb);
|
||||
|
||||
/* Update the cabinet button LEDs. rgb is a pointer to an array up to 6 bytes.
|
||||
|
||||
|
@ -171,7 +171,7 @@ void idz_io_led_gs_update(const uint8_t *rgb);
|
|||
|
||||
Minimum API version: 0x0101 */
|
||||
|
||||
void idz_io_led_set_leds(const uint8_t *rgb);
|
||||
void idz_io_led_set_leds(uint8_t board, const uint8_t *rgb);
|
||||
|
||||
/* Initialize FFB emulation. This function will be called before any
|
||||
other idz_io_ffb_*() function calls.
|
||||
|
|
|
@ -65,7 +65,8 @@ void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
|
|||
memset(cfg->boot_chip_number, ' ', sizeof(cfg->boot_chip_number));
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"led15093", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"led15093", L"portNo", 0, filename);
|
||||
cfg->port_no[0] = GetPrivateProfileIntW(L"led15093", L"portNo1", 0, filename);
|
||||
cfg->port_no[1] = GetPrivateProfileIntW(L"led15093", L"portNo2", 1, filename);
|
||||
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
|
||||
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0xA0, filename);
|
||||
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xAA53, filename);
|
||||
|
|
|
@ -1,28 +1,21 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
#include "board/sg-reader.h"
|
||||
#include "board/vfd.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
#include "hook/process.h"
|
||||
#include "hook/table.h"
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/printer.h"
|
||||
#include "hooklib/serial.h"
|
||||
#include "hooklib/spike.h"
|
||||
|
||||
#include "kemonohook/config.h"
|
||||
#include "kemonohook/hooks.h"
|
||||
#include "kemonohook/jvs.h"
|
||||
#include "kemonohook/kemono-dll.h"
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "unityhook/hook.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/env.h"
|
||||
|
||||
|
@ -47,29 +40,38 @@ static DWORD CALLBACK kemono_pre_startup(void) {
|
|||
|
||||
// 2.02 does not call printer update functions
|
||||
uint16_t ret;
|
||||
fwdlusb_updateFirmware_main(1, "UnityApp\\Parade_Data\\StreamingAssets\\Printer\\E0223100-014E-C300-MAINAPP.BIN", &ret);
|
||||
if (ret != 0){
|
||||
fwdlusb_updateFirmware_main(
|
||||
1,
|
||||
"UnityApp\\Parade_Data\\StreamingAssets\\Printer\\E0223100-014E-C300-"
|
||||
"MAINAPP.BIN",
|
||||
&ret);
|
||||
if (ret != 0) {
|
||||
goto fail;
|
||||
}
|
||||
fwdlusb_updateFirmware_dsp(2, "UnityApp\\Parade_Data\\StreamingAssets\\Printer\\E0223200-0101-C300-DSPAPP.BIN", &ret);
|
||||
if (ret != 0){
|
||||
fwdlusb_updateFirmware_dsp(
|
||||
2,
|
||||
"UnityApp\\Parade_Data\\StreamingAssets\\Printer\\E0223200-0101-C300-"
|
||||
"DSPAPP.BIN",
|
||||
&ret);
|
||||
if (ret != 0) {
|
||||
goto fail;
|
||||
}
|
||||
fwdlusb_updateFirmware_param(3, "UnityApp\\Parade_Data\\StreamingAssets\\Printer\\D0460700-0101-C300-PARAM.BIN", &ret);
|
||||
if (ret != 0){
|
||||
fwdlusb_updateFirmware_param(
|
||||
3,
|
||||
"UnityApp\\Parade_Data\\StreamingAssets\\Printer\\D0460700-0101-C300-"
|
||||
"PARAM.BIN",
|
||||
&ret);
|
||||
if (ret != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printer_hook_init(&kemono_hook_cfg.printer, 0, kemono_hook_mod);
|
||||
printer_set_dimensions(720, 1028); // printer doesn't call setimageformat
|
||||
printer_set_dimensions(720, 1028); // printer doesn't call setimageformat
|
||||
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
hr = platform_hook_init(
|
||||
&kemono_hook_cfg.platform,
|
||||
"SDFL",
|
||||
"AAW1",
|
||||
kemono_hook_mod);
|
||||
hr = platform_hook_init(&kemono_hook_cfg.platform, "SDFL", "AAW1",
|
||||
kemono_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
@ -93,7 +95,9 @@ static DWORD CALLBACK kemono_pre_startup(void) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
hr = led15093_hook_init(&kemono_hook_cfg.led15093, kemono_dll.led_init, kemono_dll.led_set_leds, 10, 1, 1, 2);
|
||||
unsigned int led_port_no[2] = {10, 0};
|
||||
hr = led15093_hook_init(&kemono_hook_cfg.led15093, kemono_dll.led_init,
|
||||
kemono_dll.led_set_leds, led_port_no);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
@ -106,7 +110,8 @@ static DWORD CALLBACK kemono_pre_startup(void) {
|
|||
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
|
||||
hooked earlier in the `kemonohook` initialization. */
|
||||
|
||||
unity_hook_init(&kemono_hook_cfg.unity, kemono_hook_mod, kemono_extra_hooks_load);
|
||||
unity_hook_init(&kemono_hook_cfg.unity, kemono_hook_mod,
|
||||
kemono_extra_hooks_load);
|
||||
|
||||
/* Initialize debug helpers */
|
||||
|
||||
|
@ -118,7 +123,7 @@ static DWORD CALLBACK kemono_pre_startup(void) {
|
|||
|
||||
return kemono_startup();
|
||||
|
||||
fail:
|
||||
fail:
|
||||
ExitProcess(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
@ -134,7 +139,7 @@ BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) {
|
|||
hr = process_hijack_startup(kemono_pre_startup, &kemono_startup);
|
||||
|
||||
if (!SUCCEEDED(hr)) {
|
||||
dprintf("Failed to hijack process startup: %x\n", (int) hr);
|
||||
dprintf("Failed to hijack process startup: %x\n", (int)hr);
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
|
|
|
@ -12,24 +12,71 @@
|
|||
#include "platform/config.h"
|
||||
|
||||
void mai2_dll_config_load(
|
||||
struct mai2_dll_config *cfg,
|
||||
const wchar_t *filename)
|
||||
struct mai2_dll_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"mai2io",
|
||||
L"path",
|
||||
L"",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
filename);
|
||||
L"mai2io",
|
||||
L"path",
|
||||
L"",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
filename);
|
||||
}
|
||||
|
||||
void touch_config_load(
|
||||
struct touch_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable_1p = GetPrivateProfileIntW(L"touch", L"p1Enable", 1, filename);
|
||||
cfg->enable_2p = GetPrivateProfileIntW(L"touch", L"p2Enable", 1, filename);
|
||||
}
|
||||
|
||||
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[0] = GetPrivateProfileIntW(L"led15070", L"portNo1", 0, filename);
|
||||
cfg->port_no[1] = GetPrivateProfileIntW(L"led15070", L"portNo2", 0, filename);
|
||||
cfg->fw_ver = GetPrivateProfileIntW(L"led15070", L"fwVer", 0x90, filename);
|
||||
cfg->fw_sum = GetPrivateProfileIntW(L"led15070", L"fwSum", 0x00, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"led15070",
|
||||
L"boardNumber",
|
||||
L"15070-04",
|
||||
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 mai2_hook_config_load(
|
||||
struct mai2_hook_config *cfg,
|
||||
const wchar_t *filename)
|
||||
struct mai2_hook_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
@ -40,5 +87,7 @@ void mai2_hook_config_load(
|
|||
io4_config_load(&cfg->io4, filename);
|
||||
vfd_config_load(&cfg->vfd, filename);
|
||||
mai2_dll_config_load(&cfg->dll, filename);
|
||||
touch_config_load(&cfg->touch, filename);
|
||||
led15070_config_load(&cfg->led15070, filename);
|
||||
unity_config_load(&cfg->unity, filename);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
|
||||
#include "platform/config.h"
|
||||
|
||||
#include "mai2hook/touch.h"
|
||||
|
||||
#include "board/led15070.h"
|
||||
|
||||
#include "unityhook/config.h"
|
||||
|
||||
struct mai2_hook_config {
|
||||
|
@ -19,13 +23,23 @@ struct mai2_hook_config {
|
|||
struct io4_config io4;
|
||||
struct vfd_config vfd;
|
||||
struct mai2_dll_config dll;
|
||||
struct touch_config touch;
|
||||
struct led15070_config led15070;
|
||||
struct unity_config unity;
|
||||
};
|
||||
|
||||
void mai2_dll_config_load(
|
||||
struct mai2_dll_config *cfg,
|
||||
const wchar_t *filename);
|
||||
struct mai2_dll_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
||||
void touch_config_load(
|
||||
struct touch_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
||||
void led15070_config_load(
|
||||
struct led15070_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
||||
void mai2_hook_config_load(
|
||||
struct mai2_hook_config *cfg,
|
||||
const wchar_t *filename);
|
||||
struct mai2_hook_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
|
|
@ -66,17 +66,54 @@ static DWORD CALLBACK mai2_pre_startup(void)
|
|||
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
struct dipsw_config new_dipsw_config[8] = {
|
||||
{L"Delivery Server", L"Server", L"Client"},
|
||||
};
|
||||
|
||||
// Set the system dip switch configuration
|
||||
memcpy(mai2_hook_cfg.platform.system.dipsw_config, new_dipsw_config,
|
||||
sizeof(new_dipsw_config));
|
||||
|
||||
hr = platform_hook_init(
|
||||
&mai2_hook_cfg.platform,
|
||||
"SDEZ",
|
||||
"ACA1",
|
||||
mai2_hook_mod);
|
||||
&mai2_hook_cfg.platform,
|
||||
"SDEZ",
|
||||
"ACA1",
|
||||
mai2_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&mai2_hook_cfg.aime, 1, 1, mai2_hook_mod);
|
||||
/* Initialize DLLs */
|
||||
|
||||
hr = mai2_dll_init(&mai2_hook_cfg.dll, mai2_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Touch Panel uses COM3 and COM4
|
||||
|
||||
hr = touch_hook_init(&mai2_hook_cfg.touch);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// LED board uses COM21 and COM23
|
||||
unsigned int led_port_no[2] = {21, 23};
|
||||
hr = led15070_hook_init(&mai2_hook_cfg.led15070,
|
||||
mai2_dll.led_init,
|
||||
mai2_dll.led_set_fet_output,
|
||||
mai2_dll.led_dc_update,
|
||||
mai2_dll.led_gs_update,
|
||||
led_port_no);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&mai2_hook_cfg.aime, 1, 3, mai2_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
@ -88,12 +125,6 @@ static DWORD CALLBACK mai2_pre_startup(void)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
hr = mai2_dll_init(&mai2_hook_cfg.dll, mai2_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = mai2_io4_hook_init(&mai2_hook_cfg.io4);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
|
|
|
@ -21,7 +21,28 @@ const struct dll_bind_sym mai2_dll_syms[] = {
|
|||
}, {
|
||||
.sym = "mai2_io_get_gamebtns",
|
||||
.off = offsetof(struct mai2_dll, get_gamebtns),
|
||||
}
|
||||
}, {
|
||||
.sym = "mai2_io_touch_init",
|
||||
.off = offsetof(struct mai2_dll, touch_init),
|
||||
}, {
|
||||
.sym = "mai2_io_touch_set_sens",
|
||||
.off = offsetof(struct mai2_dll, touch_set_sens),
|
||||
}, {
|
||||
.sym = "mai2_io_touch_update",
|
||||
.off = offsetof(struct mai2_dll, touch_update),
|
||||
}, {
|
||||
.sym = "mai2_io_led_init",
|
||||
.off = offsetof(struct mai2_dll, led_init),
|
||||
}, {
|
||||
.sym = "mai2_io_led_set_fet_output",
|
||||
.off = offsetof(struct mai2_dll, led_set_fet_output),
|
||||
}, {
|
||||
.sym = "mai2_io_led_dc_update",
|
||||
.off = offsetof(struct mai2_dll, led_dc_update),
|
||||
}, {
|
||||
.sym = "mai2_io_led_gs_update",
|
||||
.off = offsetof(struct mai2_dll, led_gs_update),
|
||||
},
|
||||
};
|
||||
|
||||
struct mai2_dll mai2_dll;
|
||||
|
@ -67,7 +88,7 @@ HRESULT mai2_dll_init(const struct mai2_dll_config *cfg, HINSTANCE self)
|
|||
if (get_api_version != NULL) {
|
||||
mai2_dll.api_version = get_api_version();
|
||||
} else {
|
||||
mai2_dll.api_version = 0x0100;
|
||||
mai2_dll.api_version = 0x0101;
|
||||
dprintf("Custom IO DLL does not expose mai2_io_get_api_version, "
|
||||
"assuming API version 1.0.\n"
|
||||
"Please ask the developer to update their DLL.\n");
|
||||
|
|
|
@ -10,6 +10,13 @@ struct mai2_dll {
|
|||
HRESULT (*poll)(void);
|
||||
void (*get_opbtns)(uint8_t *opbtn);
|
||||
void (*get_gamebtns)(uint16_t *player1, uint16_t *player2);
|
||||
HRESULT (*touch_init)(mai2_io_touch_callback_t callback);
|
||||
void (*touch_set_sens)(uint8_t *bytes);
|
||||
void (*touch_update)(bool player1, bool player2);
|
||||
HRESULT (*led_init)(void);
|
||||
void (*led_set_fet_output)(uint8_t board, const uint8_t *rgb);
|
||||
void (*led_dc_update)(uint8_t board, const uint8_t *rgb);
|
||||
void (*led_gs_update)(uint8_t board, const uint8_t *rgb);
|
||||
};
|
||||
|
||||
struct mai2_dll_config {
|
||||
|
|
|
@ -15,4 +15,11 @@ EXPORTS
|
|||
mai2_io_get_gamebtns
|
||||
mai2_io_get_opbtns
|
||||
mai2_io_init
|
||||
mai2_io_poll
|
||||
mai2_io_poll
|
||||
mai2_io_touch_init
|
||||
mai2_io_touch_set_sens
|
||||
mai2_io_touch_update
|
||||
mai2_io_led_init
|
||||
mai2_io_led_set_fet_output
|
||||
mai2_io_led_dc_update
|
||||
mai2_io_led_gs_update
|
||||
|
|
|
@ -23,6 +23,8 @@ shared_library(
|
|||
'dllmain.c',
|
||||
'io4.c',
|
||||
'io4.h',
|
||||
'touch.c',
|
||||
'touch.h',
|
||||
'mai2-dll.c',
|
||||
'mai2-dll.h',
|
||||
],
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hooklib/fdshark.h"
|
||||
#include "hooklib/reg.h"
|
||||
|
||||
#include "mai2hook/mai2-dll.h"
|
||||
#include "mai2hook/touch.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
|
||||
static HRESULT read_reg_touch_1p(void *bytes, uint32_t *nbytes)
|
||||
{
|
||||
return reg_hook_read_wstr(bytes, nbytes, L"COM3");
|
||||
}
|
||||
|
||||
static HRESULT read_reg_touch_2p(void *bytes, uint32_t *nbytes)
|
||||
{
|
||||
return reg_hook_read_wstr(bytes, nbytes, L"COM4");
|
||||
}
|
||||
|
||||
static const struct reg_hook_val touch_reg_key[] = {
|
||||
{
|
||||
.name = L"\\Device\\RealTouchBoard0",
|
||||
.read = read_reg_touch_1p,
|
||||
.type = REG_SZ,
|
||||
},
|
||||
{
|
||||
.name = L"\\Device\\RealTouchBoard1",
|
||||
.read = read_reg_touch_2p,
|
||||
.type = REG_SZ,
|
||||
},
|
||||
};
|
||||
|
||||
const char *sensor_map[34] = {
|
||||
"A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", // 0x41 - 0x48
|
||||
"B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", // 0x49 - 0x50
|
||||
"C1", "C2", // 0x51 - 0x52
|
||||
"D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", // 0x53 - 0x5A
|
||||
"E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8" // 0x5B - 0x62
|
||||
};
|
||||
|
||||
const char *sensor_to_str(uint8_t sensor)
|
||||
{
|
||||
if (sensor < 0x41 || sensor > 0x62)
|
||||
{
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
return sensor_map[sensor - 0x41];
|
||||
}
|
||||
|
||||
static CRITICAL_SECTION touch_1p_lock;
|
||||
static struct uart touch_1p_uart;
|
||||
static uint8_t touch_1p_written_bytes[64];
|
||||
static uint8_t touch_1p_readable_bytes[64];
|
||||
static bool touch_1p_status = false;
|
||||
|
||||
static CRITICAL_SECTION touch_2p_lock;
|
||||
static struct uart touch_2p_uart;
|
||||
static uint8_t touch_2p_written_bytes[64];
|
||||
static uint8_t touch_2p_readable_bytes[64];
|
||||
static bool touch_2p_status = false;
|
||||
|
||||
HRESULT touch_hook_init(const struct touch_config *cfg)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable_1p && !cfg->enable_2p)
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT hr = reg_hook_push_key(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", touch_reg_key, _countof(touch_reg_key));
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (cfg->enable_1p)
|
||||
{
|
||||
dprintf("Mai2 touch 1P: Init.\n");
|
||||
|
||||
InitializeCriticalSection(&touch_1p_lock);
|
||||
uart_init(&touch_1p_uart, 3);
|
||||
touch_1p_uart.written.bytes = touch_1p_written_bytes;
|
||||
touch_1p_uart.written.nbytes = sizeof(touch_1p_written_bytes);
|
||||
touch_1p_uart.readable.bytes = touch_1p_readable_bytes;
|
||||
touch_1p_uart.readable.nbytes = sizeof(touch_1p_readable_bytes);
|
||||
}
|
||||
|
||||
if (cfg->enable_2p)
|
||||
{
|
||||
dprintf("Mai2 touch 2P: Init.\n");
|
||||
|
||||
InitializeCriticalSection(&touch_2p_lock);
|
||||
uart_init(&touch_2p_uart, 4);
|
||||
touch_2p_uart.written.bytes = touch_2p_written_bytes;
|
||||
touch_2p_uart.written.nbytes = sizeof(touch_2p_written_bytes);
|
||||
touch_2p_uart.readable.bytes = touch_2p_readable_bytes;
|
||||
touch_2p_uart.readable.nbytes = sizeof(touch_2p_readable_bytes);
|
||||
}
|
||||
|
||||
return iohook_push_handler(touch_handle_irp);
|
||||
}
|
||||
|
||||
static HRESULT touch_handle_irp(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(irp != NULL);
|
||||
|
||||
if (uart_match_irp(&touch_1p_uart, irp))
|
||||
{
|
||||
EnterCriticalSection(&touch_1p_lock);
|
||||
hr = touch_handle_irp_locked(irp, &touch_1p_uart);
|
||||
LeaveCriticalSection(&touch_1p_lock);
|
||||
}
|
||||
else if (uart_match_irp(&touch_2p_uart, irp))
|
||||
{
|
||||
EnterCriticalSection(&touch_2p_lock);
|
||||
hr = touch_handle_irp_locked(irp, &touch_2p_uart);
|
||||
LeaveCriticalSection(&touch_2p_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (irp->op == IRP_OP_OPEN)
|
||||
{
|
||||
dprintf("Mai2 touch port %d: Starting backend\n", uart->port_no);
|
||||
hr = mai2_dll.touch_init(touch_auto_scan);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
dprintf("Mai2 touch port %d: Backend error: %x\n", uart->port_no, (int)hr);
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE)
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
#if defined(LOG_MAI2_TOUCH)
|
||||
dprintf("Mai2 touch port %d WRITE:\n", uart->port_no);
|
||||
dump_iobuf(&uart->written);
|
||||
#endif
|
||||
uint8_t port_no = uart->port_no;
|
||||
uint8_t *src = uart->written.bytes;
|
||||
uint8_t *dest = uart->readable.bytes;
|
||||
|
||||
switch (src[3])
|
||||
{
|
||||
case commandRSET:
|
||||
dprintf("Mai2 touch port %d: Reset\n", port_no);
|
||||
break;
|
||||
|
||||
case commandHALT: // Enter Conditioning mode and stop sending touch data.
|
||||
dprintf("Mai2 touch port %d: Halt\n", port_no);
|
||||
assert(mai2_dll.touch_update != NULL);
|
||||
if (port_no == 3)
|
||||
{
|
||||
EnterCriticalSection(&touch_1p_lock);
|
||||
touch_1p_status = false;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_1p_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnterCriticalSection(&touch_2p_lock);
|
||||
touch_2p_status = false;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_2p_lock);
|
||||
}
|
||||
break;
|
||||
|
||||
case commandSTAT: // Exit Conditioning mode and resume sending touch data.
|
||||
dprintf("Mai2 touch port %d: Stat\n", port_no);
|
||||
assert(mai2_dll.touch_update != NULL);
|
||||
if (port_no == 3)
|
||||
{
|
||||
EnterCriticalSection(&touch_1p_lock);
|
||||
touch_1p_status = true;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_1p_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnterCriticalSection(&touch_2p_lock);
|
||||
touch_2p_status = true;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_2p_lock);
|
||||
}
|
||||
break;
|
||||
|
||||
case commandRatio:
|
||||
#if defined(LOG_MAI2_TOUCH)
|
||||
dprintf("Mai2 touch side %c: set sensor %s ratio to %d\n", src[1], sensor_to_str(src[2]), src[4]);
|
||||
#endif
|
||||
dest[0] = res_start;
|
||||
dest[1] = src[1]; // L,R
|
||||
dest[2] = src[2]; // sensor
|
||||
dest[3] = commandRatio;
|
||||
dest[4] = src[4]; // Ratio
|
||||
dest[5] = res_end;
|
||||
uart->readable.pos = 6;
|
||||
// The Ratio is fixed at 0x72 and does not need to be sent to mai2io for processing.
|
||||
break;
|
||||
|
||||
case commandSens:
|
||||
#if defined(LOG_MAI2_TOUCH)
|
||||
dprintf("Mai2 touch side %c: set sensor %s sensitivity to %d\n", src[1], sensor_to_str(src[2]), src[4]);
|
||||
#endif
|
||||
dest[0] = res_start;
|
||||
dest[1] = src[1]; // L,R
|
||||
dest[2] = src[2]; // sensor
|
||||
dest[3] = commandSens;
|
||||
dest[4] = src[4]; // Sensitivity
|
||||
dest[5] = res_end;
|
||||
uart->readable.pos = 6;
|
||||
mai2_dll.touch_set_sens(dest);
|
||||
break;
|
||||
|
||||
default:
|
||||
dprintf("Mai2 touch port %d: Unknow %02x\n", port_no, src[3]);
|
||||
break;
|
||||
}
|
||||
#if defined(LOG_MAI2_TOUCH)
|
||||
dprintf("Mai2 touch port %d READ:\n", uart->port_no);
|
||||
dump_iobuf(&uart->readable);
|
||||
#endif
|
||||
uart->written.pos = 0;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static void touch_auto_scan(const uint8_t player, const uint8_t state[7])
|
||||
{
|
||||
struct uart *touch_uart = player == 1 ? &touch_1p_uart : &touch_2p_uart;
|
||||
touch_uart->readable.bytes[0] = res_start;
|
||||
memcpy(&touch_uart->readable.bytes[1], state, 7);
|
||||
touch_uart->readable.bytes[8] = res_end;
|
||||
touch_uart->readable.pos = 9;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hooklib/uart.h"
|
||||
|
||||
struct touch_config
|
||||
{
|
||||
bool enable_1p;
|
||||
bool enable_2p;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
commandRSET = 0x45, // E
|
||||
commandHALT = 0x4C, // L
|
||||
commandSTAT = 0x41, // A
|
||||
commandRatio = 0x72, // r
|
||||
commandSens = 0x6B, // k
|
||||
req_start = 0x7b, // {
|
||||
req_end = 0x7d, // }
|
||||
res_start = 0x28, // (
|
||||
res_end = 0x29, // )
|
||||
};
|
||||
|
||||
extern const char *sensor_map[34];
|
||||
const char *sensor_to_str(uint8_t sensor);
|
||||
|
||||
HRESULT touch_hook_init(const struct touch_config *cfg);
|
||||
static HRESULT touch_handle_irp(struct irp *irp);
|
||||
static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart);
|
||||
|
||||
/* Called in mai2io to send touch data.
|
||||
Similar to chuni slider_res_auto_scan, but the host does not require periodic updates.
|
||||
Touch data is sent only when there is a change. */
|
||||
static void touch_auto_scan(const uint8_t player, const uint8_t state[7]);
|
|
@ -6,6 +6,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "mai2io/config.h"
|
||||
#include "mai2hook/touch.h"
|
||||
|
||||
/*
|
||||
Maimai DX Default key binding
|
||||
|
@ -15,9 +16,24 @@ Maimai DX Default key binding
|
|||
static const int mai2_io_1p_default[] = {'W', 'E', 'D', 'C', 'X', 'Z', 'A', 'Q', '3'};
|
||||
static const int mai2_io_2p_default[] = {0x68, 0x69, 0x66, 0x63, 0x62, 0x61, 0x64, 0x67, 0x54};
|
||||
|
||||
static const int mai2_io_1p_touch_default[] = {
|
||||
'T', 'Y', 'H', 'N', 'B', 'V', 'F', 'R',
|
||||
'T', 'Y', 'H', 'N', 'B', 'V', 'F', 'R',
|
||||
'G', 'G',
|
||||
'T', 'Y', 'H', 'N', 'B', 'V', 'F', 'R',
|
||||
'T', 'Y', 'H', 'N', 'B', 'V', 'F', 'R',
|
||||
};
|
||||
static const int mai2_io_2p_touch_default[] = {
|
||||
'I', 'O', 'L', VK_OEM_PERIOD, VK_OEM_COMMA, 'M', 'J', 'U',
|
||||
'I', 'O', 'L', VK_OEM_PERIOD, VK_OEM_COMMA, 'M', 'J', 'U',
|
||||
'K', 'K',
|
||||
'I', 'O', 'L', VK_OEM_PERIOD, VK_OEM_COMMA, 'M', 'J', 'U',
|
||||
'I', 'O', 'L', VK_OEM_PERIOD, VK_OEM_COMMA, 'M', 'J', 'U',
|
||||
};
|
||||
|
||||
void mai2_io_config_load(
|
||||
struct mai2_io_config *cfg,
|
||||
const wchar_t *filename)
|
||||
struct mai2_io_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
wchar_t key[16];
|
||||
int i;
|
||||
|
@ -28,22 +44,41 @@ void mai2_io_config_load(
|
|||
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", VK_F1, filename);
|
||||
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", VK_F2, filename);
|
||||
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", VK_F3, filename);
|
||||
cfg->vk_btn_enable = GetPrivateProfileIntW(L"button", L"enable", 1, filename);
|
||||
|
||||
for (i = 0 ; i < 9 ; i++) {
|
||||
swprintf_s(key, _countof(key), L"1p_btn%i", i + 1);
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
swprintf_s(key, _countof(key), L"p1Btn%i", i + 1);
|
||||
cfg->vk_1p_btn[i] = GetPrivateProfileIntW(
|
||||
L"button",
|
||||
key,
|
||||
mai2_io_1p_default[i],
|
||||
filename);
|
||||
L"button",
|
||||
key,
|
||||
mai2_io_1p_default[i],
|
||||
filename);
|
||||
|
||||
swprintf_s(key, _countof(key), L"p2Btn%i", i + 1);
|
||||
cfg->vk_2p_btn[i] = GetPrivateProfileIntW(
|
||||
L"button",
|
||||
key,
|
||||
mai2_io_2p_default[i],
|
||||
filename);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < 9 ; i++) {
|
||||
swprintf_s(key, _countof(key), L"2p_btn%i", i + 1);
|
||||
cfg->vk_2p_btn[i] = GetPrivateProfileIntW(
|
||||
L"button",
|
||||
key,
|
||||
mai2_io_2p_default[i],
|
||||
filename);
|
||||
cfg->debug_input_1p = GetPrivateProfileIntW(L"touch", L"p1DebugInput", 0, filename);
|
||||
cfg->debug_input_2p = GetPrivateProfileIntW(L"touch", L"p2DebugInput", 0, filename);
|
||||
for (i = 0; i < 34; i++)
|
||||
{
|
||||
swprintf_s(key, _countof(key), L"p1Touch%S", sensor_map[i]);
|
||||
cfg->vk_1p_touch[i] = GetPrivateProfileIntW(
|
||||
L"touch",
|
||||
key,
|
||||
mai2_io_1p_touch_default[i],
|
||||
filename);
|
||||
|
||||
swprintf_s(key, _countof(key), L"p2Touch%S", sensor_map[i]);
|
||||
cfg->vk_2p_touch[i] = GetPrivateProfileIntW(
|
||||
L"touch",
|
||||
key,
|
||||
mai2_io_2p_touch_default[i],
|
||||
filename);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,13 @@ struct mai2_io_config {
|
|||
uint8_t vk_test;
|
||||
uint8_t vk_service;
|
||||
uint8_t vk_coin;
|
||||
bool vk_btn_enable;
|
||||
uint8_t vk_1p_btn[9];
|
||||
uint8_t vk_2p_btn[9];
|
||||
bool debug_input_1p;
|
||||
bool debug_input_2p;
|
||||
uint8_t vk_1p_touch[34];
|
||||
uint8_t vk_2p_touch[34];
|
||||
};
|
||||
|
||||
void mai2_io_config_load(
|
||||
|
|
159
mai2io/mai2io.c
159
mai2io/mai2io.c
|
@ -1,10 +1,11 @@
|
|||
#include <windows.h>
|
||||
#include "mai2io/mai2io.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <process.h>
|
||||
|
||||
#include "mai2io/mai2io.h"
|
||||
#include "mai2hook/touch.h"
|
||||
#include "mai2io/config.h"
|
||||
#include "util/dprintf.h"
|
||||
#include "util/env.h"
|
||||
|
||||
static uint8_t mai2_opbtn;
|
||||
|
@ -12,21 +13,21 @@ static uint16_t mai2_player1_btn;
|
|||
static uint16_t mai2_player2_btn;
|
||||
static struct mai2_io_config mai2_io_cfg;
|
||||
static bool mai2_io_coin;
|
||||
mai2_io_touch_callback_t _callback;
|
||||
static HANDLE mai2_io_touch_1p_thread;
|
||||
static bool mai2_io_touch_1p_stop_flag;
|
||||
static HANDLE mai2_io_touch_2p_thread;
|
||||
static bool mai2_io_touch_2p_stop_flag;
|
||||
|
||||
uint16_t mai2_io_get_api_version(void)
|
||||
{
|
||||
return 0x0100;
|
||||
}
|
||||
uint16_t mai2_io_get_api_version(void) { return 0x0101; }
|
||||
|
||||
HRESULT mai2_io_init(void)
|
||||
{
|
||||
HRESULT mai2_io_init(void) {
|
||||
mai2_io_config_load(&mai2_io_cfg, get_config_path());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT mai2_io_poll(void)
|
||||
{
|
||||
HRESULT mai2_io_poll(void) {
|
||||
mai2_opbtn = 0;
|
||||
mai2_player1_btn = 0;
|
||||
mai2_player2_btn = 0;
|
||||
|
@ -47,8 +48,13 @@ HRESULT mai2_io_poll(void)
|
|||
} else {
|
||||
mai2_io_coin = false;
|
||||
}
|
||||
// If sinmai has enabled DebugInput, there is no need to input buttons
|
||||
// through hook amdaemon.
|
||||
if (!mai2_io_cfg.vk_btn_enable) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//Player 1
|
||||
// Player 1
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_btn[0])) {
|
||||
mai2_player1_btn |= MAI2_IO_GAMEBTN_1;
|
||||
}
|
||||
|
@ -85,7 +91,7 @@ HRESULT mai2_io_poll(void)
|
|||
mai2_player1_btn |= MAI2_IO_GAMEBTN_SELECT;
|
||||
}
|
||||
|
||||
//Player 2
|
||||
// Player 2
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_btn[0])) {
|
||||
mai2_player2_btn |= MAI2_IO_GAMEBTN_1;
|
||||
}
|
||||
|
@ -125,20 +131,137 @@ HRESULT mai2_io_poll(void)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
void mai2_io_get_opbtns(uint8_t *opbtn)
|
||||
{
|
||||
void mai2_io_get_opbtns(uint8_t *opbtn) {
|
||||
if (opbtn != NULL) {
|
||||
*opbtn = mai2_opbtn;
|
||||
}
|
||||
}
|
||||
|
||||
void mai2_io_get_gamebtns(uint16_t *player1, uint16_t *player2)
|
||||
{
|
||||
void mai2_io_get_gamebtns(uint16_t *player1, uint16_t *player2) {
|
||||
if (player1 != NULL) {
|
||||
*player1 = mai2_player1_btn;
|
||||
}
|
||||
|
||||
if (player2 != NULL ){
|
||||
if (player2 != NULL) {
|
||||
*player2 = mai2_player2_btn;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT mai2_io_touch_init(mai2_io_touch_callback_t callback) {
|
||||
_callback = callback;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void mai2_io_touch_set_sens(uint8_t *bytes) {
|
||||
#if 0
|
||||
dprintf("Mai2 touch side %c: set sensor %s sensitivity to %d\n", bytes[1], sensor_to_str(bytes[2]), bytes[4]);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void mai2_io_touch_update(bool player1, bool player2) {
|
||||
if (mai2_io_cfg.debug_input_1p) {
|
||||
if (player1 && mai2_io_touch_1p_thread == NULL) {
|
||||
mai2_io_touch_1p_thread = (HANDLE)_beginthreadex(
|
||||
NULL, 0, mai2_io_touch_1p_thread_proc, _callback, 0, NULL);
|
||||
} else if (!player1 && mai2_io_touch_1p_thread != NULL) {
|
||||
mai2_io_touch_1p_stop_flag = true;
|
||||
|
||||
WaitForSingleObject(mai2_io_touch_1p_thread, INFINITE);
|
||||
CloseHandle(mai2_io_touch_1p_thread);
|
||||
mai2_io_touch_1p_thread = NULL;
|
||||
|
||||
mai2_io_touch_1p_stop_flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mai2_io_cfg.debug_input_2p) {
|
||||
if (player2 && mai2_io_touch_2p_thread == NULL) {
|
||||
mai2_io_touch_2p_thread = (HANDLE)_beginthreadex(
|
||||
NULL, 0, mai2_io_touch_2p_thread_proc, _callback, 0, NULL);
|
||||
} else if (!player2 && mai2_io_touch_2p_thread != NULL) {
|
||||
mai2_io_touch_2p_stop_flag = true;
|
||||
|
||||
WaitForSingleObject(mai2_io_touch_2p_thread, INFINITE);
|
||||
CloseHandle(mai2_io_touch_2p_thread);
|
||||
mai2_io_touch_2p_thread = NULL;
|
||||
|
||||
mai2_io_touch_2p_stop_flag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int __stdcall mai2_io_touch_1p_thread_proc(void *ctx) {
|
||||
mai2_io_touch_callback_t callback = ctx;
|
||||
|
||||
while (!mai2_io_touch_1p_stop_flag) {
|
||||
uint8_t state[7] = {0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
for (int i = 0; i < 34; i++) {
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_touch[i])) {
|
||||
int byteIndex = i / 5;
|
||||
int bitIndex = i % 5;
|
||||
state[byteIndex] |= (1 << bitIndex);
|
||||
}
|
||||
}
|
||||
callback(1, state);
|
||||
Sleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int __stdcall mai2_io_touch_2p_thread_proc(void *ctx) {
|
||||
mai2_io_touch_callback_t callback = ctx;
|
||||
|
||||
while (!mai2_io_touch_2p_stop_flag) {
|
||||
uint8_t state[7] = {0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
for (int i = 0; i < 34; i++) {
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_touch[i])) {
|
||||
int byteIndex = i / 5;
|
||||
int bitIndex = i % 5;
|
||||
state[byteIndex] |= (1 << bitIndex);
|
||||
}
|
||||
}
|
||||
callback(2, state);
|
||||
Sleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
HRESULT mai2_io_led_init(void) { return S_OK; }
|
||||
|
||||
void mai2_io_led_set_fet_output(uint8_t board, const uint8_t *rgb) {
|
||||
#if 0
|
||||
uint8_t player = board + 1;
|
||||
dprintf("MAI2 LED %dP: BodyLed brightness: %d%%\n", player,
|
||||
(rgb[0] * 100) / 255);
|
||||
dprintf("MAI2 LED %dP: ExtLed brightness: %d%%\n", player,
|
||||
(rgb[1] * 100) / 255);
|
||||
dprintf("MAI2 LED %dP: SideLed brightness: %d%%\n", player,
|
||||
(rgb[2] * 100) / 255);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void mai2_io_led_dc_update(uint8_t board, const uint8_t *rgb) {
|
||||
#if 0
|
||||
uint8_t player = board + 1;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
dprintf("Mai2 LED %dP: LED %d: %02X %02X %02X Speed: %02X\n", player
|
||||
i, rgb[i * 4], rgb[i * 4 + 1], rgb[i * 4 + 2], rgb[i * 4 + 3]);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void mai2_io_led_gs_update(uint8_t board, const uint8_t *rgb) {
|
||||
#if 0
|
||||
uint8_t player = board + 1;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
dprintf("Mai2 LED %dP: LED %d: %02X %02X %02X Speed: %02X\n", player, i,
|
||||
rgb[i * 4], rgb[i * 4 + 1], rgb[i * 4 + 2], rgb[i * 4 + 3]);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue