Compare commits

...

7 Commits

38 changed files with 2387 additions and 20 deletions

81
board/led15070-cmd.h Normal file
View File

@ -0,0 +1,81 @@
#pragma once
#include "board/led15070-frame.h"
/* Command IDs */
enum {
LED_15070_CMD_RESET = 0x10,
LED_15070_CMD_SET_INPUT = 0x28, // No known use case
LED_15070_CMD_SET_NORMAL_12BIT = 0x30, // TODO
LED_15070_CMD_SET_NORMAL_8BIT = 0x31,
LED_15070_CMD_SET_MULTI_FLASH_8BIT = 0x32,
LED_15070_CMD_SET_MULTI_FADE_8BIT = 0x33,
LED_15070_CMD_SET_PALETTE_7_NORMAL_LED = 0x34, // No known use case
LED_15070_CMD_SET_PALETTE_6_FLASH_LED = 0x35, // No known use case
LED_15070_CMD_SET_15DC_OUT = 0x36, // No known use case
LED_15070_CMD_SET_15GS_OUT = 0x37, // No known use case
LED_15070_CMD_SET_PSC_MAX = 0x38, // No known use case
LED_15070_CMD_SET_FET_OUTPUT = 0x39,
LED_15070_CMD_SET_GS_PALETTE = 0x3A,
LED_15070_CMD_DC_UPDATE = 0x3B,
LED_15070_CMD_GS_UPDATE = 0x3C,
LED_15070_CMD_ROTATE = 0x3E, // No known use case, wtf is this?
LED_15070_CMD_SET_DC_DATA = 0x3F,
LED_15070_CMD_EEPROM_WRITE = 0x7B,
LED_15070_CMD_EEPROM_READ = 0x7C,
LED_15070_CMD_ACK_ON = 0x7D,
LED_15070_CMD_ACK_OFF = 0x7E,
LED_15070_CMD_BOARD_INFO = 0xF0,
LED_15070_CMD_BOARD_STATUS = 0xF1,
LED_15070_CMD_FW_SUM = 0xF2,
LED_15070_CMD_PROTOCOL_VER = 0xF3,
LED_15070_CMD_TO_BOOT_MODE = 0xFD,
LED_15070_CMD_FW_UPDATE = 0xFE,
};
/* Response codes */
enum {
LED_15070_STATUS_OK = 0x01,
LED_15070_STATUS_SUM_ERR = 0x02,
LED_15070_STATUS_PARITY_ERR = 0x03,
LED_15070_STATUS_FRAMING_ERR = 0x04,
LED_15070_STATUS_OVERRUN_ERR = 0x05,
LED_15070_STATUS_BUFFER_OVERFLOW = 0x06,
};
enum {
LED_15070_REPORT_OK = 0x01,
LED_15070_REPORT_WAIT = 0x02,
LED_15070_REPORT_ERR1 = 0x03,
LED_15070_REPORT_ERR2 = 0x04,
};
/* Request data structures */
struct led15070_req_any {
struct led15070_hdr hdr;
uint8_t cmd;
uint8_t payload[256];
};
/* Response data structures */
struct led15070_resp_any {
struct led15070_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
uint8_t data[32];
};
struct led15070_resp_board_info {
struct led15070_hdr hdr;
uint8_t status;
uint8_t cmd;
uint8_t report;
char board_num[8];
uint8_t endcode; // Always 0xFF
uint8_t fw_ver;
};

194
board/led15070-frame.c Normal file
View File

@ -0,0 +1,194 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "board/led15070-frame.h"
#include "hook/iobuf.h"
static void led15070_frame_sync(struct iobuf *src);
static HRESULT led15070_frame_accept(const struct iobuf *dest);
static HRESULT led15070_frame_encode_byte(struct iobuf *dest, uint8_t byte);
/* Frame structure:
[0] Sync byte (0xE0)
[1] Destination address
[2] Source Address
[3] Length of data/payload
[4] Data/payload
For requests (host to board):
[0] Command
... Payload
For responses (board to host):
[0] Status
[1] Command
[2] Report
... Payload
[n] Checksum: Sum of all prior bytes (excluding sync byte)
Byte stuffing:
0xD0 is an escape byte. Un-escape the subsequent byte by adding 1. */
static void led15070_frame_sync(struct iobuf *src)
{
size_t i;
for (i = 0 ; i < src->pos && src->bytes[i] != 0xE0 ; i++);
src->pos -= i;
memmove(&src->bytes[0], &src->bytes[i], i);
}
static HRESULT led15070_frame_accept(const struct iobuf *dest)
{
uint8_t checksum;
size_t i;
if (dest->pos < 3 || dest->pos != dest->bytes[3] + 5) {
return S_FALSE;
}
checksum = 0;
for (i = 1 ; i < dest->pos - 1 ; i++) {
checksum += dest->bytes[i];
}
//dprintf("LED checksum %02x, expected %02x\n", checksum, dest->bytes[dest->pos - 1]);
if (checksum != dest->bytes[dest->pos - 1]) {
return HRESULT_FROM_WIN32(ERROR_CRC);
}
return S_OK;
}
HRESULT led15070_frame_decode(struct iobuf *dest, struct iobuf *src)
{
uint8_t byte;
bool escape;
size_t i;
HRESULT hr;
assert(dest != NULL);
assert(dest->bytes != NULL || dest->nbytes == 0);
assert(dest->pos <= dest->nbytes);
assert(src != NULL);
assert(src->bytes != NULL || src->nbytes == 0);
assert(src->pos <= src->nbytes);
led15070_frame_sync(src);
dest->pos = 0;
escape = false;
for (i = 0, hr = S_FALSE ; i < src->pos && hr == S_FALSE ; i++) {
/* Step the FSM to unstuff another byte */
byte = src->bytes[i];
if (dest->pos >= dest->nbytes) {
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
} else if (i == 0) {
dest->bytes[dest->pos++] = byte;
} else if (byte == 0xE0) {
hr = E_FAIL;
} else if (byte == 0xD0) {
if (escape) {
hr = E_FAIL;
}
escape = true;
} else if (escape) {
dest->bytes[dest->pos++] = byte + 1;
escape = false;
} else {
dest->bytes[dest->pos++] = byte;
}
/* Try to accept the packet we've built up so far */
if (SUCCEEDED(hr)) {
hr = led15070_frame_accept(dest);
}
}
/* Handle FSM terminal state */
if (hr != S_FALSE) {
/* Frame was either accepted or rejected, remove it from src */
memmove(&src->bytes[0], &src->bytes[i], src->pos - i);
src->pos -= i;
}
return hr;
}
HRESULT led15070_frame_encode(
struct iobuf *dest,
const void *ptr,
size_t nbytes)
{
const uint8_t *src;
uint8_t checksum;
uint8_t byte;
size_t i;
HRESULT hr;
assert(dest != NULL);
assert(dest->bytes != NULL || dest->nbytes == 0);
assert(dest->pos <= dest->nbytes);
assert(ptr != NULL);
src = ptr;
assert(nbytes >= 3 && src[0] == 0xE0 && src[3] + 4 == nbytes);
if (dest->pos >= dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = 0xE0;
checksum = 0;
// dprintf("%02x ", 0xe0);
for (i = 1 ; i < nbytes ; i++) {
byte = src[i];
checksum += byte;
// dprintf("%02x ", byte);
hr = led15070_frame_encode_byte(dest, byte);
if (FAILED(hr)) {
return hr;
}
}
// dprintf("%02x \n", checksum);
return led15070_frame_encode_byte(dest, checksum);
}
static HRESULT led15070_frame_encode_byte(struct iobuf *dest, uint8_t byte)
{
if (byte == 0xE0 || byte == 0xD0) {
if (dest->pos + 2 > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = 0xD0;
dest->bytes[dest->pos++] = byte - 1;
} else {
if (dest->pos + 1 > dest->nbytes) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
dest->bytes[dest->pos++] = byte;
}
return S_OK;
}

26
board/led15070-frame.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/iobuf.h"
enum {
LED_15070_FRAME_SYNC = 0xE0,
};
struct led15070_hdr {
uint8_t sync;
uint8_t dest_adr;
uint8_t src_adr;
uint8_t nbytes;
};
HRESULT led15070_frame_decode(struct iobuf *dest, struct iobuf *src);
HRESULT led15070_frame_encode(
struct iobuf *dest,
const void *ptr,
size_t nbytes);

1250
board/led15070.c Normal file

File diff suppressed because it is too large Load Diff

29
board/led15070.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct led15070_config {
bool enable;
unsigned int port_no;
char board_number[8];
uint8_t fw_ver;
uint16_t fw_sum;
wchar_t eeprom_path[MAX_PATH];
};
typedef HRESULT (*io_led_init_t)(void);
typedef void (*io_led_set_fet_output_t)(const uint8_t *rgb);
typedef void (*io_led_dc_update_t)(const uint8_t *rgb);
typedef void (*io_led_gs_update_t)(const uint8_t *rgb);
HRESULT led15070_hook_init(
const struct led15070_config *cfg,
io_led_init_t _led_init,
io_led_set_fet_output_t _led_set_fet_output,
io_led_dc_update_t _led_dc_update,
io_led_gs_update_t _led_gs_update,
unsigned int first_port,
unsigned int num_boards);

View File

@ -25,6 +25,11 @@ board_lib = static_library(
'led15093-frame.h',
'led15093.c',
'led15093.h',
'led15070-cmd.h',
'led15070-frame.c',
'led15070-frame.h',
'led15070.c',
'led15070.h',
'sg-cmd.c',
'sg-cmd.h',
'sg-frame.c',

View File

@ -1,3 +1,31 @@
/*
"Wonderland Wars" (carol*) hook
Devices:
JVS: 837-14572 "Type 3" I/O Board
[Satellite]
USB: "WinTouch" Controller Board
^ (DIPSW2 ON, Version 5.xx.xx or above)
COM1: 3M Touch Systems 78-0011-2353-4 Touch Controller Board
^ (DIPSW2 OFF)
COM10: TN32MSEC003S "Gen 1" Aime Reader
OR
837-15286 "Gen 2" Aime Reader
^ (Version 1.6x.xx or above)
COM11: 837-15070-02 LED Controller Board
COM12: 837-15312 Pen Controller I/O Board
[Terminal]
COM10: 837-15286 "Gen 2" Aime Reader
*: SEGA's abbreviation for Lewis Carroll, author of Alice's Adventures in
Wonderland.
*/
#include <windows.h>
#include <stdlib.h>

View File

@ -1,3 +1,15 @@
/*
"CHUNITHM" (chuni) hook
Devices
JVS: 837-14572 "Type 3" I/O Board
COM1: 837-15330 Ground Slider
COM10: 837-15093-06 LED Controller Board
COM11: 837-15093-06 LED Controller Board
COM12: TN32MSEC003S "Gen 1" Aime Reader
*/
#include <windows.h>
#include <stdlib.h>

View File

@ -1,3 +1,27 @@
/*
"CHUNITHM NEW" (chusan) hook
Devices
USB: 837-15257-02 "Type 4" I/O Board
COM1: 837-15330 Ground Slider
[CVT mode (DIPSW2 ON)]
COM2: 837-15093-06 LED Controller Board
COM3: 837-15093-06 LED Controller Board
COM4: 837-15286 "Gen 2" Aime Reader
[SP mode (DIPSW2 OFF)]
USB: 837-15067-02 USB Serial I/F Board
connected to
837-15093-06 LED Controller Board (COM20)
837-15093-06 LED Controller Board (COM21)
COM2: 200-6275 VFD GP1232A02A FUTABA Board
COM4: 837-15396 "Gen 3" Aime Reader
*/
#include <windows.h>
#include <stddef.h>

View File

@ -1,3 +1,16 @@
/*
"Card Maker" (cm) hook
Devices
USB: 837-15257-01 "Type 4" I/O Board
USB: 838-20006 "WinTouch" Controller Board
USB: 630-00009 Sinfonia CHC-C310 Printer
COM1: 837-15396 "Gen 3" Aime Reader
COM2: 200-6275 VFD GP1232A02A FUTABA Board
COM3: 220-5872 AS-6DB Coin Selector
*/
#include <windows.h>
#include <stdlib.h>
@ -55,6 +68,12 @@ static DWORD CALLBACK cm_pre_startup(void)
goto fail;
}
hr = cm_dll_init(&cm_hook_cfg.dll, cm_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&cm_hook_cfg.aime, 1, 1, cm_hook_mod);
if (FAILED(hr)) {
@ -67,12 +86,6 @@ static DWORD CALLBACK cm_pre_startup(void)
goto fail;
}
hr = cm_dll_init(&cm_hook_cfg.dll, cm_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = cm_io4_hook_init(&cm_hook_cfg.io4);
if (FAILED(hr)) {

View File

@ -43,6 +43,7 @@ default=127.0.0.1
; Chunithm is extremely picky about its LAN environment, so leaving this
; setting enabled is strongly recommended.
enable=1
; The final octet of the local host's IP address on the virtualized subnet (so,
; if the keychip subnet is `192.168.32.0` and this value is set to `11`, then the
; local host's virtualized LAN IP is `192.168.32.11`).

View File

@ -75,6 +75,25 @@ dipsw3=0
dipsw4=0
dipsw5=0
; -----------------------------------------------------------------------------
; LED settings
; -----------------------------------------------------------------------------
[led15070]
; Enable emulation of the 837-15070-02 controlled lights, which handle the
; cabinet and seat LEDs.
enable=1
; -----------------------------------------------------------------------------
; Misc. hooks settings
; -----------------------------------------------------------------------------
[indrun]
; Hooks to patch GameProject-Win64-Shipping.exe and IndRun.dll. This is needed
; to boot version 1.60.00 and up. The hooks are not needed for version 1.50.00
; and below.
enable=1
; -----------------------------------------------------------------------------
; Custom IO settings
; -----------------------------------------------------------------------------

View File

@ -1,3 +1,14 @@
/*
"Hatsune Miku Project DIVA Arcade " (diva) hook
Devices
JVS: 837-14572 "Type 3" I/O Board
COM1: 3M Touch Systems 78-0011-2353-4 Touch Controller Board
COM10: TN32MSEC003S "Gen 1" Aime Reader
COM11: 837-15275 Touch Slider
*/
#include <windows.h>
#include <stdlib.h>

View File

@ -1,3 +1,20 @@
/*
"Fate Grand/Order Arcade" (fgo) hook
Devices
USB: 837-15257 "Type 4" I/O Board
USB: 838-15405 "WinTouch" Controller Board
USB: 630-00008 Sinfonia CHC-C330 Printer
USB: 837-14509-02 USB-SER I/F BD Mini-B FTDI Board
connected to
837-15093-06 LED Controller Board
COM1: 200-6275 VFD GP1232A02A FUTABA Board
COM2: 837-15345 RFID Deck Reader Noard
COM3: 837-15396 "Gen 3" Aime Reader
COM4: 837-15347 RFID Reader/Writer Board (inside the printer)
*/
#include <windows.h>
#include <stdlib.h>

View File

@ -13,6 +13,43 @@
#include "platform/config.h"
#include "platform/platform.h"
void led15070_config_load(struct led15070_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
wchar_t tmpstr[16];
cfg->enable = GetPrivateProfileIntW(L"led15070", L"enable", 1, filename);
cfg->port_no = GetPrivateProfileIntW(L"led15070", L"portNo", 0, filename);
cfg->fw_ver = GetPrivateProfileIntW(L"led15070", L"fwVer", 0x90, filename);
/* TODO: Unknown, no firmware file available */
cfg->fw_sum = GetPrivateProfileIntW(L"led15070", L"fwSum", 0x0000, filename);
GetPrivateProfileStringW(
L"led15070",
L"boardNumber",
L"15070-02",
tmpstr,
_countof(tmpstr),
filename);
size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number));
for (int i = n; i < sizeof(cfg->board_number); i++)
{
cfg->board_number[i] = ' ';
}
GetPrivateProfileStringW(
L"led15070",
L"eepromPath",
L"DEVICE",
cfg->eeprom_path,
_countof(cfg->eeprom_path),
filename);
}
void idac_dll_config_load(
struct idac_dll_config *cfg,
const wchar_t *filename)
@ -29,6 +66,16 @@ void idac_dll_config_load(
filename);
}
void indrun_config_load(
struct indrun_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"indrun", L"enable", 1, filename);
}
void idac_hook_config_load(
struct idac_hook_config *cfg,
const wchar_t *filename)
@ -42,6 +89,8 @@ void idac_hook_config_load(
zinput_config_load(&cfg->zinput, filename);
dvd_config_load(&cfg->dvd, filename);
io4_config_load(&cfg->io4, filename);
led15070_config_load(&cfg->led15070, filename);
indrun_config_load(&cfg->indrun, filename);
}
void zinput_config_load(struct zinput_config *cfg, const wchar_t *filename)

View File

@ -4,11 +4,13 @@
#include <stddef.h>
#include "board/config.h"
#include "board/led15070.h"
#include "hooklib/dvd.h"
#include "idachook/idac-dll.h"
#include "idachook/zinput.h"
#include "idachook/indrun.h"
#include "platform/platform.h"
@ -19,6 +21,8 @@ struct idac_hook_config {
struct io4_config io4;
struct idac_dll_config dll;
struct zinput_config zinput;
struct led15070_config led15070;
struct indrun_config indrun;
};
void idac_dll_config_load(
@ -29,4 +33,10 @@ void idac_hook_config_load(
struct idac_hook_config *cfg,
const wchar_t *filename);
void zinput_config_load(struct zinput_config *cfg, const wchar_t *filename);
void zinput_config_load(
struct zinput_config *cfg,
const wchar_t *filename);
void indrun_config_load(
struct indrun_config *cfg,
const wchar_t *filename);

View File

@ -1,3 +1,16 @@
/*
"Initial D THE ARCADE" (idac) hook
Devices
USB: 837-15257 "Type 4" I/O Board
COM1: 838-15069 MOTOR DRIVE BD RS232/422 Board
COM2: 837-15070-02 IC BD LED Controller Board
COM3: 837-15286 "Gen 2" Aime Reader (DIPSW2 OFF)
OR
837-15396 "Gen 3" Aime Reader (DIPSW2 ON)
*/
#include <windows.h>
#include <shlwapi.h>
@ -53,13 +66,13 @@ static DWORD CALLBACK idac_pre_startup(void)
goto fail;
}
hr = sg_reader_hook_init(&idac_hook_cfg.aime, 3, 3, idac_hook_mod);
hr = idac_dll_init(&idac_hook_cfg.dll, idac_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = idac_dll_init(&idac_hook_cfg.dll, idac_hook_mod);
hr = sg_reader_hook_init(&idac_hook_cfg.aime, 3, 3, idac_hook_mod);
if (FAILED(hr)) {
goto fail;
@ -71,6 +84,20 @@ static DWORD CALLBACK idac_pre_startup(void)
goto fail;
}
hr = led15070_hook_init(&idac_hook_cfg.led15070, idac_dll.led_init,
idac_dll.led_set_fet_output, NULL, idac_dll.led_gs_update, 2, 1);
if (FAILED(hr)) {
goto fail;
}
/* Initialize native plugin DLL hooks
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the initialization. */
indrun_hook_init(&idac_hook_cfg.indrun);
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");

View File

@ -24,6 +24,18 @@ const struct dll_bind_sym idac_dll_syms[] = {
}, {
.sym = "idac_io_get_analogs",
.off = offsetof(struct idac_dll, get_analogs),
}, {
.sym = "idac_io_led_init",
.off = offsetof(struct idac_dll, led_init),
}, {
.sym = "idac_io_led_set_fet_output",
.off = offsetof(struct idac_dll, led_set_fet_output),
}, {
.sym = "idac_io_led_gs_update",
.off = offsetof(struct idac_dll, led_gs_update),
}, {
.sym = "idac_io_led_set_leds",
.off = offsetof(struct idac_dll, led_set_leds),
}
};

View File

@ -11,6 +11,10 @@ struct idac_dll {
void (*get_gamebtns)(uint8_t *gamebtn);
void (*get_shifter)(uint8_t *gear);
void (*get_analogs)(struct idac_io_analog_state *out);
HRESULT (*led_init)(void);
void (*led_set_fet_output)(const uint8_t *rgb);
void (*led_gs_update)(const uint8_t *rgb);
void (*led_set_leds)(const uint8_t *rgb);
};
struct idac_dll_config {

View File

@ -17,3 +17,7 @@ EXPORTS
idac_io_get_gamebtns
idac_io_get_shifter
idac_io_get_analogs
idac_io_led_init
idac_io_led_set_fet_output
idac_io_led_gs_update
idac_io_led_set_leds

260
idachook/indrun.c Normal file
View File

@ -0,0 +1,260 @@
#include <assert.h>
#include <stdbool.h>
#include "hook/table.h"
#include "hooklib/dll.h"
#include "util/dprintf.h"
#include "indrun.h"
static const wchar_t *target_modules[] = {
L"IndRun.dll",
};
static const size_t target_modules_len = _countof(target_modules);
static void dll_hook_insert_hooks(HMODULE target);
static void app_hook_insert_hooks(HMODULE target);
static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name);
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
static int WINAPI hook_GetSystemMetrics(int nIndex);
static int (WINAPI *next_GetSystemMetrics)(int nIndex);
static BOOL WINAPI hook_GetComputerNameW(LPWSTR lpBuffer, LPDWORD nSize);
static DWORD WINAPI hook_GetCurrentDirectoryW( DWORD nBufferLength, LPWSTR lpBuffer);
static BOOL WINAPI hook_GetVersionExW(LPOSVERSIONINFOW lpVersionInformation);
static int (WINAPI *next_GetVersionExW)(LPOSVERSIONINFOW lpVersionInformation);
static BOOL WINAPI hook_VerifyVersionInfoW(LPOSVERSIONINFOEXW lpVersionInformation, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
static BOOL (WINAPI *next_VerifyVersionInfoW)(LPOSVERSIONINFOEXW lpVersionInformation, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
static BOOL WINAPI hook_K32EnumProcesses(DWORD *lpidProcess, DWORD cb, LPDWORD lpcbNeeded);
static BOOL (WINAPI *next_K32EnumProcesses)(DWORD *lpidProcess, DWORD cb, LPDWORD lpcbNeeded);
static BOOL WINAPI hook_GetUserNameW(LPWSTR lpBuffer, LPDWORD pcbBuffer);
static const struct hook_symbol idac_app_user32_syms[] = {
{
.name = "GetSystemMetrics",
.patch = hook_GetSystemMetrics,
.link = (void **) &next_GetSystemMetrics,
}
};
static const struct hook_symbol idac_app_kernel32_syms[] = {
{
.name = "GetComputerNameW",
.patch = hook_GetComputerNameW,
},
{
.name = "GetCurrentDirectoryW",
.patch = hook_GetCurrentDirectoryW,
},
{
.name = "GetVersionExW",
.patch = hook_GetVersionExW,
.link = (void **) &next_GetVersionExW,
},
{
.name = "VerifyVersionInfoW",
.patch = hook_VerifyVersionInfoW,
.link = (void **) &next_VerifyVersionInfoW,
},
{
.name = "K32EnumProcesses",
.patch = hook_K32EnumProcesses,
.link = (void **) &next_K32EnumProcesses,
}
};
static const struct hook_symbol idac_app_advapi32_syms[] = {
{
.name = "GetUserNameW",
.patch = hook_GetUserNameW,
}
};
static const struct hook_symbol indrun_kernel32_syms[] = {
{
.name = "LoadLibraryW",
.patch = hook_LoadLibraryW,
.link = (void **) &next_LoadLibraryW,
}
};
void indrun_hook_init(struct indrun_config *cfg)
{
assert(cfg != NULL);
if (!cfg->enable) {
return;
}
dprintf("IDAC: Hooks enabled.\n");
// GameProject-Win64-Shipping.exe hooks
app_hook_insert_hooks(NULL);
// IndRun.dll hooks
dll_hook_insert_hooks(NULL);
}
static void dll_hook_insert_hooks(HMODULE target) {
hook_table_apply(
target,
"kernel32.dll",
indrun_kernel32_syms,
_countof(indrun_kernel32_syms));
}
void app_hook_insert_hooks(HMODULE target) {
hook_table_apply(
target,
"user32.dll",
idac_app_user32_syms,
_countof(idac_app_user32_syms));
hook_table_apply(
target,
"kernel32.dll",
idac_app_kernel32_syms,
_countof(idac_app_kernel32_syms));
hook_table_apply(
target,
"advapi32.dll",
idac_app_advapi32_syms,
_countof(idac_app_advapi32_syms));
}
static HMODULE WINAPI hook_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("IDAC: Hooked %S\n", target_module);
dll_hook_insert_hooks(result);
app_hook_insert_hooks(result);
}
}
return result;
}
static int WINAPI hook_GetSystemMetrics(int nIndex) {
int ret = next_GetSystemMetrics(nIndex);
// Disable mouse buttons detection
if (nIndex == SM_CMOUSEBUTTONS) {
dprintf("IDAC: GetSystemMetrics(%d) -> 0\n", nIndex);
return 0;
}
return ret;
}
static BOOL WINAPI hook_GetComputerNameW(LPWSTR lpBuffer, LPDWORD nSize) {
dprintf("IDAC: GetComputerNameW -> ACAE01A99999999\n");
// Fake the computer name as ACAE01A999999999
wcscpy(lpBuffer, L"ACAE01A999999999");
*nSize = _countof(L"ACAE01A99999999");
return TRUE;
}
static DWORD WINAPI hook_GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer) {
dprintf("IDAC: GetCurrentDirectoryW -> X:\\\n");
// Fake the current diretory as X:
wcscpy(lpBuffer, L"X");
return 1;
}
static BOOL WINAPI hook_GetVersionExW(LPOSVERSIONINFOW lpVersionInformation) {
int result = next_GetVersionExW(lpVersionInformation);
// Fake the version as Windows 10 1809
if (result) {
dprintf("IDAC: GetVersionExW -> Windows 10 1809\n");
lpVersionInformation->dwMajorVersion = 10;
lpVersionInformation->dwMinorVersion = 0;
lpVersionInformation->dwBuildNumber = 17763;
return TRUE;
}
return result;
}
static BOOL WINAPI hook_GetUserNameW(LPWSTR lpBuffer, LPDWORD pcbBuffer) {
dprintf("IDAC: GetUserNameW -> AppUser\n");
// Fake the user name as AppUser
wcscpy(lpBuffer, L"AppUser");
*pcbBuffer = _countof(L"AppUser");
return TRUE;
}
static BOOL WINAPI hook_VerifyVersionInfoW(LPOSVERSIONINFOEXW lpVersionInformation, DWORD dwTypeMask, DWORDLONG dwlConditionMask) {
BOOL result = next_VerifyVersionInfoW(lpVersionInformation, dwTypeMask, dwlConditionMask);
// Fake the version as Windows 10 1809
if (lpVersionInformation->dwBuildNumber == 17763) {
dprintf("IDAC: VerifyVersionInfoW -> Windows 10 1809\n");
return TRUE;
}
return result;
}
static BOOL WINAPI hook_K32EnumProcesses(DWORD *lpidProcess, DWORD cb, LPDWORD lpcbNeeded) {
BOOL result = next_K32EnumProcesses(lpidProcess, cb, lpcbNeeded);
// Rteurn an empy process list
dprintf("IDAC: K32EnumProcesses -> NULL\n");
lpidProcess = NULL;
*lpcbNeeded = 0;
return TRUE;
}

9
idachook/indrun.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <windows.h>
struct indrun_config {
bool enable;
};
void indrun_hook_init(struct indrun_config *cfg);

View File

@ -11,10 +11,12 @@
#include "util/dprintf.h"
static HRESULT idac_io4_poll(void *ctx, struct io4_state *state);
static HRESULT idac_io4_write_gpio(uint8_t* payload, size_t len);
static uint16_t coins;
static const struct io4_ops idac_io4_ops = {
.poll = idac_io4_poll,
.write_gpio = idac_io4_write_gpio
};
static const uint16_t idac_gear_signals[] = {
@ -128,3 +130,34 @@ static HRESULT idac_io4_poll(void *ctx, struct io4_state *state)
return S_OK;
}
static HRESULT idac_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 IDAC, 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 15070 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 ON/OFF values.
uint8_t rgb_out[6] = {
lights_data & IDAC_IO_LED_START ? 0xFF : 0x00,
lights_data & IDAC_IO_LED_VIEW_CHANGE ? 0xFF : 0x00,
lights_data & IDAC_IO_LED_UP ? 0xFF : 0x00,
lights_data & IDAC_IO_LED_DOWN ? 0xFF : 0x00,
lights_data & IDAC_IO_LED_RIGHT ? 0xFF : 0x00,
lights_data & IDAC_IO_LED_LEFT ? 0xFF : 0x00,
};
idac_dll.led_set_leds(rgb_out);
return S_OK;
}

