forked from Hay1tsme/segatools
Compare commits
19 Commits
2024-02-27
...
develop
Author | SHA1 | Date |
---|---|---|
Dniel97 | d8b3d41809 | |
Dniel97 | 3bfb046afc | |
Dniel97 | 9fe98b227b | |
Dniel97 | b77ce7b457 | |
Dniel97 | 517469a60c | |
Dniel97 | 1069cfee26 | |
Dniel97 | d3a0faa530 | |
arcfox | 00b3d5b7bb | |
arcfox | 04fcd0d09a | |
Dniel97 | 25e954fb55 | |
Dniel97 | a8c6ac70e4 | |
Dniel97 | eb1ec0e261 | |
Sucareto | 482a6e530a | |
beerpsi | 65173e1fa6 | |
beerpsi | 4041844ea9 | |
Dniel97 | 47a65e5e51 | |
Dniel97 | 774a639bb7 | |
Dniel97 | 097b74d849 | |
Dniel97 | 2590e749ca |
|
@ -1,6 +1,6 @@
|
||||||
# Segatools
|
# Segatools
|
||||||
|
|
||||||
Version: `2024-02-27`
|
Version: `2024-03-13`
|
||||||
|
|
||||||
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
|
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
|
||||||
|
|
||||||
|
@ -8,7 +8,9 @@ Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platfo
|
||||||
|
|
||||||
* CHUNITHM
|
* CHUNITHM
|
||||||
* up to [CHUNITHM PARADISE LOST](doc/chunihook.md)
|
* up to [CHUNITHM PARADISE LOST](doc/chunihook.md)
|
||||||
* starting from CHUNITHM NEW!!
|
* starting from CHUNITHM NEW!!
|
||||||
|
* crossbeats REV.
|
||||||
|
* up to crossbeats REV. SUNRISE
|
||||||
* Initial D
|
* Initial D
|
||||||
* [Initial D Arcade Stage Zero](doc/idzhook.md)
|
* [Initial D Arcade Stage Zero](doc/idzhook.md)
|
||||||
* Initial D THE ARCADE
|
* Initial D THE ARCADE
|
||||||
|
|
|
@ -68,7 +68,6 @@ static void aime_io_config_read(
|
||||||
cfg->felica_path,
|
cfg->felica_path,
|
||||||
_countof(cfg->felica_path),
|
_countof(cfg->felica_path),
|
||||||
filename);
|
filename);
|
||||||
// dprintf("NFC: felicaPath GetLastError %lx\n", GetLastError());
|
|
||||||
|
|
||||||
cfg->felica_gen = GetPrivateProfileIntW(
|
cfg->felica_gen = GetPrivateProfileIntW(
|
||||||
L"aime",
|
L"aime",
|
||||||
|
|
69
board/io3.c
69
board/io3.c
|
@ -79,6 +79,11 @@ static HRESULT io3_cmd_read_analogs(
|
||||||
struct const_iobuf *req_buf,
|
struct const_iobuf *req_buf,
|
||||||
struct iobuf *resp_buf);
|
struct iobuf *resp_buf);
|
||||||
|
|
||||||
|
static HRESULT io3_cmd_read_rotarys(
|
||||||
|
struct io3 *io3,
|
||||||
|
struct const_iobuf *req_buf,
|
||||||
|
struct iobuf *resp_buf);
|
||||||
|
|
||||||
static HRESULT io3_cmd_write_gpio(
|
static HRESULT io3_cmd_write_gpio(
|
||||||
struct io3 *io3,
|
struct io3 *io3,
|
||||||
struct const_iobuf *req_buf,
|
struct const_iobuf *req_buf,
|
||||||
|
@ -116,6 +121,13 @@ static uint8_t io3_features[] = {
|
||||||
|
|
||||||
0x03, 8, 10, 0,
|
0x03, 8, 10, 0,
|
||||||
|
|
||||||
|
/* Feature : 0x04 : Rotary inputs
|
||||||
|
Param1 : 4 : Number of rotary channels
|
||||||
|
Param2 : 0 : N/A
|
||||||
|
Param3 : 0 : N/A */
|
||||||
|
|
||||||
|
0x04, 4, 0, 0,
|
||||||
|
|
||||||
/* Feature : 0x12 : GPIO outputs
|
/* Feature : 0x12 : GPIO outputs
|
||||||
Param1 : 3 : Number of ports (8 bits per port)
|
Param1 : 3 : Number of ports (8 bits per port)
|
||||||
Param2 : 0 : N/A
|
Param2 : 0 : N/A
|
||||||
|
@ -218,6 +230,9 @@ static HRESULT io3_cmd(
|
||||||
|
|
||||||
case JVS_CMD_READ_ANALOGS:
|
case JVS_CMD_READ_ANALOGS:
|
||||||
return io3_cmd_read_analogs(io3, req, resp);
|
return io3_cmd_read_analogs(io3, req, resp);
|
||||||
|
|
||||||
|
case JVS_CMD_READ_ROTARYS:
|
||||||
|
return io3_cmd_read_rotarys(io3, req, resp);
|
||||||
|
|
||||||
case JVS_CMD_WRITE_GPIO:
|
case JVS_CMD_WRITE_GPIO:
|
||||||
return io3_cmd_write_gpio(io3, req, resp);
|
return io3_cmd_write_gpio(io3, req, resp);
|
||||||
|
@ -536,6 +551,60 @@ static HRESULT io3_cmd_read_analogs(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HRESULT io3_cmd_read_rotarys(
|
||||||
|
struct io3 *io3,
|
||||||
|
struct const_iobuf *req_buf,
|
||||||
|
struct iobuf *resp_buf)
|
||||||
|
{
|
||||||
|
struct jvs_req_read_rotarys req;
|
||||||
|
uint16_t rotarys[4];
|
||||||
|
uint8_t i;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
/* Read req */
|
||||||
|
|
||||||
|
hr = iobuf_read(req_buf, &req, sizeof(req));
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.nrotarys > _countof(rotarys)) {
|
||||||
|
dprintf("JVS I/O: Invalid analog count %i\n", req.nrotarys);
|
||||||
|
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//dprintf("JVS I/O: Read rotarys, nrotarys=%i\n", req.nrotarys);
|
||||||
|
|
||||||
|
/* Write report byte */
|
||||||
|
|
||||||
|
hr = iobuf_write_8(resp_buf, 0x01);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write analogs */
|
||||||
|
|
||||||
|
memset(rotarys, 0, sizeof(rotarys));
|
||||||
|
|
||||||
|
if (io3->ops->read_rotarys != NULL) {
|
||||||
|
io3->ops->read_rotarys(io3->ops_ctx, rotarys, req.nrotarys);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0 ; i < req.nrotarys ; i++) {
|
||||||
|
hr = iobuf_write_be16(resp_buf, rotarys[i]);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static HRESULT io3_cmd_write_gpio(
|
static HRESULT io3_cmd_write_gpio(
|
||||||
struct io3 *io3,
|
struct io3 *io3,
|
||||||
struct const_iobuf *req_buf,
|
struct const_iobuf *req_buf,
|
||||||
|
|
|
@ -18,6 +18,7 @@ struct io3_ops {
|
||||||
void (*write_gpio)(void *ctx, uint32_t state);
|
void (*write_gpio)(void *ctx, uint32_t state);
|
||||||
void (*read_switches)(void *ctx, struct io3_switch_state *out);
|
void (*read_switches)(void *ctx, struct io3_switch_state *out);
|
||||||
void (*read_analogs)(void *ctx, uint16_t *analogs, uint8_t nanalogs);
|
void (*read_analogs)(void *ctx, uint16_t *analogs, uint8_t nanalogs);
|
||||||
|
void (*read_rotarys)(void *ctx, uint16_t *rotaries, uint8_t nrotaries);
|
||||||
void (*read_coin_counter)(void *ctx, uint8_t slot_no, uint16_t *out);
|
void (*read_coin_counter)(void *ctx, uint8_t slot_no, uint16_t *out);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ static_assert(sizeof(struct io4_report_in) == 0x40, "IO4 IN report size");
|
||||||
struct io4_report_out {
|
struct io4_report_out {
|
||||||
uint8_t report_id;
|
uint8_t report_id;
|
||||||
uint8_t cmd;
|
uint8_t cmd;
|
||||||
uint8_t payload[62];
|
uint8_t payload[IO4_REPORT_OUT_PAYLOAD_LEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(struct io4_report_out) == 0x40, "IO4 OUT report size");
|
static_assert(sizeof(struct io4_report_out) == 0x40, "IO4 OUT report size");
|
||||||
|
@ -223,7 +223,11 @@ static HRESULT io4_handle_write(struct irp *irp)
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
case IO4_CMD_SET_GENERAL_OUTPUT:
|
case IO4_CMD_SET_GENERAL_OUTPUT:
|
||||||
dprintf("USB I/O: GPIO Out\n");
|
// dprintf("USB I/O: GPIO Out\n");
|
||||||
|
|
||||||
|
if (io4_ops->write_gpio != NULL) {
|
||||||
|
return io4_ops->write_gpio(out.payload, IO4_REPORT_OUT_PAYLOAD_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define IO4_REPORT_OUT_PAYLOAD_LEN 62
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
/* System buttons in button[0] */
|
/* System buttons in button[0] */
|
||||||
|
|
||||||
|
@ -24,6 +26,7 @@ struct io4_state {
|
||||||
|
|
||||||
struct io4_ops {
|
struct io4_ops {
|
||||||
HRESULT (*poll)(void *ctx, struct io4_state *state);
|
HRESULT (*poll)(void *ctx, struct io4_state *state);
|
||||||
|
HRESULT (*write_gpio)(uint8_t* payload, size_t len);
|
||||||
};
|
};
|
||||||
|
|
||||||
HRESULT io4_hook_init(
|
HRESULT io4_hook_init(
|
||||||
|
|
|
@ -25,6 +25,13 @@ struct sg_res_header {
|
||||||
uint8_t payload_len;
|
uint8_t payload_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* struct to save the version string with its length
|
||||||
|
to fix NUL terminator issues */
|
||||||
|
struct version_info {
|
||||||
|
const char *version;
|
||||||
|
uint8_t length;
|
||||||
|
};
|
||||||
|
|
||||||
typedef HRESULT (*sg_dispatch_fn_t)(
|
typedef HRESULT (*sg_dispatch_fn_t)(
|
||||||
void *ctx,
|
void *ctx,
|
||||||
const void *req,
|
const void *req,
|
||||||
|
|
|
@ -27,11 +27,11 @@ static HRESULT sg_led_cmd_set_color(
|
||||||
const struct sg_led *led,
|
const struct sg_led *led,
|
||||||
const struct sg_led_req_set_color *req);
|
const struct sg_led_req_set_color *req);
|
||||||
|
|
||||||
const char *sg_led_info[] = {
|
static const struct version_info led_version[] = {
|
||||||
"15084\xFF\x10\x00\x12",
|
{"15084\xFF\x10\x00\x12", 9},
|
||||||
"000-00000\xFF\x11\x40",
|
{"000-00000\xFF\x11\x40", 12},
|
||||||
// maybe the same?
|
// maybe the same?
|
||||||
"000-00000\xFF\x11\x40"
|
{"000-00000\xFF\x11\x40", 12}
|
||||||
};
|
};
|
||||||
|
|
||||||
void sg_led_init(
|
void sg_led_init(
|
||||||
|
@ -156,10 +156,10 @@ static HRESULT sg_led_cmd_get_info(
|
||||||
{
|
{
|
||||||
sg_led_dprintf(led, "Get info\n");
|
sg_led_dprintf(led, "Get info\n");
|
||||||
|
|
||||||
unsigned int len = strlen(sg_led_info[led->gen - 1]);
|
const struct version_info *fw = &led_version[led->gen - 1];
|
||||||
|
|
||||||
sg_res_init(&res->res, req, len);
|
sg_res_init(&res->res, req, fw->length);
|
||||||
memcpy(res->payload, sg_led_info[led->gen - 1], len);
|
memcpy(res->payload, fw->version, fw->length);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,16 +65,16 @@ static HRESULT sg_nfc_cmd_dummy(
|
||||||
const struct sg_req_header *req,
|
const struct sg_req_header *req,
|
||||||
struct sg_res_header *res);
|
struct sg_res_header *res);
|
||||||
|
|
||||||
static const char *hw_version[] = {
|
static const struct version_info hw_version[] = {
|
||||||
"TN32MSEC003S H/W Ver3.0",
|
{"TN32MSEC003S H/W Ver3.0", 23},
|
||||||
"837-15286",
|
{"837-15286", 9},
|
||||||
"837-15396"
|
{"837-15396", 9}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *fw_version[] = {
|
static const struct version_info fw_version[] = {
|
||||||
"TN32MSEC003S F/W Ver1.2",
|
{"TN32MSEC003S F/W Ver1.2", 23},
|
||||||
"\x94",
|
{"\x94", 1},
|
||||||
"\x94"
|
{"\x94", 1}
|
||||||
};
|
};
|
||||||
|
|
||||||
void sg_nfc_init(
|
void sg_nfc_init(
|
||||||
|
@ -217,11 +217,11 @@ static HRESULT sg_nfc_cmd_get_fw_version(
|
||||||
const struct sg_req_header *req,
|
const struct sg_req_header *req,
|
||||||
struct sg_nfc_res_get_fw_version *res)
|
struct sg_nfc_res_get_fw_version *res)
|
||||||
{
|
{
|
||||||
unsigned int len = strlen(fw_version[nfc->gen - 1]);
|
const struct version_info *fw = &fw_version[nfc->gen - 1];
|
||||||
|
|
||||||
/* Dest version is not NUL terminated, this is intentional */
|
/* Dest version is not NUL terminated, this is intentional */
|
||||||
sg_res_init(&res->res, req, len);
|
sg_res_init(&res->res, req, fw->length);
|
||||||
memcpy(res->version, fw_version[nfc->gen - 1], len);
|
memcpy(res->version, fw->version, fw->length);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -231,11 +231,11 @@ static HRESULT sg_nfc_cmd_get_hw_version(
|
||||||
const struct sg_req_header *req,
|
const struct sg_req_header *req,
|
||||||
struct sg_nfc_res_get_hw_version *res)
|
struct sg_nfc_res_get_hw_version *res)
|
||||||
{
|
{
|
||||||
unsigned int len = strlen(hw_version[nfc->gen - 1]);
|
const struct version_info *hw = &hw_version[nfc->gen - 1];
|
||||||
|
|
||||||
/* Dest version is not NUL terminated, this is intentional */
|
/* Dest version is not NUL terminated, this is intentional */
|
||||||
sg_res_init(&res->res, req, len);
|
sg_res_init(&res->res, req, hw->length);
|
||||||
memcpy(res->version, hw_version[nfc->gen - 1], len);
|
memcpy(res->version, hw->version, hw->length);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
58
board/vfd.c
58
board/vfd.c
|
@ -26,6 +26,7 @@ static HRESULT vfd_handle_irp(struct irp *irp);
|
||||||
static struct uart vfd_uart;
|
static struct uart vfd_uart;
|
||||||
static uint8_t vfd_written[512];
|
static uint8_t vfd_written[512];
|
||||||
static uint8_t vfd_readable[512];
|
static uint8_t vfd_readable[512];
|
||||||
|
UINT codepage;
|
||||||
|
|
||||||
HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no)
|
HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no)
|
||||||
{
|
{
|
||||||
|
@ -41,6 +42,7 @@ HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no)
|
||||||
vfd_uart.readable.bytes = vfd_readable;
|
vfd_uart.readable.bytes = vfd_readable;
|
||||||
vfd_uart.readable.nbytes = sizeof(vfd_readable);
|
vfd_uart.readable.nbytes = sizeof(vfd_readable);
|
||||||
|
|
||||||
|
codepage = GetACP();
|
||||||
dprintf("VFD: hook enabled.\n");
|
dprintf("VFD: hook enabled.\n");
|
||||||
|
|
||||||
return iohook_push_handler(vfd_handle_irp);
|
return iohook_push_handler(vfd_handle_irp);
|
||||||
|
@ -62,8 +64,60 @@ static HRESULT vfd_handle_irp(struct irp *irp)
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
dprintf("VFD TX:\n");
|
uint8_t cmd = 0;
|
||||||
dump_iobuf(&vfd_uart.written);
|
uint8_t str_1[512];
|
||||||
|
uint8_t str_2[512];
|
||||||
|
uint8_t str_1_len = 0;
|
||||||
|
uint8_t str_2_len = 0;
|
||||||
|
for (size_t i = 0; i < vfd_uart.written.pos; i++) {
|
||||||
|
if (vfd_uart.written.bytes[i] == 0x1B) {
|
||||||
|
i++;
|
||||||
|
cmd = vfd_uart.written.bytes[i];
|
||||||
|
if (cmd == 0x30) {
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
else if (cmd == 0x50) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cmd == 0x30) {
|
||||||
|
str_1[str_1_len++] = vfd_uart.written.bytes[i];
|
||||||
|
}
|
||||||
|
else if (cmd == 0x50) {
|
||||||
|
str_2[str_2_len++] = vfd_uart.written.bytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_1_len) {
|
||||||
|
str_1[str_1_len++] = '\0';
|
||||||
|
if (codepage != 932) {
|
||||||
|
WCHAR buffer[512];
|
||||||
|
MultiByteToWideChar(932, 0, (LPCSTR)str_1, str_1_len, buffer, str_1_len);
|
||||||
|
char str_recode[str_1_len * 3];
|
||||||
|
WideCharToMultiByte(codepage, 0, buffer, str_1_len, str_recode, str_1_len * 3, NULL, NULL);
|
||||||
|
dprintf("VFD: %s\n", str_recode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dprintf("VFD: %s\n", str_1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_2_len) {
|
||||||
|
str_2[str_2_len++] = '\0';
|
||||||
|
if (codepage != 932) {
|
||||||
|
WCHAR buffer[512];
|
||||||
|
MultiByteToWideChar(932, 0, (LPCSTR)str_2, str_2_len, buffer, str_2_len);
|
||||||
|
char str_recode[str_2_len * 3];
|
||||||
|
WideCharToMultiByte(codepage, 0, buffer, str_2_len, str_recode, str_2_len * 3, NULL, NULL);
|
||||||
|
dprintf("VFD: %s\n", str_recode);
|
||||||
|
} else {
|
||||||
|
dprintf("VFD: %s\n", str_2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dprintf("VFD TX:\n");
|
||||||
|
// dump_iobuf(&vfd_uart.written);
|
||||||
vfd_uart.written.pos = 0;
|
vfd_uart.written.pos = 0;
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
|
|
|
@ -163,9 +163,17 @@ HRESULT chuni_io_led_init(void);
|
||||||
Chunithm uses two chains/boards with WS2811 protocol (each logical led corresponds to 3 physical leds).
|
Chunithm uses two chains/boards with WS2811 protocol (each logical led corresponds to 3 physical leds).
|
||||||
board 0 is on the left side and board 1 on the right side of the cab
|
board 0 is on the left side and board 1 on the right side of the cab
|
||||||
|
|
||||||
left side has 5*10 rgb values for the billboard, followed by 3 rgb values for the air tower
|
Board 0 has 53 LEDs:
|
||||||
right side has 6*10 rgb values for the billboard, followed by 3 rgb values for the air tower
|
[0]-[49]: snakes through left half of billboard (first column starts at top)
|
||||||
|
[50]-[52]: left side partition LEDs
|
||||||
|
|
||||||
|
Board 1 has 63 LEDs:
|
||||||
|
[0]-[59]: right half of billboard (first column starts at bottom)
|
||||||
|
[60]-[62]: right side partition LEDs
|
||||||
|
|
||||||
|
Board 2 is the slider and has 31 LEDs:
|
||||||
|
[0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
|
||||||
|
|
||||||
Each rgb value is comprised of 3 bytes in R,G,B order
|
Each rgb value is comprised of 3 bytes in R,G,B order
|
||||||
|
|
||||||
NOTE: billboard strips have alternating direction (bottom to top, top to bottom, ...)
|
NOTE: billboard strips have alternating direction (bottom to top, top to bottom, ...)
|
||||||
|
|
|
@ -57,11 +57,11 @@ void chuni_io_config_load(
|
||||||
filename);
|
filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg->led_output_pipe = GetPrivateProfileIntW(L"led", L"cabLedOutputPipe", 1, filename);
|
cfg->cab_led_output_pipe = GetPrivateProfileIntW(L"led", L"cabLedOutputPipe", 1, filename);
|
||||||
cfg->led_output_serial = GetPrivateProfileIntW(L"led", L"cabLedOutputSerial", 0, filename);
|
cfg->cab_led_output_serial = GetPrivateProfileIntW(L"led", L"cabLedOutputSerial", 0, filename);
|
||||||
|
|
||||||
cfg->slider_led_output_pipe = GetPrivateProfileIntW(L"led", L"controllerLedOutputPipe", 1, filename);
|
cfg->controller_led_output_pipe = GetPrivateProfileIntW(L"led", L"controllerLedOutputPipe", 1, filename);
|
||||||
cfg->slider_led_output_serial = GetPrivateProfileIntW(L"led", L"controllerLedOutputSerial", 0, filename);
|
cfg->controller_led_output_serial = GetPrivateProfileIntW(L"led", L"controllerLedOutputSerial", 0, filename);
|
||||||
|
|
||||||
cfg->led_serial_baud = GetPrivateProfileIntW(L"led", L"serialBaud", 921600, filename);
|
cfg->led_serial_baud = GetPrivateProfileIntW(L"led", L"serialBaud", 921600, filename);
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ void chuni_io_config_load(
|
||||||
L"serialPort",
|
L"serialPort",
|
||||||
L"COM5",
|
L"COM5",
|
||||||
port_input,
|
port_input,
|
||||||
6,
|
_countof(port_input),
|
||||||
filename);
|
filename);
|
||||||
|
|
||||||
// Sanitize the output path. If it's a serial COM port, it needs to be prefixed
|
// Sanitize the output path. If it's a serial COM port, it needs to be prefixed
|
||||||
|
|
|
@ -12,16 +12,15 @@ struct chuni_io_config {
|
||||||
uint8_t vk_cell[32];
|
uint8_t vk_cell[32];
|
||||||
|
|
||||||
// Which ways to output LED information are enabled
|
// Which ways to output LED information are enabled
|
||||||
bool led_output_pipe;
|
bool cab_led_output_pipe;
|
||||||
bool led_output_serial;
|
bool cab_led_output_serial;
|
||||||
|
|
||||||
bool slider_led_output_pipe;
|
bool controller_led_output_pipe;
|
||||||
bool slider_led_output_serial;
|
bool controller_led_output_serial;
|
||||||
|
|
||||||
// The name of a COM port to output LED data on, in serial mode
|
// The name of a COM port to output LED data on, in serial mode
|
||||||
wchar_t led_serial_port[12];
|
wchar_t led_serial_port[12];
|
||||||
int32_t led_serial_baud;
|
int32_t led_serial_baud;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void chuni_io_config_load(
|
void chuni_io_config_load(
|
||||||
|
|
|
@ -48,15 +48,15 @@ HRESULT led_output_init(struct chuni_io_config* const cfg)
|
||||||
led_escaped_buf[i].data_len = chuni_led_board_data_lens[i];
|
led_escaped_buf[i].data_len = chuni_led_board_data_lens[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
any_outputs_enabled = config->led_output_pipe || config->slider_led_output_pipe
|
any_outputs_enabled = config->cab_led_output_pipe || config->controller_led_output_pipe
|
||||||
|| config->led_output_serial || config->slider_led_output_serial;
|
|| config->cab_led_output_serial || config->controller_led_output_serial;
|
||||||
|
|
||||||
if (config->led_output_pipe || config->slider_led_output_pipe)
|
if (config->cab_led_output_pipe || config->controller_led_output_pipe)
|
||||||
{
|
{
|
||||||
led_pipe_init(); // don't really care about errors here tbh
|
led_pipe_init(); // don't really care about errors here tbh
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->led_output_serial || config->slider_led_output_serial)
|
if (config->cab_led_output_serial || config->controller_led_output_serial)
|
||||||
{
|
{
|
||||||
led_serial_init(config->led_serial_port, config->led_serial_baud);
|
led_serial_init(config->led_serial_port, config->led_serial_baud);
|
||||||
}
|
}
|
||||||
|
@ -106,13 +106,13 @@ void led_output_update(uint8_t board, const byte* rgb)
|
||||||
|
|
||||||
if (board < 2)
|
if (board < 2)
|
||||||
{
|
{
|
||||||
// billboard
|
// billboard (cab)
|
||||||
if (config->led_output_pipe)
|
if (config->cab_led_output_pipe)
|
||||||
{
|
{
|
||||||
led_pipe_update(escaped_data);
|
led_pipe_update(escaped_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->led_output_serial)
|
if (config->cab_led_output_serial)
|
||||||
{
|
{
|
||||||
led_serial_update(escaped_data);
|
led_serial_update(escaped_data);
|
||||||
}
|
}
|
||||||
|
@ -120,12 +120,12 @@ void led_output_update(uint8_t board, const byte* rgb)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// slider
|
// slider
|
||||||
if (config->slider_led_output_pipe)
|
if (config->controller_led_output_pipe)
|
||||||
{
|
{
|
||||||
led_pipe_update(escaped_data);
|
led_pipe_update(escaped_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->slider_led_output_serial)
|
if (config->controller_led_output_serial)
|
||||||
{
|
{
|
||||||
led_serial_update(escaped_data);
|
led_serial_update(escaped_data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
Credits:
|
Credits:
|
||||||
somewhatlurker, skogaby
|
somewhatlurker, skogaby
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
|
@ -170,7 +170,7 @@ HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self)
|
||||||
"\"%s\". Please contact your IO DLL's developer for "
|
"\"%s\". Please contact your IO DLL's developer for "
|
||||||
"further assistance.\n",
|
"further assistance.\n",
|
||||||
sym->sym);
|
sym->sym);
|
||||||
dprintf("imported %d symbols\n",bind_count);
|
dprintf("imported %d symbols\n", bind_count);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -40,4 +40,5 @@ void cm_hook_config_load(
|
||||||
vfd_config_load(&cfg->vfd, filename);
|
vfd_config_load(&cfg->vfd, filename);
|
||||||
touch_screen_config_load(&cfg->touch, filename);
|
touch_screen_config_load(&cfg->touch, filename);
|
||||||
cm_dll_config_load(&cfg->dll, filename);
|
cm_dll_config_load(&cfg->dll, filename);
|
||||||
|
unity_config_load(&cfg->unity, filename);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include "platform/config.h"
|
#include "platform/config.h"
|
||||||
|
|
||||||
|
#include "unityhook/config.h"
|
||||||
|
|
||||||
struct cm_hook_config {
|
struct cm_hook_config {
|
||||||
struct platform_config platform;
|
struct platform_config platform;
|
||||||
struct aime_config aime;
|
struct aime_config aime;
|
||||||
|
@ -19,6 +21,7 @@ struct cm_hook_config {
|
||||||
struct vfd_config vfd;
|
struct vfd_config vfd;
|
||||||
struct cm_dll_config dll;
|
struct cm_dll_config dll;
|
||||||
struct touch_screen_config touch;
|
struct touch_screen_config touch;
|
||||||
|
struct unity_config unity;
|
||||||
};
|
};
|
||||||
|
|
||||||
void cm_dll_config_load(
|
void cm_dll_config_load(
|
||||||
|
|
|
@ -16,10 +16,11 @@
|
||||||
#include "cmhook/config.h"
|
#include "cmhook/config.h"
|
||||||
#include "cmhook/io4.h"
|
#include "cmhook/io4.h"
|
||||||
#include "cmhook/cm-dll.h"
|
#include "cmhook/cm-dll.h"
|
||||||
#include "cmhook/unity.h"
|
|
||||||
|
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
|
|
||||||
|
#include "unityhook/hook.h"
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
static HMODULE cm_hook_mod;
|
static HMODULE cm_hook_mod;
|
||||||
|
@ -83,7 +84,7 @@ static DWORD CALLBACK cm_pre_startup(void)
|
||||||
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
|
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
|
||||||
hooked earlier in the `cmhook` initialization. */
|
hooked earlier in the `cmhook` initialization. */
|
||||||
|
|
||||||
unity_hook_init();
|
unity_hook_init(&cm_hook_cfg.unity, cm_hook_mod);
|
||||||
|
|
||||||
/* Initialize debug helpers */
|
/* Initialize debug helpers */
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ shared_library(
|
||||||
hooklib_lib,
|
hooklib_lib,
|
||||||
cmio_lib,
|
cmio_lib,
|
||||||
platform_lib,
|
platform_lib,
|
||||||
|
unityhook_lib,
|
||||||
util_lib,
|
util_lib,
|
||||||
],
|
],
|
||||||
sources : [
|
sources : [
|
||||||
|
@ -26,7 +27,5 @@ shared_library(
|
||||||
'io4.h',
|
'io4.h',
|
||||||
'cm-dll.c',
|
'cm-dll.c',
|
||||||
'cm-dll.h',
|
'cm-dll.h',
|
||||||
'unity.h',
|
|
||||||
'unity.c',
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#include "hook/table.h"
|
|
||||||
|
|
||||||
#include "hooklib/dll.h"
|
|
||||||
#include "hooklib/path.h"
|
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
|
||||||
|
|
||||||
static void dll_hook_insert_hooks(HMODULE target);
|
|
||||||
|
|
||||||
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name);
|
|
||||||
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
|
|
||||||
|
|
||||||
static const struct hook_symbol unity_kernel32_syms[] = {
|
|
||||||
{
|
|
||||||
.name = "LoadLibraryW",
|
|
||||||
.patch = my_LoadLibraryW,
|
|
||||||
.link = (void **) &next_LoadLibraryW,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const wchar_t *target_modules[] = {
|
|
||||||
L"mono.dll",
|
|
||||||
L"cri_ware_unity.dll",
|
|
||||||
};
|
|
||||||
static const size_t target_modules_len = _countof(target_modules);
|
|
||||||
|
|
||||||
void unity_hook_init(void)
|
|
||||||
{
|
|
||||||
dll_hook_insert_hooks(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dll_hook_insert_hooks(HMODULE target)
|
|
||||||
{
|
|
||||||
hook_table_apply(
|
|
||||||
target,
|
|
||||||
"kernel32.dll",
|
|
||||||
unity_kernel32_syms,
|
|
||||||
_countof(unity_kernel32_syms));
|
|
||||||
}
|
|
||||||
|
|
||||||
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
|
|
||||||
{
|
|
||||||
const wchar_t *name_end;
|
|
||||||
const wchar_t *target_module;
|
|
||||||
bool already_loaded;
|
|
||||||
HMODULE result;
|
|
||||||
size_t name_len;
|
|
||||||
size_t target_module_len;
|
|
||||||
|
|
||||||
if (name == NULL) {
|
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the module is already loaded
|
|
||||||
already_loaded = GetModuleHandleW(name) != NULL;
|
|
||||||
|
|
||||||
// Must call the next handler so the DLL reference count is incremented
|
|
||||||
result = next_LoadLibraryW(name);
|
|
||||||
|
|
||||||
if (!already_loaded && result != NULL) {
|
|
||||||
name_len = wcslen(name);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < target_modules_len; i++) {
|
|
||||||
target_module = target_modules[i];
|
|
||||||
target_module_len = wcslen(target_module);
|
|
||||||
|
|
||||||
// Check if the newly loaded library is at least the length of
|
|
||||||
// the name of the target module
|
|
||||||
if (name_len < target_module_len) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
name_end = &name[name_len - target_module_len];
|
|
||||||
|
|
||||||
// Check if the name of the newly loaded library is one of the
|
|
||||||
// modules the path hooks should be injected into
|
|
||||||
if (_wcsicmp(name_end, target_module) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
dprintf("Unity: Loaded %S\n", target_module);
|
|
||||||
|
|
||||||
dll_hook_insert_hooks(result);
|
|
||||||
path_hook_insert_hooks(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
void unity_hook_init(void);
|
|
|
@ -23,22 +23,32 @@ void cxb_dll_config_load(
|
||||||
struct cxb_dll_config *cfg,
|
struct cxb_dll_config *cfg,
|
||||||
const wchar_t *filename)
|
const wchar_t *filename)
|
||||||
{
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"cxbio",
|
||||||
|
L"path",
|
||||||
|
L"",
|
||||||
|
cfg->path,
|
||||||
|
_countof(cfg->path),
|
||||||
|
filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void revio_config_load(struct revio_config *cfg, const wchar_t *filename)
|
void revio_config_load(struct revio_config *cfg, const wchar_t *filename)
|
||||||
{
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
}
|
cfg->enable = GetPrivateProfileIntW(L"revio", L"enable", 1, filename);
|
||||||
|
|
||||||
void network_config_load(struct network_config *cfg, const wchar_t *filename)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void led_config_load(struct led_config *cfg, const wchar_t *filename)
|
void led_config_load(struct led_config *cfg, const wchar_t *filename)
|
||||||
{
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
cfg->enable = GetPrivateProfileIntW(L"led", L"enable", 1, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cxb_hook_config_load(
|
void cxb_hook_config_load(
|
||||||
|
@ -56,6 +66,5 @@ void cxb_hook_config_load(
|
||||||
gfx_config_load(&cfg->gfx, filename);
|
gfx_config_load(&cfg->gfx, filename);
|
||||||
cxb_dll_config_load(&cfg->dll, filename);
|
cxb_dll_config_load(&cfg->dll, filename);
|
||||||
revio_config_load(&cfg->revio, filename);
|
revio_config_load(&cfg->revio, filename);
|
||||||
network_config_load(&cfg->network, filename);
|
|
||||||
led_config_load(&cfg->led, filename);
|
led_config_load(&cfg->led, filename);
|
||||||
}
|
}
|
|
@ -10,7 +10,6 @@
|
||||||
#include "cxbhook/cxb-dll.h"
|
#include "cxbhook/cxb-dll.h"
|
||||||
#include "cxbhook/revio.h"
|
#include "cxbhook/revio.h"
|
||||||
#include "cxbhook/led.h"
|
#include "cxbhook/led.h"
|
||||||
#include "cxbhook/network.h"
|
|
||||||
|
|
||||||
#include "gfxhook/gfx.h"
|
#include "gfxhook/gfx.h"
|
||||||
|
|
||||||
|
@ -23,7 +22,6 @@ struct cxb_hook_config {
|
||||||
struct gfx_config gfx;
|
struct gfx_config gfx;
|
||||||
struct cxb_dll_config dll;
|
struct cxb_dll_config dll;
|
||||||
struct revio_config revio;
|
struct revio_config revio;
|
||||||
struct network_config network;
|
|
||||||
struct led_config led;
|
struct led_config led;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,7 +30,6 @@ void cxb_dll_config_load(
|
||||||
const wchar_t *filename);
|
const wchar_t *filename);
|
||||||
|
|
||||||
void revio_config_load(struct revio_config *cfg, const wchar_t *filename);
|
void revio_config_load(struct revio_config *cfg, const wchar_t *filename);
|
||||||
void network_config_load(struct network_config *cfg, const wchar_t *filename);
|
|
||||||
void led_config_load(struct led_config *cfg, const wchar_t *filename);
|
void led_config_load(struct led_config *cfg, const wchar_t *filename);
|
||||||
|
|
||||||
void cxb_hook_config_load(
|
void cxb_hook_config_load(
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "cxbhook/config.h"
|
#include "cxbhook/config.h"
|
||||||
#include "cxbhook/revio.h"
|
#include "cxbhook/revio.h"
|
||||||
#include "cxbhook/led.h"
|
#include "cxbhook/led.h"
|
||||||
#include "cxbhook/network.h"
|
|
||||||
|
|
||||||
#include "cxbio/cxbio.h"
|
#include "cxbio/cxbio.h"
|
||||||
|
|
||||||
|
@ -103,12 +102,6 @@ static DWORD CALLBACK cxb_pre_startup(void)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = network_hook_init(&cxb_hook_cfg.network);
|
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = led_hook_init(&cxb_hook_cfg.led);
|
hr = led_hook_init(&cxb_hook_cfg.led);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "cxbhook/led.h"
|
#include "cxbhook/led.h"
|
||||||
#include "cxbhook/cxb-dll.h"
|
#include "cxbhook/cxb-dll.h"
|
||||||
|
|
||||||
#include "hooklib/procaddr.h"
|
#include "hook/procaddr.h"
|
||||||
|
|
||||||
#include "hook/table.h"
|
#include "hook/table.h"
|
||||||
|
|
||||||
|
@ -49,8 +49,14 @@ static struct hook_symbol lamp_syms[] = {
|
||||||
|
|
||||||
HRESULT led_hook_init(struct led_config *cfg)
|
HRESULT led_hook_init(struct led_config *cfg)
|
||||||
{
|
{
|
||||||
dprintf("LED: Init\n");
|
assert(cfg != NULL);
|
||||||
return proc_addr_table_push("CommLamp.dll", lamp_syms, _countof(lamp_syms));
|
|
||||||
|
if (!cfg->enable) {
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("LED: Hook enabled.\n");
|
||||||
|
return proc_addr_table_push(NULL, "CommLamp.dll", lamp_syms, _countof(lamp_syms));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int my_cCommLamp_Open(char *port)
|
static int my_cCommLamp_Open(char *port)
|
||||||
|
|
|
@ -30,7 +30,5 @@ shared_library(
|
||||||
'revio.h',
|
'revio.h',
|
||||||
'led.c',
|
'led.c',
|
||||||
'led.h',
|
'led.h',
|
||||||
'network.c',
|
|
||||||
'network.h',
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
#include <windows.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "cxbhook/network.h"
|
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
|
||||||
|
|
||||||
HRESULT network_hook_init(struct network_config *cfg)
|
|
||||||
{
|
|
||||||
dprintf("Network: Init\n");
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
struct network_config {
|
|
||||||
bool enable;
|
|
||||||
bool disable_ssl;
|
|
||||||
char title_server[PATH_MAX];
|
|
||||||
};
|
|
||||||
|
|
||||||
HRESULT network_hook_init(struct network_config *cfg);
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "cxbhook/revio.h"
|
#include "cxbhook/revio.h"
|
||||||
#include "cxbhook/cxb-dll.h"
|
#include "cxbhook/cxb-dll.h"
|
||||||
|
|
||||||
#include "hooklib/procaddr.h"
|
#include "hook/procaddr.h"
|
||||||
|
|
||||||
#include "hook/table.h"
|
#include "hook/table.h"
|
||||||
|
|
||||||
|
@ -82,8 +82,14 @@ static struct hook_symbol revio_syms[] = {
|
||||||
|
|
||||||
HRESULT revio_hook_init(struct revio_config *cfg)
|
HRESULT revio_hook_init(struct revio_config *cfg)
|
||||||
{
|
{
|
||||||
dprintf("Revio: Init\n");
|
assert(cfg != NULL);
|
||||||
return proc_addr_table_push("CommIo.dll", revio_syms, _countof(revio_syms));
|
|
||||||
|
if (!cfg->enable) {
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("Revio: Hook enabled.\n");
|
||||||
|
return proc_addr_table_push(NULL, "CommIo.dll", revio_syms, _countof(revio_syms));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int my_cCommIo_Open(char *port)
|
static int my_cCommIo_Open(char *port)
|
||||||
|
@ -154,7 +160,7 @@ static int my_cCommIo_GetTrigger()
|
||||||
|
|
||||||
out &= ~last_triggers;
|
out &= ~last_triggers;
|
||||||
|
|
||||||
dprintf("Revio: GetTrigger %X\n", out);
|
// dprintf("Revio: GetTrigger %X\n", out);
|
||||||
last_triggers = out;
|
last_triggers = out;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -188,7 +194,7 @@ static int my_cCommIo_GetRelease()
|
||||||
|
|
||||||
out &= ~btns;
|
out &= ~btns;
|
||||||
|
|
||||||
dprintf("Revio: GetRelease %X\n", out);
|
// dprintf("Revio: GetRelease %X\n", out);
|
||||||
last_triggers = btns;
|
last_triggers = btns;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,20 +63,6 @@ framed=1
|
||||||
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
||||||
monitor=0
|
monitor=0
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Custom IO settings
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
[aimeio]
|
|
||||||
; To use a custom card reader IO DLL enter its path here.
|
|
||||||
; Leave empty if you want to use Segatools built-in keyboard input.
|
|
||||||
path=
|
|
||||||
|
|
||||||
[chuniio]
|
|
||||||
; To use a custom Chunithm IO DLL enter its path here.
|
|
||||||
; Leave empty if you want to use Segatools built-in keyboard input.
|
|
||||||
path=
|
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
; LED settings
|
; LED settings
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
|
@ -122,6 +108,20 @@ controllerLedOutputSerial=0
|
||||||
; [0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
|
; [0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
|
||||||
|
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Custom IO settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[aimeio]
|
||||||
|
; To use a custom card reader IO DLL enter its path here.
|
||||||
|
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||||
|
path=
|
||||||
|
|
||||||
|
[chuniio]
|
||||||
|
; To use a custom Chunithm IO DLL enter its path here.
|
||||||
|
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||||
|
path=
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
; Input settings
|
; Input settings
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -88,25 +88,6 @@ framed=0
|
||||||
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
||||||
monitor=0
|
monitor=0
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Custom IO settings
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
[aimeio]
|
|
||||||
; To use a custom card reader IO DLL (x64) enter its path here.
|
|
||||||
; Leave empty if you want to use Segatools built-in keyboard input.
|
|
||||||
path=
|
|
||||||
|
|
||||||
[chuniio]
|
|
||||||
; Uncomment this if you have custom chuniio implementation comprised of a single 32bit DLL.
|
|
||||||
; (will use chu2to3 engine internally)
|
|
||||||
;path=
|
|
||||||
|
|
||||||
; Uncomment both of these if you have custom chuniio implementation comprised of two DLLs.
|
|
||||||
; x86 chuniio to path32, x64 to path64. Both are necessary.
|
|
||||||
;path32=
|
|
||||||
;path64=
|
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
; LED settings
|
; LED settings
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
|
@ -152,6 +133,25 @@ controllerLedOutputSerial=0
|
||||||
; [0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
|
; [0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
|
||||||
|
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Custom IO settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[aimeio]
|
||||||
|
; To use a custom card reader IO DLL (x64) enter its path here.
|
||||||
|
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||||
|
path=
|
||||||
|
|
||||||
|
[chuniio]
|
||||||
|
; Uncomment this if you have custom chuniio implementation comprised of a single 32bit DLL.
|
||||||
|
; (will use chu2to3 engine internally)
|
||||||
|
;path=
|
||||||
|
|
||||||
|
; Uncomment both of these if you have custom chuniio implementation comprised of two DLLs.
|
||||||
|
; x86 chuniio to path32, x64 to path64. Both are necessary.
|
||||||
|
;path32=
|
||||||
|
;path64=
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
; Input settings
|
; Input settings
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -68,6 +68,11 @@ dipsw1=0
|
||||||
; Enable/Disable WinTouch emulation
|
; Enable/Disable WinTouch emulation
|
||||||
enable=0
|
enable=0
|
||||||
|
|
||||||
|
[unity]
|
||||||
|
; Path to a .NET DLL that should run before the game. Useful for loading
|
||||||
|
; modding frameworks such as BepInEx.
|
||||||
|
targetAssembly=
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
; Custom IO settings
|
; Custom IO settings
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -19,10 +19,10 @@ appdata=
|
||||||
|
|
||||||
[aime]
|
[aime]
|
||||||
; Aime reader emulation
|
; Aime reader emulation
|
||||||
; CXB is stupid, so we have to make the paths go back one
|
|
||||||
enable=1
|
enable=1
|
||||||
|
; CXB is stupid, so we have to make the paths go back two directories. This
|
||||||
|
; will load the file from "resource\DEVICE\aime.txt".
|
||||||
aimePath=../DEVICE/aime.txt
|
aimePath=../DEVICE/aime.txt
|
||||||
felicaPath=../DEVICE/felica.txt
|
|
||||||
|
|
||||||
[led]
|
[led]
|
||||||
; Emulation for the LED board. Currently it's just dummy responses,
|
; Emulation for the LED board. Currently it's just dummy responses,
|
||||||
|
@ -39,6 +39,10 @@ enable=1
|
||||||
; Note that 127.0.0.1, localhost etc are specifically rejected.
|
; Note that 127.0.0.1, localhost etc are specifically rejected.
|
||||||
default=127.0.0.1
|
default=127.0.0.1
|
||||||
|
|
||||||
|
; Set the title server hostname or IP address here, as the title server
|
||||||
|
; is hardcoded in the game.
|
||||||
|
title=https://127.0.0.1:9002
|
||||||
|
|
||||||
[netenv]
|
[netenv]
|
||||||
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
|
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
|
||||||
; Crossbeats is extremely picky about its LAN environment, so leaving this
|
; Crossbeats is extremely picky about its LAN environment, so leaving this
|
||||||
|
@ -106,13 +110,14 @@ path=
|
||||||
|
|
||||||
[revio]
|
[revio]
|
||||||
; Enable emulation of the rev IO board
|
; Enable emulation of the rev IO board
|
||||||
enabe=1
|
enable=1
|
||||||
; Test button virtual-key code. Default is the F1 key.
|
; Test button virtual-key code. Default is the F1 key.
|
||||||
test=0x70
|
test=0x70
|
||||||
; Service button virtual-key code. Default is the F2 key.
|
; Service button virtual-key code. Default is the F2 key.
|
||||||
service=0x71
|
service=0x71
|
||||||
; Keyboard button to increment coin counter. Default is the F3 key.
|
; Keyboard button to increment coin counter. Default is the F3 key.
|
||||||
coin=0x72
|
coin=0x72
|
||||||
|
|
||||||
; Menu up key. Default is up arrow.
|
; Menu up key. Default is up arrow.
|
||||||
up=0x26
|
up=0x26
|
||||||
; Menu down key. Default is down arrow.
|
; Menu down key. Default is down arrow.
|
||||||
|
|
|
@ -128,7 +128,6 @@ path=
|
||||||
; world. An improved solution will be provided later.
|
; world. An improved solution will be provided later.
|
||||||
|
|
||||||
[io4]
|
[io4]
|
||||||
; Input API selection for JVS input emulator.
|
|
||||||
; Test button virtual-key code. Default is the F1 key.
|
; Test button virtual-key code. Default is the F1 key.
|
||||||
test=0x70
|
test=0x70
|
||||||
; Service button virtual-key code. Default is the F2 key.
|
; Service button virtual-key code. Default is the F2 key.
|
||||||
|
|
|
@ -2,20 +2,6 @@
|
||||||
|
|
||||||
pushd %~dp0
|
pushd %~dp0
|
||||||
|
|
||||||
REM set the APP_DIR to the Y drive
|
|
||||||
set APP_DIR=Y:\SDGT
|
|
||||||
|
|
||||||
REM create the APP_DIR if it doesn't exist and redirect it to the TEMP folder
|
|
||||||
if not exist "%APP_DIR%" (
|
|
||||||
subst Y: %TEMP%
|
|
||||||
REM timeout /t 1
|
|
||||||
if not exist "%APP_DIR%" (
|
|
||||||
mkdir "%APP_DIR%"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Mounted the Y:\ drive to the %TEMP%\SDGT folder
|
|
||||||
|
|
||||||
set AMDAEMON_CFG=config_common.json ^
|
set AMDAEMON_CFG=config_common.json ^
|
||||||
config_ex.json ^
|
config_ex.json ^
|
||||||
config_jp.json ^
|
config_jp.json ^
|
||||||
|
@ -40,11 +26,14 @@ config_seat_single_jp.json ^
|
||||||
config_hook.json
|
config_hook.json
|
||||||
|
|
||||||
start "AM Daemon" /min inject -d -k idachook.dll amdaemon.exe -c %AMDAEMON_CFG%
|
start "AM Daemon" /min inject -d -k idachook.dll amdaemon.exe -c %AMDAEMON_CFG%
|
||||||
inject -d -k idachook.dll ..\WindowsNoEditor\GameProject.exe -culture=en launch=Cabinet ABSLOG="..\..\..\..\..\Userdata\GameProject.log" -Master -UserDir="..\..\..\Userdata" -NotInstalled -UNATTENDED
|
|
||||||
taskkill /f /im amdaemon.exe > nul 2>&1
|
|
||||||
|
|
||||||
REM unmount the APP_DIR
|
rem JP
|
||||||
subst Y: /d > nul 2>&1
|
rem inject -d -k idachook.dll ..\WindowsNoEditor\GameProject\Binaries\Win64\GameProject-Win64-Shipping.exe -culture=ja launch=Cabinet ABSLOG="..\..\..\..\..\Userdata\GameProject.log" -Master -UserDir="..\..\..\Userdata" -NotInstalled -UNATTENDED
|
||||||
|
|
||||||
|
rem EXP
|
||||||
|
inject -d -k idachook.dll ..\WindowsNoEditor\GameProject\Binaries\Win64\GameProject-Win64-Shipping.exe -culture=en launch=Cabinet ABSLOG="..\..\..\..\..\Userdata\GameProject.log" -Master -UserDir="..\..\..\Userdata" -NotInstalled -UNATTENDED
|
||||||
|
|
||||||
|
taskkill /f /im amdaemon.exe > nul 2>&1
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo Game processes have terminated
|
echo Game processes have terminated
|
||||||
|
|
|
@ -69,6 +69,18 @@ freeplay=0
|
||||||
; this to 1 on exactly one machine and set this to 0 on all others.
|
; this to 1 on exactly one machine and set this to 0 on all others.
|
||||||
dipsw1=1
|
dipsw1=1
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; Misc. hook settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[unity]
|
||||||
|
; Enable Unity hook. This will allow you to run custom .NET code before the game
|
||||||
|
enable=1
|
||||||
|
|
||||||
|
; Path to a .NET DLL that should run before the game. Useful for loading
|
||||||
|
; modding frameworks such as BepInEx.
|
||||||
|
targetAssembly=
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
; Custom IO settings
|
; Custom IO settings
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
pushd %~dp0
|
pushd %~dp0
|
||||||
|
|
||||||
start "AM Daemon" /min inject -d -k mai2hook.dll amdaemon.exe -f -c config_common.json config_server.json config_client.json
|
start "AM Daemon" /min inject -d -k mai2hook.dll amdaemon.exe -f -c config_common.json config_server.json config_client.json
|
||||||
inject -d -k mai2hook.dll sinmai -screen-fullscreen 0
|
inject -d -k mai2hook.dll sinmai -screen-fullscreen 0 -popupwindow -screen-width 2160 -screen-height 1920 -silent-crashes
|
||||||
|
|
||||||
taskkill /f /im amdaemon.exe > nul 2>&1
|
taskkill /f /im amdaemon.exe > nul 2>&1
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
|
|
|
@ -72,6 +72,62 @@ dipsw1=1
|
||||||
[gfx]
|
[gfx]
|
||||||
enable=1
|
enable=1
|
||||||
|
|
||||||
|
[unity]
|
||||||
|
; Enable Unity hook. This will allow you to run custom .NET code before the game
|
||||||
|
enable=1
|
||||||
|
|
||||||
|
; Path to a .NET DLL that should run before the game. Useful for loading
|
||||||
|
; modding frameworks such as BepInEx.
|
||||||
|
targetAssembly=
|
||||||
|
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; LED settings
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[led15093]
|
||||||
|
; Enable emulation of the 15093-06 controlled lights, which handle the air tower
|
||||||
|
; RGBs and the rear LED panel (billboard) on the cabinet.
|
||||||
|
enable=1
|
||||||
|
|
||||||
|
[led]
|
||||||
|
; Output billboard LED strip data to a named pipe called "\\.\pipe\ongeki_led"
|
||||||
|
cabLedOutputPipe=1
|
||||||
|
; Output billboard LED strip data to serial
|
||||||
|
cabLedOutputSerial=0
|
||||||
|
|
||||||
|
; Output slider LED data to the named pipe
|
||||||
|
controllerLedOutputPipe=1
|
||||||
|
; Output slider LED data to the serial port
|
||||||
|
controllerLedOutputSerial=0
|
||||||
|
|
||||||
|
; Serial port to send data to if using serial output. Default is COM5.
|
||||||
|
;serialPort=COM5
|
||||||
|
; Baud rate for serial data
|
||||||
|
;serialBaud=921600
|
||||||
|
|
||||||
|
; Data output a sequence of bytes, with JVS-like framing.
|
||||||
|
; Each "packet" starts with 0xE0 as a sync. To avoid E0 appearing elsewhere,
|
||||||
|
; 0xD0 is used as an escape character -- if you receive D0 in the output, ignore
|
||||||
|
; it and use the next sent byte plus one instead.
|
||||||
|
;
|
||||||
|
; After the sync is one byte for the board number that was updated, followed by
|
||||||
|
; the red, green and blue values for each LED.
|
||||||
|
;
|
||||||
|
; Board 0 has 61 LEDs:
|
||||||
|
; [0]-[1]: left side button
|
||||||
|
; [2]-[8]: left pillar lower LEDs
|
||||||
|
; [9]-[17]: left pillar center LEDs
|
||||||
|
; [18]-[24]: left pillar upper LEDs
|
||||||
|
; [25]-[35]: billboard LEDs
|
||||||
|
; [36]-[42]: right pillar upper LEDs
|
||||||
|
; [43]-[51]: right pillar center LEDs
|
||||||
|
; [52]-[58]: right pillar lower LEDs
|
||||||
|
; [59]-[60]: right side button
|
||||||
|
;
|
||||||
|
; Board 1 has 6 LEDs:
|
||||||
|
; [0]-[5]: 3 left and 3 right controller buttons
|
||||||
|
;
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
; Custom IO settings
|
; Custom IO settings
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
|
@ -81,7 +137,7 @@ enable=1
|
||||||
; Leave empty if you want to use Segatools built-in keyboard input.
|
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||||
path=
|
path=
|
||||||
|
|
||||||
[fgoio]
|
[mu3io]
|
||||||
; To use a custom O.N.G.E.K.I. IO DLL enter its path here.
|
; To use a custom O.N.G.E.K.I. IO DLL enter its path here.
|
||||||
; Leave empty if you want to use Segatools built-in keyboard/gamepad input.
|
; Leave empty if you want to use Segatools built-in keyboard/gamepad input.
|
||||||
path=
|
path=
|
||||||
|
|
|
@ -153,6 +153,13 @@ setting. Also, loopback addresses are specifically checked for and rejected by
|
||||||
the games themselves; this needs to be a LAN or WAN IP (or a hostname that
|
the games themselves; this needs to be a LAN or WAN IP (or a hostname that
|
||||||
resolves to one).
|
resolves to one).
|
||||||
|
|
||||||
|
### `title`
|
||||||
|
|
||||||
|
Default: `title`
|
||||||
|
|
||||||
|
Leave it as `title` to use the title server returned by ALL.Net. Rewrites
|
||||||
|
the title server hostname for certain games, such as crossbeats REV.
|
||||||
|
|
||||||
### `router`
|
### `router`
|
||||||
|
|
||||||
Default: Empty string (i.e. use value from `default` setting)
|
Default: Empty string (i.e. use value from `default` setting)
|
||||||
|
@ -388,13 +395,29 @@ Bit values are:
|
||||||
- 3: EXP: Export (for Asian markets)
|
- 3: EXP: Export (for Asian markets)
|
||||||
- 4: CHS: China (Simplified Chinese?)
|
- 4: CHS: China (Simplified Chinese?)
|
||||||
|
|
||||||
|
### `billingCa`
|
||||||
|
|
||||||
|
Default: `DEVICE\\ca.crt`
|
||||||
|
|
||||||
|
Set the billing certificate path. This has to match the one used for the
|
||||||
|
SSL billing server. The DER certificate must fit in 1024 bytes so it must be
|
||||||
|
small.
|
||||||
|
|
||||||
|
### `billingPub`
|
||||||
|
|
||||||
|
Default: `DEVICE\\billing.pub`
|
||||||
|
|
||||||
|
Set the actual keychip RSA public key path. This public key has to match the
|
||||||
|
private key `billing.key` of the billing server in order to decrypt/encrypt
|
||||||
|
the billing transactions.
|
||||||
|
|
||||||
### `billingType`
|
### `billingType`
|
||||||
|
|
||||||
Default: `1`
|
Default: `1`
|
||||||
|
|
||||||
Set the billing "type" for the keychip. The type determins what kind of revenue share,
|
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
|
if any, the game maker has with SEGA. Some games may be picky and require types other
|
||||||
then 1 (ex. Crossbeats requires billing type 2), so this option is provided if this
|
then 1 (ex. crossbeats REV. requires billing type 2), so this option is provided if this
|
||||||
is an issue. Billing types are:
|
is an issue. Billing types are:
|
||||||
|
|
||||||
- 0: No billing?
|
- 0: No billing?
|
||||||
|
|
|
@ -28,7 +28,7 @@ const struct dll_bind_sym fgo_dll_syms[] = {
|
||||||
.sym = "fgo_io_led_init",
|
.sym = "fgo_io_led_init",
|
||||||
.off = offsetof(struct fgo_dll, led_init),
|
.off = offsetof(struct fgo_dll, led_init),
|
||||||
}, {
|
}, {
|
||||||
.sym = "fgo_io_led_set_leds",
|
.sym = "fgo_io_led_set_colors",
|
||||||
.off = offsetof(struct fgo_dll, led_set_leds),
|
.off = offsetof(struct fgo_dll, led_set_leds),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@ EXPORTS
|
||||||
fgo_io_init
|
fgo_io_init
|
||||||
fgo_io_poll
|
fgo_io_poll
|
||||||
fgo_io_led_init
|
fgo_io_led_init
|
||||||
fgo_io_led_set_leds
|
fgo_io_led_set_colors
|
||||||
fwdlusb_open
|
fwdlusb_open
|
||||||
fwdlusb_close
|
fwdlusb_close
|
||||||
fwdlusb_listupPrinter
|
fwdlusb_listupPrinter
|
||||||
|
|
|
@ -145,7 +145,7 @@ HRESULT fgo_io_led_init(void)
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fgo_io_led_set_leds(uint8_t board, uint8_t *rgb)
|
void fgo_io_led_set_colors(uint8_t board, uint8_t *rgb)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,4 +83,4 @@ HRESULT fgo_io_led_init(void);
|
||||||
|
|
||||||
Exact layout is TBD. */
|
Exact layout is TBD. */
|
||||||
|
|
||||||
void fgo_io_led_set_leds(uint8_t board, uint8_t *rgb);
|
void fgo_io_led_set_colors(uint8_t board, uint8_t *rgb);
|
||||||
|
|
108
hooklib/dns.c
108
hooklib/dns.c
|
@ -13,6 +13,8 @@
|
||||||
#include "hook/hr.h"
|
#include "hook/hr.h"
|
||||||
#include "hook/table.h"
|
#include "hook/table.h"
|
||||||
|
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
#include "hooklib/dns.h"
|
#include "hooklib/dns.h"
|
||||||
|
|
||||||
/* Latest w32headers does not include DnsQueryEx, so we'll have to "polyfill"
|
/* Latest w32headers does not include DnsQueryEx, so we'll have to "polyfill"
|
||||||
|
@ -66,13 +68,19 @@ static int WSAAPI hook_getaddrinfo(
|
||||||
const char *pServiceName,
|
const char *pServiceName,
|
||||||
const ADDRINFOA *pHints,
|
const ADDRINFOA *pHints,
|
||||||
ADDRINFOA **ppResult);
|
ADDRINFOA **ppResult);
|
||||||
|
|
||||||
static HINTERNET WINAPI hook_WinHttpConnect(
|
static HINTERNET WINAPI hook_WinHttpConnect(
|
||||||
HINTERNET hSession,
|
HINTERNET hSession,
|
||||||
const wchar_t *pwszServerName,
|
const wchar_t *pwszServerName,
|
||||||
INTERNET_PORT nServerPort,
|
INTERNET_PORT nServerPort,
|
||||||
DWORD dwReserved);
|
DWORD dwReserved);
|
||||||
|
|
||||||
|
static bool WINAPI hook_WinHttpCrackUrl(
|
||||||
|
const wchar_t *pwszUrl,
|
||||||
|
DWORD dwUrlLength,
|
||||||
|
DWORD dwFlags,
|
||||||
|
LPURL_COMPONENTS lpUrlComponents);
|
||||||
|
|
||||||
/* Link pointers */
|
/* Link pointers */
|
||||||
|
|
||||||
static DNS_STATUS (WINAPI *next_DnsQuery_A)(
|
static DNS_STATUS (WINAPI *next_DnsQuery_A)(
|
||||||
|
@ -108,6 +116,12 @@ static HINTERNET (WINAPI *next_WinHttpConnect)(
|
||||||
INTERNET_PORT nServerPort,
|
INTERNET_PORT nServerPort,
|
||||||
DWORD dwReserved);
|
DWORD dwReserved);
|
||||||
|
|
||||||
|
static bool (WINAPI *next_WinHttpCrackUrl)(
|
||||||
|
const wchar_t *pwszUrl,
|
||||||
|
DWORD dwUrlLength,
|
||||||
|
DWORD dwFlags,
|
||||||
|
LPURL_COMPONENTS lpUrlComponents);
|
||||||
|
|
||||||
static const struct hook_symbol dns_hook_syms_dnsapi[] = {
|
static const struct hook_symbol dns_hook_syms_dnsapi[] = {
|
||||||
{
|
{
|
||||||
.name = "DnsQuery_A",
|
.name = "DnsQuery_A",
|
||||||
|
@ -138,13 +152,19 @@ static const struct hook_symbol dns_hook_syms_winhttp[] = {
|
||||||
.name = "WinHttpConnect",
|
.name = "WinHttpConnect",
|
||||||
.patch = hook_WinHttpConnect,
|
.patch = hook_WinHttpConnect,
|
||||||
.link = (void **) &next_WinHttpConnect,
|
.link = (void **) &next_WinHttpConnect,
|
||||||
|
}, {
|
||||||
|
.name = "WinHttpCrackUrl",
|
||||||
|
.patch = hook_WinHttpCrackUrl,
|
||||||
|
.link = (void **) &next_WinHttpCrackUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool dns_hook_initted;
|
static bool dns_hook_initted;
|
||||||
static CRITICAL_SECTION dns_hook_lock;
|
static CRITICAL_SECTION dns_hook_lock;
|
||||||
static struct dns_hook_entry *dns_hook_entries;
|
static struct dns_hook_entry *dns_hook_entries;
|
||||||
static size_t dns_hook_nentries;
|
static size_t dns_hook_nentries;
|
||||||
|
static char received_title_url[255];
|
||||||
|
|
||||||
static void dns_hook_init(void)
|
static void dns_hook_init(void)
|
||||||
{
|
{
|
||||||
|
@ -174,6 +194,37 @@ static void dns_hook_init(void)
|
||||||
_countof(dns_hook_syms_winhttp));
|
_countof(dns_hook_syms_winhttp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function match domain and subdomains like *.naominet.jp.
|
||||||
|
bool match_domain(const wchar_t* target, const wchar_t* pattern) {
|
||||||
|
if (_wcsicmp(pattern, target) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pattern_ptr_index = 0;
|
||||||
|
int target_ptr_index = 0;
|
||||||
|
|
||||||
|
while (pattern[pattern_ptr_index] != '\0' && target[target_ptr_index] != '\0') {
|
||||||
|
if (pattern[pattern_ptr_index] == '*') {
|
||||||
|
pattern_ptr_index++; // Check next character for wildcard match.
|
||||||
|
|
||||||
|
while (pattern[pattern_ptr_index] != target[target_ptr_index]) {
|
||||||
|
target_ptr_index++;
|
||||||
|
|
||||||
|
if (target[target_ptr_index] == '\0') return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pattern[pattern_ptr_index] != target[target_ptr_index]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pattern_ptr_index++;
|
||||||
|
target_ptr_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pattern[pattern_ptr_index] == '\0' && target[target_ptr_index] == '\0';
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src)
|
HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
@ -277,7 +328,7 @@ static DNS_STATUS WINAPI hook_DnsQuery_A(
|
||||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||||
pos = &dns_hook_entries[i];
|
pos = &dns_hook_entries[i];
|
||||||
|
|
||||||
if (_wcsicmp(wstr, pos->from) == 0) {
|
if (match_domain(wstr, pos->from)) {
|
||||||
if(pos->to == NULL) {
|
if(pos->to == NULL) {
|
||||||
LeaveCriticalSection(&dns_hook_lock);
|
LeaveCriticalSection(&dns_hook_lock);
|
||||||
hr = HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
hr = HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
||||||
|
@ -341,7 +392,7 @@ static DNS_STATUS WINAPI hook_DnsQuery_W(
|
||||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||||
pos = &dns_hook_entries[i];
|
pos = &dns_hook_entries[i];
|
||||||
|
|
||||||
if (_wcsicmp(pszName, pos->from) == 0) {
|
if (match_domain(pszName, pos->from)) {
|
||||||
if(pos->to == NULL) {
|
if(pos->to == NULL) {
|
||||||
LeaveCriticalSection(&dns_hook_lock);
|
LeaveCriticalSection(&dns_hook_lock);
|
||||||
return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
||||||
|
@ -385,7 +436,7 @@ static DNS_STATUS WINAPI hook_DnsQueryEx(
|
||||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||||
pos = &dns_hook_entries[i];
|
pos = &dns_hook_entries[i];
|
||||||
|
|
||||||
if (_wcsicmp(pRequest->QueryName, pos->from) == 0) {
|
if (match_domain(pRequest->QueryName, pos->from)) {
|
||||||
if(pos->to == NULL) {
|
if(pos->to == NULL) {
|
||||||
LeaveCriticalSection(&dns_hook_lock);
|
LeaveCriticalSection(&dns_hook_lock);
|
||||||
return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR);
|
||||||
|
@ -452,7 +503,7 @@ static int WSAAPI hook_getaddrinfo(
|
||||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||||
pos = &dns_hook_entries[i];
|
pos = &dns_hook_entries[i];
|
||||||
|
|
||||||
if (_wcsicmp(wstr, pos->from) == 0) {
|
if (match_domain(wstr, pos->from)) {
|
||||||
if(pos->to == NULL) {
|
if(pos->to == NULL) {
|
||||||
LeaveCriticalSection(&dns_hook_lock);
|
LeaveCriticalSection(&dns_hook_lock);
|
||||||
result = EAI_NONAME;
|
result = EAI_NONAME;
|
||||||
|
@ -506,7 +557,7 @@ static HINTERNET WINAPI hook_WinHttpConnect(
|
||||||
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||||
pos = &dns_hook_entries[i];
|
pos = &dns_hook_entries[i];
|
||||||
|
|
||||||
if (_wcsicmp(pwszServerName, pos->from) == 0) {
|
if (match_domain(pwszServerName, pos->from)) {
|
||||||
if(pos->to == NULL) {
|
if(pos->to == NULL) {
|
||||||
LeaveCriticalSection(&dns_hook_lock);
|
LeaveCriticalSection(&dns_hook_lock);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -522,3 +573,48 @@ static HINTERNET WINAPI hook_WinHttpConnect(
|
||||||
|
|
||||||
return next_WinHttpConnect(hSession, pwszServerName, nServerPort, dwReserved);
|
return next_WinHttpConnect(hSession, pwszServerName, nServerPort, dwReserved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hook to replace CXB title url
|
||||||
|
static bool WINAPI hook_WinHttpCrackUrl(
|
||||||
|
const wchar_t *pwszUrl,
|
||||||
|
DWORD dwUrlLength,
|
||||||
|
DWORD dwFlags,
|
||||||
|
LPURL_COMPONENTS lpUrlComponents)
|
||||||
|
{
|
||||||
|
const struct dns_hook_entry *pos;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
EnterCriticalSection(&dns_hook_lock);
|
||||||
|
|
||||||
|
for (i = 0 ; i < dns_hook_nentries ; i++) {
|
||||||
|
pos = &dns_hook_entries[i];
|
||||||
|
|
||||||
|
if (match_domain(pwszUrl, pos->from)) {
|
||||||
|
wchar_t* toAddr = pos->to;
|
||||||
|
wchar_t titleBuffer[255];
|
||||||
|
|
||||||
|
if(wcscmp(toAddr, L"title") == 0) {
|
||||||
|
size_t wstr_c;
|
||||||
|
mbstowcs_s(&wstr_c, titleBuffer, 255, received_title_url, strlen(received_title_url));
|
||||||
|
toAddr = titleBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = next_WinHttpCrackUrl(
|
||||||
|
toAddr,
|
||||||
|
wcslen(toAddr),
|
||||||
|
dwFlags,
|
||||||
|
lpUrlComponents
|
||||||
|
);
|
||||||
|
LeaveCriticalSection(&dns_hook_lock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LeaveCriticalSection(&dns_hook_lock);
|
||||||
|
return next_WinHttpCrackUrl(
|
||||||
|
pwszUrl,
|
||||||
|
dwUrlLength,
|
||||||
|
dwFlags,
|
||||||
|
lpUrlComponents
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -23,8 +23,6 @@ hooklib_lib = static_library(
|
||||||
'fdshark.h',
|
'fdshark.h',
|
||||||
'path.c',
|
'path.c',
|
||||||
'path.h',
|
'path.h',
|
||||||
'procaddr.c',
|
|
||||||
'procaddr.h',
|
|
||||||
'reg.c',
|
'reg.c',
|
||||||
'reg.h',
|
'reg.h',
|
||||||
'setupapi.c',
|
'setupapi.c',
|
||||||
|
|
|
@ -1,125 +0,0 @@
|
||||||
#include <windows.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
|
|
||||||
#include "hooklib/procaddr.h"
|
|
||||||
|
|
||||||
#include "hook/table.h"
|
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
|
||||||
|
|
||||||
static struct proc_addr_table *proc_addr_hook_list;
|
|
||||||
static size_t proc_addr_hook_count;
|
|
||||||
static CRITICAL_SECTION proc_addr_hook_lock;
|
|
||||||
static bool proc_addr_hook_initted;
|
|
||||||
|
|
||||||
static FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name);
|
|
||||||
static FARPROC (WINAPI *next_GetProcAddress)(HMODULE hModule, const char *name);
|
|
||||||
static void proc_addr_hook_init(void);
|
|
||||||
|
|
||||||
static const struct hook_symbol win32_hooks[] = {
|
|
||||||
{
|
|
||||||
.name = "GetProcAddress",
|
|
||||||
.patch = my_GetProcAddress,
|
|
||||||
.link = (void **) &next_GetProcAddress
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
HRESULT proc_addr_table_push(
|
|
||||||
const char *target,
|
|
||||||
struct hook_symbol *syms,
|
|
||||||
size_t nsyms
|
|
||||||
)
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
struct proc_addr_table *new_item;
|
|
||||||
struct proc_addr_table *new_mem;
|
|
||||||
|
|
||||||
proc_addr_hook_init();
|
|
||||||
|
|
||||||
EnterCriticalSection(&proc_addr_hook_lock);
|
|
||||||
|
|
||||||
new_mem = realloc(
|
|
||||||
proc_addr_hook_list,
|
|
||||||
(proc_addr_hook_count + 1) * sizeof(struct proc_addr_table));
|
|
||||||
|
|
||||||
if (new_mem == NULL) {
|
|
||||||
hr = E_OUTOFMEMORY;
|
|
||||||
|
|
||||||
LeaveCriticalSection(&proc_addr_hook_lock);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_item = &new_mem[proc_addr_hook_count];
|
|
||||||
new_item->name = target;
|
|
||||||
new_item->nsyms = nsyms;
|
|
||||||
new_item->syms = syms;
|
|
||||||
|
|
||||||
proc_addr_hook_list = new_mem;
|
|
||||||
proc_addr_hook_count++;
|
|
||||||
hr = S_OK;
|
|
||||||
|
|
||||||
LeaveCriticalSection(&proc_addr_hook_lock);
|
|
||||||
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void proc_addr_hook_init(void)
|
|
||||||
{
|
|
||||||
if (proc_addr_hook_initted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dprintf("ProcAddr: Hook init\n");
|
|
||||||
proc_addr_hook_initted = true;
|
|
||||||
|
|
||||||
InitializeCriticalSection(&proc_addr_hook_lock);
|
|
||||||
|
|
||||||
hook_table_apply(
|
|
||||||
NULL,
|
|
||||||
"kernel32.dll",
|
|
||||||
win32_hooks,
|
|
||||||
_countof(win32_hooks));
|
|
||||||
}
|
|
||||||
|
|
||||||
FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name)
|
|
||||||
{
|
|
||||||
uintptr_t ordinal = (uintptr_t) name;
|
|
||||||
char mod_path[PATH_MAX];
|
|
||||||
char *mod_name;
|
|
||||||
const struct hook_symbol *sym;
|
|
||||||
FARPROC result = next_GetProcAddress(hModule, name);
|
|
||||||
|
|
||||||
GetModuleFileNameA(hModule, mod_path, PATH_MAX);
|
|
||||||
mod_name = basename(mod_path);
|
|
||||||
|
|
||||||
for (int i = 0; i < proc_addr_hook_count; i++) {
|
|
||||||
|
|
||||||
if (strcmp(proc_addr_hook_list[i].name, mod_name) == 0) {
|
|
||||||
|
|
||||||
for (int j = 0; j < proc_addr_hook_list[i].nsyms; j++) {
|
|
||||||
sym = &proc_addr_hook_list[i].syms[j];
|
|
||||||
|
|
||||||
if (ordinal > 0xFFFF) {
|
|
||||||
|
|
||||||
if (strcmp(sym->name, name) == 0) {
|
|
||||||
|
|
||||||
dprintf("ProcAddr: Hooking %s from %s\n", name, mod_name);
|
|
||||||
result = (FARPROC) sym->patch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
if (sym->ordinal == ordinal) {
|
|
||||||
|
|
||||||
dprintf("ProcAddr: Hooking Ord %p from %s\n", (void *)ordinal, mod_name);
|
|
||||||
result = (FARPROC) sym->patch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <windows.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "hook/table.h"
|
|
||||||
|
|
||||||
struct proc_addr_table {
|
|
||||||
const char *name;
|
|
||||||
size_t nsyms;
|
|
||||||
struct hook_symbol *syms;
|
|
||||||
};
|
|
||||||
|
|
||||||
HRESULT proc_addr_table_push(
|
|
||||||
const char *target,
|
|
||||||
struct hook_symbol *syms,
|
|
||||||
size_t nsyms
|
|
||||||
);
|
|
165
hooklib/reg.c
165
hooklib/reg.c
|
@ -7,6 +7,7 @@
|
||||||
#include "hook/table.h"
|
#include "hook/table.h"
|
||||||
|
|
||||||
#include "hooklib/reg.h"
|
#include "hooklib/reg.h"
|
||||||
|
#include "hook/procaddr.h"
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
#include "util/dprintf.h"
|
||||||
#include "util/str.h"
|
#include "util/str.h"
|
||||||
|
@ -99,6 +100,29 @@ static LSTATUS WINAPI hook_RegGetValueW(
|
||||||
uint32_t *numData
|
uint32_t *numData
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static LSTATUS WINAPI hook_RegQueryInfoKeyW(
|
||||||
|
HKEY hKey,
|
||||||
|
LPWSTR lpClass,
|
||||||
|
LPDWORD lpcchClass,
|
||||||
|
LPDWORD lpReserved,
|
||||||
|
LPDWORD lpcSubKeys,
|
||||||
|
LPDWORD lpcbMaxSubKeyLen,
|
||||||
|
LPDWORD lpcbMaxClassLen,
|
||||||
|
LPDWORD lpcValues,
|
||||||
|
LPDWORD lpcbMaxValueNameLen,
|
||||||
|
LPDWORD lpcbMaxValueLen,
|
||||||
|
LPDWORD lpcbSecurityDescriptor,
|
||||||
|
PFILETIME lpftLastWriteTime);
|
||||||
|
|
||||||
|
static LSTATUS WINAPI hook_RegEnumValueW(
|
||||||
|
HKEY hkey,
|
||||||
|
DWORD dwIndex,
|
||||||
|
LPWSTR lpValueName,
|
||||||
|
LPDWORD lpcchValueName,
|
||||||
|
LPDWORD lpReserved,
|
||||||
|
LPDWORD lpType,
|
||||||
|
LPBYTE lpData,
|
||||||
|
LPDWORD lpcbData);
|
||||||
/* Link pointers */
|
/* Link pointers */
|
||||||
|
|
||||||
static LSTATUS (WINAPI *next_RegOpenKeyExW)(
|
static LSTATUS (WINAPI *next_RegOpenKeyExW)(
|
||||||
|
@ -155,6 +179,30 @@ static LSTATUS (WINAPI *next_RegGetValueW)(
|
||||||
uint32_t *numData
|
uint32_t *numData
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static LSTATUS (WINAPI *next_RegQueryInfoKeyW)(
|
||||||
|
HKEY hKey,
|
||||||
|
LPWSTR lpClass,
|
||||||
|
LPDWORD lpcchClass,
|
||||||
|
LPDWORD lpReserved,
|
||||||
|
LPDWORD lpcSubKeys,
|
||||||
|
LPDWORD lpcbMaxSubKeyLen,
|
||||||
|
LPDWORD lpcbMaxClassLen,
|
||||||
|
LPDWORD lpcValues,
|
||||||
|
LPDWORD lpcbMaxValueNameLen,
|
||||||
|
LPDWORD lpcbMaxValueLen,
|
||||||
|
LPDWORD lpcbSecurityDescriptor,
|
||||||
|
PFILETIME lpftLastWriteTime);
|
||||||
|
|
||||||
|
static LSTATUS (WINAPI *next_RegEnumValueW)(
|
||||||
|
HKEY hkey,
|
||||||
|
DWORD dwIndex,
|
||||||
|
LPWSTR lpValueName,
|
||||||
|
LPDWORD lpcchValueName,
|
||||||
|
LPDWORD lpReserved,
|
||||||
|
LPDWORD lpType,
|
||||||
|
LPBYTE lpData,
|
||||||
|
LPDWORD lpcbData);
|
||||||
|
|
||||||
static const struct hook_symbol reg_hook_syms[] = {
|
static const struct hook_symbol reg_hook_syms[] = {
|
||||||
{
|
{
|
||||||
.name = "RegOpenKeyExW",
|
.name = "RegOpenKeyExW",
|
||||||
|
@ -184,6 +232,14 @@ static const struct hook_symbol reg_hook_syms[] = {
|
||||||
.name = "RegGetValueW",
|
.name = "RegGetValueW",
|
||||||
.patch = hook_RegGetValueW,
|
.patch = hook_RegGetValueW,
|
||||||
.link = (void **) &next_RegGetValueW,
|
.link = (void **) &next_RegGetValueW,
|
||||||
|
}, {
|
||||||
|
.name = "RegQueryInfoKeyW",
|
||||||
|
.patch = hook_RegQueryInfoKeyW,
|
||||||
|
.link = (void **) &next_RegQueryInfoKeyW,
|
||||||
|
}, {
|
||||||
|
.name = "RegEnumValueW",
|
||||||
|
.patch = hook_RegEnumValueW,
|
||||||
|
.link = (void **) &next_RegEnumValueW,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -254,11 +310,24 @@ static void reg_hook_init(void)
|
||||||
InitializeCriticalSection(®_hook_lock);
|
InitializeCriticalSection(®_hook_lock);
|
||||||
dprintf("Reg hook init\n");
|
dprintf("Reg hook init\n");
|
||||||
|
|
||||||
|
reg_hook_insert_hooks(NULL);
|
||||||
|
|
||||||
|
proc_addr_table_push(
|
||||||
|
NULL,
|
||||||
|
"ADVAPI32.dll",
|
||||||
|
(struct hook_symbol *) reg_hook_syms,
|
||||||
|
_countof(reg_hook_syms));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void reg_hook_insert_hooks(HMODULE target)
|
||||||
|
{
|
||||||
hook_table_apply(
|
hook_table_apply(
|
||||||
NULL,
|
target,
|
||||||
"advapi32.dll",
|
"advapi32.dll",
|
||||||
reg_hook_syms,
|
reg_hook_syms,
|
||||||
_countof(reg_hook_syms));
|
_countof(reg_hook_syms));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static LRESULT reg_hook_propagate_hr(HRESULT hr)
|
static LRESULT reg_hook_propagate_hr(HRESULT hr)
|
||||||
|
@ -331,6 +400,7 @@ static LSTATUS reg_hook_open_locked(
|
||||||
/* Assume reg keys are referenced from a root key and not from some
|
/* Assume reg keys are referenced from a root key and not from some
|
||||||
intermediary key */
|
intermediary key */
|
||||||
key = ®_hook_keys[i];
|
key = ®_hook_keys[i];
|
||||||
|
//dprintf("Reg: %ls vs %ls\n", name, key->name);
|
||||||
|
|
||||||
if (key->root == parent && wstr_ieq(key->name, name)) {
|
if (key->root == parent && wstr_ieq(key->name, name)) {
|
||||||
break;
|
break;
|
||||||
|
@ -821,6 +891,99 @@ static LSTATUS WINAPI hook_RegGetValueW(
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LSTATUS WINAPI hook_RegQueryInfoKeyW(
|
||||||
|
HKEY hKey,
|
||||||
|
LPWSTR lpClass,
|
||||||
|
LPDWORD lpcchClass,
|
||||||
|
LPDWORD lpReserved,
|
||||||
|
LPDWORD lpcSubKeys,
|
||||||
|
LPDWORD lpcbMaxSubKeyLen,
|
||||||
|
LPDWORD lpcbMaxClassLen,
|
||||||
|
LPDWORD lpcValues,
|
||||||
|
LPDWORD lpcbMaxValueNameLen,
|
||||||
|
LPDWORD lpcbMaxValueLen,
|
||||||
|
LPDWORD lpcbSecurityDescriptor,
|
||||||
|
PFILETIME lpftLastWriteTime)
|
||||||
|
{
|
||||||
|
struct reg_hook_key *key;
|
||||||
|
LSTATUS err;
|
||||||
|
|
||||||
|
EnterCriticalSection(®_hook_lock);
|
||||||
|
|
||||||
|
key = reg_hook_match_key_locked(hKey);
|
||||||
|
|
||||||
|
/* Check if this is a virtualized registry key */
|
||||||
|
|
||||||
|
if (key == NULL) {
|
||||||
|
LeaveCriticalSection(®_hook_lock);
|
||||||
|
|
||||||
|
return next_RegQueryInfoKeyW(
|
||||||
|
hKey,
|
||||||
|
lpClass,
|
||||||
|
lpcchClass,
|
||||||
|
lpReserved,
|
||||||
|
lpcSubKeys,
|
||||||
|
lpcbMaxSubKeyLen,
|
||||||
|
lpcbMaxClassLen,
|
||||||
|
lpcValues,
|
||||||
|
lpcbMaxValueNameLen,
|
||||||
|
lpcbMaxValueLen,
|
||||||
|
lpcbSecurityDescriptor,
|
||||||
|
lpftLastWriteTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the only one I've seen even be changed, so it's all I'm doing
|
||||||
|
// until I see otherwise.
|
||||||
|
*lpcValues = key->nvals;
|
||||||
|
LeaveCriticalSection(®_hook_lock);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LSTATUS WINAPI hook_RegEnumValueW(
|
||||||
|
HKEY hkey,
|
||||||
|
DWORD dwIndex,
|
||||||
|
LPWSTR lpValueName,
|
||||||
|
LPDWORD lpcchValueName,
|
||||||
|
LPDWORD lpReserved,
|
||||||
|
LPDWORD lpType,
|
||||||
|
LPBYTE lpData,
|
||||||
|
LPDWORD lpcbData)
|
||||||
|
{
|
||||||
|
struct reg_hook_key *key;
|
||||||
|
HRESULT hr;
|
||||||
|
LSTATUS err;
|
||||||
|
|
||||||
|
EnterCriticalSection(®_hook_lock);
|
||||||
|
|
||||||
|
key = reg_hook_match_key_locked(hkey);
|
||||||
|
|
||||||
|
/* Check if this is a virtualized registry key */
|
||||||
|
|
||||||
|
if (key == NULL) {
|
||||||
|
LeaveCriticalSection(®_hook_lock);
|
||||||
|
|
||||||
|
return next_RegEnumValueW(
|
||||||
|
hkey,
|
||||||
|
dwIndex,
|
||||||
|
lpValueName,
|
||||||
|
lpcchValueName,
|
||||||
|
lpReserved,
|
||||||
|
lpType,
|
||||||
|
lpData,
|
||||||
|
lpcbData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwIndex >= key->nvals) {
|
||||||
|
LeaveCriticalSection(®_hook_lock);
|
||||||
|
return ERROR_NO_MORE_ITEMS; // Pretty sure this is what it actually returns here?
|
||||||
|
}
|
||||||
|
|
||||||
|
wcscpy_s(lpValueName, *lpcchValueName, key->vals[dwIndex].name);
|
||||||
|
*lpcchValueName = wcslen(key->vals[dwIndex].name);
|
||||||
|
LeaveCriticalSection(®_hook_lock);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT reg_hook_read_bin(
|
HRESULT reg_hook_read_bin(
|
||||||
void *bytes,
|
void *bytes,
|
||||||
uint32_t *nbytes,
|
uint32_t *nbytes,
|
||||||
|
|
|
@ -12,6 +12,8 @@ struct reg_hook_val {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void reg_hook_insert_hooks(HMODULE target);
|
||||||
|
|
||||||
HRESULT reg_hook_push_key(
|
HRESULT reg_hook_push_key(
|
||||||
HKEY root,
|
HKEY root,
|
||||||
const wchar_t *name,
|
const wchar_t *name,
|
||||||
|
|
|
@ -119,10 +119,19 @@ void touch_screen_hook_init(const struct touch_screen_config *cfg, HINSTANCE sel
|
||||||
defaultCursor = LoadCursorA(NULL, IDC_CROSS);
|
defaultCursor = LoadCursorA(NULL, IDC_CROSS);
|
||||||
|
|
||||||
memcpy(&touch_config, cfg, sizeof(*cfg));
|
memcpy(&touch_config, cfg, sizeof(*cfg));
|
||||||
hook_table_apply(NULL, "user32.dll", touch_hooks, _countof(touch_hooks));
|
touch_hook_insert_hooks(NULL);
|
||||||
dprintf("TOUCH: hook enabled.\n");
|
dprintf("TOUCH: hook enabled.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void touch_hook_insert_hooks(HMODULE target)
|
||||||
|
{
|
||||||
|
hook_table_apply(
|
||||||
|
target,
|
||||||
|
"user32.dll",
|
||||||
|
touch_hooks,
|
||||||
|
_countof(touch_hooks));
|
||||||
|
}
|
||||||
|
|
||||||
static HCURSOR WINAPI hook_SetCursor(HCURSOR cursor) {
|
static HCURSOR WINAPI hook_SetCursor(HCURSOR cursor) {
|
||||||
if (cursor == 0 && touch_config.cursor)
|
if (cursor == 0 && touch_config.cursor)
|
||||||
return next_SetCursor(defaultCursor);
|
return next_SetCursor(defaultCursor);
|
||||||
|
|
|
@ -14,3 +14,4 @@ struct touch_screen_config {
|
||||||
blah blah you know the drill by now. */
|
blah blah you know the drill by now. */
|
||||||
|
|
||||||
void touch_screen_hook_init(const struct touch_screen_config *cfg, HINSTANCE self);
|
void touch_screen_hook_init(const struct touch_screen_config *cfg, HINSTANCE self);
|
||||||
|
void touch_hook_insert_hooks(HMODULE target);
|
||||||
|
|
|
@ -13,7 +13,6 @@ EXPORTS
|
||||||
amDllVideoSetResolution @3
|
amDllVideoSetResolution @3
|
||||||
idac_io_get_api_version
|
idac_io_get_api_version
|
||||||
idac_io_init
|
idac_io_init
|
||||||
idac_io_poll
|
|
||||||
idac_io_get_opbtns
|
idac_io_get_opbtns
|
||||||
idac_io_get_gamebtns
|
idac_io_get_gamebtns
|
||||||
idac_io_get_shifter
|
idac_io_get_shifter
|
||||||
|
|
|
@ -52,10 +52,6 @@ HRESULT idac_xi_init(const struct idac_xi_config *cfg, const struct idac_io_back
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT idac_io_poll(void) {
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HRESULT idac_xi_config_apply(const struct idac_xi_config *cfg) {
|
static HRESULT idac_xi_config_apply(const struct idac_xi_config *cfg) {
|
||||||
/* Deadzones check */
|
/* Deadzones check */
|
||||||
if (cfg->left_stick_deadzone > 32767 || cfg->left_stick_deadzone < 0) {
|
if (cfg->left_stick_deadzone > 32767 || cfg->left_stick_deadzone < 0) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ enum {
|
||||||
JVS_CMD_READ_SWITCHES = 0x20,
|
JVS_CMD_READ_SWITCHES = 0x20,
|
||||||
JVS_CMD_READ_COIN = 0x21,
|
JVS_CMD_READ_COIN = 0x21,
|
||||||
JVS_CMD_READ_ANALOGS = 0x22,
|
JVS_CMD_READ_ANALOGS = 0x22,
|
||||||
|
JVS_CMD_READ_ROTARYS = 0x23,
|
||||||
JVS_CMD_WRITE_GPIO = 0x32,
|
JVS_CMD_WRITE_GPIO = 0x32,
|
||||||
JVS_CMD_RESET = 0xF0,
|
JVS_CMD_RESET = 0xF0,
|
||||||
JVS_CMD_ASSIGN_ADDR = 0xF1,
|
JVS_CMD_ASSIGN_ADDR = 0xF1,
|
||||||
|
@ -32,6 +33,11 @@ struct jvs_req_read_analogs {
|
||||||
uint8_t nanalogs;
|
uint8_t nanalogs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct jvs_req_read_rotarys {
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t nrotarys;
|
||||||
|
};
|
||||||
|
|
||||||
struct jvs_req_reset {
|
struct jvs_req_reset {
|
||||||
uint8_t cmd;
|
uint8_t cmd;
|
||||||
uint8_t unknown;
|
uint8_t unknown;
|
||||||
|
|
|
@ -39,4 +39,5 @@ void mai2_hook_config_load(
|
||||||
io4_config_load(&cfg->io4, filename);
|
io4_config_load(&cfg->io4, filename);
|
||||||
vfd_config_load(&cfg->vfd, filename);
|
vfd_config_load(&cfg->vfd, filename);
|
||||||
mai2_dll_config_load(&cfg->dll, filename);
|
mai2_dll_config_load(&cfg->dll, filename);
|
||||||
|
unity_config_load(&cfg->unity, filename);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
#include "platform/config.h"
|
#include "platform/config.h"
|
||||||
|
|
||||||
|
#include "unityhook/config.h"
|
||||||
|
|
||||||
struct mai2_hook_config {
|
struct mai2_hook_config {
|
||||||
struct platform_config platform;
|
struct platform_config platform;
|
||||||
struct aime_config aime;
|
struct aime_config aime;
|
||||||
|
@ -17,6 +19,7 @@ struct mai2_hook_config {
|
||||||
struct io4_config io4;
|
struct io4_config io4;
|
||||||
struct vfd_config vfd;
|
struct vfd_config vfd;
|
||||||
struct mai2_dll_config dll;
|
struct mai2_dll_config dll;
|
||||||
|
struct unity_config unity;
|
||||||
};
|
};
|
||||||
|
|
||||||
void mai2_dll_config_load(
|
void mai2_dll_config_load(
|
||||||
|
|
|
@ -12,10 +12,11 @@
|
||||||
#include "mai2hook/config.h"
|
#include "mai2hook/config.h"
|
||||||
#include "mai2hook/io4.h"
|
#include "mai2hook/io4.h"
|
||||||
#include "mai2hook/mai2-dll.h"
|
#include "mai2hook/mai2-dll.h"
|
||||||
#include "mai2hook/unity.h"
|
|
||||||
|
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
|
|
||||||
|
#include "unityhook/hook.h"
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
static HMODULE mai2_hook_mod;
|
static HMODULE mai2_hook_mod;
|
||||||
|
@ -80,7 +81,7 @@ static DWORD CALLBACK mai2_pre_startup(void)
|
||||||
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
|
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
|
||||||
hooked earlier in the `mai2hook` initialization. */
|
hooked earlier in the `mai2hook` initialization. */
|
||||||
|
|
||||||
unity_hook_init();
|
unity_hook_init(&mai2_hook_cfg.unity, mai2_hook_mod);
|
||||||
|
|
||||||
/* Initialize debug helpers */
|
/* Initialize debug helpers */
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ shared_library(
|
||||||
hooklib_lib,
|
hooklib_lib,
|
||||||
mai2io_lib,
|
mai2io_lib,
|
||||||
platform_lib,
|
platform_lib,
|
||||||
|
unityhook_lib,
|
||||||
util_lib,
|
util_lib,
|
||||||
],
|
],
|
||||||
sources : [
|
sources : [
|
||||||
|
@ -25,7 +26,5 @@ shared_library(
|
||||||
'io4.h',
|
'io4.h',
|
||||||
'mai2-dll.c',
|
'mai2-dll.c',
|
||||||
'mai2-dll.h',
|
'mai2-dll.h',
|
||||||
'unity.h',
|
|
||||||
'unity.c',
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
void unity_hook_init(void);
|
|
|
@ -14,6 +14,7 @@ add_project_arguments(
|
||||||
'-D_WIN32_WINNT=_WIN32_WINNT_WIN7',
|
'-D_WIN32_WINNT=_WIN32_WINNT_WIN7',
|
||||||
'-DMINGW_HAS_SECURE_API=1',
|
'-DMINGW_HAS_SECURE_API=1',
|
||||||
'-Wno-unused',
|
'-Wno-unused',
|
||||||
|
# '-ggdb', # Add debug information
|
||||||
language: 'c',
|
language: 'c',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,7 +24,6 @@ if cc.get_id() != 'msvc'
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
'-ffunction-sections',
|
'-ffunction-sections',
|
||||||
'-fdata-sections',
|
'-fdata-sections',
|
||||||
'-flto', # Enable Link-Time Optimization
|
|
||||||
language: 'c',
|
language: 'c',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,8 +32,9 @@ if cc.get_id() != 'msvc'
|
||||||
'-Wl,--exclude-all-symbols',
|
'-Wl,--exclude-all-symbols',
|
||||||
'-Wl,--gc-sections',
|
'-Wl,--gc-sections',
|
||||||
'-static-libgcc',
|
'-static-libgcc',
|
||||||
'-flto', # Enable Link-Time Optimization
|
# '-ggdb', # Add debug information
|
||||||
'-Wl,-s', # Strip debug symbols
|
'-lcrypt32', # Bcrypt needed for prashook
|
||||||
|
# '-Wl,-s', # Strip debug symbols
|
||||||
language: 'c',
|
language: 'c',
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
@ -42,6 +43,7 @@ shlwapi_lib = cc.find_library('shlwapi')
|
||||||
dinput8_lib = cc.find_library('dinput8')
|
dinput8_lib = cc.find_library('dinput8')
|
||||||
dxguid_lib = cc.find_library('dxguid')
|
dxguid_lib = cc.find_library('dxguid')
|
||||||
xinput_lib = cc.find_library('xinput')
|
xinput_lib = cc.find_library('xinput')
|
||||||
|
pathcch_lib = cc.find_library('pathcch')
|
||||||
|
|
||||||
inc = include_directories('.')
|
inc = include_directories('.')
|
||||||
capnhook = subproject('capnhook')
|
capnhook = subproject('capnhook')
|
||||||
|
@ -55,6 +57,7 @@ subdir('platform')
|
||||||
subdir('util')
|
subdir('util')
|
||||||
|
|
||||||
subdir('gfxhook')
|
subdir('gfxhook')
|
||||||
|
subdir('unityhook')
|
||||||
|
|
||||||
subdir('aimeio')
|
subdir('aimeio')
|
||||||
subdir('chuniio')
|
subdir('chuniio')
|
||||||
|
|
|
@ -28,6 +28,66 @@ void mu3_dll_config_load(
|
||||||
filename);
|
filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
wchar_t tmpstr[16];
|
||||||
|
|
||||||
|
memset(cfg->board_number, ' ', sizeof(cfg->board_number));
|
||||||
|
memset(cfg->chip_number, ' ', sizeof(cfg->chip_number));
|
||||||
|
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->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);
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"led15093",
|
||||||
|
L"boardNumber",
|
||||||
|
L"15093-06",
|
||||||
|
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"led15093",
|
||||||
|
L"chipNumber",
|
||||||
|
L"6710A",
|
||||||
|
tmpstr,
|
||||||
|
_countof(tmpstr),
|
||||||
|
filename);
|
||||||
|
|
||||||
|
n = wcstombs(cfg->chip_number, tmpstr, sizeof(cfg->chip_number));
|
||||||
|
for (int i = n; i < sizeof(cfg->chip_number); i++)
|
||||||
|
{
|
||||||
|
cfg->chip_number[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"led15093",
|
||||||
|
L"bootChipNumber",
|
||||||
|
L"6709 ",
|
||||||
|
tmpstr,
|
||||||
|
_countof(tmpstr),
|
||||||
|
filename);
|
||||||
|
|
||||||
|
n = wcstombs(cfg->boot_chip_number, tmpstr, sizeof(cfg->boot_chip_number));
|
||||||
|
for (int i = n; i < sizeof(cfg->boot_chip_number); i++)
|
||||||
|
{
|
||||||
|
cfg->boot_chip_number[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void mu3_hook_config_load(
|
void mu3_hook_config_load(
|
||||||
struct mu3_hook_config *cfg,
|
struct mu3_hook_config *cfg,
|
||||||
const wchar_t *filename)
|
const wchar_t *filename)
|
||||||
|
@ -40,6 +100,8 @@ void mu3_hook_config_load(
|
||||||
dvd_config_load(&cfg->dvd, filename);
|
dvd_config_load(&cfg->dvd, filename);
|
||||||
io4_config_load(&cfg->io4, filename);
|
io4_config_load(&cfg->io4, filename);
|
||||||
gfx_config_load(&cfg->gfx, filename);
|
gfx_config_load(&cfg->gfx, filename);
|
||||||
|
led15093_config_load(&cfg->led15093, filename);
|
||||||
vfd_config_load(&cfg->vfd, filename);
|
vfd_config_load(&cfg->vfd, filename);
|
||||||
mu3_dll_config_load(&cfg->dll, filename);
|
mu3_dll_config_load(&cfg->dll, filename);
|
||||||
|
unity_config_load(&cfg->unity, filename);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "board/config.h"
|
#include "board/config.h"
|
||||||
// #include "board/led15093.h"
|
#include "board/led15093.h"
|
||||||
|
|
||||||
#include "gfxhook/gfx.h"
|
#include "gfxhook/gfx.h"
|
||||||
|
|
||||||
|
@ -13,15 +13,18 @@
|
||||||
|
|
||||||
#include "platform/config.h"
|
#include "platform/config.h"
|
||||||
|
|
||||||
|
#include "unityhook/config.h"
|
||||||
|
|
||||||
struct mu3_hook_config {
|
struct mu3_hook_config {
|
||||||
struct platform_config platform;
|
struct platform_config platform;
|
||||||
struct aime_config aime;
|
struct aime_config aime;
|
||||||
struct dvd_config dvd;
|
struct dvd_config dvd;
|
||||||
struct io4_config io4;
|
struct io4_config io4;
|
||||||
struct gfx_config gfx;
|
struct gfx_config gfx;
|
||||||
// struct led15093_config led15093;
|
struct led15093_config led15093;
|
||||||
struct vfd_config vfd;
|
struct vfd_config vfd;
|
||||||
struct mu3_dll_config dll;
|
struct mu3_dll_config dll;
|
||||||
|
struct unity_config unity;
|
||||||
};
|
};
|
||||||
|
|
||||||
void mu3_dll_config_load(
|
void mu3_dll_config_load(
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "board/io4.h"
|
|
||||||
#include "board/sg-reader.h"
|
#include "board/sg-reader.h"
|
||||||
#include "board/vfd.h"
|
#include "board/vfd.h"
|
||||||
|
|
||||||
|
@ -20,10 +19,12 @@
|
||||||
#include "mu3hook/config.h"
|
#include "mu3hook/config.h"
|
||||||
#include "mu3hook/io4.h"
|
#include "mu3hook/io4.h"
|
||||||
#include "mu3hook/mu3-dll.h"
|
#include "mu3hook/mu3-dll.h"
|
||||||
#include "mu3hook/unity.h"
|
|
||||||
|
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
|
|
||||||
|
#include "unityhook/config.h"
|
||||||
|
#include "unityhook/hook.h"
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
static HMODULE mu3_hook_mod;
|
static HMODULE mu3_hook_mod;
|
||||||
|
@ -61,14 +62,18 @@ static DWORD CALLBACK mu3_pre_startup(void)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
hr = mu3_dll_init(&mu3_hook_cfg.dll, mu3_hook_mod);
|
||||||
// Does not work, Unity moment
|
|
||||||
hr = led15093_hook_init(&mu3_hook_cfg.led15093, 3, 1, 1, 2);
|
if (FAILED(hr)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = led15093_hook_init(&mu3_hook_cfg.led15093,
|
||||||
|
mu3_dll.led_init, mu3_dll.led_set_leds, 3, 1, 1, 2);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1, 1, mu3_hook_mod);
|
hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1, 1, mu3_hook_mod);
|
||||||
|
|
||||||
|
@ -82,12 +87,6 @@ static DWORD CALLBACK mu3_pre_startup(void)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = mu3_dll_init(&mu3_hook_cfg.dll, mu3_hook_mod);
|
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = mu3_io4_hook_init(&mu3_hook_cfg.io4);
|
hr = mu3_io4_hook_init(&mu3_hook_cfg.io4);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
|
@ -99,7 +98,7 @@ static DWORD CALLBACK mu3_pre_startup(void)
|
||||||
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
|
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
|
||||||
hooked earlier in the `mu3hook` initialization. */
|
hooked earlier in the `mu3hook` initialization. */
|
||||||
|
|
||||||
unity_hook_init();
|
unity_hook_init(&mu3_hook_cfg.unity, mu3_hook_mod);
|
||||||
|
|
||||||
/* Initialize debug helpers */
|
/* Initialize debug helpers */
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,13 @@
|
||||||
#include "util/dprintf.h"
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state);
|
static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state);
|
||||||
|
static HRESULT mu3_io4_write_gpio(uint8_t* payload, size_t len);
|
||||||
|
|
||||||
static uint16_t coins;
|
static uint16_t coins;
|
||||||
|
|
||||||
static const struct io4_ops mu3_io4_ops = {
|
static const struct io4_ops mu3_io4_ops = {
|
||||||
.poll = mu3_io4_poll,
|
.poll = mu3_io4_poll,
|
||||||
|
.write_gpio = mu3_io4_write_gpio,
|
||||||
};
|
};
|
||||||
|
|
||||||
HRESULT mu3_io4_hook_init(const struct io4_config *cfg)
|
HRESULT mu3_io4_hook_init(const struct io4_config *cfg)
|
||||||
|
@ -124,3 +127,46 @@ static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state)
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HRESULT mu3_io4_write_gpio(uint8_t* payload, size_t len)
|
||||||
|
{
|
||||||
|
// Just fast fail if there aren't enough bytes in the payload
|
||||||
|
if (len < 3)
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
// This command is used for lights in Ongeki, but it only contains button lights,
|
||||||
|
// and only in the first 3 bytes of the payload; everything else is padding to
|
||||||
|
// make the payload 62 bytes. The rest of the cabinet lights and the side button
|
||||||
|
// lights are handled separately, by the 15093 lights controller.
|
||||||
|
uint32_t lights_data = (uint32_t) ((uint8_t)(payload[0]) << 24 |
|
||||||
|
(uint8_t)(payload[1]) << 16 |
|
||||||
|
(uint8_t)(payload[2]) << 8);
|
||||||
|
|
||||||
|
// Since Sega uses an odd ordering for the first part of the bitfield,
|
||||||
|
// let's normalize the data and just send over bytes for the receiver
|
||||||
|
// to interpret as RGB values.
|
||||||
|
uint8_t rgb_out[6 * 3] = {
|
||||||
|
lights_data & MU3_IO_LED_L1_R ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_L1_G ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_L1_B ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_L2_R ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_L2_G ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_L2_B ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_L3_R ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_L3_G ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_L3_B ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_R1_R ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_R1_G ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_R1_B ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_R2_R ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_R2_G ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_R2_B ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_R3_R ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_R3_G ? 0xFF : 0x00,
|
||||||
|
lights_data & MU3_IO_LED_R3_B ? 0xFF : 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
mu3_dll.led_set_leds(1, rgb_out);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ shared_library(
|
||||||
hooklib_lib,
|
hooklib_lib,
|
||||||
mu3io_lib,
|
mu3io_lib,
|
||||||
platform_lib,
|
platform_lib,
|
||||||
|
unityhook_lib,
|
||||||
util_lib,
|
util_lib,
|
||||||
],
|
],
|
||||||
sources : [
|
sources : [
|
||||||
|
@ -27,7 +28,5 @@ shared_library(
|
||||||
'io4.h',
|
'io4.h',
|
||||||
'mu3-dll.c',
|
'mu3-dll.c',
|
||||||
'mu3-dll.h',
|
'mu3-dll.h',
|
||||||
'unity.h',
|
|
||||||
'unity.c',
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,9 +24,28 @@ const struct dll_bind_sym mu3_dll_syms[] = {
|
||||||
}, {
|
}, {
|
||||||
.sym = "mu3_io_get_lever",
|
.sym = "mu3_io_get_lever",
|
||||||
.off = offsetof(struct mu3_dll, get_lever),
|
.off = offsetof(struct mu3_dll, get_lever),
|
||||||
|
}, {
|
||||||
|
.sym = "mu3_io_led_init",
|
||||||
|
.off = offsetof(struct mu3_dll, led_init),
|
||||||
|
}, {
|
||||||
|
.sym = "mu3_io_led_set_colors",
|
||||||
|
.off = offsetof(struct mu3_dll, led_set_leds),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Helper function to determine upon dll_bind failure whether the required functions were found
|
||||||
|
NOTE: relies on symbols order declared above */
|
||||||
|
static HRESULT has_enough_symbols(uint16_t version, uint8_t count)
|
||||||
|
{
|
||||||
|
if ( version <= 0x0100 && count == 5 )
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
if ( version >= 0x0101 && count == 7 )
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
struct mu3_dll mu3_dll;
|
struct mu3_dll mu3_dll;
|
||||||
|
|
||||||
// Copypasta DLL binding and diagnostic message boilerplate.
|
// Copypasta DLL binding and diagnostic message boilerplate.
|
||||||
|
@ -86,16 +105,25 @@ HRESULT mu3_dll_init(const struct mu3_dll_config *cfg, HINSTANCE self)
|
||||||
}
|
}
|
||||||
|
|
||||||
sym = mu3_dll_syms;
|
sym = mu3_dll_syms;
|
||||||
|
const struct dll_bind_sym *init_sym = &sym[0];
|
||||||
|
|
||||||
hr = dll_bind(&mu3_dll, src, &sym, _countof(mu3_dll_syms));
|
hr = dll_bind(&mu3_dll, src, &sym, _countof(mu3_dll_syms));
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
if (src != self) {
|
if (src != self) {
|
||||||
dprintf("Ongeki IO: Custom IO DLL does not provide function "
|
// Might still be ok depending on external dll API version
|
||||||
"\"%s\". Please contact your IO DLL's developer for "
|
int bind_count = sym - init_sym;
|
||||||
"further assistance.\n",
|
if (has_enough_symbols(mu3_dll.api_version, bind_count) == S_OK)
|
||||||
sym->sym);
|
{
|
||||||
|
hr = S_OK;
|
||||||
|
} else {
|
||||||
|
dprintf("Ongeki IO: Custom IO DLL does not provide function "
|
||||||
|
"\"%s\". Please contact your IO DLL's developer for "
|
||||||
|
"further assistance.\n",
|
||||||
|
sym->sym);
|
||||||
|
|
||||||
goto end;
|
goto end;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
|
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ struct mu3_dll {
|
||||||
void (*get_opbtns)(uint8_t *opbtn);
|
void (*get_opbtns)(uint8_t *opbtn);
|
||||||
void (*get_gamebtns)(uint8_t *left, uint8_t *right);
|
void (*get_gamebtns)(uint8_t *left, uint8_t *right);
|
||||||
void (*get_lever)(int16_t *pos);
|
void (*get_lever)(int16_t *pos);
|
||||||
|
HRESULT (*led_init)(void);
|
||||||
|
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mu3_dll_config {
|
struct mu3_dll_config {
|
||||||
|
|
|
@ -23,3 +23,5 @@ EXPORTS
|
||||||
mu3_io_get_opbtns
|
mu3_io_get_opbtns
|
||||||
mu3_io_init
|
mu3_io_init
|
||||||
mu3_io_poll
|
mu3_io_poll
|
||||||
|
mu3_io_led_init
|
||||||
|
mu3_io_led_set_colors
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#include "hook/table.h"
|
|
||||||
|
|
||||||
#include "hooklib/dll.h"
|
|
||||||
#include "hooklib/path.h"
|
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
|
||||||
|
|
||||||
static void dll_hook_insert_hooks(HMODULE target);
|
|
||||||
|
|
||||||
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name);
|
|
||||||
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
|
|
||||||
|
|
||||||
static const struct hook_symbol unity_kernel32_syms[] = {
|
|
||||||
{
|
|
||||||
.name = "LoadLibraryW",
|
|
||||||
.patch = my_LoadLibraryW,
|
|
||||||
.link = (void **) &next_LoadLibraryW,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const wchar_t *target_modules[] = {
|
|
||||||
L"mono.dll",
|
|
||||||
L"cri_ware_unity.dll",
|
|
||||||
};
|
|
||||||
static const size_t target_modules_len = _countof(target_modules);
|
|
||||||
|
|
||||||
void unity_hook_init(void)
|
|
||||||
{
|
|
||||||
dll_hook_insert_hooks(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dll_hook_insert_hooks(HMODULE target)
|
|
||||||
{
|
|
||||||
hook_table_apply(
|
|
||||||
target,
|
|
||||||
"kernel32.dll",
|
|
||||||
unity_kernel32_syms,
|
|
||||||
_countof(unity_kernel32_syms));
|
|
||||||
}
|
|
||||||
|
|
||||||
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
|
|
||||||
{
|
|
||||||
const wchar_t *name_end;
|
|
||||||
const wchar_t *target_module;
|
|
||||||
bool already_loaded;
|
|
||||||
HMODULE result;
|
|
||||||
size_t name_len;
|
|
||||||
size_t target_module_len;
|
|
||||||
|
|
||||||
if (name == NULL) {
|
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the module is already loaded
|
|
||||||
already_loaded = GetModuleHandleW(name) != NULL;
|
|
||||||
|
|
||||||
// Must call the next handler so the DLL reference count is incremented
|
|
||||||
result = next_LoadLibraryW(name);
|
|
||||||
|
|
||||||
if (!already_loaded && result != NULL) {
|
|
||||||
name_len = wcslen(name);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < target_modules_len; i++) {
|
|
||||||
target_module = target_modules[i];
|
|
||||||
target_module_len = wcslen(target_module);
|
|
||||||
|
|
||||||
// Check if the newly loaded library is at least the length of
|
|
||||||
// the name of the target module
|
|
||||||
if (name_len < target_module_len) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
name_end = &name[name_len - target_module_len];
|
|
||||||
|
|
||||||
// Check if the name of the newly loaded library is one of the
|
|
||||||
// modules the path hooks should be injected into
|
|
||||||
if (_wcsicmp(name_end, target_module) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
dprintf("Unity: Loaded %S\n", target_module);
|
|
||||||
|
|
||||||
dll_hook_insert_hooks(result);
|
|
||||||
path_hook_insert_hooks(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
void unity_hook_init(void);
|
|
|
@ -14,6 +14,8 @@ void mu3_io_config_load(
|
||||||
assert(cfg != NULL);
|
assert(cfg != NULL);
|
||||||
assert(filename != NULL);
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
wchar_t output_path_input[6];
|
||||||
|
|
||||||
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", VK_F1, filename);
|
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", VK_F1, filename);
|
||||||
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", VK_F2, 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_coin = GetPrivateProfileIntW(L"io4", L"coin", VK_F3, filename);
|
||||||
|
@ -30,4 +32,25 @@ void mu3_io_config_load(
|
||||||
cfg->vk_right_3 = GetPrivateProfileIntW(L"io4", L"right3", 'L', filename);
|
cfg->vk_right_3 = GetPrivateProfileIntW(L"io4", L"right3", 'L', filename);
|
||||||
cfg->vk_left_menu = GetPrivateProfileIntW(L"io4", L"leftMenu", 'U', filename);
|
cfg->vk_left_menu = GetPrivateProfileIntW(L"io4", L"leftMenu", 'U', filename);
|
||||||
cfg->vk_right_menu = GetPrivateProfileIntW(L"io4", L"rightMenu", 'O', filename);
|
cfg->vk_right_menu = GetPrivateProfileIntW(L"io4", L"rightMenu", 'O', filename);
|
||||||
|
|
||||||
|
cfg->cab_led_output_pipe = GetPrivateProfileIntW(L"led", L"cabLedOutputPipe", 1, filename);
|
||||||
|
cfg->cab_led_output_serial = GetPrivateProfileIntW(L"led", L"cabLedOutputSerial", 0, filename);
|
||||||
|
|
||||||
|
cfg->controller_led_output_pipe = GetPrivateProfileIntW(L"led", L"controllerLedOutputPipe", 1, filename);
|
||||||
|
cfg->controller_led_output_serial = GetPrivateProfileIntW(L"led", L"controllerLedOutputSerial", 0, filename);
|
||||||
|
|
||||||
|
cfg->led_serial_baud = GetPrivateProfileIntW(L"led", L"serialBaud", 921600, filename);
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"led",
|
||||||
|
L"serialPort",
|
||||||
|
L"COM5",
|
||||||
|
output_path_input,
|
||||||
|
_countof(output_path_input),
|
||||||
|
filename);
|
||||||
|
|
||||||
|
// Sanitize the output path. If it's a serial COM port, it needs to be prefixed
|
||||||
|
// with `\\.\`.
|
||||||
|
wcsncpy(cfg->led_serial_port, L"\\\\.\\", 4);
|
||||||
|
wcsncat_s(cfg->led_serial_port, MAX_PATH, output_path_input, MAX_PATH);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,17 @@ struct mu3_io_config {
|
||||||
uint8_t vk_right_3;
|
uint8_t vk_right_3;
|
||||||
uint8_t vk_left_menu;
|
uint8_t vk_left_menu;
|
||||||
uint8_t vk_right_menu;
|
uint8_t vk_right_menu;
|
||||||
|
|
||||||
|
// Which ways to output LED information are enabled
|
||||||
|
bool cab_led_output_pipe;
|
||||||
|
bool cab_led_output_serial;
|
||||||
|
|
||||||
|
bool controller_led_output_pipe;
|
||||||
|
bool controller_led_output_serial;
|
||||||
|
|
||||||
|
// The name of a COM port to output LED data on, in serial mode
|
||||||
|
wchar_t led_serial_port[12];
|
||||||
|
int32_t led_serial_baud;
|
||||||
};
|
};
|
||||||
|
|
||||||
void mu3_io_config_load(
|
void mu3_io_config_load(
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define LED_PACKET_FRAMING 0xE0
|
||||||
|
#define LED_PACKET_ESCAPE 0xD0
|
||||||
|
#define LED_NUM_MAX 66
|
||||||
|
#define LED_BOARDS_TOTAL 2
|
||||||
|
#define LED_OUTPUT_HEADER_SIZE 2
|
||||||
|
#define LED_OUTPUT_DATA_SIZE_MAX LED_NUM_MAX * 3 * 2 // max if every byte's escaped
|
||||||
|
#define LED_OUTPUT_TOTAL_SIZE_MAX LED_OUTPUT_HEADER_SIZE + LED_OUTPUT_DATA_SIZE_MAX
|
||||||
|
|
||||||
|
// This struct is used to send data related to the button and cab LEDs
|
||||||
|
struct _ongeki_led_data_buf_t {
|
||||||
|
byte framing; // Sync byte
|
||||||
|
uint8_t board; // LED output the data is for (0: cab, 1: control deck)
|
||||||
|
byte data[LED_OUTPUT_DATA_SIZE_MAX]; // Buffer for LEDs
|
||||||
|
byte data_len; // How many bytes to output from the buffer
|
||||||
|
};
|
||||||
|
|
||||||
|
static byte ongeki_led_board_data_lens[LED_BOARDS_TOTAL] = {9*3, 6*3};
|
|
@ -0,0 +1,130 @@
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <process.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "mu3io/config.h"
|
||||||
|
#include "mu3io/leddata.h"
|
||||||
|
#include "mu3io/ledoutput.h"
|
||||||
|
#include "mu3io/pipeimpl.h"
|
||||||
|
#include "mu3io/serialimpl.h"
|
||||||
|
|
||||||
|
static struct _ongeki_led_data_buf_t mu3_led_unescaped_buf[LED_BOARDS_TOTAL];
|
||||||
|
static struct _ongeki_led_data_buf_t mu3_led_escaped_buf[LED_BOARDS_TOTAL];
|
||||||
|
|
||||||
|
static bool mu3_led_output_is_init = false;
|
||||||
|
static struct mu3_io_config* mu3_io_config;
|
||||||
|
static bool mu3_led_any_outputs_enabled;
|
||||||
|
|
||||||
|
HANDLE mu3_led_init_mutex;
|
||||||
|
|
||||||
|
HRESULT mu3_led_output_init(struct mu3_io_config* const cfg)
|
||||||
|
{
|
||||||
|
DWORD dwWaitResult = WaitForSingleObject(mu3_led_init_mutex, INFINITE);
|
||||||
|
if (dwWaitResult == WAIT_FAILED)
|
||||||
|
{
|
||||||
|
return HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
}
|
||||||
|
else if (dwWaitResult != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mu3_led_output_is_init)
|
||||||
|
{
|
||||||
|
mu3_io_config = cfg;
|
||||||
|
|
||||||
|
// Setup the framing bytes for the packets
|
||||||
|
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
|
||||||
|
mu3_led_unescaped_buf[i].framing = LED_PACKET_FRAMING;
|
||||||
|
mu3_led_unescaped_buf[i].board = i;
|
||||||
|
mu3_led_unescaped_buf[i].data_len = ongeki_led_board_data_lens[i];
|
||||||
|
|
||||||
|
mu3_led_escaped_buf[i].framing = LED_PACKET_FRAMING;
|
||||||
|
mu3_led_escaped_buf[i].board = i;
|
||||||
|
mu3_led_escaped_buf[i].data_len = ongeki_led_board_data_lens[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
mu3_led_any_outputs_enabled = mu3_io_config->cab_led_output_pipe || mu3_io_config->controller_led_output_pipe
|
||||||
|
|| mu3_io_config->cab_led_output_serial || mu3_io_config->controller_led_output_serial;
|
||||||
|
|
||||||
|
if (mu3_io_config->cab_led_output_pipe || mu3_io_config->controller_led_output_pipe)
|
||||||
|
{
|
||||||
|
mu3_led_pipe_init(); // don't really care about errors here tbh
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mu3_io_config->cab_led_output_serial || mu3_io_config->controller_led_output_serial)
|
||||||
|
{
|
||||||
|
mu3_led_serial_init(mu3_io_config->led_serial_port, mu3_io_config->led_serial_baud);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mu3_led_output_is_init = true;
|
||||||
|
|
||||||
|
ReleaseMutex(mu3_led_init_mutex);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _ongeki_led_data_buf_t* escape_led_data(struct _ongeki_led_data_buf_t* unescaped)
|
||||||
|
{
|
||||||
|
struct _ongeki_led_data_buf_t* out_struct = &mu3_led_escaped_buf[unescaped->board];
|
||||||
|
|
||||||
|
byte* in_buf = unescaped->data;
|
||||||
|
byte* out_buf = out_struct->data;
|
||||||
|
int i = 0;
|
||||||
|
int o = 0;
|
||||||
|
|
||||||
|
while (i < unescaped->data_len)
|
||||||
|
{
|
||||||
|
byte b = in_buf[i++];
|
||||||
|
if (b == LED_PACKET_FRAMING || b == LED_PACKET_ESCAPE)
|
||||||
|
{
|
||||||
|
out_buf[o++] = LED_PACKET_ESCAPE;
|
||||||
|
b--;
|
||||||
|
}
|
||||||
|
out_buf[o++] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_struct->data_len = o;
|
||||||
|
|
||||||
|
return out_struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mu3_led_output_update(int board, const byte* rgb)
|
||||||
|
{
|
||||||
|
if (board < 0 || board > 1 || !mu3_led_any_outputs_enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(mu3_led_unescaped_buf[board].data, rgb, mu3_led_unescaped_buf[board].data_len);
|
||||||
|
struct _ongeki_led_data_buf_t* escaped_data = escape_led_data(&mu3_led_unescaped_buf[board]);
|
||||||
|
|
||||||
|
if (board == 0)
|
||||||
|
{
|
||||||
|
// cab
|
||||||
|
if (mu3_io_config->cab_led_output_pipe)
|
||||||
|
{
|
||||||
|
mu3_led_pipe_update(escaped_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mu3_io_config->cab_led_output_serial)
|
||||||
|
{
|
||||||
|
mu3_led_serial_update(escaped_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// slider
|
||||||
|
if (mu3_io_config->controller_led_output_pipe)
|
||||||
|
{
|
||||||
|
mu3_led_pipe_update(escaped_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mu3_io_config->controller_led_output_serial)
|
||||||
|
{
|
||||||
|
mu3_led_serial_update(escaped_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
LED output functions
|
||||||
|
|
||||||
|
Credits:
|
||||||
|
somewhatlurker, skogaby
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "mu3io/config.h"
|
||||||
|
|
||||||
|
extern HANDLE mu3_led_init_mutex;
|
||||||
|
|
||||||
|
HRESULT mu3_led_output_init(struct mu3_io_config* const cfg);
|
||||||
|
void mu3_led_output_update(int board, const byte* rgb);
|
|
@ -8,9 +8,16 @@ mu3io_lib = static_library(
|
||||||
xinput_lib,
|
xinput_lib,
|
||||||
],
|
],
|
||||||
sources : [
|
sources : [
|
||||||
'mu3io.c',
|
|
||||||
'mu3io.h',
|
|
||||||
'config.c',
|
'config.c',
|
||||||
'config.h',
|
'config.h',
|
||||||
|
'leddata.h',
|
||||||
|
'ledoutput.c',
|
||||||
|
'ledoutput.h',
|
||||||
|
'mu3io.c',
|
||||||
|
'mu3io.h',
|
||||||
|
'pipeimpl.c',
|
||||||
|
'pipeimpl.h',
|
||||||
|
'serialimpl.c',
|
||||||
|
'serialimpl.h'
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include "mu3io/mu3io.h"
|
#include "mu3io/mu3io.h"
|
||||||
#include "mu3io/config.h"
|
#include "mu3io/config.h"
|
||||||
|
#include "mu3io/ledoutput.h"
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
static uint8_t mu3_opbtn;
|
static uint8_t mu3_opbtn;
|
||||||
|
@ -21,7 +23,7 @@ const double MOUSE_SENSITIVITY = 0.5;
|
||||||
|
|
||||||
uint16_t mu3_io_get_api_version(void)
|
uint16_t mu3_io_get_api_version(void)
|
||||||
{
|
{
|
||||||
return 0x0100;
|
return 0x0101;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT mu3_io_init(void)
|
HRESULT mu3_io_init(void)
|
||||||
|
@ -32,7 +34,17 @@ HRESULT mu3_io_init(void)
|
||||||
dprintf("XInput: Mouse lever emulation : %i\n", mu3_io_cfg.use_mouse);
|
dprintf("XInput: Mouse lever emulation : %i\n", mu3_io_cfg.use_mouse);
|
||||||
dprintf("XInput: --- End configuration ---\n");
|
dprintf("XInput: --- End configuration ---\n");
|
||||||
|
|
||||||
return S_OK;
|
mu3_led_init_mutex = CreateMutex(
|
||||||
|
NULL, // default security attributes
|
||||||
|
FALSE, // initially not owned
|
||||||
|
NULL); // unnamed mutex
|
||||||
|
|
||||||
|
if (mu3_led_init_mutex == NULL)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mu3_led_output_init(&mu3_io_cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT mu3_io_poll(void)
|
HRESULT mu3_io_poll(void)
|
||||||
|
@ -195,3 +207,13 @@ void mu3_io_get_lever(int16_t *pos)
|
||||||
*pos = mu3_lever_xpos;
|
*pos = mu3_lever_xpos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT mu3_io_led_init(void)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mu3_io_led_set_colors(uint8_t board, uint8_t *rgb)
|
||||||
|
{
|
||||||
|
mu3_led_output_update(board, rgb);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
MU3 CUSTOM IO API
|
||||||
|
|
||||||
|
Changelog:
|
||||||
|
|
||||||
|
- 0x0100: Initial API version (assumed if chuni_io_get_api_version is not
|
||||||
|
exported)
|
||||||
|
- 0x0101: Added mu3_io_led_init and mu3_io_set_leds
|
||||||
|
*/
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -18,6 +28,29 @@ enum {
|
||||||
MU3_IO_GAMEBTN_MENU = 0x10,
|
MU3_IO_GAMEBTN_MENU = 0x10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* These are the bitmasks to use when checking which
|
||||||
|
lights are triggered on incoming IO4 GPIO writes. */
|
||||||
|
MU3_IO_LED_L1_R = 1 << 31,
|
||||||
|
MU3_IO_LED_L1_G = 1 << 28,
|
||||||
|
MU3_IO_LED_L1_B = 1 << 30,
|
||||||
|
MU3_IO_LED_L2_R = 1 << 27,
|
||||||
|
MU3_IO_LED_L2_G = 1 << 29,
|
||||||
|
MU3_IO_LED_L2_B = 1 << 26,
|
||||||
|
MU3_IO_LED_L3_R = 1 << 25,
|
||||||
|
MU3_IO_LED_L3_G = 1 << 24,
|
||||||
|
MU3_IO_LED_L3_B = 1 << 23,
|
||||||
|
MU3_IO_LED_R1_R = 1 << 22,
|
||||||
|
MU3_IO_LED_R1_G = 1 << 21,
|
||||||
|
MU3_IO_LED_R1_B = 1 << 20,
|
||||||
|
MU3_IO_LED_R2_R = 1 << 19,
|
||||||
|
MU3_IO_LED_R2_G = 1 << 18,
|
||||||
|
MU3_IO_LED_R2_B = 1 << 17,
|
||||||
|
MU3_IO_LED_R3_R = 1 << 16,
|
||||||
|
MU3_IO_LED_R3_G = 1 << 15,
|
||||||
|
MU3_IO_LED_R3_B = 1 << 14,
|
||||||
|
};
|
||||||
|
|
||||||
/* Get the version of the Ongeki IO API that this DLL supports. This
|
/* Get the version of the Ongeki IO API that this DLL supports. This
|
||||||
function should return a positive 16-bit integer, where the high byte is
|
function should return a positive 16-bit integer, where the high byte is
|
||||||
the major version and the low byte is the minor version (as defined by the
|
the major version and the low byte is the minor version (as defined by the
|
||||||
|
@ -83,3 +116,43 @@ void mu3_io_get_gamebtns(uint8_t *left, uint8_t *right);
|
||||||
Minimum API version: 0x0100 */
|
Minimum API version: 0x0100 */
|
||||||
|
|
||||||
void mu3_io_get_lever(int16_t *pos);
|
void mu3_io_get_lever(int16_t *pos);
|
||||||
|
|
||||||
|
/* Initialize LED emulation. This function will be called before any
|
||||||
|
other mu3_io_led_*() function calls.
|
||||||
|
|
||||||
|
All subsequent calls may originate from arbitrary threads and some may
|
||||||
|
overlap with each other. Ensuring synchronization inside your IO DLL is
|
||||||
|
your responsibility.
|
||||||
|
|
||||||
|
Minimum API version: 0x0101 */
|
||||||
|
|
||||||
|
HRESULT mu3_io_led_init(void);
|
||||||
|
|
||||||
|
/* Update the RGB LEDs. rgb is a pointer to an array of up to 61 * 3 = 183 bytes.
|
||||||
|
|
||||||
|
ONGEKI uses one board with WS2811 protocol (each logical led corresponds to 3
|
||||||
|
physical leds). Board 0 is used for all cab lights and both WAD button lights.
|
||||||
|
|
||||||
|
Board 0 has 61 LEDs:
|
||||||
|
[0]-[1]: left side button
|
||||||
|
[2]-[8]: left pillar lower LEDs
|
||||||
|
[9]-[17]: left pillar center LEDs
|
||||||
|
[18]-[24]: left pillar upper LEDs
|
||||||
|
[25]-[35]: billboard LEDs
|
||||||
|
[36]-[42]: right pillar upper LEDs
|
||||||
|
[43]-[51]: right pillar center LEDs
|
||||||
|
[52]-[58]: right pillar lower LEDs
|
||||||
|
[59]-[60]: right side button
|
||||||
|
|
||||||
|
Board 1 has 6 LEDs:
|
||||||
|
[0]-[5]: 3 left and 3 right controller buttons
|
||||||
|
|
||||||
|
Each rgb value is comprised of 3 bytes in R,G,B order. The tricky part is
|
||||||
|
that the board 0 is called from mu3 and the board 1 is called from amdaemon.
|
||||||
|
So the library must be able to handle both calls, using shared memory f.e.
|
||||||
|
This is up to the developer to decide how to handle this, recommended way is
|
||||||
|
to use the amdaemon process as the main one and the mu3 call as a sub one.
|
||||||
|
|
||||||
|
Minimum API version: 0x0101 */
|
||||||
|
|
||||||
|
void mu3_io_led_set_colors(uint8_t board, uint8_t *rgb);
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <process.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "mu3io/leddata.h"
|
||||||
|
#include "mu3io/pipeimpl.h"
|
||||||
|
|
||||||
|
static bool mu3_pipe_update[LED_BOARDS_TOTAL];
|
||||||
|
|
||||||
|
// incoming data is copied into these to ensure it isn't written during output
|
||||||
|
static struct _ongeki_led_data_buf_t mu3_pipe_write_buf[LED_BOARDS_TOTAL];
|
||||||
|
static HANDLE mu3_pipe_write_mutex;
|
||||||
|
|
||||||
|
static HRESULT mu3_pipe_create(LPHANDLE hPipe, LPCWSTR lpszPipename, DWORD dwBufSize)
|
||||||
|
{
|
||||||
|
*hPipe = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
*hPipe = CreateNamedPipeW(
|
||||||
|
lpszPipename, // pipe name
|
||||||
|
PIPE_ACCESS_OUTBOUND, // read/write access
|
||||||
|
PIPE_TYPE_BYTE | // byte type pipe
|
||||||
|
PIPE_WAIT, // blocking mode
|
||||||
|
PIPE_UNLIMITED_INSTANCES, // max. instances
|
||||||
|
dwBufSize, // output buffer size
|
||||||
|
0, // input buffer size
|
||||||
|
0, // client time-out
|
||||||
|
NULL); // default security attribute
|
||||||
|
|
||||||
|
if (*hPipe == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT mu3_pipe_write(HANDLE hPipe, LPCVOID lpBuffer, DWORD dwSize)
|
||||||
|
{
|
||||||
|
DWORD cbWritten = 0;
|
||||||
|
|
||||||
|
bool fSuccess = WriteFile(
|
||||||
|
hPipe,
|
||||||
|
lpBuffer,
|
||||||
|
dwSize,
|
||||||
|
&cbWritten,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!fSuccess || cbWritten != dwSize)
|
||||||
|
{
|
||||||
|
DWORD last_err = GetLastError();
|
||||||
|
return (last_err == ERROR_BROKEN_PIPE) ? E_ABORT : E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int __stdcall mu3_io_led_pipe_thread_proc(void *ctx)
|
||||||
|
{
|
||||||
|
HANDLE hPipe;
|
||||||
|
LPCWSTR lpszPipename = L"\\\\.\\pipe\\ongeki_led";
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
hPipe = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
if (mu3_pipe_create(&hPipe, lpszPipename, LED_OUTPUT_TOTAL_SIZE_MAX) != S_OK)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for a connection to the pipe
|
||||||
|
bool fConnected = ConnectNamedPipe(hPipe, NULL) ?
|
||||||
|
true : (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||||
|
|
||||||
|
while (fConnected)
|
||||||
|
{
|
||||||
|
if (WaitForSingleObject(mu3_pipe_write_mutex, INFINITE) != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
|
||||||
|
if (mu3_pipe_update[i])
|
||||||
|
{
|
||||||
|
HRESULT result = mu3_pipe_write(
|
||||||
|
hPipe,
|
||||||
|
&mu3_pipe_write_buf[i],
|
||||||
|
LED_OUTPUT_HEADER_SIZE + mu3_pipe_write_buf[i].data_len);
|
||||||
|
|
||||||
|
if (result != S_OK)
|
||||||
|
{
|
||||||
|
//if (result == E_ABORT)
|
||||||
|
//{
|
||||||
|
fConnected = false;
|
||||||
|
//}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mu3_pipe_update[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseMutex(mu3_pipe_write_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlushFileBuffers(hPipe);
|
||||||
|
DisconnectNamedPipe(hPipe);
|
||||||
|
CloseHandle(hPipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT mu3_led_pipe_init()
|
||||||
|
{
|
||||||
|
mu3_pipe_write_mutex = CreateMutex(
|
||||||
|
NULL, // default security attributes
|
||||||
|
FALSE, // initially not owned
|
||||||
|
NULL); // unnamed mutex
|
||||||
|
|
||||||
|
if (mu3_pipe_write_mutex == NULL)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear out update bools
|
||||||
|
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
|
||||||
|
mu3_pipe_update[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_beginthreadex(
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
mu3_io_led_pipe_thread_proc,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mu3_led_pipe_update(struct _ongeki_led_data_buf_t* data)
|
||||||
|
{
|
||||||
|
if (data->board > 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WaitForSingleObject(mu3_pipe_write_mutex, INFINITE) != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&mu3_pipe_write_buf[data->board], data, sizeof(struct _ongeki_led_data_buf_t));
|
||||||
|
mu3_pipe_update[data->board] = true;
|
||||||
|
|
||||||
|
ReleaseMutex(mu3_pipe_write_mutex);
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
Pipe implementation for chuniio
|
||||||
|
|
||||||
|
Credits:
|
||||||
|
somewhatlurker, skogaby
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "mu3io/leddata.h"
|
||||||
|
|
||||||
|
HRESULT mu3_led_pipe_init();
|
||||||
|
void mu3_led_pipe_update(struct _ongeki_led_data_buf_t* data);
|
|
@ -0,0 +1,88 @@
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <process.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "mu3io/leddata.h"
|
||||||
|
#include "mu3io/serialimpl.h"
|
||||||
|
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
static HANDLE mu3_serial_port;
|
||||||
|
|
||||||
|
HRESULT mu3_led_serial_init(wchar_t led_com[12], DWORD baud)
|
||||||
|
{
|
||||||
|
// Setup the serial communications
|
||||||
|
BOOL status;
|
||||||
|
|
||||||
|
mu3_serial_port = CreateFileW(led_com,
|
||||||
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (mu3_serial_port == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
dprintf("Ongeki Serial LEDs: Failed to open COM port (attempted on %S)\n", led_com);
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DCB dcb_serial_params = { 0 };
|
||||||
|
dcb_serial_params.DCBlength = sizeof(dcb_serial_params);
|
||||||
|
status = GetCommState(mu3_serial_port, &dcb_serial_params);
|
||||||
|
|
||||||
|
dcb_serial_params.BaudRate = baud;
|
||||||
|
dcb_serial_params.ByteSize = 8;
|
||||||
|
dcb_serial_params.StopBits = ONESTOPBIT;
|
||||||
|
dcb_serial_params.Parity = NOPARITY;
|
||||||
|
SetCommState(mu3_serial_port, &dcb_serial_params);
|
||||||
|
|
||||||
|
COMMTIMEOUTS timeouts = { 0 };
|
||||||
|
timeouts.ReadIntervalTimeout = 50;
|
||||||
|
timeouts.ReadTotalTimeoutConstant = 50;
|
||||||
|
timeouts.ReadTotalTimeoutMultiplier = 10;
|
||||||
|
timeouts.WriteTotalTimeoutConstant = 50;
|
||||||
|
timeouts.WriteTotalTimeoutMultiplier = 10;
|
||||||
|
|
||||||
|
SetCommTimeouts(mu3_serial_port, &timeouts);
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mu3_led_serial_update(struct _ongeki_led_data_buf_t* data)
|
||||||
|
{
|
||||||
|
if (data->board > 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mu3_serial_port != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
DWORD bytes_written = 0;
|
||||||
|
|
||||||
|
BOOL status = WriteFile(
|
||||||
|
mu3_serial_port,
|
||||||
|
data,
|
||||||
|
LED_OUTPUT_HEADER_SIZE + data->data_len,
|
||||||
|
&bytes_written,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
DWORD last_err = GetLastError();
|
||||||
|
dprintf("Ongeki Serial LEDs: Serial port write failed -- %d\n", (int) last_err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dprintf("Ongeki Serial LEDs: Invalid serial port handle\n");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
Serial LED implementation for chuniio
|
||||||
|
|
||||||
|
Credits:
|
||||||
|
somewhatlurker, skogaby
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "mu3io/leddata.h"
|
||||||
|
|
||||||
|
HRESULT mu3_led_serial_init(wchar_t led_com[12], DWORD baud);
|
||||||
|
void mu3_led_serial_update(struct _ongeki_led_data_buf_t* data);
|
|
@ -113,6 +113,14 @@ void dns_config_load(struct dns_config *cfg, const wchar_t *filename)
|
||||||
cfg->aimedb,
|
cfg->aimedb,
|
||||||
_countof(cfg->aimedb),
|
_countof(cfg->aimedb),
|
||||||
filename);
|
filename);
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"dns",
|
||||||
|
L"title",
|
||||||
|
L"title",
|
||||||
|
cfg->title,
|
||||||
|
_countof(cfg->title),
|
||||||
|
filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hwmon_config_load(struct hwmon_config *cfg, const wchar_t *filename)
|
void hwmon_config_load(struct hwmon_config *cfg, const wchar_t *filename)
|
||||||
|
|
|
@ -82,6 +82,13 @@ HRESULT dns_platform_hook_init(const struct dns_config *cfg)
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// crossbeats REV.
|
||||||
|
hr = dns_hook_push(L"https://rev-ent.ac.capcom.jp:443", cfg->title);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
// AimePay
|
// AimePay
|
||||||
hr = dns_hook_push(L"api-aime.am-all.net", cfg->startup);
|
hr = dns_hook_push(L"api-aime.am-all.net", cfg->startup);
|
||||||
|
|
||||||
|
@ -119,7 +126,13 @@ HRESULT dns_platform_hook_init(const struct dns_config *cfg)
|
||||||
|
|
||||||
// Disable api/polling to the original servers
|
// Disable api/polling to the original servers
|
||||||
|
|
||||||
hr = dns_hook_push(L"amlog.sys-all.net", NULL);
|
hr = dns_hook_push(L"*.amlog.sys-all.net", NULL);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = dns_hook_push(L"*.d-amlog.sys-all.net", NULL);
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return hr;
|
return hr;
|
||||||
|
|
|
@ -11,6 +11,7 @@ struct dns_config {
|
||||||
wchar_t startup[128];
|
wchar_t startup[128];
|
||||||
wchar_t billing[128];
|
wchar_t billing[128];
|
||||||
wchar_t aimedb[128];
|
wchar_t aimedb[128];
|
||||||
|
wchar_t title[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
HRESULT dns_platform_hook_init(const struct dns_config *cfg);
|
HRESULT dns_platform_hook_init(const struct dns_config *cfg);
|
||||||
|
|
|
@ -14,22 +14,22 @@
|
||||||
#include "util/str.h"
|
#include "util/str.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
NUSEC_IOCTL_PING = 0x22A114,
|
NUSEC_IOCTL_PING = CTL_CODE(0x22, 0x845, METHOD_BUFFERED, FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_ERASE_TRACE_LOG = 0x22E188,
|
NUSEC_IOCTL_GET_PLAY_COUNT = CTL_CODE(0x22, 0x854, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_TD_ERASE_USED = 0x22E18C,
|
NUSEC_IOCTL_ADD_PLAY_COUNT = CTL_CODE(0x22, 0x855, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_ADD_PLAY_COUNT = 0x22E154,
|
NUSEC_IOCTL_ERASE_TRACE_LOG = CTL_CODE(0x22, 0x862, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_GET_BILLING_CA_CERT = 0x22E1C4,
|
NUSEC_IOCTL_TD_ERASE_USED = CTL_CODE(0x22, 0x863, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_GET_BILLING_PUBKEY = 0x22E1C8,
|
NUSEC_IOCTL_PUT_TRACE_LOG_DATA = CTL_CODE(0x22, 0x864, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_GET_NEARFULL = 0x22E20C,
|
NUSEC_IOCTL_GET_TRACE_LOG_DATA = CTL_CODE(0x22, 0x865, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_GET_NVRAM_AVAILABLE = 0x22E19C,
|
NUSEC_IOCTL_GET_TRACE_LOG_STATE = CTL_CODE(0x22, 0x866, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_GET_NVRAM_GEOMETRY = 0x22E24C,
|
NUSEC_IOCTL_GET_NVRAM_AVAILABLE = CTL_CODE(0x22, 0x867, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_GET_PLAY_COUNT = 0x22E150,
|
NUSEC_IOCTL_GET_BILLING_CA_CERT = CTL_CODE(0x22, 0x871, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_GET_PLAY_LIMIT = 0x22E204,
|
NUSEC_IOCTL_GET_BILLING_PUBKEY = CTL_CODE(0x22, 0x872, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_GET_TRACE_LOG_DATA = 0x22E194,
|
NUSEC_IOCTL_GET_PLAY_LIMIT = CTL_CODE(0x22, 0x881, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_GET_TRACE_LOG_STATE = 0x22E198,
|
NUSEC_IOCTL_PUT_PLAY_LIMIT = CTL_CODE(0x22, 0x882, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_PUT_NEARFULL = 0x22E210,
|
NUSEC_IOCTL_GET_NEARFULL = CTL_CODE(0x22, 0x883, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_PUT_PLAY_LIMIT = 0x22E208,
|
NUSEC_IOCTL_PUT_NEARFULL = CTL_CODE(0x22, 0x884, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
NUSEC_IOCTL_PUT_TRACE_LOG_DATA = 0x22E190,
|
NUSEC_IOCTL_GET_NVRAM_GEOMETRY = CTL_CODE(0x22, 0x893, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nusec_log_record {
|
struct nusec_log_record {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[wrap-git]
|
[wrap-git]
|
||||||
directory = capnhook
|
directory = capnhook
|
||||||
url = https://github.com/decafcode/capnhook
|
url = https://github.com/Hay1tsme/capnhook
|
||||||
revision = 69f7e3b48c2e0ff5be1d7a83cdcc2597a458357b
|
revision = 09306229f1fd09bae0e617865a26778d3537ff93
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
void unity_config_load(struct unity_config *cfg, const wchar_t *filename) {
|
||||||
|
cfg->enable = GetPrivateProfileIntW(L"unity", L"enable", 1, filename);
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"unity",
|
||||||
|
L"targetAssembly",
|
||||||
|
L"",
|
||||||
|
cfg->target_assembly,
|
||||||
|
_countof(cfg->target_assembly),
|
||||||
|
filename
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
struct unity_config {
|
||||||
|
bool enable;
|
||||||
|
wchar_t target_assembly[MAX_PATH];
|
||||||
|
};
|
||||||
|
|
||||||
|
void unity_config_load(struct unity_config *cfg, const wchar_t *filename);
|
|
@ -0,0 +1,184 @@
|
||||||
|
// A simplified version of NeighTools' UnityDoorstop, allowing mod loaders
|
||||||
|
// like BepInEx to be loaded into Unity games.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: CC0
|
||||||
|
// https://github.com/NeighTools/UnityDoorstop
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <pathcch.h>
|
||||||
|
#include <psapi.h>
|
||||||
|
|
||||||
|
#include "hook/procaddr.h"
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
#include "doorstop.h"
|
||||||
|
#include "mono.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static void * my_mono_jit_init_version(const char *root_domain_name, const char *runtime_version);
|
||||||
|
void doorstop_invoke(void *domain);
|
||||||
|
|
||||||
|
static char module_name[MAX_PATH];
|
||||||
|
static bool doorstop_hook_initted;
|
||||||
|
static struct unity_config unity_config;
|
||||||
|
static struct hook_symbol unity_mono_syms[] = {
|
||||||
|
{
|
||||||
|
.name = "mono_jit_init_version",
|
||||||
|
.patch = my_mono_jit_init_version,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void doorstop_mono_hook_init(const struct unity_config *cfg, HINSTANCE module) {
|
||||||
|
if (doorstop_hook_initted || cfg->target_assembly[0] == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetModuleBaseNameA(GetCurrentProcess(), module, module_name, MAX_PATH);
|
||||||
|
|
||||||
|
memcpy(&unity_config, cfg, sizeof(*cfg));
|
||||||
|
load_mono_functions(module);
|
||||||
|
proc_addr_table_push(NULL, module_name, unity_mono_syms, _countof(unity_mono_syms));
|
||||||
|
|
||||||
|
doorstop_hook_initted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void * my_mono_jit_init_version(const char *root_domain_name, const char *runtime_version) {
|
||||||
|
dprintf("Unity: Starting Mono domain \"%s\"\n", root_domain_name);
|
||||||
|
|
||||||
|
SetEnvironmentVariableW(L"DOORSTOP_DLL_SEARCH_DIRS", widen(mono_assembly_getrootdir()));
|
||||||
|
|
||||||
|
void* domain = mono_jit_init_version(root_domain_name, runtime_version);
|
||||||
|
|
||||||
|
doorstop_invoke(domain);
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doorstop_invoke(void* domain) {
|
||||||
|
if (GetEnvironmentVariableW(L"DOORSTOP_INITIALIZED", NULL, 0) != 0) {
|
||||||
|
dprintf("Unity: Doorstop is already initialized.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetEnvironmentVariableW(L"DOORSTOP_INITIALIZED", L"TRUE");
|
||||||
|
|
||||||
|
mono_thread_set_main(mono_thread_current());
|
||||||
|
|
||||||
|
if (mono_domain_set_config) {
|
||||||
|
#define CONFIG_EXT L".config"
|
||||||
|
|
||||||
|
wchar_t config_path[MAX_PATH];
|
||||||
|
size_t config_path_len = GetModuleFileNameW(NULL, config_path, MAX_PATH);
|
||||||
|
wchar_t *folder_name = wcsdup(config_path);
|
||||||
|
|
||||||
|
PathCchRemoveFileSpec(folder_name, config_path_len + 1);
|
||||||
|
wmemcpy(config_path + config_path_len, CONFIG_EXT, sizeof(CONFIG_EXT) / sizeof(CONFIG_EXT[0]));
|
||||||
|
|
||||||
|
char *config_path_n = narrow(config_path);
|
||||||
|
char *folder_name_n = narrow(folder_name);
|
||||||
|
|
||||||
|
dprintf("Unity: Setting config paths: base dir: %s; config path: %s\n", folder_name_n, config_path_n);
|
||||||
|
|
||||||
|
mono_domain_set_config(domain, folder_name_n, config_path_n);
|
||||||
|
|
||||||
|
free(folder_name);
|
||||||
|
free(config_path_n);
|
||||||
|
free(folder_name_n);
|
||||||
|
|
||||||
|
#undef CONFIG_EXT
|
||||||
|
}
|
||||||
|
|
||||||
|
SetEnvironmentVariableW(L"DOORSTOP_INVOKE_DLL_PATH", unity_config.target_assembly);
|
||||||
|
|
||||||
|
char *assembly_dir = mono_assembly_getrootdir();
|
||||||
|
dprintf("Unity: Assembly directory: %s\n", assembly_dir);
|
||||||
|
|
||||||
|
SetEnvironmentVariableA("DOORSTOP_MANAGED_FOLDER_DIR", assembly_dir);
|
||||||
|
|
||||||
|
wchar_t app_path[MAX_PATH];
|
||||||
|
GetModuleFileNameW(NULL, app_path, MAX_PATH);
|
||||||
|
SetEnvironmentVariableW(L"DOORSTOP_PROCESS_PATH", app_path);
|
||||||
|
|
||||||
|
char* dll_path = narrow(unity_config.target_assembly);
|
||||||
|
|
||||||
|
dprintf("Unity: Loading assembly: %s\n", dll_path);
|
||||||
|
|
||||||
|
void* assembly = mono_domain_assembly_open(domain, dll_path);
|
||||||
|
|
||||||
|
if (!assembly) {
|
||||||
|
dprintf("Unity: Failed to load assembly\n");
|
||||||
|
free(dll_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *image = mono_assembly_get_image(assembly);
|
||||||
|
|
||||||
|
if (!image) {
|
||||||
|
dprintf("Unity: Assembly image doesn't exist\n");
|
||||||
|
free(dll_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BepInEx 5.4.23 has upgrade its doorstop version,
|
||||||
|
// which forces entrypoint to Doorstop.Entrypoint:Start
|
||||||
|
|
||||||
|
void *desc = mono_method_desc_new("Doorstop.Entrypoint:Start", TRUE);
|
||||||
|
void *method = mono_method_desc_search_in_image(desc, image);
|
||||||
|
|
||||||
|
if (!method) {
|
||||||
|
// Fallback to old entrypoint definition.
|
||||||
|
|
||||||
|
desc = mono_method_desc_new("*:Main", FALSE);
|
||||||
|
method = mono_method_desc_search_in_image(desc, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!method) {
|
||||||
|
dprintf("Unity: Assembly does not have a valid entrypoint.\n");
|
||||||
|
free(dll_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *signature = mono_method_signature(method);
|
||||||
|
UINT32 params = mono_signature_get_param_count(signature);
|
||||||
|
void **args = NULL;
|
||||||
|
|
||||||
|
if (params == 1) {
|
||||||
|
// If there is a parameter, it's most likely a string[].
|
||||||
|
void *args_array = mono_array_new(domain, mono_get_string_class(), 0);
|
||||||
|
args = malloc(sizeof(void*) * 1);
|
||||||
|
args[0] = args_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("Unity: Invoking method %p\n", method);
|
||||||
|
|
||||||
|
void *exc = NULL;
|
||||||
|
mono_runtime_invoke(method, NULL, args, &exc);
|
||||||
|
|
||||||
|
if (exc) {
|
||||||
|
dprintf("Unity: Error invoking method!\n");
|
||||||
|
|
||||||
|
void *ex_class = mono_get_exception_class();
|
||||||
|
void *to_string_desc = mono_method_desc_new("*:ToString()", FALSE);
|
||||||
|
void* to_string_method = mono_method_desc_search_in_class(to_string_desc, ex_class);
|
||||||
|
|
||||||
|
mono_method_desc_free(to_string_desc);
|
||||||
|
|
||||||
|
if (to_string_method) {
|
||||||
|
void* real_to_string_method = mono_object_get_virtual_method(exc, to_string_method);
|
||||||
|
void* exc2 = NULL;
|
||||||
|
void* str = mono_runtime_invoke(real_to_string_method, exc, NULL, &exc2);
|
||||||
|
|
||||||
|
if (!exc2) {
|
||||||
|
char* exc_str = mono_string_to_utf8(str);
|
||||||
|
dprintf("Unity: Error message: %s\n", exc_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mono_method_desc_free(desc);
|
||||||
|
free(dll_path);
|
||||||
|
|
||||||
|
if (args) {
|
||||||
|
free(args);
|
||||||
|
args = NULL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
void doorstop_mono_hook_init(const struct unity_config *cfg, HINSTANCE module);
|
|
@ -1,48 +1,91 @@
|
||||||
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#include "hook/table.h"
|
#include "hook/table.h"
|
||||||
|
#include "hook/procaddr.h"
|
||||||
|
#include "hook/iohook.h"
|
||||||
|
|
||||||
#include "hooklib/dll.h"
|
#include "hooklib/dll.h"
|
||||||
#include "hooklib/path.h"
|
#include "hooklib/path.h"
|
||||||
|
#include "hooklib/printer.h"
|
||||||
|
#include "hooklib/reg.h"
|
||||||
|
#include "hooklib/touch.h"
|
||||||
|
#include "hooklib/serial.h"
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
#include "util/dprintf.h"
|
||||||
|
|
||||||
|
#include "doorstop.h"
|
||||||
|
#include "hook.h"
|
||||||
|
|
||||||
|
static bool unity_hook_initted;
|
||||||
|
static struct unity_config unity_config;
|
||||||
|
|
||||||
|
static const wchar_t *target_modules[] = {
|
||||||
|
L"mono.dll",
|
||||||
|
L"mono-2.0-bdwgc.dll",
|
||||||
|
L"cri_ware_unity.dll",
|
||||||
|
L"SerialPortAPI.dll",
|
||||||
|
L"C300usb.dll",
|
||||||
|
L"C300FWDLusb.dll",
|
||||||
|
L"apmled.dll",
|
||||||
|
L"apmmount.dll",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const size_t target_modules_len = _countof(target_modules);
|
||||||
|
|
||||||
static void dll_hook_insert_hooks(HMODULE target);
|
static void dll_hook_insert_hooks(HMODULE target);
|
||||||
|
|
||||||
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name);
|
static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name);
|
||||||
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
|
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
|
||||||
|
static HMODULE WINAPI hook_LoadLibraryExW(const wchar_t *name, HANDLE hFile, DWORD dwFlags);
|
||||||
|
static HMODULE (WINAPI *next_LoadLibraryExW)(const wchar_t *name, HANDLE hFile, DWORD dwFlags);
|
||||||
|
|
||||||
static const struct hook_symbol unity_kernel32_syms[] = {
|
static const struct hook_symbol unity_kernel32_syms[] = {
|
||||||
{
|
{
|
||||||
.name = "LoadLibraryW",
|
.name = "LoadLibraryW",
|
||||||
.patch = my_LoadLibraryW,
|
.patch = hook_LoadLibraryW,
|
||||||
.link = (void **) &next_LoadLibraryW,
|
.link = (void **) &next_LoadLibraryW,
|
||||||
},
|
}, {
|
||||||
|
.name = "LoadLibraryExW",
|
||||||
|
.patch = hook_LoadLibraryExW,
|
||||||
|
.link = (void **) &next_LoadLibraryExW,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const wchar_t *target_modules[] = {
|
|
||||||
L"mono-2.0-bdwgc.dll",
|
|
||||||
L"cri_ware_unity.dll",
|
|
||||||
};
|
|
||||||
static const size_t target_modules_len = _countof(target_modules);
|
|
||||||
|
|
||||||
void unity_hook_init(void)
|
void unity_hook_init(const struct unity_config *cfg, HINSTANCE self) {
|
||||||
{
|
assert(cfg != NULL);
|
||||||
|
|
||||||
|
if (!cfg->enable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unity_hook_initted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&unity_config, cfg, sizeof(*cfg));
|
||||||
dll_hook_insert_hooks(NULL);
|
dll_hook_insert_hooks(NULL);
|
||||||
|
|
||||||
|
unity_hook_initted = true;
|
||||||
|
dprintf("Unity: Hook enabled.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dll_hook_insert_hooks(HMODULE target)
|
static void dll_hook_insert_hooks(HMODULE target) {
|
||||||
{
|
|
||||||
hook_table_apply(
|
hook_table_apply(
|
||||||
target,
|
target,
|
||||||
"kernel32.dll",
|
"kernel32.dll",
|
||||||
unity_kernel32_syms,
|
unity_kernel32_syms,
|
||||||
_countof(unity_kernel32_syms));
|
_countof(unity_kernel32_syms));
|
||||||
}
|
}
|
||||||
|
|
||||||
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
|
static HMODULE WINAPI hook_LoadLibraryExW(const wchar_t *name, HANDLE hFile, DWORD dwFlags)
|
||||||
|
{
|
||||||
|
// dprintf("Unity: LoadLibraryExW %ls\n", name);
|
||||||
|
return hook_LoadLibraryW(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name)
|
||||||
{
|
{
|
||||||
const wchar_t *name_end;
|
const wchar_t *name_end;
|
||||||
const wchar_t *target_module;
|
const wchar_t *target_module;
|
||||||
|
@ -66,6 +109,11 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
|
||||||
if (!already_loaded && result != NULL) {
|
if (!already_loaded && result != NULL) {
|
||||||
name_len = wcslen(name);
|
name_len = wcslen(name);
|
||||||
|
|
||||||
|
// mono entrypoint for injecting target_assembly
|
||||||
|
if (GetProcAddress(result, "mono_jit_init_version")) {
|
||||||
|
doorstop_mono_hook_init(&unity_config, result);
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < target_modules_len; i++) {
|
for (size_t i = 0; i < target_modules_len; i++) {
|
||||||
target_module = target_modules[i];
|
target_module = target_modules[i];
|
||||||
target_module_len = wcslen(target_module);
|
target_module_len = wcslen(target_module);
|
||||||
|
@ -88,6 +136,14 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
|
||||||
|
|
||||||
dll_hook_insert_hooks(result);
|
dll_hook_insert_hooks(result);
|
||||||
path_hook_insert_hooks(result);
|
path_hook_insert_hooks(result);
|
||||||
|
|
||||||
|
// printer_hook_insert_hooks(result);
|
||||||
|
|
||||||
|
reg_hook_insert_hooks(result);
|
||||||
|
|
||||||
|
proc_addr_insert_hooks(result);
|
||||||
|
serial_hook_apply_hooks(result);
|
||||||
|
iohook_apply_hooks(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
void unity_hook_init(const struct unity_config *cfg, HINSTANCE self);
|
|
@ -0,0 +1,20 @@
|
||||||
|
unityhook_lib = static_library(
|
||||||
|
'unityhook',
|
||||||
|
include_directories: inc,
|
||||||
|
implicit_include_directories: false,
|
||||||
|
c_pch: '../precompiled.h',
|
||||||
|
dependencies: [
|
||||||
|
capnhook.get_variable('hook_dep'),
|
||||||
|
pathcch_lib
|
||||||
|
],
|
||||||
|
sources: [
|
||||||
|
'mono.h',
|
||||||
|
'config.c',
|
||||||
|
'config.h',
|
||||||
|
'doorstop.c',
|
||||||
|
'doorstop.h',
|
||||||
|
'hook.c',
|
||||||
|
'hook.h',
|
||||||
|
'util.h'
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,100 @@
|
||||||
|
// SPDX-License-Identifier: CC0
|
||||||
|
// https://github.com/NeighTools/UnityDoorstop
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// Here we define the pointers to some functions within mono.dll
|
||||||
|
// Note to C learners: these are not signature definitions, but rather "variable"
|
||||||
|
// definitions with the function pointer type.
|
||||||
|
|
||||||
|
// Note: we use void* instead of the real intented structs defined in mono API
|
||||||
|
// This way we don't need to include or define any of Mono's structs, which saves space
|
||||||
|
// This, obviously, comes with a drawback of not being able to easily access the contents of the structs
|
||||||
|
|
||||||
|
void * (*mono_thread_current)();
|
||||||
|
void (*mono_thread_set_main)(void *);
|
||||||
|
|
||||||
|
void *(*mono_jit_init_version)(const char *root_domain_name, const char *runtime_version);
|
||||||
|
void *(*mono_domain_assembly_open)(void *domain, const char *name);
|
||||||
|
void *(*mono_assembly_get_image)(void *assembly);
|
||||||
|
void *(*mono_runtime_invoke)(void *method, void *obj, void **params, void **exc);
|
||||||
|
|
||||||
|
void *(*mono_method_desc_new)(const char *name, int include_namespace);
|
||||||
|
void* (*mono_method_desc_search_in_image)(void* desc, void* image);
|
||||||
|
void *(*mono_method_desc_search_in_class)(void *desc, void *klass);
|
||||||
|
void (*mono_method_desc_free)(void *desc);
|
||||||
|
void *(*mono_method_signature)(void *method);
|
||||||
|
UINT32 (*mono_signature_get_param_count)(void *sig);
|
||||||
|
|
||||||
|
void (*mono_domain_set_config)(void *domain, char *base_dir, char *config_file_name);
|
||||||
|
void *(*mono_array_new)(void *domain, void *eclass, uintptr_t n);
|
||||||
|
void *(*mono_get_string_class)();
|
||||||
|
|
||||||
|
char *(*mono_assembly_getrootdir)();
|
||||||
|
|
||||||
|
// Additional funcs to bootstrap custom MONO
|
||||||
|
void (*mono_set_dirs)(const char* assembly_dir, const char* config_dir);
|
||||||
|
void (*mono_config_parse)(const char* filename);
|
||||||
|
void (*mono_set_assemblies_path)(const char* path);
|
||||||
|
void *(*mono_object_to_string)(void* obj, void** exc);
|
||||||
|
char *(*mono_string_to_utf8)(void* s);
|
||||||
|
|
||||||
|
void *(*mono_image_open_from_data_with_name)(void *data, DWORD data_len, int need_copy, void *status, int refonly,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
|
void* (*mono_get_exception_class)();
|
||||||
|
void* (*mono_object_get_virtual_method)(void* obj_raw, void* method);
|
||||||
|
|
||||||
|
void* (*mono_jit_parse_options)(int argc, const char** argv);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MONO_DEBUG_FORMAT_NONE,
|
||||||
|
MONO_DEBUG_FORMAT_MONO,
|
||||||
|
/* Deprecated, the mdb debugger is not longer supported. */
|
||||||
|
MONO_DEBUG_FORMAT_DEBUGGER
|
||||||
|
} MonoDebugFormat;
|
||||||
|
|
||||||
|
void* (*mono_debug_init)(MonoDebugFormat format);
|
||||||
|
void* (*mono_debug_domain_create)(void* domain);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Loads Mono C API function pointers so that the above definitions can be called.
|
||||||
|
* \param mono_lib Mono.dll module.
|
||||||
|
*/
|
||||||
|
void load_mono_functions(HMODULE mono_lib) {
|
||||||
|
// Enjoy the fact that C allows such sloppy casting
|
||||||
|
// In C++ you would have to cast to the precise function pointer type
|
||||||
|
#define GET_MONO_PROC(name) name = (void*)GetProcAddress(mono_lib, #name)
|
||||||
|
|
||||||
|
// Find and assign all our functions that we are going to use
|
||||||
|
GET_MONO_PROC(mono_domain_assembly_open);
|
||||||
|
GET_MONO_PROC(mono_assembly_get_image);
|
||||||
|
GET_MONO_PROC(mono_runtime_invoke);
|
||||||
|
GET_MONO_PROC(mono_jit_init_version);
|
||||||
|
GET_MONO_PROC(mono_method_desc_new);
|
||||||
|
GET_MONO_PROC(mono_method_desc_search_in_class);
|
||||||
|
GET_MONO_PROC(mono_method_desc_search_in_image);
|
||||||
|
GET_MONO_PROC(mono_method_desc_free);
|
||||||
|
GET_MONO_PROC(mono_method_signature);
|
||||||
|
GET_MONO_PROC(mono_signature_get_param_count);
|
||||||
|
GET_MONO_PROC(mono_array_new);
|
||||||
|
GET_MONO_PROC(mono_get_string_class);
|
||||||
|
GET_MONO_PROC(mono_assembly_getrootdir);
|
||||||
|
GET_MONO_PROC(mono_thread_current);
|
||||||
|
GET_MONO_PROC(mono_thread_set_main);
|
||||||
|
GET_MONO_PROC(mono_domain_set_config);
|
||||||
|
GET_MONO_PROC(mono_set_dirs);
|
||||||
|
GET_MONO_PROC(mono_config_parse);
|
||||||
|
GET_MONO_PROC(mono_set_assemblies_path);
|
||||||
|
GET_MONO_PROC(mono_object_to_string);
|
||||||
|
GET_MONO_PROC(mono_string_to_utf8);
|
||||||
|
GET_MONO_PROC(mono_image_open_from_data_with_name);
|
||||||
|
GET_MONO_PROC(mono_get_exception_class);
|
||||||
|
GET_MONO_PROC(mono_object_get_virtual_method);
|
||||||
|
GET_MONO_PROC(mono_jit_parse_options);
|
||||||
|
GET_MONO_PROC(mono_debug_init);
|
||||||
|
GET_MONO_PROC(mono_debug_domain_create);
|
||||||
|
|
||||||
|
#undef GET_MONO_PROC
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
wchar_t *widen(const char *str) {
|
||||||
|
const int reqsz = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
||||||
|
wchar_t *result = malloc(reqsz * sizeof(wchar_t));
|
||||||
|
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, str, -1, result, reqsz);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *narrow(const wchar_t *str) {
|
||||||
|
const int reqsz = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
|
||||||
|
char *result = malloc(reqsz * sizeof(char));
|
||||||
|
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, str, -1, result, reqsz, NULL, NULL);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue