create theatrhythm dll

This commit is contained in:
Hay1tsme 2024-02-10 23:38:24 -05:00
parent ae23b1f174
commit 0263157f1a
79 changed files with 686 additions and 3322 deletions

View File

@ -10,6 +10,9 @@
"platform.h": "c",
"stdbool.h": "c",
"dprintf.h": "c",
"slider-frame.h": "c"
"slider-frame.h": "c",
"aime-dll.h": "c",
"sg-reader.h": "c",
"reg.h": "c"
}
}

View File

@ -1,142 +1,33 @@
$(BUILD_DIR_ZIP)/chuni.zip:
$(BUILD_DIR_ZIP)/siva.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/chuni
$(V)mkdir -p $(BUILD_DIR_ZIP)/chuni/DEVICE
$(V)mkdir -p $(BUILD_DIR_ZIP)/siva
$(V)mkdir -p $(BUILD_DIR_ZIP)/siva/DEVICE
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_32)/chunihook/chunihook.dll \
$(DIST_DIR)/chuni/taitools.ini \
$(DIST_DIR)/chuni/start.bat \
$(BUILD_DIR_ZIP)/chuni
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/chuni/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/chuni/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/chuni ; zip -r ../chuni.zip *
$(BUILD_DIR_ZIP)/cxb.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/cxb
$(V)mkdir -p $(BUILD_DIR_ZIP)/cxb/DEVICE
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_32)/cxbhook/cxbhook.dll \
$(DIST_DIR)/cxb/taitools.ini \
$(DIST_DIR)/cxb/start.bat \
$(BUILD_DIR_ZIP)/cxb
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/cxb/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/cxb/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/cxb ; zip -r ../cxb.zip *
$(BUILD_DIR_ZIP)/diva.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/diva
$(V)mkdir -p $(BUILD_DIR_ZIP)/diva/DEVICE
$(BUILD_DIR_32)/sivahook/sivahook.dll \
$(BUILD_DIR_ZIP)/siva
$(V)mv $(BUILD_DIR_ZIP)/siva/inject.exe \
$(BUILD_DIR_ZIP)/siva/inject_32.exe
$(V)mv $(BUILD_DIR_ZIP)/siva/sivahook.dll \
$(BUILD_DIR_ZIP)/siva/sivahook_32.dll
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/divahook/divahook.dll \
$(DIST_DIR)/diva/taitools.ini \
$(DIST_DIR)/diva/start.bat \
$(BUILD_DIR_ZIP)/diva
$(BUILD_DIR_64)/sivahook/sivahook.dll \
$(DIST_DIR)/siva/taitools.ini \
$(DIST_DIR)/siva/start.bat \
$(BUILD_DIR_ZIP)/siva
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/diva/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/diva/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/diva ; zip -r ../diva.zip *
$(BUILD_DIR_ZIP)/carol.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/carol
$(V)mkdir -p $(BUILD_DIR_ZIP)/carol/DEVICE
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_32)/carolhook/carolhook.dll \
$(DIST_DIR)/carol/taitools.ini \
$(DIST_DIR)/carol/start.bat \
$(BUILD_DIR_ZIP)/carol
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/carol/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/carol/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/carol ; zip -r ../carol.zip *
$(BUILD_DIR_ZIP)/idz.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/idz
$(V)mkdir -p $(BUILD_DIR_ZIP)/idz/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/idzhook/idzhook.dll \
$(DIST_DIR)/idz/taitools.ini \
$(DIST_DIR)/idz/start.bat \
$(BUILD_DIR_ZIP)/idz
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/idz/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/idz/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/idz ; zip -r ../idz.zip *
$(BUILD_DIR_ZIP)/mercury.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/mercury
$(V)mkdir -p $(BUILD_DIR_ZIP)/mercury/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/mercuryhook/mercuryhook.dll \
$(DIST_DIR)/mercury/taitools.ini \
$(DIST_DIR)/mercury/start.bat \
$(BUILD_DIR_ZIP)/mercury
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/mercury/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/mercury/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mercury ; zip -r ../mercury.zip *
$(BUILD_DIR_ZIP)/mu3.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/mu3
$(V)mkdir -p $(BUILD_DIR_ZIP)/mu3/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/mu3hook/mu3hook.dll \
$(DIST_DIR)/mu3/taitools.ini \
$(DIST_DIR)/mu3/start.bat \
$(BUILD_DIR_ZIP)/mu3
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/mu3/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/mu3/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mu3 ; zip -r ../mu3.zip *
$(BUILD_DIR_ZIP)/mai2.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/mai2
$(V)mkdir -p $(BUILD_DIR_ZIP)/mai2/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/mai2hook/mai2hook.dll \
$(DIST_DIR)/mai2/taitools.ini \
$(DIST_DIR)/mai2/start.bat \
$(BUILD_DIR_ZIP)/mai2
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/mai2/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/mai2/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mai2 ; zip -r ../mai2.zip *
$(BUILD_DIR_ZIP)/siva/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/siva/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/siva ; zip -r ../siva.zip *
$(BUILD_DIR_ZIP)/doc.zip: \
$(DOC_DIR)/config \
$(DOC_DIR)/chunihook.md \
$(DOC_DIR)/idzhook.md \
| $(zipdir)/
$(V)echo ... $@
$(V)zip -r $@ $^
$(BUILD_DIR_ZIP)/taitools.zip: \
$(BUILD_DIR_ZIP)/chuni.zip \
$(BUILD_DIR_ZIP)/cxb.zip \
$(BUILD_DIR_ZIP)/carol.zip \
$(BUILD_DIR_ZIP)/diva.zip \
$(BUILD_DIR_ZIP)/doc.zip \
$(BUILD_DIR_ZIP)/idz.zip \
$(BUILD_DIR_ZIP)/mercury.zip \
$(BUILD_DIR_ZIP)/mu3.zip \
$(BUILD_DIR_ZIP)/mai2.zip \
$(BUILD_DIR_ZIP)/siva.zip \
CHANGELOG.md \
README.md \

View File

@ -1,112 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "board/aime-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym aime_dll_syms[] = {
{
.sym = "aime_io_init",
.off = offsetof(struct aime_dll, init),
}, {
.sym = "aime_io_nfc_poll",
.off = offsetof(struct aime_dll, nfc_poll),
}, {
.sym = "aime_io_nfc_get_aime_id",
.off = offsetof(struct aime_dll, nfc_get_aime_id),
}, {
.sym = "aime_io_nfc_get_felica_id",
.off = offsetof(struct aime_dll, nfc_get_felica_id),
}, {
.sym = "aime_io_led_set_color",
.off = offsetof(struct aime_dll, led_set_color),
}
};
struct aime_dll aime_dll;
// Copypasta DLL binding and diagnostic message boilerplate.
// Not much of this lends itself to being easily factored out. Also there
// will be a lot of API-specific branching code here eventually as new API
// versions get defined, so even though these functions all look the same
// now this won't remain the case forever.
HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self)
{
uint16_t (*get_api_version)(void);
const struct dll_bind_sym *sym;
HINSTANCE owned;
HINSTANCE src;
HRESULT hr;
assert(cfg != NULL);
assert(self != NULL);
if (cfg->path[0] != L'\0') {
owned = LoadLibraryW(cfg->path);
if (owned == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("NFC Assembly: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("NFC Assembly: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "aime_io_get_api_version");
if (get_api_version != NULL) {
aime_dll.api_version = get_api_version();
} else {
aime_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose aime_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (aime_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("NFC Assembly: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Taitools.\n",
aime_dll.api_version);
goto end;
}
sym = aime_dll_syms;
hr = dll_bind(&aime_dll, src, &sym, _countof(aime_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("NFC Assembly: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
goto end;
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}
}
owned = NULL;
end:
if (owned != NULL) {
FreeLibrary(owned);
}
return hr;
}

View File

@ -1,25 +0,0 @@
#pragma once
#include <windows.h>
#include "aimeio/aimeio.h"
struct aime_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*nfc_poll)(uint8_t unit_no);
HRESULT (*nfc_get_aime_id)(
uint8_t unit_no,
uint8_t *luid,
size_t luid_size);
HRESULT (*nfc_get_felica_id)(uint8_t unit_no, uint64_t *IDm);
void (*led_set_color)(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b);
};
struct aime_dll_config {
wchar_t path[MAX_PATH];
};
extern struct aime_dll aime_dll;
HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self);

View File

@ -5,32 +5,7 @@
#include <stddef.h>
#include <stdlib.h>
#include "board/aime-dll.h"
#include "board/config.h"
#include "board/sg-reader.h"
static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"aimeio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
aime_dll_config_load(&cfg->dll, filename);
cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename);
}
void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
{

View File

@ -4,7 +4,5 @@
#include <stddef.h>
#include "board/io4.h"
#include "board/sg-reader.h"
void aime_config_load(struct aime_config *cfg, const wchar_t *filename);
void io4_config_load(struct io4_config *cfg, const wchar_t *filename);

View File

@ -10,8 +10,6 @@ board_lib = static_library(
iccard_lib,
],
sources : [
'aime-dll.c',
'aime-dll.h',
'config.c',
'config.h',
'guid.c',
@ -30,7 +28,5 @@ board_lib = static_library(
'sg-nfc.c',
'sg-nfc.h',
'sg-nfc-cmd.h',
'sg-reader.c',
'sg-reader.h',
],
)

View File

@ -1,186 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include "board/aime-dll.h"
#include "board/sg-led.h"
#include "board/sg-nfc.h"
#include "board/sg-reader.h"
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT sg_reader_handle_irp(struct irp *irp);
static HRESULT sg_reader_handle_irp_locked(struct irp *irp);
static HRESULT sg_reader_nfc_poll(void *ctx);
static HRESULT sg_reader_nfc_get_aime_id(
void *ctx,
uint8_t *luid,
size_t luid_size);
static HRESULT sg_reader_nfc_get_felica_id(void *ctx, uint64_t *IDm);
static void sg_reader_led_set_color(void *ctx, uint8_t r, uint8_t g, uint8_t b);
static const struct sg_nfc_ops sg_reader_nfc_ops = {
.poll = sg_reader_nfc_poll,
.get_aime_id = sg_reader_nfc_get_aime_id,
.get_felica_id = sg_reader_nfc_get_felica_id,
};
static const struct sg_led_ops sg_reader_led_ops = {
.set_color = sg_reader_led_set_color,
};
static CRITICAL_SECTION sg_reader_lock;
static bool sg_reader_started;
static HRESULT sg_reader_start_hr;
static struct uart sg_reader_uart;
static uint8_t sg_reader_written_bytes[520];
static uint8_t sg_reader_readable_bytes[520];
static struct sg_nfc sg_reader_nfc;
static struct sg_led sg_reader_led;
HRESULT sg_reader_hook_init(
const struct aime_config *cfg,
unsigned int port_no,
HINSTANCE self)
{
HRESULT hr;
assert(cfg != NULL);
assert(self != NULL);
if (!cfg->enable) {
return S_FALSE;
}
hr = aime_dll_init(&cfg->dll, self);
if (FAILED(hr)) {
return hr;
}
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, NULL);
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, NULL);
InitializeCriticalSection(&sg_reader_lock);
uart_init(&sg_reader_uart, port_no);
sg_reader_uart.written.bytes = sg_reader_written_bytes;
sg_reader_uart.written.nbytes = sizeof(sg_reader_written_bytes);
sg_reader_uart.readable.bytes = sg_reader_readable_bytes;
sg_reader_uart.readable.nbytes = sizeof(sg_reader_readable_bytes);
return iohook_push_handler(sg_reader_handle_irp);
}
static HRESULT sg_reader_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&sg_reader_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&sg_reader_lock);
hr = sg_reader_handle_irp_locked(irp);
LeaveCriticalSection(&sg_reader_lock);
return hr;
}
static HRESULT sg_reader_handle_irp_locked(struct irp *irp)
{
HRESULT hr;
#if 0
if (irp->op == IRP_OP_WRITE) {
dprintf("WRITE:\n");
dump_const_iobuf(&irp->write);
}
#endif
#if 0
if (irp->op == IRP_OP_READ) {
dprintf("READ:\n");
dump_iobuf(&sg_reader_uart.readable);
}
#endif
if (irp->op == IRP_OP_OPEN) {
/* Unfortunately the card reader UART gets opened and closed
repeatedly */
if (!sg_reader_started) {
dprintf("NFC Assembly: Starting backend DLL\n");
hr = aime_dll.init();
sg_reader_started = true;
sg_reader_start_hr = hr;
if (FAILED(hr)) {
dprintf("NFC Assembly: Backend error: %x\n", (int) hr);
return hr;
}
} else {
hr = sg_reader_start_hr;
if (FAILED(hr)) {
return hr;
}
}
}
hr = uart_handle_irp(&sg_reader_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
sg_nfc_transact(
&sg_reader_nfc,
&sg_reader_uart.readable,
sg_reader_uart.written.bytes,
sg_reader_uart.written.pos);
sg_led_transact(
&sg_reader_led,
&sg_reader_uart.readable,
sg_reader_uart.written.bytes,
sg_reader_uart.written.pos);
sg_reader_uart.written.pos = 0;
return hr;
}
static HRESULT sg_reader_nfc_poll(void *ctx)
{
return aime_dll.nfc_poll(0);
}
static HRESULT sg_reader_nfc_get_aime_id(
void *ctx,
uint8_t *luid,
size_t luid_size)
{
return aime_dll.nfc_get_aime_id(0, luid, luid_size);
}
static HRESULT sg_reader_nfc_get_felica_id(void *ctx, uint64_t *IDm)
{
return aime_dll.nfc_get_felica_id(0, IDm);
}
static void sg_reader_led_set_color(void *ctx, uint8_t r, uint8_t g, uint8_t b)
{
aime_dll.led_set_color(0, r, g, b);
}

View File

@ -1,17 +0,0 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include "board/aime-dll.h"
struct aime_config {
struct aime_dll_config dll;
bool enable;
};
HRESULT sg_reader_hook_init(
const struct aime_config *cfg,
unsigned int port_no,
HINSTANCE self);

View File

@ -1,48 +0,0 @@
[vfs]
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
amfs=
; Insert the path to the game Option directory here (contains Axxx directories)
option=
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=
[dns]
; Insert the hostname or IP address of the server you wish to use here.
; Note that 127.0.0.1, localhost etc are specifically rejected.
default=127.0.0.1
[netenv]
; Simulate an ideal LAN environment.
enable=1
[gpio]
dipsw1=1
[keychip]
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168.
subnet=192.168.126.0
[gfx]
; Force the game to run windowed.
windowed=1
; Add a frame to the game window if running windowed.
framed=1
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
monitor=0
[aimeio]
; To use a custom card reader IO DLL enter its path here.
; Leave empty if you want to use Taitools built-in keyboard input.
path=
[io3]
; Test button virtual-key code. Default is the 1 key.
test=0x31
; Service button virtual-key code. Default is the 2 key.
service=0x32
; Keyboard button to increment coin counter. Default is the 3 key.
coin=0x33

13
dist/carol/start.bat vendored
View File

@ -1,13 +0,0 @@
@echo off
pushd %~dp0
taskkill /f /im aimeReaderHost.exe > nul 2>&1
start /min inject -d -k carolhook.dll aimeReaderHost.exe -p 10
inject -d -k carolhook.dll carol_nu.exe
taskkill /f /im aimeReaderHost.exe > nul 2>&1
echo.
echo Game processes have terminated
pause

View File

@ -1,80 +0,0 @@
[vfs]
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
amfs=
; Insert the path to the game Option directory here (contains Axxx directories)
option=
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=
[dns]
; Insert the hostname or IP address of the server you wish to use here.
; Note that 127.0.0.1, localhost etc are specifically rejected.
default=127.0.0.1
[netenv]
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
; Chunithm is extremely picky about its LAN environment, so leaving this
; setting enabled is strongly recommended.
enable=1
[keychip]
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168.
subnet=192.168.139.0
[gfx]
; Force the game to run windowed.
windowed=1
; Add a frame to the game window if running windowed.
framed=1
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
monitor=0
[aimeio]
; To use a custom card reader IO DLL enter its path here.
; Leave empty if you want to use Taitools built-in keyboard input.
path=
[chuniio]
; To use a custom Chunithm IO DLL enter its path here.
; Leave empty if you want to use Taitools built-in keyboard input.
path=
; -----------------------------------------------------------------------------
; Input settings
; -----------------------------------------------------------------------------
; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal
; (not prefixed with 0x) virtual-key codes, a list of which can be found here:
;
; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
;
; This is, admittedly, not the most user-friendly configuration method in the
; world. An improved solution will be provided later.
[io3]
; Test button virtual-key code. Default is the 1 key.
test=0x31
; Service button virtual-key code. Default is the 2 key.
service=0x32
; Keyboard button to increment coin counter. Default is the 3 key.
coin=0x33
; Key bindings for each of the 32 touch cells. The default key map, depicted
; in left-to-right order, is as follows:
;
; SSSSDDDDFFFFGGGGHHHHJJJJKKKKLLLL
;
; Touch cells are numbered FROM RIGHT TO LEFT! starting from 1. This is in
; order to match the numbering used in the operator menu and service manual.
;
; Uncomment and complete the following sequence of settings to configure a
; custom high-precision touch strip controller if you have one.
[slider]
;cell32=0x53
;cell31=0x53
;cell30=0x53
; ... etc ...

11
dist/chuni/start.bat vendored
View File

@ -1,11 +0,0 @@
@echo off
pushd %~dp0
start /min inject -d -k chunihook.dll aimeReaderHost.exe -p 12
inject -d -k chunihook.dll chuniApp.exe
taskkill /f /im aimeReaderHost.exe > nul 2>&1
echo.
echo Game processes have terminated
pause

View File

@ -1,4 +0,0 @@
[aime]
; CXB is stupid, so we have to make the paths go back one
aimePath=../DEVICE/aime.txt
felicaPath=../DEVICE/felica.txt

View File

@ -1,75 +0,0 @@
[vfs]
; Make sure theses are full paths and not relative or you will have a bad time
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
amfs=
; Insert the path to the game Option directory here (contains Axxx directories)
option=
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=
[dns]
; Insert the hostname or IP address of the server you wish to use here.
; Note that 127.0.0.1, localhost etc are specifically rejected.
default=127.0.0.1
[netenv]
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
; Crossbeats is extremely picky about its LAN environment, so leaving this
; setting enabled is strongly recommended.
enable=1
[keychip]
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168.
subnet=192.168.100.0
billingCa=../DEVICE/ca.crt
billingPub=../DEVICE/billing.pub
billingType=2
[gfx]
; Force the game to run windowed.
windowed=1
; Add a frame to the game window if running windowed.
framed=1
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
monitor=0
[aime]
; Aime reader emulation
; CXB is stupid, so we have to make the paths go back one
enable=1
aimePath=../DEVICE/aime.txt
felicaPath=../DEVICE/felica.txt
[eeprom]
; See above
path=../DEVICE/eeprom.bin
[sram]
; See above
path=../DEVICE/sram.bin
[led]
; Emulation for the LED board. Currently it's just dummy responses,
; but if somebody wants to make their keyboard or whatever light
; up with the game they can
enable=1
[revio]
; Enable emulation of the rev IO board
enabe=1
; Test button virtual-key code. Default is the 1 key.
test=0x31
; Service button virtual-key code. Default is the 2 key.
service=0x32
; Keyboard button to increment coin counter. Default is the 3 key.
coin=0x33
; Menu up key. Default is up arrow.
up=0x26
; Menu down key. Default is down arrow.
down=0x28
; Menu cancel key. Default is the 4 key.
cancel=0x34

9
dist/cxb/start.bat vendored
View File

@ -1,9 +0,0 @@
@echo off
pushd %~dp0
inject -d -k cxbhook.dll Rev_v11.exe
echo.
echo Game processes have terminated
pause

View File

@ -1,54 +0,0 @@
[vfs]
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
amfs=
; Insert the path to the game Option directory here (contains Axxx directories)
option=
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=
[dns]
; Insert the hostname or IP address of the server you wish to use here.
; Note that 127.0.0.1, localhost etc are specifically rejected.
default=127.0.0.1
[netenv]
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
; Chunithm is extremely picky about its LAN environment, so leaving this
; setting enabled is strongly recommended.
enable=1
[gpio]
dipsw1=1
[keychip]
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168.
subnet=192.168.150.0
[slider]
cell1=0x51
cell2=0x57
cell3=0x45
cell4=0x52
cell5=0x55
cell6=0x49
cell7=0x4F
cell8=0x50
[buttons]
key1=0x27
key2=0x28
key3=0x25
key4=0x26
key5=0x20
; Sliders : <- QWER UIOP ->
; Triangle : Up arrow
; Square : Left Arrow
; Cross : Down Arrow
; Circle : Right arrow
; Enter : Space

9
dist/diva/start.bat vendored
View File

@ -1,9 +0,0 @@
@echo off
pushd %~dp0
inject -d -k divahook.dll diva.exe
echo.
echo Game processes have terminated
pause

116
dist/idz/segatools.ini vendored
View File

@ -1,116 +0,0 @@
[vfs]
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
amfs=
; Insert the path to the game Option directory here (contains OPxx directories)
option=
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=
[dns]
; Insert the hostname or IP address of the server you wish to use here.
; Note that 127.0.0.1, localhost etc are specifically rejected.
default=127.0.0.1
[ds]
; Region code on the emulated AMEX board DS EEPROM.
; 1: Japan
; 4: Export (some UI elements in English)
;
; NOTE: Changing this setting causes a factory reset.
region=1
[netenv]
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
; SEGA games are somewhat picky about their LAN environment, so leaving this
; setting enabled is recommended.
enable=1
[keychip]
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168.
subnet=192.168.100.0
[gpio]
; Emulated Nu DIP switch for Distribution Server setting.
;
; If multiple machines are present on the same LAN then set this to 1 on
; exactly one machine and set this to 0 on all others.
dipsw1=1
[aimeio]
; To use a custom card reader IO DLL enter its path here.
; Leave empty if you want to use Taitools built-in keyboard input.
path=
[idzio]
; To use a custom Initial D Zero IO DLL enter its path here.
; Leave empty if you want to use Taitools built-in gamepad/wheel input.
path=
[io3]
; Input API selection for JVS input emulator.
; Set "xinput" to use a gamepad and "dinput" to use a steering wheel.
mode=xinput
; Automatically reset the simulated shifter to Neutral when XInput Start is
; pressed (e.g. when navigating menus between races).
autoNeutral=1
; Use the left thumbstick for steering instead of both on XInput Controllers.
; Not recommended as it will not give you the precision needed for this game
singleStickSteering=0
; Adjust scaling for steering wheel input.
;
; This setting scales the steering wheel input so that the maximum positive
; and minimum negative steering inputs reported in the operator menu's input
; test screen do not exceed the value below. The maximum possible value is 128,
; and the value that matches the input range of a real cabinet is 97.
;
; NOTE: This is not the same thing as DirectInput steering wheel movement
; range! Taitools cannot control the maximum angle of your physical steering
; wheel controller, this setting is vendor-specific and can only be adjusted
; in the Control Panel.
restrict=97
[dinput]
; Name of the DirectInput wheel to use (or any text that occurs in its name)
; Example: TMX
;
; If this is left blank then the first DirectInput device will be used.
deviceName=
; Name of the positional shifter to use (or any subset thereof).
; Leave blank if you do not have a positional shifter; a positional shifter
; will be simulated using the configured Shift Down and Shift Up buttons
; in this case.
;
; Can be the same device as the wheel.
;
; Example: T500
shifterName=
; Pedal mappings. Valid axis names are:
;
; X, Y, Z, RX, RY, RZ, U, V
;
; (U and V are old names for Slider 1 and Slider 2).
; The examples below are valid for a Thrustmaster TMX.
brakeAxis=RZ
accelAxis=Y
; DirectInput button numbers to map to menu inputs. Note that buttons are
; numbered from 1; some software numbers buttons from 0.
start=3
viewChg=10
; Button mappings for the simulated six-speed shifter.
shiftDn=1
shiftUp=2
; Button mappings for the positional shifter, if present.
gear1=1
gear2=2
gear3=3
gear4=4
gear5=5
gear6=6
; Invert the accelerator and or brake axis
; (Needed when using DirectInput for the Dualshock 4 for example)
reverseAccelAxis=0
reverseBrakeAxis=0

10
dist/idz/start.bat vendored
View File

@ -1,10 +0,0 @@
@echo off
pushd %~dp0
.\inject.exe -k .\idzhook.dll .\InitialD0_DX11_Nu.exe
.\inject.exe -d -k .\idzhook.dll .\amdaemon.exe -c configDHCP_Final_Common.json configDHCP_Final_JP.json configDHCP_Final_JP_ST1.json configDHCP_Final_JP_ST2.json configDHCP_Final_EX.json configDHCP_Final_EX_ST1.json configDHCP_Final_EX_ST2.json
echo.
echo Game processes have terminated
pause

View File

@ -1,44 +0,0 @@
[vfs]
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
amfs=amfs
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=appdata
option=option
[dns]
; Insert the hostname or IP address of the server you wish to use here.
; Note that 127.0.0.1, localhost etc are specifically rejected.
default=127.0.0.1
[ds]
; Region code on the emulated AMEX board DS EEPROM.
; 1: Japan
; 4: Export (some UI elements in English)
;
; NOTE: Changing this setting causes a factory reset.
region=1
[netenv]
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
; SEGA games are somewhat picky about their LAN environment, so leaving this
; setting enabled is recommended.
enable=1
[keychip]
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168.
subnet=192.168.172.0
[gfx]
enable=1
[io4]
; Delete
test=0x2E
; End
service=0x23
; Insert
coin=0x2D

11
dist/mai2/start.bat vendored
View File

@ -1,11 +0,0 @@
@echo off
pushd %~dp0
taskkill /f /im amdaemon.exe > nul 2>&1
start inject -d -k mai2hook.dll amdaemon.exe -f -c config_client.json config_common.json config_server.json
inject.exe -d -k mai2hook.dll Sinmai.exe -screen-fullscreen 0 -screen-width 2160 -screen-height 1920
taskkill /f /im amdaemon.exe > nul 2>&1
echo Game processes have terminated

View File

@ -1,56 +0,0 @@
[vfs]
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
amfs=amfs
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=appdata
option=option
[dns]
; Insert the hostname or IP address of the server you wish to use here.
; Note that 127.0.0.1, localhost etc are specifically rejected.
default=127.0.0.1
[ds]
; Region code on the emulated AMEX board DS EEPROM.
; 1: Japan
; 4: Export (some UI elements in English)
;
; NOTE: Changing this setting causes a factory reset.
region=1
[netenv]
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
; SEGA games are somewhat picky about their LAN environment, so leaving this
; setting enabled is recommended.
enable=1
[keychip]
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168.
subnet=192.168.174.0
[gfx]
enable=1
[io4]
; Input API selection for JVS input emulator.
test=0x2D
service=0x2E
coin=0x24
volup=0x26
voldown=0x28
; Hooks related to the touch boards
[touch]
enable=1
; Hooks related to the LED board (codenamed Elisabeth)
[elisabeth]
enable=1
;[mercuryio]
; Use mercuryio.dll
;path=mercuryio.dll

View File

@ -1,15 +0,0 @@
@echo off
pushd %~dp0
taskkill /f /im amdaemon.exe > nul 2>&1
REM USA
REM start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_usa.json
REM JP
start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_jpn.json
inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury/Binaries/Win64/Mercury-Win64-Shipping.exe
taskkill /f /im amdaemon.exe > nul 2>&1
echo Game processes have terminated

View File

@ -1,60 +0,0 @@
[vfs]
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
amfs=amfs
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=appdata
option=option
[dns]
; Insert the hostname or IP address of the server you wish to use here.
; Note that 127.0.0.1, localhost etc are specifically rejected.
default=127.0.0.1
[ds]
; Region code on the emulated AMEX board DS EEPROM.
; 1: Japan
; 4: Export (some UI elements in English)
;
; NOTE: Changing this setting causes a factory reset.
region=1
[netenv]
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
; SEGA games are somewhat picky about their LAN environment, so leaving this
; setting enabled is recommended.
enable=1
[keychip]
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168.
subnet=192.168.162.0
[gfx]
enable=1
[io4]
; Input API selection for JVS input emulator.
; Set "1" to use a xinput gamepad and set "2" to use keyboard.
mode=2
test=0x31
service=0x32
[dinput]
LEFT_A=0x53
LEFT_B=0x44
LEFT_C=0x46
LEFT_MENU=0x51
LEFT_SIDE=0x52
RIGHT_A=0x4A
RIGHT_B=0x4B
RIGHT_C=0x4C
RIGHT_MENU=0x50
RIGHT_SIDE=0x55
SLIDER_LEFT=0x54
SLIDER_RIGHT=0x59
;Change move speed of slider when use dinput
SLIDER_SPEED=1000

11
dist/mu3/start.bat vendored
View File

@ -1,11 +0,0 @@
@echo off
pushd %~dp0
taskkill /f /im amdaemon.exe > nul 2>&1
start inject -d -k mu3hook.dll amdaemon.exe -f -c config_client.json config_common.json config_server.json
inject -d -k mu3hook.dll mu3.exe
taskkill /f /im amdaemon.exe > nul 2>&1
echo Game processes have terminated

0
dist/siva/start.bat vendored Normal file
View File

0
dist/siva/taitools.ini vendored Normal file
View File

View File

@ -101,6 +101,17 @@ static BOOL WINAPI hook_PathFileExistsA(LPCSTR pszPath);
static BOOL WINAPI hook_PathFileExistsW(LPCWSTR pszPath);
static UINT WINAPI hook_GetPrivateProfileIntW(LPCWSTR lpAppName, LPCWSTR lpKeyName, INT nDefault, LPCWSTR lpFileName);
static DWORD WINAPI hook_GetPrivateProfileStringW(
LPCWSTR lpAppName,
LPCWSTR lpKeyName,
LPCWSTR lpDefault,
LPWSTR lpReturnedString,
DWORD nSize,
LPCWSTR lpFileName
);
/* Link pointers */
static BOOL (WINAPI *next_CreateDirectoryA)(
@ -185,6 +196,17 @@ static BOOL (WINAPI *next_PathFileExistsA)(LPCSTR pszPath);
static BOOL (WINAPI *next_PathFileExistsW)(LPCWSTR pszPath);
static UINT (WINAPI *next_GetPrivateProfileIntW)(LPCWSTR lpAppName, LPCWSTR lpKeyName, INT nDefault, LPCWSTR lpFileName);
static DWORD (WINAPI *next_GetPrivateProfileStringW)(
LPCWSTR lpAppName,
LPCWSTR lpKeyName,
LPCWSTR lpDefault,
LPWSTR lpReturnedString,
DWORD nSize,
LPCWSTR lpFileName
);
/* Hook table */
static const struct hook_symbol path_hook_syms[] = {
@ -260,7 +282,15 @@ static const struct hook_symbol path_hook_syms[] = {
.name = "PathFileExistsW",
.patch = hook_PathFileExistsW,
.link = (void **) &next_PathFileExistsW,
}
}, {
.name = "GetPrivateProfileIntW",
.patch = hook_GetPrivateProfileIntW,
.link = (void **) &next_GetPrivateProfileIntW,
}, {
.name = "GetPrivateProfileStringW",
.patch = hook_GetPrivateProfileStringW,
.link = (void **) &next_GetPrivateProfileStringW,
},
};
static bool path_hook_initted;
@ -906,3 +936,46 @@ static BOOL WINAPI hook_PathFileExistsW(LPCWSTR pszPath)
return ok;
}
static UINT WINAPI hook_GetPrivateProfileIntW(LPCWSTR lpAppName, LPCWSTR lpKeyName, INT nDefault, LPCWSTR lpFileName)
{
wchar_t *trans;
UINT ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return FALSE;
}
ok = next_GetPrivateProfileIntW(lpAppName, lpKeyName, nDefault, trans ? trans : lpFileName);
free(trans);
return ok;
}
static DWORD WINAPI hook_GetPrivateProfileStringW(
LPCWSTR lpAppName,
LPCWSTR lpKeyName,
LPCWSTR lpDefault,
LPWSTR lpReturnedString,
DWORD nSize,
LPCWSTR lpFileName
)
{
wchar_t *trans;
DWORD ok;
ok = path_transform_w(&trans, lpFileName);
if (!ok) {
return FALSE;
}
ok = next_GetPrivateProfileStringW(lpAppName, lpKeyName, lpDefault, lpReturnedString, nSize, trans ? trans : lpFileName);
free(trans);
return ok;
}

View File

@ -36,9 +36,9 @@ HRESULT aime_card_populate(
mifare->sectors[0].blocks[0].bytes[14] = 'T';
mifare->sectors[0].blocks[0].bytes[15] = 'U';
memcpy_s(mifare->sectors[0].blocks[1].bytes, 4, 'T053', 4);
memcpy_s(mifare->sectors[0].blocks[1].bytes[4], 12, card_sn, 12);
memcpy_s(mifare->sectors[0].blocks[2].bytes, 16, card_sn[12], 4);
memcpy_s(mifare->sectors[0].blocks[1].bytes, 4, (uint8_t *)"T053", 4);
memcpy_s(mifare->sectors[0].blocks[1].bytes + 4, 12, card_sn, 12);
memcpy_s(mifare->sectors[0].blocks[2].bytes, 16, card_sn + 12, 4);
return S_OK;
}

113
ll3hook/dllmain.c Normal file
View File

@ -0,0 +1,113 @@
#include <windows.h>
#include "board/io4.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "gfxhook/gfx.h"
#include "gfxhook/d3d11.h"
#include "ll3hook/config.h"
#include "ll3hook/ll3-dll.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE ll3_hook_mod;
static process_entry_t ll3_startup;
static struct ll3_hook_config ll3_hook_cfg;
/* This hook is based on mu3hook, with leaked ll3hook i/o codes. */
static DWORD CALLBACK ll3_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin ll3_pre_startup ---\n");
/* Load config */
ll3_hook_config_load(&ll3_hook_cfg, L".\\taitools.ini");
/* Hook Win32 APIs */
serial_hook_init();
gfx_hook_init(&ll3_hook_cfg.gfx);
gfx_d3d11_hook_init(&ll3_hook_cfg.gfx, ll3_hook_mod);
/* Initialize emulation hooks */
hr = platform_hook_init(
&ll3_hook_cfg.platform,
3800,
ll3_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&ll3_hook_cfg.aime, 1, ll3_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(2);
if (FAILED(hr)) {
goto fail;
}
hr = ll3_dll_init(&ll3_hook_cfg.dll, ll3_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = ll3_io4_hook_init(&ll3_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
/* Start elisabeth Hooks for the LED and IO Board DLLs */
elisabeth_hook_init(&ll3_hook_cfg.elisabeth);
touch_hook_init(&ll3_hook_cfg.touch);
/* Initialize debug helpers */
spike_hook_init(L".\\taitools.ini");
dprintf("--- End ll3_pre_startup ---\n");
/* Jump to EXE start address */
return ll3_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
ll3_hook_mod = mod;
hr = process_hijack_startup(ll3_pre_startup, &ll3_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

View File

@ -1,20 +1,19 @@
shared_library(
'mercuryhook',
'll3hook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'mercuryhook.def',
vs_module_defs : 'll3hook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
],
link_with : [
aimeio_lib,
gfxhook_lib,
board_lib,
hooklib_lib,
mercuryio_lib,
ll3io_lib,
platform_lib,
util_lib,
],
@ -22,13 +21,7 @@ shared_library(
'config.c',
'config.h',
'dllmain.c',
'io4.c',
'io4.h',
'mercury-dll.c',
'mercury-dll.h',
'elisabeth.h',
'elisabeth.c',
'touch.h',
'touch.c'
'll3-dll.c',
'll3-dll.h',
],
)

View File

@ -1,74 +0,0 @@
#include <assert.h>
#include <stddef.h>
#include "board/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "gfxhook/config.h"
#include "mercuryhook/config.h"
#include "platform/config.h"
void mercury_dll_config_load(
struct mercury_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"mercuryio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void touch_config_load(
struct touch_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(
L"touch",
L"enable",
1,
filename);
}
void elisabeth_config_load(
struct elisabeth_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(
L"elisabeth",
L"enable",
1,
filename);
}
void mercury_hook_config_load(
struct mercury_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
aime_config_load(&cfg->aime, filename);
dvd_config_load(&cfg->dvd, filename);
io4_config_load(&cfg->io4, filename);
gfx_config_load(&cfg->gfx, filename);
mercury_dll_config_load(&cfg->dll, filename);
touch_config_load(&cfg->touch, filename);
elisabeth_config_load(&cfg->elisabeth, filename);
}

View File

@ -1,33 +0,0 @@
#pragma once
#include <stddef.h>
#include "board/config.h"
#include "hooklib/dvd.h"
#include "gfxhook/gfx.h"
#include "mercuryhook/mercury-dll.h"
#include "mercuryhook/touch.h"
#include "mercuryhook/elisabeth.h"
#include "platform/config.h"
struct mercury_hook_config {
struct platform_config platform;
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct gfx_config gfx;
struct mercury_dll_config dll;
struct touch_config touch;
struct elisabeth_config elisabeth;
};
void mercury_dll_config_load(
struct mercury_dll_config *cfg,
const wchar_t *filename);
void mercury_hook_config_load(
struct mercury_hook_config *cfg,
const wchar_t *filename);

View File

@ -1,121 +0,0 @@
#include <windows.h>
#include "board/io4.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "gfxhook/gfx.h"
#include "gfxhook/d3d11.h"
#include "mercuryhook/config.h"
#include "mercuryhook/io4.h"
#include "mercuryhook/mercury-dll.h"
#include "mercuryhook/elisabeth.h"
#include "mercuryhook/touch.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE mercury_hook_mod;
static process_entry_t mercury_startup;
static struct mercury_hook_config mercury_hook_cfg;
/* This hook is based on mu3hook, with leaked mercuryhook i/o codes. */
static DWORD CALLBACK mercury_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin mercury_pre_startup ---\n");
/* Load config */
mercury_hook_config_load(&mercury_hook_cfg, L".\\taitools.ini");
/* Hook Win32 APIs */
dvd_hook_init(&mercury_hook_cfg.dvd, mercury_hook_mod);
serial_hook_init();
gfx_hook_init(&mercury_hook_cfg.gfx);
gfx_d3d11_hook_init(&mercury_hook_cfg.gfx, mercury_hook_mod);
/* Initialize emulation hooks */
hr = platform_hook_init(
&mercury_hook_cfg.platform,
"SDFE",
"ACA1",
mercury_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&mercury_hook_cfg.aime, 1, mercury_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(2);
if (FAILED(hr)) {
goto fail;
}
hr = mercury_dll_init(&mercury_hook_cfg.dll, mercury_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = mercury_io4_hook_init(&mercury_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
/* Start elisabeth Hooks for the LED and IO Board DLLs */
elisabeth_hook_init(&mercury_hook_cfg.elisabeth);
touch_hook_init(&mercury_hook_cfg.touch);
/* Initialize debug helpers */
spike_hook_init(L".\\taitools.ini");
dprintf("--- End mercury_pre_startup ---\n");
/* Jump to EXE start address */
return mercury_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
mercury_hook_mod = mod;
hr = process_hijack_startup(mercury_pre_startup, &mercury_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

View File

@ -1,87 +0,0 @@
#include <initguid.h>
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include "mercuryhook/elisabeth.h"
#include "mercuryhook/mercury-dll.h"
#include "hook/table.h"
#include "hooklib/uart.h"
#include "hooklib/dll.h"
#include "hooklib/path.h"
#include "hooklib/setupapi.h"
#include "util/dprintf.h"
/* Hooks targeted DLLs dynamically loaded by elisabeth. */
static void dll_hook_insert_hooks(HMODULE target);
static FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name);
static FARPROC (WINAPI *next_GetProcAddress)(HMODULE hModule, const char *name);
static int my_USBIntLED_Init();
static int my_USBIntLED_set();
static const struct hook_symbol win32_hooks[] = {
{
.name = "GetProcAddress",
.patch = my_GetProcAddress,
.link = (void **) &next_GetProcAddress
}
};
HRESULT elisabeth_hook_init(struct elisabeth_config *cfg)
{
if (!cfg->enable) {
return S_OK;
}
dll_hook_insert_hooks(NULL);
dprintf("Elisabeth: Init\n");
return S_OK;
}
static void dll_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"kernel32.dll",
win32_hooks,
_countof(win32_hooks));
}
FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name)
{
uintptr_t ordinal = (uintptr_t) name;
FARPROC result = next_GetProcAddress(hModule, name);
if (ordinal > 0xFFFF) {
/* Import by name */
if (strcmp(name, "USBIntLED_Init") == 0) {
result = (FARPROC) my_USBIntLED_Init;
}
if (strcmp(name, "USBIntLED_set") == 0) {
result = (FARPROC) my_USBIntLED_set;
}
}
return result;
}
/* Intercept the call to initialize the LED board. */
static int my_USBIntLED_Init()
{
dprintf("Elisabeth: my_USBIntLED_Init hit!\n");
return 1;
}
static int my_USBIntLED_set(int data1, struct led_data data2)
{
assert(mercury_dll.set_leds != NULL);
mercury_dll.set_leds(data2);
return 1;
}

View File

@ -1,13 +0,0 @@
#pragma once
#include <stdbool.h>
struct led_data {
DWORD unitCount;
uint8_t rgba[480 * 4];
};
struct elisabeth_config {
bool enable;
};
HRESULT elisabeth_hook_init(struct elisabeth_config *cfg);

View File

@ -1,91 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "board/io4.h"
#include "mercuryhook/mercury-dll.h"
#include "util/dprintf.h"
bool mercury_io_coin = false;
uint16_t mercury_io_coins = 0;
static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state);
static const struct io4_ops mercury_io4_ops = {
.poll = mercury_io4_poll,
};
HRESULT mercury_io4_hook_init(const struct io4_config *cfg)
{
HRESULT hr;
assert(mercury_dll.init != NULL);
hr = io4_hook_init(cfg, &mercury_io4_ops, NULL);
if (FAILED(hr)) {
return hr;
}
return mercury_dll.init();
}
static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state)
{
uint8_t opbtn;
uint8_t gamebtn;
HRESULT hr;
assert(mercury_dll.poll != NULL);
assert(mercury_dll.get_opbtns != NULL);
assert(mercury_dll.get_gamebtns != NULL);
memset(state, 0, sizeof(*state));
hr = mercury_dll.poll();
if (FAILED(hr)) {
return hr;
}
opbtn = 0;
gamebtn = 0;
mercury_dll.get_opbtns(&opbtn);
mercury_dll.get_gamebtns(&gamebtn);
if (opbtn & MERCURY_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & MERCURY_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
if (opbtn & MERCURY_IO_OPBTN_COIN) {
if (!mercury_io_coin) {
mercury_io_coin = true;
mercury_io_coins++;
}
}
else {
mercury_io_coin = false;
}
state->chutes[0] = 128 + 256 * mercury_io_coins;
if (gamebtn & MERCURY_IO_GAMEBTN_VOL_UP) {
state->buttons[0] |= 1 << 1;
}
if (gamebtn & MERCURY_IO_GAMEBTN_VOL_DOWN) {
state->buttons[0] |= 1 << 0;
}
return S_OK;
}

View File

@ -1,7 +0,0 @@
#pragma once
#include <windows.h>
#include "board/io4.h"
HRESULT mercury_io4_hook_init(const struct io4_config *cfg);

View File

@ -1,118 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "mercuryhook/mercury-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym mercury_dll_syms[] = {
{
.sym = "mercury_io_init",
.off = offsetof(struct mercury_dll, init),
}, {
.sym = "mercury_io_poll",
.off = offsetof(struct mercury_dll, poll),
}, {
.sym = "mercury_io_get_opbtns",
.off = offsetof(struct mercury_dll, get_opbtns),
}, {
.sym = "mercury_io_get_gamebtns",
.off = offsetof(struct mercury_dll, get_gamebtns),
}, {
.sym = "mercury_io_touch_init",
.off = offsetof(struct mercury_dll, touch_init),
}, {
.sym = "mercury_io_touch_start",
.off = offsetof(struct mercury_dll, touch_start),
}, {
.sym = "mercury_io_touch_set_leds",
.off = offsetof(struct mercury_dll, set_leds),
}
};
struct mercury_dll mercury_dll;
// Copypasta DLL binding and diagnostic message boilerplate.
// Not much of this lends itself to being easily factored out. Also there
// will be a lot of API-specific branching code here eventually as new API
// versions get defined, so even though these functions all look the same
// now this won't remain the case forever.
HRESULT mercury_dll_init(const struct mercury_dll_config *cfg, HINSTANCE self)
{
uint16_t (*get_api_version)(void);
const struct dll_bind_sym *sym;
HINSTANCE owned;
HINSTANCE src;
HRESULT hr;
assert(cfg != NULL);
assert(self != NULL);
if (cfg->path[0] != L'\0') {
owned = LoadLibraryW(cfg->path);
if (owned == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("Wacca IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Wacca IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "mercury_io_get_api_version");
if (get_api_version != NULL) {
mercury_dll.api_version = get_api_version();
} else {
mercury_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose mercury_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (mercury_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Wacca IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Taitools.\n",
mercury_dll.api_version);
goto end;
}
sym = mercury_dll_syms;
hr = dll_bind(&mercury_dll, src, &sym, _countof(mercury_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Wacca IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
goto end;
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}
}
owned = NULL;
end:
if (owned != NULL) {
FreeLibrary(owned);
}
return hr;
}

View File

@ -1,25 +0,0 @@
#pragma once
#include <windows.h>
#include "mercuryio/mercuryio.h"
#include "mercuryhook/elisabeth.h"
struct mercury_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*poll)(void);
void (*get_opbtns)(uint8_t *opbtn);
void (*get_gamebtns)(uint8_t *gamebtn);
HRESULT (*touch_init)(void);
void (*touch_start)(mercury_io_touch_callback_t callback);
void (*set_leds)(struct led_data data);
};
struct mercury_dll_config {
wchar_t path[MAX_PATH];
};
extern struct mercury_dll mercury_dll;
HRESULT mercury_dll_init(const struct mercury_dll_config *cfg, HINSTANCE self);

View File

@ -1,21 +0,0 @@
LIBRARY mercuryhook
EXPORTS
aime_io_get_api_version
aime_io_init
aime_io_led_set_color
aime_io_nfc_get_aime_id
aime_io_nfc_get_felica_id
aime_io_nfc_poll
amDllVideoClose @2
amDllVideoGetVBiosVersion @4
amDllVideoOpen @1
amDllVideoSetResolution @3
mercury_io_get_api_version
mercury_io_get_gamebtns
mercury_io_get_opbtns
mercury_io_touch_init
mercury_io_touch_start
mercury_io_touch_set_leds
mercury_io_init
mercury_io_poll

View File

@ -1,518 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "board/slider-cmd.h"
#include "board/slider-frame.h"
#include "mercuryhook/mercury-dll.h"
#include "mercuryhook/touch.h"
#include "hook/iobuf.h"
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "hooklib/fdshark.h"
#include "util/dprintf.h"
#include "util/dump.h"
const char SYNC_BOARD_VER[6] = "190523";
const char UNIT_BOARD_VER[6] = "190514";
static HRESULT touch_handle_irp(struct irp *irp);
static HRESULT touch0_handle_irp_locked(struct irp *irp);
static HRESULT touch1_handle_irp_locked(struct irp *irp);
static HRESULT touch_req_dispatch(const struct touch_req *req);
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, int side);
static uint8_t calc_checksum(const void *ptr, size_t nbytes);
static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req);
static HRESULT touch_handle_next_read(const struct touch_req *req);
static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req);
static HRESULT touch_handle_mystery1(const struct touch_req *req);
static HRESULT touch_handle_mystery2(const struct touch_req *req);
static HRESULT touch_handle_start_auto_scan(const struct touch_req *req);
static void touch_res_auto_scan(const bool *state);
uint8_t input_frame_count_0 = 0x7b;
uint8_t input_frame_count_1 = 0x7b;
bool touch0_auto = false;
bool touch1_auto = false;
static CRITICAL_SECTION touch0_lock;
static struct uart touch0_uart;
static uint8_t touch0_written_bytes[520];
static uint8_t touch0_readable_bytes[520];
static CRITICAL_SECTION touch1_lock;
static struct uart touch1_uart;
static uint8_t touch1_written_bytes[520];
static uint8_t touch1_readable_bytes[520];
HRESULT touch_hook_init(const struct touch_config *cfg)
{
assert(cfg != NULL);
assert(mercury_dll.touch_init != NULL);
if (!cfg->enable) {
return S_FALSE;
}
InitializeCriticalSection(&touch0_lock);
InitializeCriticalSection(&touch1_lock);
dprintf("Wacca touch: Init\n");
uart_init(&touch0_uart, 3);
touch0_uart.written.bytes = touch0_written_bytes;
touch0_uart.written.nbytes = sizeof(touch0_written_bytes);
touch0_uart.readable.bytes = touch0_readable_bytes;
touch0_uart.readable.nbytes = sizeof(touch0_readable_bytes);
uart_init(&touch1_uart, 4);
touch1_uart.written.bytes = touch1_written_bytes;
touch1_uart.written.nbytes = sizeof(touch1_written_bytes);
touch1_uart.readable.bytes = touch1_readable_bytes;
touch1_uart.readable.nbytes = sizeof(touch1_readable_bytes);
return iohook_push_handler(touch_handle_irp);
}
static HRESULT touch_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (uart_match_irp(&touch0_uart, irp)) {
EnterCriticalSection(&touch0_lock);
hr = touch0_handle_irp_locked(irp);
LeaveCriticalSection(&touch0_lock);
}
else if (uart_match_irp(&touch1_uart, irp)) {
EnterCriticalSection(&touch1_lock);
hr = touch1_handle_irp_locked(irp);
LeaveCriticalSection(&touch1_lock);
}
else {
return iohook_invoke_next(irp);
}
return hr;
}
static HRESULT touch0_handle_irp_locked(struct irp *irp)
{
struct touch_req req;
HRESULT hr;
if (irp->op == IRP_OP_OPEN) {
dprintf("Wacca touch0: Starting backend\n");
hr = mercury_dll.touch_init();
if (FAILED(hr)) {
dprintf("Wacca touch: Backend error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&touch0_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("TX0 Buffer:\n");
dump_iobuf(&touch0_uart.written);
#endif
hr = touch_frame_decode(&req, &touch0_uart.written, 0);
if (hr != S_OK) {
if (FAILED(hr)) {
dprintf("Wacca touch: Deframe error: %x\n", (int) hr);
}
return hr;
}
hr = touch_req_dispatch(&req);
if (FAILED(hr)) {
dprintf("Wacca touch: Processing error: %x\n", (int) hr);
}
return hr;
}
}
static HRESULT touch1_handle_irp_locked(struct irp *irp)
{
struct touch_req req;
HRESULT hr;
if (irp->op == IRP_OP_OPEN) {
dprintf("Wacca touch1: Starting backend\n");
hr = mercury_dll.touch_init();
if (FAILED(hr)) {
dprintf("Wacca touch: Backend error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&touch1_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("TX1 Buffer:\n");
dump_iobuf(&touch1_uart.written);
#endif
hr = touch_frame_decode(&req, &touch1_uart.written, 1);
if (hr != S_OK) {
if (FAILED(hr)) {
dprintf("Wacca touch: Deframe error: %x\n", (int) hr);
}
return hr;
}
hr = touch_req_dispatch(&req);
if (FAILED(hr)) {
dprintf("Wacca touch: Processing error: %x\n", (int) hr);
}
return hr;
}
}
static HRESULT touch_req_dispatch(const struct touch_req *req)
{
switch (req->cmd) {
case CMD_GET_SYNC_BOARD_VER:
return touch_handle_get_sync_board_ver(req);
case CMD_NEXT_READ:
return touch_handle_next_read(req);
case CMD_GET_UNIT_BOARD_VER:
return touch_handle_get_unit_board_ver(req);
case CMD_MYSTERY1:
return touch_handle_mystery1(req);
case CMD_MYSTERY2:
return touch_handle_mystery2(req);
case CMD_START_AUTO_SCAN:
return touch_handle_start_auto_scan(req);
case CMD_BEGIN_WRITE:
dprintf("Wacca touch: Begin write for side %d\n", req->side);
return S_OK;
case CMD_NEXT_WRITE:
dprintf("Wacca touch: continue write for side %d\n", req->side);
return S_OK;
default:
dprintf("Wacca touch: Unhandled command %02x\n", req->cmd);
return S_OK;
}
}
static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req)
{
struct touch_resp_get_sync_board_ver resp;
HRESULT hr;
memset(&resp, 0, sizeof(resp));
dprintf("Wacca Touch%d: Get sync board version\n", req->side);
resp.cmd = 0xa0;
memcpy(resp.version, SYNC_BOARD_VER, sizeof(SYNC_BOARD_VER));
resp.checksum = 0;
resp.checksum = calc_checksum(&resp, sizeof(resp));
if (req->side == 0) {
hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp));
}
else {
hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp));
}
return hr;
}
static HRESULT touch_handle_next_read(const struct touch_req *req)
{
struct touch_resp_startup resp;
HRESULT hr;
char *rev;
memset(&resp, 0, sizeof(resp));
dprintf("Wacca Touch%d: Read section %2hx\n", req->side, req->data[2]);
switch (req->data[2]) {
// These can be found in the config file
case 0x30:
rev = " 0 0 1 2 3 4 5 15 15 15 15 15 15 11 11 11";
break;
case 0x31:
rev = " 11 11 11 128 103 103 115 138 127 103 105 111 126 113 95 100";
break;
case 0x33:
rev = " 101 115 98 86 76 67 68 48 117 0 82 154 0 6 35 4";
break;
default:
dprintf("Wacca touch: BAD READ REQUEST %2hx\n", req->data[2]);
return 1;
}
memcpy(resp.data, rev, 80 * sizeof(char));
resp.checksum = 0;
resp.checksum = calc_checksum(&resp, sizeof(resp));
if (req->side == 0) {
hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp));
}
else {
hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp));
}
return hr;
}
static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req)
{
struct touch_resp_get_unit_board_ver resp;
HRESULT hr;
memset(&resp, 0, sizeof(resp));
dprintf("Wacca Touch%d: get unit board version\n", req->side);
memset(resp.version, 0, sizeof(resp.version));
memcpy(resp.version, SYNC_BOARD_VER, sizeof(SYNC_BOARD_VER));
for (int i = 0; i < 6; i++ )
memcpy(&resp.version[7 + (6 * i)], UNIT_BOARD_VER, sizeof(UNIT_BOARD_VER));
resp.cmd = 0xa8;
resp.checksum = 0;
if (req->side == 0) {
resp.version[6] = 'R';
resp.checksum = calc_checksum(&resp, sizeof(resp));
#if 0
for (int i = 0; i < sizeof(resp.version); i++) {
dprintf("0x%02x ", resp.version[i]);
}
dprintf("\n");
#endif
hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp));
}
else {
resp.version[6] = 'L';
resp.checksum = calc_checksum(&resp, sizeof(resp));
#if 0
for (int i = 0; i < sizeof(resp.version); i++) {
dprintf("0x%02x ", resp.version[i]);
}
dprintf("\n");
#endif
hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp));
}
return hr;
}
static HRESULT touch_handle_mystery1(const struct touch_req *req)
{
struct touch_resp_mystery1 resp;
HRESULT hr;
memset(&resp, 0, sizeof(resp));
dprintf("Wacca Touch%d: Command A2\n", req->side);
resp.cmd = 0xa2;
resp.data = 0x3f;
resp.checksum = 0;
resp.checksum = calc_checksum(&resp, sizeof(resp));
if (req->side == 0) {
hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp));
}
else {
hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp));
}
return hr;
}
static HRESULT touch_handle_mystery2(const struct touch_req *req)
{
struct touch_resp_mystery2 resp;
HRESULT hr;
memset(&resp, 0, sizeof(resp));
dprintf("Wacca Touch%d: Command 94\n", req->side);
resp.cmd = 0x94;
resp.data = 0;
resp.checksum = 0;
resp.checksum = calc_checksum(&resp, sizeof(resp));
if (req->side == 0) {
hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp));
}
else {
hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp));
}
return hr;
}
static HRESULT touch_handle_start_auto_scan(const struct touch_req *req)
{
struct touch_resp_start_auto resp;
HRESULT hr;
uint8_t data1[24] = { 0 };
// Unsure what this does. It seems to change every request on a real board,
// but the game doesn't seem to mind that it's the same
uint8_t data2[9] = { 0x0d, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 };
dprintf("Wacca Touch%d: Start Auto", req->side);
#if 0
for (int i = 0; i < req->data_length; i++)
dprintf("0x%02x ", req->data[i]);
#endif
dprintf("\n");
resp.cmd = 0x9c;
resp.data = 0;
resp.checksum = 0x49;
resp.frame.cmd= 0x81;
memcpy(resp.frame.data1, data1, sizeof(data1));
memcpy(resp.frame.data2, data2, sizeof(data2));
resp.frame.checksum = 0;
resp.frame.checksum = calc_checksum(&resp.frame, sizeof(resp.frame));
if (req->side == 0) {
resp.frame.count = input_frame_count_0++;
hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp));
touch0_auto = true;
}
else {
resp.frame.count = input_frame_count_1++;
hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp));
touch1_auto = true;
}
mercury_dll.touch_start(touch_res_auto_scan);
return hr;
}
static void touch_res_auto_scan(const bool *state)
{
struct touch_input_frame frame0;
struct touch_input_frame frame1;
memset(&frame0, 0, sizeof(frame0));
memset(&frame1, 0, sizeof(frame1));
uint8_t dataR[24] = { 0 };
uint8_t dataL[24] = { 0 };
// this changes every input on a real board but
// the game doesn't seem to care about it...
uint8_t data2[9] = { 0x0d, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 };
uint8_t counter = 0;
frame0.cmd = 0x81;
frame0.count = input_frame_count_0++;
input_frame_count_0 %= 0x7f;
frame1.cmd = 0x81;
frame1.count = input_frame_count_1++;
input_frame_count_1 %= 0x7f;
for (int i = 0; i < 24; i++) {
for (int j = 0; j < 5; j++) {
if (state[counter]) {
dataR[i] |= (1 << j);
}
if (state[counter+120]) {
dataL[i] |= (1 << j);
}
counter++;
}
}
memcpy(frame0.data1, dataR, sizeof(dataR));
memcpy(frame0.data2, data2, sizeof(data2));
memcpy(frame1.data1, dataL, sizeof(dataL));
memcpy(frame1.data2, data2, sizeof(data2));
frame0.checksum = 0;
frame0.checksum = calc_checksum(&frame0, sizeof(frame0));
frame1.checksum = 0;
frame1.checksum = calc_checksum(&frame1, sizeof(frame1));
if (touch0_auto) {
//dprintf("Wacca touch: Touch0 auto frame #%2hx sent\n", frame0.count);
EnterCriticalSection(&touch0_lock);
iobuf_write(&touch0_uart.readable, &frame0, sizeof(frame0));
LeaveCriticalSection(&touch0_lock);
}
if (touch1_auto) {
//dprintf("Wacca touch: Touch1 auto frame #%2hx sent\n", frame0.count);
EnterCriticalSection(&touch1_lock);
iobuf_write(&touch1_uart.readable, &frame1, sizeof(frame1));
LeaveCriticalSection(&touch1_lock);
}
}
/* Decodes the response into a struct that's easier to work with. */
static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, int side)
{
dest->side = side;
dest->cmd = iobuf->bytes[0];
iobuf->pos--;
dest->data_length = iobuf->pos;
if (dest->data_length > 0) {
for (int i = 1; i < dest->data_length; i++) {
dest->data[i-1] = iobuf->bytes[i];
}
}
iobuf->pos -= dest->data_length;
return S_OK;
}
/* The last byte of every response is a checksum.
* This checksum is calculated by bitwise XORing
* every byte in the response, then dropping the MSB.
* Thanks the CrazyRedMachine for figuring that out!!
*/
static uint8_t calc_checksum(const void *ptr, size_t nbytes)
{
const uint8_t *src;
uint8_t checksum = 0;
src = ptr;
for (size_t i = 0; i < nbytes; i++) {
//dprintf("Wacca touch: Calculating %2hx\n", src[i]);
checksum = checksum^(src[i]);
}
//dprintf("Wacca touch: Checksum is %2hx\n", checksum&0x7f);
return checksum&0x7f;
}