View File

@ -28,5 +28,7 @@ shared_library(
'io4.h',
'zinput.c',
'zinput.h',
'indrun.c',
'indrun.h',
],
)

View File

@ -19,7 +19,7 @@ static bool idac_io_coin;
uint16_t idac_io_get_api_version(void)
{
return 0x0100;
return 0x0101;
}
HRESULT idac_io_init(void)
@ -118,3 +118,44 @@ void idac_io_get_analogs(struct idac_io_analog_state *out)
out->accel = tmp.accel;
out->brake = tmp.brake;
}
HRESULT idac_io_led_init(void)
{
return S_OK;
}
void idac_io_led_set_fet_output(const uint8_t *rgb)
{
#if 0
dprintf("IDAC LED: LEFT SEAT LED: %02X\n", rgb[0]);
dprintf("IDAC LED: RIGHT SEAT LED: %02X\n", rgb[1]);
#endif
return;
}
void idac_io_led_gs_update(const uint8_t *rgb)
{
#if 0
for (int i = 0; i < 9; i++) {
dprintf("IDAC LED: LED %d: %02X %02X %02X Speed: %02X\n",
i, rgb[i * 4], rgb[i * 4 + 1], rgb[i * 4 + 2], rgb[i * 4 + 3]);
}
#endif
return;
}
void idac_io_led_set_leds(const uint8_t *rgb)
{
#if 0
dprintf("IDAC LED: START: %02X\n", rgb[0]);
dprintf("IDAC LED: VIEW CHANGE: %02X\n", rgb[1]);
dprintf("IDAC LED: UP: %02X\n", rgb[2]);
dprintf("IDAC LED: DOWN: %02X\n", rgb[3]);
dprintf("IDAC LED: RIGHT: %02X\n", rgb[4]);
dprintf("IDAC LED: LEFT: %02X\n", rgb[5]);
#endif
return;
}

View File

@ -6,3 +6,7 @@ EXPORTS
idac_io_get_gamebtns
idac_io_get_shifter
idac_io_get_analogs
idac_io_led_init
idac_io_led_set_fet_output
idac_io_led_gs_update
idac_io_led_set_leds

