segatools/fgohook/ftdi.c

228 lines
5.4 KiB
C

/*
SEGA 837-14509-02 USB -> Serial Adapter hook
The 837-15093-06 LED controller is connected to the ALLS with an adapter.
This tiny board has a FTDI FT232BL chip, and is referenced in schematics as
"USB-SER I/F BD".
The game queries the presence of the FTDI board itself, followed by a
registry check to see which port number is assigned to the FTDI board.
If these fail, the "CABINET LED" check on startup will always return "NG".
Credits:
OLEG
*/
#include <windows.h>
#include <setupapi.h>
#include <stdint.h>
#include <assert.h>
#include <stdio.h>
#include "fgohook/ftdi.h"
#include "hook/iohook.h"
#include "hook/table.h"
#include "hooklib/setupapi.h"
#include "util/dprintf.h"
static struct ftdi_config ftdi_cfg;
static BOOL WINAPI hook_SetupDiGetDeviceRegistryPropertyA(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
uint32_t Property,
uint32_t *PropertyRegDataType,
void *PropertyBuffer,
uint32_t PropertyBufferSize,
uint32_t *RequiredSize
);
static HKEY WINAPI hook_SetupDiOpenDevRegKey(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
uint32_t Scope,
uint32_t HwProfile,
uint32_t KeyType,
REGSAM samDesired
);
static LSTATUS WINAPI hook_RegQueryValueExA(
HKEY handle,
const char *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
static LSTATUS WINAPI hook_RegCloseKey(HKEY handle);
static BOOL (WINAPI *next_SetupDiGetDeviceRegistryPropertyA)(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
uint32_t Property,
uint32_t *PropertyRegDataType,
void *PropertyBuffer,
uint32_t PropertyBufferSize,
uint32_t *RequiredSize
);
static HKEY (WINAPI *next_SetupDiOpenDevRegKey)(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
uint32_t Scope,
uint32_t HwProfile,
uint32_t KeyType,
REGSAM samDesired
);
static LSTATUS (WINAPI *next_RegQueryValueExA)(
HKEY handle,
const char *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes);
static LSTATUS (WINAPI *next_RegCloseKey)(HKEY handle);
static const struct hook_symbol setupapi_syms[] = {
{
.name = "SetupDiGetDeviceRegistryPropertyA",
.patch = hook_SetupDiGetDeviceRegistryPropertyA,
.link = (void *) &next_SetupDiGetDeviceRegistryPropertyA,
}, {
.name = "SetupDiOpenDevRegKey",
.patch = hook_SetupDiOpenDevRegKey,
.link = (void *) &next_SetupDiOpenDevRegKey,
}
};
static const struct hook_symbol reg_syms[] = {
{
.name = "RegQueryValueExA",
.patch = hook_RegQueryValueExA,
.link = (void *) &next_RegQueryValueExA,
}, {
.name = "RegCloseKey",
.patch = hook_RegCloseKey,
.link = (void *) &next_RegCloseKey,
}
};
#define device_fake_key 0xDEADBEEF
static HANDLE ftdi_fd;
static char port_name[8];
HRESULT ftdi_hook_init(const struct ftdi_config *cfg, unsigned int port_no) {
HRESULT hr;
assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
if (cfg->port_no != 0) {
port_no = cfg->port_no;
}
hook_table_apply(
NULL,
"setupapi.dll",
setupapi_syms,
_countof(setupapi_syms));
hook_table_apply(
NULL,
"advapi32.dll",
reg_syms,
_countof(reg_syms));
memcpy(&ftdi_cfg, cfg, sizeof(*cfg));
hr = iohook_open_nul_fd(&ftdi_fd);
if (FAILED(hr)) {
return hr;
}
hr = setupapi_add_phantom_dev(&ftdi_guid, L"$ftdi");
if (FAILED(hr)) {
return hr;
}
sprintf(port_name, "COM%d", port_no);
dprintf("FTDI: Hook enabled.\n");
return S_OK;
}
static BOOL WINAPI hook_SetupDiGetDeviceRegistryPropertyA(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
uint32_t Property,
uint32_t *PropertyRegDataType,
void *PropertyBuffer,
uint32_t PropertyBufferSize,
uint32_t *RequiredSize
) {
if (!PropertyBuffer || PropertyBufferSize == 0) {
*RequiredSize = 16;
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
*PropertyRegDataType = 1;
return TRUE;
}
static HKEY WINAPI hook_SetupDiOpenDevRegKey(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
uint32_t Scope,
uint32_t HwProfile,
uint32_t KeyType,
REGSAM samDesired
) {
return (HKEY) device_fake_key;
}
static LSTATUS WINAPI hook_RegCloseKey(HKEY handle) {
if (handle == (HKEY) device_fake_key)
return ERROR_SUCCESS;
return next_RegCloseKey(handle);
}
static LSTATUS WINAPI hook_RegQueryValueExA(
HKEY handle,
const char *name,
void *reserved,
uint32_t *type,
void *bytes,
uint32_t *nbytes) {
if (handle == (HKEY) device_fake_key && !strcmp(name, "PortName")) {
strcpy(bytes, port_name);
*nbytes = 5;
*type = 1;
return ERROR_SUCCESS;
}
return next_RegQueryValueExA(
handle,
name,
reserved,
type,
bytes,
nbytes);
}