View File

@ -1,73 +0,0 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct touch_config {
bool enable;
};
enum touch_cmd {
CMD_GET_SYNC_BOARD_VER = 0xa0,
CMD_NEXT_READ = 0x72,
CMD_GET_UNIT_BOARD_VER = 0xa8,
CMD_MYSTERY1 = 0xa2,
CMD_MYSTERY2 = 0x94,
CMD_START_AUTO_SCAN = 0xc9,
CMD_BEGIN_WRITE = 0x77,
CMD_NEXT_WRITE = 0x20
};
struct touch_req {
uint8_t side; // COM3 or COM4
uint8_t cmd; // First byte is the command byte
uint8_t data[256]; // rest of the data goes here
uint8_t data_length; // Size of the data including command byte
};
struct touch_input_frame {
uint8_t cmd;
uint8_t data1[24];
uint8_t data2[9];
uint8_t count;
uint8_t checksum;
};
struct touch_resp_get_sync_board_ver {
uint8_t cmd;
char version[6];
uint8_t checksum;
};
struct touch_resp_startup {
char data[80];
uint8_t checksum;
};
struct touch_resp_get_unit_board_ver {
uint8_t cmd;
uint8_t version[43];
uint8_t checksum;
};
struct touch_resp_mystery1 {
uint8_t cmd;
uint8_t data;
uint8_t checksum;
};
struct touch_resp_mystery2 {
uint8_t cmd;
uint8_t data;
uint8_t checksum;
};
struct touch_resp_start_auto {
uint8_t cmd;
uint8_t data;
uint8_t checksum;
struct touch_input_frame frame;
};
HRESULT touch_hook_init(const struct touch_config *cfg);