View File

@ -19,6 +19,17 @@ enum {
IDAC_IO_GAMEBTN_VIEW_CHANGE = 0x20,
};
enum {
/* These are the bitmasks to use when checking which
lights are triggered on incoming IO4 GPIO writes. */
IDAC_IO_LED_START = 1 << 31,
IDAC_IO_LED_VIEW_CHANGE = 1 << 30,
IDAC_IO_LED_UP = 1 << 25,
IDAC_IO_LED_DOWN = 1 << 24,
IDAC_IO_LED_LEFT = 1 << 23,
IDAC_IO_LED_RIGHT = 1 << 22,
};
struct idac_io_analog_state {
/* Current steering wheel position, where zero is the centered position.
@ -92,4 +103,60 @@ void idac_io_get_analogs(struct idac_io_analog_state *out);
Minimum API version: 0x0100 */
void idac_io_get_shifter(uint8_t *gear);
void idac_io_get_shifter(uint8_t *gear);
/* Initialize LED emulation. This function will be called before any
other idac_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 idac_io_led_init(void);
/* Update the FET outputs. rgb is a pointer to an array up to 3 bytes.
The following bits are used to control the FET outputs:
[0]: LEFT SEAT LED
[1]: RIGHT SEAT LED
The LED is truned on when the byte is 255 and turned off when the byte is 0.
Minimum API version: 0x0101 */
void idac_io_led_set_fet_output(const uint8_t *rgb);
/* Update the RGB LEDs. rgb is a pointer to an array up to 32 * 4 = 128 bytes.
The LEDs are laid out as follows:
[0]: LEFT UP LED
[1-2]: LEFT CENTER LED
[3]: LEFT DOWN LED
[5]: RIGHT UP LED
[6-7]: RIGHT CENTER LED
[8]: RIGHT DOWN LED
Each rgb value is comprised for 4 bytes in the order of R, G, B, Speed.
Speed is a value from 0 to 255, where 0 is the fastest speed and 255 is the slowest.
Minimum API version: 0x0101 */
void idac_io_led_gs_update(const uint8_t *rgb);
/* Update the cabinet button LEDs. rgb is a pointer to an array up to 6 bytes.
The LEDs are laid out as follows:
[0]: START LED
[1]: VIEW CHANGE LED
[2]: UP LED
[3]: DOWN LED
[4]: RIGHT LED
[5]: LEFT LED
The LED is turned on when the byte is 255 and turned off when the byte is 0.
Minimum API version: 0x0101 */
void idac_io_led_set_leds(const uint8_t *rgb);

View File

@ -1,3 +1,14 @@
/*
"Initial D ARCADE STAGE Zero" (idz) hook
Devices
JVS: 837-15257 "Type 4" I/O Board
COM1: 838-15069 MOTOR DRIVE BD RS232/422 Board
COM10: 837-15286 "Gen 2" Aime Reader
COM11: 837-15070-02 IC BD LED Controller Board
*/
#include <windows.h>
#include <shlwapi.h>

View File

@ -1,3 +1,22 @@
/*
"maimai DX" (mai2) hook
Devices
USB: 837-15257-01 "Type 4" I/O Board
USB: 2 * 601-13216 USB "QR Code" Camera (SDEZ2, SDEZ3)
USB: 601-13249 USB "Player" Camera (SDEZ1)
USB: 837-15067-02 IC BD USB to Serial 232
connected to
837-15070-04 LED Board Controller (COM21)
837-15070-04 LED Board Controller (COM23)
COM1: 837-15396 "Gen 3" Aime Reader
COM2: 200-6275 VFD GP1232A02A FUTABA Board
COM3: 509-6483 Touch Panel Controller
COM4: 509-6483 Touch Panel Controller
*/
#include <windows.h>
#include "board/io4.h"
@ -5,6 +24,8 @@
#include "board/vfd.h"
#include "hook/process.h"
#include "hook/table.h"
#include "hook/iohook.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"

View File

@ -1,3 +1,16 @@
/*
"WACCA" (mercury) hook
Devices
USB: 837-15257-01 "Type 4" I/O Board
USB: 14-1497-R "Elisabeth" LED Board Controller
COM1: 837-15396 "Gen 3" Aime Reader
COM2: 200-6275 VFD GP1232A02A FUTABA Board
COM3: PSS-7135-L02-01 "Left Side" Touch Board
COM4: PSS-7135-L02-01 "Right Sdde" Touch Board
*/
#include <windows.h>
#include "board/io4.h"

View File

@ -1,3 +1,15 @@
/*
"O.N.G.E.K.I." (mu3) hook
Devices
USB: 837-15257-01 "Type 4" I/O Board
USB: 3 * 601-13216 USB "QR Code" Camera (SDDT1-SDDT3)
COM1: 837-15396 "Gen 3" Aime Reader
COM2: 200-6275 VFD GP1232A02A FUTABA Board
COM3: 837-15093-06 LED Controller Board
*/
#include <windows.h>
#include <stdlib.h>

View File

@ -151,7 +151,7 @@ HRESULT mu3_io_led_init(void);
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.
to use the amdaemon process as the main one and the mu3 process as a sub one.
Minimum API version: 0x0101 */

View File

@ -105,6 +105,21 @@ HRESULT dns_platform_hook_init(const struct dns_config *cfg)
hr = dns_hook_push(L"shop.tfps.thincacloud.com", cfg->startup);
if (FAILED(hr)) {
return hr;
}
// CHN
// PowerOn
hr = dns_hook_push(L"at.sys-all.cn", cfg->startup);
if (FAILED(hr)) {
return hr;
}
// WeChat AimeDB Server
hr = dns_hook_push(L"ai.sys-all.cn", cfg->aimedb);
if (FAILED(hr)) {
return hr;
}

View File

@ -76,7 +76,7 @@ HRESULT platform_hook_init(
return hr;
}
hr = vfs_hook_init(&cfg->vfs);
hr = vfs_hook_init(&cfg->vfs, game_id);
if (FAILED(hr)) {
return hr;

View File

@ -9,6 +9,9 @@
#include "hooklib/path.h"
#include "hooklib/reg.h"
#include "hook/procaddr.h"
#include "hook/table.h"
#include "platform/vfs.h"
#include "util/dprintf.h"
@ -31,6 +34,26 @@ static HRESULT vfs_path_hook_option(
static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes);
static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes);
static wchar_t* hook_System_getAppRootPath();
static wchar_t* (*next_System_getAppRootPath)();
static wchar_t* hook_AppImage_getOptionMountRootPath();
static wchar_t* (*next_AppImage_getOptionMountRootPath)();
static const struct hook_symbol amdaemon_syms[] = {
{
.name = "System_getAppRootPath",
.patch = hook_System_getAppRootPath,
.link = (void **) &next_System_getAppRootPath,
},
{
.name = "AppImage_getOptionMountRootPath",
.patch = hook_AppImage_getOptionMountRootPath,
.link = (void **) &next_AppImage_getOptionMountRootPath,
},
};
static wchar_t game[5] = {0};
static wchar_t vfs_nthome_real[MAX_PATH];
static const wchar_t vfs_nthome[] = L"C:\\Documents and Settings\\AppUser";
static const size_t vfs_nthome_len = _countof(vfs_nthome) - 1;
@ -55,7 +78,7 @@ static const struct reg_hook_val vfs_reg_vals[] = {
static struct vfs_config vfs_config;
HRESULT vfs_hook_init(const struct vfs_config *config)
HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id)
{
wchar_t temp[MAX_PATH];
size_t nthome_len;
@ -68,6 +91,8 @@ HRESULT vfs_hook_init(const struct vfs_config *config)
return S_FALSE;
}
mbstowcs(game, game_id, 4);
if (config->amfs[0] == L'\0') {
dprintf("Vfs: FATAL: AMFS path not specified in INI file\n");
@ -175,6 +200,13 @@ HRESULT vfs_hook_init(const struct vfs_config *config)
return hr;
}
proc_addr_table_push(
NULL,
"amdaemon_api.dll",
amdaemon_syms,
_countof(amdaemon_syms)
);
return S_OK;
}
@ -287,8 +319,6 @@ static HRESULT vfs_path_hook(const wchar_t *src, wchar_t *dest, size_t *count)
}
switch (src[0]) {
// case L'D': // later AMDaemon versions default to D: for AMFS if it can't find it
// case L'd':
case L'e':
case L'E':
redir = vfs_config.amfs;
@ -479,3 +509,21 @@ static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"Y:\\");
}
static wchar_t* hook_System_getAppRootPath()
{
wchar_t *path = malloc(sizeof(wchar_t) * MAX_PATH);
wcscpy_s(path, MAX_PATH, vfs_config.appdata);
wcscat_s(path, MAX_PATH, game);
wcscat_s(path, MAX_PATH, L"\\");
return path;
}
static wchar_t* hook_AppImage_getOptionMountRootPath()
{
wchar_t *path = malloc(sizeof(wchar_t) * MAX_PATH);
wcscpy_s(path, MAX_PATH, vfs_config.option);
return path;
}