View File

@ -1,44 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "mercuryio/config.h"
static const int mercury_io_default_cells[] = {
'1','1','1','2','2','2','3','3','3','4','4','4','5','5','5','6','6','6','7','7','7','8','8','8','9','9','9','0','0','0',
'1','1','1','2','2','2','3','3','3','4','4','4','5','5','5','6','6','6','7','7','7','8','8','8','9','9','9','0','0','0',
'Q','Q','Q','W','W','W','E','E','E','R','R','R','T','T','T','Y','Y','Y','U','U','U','I','I','I','O','O','O','P','P','P',
'Q','Q','Q','W','W','W','E','E','E','R','R','R','T','T','T','Y','Y','Y','U','U','U','I','I','I','O','O','O','P','P','P',
'A','A','A','S','S','S','D','D','D','F','F','F','G','G','G','H','H','H','J','J','J','K','K','K','L','L','L',VK_OEM_1,VK_OEM_1,VK_OEM_1,
'A','A','A','S','S','S','D','D','D','F','F','F','G','G','G','H','H','H','J','J','J','K','K','K','L','L','L',VK_OEM_1,VK_OEM_1,VK_OEM_1,
'Z','Z','Z','X','X','X','C','C','C','V','V','V','B','B','B','N','N','N','M','M','M',VK_OEM_COMMA,VK_OEM_COMMA,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_PERIOD,VK_OEM_PERIOD,VK_OEM_2,VK_OEM_2,VK_OEM_2,
'Z','Z','Z','X','X','X','C','C','C','V','V','V','B','B','B','N','N','N','M','M','M',VK_OEM_COMMA,VK_OEM_COMMA,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_PERIOD,VK_OEM_PERIOD,VK_OEM_2,VK_OEM_2,VK_OEM_2,
};
void mercury_io_config_load(
struct mercury_io_config *cfg,
const wchar_t *filename)
{
wchar_t key[240];
int i;
assert(cfg != NULL);
assert(filename != NULL);
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", 0x2D, filename);
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", 0x2E, filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", 0x24, filename);
cfg->vk_vol_up = GetPrivateProfileIntW(L"io4", L"volup", 0x26, filename);
cfg->vk_vol_down = GetPrivateProfileIntW(L"io4", L"voldown", 0x28, filename);
for (i = 0 ; i < 240 ; i++) {
swprintf_s(key, _countof(key), L"cell%i", i + 1);
cfg->vk_cell[i] = GetPrivateProfileIntW(
L"touch",
key,
mercury_io_default_cells[i],
filename);
}
}

View File

@ -1,19 +0,0 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
struct mercury_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
uint8_t vk_vol_up;
uint8_t vk_vol_down;
uint8_t vk_cell[240];
};
void mercury_io_config_load(
struct mercury_io_config *cfg,
const wchar_t *filename);

View File

@ -1,121 +0,0 @@
#include <windows.h>
#include <limits.h>
#include <stdint.h>
#include <process.h>
#include "mercuryio/mercuryio.h"
#include "mercuryio/config.h"
#include "mercuryhook/elisabeth.h"
static unsigned int __stdcall mercury_io_touch_thread_proc(void *ctx);
static uint8_t mercury_opbtn;
static uint8_t mercury_gamebtn;
static struct mercury_io_config mercury_io_cfg;
static bool mercury_io_touch_stop_flag;
static HANDLE mercury_io_touch_thread;
uint16_t mercury_io_get_api_version(void)
{
return 0x0100;
}
HRESULT mercury_io_init(void)
{
mercury_io_config_load(&mercury_io_cfg, L".\\taitools.ini");
return S_OK;
}
HRESULT mercury_io_poll(void)
{
mercury_opbtn = 0;
mercury_gamebtn = 0;
if (GetAsyncKeyState(mercury_io_cfg.vk_test)) {
mercury_opbtn |= MERCURY_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_service)) {
mercury_opbtn |= MERCURY_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_coin)) {
mercury_opbtn |= MERCURY_IO_OPBTN_COIN;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_vol_up)) {
mercury_gamebtn |= MERCURY_IO_GAMEBTN_VOL_UP;
}
if (GetAsyncKeyState(mercury_io_cfg.vk_vol_down)) {
mercury_gamebtn |= MERCURY_IO_GAMEBTN_VOL_DOWN;
}
return S_OK;
}
void mercury_io_get_opbtns(uint8_t *opbtn)
{
if (opbtn != NULL) {
*opbtn = mercury_opbtn;
}
}
void mercury_io_get_gamebtns(uint8_t *gamebtn)
{
if (gamebtn != NULL) {
*gamebtn = mercury_gamebtn;
}
}
HRESULT mercury_io_touch_init(void)
{
return S_OK;
}
void mercury_io_touch_start(mercury_io_touch_callback_t callback)
{
if (mercury_io_touch_thread != NULL) {
return;
}
mercury_io_touch_thread = (HANDLE) _beginthreadex(
NULL,
0,
mercury_io_touch_thread_proc,
callback,
0,
NULL
);
}
void mercury_io_touch_set_leds(struct led_data data)
{
}
static unsigned int __stdcall mercury_io_touch_thread_proc(void *ctx)
{
mercury_io_touch_callback_t callback;
bool cellPressed[240];
size_t i;
callback = ctx;
while (!mercury_io_touch_stop_flag) {
for (i = 0 ; i < _countof(cellPressed) ; i++) {
if (GetAsyncKeyState(mercury_io_cfg.vk_cell[i])) {
cellPressed[i] = true;
} else {
cellPressed[i] = false;
}
}
callback(cellPressed);
Sleep(1);
}
return 0;
}

View File

@ -1,11 +0,0 @@
LIBRARY mercuryio
EXPORTS
mercury_io_get_api_version
mercury_io_init
mercury_io_poll
mercury_io_get_opbtns
mercury_io_get_gamebtns
mercury_io_touch_init
mercury_io_touch_start
mercury_io_touch_set_leds

View File

@ -1,71 +0,0 @@
#pragma once
#include <windows.h>
#include <stdint.h>
#include <stdbool.h>
#include "mercuryhook/elisabeth.h"
enum {
MERCURY_IO_OPBTN_TEST = 0x01,
MERCURY_IO_OPBTN_SERVICE = 0x02,
MERCURY_IO_OPBTN_COIN = 0x04,
};
enum {
MERCURY_IO_GAMEBTN_VOL_UP = 0x01,
MERCURY_IO_GAMEBTN_VOL_DOWN = 0x02,
};
typedef void (*mercury_io_touch_callback_t)(const bool *state);
/* Get the version of the Wacca IO API that this DLL supports. This
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
Semantic Versioning standard).
The latest API version as of this writing is 0x0100. */
uint16_t mercury_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after mercury_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT mercury_io_init(void);
/* Send any queued outputs (of which there are currently none, though this may
change in subsequent API versions) and retrieve any new inputs.
Minimum API version: 0x0100 */
HRESULT mercury_io_poll(void);
/* Get the state of the cabinet's operator buttons as of the last poll. See
MERCURY_IO_OPBTN enum above: this contains bit mask definitions for button
states returned in *opbtn. All buttons are active-high.
Minimum API version: 0x0100 */
void mercury_io_get_opbtns(uint8_t *opbtn);
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
MERCURY_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into
a left hand side set of inputs and a right hand side set of inputs: the bit
mappings are the same in both cases.
All buttons are active-high, even though some buttons' electrical signals
on a real cabinet are active-low.
Minimum API version: 0x0100 */
void mercury_io_get_gamebtns(uint8_t *gamebtn);
HRESULT mercury_io_touch_init(void);
void mercury_io_touch_start(mercury_io_touch_callback_t callback);
void mercury_io_touch_set_leds(struct led_data data);

View File

@ -1,13 +0,0 @@
mercuryio_lib = static_library(
'mercuryio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'mercuryio.c',
'mercuryio.h',
'config.c',
'config.h',
],
)

View File

@ -53,9 +53,7 @@ subdir('util')
subdir('gfxhook')
subdir('mu3io')
subdir('mercuryio')
subdir('sivaio')
subdir('minihook')
subdir('mu3hook')
subdir('mercuryhook')
subdir('sivahook')

View File

@ -1,30 +0,0 @@
#pragma once
#include <stddef.h>
#include "board/config.h"
#include "gfxhook/gfx.h"
#include "hooklib/dvd.h"
#include "mu3hook/mu3-dll.h"
#include "platform/config.h"
struct mu3_hook_config {
struct platform_config platform;
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct gfx_config gfx;
struct mu3_dll_config dll;
};
void mu3_dll_config_load(
struct mu3_dll_config *cfg,
const wchar_t *filename);
void mu3_hook_config_load(
struct mu3_hook_config *cfg,
const wchar_t *filename);

View File