View File

@ -12,4 +12,4 @@ struct vfs_config {
wchar_t option[MAX_PATH];
};
HRESULT vfs_hook_init(const struct vfs_config *config);
HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id);

View File

@ -1,3 +1,18 @@
/*
"SEGA World Drivers Championship" (swdc) hook
Devices
USB: 837-15257 "Type 4" I/O Board
USB: 838-15415 Indicator BD Main Board (COM21)
WITH
838-15416 Indicator BD LED Board
COM1: 838-15069 MOTOR DRIVE BD RS232/422 board
COM2: 837-15396 "Gen 3" Aime reader
COM3: 837-15070-04 IC BD LED controller board
COM4: 200-6275 VFD GP1232A02A FUTABA board
*/
#include <windows.h>
#include <shlwapi.h>

View File

@ -24,11 +24,13 @@ static const wchar_t *target_modules[] = {
L"mono.dll",
L"mono-2.0-bdwgc.dll",
L"cri_ware_unity.dll",
L"amdaemon_api.dll",
L"SerialPortAPI.dll",
L"C300usb.dll",
L"C300FWDLusb.dll",
L"apmled.dll",
L"apmmount.dll",
L"HKBSys_api.dll",
L"amptw.dll"
};
static const size_t target_modules_len = _countof(target_modules);
@ -138,9 +140,7 @@ static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name)
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);