@ -1,126 +0,0 @@
#include <windows.h>
#include <stdlib.h>
#include "board/io4.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "gfxhook/d3d9.h"
#include "gfxhook/d3d11.h"
#include "gfxhook/dxgi.h"
#include "gfxhook/gfx.h"
#include "hook/process.h"
#include "hooklib/dvd.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "mu3hook/config.h"
#include "mu3hook/io4.h"
#include "mu3hook/mu3-dll.h"
#include "mu3hook/unity.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE mu3_hook_mod;
static process_entry_t mu3_startup;
static struct mu3_hook_config mu3_hook_cfg;
static DWORD CALLBACK mu3_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin mu3_pre_startup ---\n");
/* Load config */
mu3_hook_config_load(&mu3_hook_cfg, L".\\taitools.ini");
/* Hook Win32 APIs */
dvd_hook_init(&mu3_hook_cfg.dvd, mu3_hook_mod);
gfx_hook_init(&mu3_hook_cfg.gfx);
gfx_d3d9_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod);
gfx_d3d11_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod);
gfx_dxgi_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod);
serial_hook_init();
/* Initialize emulation hooks */
hr = platform_hook_init(
&mu3_hook_cfg.platform,
"SDDT",
"ACA1",
mu3_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1, mu3_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(2);
if (FAILED(hr)) {
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);
if (FAILED(hr)) {
goto fail;
}
/* Initialize Unity native plugin DLL hooks
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `mu3hook` initialization. */
unity_hook_init();
/* Initialize debug helpers */
spike_hook_init(L".\\taitools.ini");
dprintf("--- End mu3_pre_startup ---\n");
/* Jump to EXE start address */
return mu3_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
mu3_hook_mod = mod;
hr = process_hijack_startup(mu3_pre_startup, &mu3_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

View File

@ -1,120 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/io4.h"
#include "mu3hook/mu3-dll.h"
#include "util/dprintf.h"
static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state);
static const struct io4_ops mu3_io4_ops = {
.poll = mu3_io4_poll,
};
HRESULT mu3_io4_hook_init(const struct io4_config *cfg)
{
HRESULT hr;
assert(mu3_dll.init != NULL);
hr = io4_hook_init(cfg, &mu3_io4_ops, NULL);
if (FAILED(hr)) {
return hr;
}
return mu3_dll.init();
}
static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state)
{
uint8_t opbtn;
uint8_t left;
uint8_t right;
int16_t lever;
HRESULT hr;
assert(mu3_dll.poll != NULL);
assert(mu3_dll.get_opbtns != NULL);
assert(mu3_dll.get_gamebtns != NULL);
assert(mu3_dll.get_lever != NULL);
memset(state, 0, sizeof(*state));
hr = mu3_dll.poll();
if (FAILED(hr)) {
return hr;
}
opbtn = 0;
left = 0;
right = 0;
lever = 0;
mu3_dll.get_opbtns(&opbtn);
mu3_dll.get_gamebtns(&left, &right);
mu3_dll.get_lever(&lever);
if (opbtn & MU3_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & MU3_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
if (left & MU3_IO_GAMEBTN_1) {
state->buttons[0] |= 1 << 0;
}
if (left & MU3_IO_GAMEBTN_2) {
state->buttons[0] |= 1 << 5;
}
if (left & MU3_IO_GAMEBTN_3) {
state->buttons[0] |= 1 << 4;
}
if (right & MU3_IO_GAMEBTN_1) {
state->buttons[0] |= 1 << 1;
}
if (right & MU3_IO_GAMEBTN_2) {
state->buttons[1] |= 1 << 0;
}
if (right & MU3_IO_GAMEBTN_3) {
state->buttons[0] |= 1 << 15;
}
if (left & MU3_IO_GAMEBTN_MENU) {
state->buttons[1] |= 1 << 14;
}
if (right & MU3_IO_GAMEBTN_MENU) {
state->buttons[0] |= 1 << 13;
}
if (!(left & MU3_IO_GAMEBTN_SIDE)) {
state->buttons[1] |= 1 << 15; /* L-Side, active-low */
}
if (!(right & MU3_IO_GAMEBTN_SIDE)) {
state->buttons[0] |= 1 << 14; /* R-Side, active-low */
}
/* Lever increases right-to-left, not left-to-right.
Use 0x7FFF as the center point instead of 0x8000; the latter would
overflow when the lever pos is INT16_MIN. */
state->adcs[0] = 0x7FFF - lever;
return S_OK;
}

View File

@ -1,7 +0,0 @@
#pragma once
#include <windows.h>
#include "board/io4.h"
HRESULT mu3_io4_hook_init(const struct io4_config *cfg);

View File

@ -1,112 +0,0 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "mu3hook/mu3-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym mu3_dll_syms[] = {
{
.sym = "mu3_io_init",
.off = offsetof(struct mu3_dll, init),
}, {
.sym = "mu3_io_poll",
.off = offsetof(struct mu3_dll, poll),
}, {
.sym = "mu3_io_get_opbtns",
.off = offsetof(struct mu3_dll, get_opbtns),
}, {
.sym = "mu3_io_get_gamebtns",
.off = offsetof(struct mu3_dll, get_gamebtns),
}, {
.sym = "mu3_io_get_lever",
.off = offsetof(struct mu3_dll, get_lever),
}
};
struct mu3_dll mu3_dll;
// Copypasta DLL binding and diagnostic message boilerplate.
// Not much of this lends itself to being easily factored out. Also there
// will be a lot of API-specific branching code here eventually as new API
// versions get defined, so even though these functions all look the same
// now this won't remain the case forever.
HRESULT mu3_dll_init(const struct mu3_dll_config *cfg, HINSTANCE self)
{
uint16_t (*get_api_version)(void);
const struct dll_bind_sym *sym;
HINSTANCE owned;
HINSTANCE src;
HRESULT hr;
assert(cfg != NULL);
assert(self != NULL);
if (cfg->path[0] != L'\0') {
owned = LoadLibraryW(cfg->path);
if (owned == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("Ongeki IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Ongeki IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "mu3_io_get_api_version");
if (get_api_version != NULL) {
mu3_dll.api_version = get_api_version();
} else {
mu3_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose mu3_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (mu3_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Ongeki IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Taitools.\n",
mu3_dll.api_version);
goto end;
}
sym = mu3_dll_syms;
hr = dll_bind(&mu3_dll, src, &sym, _countof(mu3_dll_syms));
if (FAILED(hr)) {
if (src != self) {
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;
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}
}
owned = NULL;
end:
if (owned != NULL) {
FreeLibrary(owned);
}
return hr;
}

View File

@ -1,22 +0,0 @@
#pragma once
#include <windows.h>
#include "mu3io/mu3io.h"
struct mu3_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*poll)(void);
void (*get_opbtns)(uint8_t *opbtn);
void (*get_gamebtns)(uint8_t *left, uint8_t *right);
void (*get_lever)(int16_t *pos);
};
struct mu3_dll_config {
wchar_t path[MAX_PATH];
};
extern struct mu3_dll mu3_dll;
HRESULT mu3_dll_init(const struct mu3_dll_config *cfg, HINSTANCE self);

View File

@ -1,25 +0,0 @@
LIBRARY mu3hook
EXPORTS
CreateDXGIFactory
CreateDXGIFactory1
CreateDXGIFactory2
D3D11CreateDevice
D3D11CreateDeviceAndSwapChain
Direct3DCreate9
aime_io_get_api_version
aime_io_init
aime_io_led_set_color
aime_io_nfc_get_aime_id
aime_io_nfc_get_felica_id
aime_io_nfc_poll
amDllVideoClose @2
amDllVideoGetVBiosVersion @4
amDllVideoOpen @1
amDllVideoSetResolution @3
mu3_io_get_api_version
mu3_io_get_gamebtns
mu3_io_get_lever
mu3_io_get_opbtns
mu3_io_init
mu3_io_poll

View File

@ -1,148 +0,0 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include "mu3io/mu3io.h"
static uint8_t mu3_opbtn;
static uint8_t mu3_left_btn;
static uint8_t mu3_right_btn;
static int16_t mu3_lever_pos;
static int16_t mu3_lever_xpos;
uint16_t mu3_io_get_api_version(void)
{
return 0x0100;
}
HRESULT mu3_io_init(void)
{
return S_OK;
}
HRESULT mu3_io_poll(void)
{
int lever;
int xlever;
XINPUT_STATE xi;
WORD xb;
mu3_opbtn = 0;
mu3_left_btn = 0;
mu3_right_btn = 0;
if (GetAsyncKeyState('1') & 0x8000) {
mu3_opbtn |= MU3_IO_OPBTN_TEST;
}
if (GetAsyncKeyState('2') & 0x8000) {
mu3_opbtn |= MU3_IO_OPBTN_SERVICE;
}
memset(&xi, 0, sizeof(xi));
XInputGetState(0, &xi);
xb = xi.Gamepad.wButtons;
if (xb & XINPUT_GAMEPAD_DPAD_LEFT) {
mu3_left_btn |= MU3_IO_GAMEBTN_1;
}
if (xb & XINPUT_GAMEPAD_DPAD_UP) {
mu3_left_btn |= MU3_IO_GAMEBTN_2;
}
if (xb & XINPUT_GAMEPAD_DPAD_RIGHT) {
mu3_left_btn |= MU3_IO_GAMEBTN_3;
}
if (xb & XINPUT_GAMEPAD_X) {
mu3_right_btn |= MU3_IO_GAMEBTN_1;
}
if (xb & XINPUT_GAMEPAD_Y) {
mu3_right_btn |= MU3_IO_GAMEBTN_2;
}
if (xb & XINPUT_GAMEPAD_B) {
mu3_right_btn |= MU3_IO_GAMEBTN_3;
}
if (xb & XINPUT_GAMEPAD_BACK) {
mu3_left_btn |= MU3_IO_GAMEBTN_MENU;
}
if (xb & XINPUT_GAMEPAD_START) {
mu3_right_btn |= MU3_IO_GAMEBTN_MENU;
}
if (xb & XINPUT_GAMEPAD_LEFT_SHOULDER) {
mu3_left_btn |= MU3_IO_GAMEBTN_SIDE;
}
if (xb & XINPUT_GAMEPAD_RIGHT_SHOULDER) {
mu3_right_btn |= MU3_IO_GAMEBTN_SIDE;
}
lever = mu3_lever_pos;
if (abs(xi.Gamepad.sThumbLX) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) {
lever += xi.Gamepad.sThumbLX / 24;
}
if (abs(xi.Gamepad.sThumbRX) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) {
lever += xi.Gamepad.sThumbRX / 24;
}
if (lever < INT16_MIN) {
lever = INT16_MIN;
}
if (lever > INT16_MAX) {
lever = INT16_MAX;
}
mu3_lever_pos = lever;
xlever = mu3_lever_pos
- xi.Gamepad.bLeftTrigger * 64
+ xi.Gamepad.bRightTrigger * 64;
if (xlever < INT16_MIN) {
xlever = INT16_MIN;
}
if (xlever > INT16_MAX) {
xlever = INT16_MAX;
}
mu3_lever_xpos = xlever;
return S_OK;
}
void mu3_io_get_opbtns(uint8_t *opbtn)
{
if (opbtn != NULL) {
*opbtn = mu3_opbtn;
}
}
void mu3_io_get_gamebtns(uint8_t *left, uint8_t *right)
{
if (left != NULL) {
*left = mu3_left_btn;
}
if (right != NULL ){
*right = mu3_right_btn;
}
}
void mu3_io_get_lever(int16_t *pos)
{
if (pos != NULL) {
*pos = mu3_lever_xpos;
}
}

View File

@ -1,84 +0,0 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
MU3_IO_OPBTN_TEST = 0x01,
MU3_IO_OPBTN_SERVICE = 0x02,
};
enum {
MU3_IO_GAMEBTN_1 = 0x01,
MU3_IO_GAMEBTN_2 = 0x02,
MU3_IO_GAMEBTN_3 = 0x04,
MU3_IO_GAMEBTN_SIDE = 0x08,
MU3_IO_GAMEBTN_MENU = 0x10,
};
/* 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
the major version and the low byte is the minor version (as defined by the
Semantic Versioning standard).
The latest API version as of this writing is 0x0100. */
uint16_t mu3_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after mu3_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT mu3_io_init(void);
/* Send any queued outputs (of which there are currently none, though this may
change in subsequent API versions) and retrieve any new inputs.
Minimum API version: 0x0100 */
HRESULT mu3_io_poll(void);
/* Get the state of the cabinet's operator buttons as of the last poll. See
MU3_IO_OPBTN enum above: this contains bit mask definitions for button
states returned in *opbtn. All buttons are active-high.
Minimum API version: 0x0100 */
void mu3_io_get_opbtns(uint8_t *opbtn);
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
MU3_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into
a left hand side set of inputs and a right hand side set of inputs: the bit
mappings are the same in both cases.
All buttons are active-high, even though some buttons' electrical signals
on a real cabinet are active-low.
Minimum API version: 0x0100 */
void mu3_io_get_gamebtns(uint8_t *left, uint8_t *right);
/* Get the position of the cabinet lever as of the last poll. The center
position should be equal to or close to zero.
The operator will be required to calibrate the lever's range of motion on
first power-on, so the lever position reported through this API does not
need to perfectly centered or cover every single position value possible,
but it should be reasonably close in order to make things easier for the
operator.
The calibration screen displays the leftmost and rightmost position signal
returned from the cabinet's ADC encoder as a pair of raw two's complement
hexadecimal values. On a real cabinet these leftmost and rightmost
positions are somewhere around 0xB000 and 0x5000 respectively (remember
that negative values i.e. left positions have a high most-significant bit),
although these values can easily vary by +/- 0x1000 across different
cabinets.
Minimum API version: 0x0100 */
void mu3_io_get_lever(int16_t *pos);

0
pki/billing.pub Normal file
View File

0
pki/ca.crt Normal file
View File

View File

@ -22,6 +22,5 @@ struct platform_config {
HRESULT platform_hook_init(
const struct platform_config *cfg,
const char *game_id,
const char *platform_id,
const uint32_t game_id,
HMODULE redir_mod);

View File

@ -71,6 +71,8 @@ HRESULT syscfg_hook_init(const struct syscfg_config *cfg, const uint32_t gid)
L"SOFTWARE\\taito\\typex",
fake_com_keys,
_countof(fake_com_keys));
return hr;
}
static HRESULT syscfg_game_kind(void *bytes, uint32_t *nbytes)

View File

@ -23,7 +23,6 @@ static struct vfs_config vfs_config;
HRESULT vfs_hook_init(const struct vfs_config *config)
{
wchar_t temp[MAX_PATH];
size_t nthome_len;
DWORD home_ok;
HRESULT hr;
@ -52,14 +51,6 @@ HRESULT vfs_hook_init(const struct vfs_config *config)
(int) hr);
}
/* Need to create the temp subdirectory, not just nthome itself */
hr = vfs_mkdir_rec(temp);
if (FAILED(hr)) {
dprintf("Vfs: Failed to create %S: %x\n", temp, (int) hr);
}
/* Not auto-creating option directory as it is normally a read-only mount */
hr = path_hook_push(vfs_path_hook);

View File

@ -1,26 +1,19 @@
#include <assert.h>
#include <stddef.h>
#include "board/config.h"
#include "gfxhook/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "mu3hook/config.h"
#include "sivahook/config.h"
#include "platform/config.h"
void mu3_dll_config_load(
struct mu3_dll_config *cfg,
void siva_dll_config_load(
struct siva_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"mu3io",
L"sivaio",
L"path",
L"",
cfg->path,
@ -28,17 +21,14 @@ void mu3_dll_config_load(
filename);
}
void mu3_hook_config_load(
struct mu3_hook_config *cfg,
void siva_hook_config_load(
struct siva_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
aime_config_load(&cfg->aime, filename);
dvd_config_load(&cfg->dvd, filename);
io4_config_load(&cfg->io4, filename);
siva_dll_config_load(&cfg->dll, filename);
gfx_config_load(&cfg->gfx, filename);
mu3_dll_config_load(&cfg->dll, filename);
}
}

19
sivahook/config.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <stddef.h>
#include "sivahook/siva-dll.h"
#include "platform/config.h"
#include "gfxhook/config.h"
#include "board/config.h"
struct siva_hook_config {
struct platform_config platform;
struct siva_dll_config dll;
struct gfx_config gfx;
};
void siva_hook_config_load(
struct siva_hook_config *cfg,
const wchar_t *filename);

91
sivahook/dllmain.c Normal file
View File

@ -0,0 +1,91 @@
#include <windows.h>
#include "board/io4.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "gfxhook/gfx.h"
#include "gfxhook/d3d11.h"
#include "sivahook/config.h"
#include "sivahook/siva-dll.h"
#include "sivahook/unity.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE siva_hook_mod;
static process_entry_t siva_startup;
static struct siva_hook_config siva_hook_cfg;
/* This hook is based on mu3hook, with leaked sivahook i/o codes. */
static DWORD CALLBACK siva_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin siva_pre_startup ---\n");
/* Load config */
siva_hook_config_load(&siva_hook_cfg, L".\\taitools.ini");
/* Hook Win32 APIs */
serial_hook_init();
gfx_hook_init(&siva_hook_cfg.gfx);
gfx_d3d11_hook_init(&siva_hook_cfg.gfx, siva_hook_mod);
/* Initialize emulation hooks */
hr = platform_hook_init(
&siva_hook_cfg.platform,
3000,
siva_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = siva_dll_init(&siva_hook_cfg.dll, siva_hook_mod);
if (FAILED(hr)) {
goto fail;
}
unity_hook_init();
/* Initialize debug helpers */
spike_hook_init(L".\\taitools.ini");
dprintf("--- End siva_pre_startup ---\n");
/* Jump to EXE start address */
return siva_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
siva_hook_mod = mod;
hr = process_hijack_startup(siva_pre_startup, &siva_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

View File

@ -1,21 +1,19 @@
shared_library(
'mu3hook',
'sivahook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'mu3hook.def',
vs_module_defs : 'sivahook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
xinput_lib,
],
link_with : [
aimeio_lib,
board_lib,
gfxhook_lib,
board_lib,
hooklib_lib,
mu3io_lib,
sivaio_lib,
platform_lib,
util_lib,
],
@ -23,11 +21,9 @@ shared_library(
'config.c',
'config.h',
'dllmain.c',
'io4.c',
'io4.h',
'mu3-dll.c',
'mu3-dll.h',
'unity.h',
'siva-dll.c',
'siva-dll.h',
'unity.c',
'unity.h',
],
)

100
sivahook/siva-dll.c Normal file
View File

@ -0,0 +1,100 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "sivahook/siva-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym siva_dll_syms[] = {
{
.sym = "siva_io_init",
.off = offsetof(struct siva_dll, init),
}, {
.sym = "siva_io_read_coin_counter",
.off = offsetof(struct siva_dll, read_coin_counter),
}, {
.sym = "siva_io_get_btns",
.off = offsetof(struct siva_dll, get_btns),
},
};
struct siva_dll siva_dll;
HRESULT siva_dll_init(const struct siva_dll_config *cfg, HINSTANCE self)
{
uint16_t (*get_api_version)(void);
const struct dll_bind_sym *sym;
HINSTANCE owned;
HINSTANCE src;
HRESULT hr;
assert(cfg != NULL);
assert(self != NULL);
if (cfg->path[0] != L'\0') {
owned = LoadLibraryW(cfg->path);
if (owned == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("Siva IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Siva IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "siva_io_get_api_version");
if (get_api_version != NULL) {
siva_dll.api_version = get_api_version();
} else {
siva_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose siva_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (siva_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Siva IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
siva_dll.api_version);
goto end;
}
sym = siva_dll_syms;
hr = dll_bind(&siva_dll, src, &sym, _countof(siva_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Siva IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
goto end;
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}
}
owned = NULL;
end:
if (owned != NULL) {
FreeLibrary(owned);
}
return hr;
}

20
sivahook/siva-dll.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <windows.h>
#include "sivaio/sivaio.h"
struct siva_dll {
uint16_t api_version;
HRESULT (*init)(void);
void (*read_coin_counter)(uint16_t *coins, uint16_t *services);
void (*get_btns)(uint8_t *btn, uint8_t *stick);
};
struct siva_dll_config {
wchar_t path[MAX_PATH];
};
extern struct siva_dll siva_dll;
HRESULT siva_dll_init(const struct siva_dll_config *cfg, HINSTANCE self);

7
sivahook/sivahook.def Normal file
View File

@ -0,0 +1,7 @@
LIBRARY sivahook
EXPORTS
siva_io_get_api_version
siva_io_init
siva_io_read_coin_counter
siva_io_get_btns

View File

@ -3,9 +3,13 @@
#include <windows.h>
#include "hook/table.h"
#include "hook/iohook.h"
#include "hooklib/dll.h"
#include "hooklib/path.h"
#include "hooklib/serial.h"
#include "hooklib/reg.h"
#include "hook/procaddr.h"
#include "util/dprintf.h"
@ -25,8 +29,15 @@ static const struct hook_symbol unity_kernel32_syms[] = {
static const wchar_t *target_modules[] = {
L"mono.dll",
L"cri_ware_unity.dll",
L"SimpleNesys.dll",
L"ismACIO.dll",
L"NESiCAReader.dll",
};
static const wchar_t *dep_hooks[] = {};
static const size_t target_modules_len = _countof(target_modules);
static const size_t dep_hooks_len = _countof(dep_hooks);
void unity_hook_init(void)
{
@ -46,10 +57,13 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
{
const wchar_t *name_end;
const wchar_t *target_module;
const wchar_t *target_dep;
bool already_loaded;
HMODULE result;
HMODULE dep_mod;
size_t name_len;
size_t target_module_len;
size_t dep_len;
if (name == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
@ -88,6 +102,20 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
dll_hook_insert_hooks(result);
path_hook_insert_hooks(result);
reg_hook_insert_hooks(result);
proc_addr_insert_hooks(result);
}
for (size_t i = 0; i < dep_hooks_len; i++) {
target_dep = dep_hooks[i];
dep_mod = GetModuleHandleW(target_dep);
if (dep_mod != NULL) {
dprintf("Unity: Hook dependency %ls\n", target_dep);
iohook_apply_hooks(dep_mod);
serial_hook_apply_hooks(dep_mod);
reg_hook_insert_hooks(dep_mod);
}
}
}

30
sivaio/config.c Normal file
View File

@ -0,0 +1,30 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "sivaio/config.h"
void siva_io_config_load(struct siva_input_config *cfg, const wchar_t *filename)
{
cfg->test = GetPrivateProfileIntW(L"jvs", L"test", VK_HOME, filename);
cfg->service = GetPrivateProfileIntW(L"jvs", L"service", VK_DELETE, filename);
cfg->coin = GetPrivateProfileIntW(L"jvs", L"coin", VK_INSERT, filename);
cfg->is_xinput = GetPrivateProfileIntW(L"deck", L"deck", 0, filename);
cfg->xinput_player = GetPrivateProfileIntW(L"deck", L"controller_num", 0, filename);
cfg->btn_l = GetPrivateProfileIntW(L"deck", L"left_button", 'C', filename);
cfg->btn_r = GetPrivateProfileIntW(L"deck", L"right_button", 'N', filename);
cfg->stick_l_up = GetPrivateProfileIntW(L"deck", L"left_stick_up", 'W', filename);
cfg->stick_l_right = GetPrivateProfileIntW(L"deck", L"left_stick_right", 'D', filename);
cfg->stick_l_down = GetPrivateProfileIntW(L"deck", L"left_stick_down", 'S', filename);
cfg->stick_l_left = GetPrivateProfileIntW(L"deck", L"left_stick_left", 'A', filename);
cfg->stick_r_up = GetPrivateProfileIntW(L"deck", L"right_stick_up", 'I', filename);
cfg->stick_r_right = GetPrivateProfileIntW(L"deck", L"right_stick_right", 'L', filename);
cfg->stick_r_down = GetPrivateProfileIntW(L"deck", L"right_stick_down", 'K', filename);
cfg->stick_r_left = GetPrivateProfileIntW(L"deck", L"right_stick_left", 'J', filename);
}

28
sivaio/config.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#pragma pack(push, 1)
struct siva_input_config {
uint8_t is_xinput;
uint8_t xinput_player;
uint8_t test;
uint8_t service;
uint8_t coin;
uint8_t btn_l;
uint8_t btn_r;
uint8_t stick_l_up;
uint8_t stick_l_right;
uint8_t stick_l_down;
uint8_t stick_l_left;
uint8_t stick_r_up;
uint8_t stick_r_right;
uint8_t stick_r_down;
uint8_t stick_r_left;
};
#pragma pack(pop)
void siva_io_config_load(struct siva_input_config *cfg, const wchar_t *filename);

View File

@ -1,5 +1,5 @@
mu3io_lib = static_library(
'mu3io',
sivaio_lib = static_library(
'sivaio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
@ -8,7 +8,9 @@ mu3io_lib = static_library(
xinput_lib,
],
sources : [
'mu3io.c',
'mu3io.h',
'sivaio.c',
'sivaio.h',
'config.c',
'config.h',
],
)

64
sivaio/sivaio.c Normal file
View File

@ -0,0 +1,64 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include <stdbool.h>
#include "sivaio/sivaio.h"
#include "sivaio/config.h"
#include "util/dprintf.h"
static bool siva_io_coin = false;
static bool siva_io_service = false;
static uint16_t siva_coin_ct = 0;
static uint16_t siva_service_ct = 0;
static struct siva_input_config cfg;
uint16_t siva_io_get_api_version(void)
{
return 0x0100;
}
HRESULT siva_io_init(void)
{
dprintf("Siva IO: Init\n");
siva_io_config_load(&cfg, L".\\bananatools.ini");
return S_OK;
}
void siva_io_get_btns(uint8_t *btn, uint8_t *stick)
{
if (GetAsyncKeyState(cfg.test) & 0x8000) {
*btn |= SIVA_BTN_TEST;
}
if (GetAsyncKeyState(cfg.service) & 0x8000) {
*btn |= SIVA_BTN_SERVICE;
}
}
void siva_io_read_coin_counter(uint16_t *coins, uint16_t *services)
{
if (GetAsyncKeyState(cfg.coin) & 0x8000) {
if (!siva_io_coin) {
siva_io_coin = true;
siva_coin_ct++;
}
} else {
siva_io_coin = false;
}
if (GetAsyncKeyState(cfg.service) & 0x8000) {
if (!siva_io_service) {
siva_io_service = true;
siva_service_ct++;
}
} else {
siva_io_service = false;
}
*coins = siva_coin_ct;
*services = siva_service_ct;
}

57
sivaio/sivaio.h Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
SIVA_BTN_TEST = 0x01,
SIVA_BTN_SERVICE = 0x02,
SIVA_BTN_COIN = 0x03,
SIVA_BTN_LEFT = 0x04,
SIVA_BTN_RIGHT = 0x05,
};
enum {
SIVA_STICK_L_UP = 0x01,
SIVA_STICK_L_RIGHT = 0x02,
SIVA_STICK_L_DOWN = 0x03,
SIVA_STICK_L_LEFT = 0x04,
SIVA_STICK_R_UP = 0x05,
SIVA_STICK_R_RIGHT = 0x06,
SIVA_STICK_R_DOWN = 0x07,
SIVA_STICK_R_LEFT = 0x08,
};
/* Get the version of the Theatrhythm IO API that this DLL supports. This
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
Semantic Versioning standard).
The latest API version as of this writing is 0x0100. */
uint16_t siva_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after siva_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT siva_io_init(void);
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
SIVA_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into
a left hand side set of inputs and a right hand side set of inputs: the bit
mappings are the same in both cases.
All buttons are active-high, even though some buttons' electrical signals
on a real cabinet are active-low.
Minimum API version: 0x0100 */
void siva_io_get_btns(uint8_t *btn, uint8_t *stick);
void siva_io_read_coin_counter(uint16_t *coins, uint16_t *services);