add games

This commit is contained in:
Hay1tsme 2023-09-18 04:05:22 -04:00
parent f7cd4f9d28
commit 8ff356b84c
61 changed files with 2673 additions and 0 deletions

View File

@ -20,9 +20,44 @@ $(BUILD_DIR_ZIP)/taiko.zip:
$(V)strip $(BUILD_DIR_ZIP)/taiko/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/taiko ; zip -r ../taiko.zip *
$(BUILD_DIR_ZIP)/exvs2.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/exvs2
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/exvs2hook/exvs2hook.dll \
$(DIST_DIR)/exvs2/bananatools.ini \
$(DIST_DIR)/exvs2/start.bat \
$(BUILD_DIR_ZIP)/exvs2
$(V)strip $(BUILD_DIR_ZIP)/exvs2/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/exvs2 ; zip -r ../exvs2.zip *
$(BUILD_DIR_ZIP)/sao.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/sao
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/saohook/saohook.dll \
$(DIST_DIR)/sao/bananatools.ini \
$(DIST_DIR)/sao/start.bat \
$(BUILD_DIR_ZIP)/sao
$(V)strip $(BUILD_DIR_ZIP)/sao/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/sao ; zip -r ../sao.zip *
$(BUILD_DIR_ZIP)/mkac.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/mkac
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_32)/mkachook/mkachook.dll \
$(DIST_DIR)/mkac/bananatools.ini \
$(DIST_DIR)/mkac/start.bat \
$(BUILD_DIR_ZIP)/mkac
$(V)strip $(BUILD_DIR_ZIP)/mkac/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mkac ; zip -r ../mkac.zip *
$(BUILD_DIR_ZIP)/doc.zip: \
$(DOC_DIR)/ferrumhook.md \
$(DOC_DIR)/taikohook.md \
$(DOC_DIR)/exvs2hook.md \
$(DOC_DIR)/saohook.md \
| $(zipdir)/
$(V)echo ... $@
$(V)zip -r $@ $^
@ -30,6 +65,9 @@ $(BUILD_DIR_ZIP)/doc.zip: \
$(BUILD_DIR_ZIP)/bananatools.zip: \
$(BUILD_DIR_ZIP)/ferrum.zip \
$(BUILD_DIR_ZIP)/taiko.zip \
$(BUILD_DIR_ZIP)/exvs2.zip \
$(BUILD_DIR_ZIP)/sao.zip \
$(BUILD_DIR_ZIP)/mkac.zip \
$(BUILD_DIR_ZIP)/doc.zip \
README.md \

77
dist/exvs2/bananatools.ini vendored Normal file
View File

@ -0,0 +1,77 @@
; Controls the virtual file system hooks. These redirect file i/o
; requests to a folder specified below, instead of the drive.
; These are all required, even if the game doesn't use one of them.
[vfs]
path=
[dns]
default=localhost
; Security dongle emulation, disable if you have a
; real dongle connected that you want to use
[dongle]
enable=1
serial=123456789012
; Set the network environment. Most games seem to want 192.168.123.X
[netenv]
enable=1
subnet=192.168.85.0
[misc]
systemVersion=GX2100-1-NA-SYS0-A06
; Graphics hook, may cause crashes in some games
[gfx]
enable=1
windowed=1
framed=0
monitor=0
; Control the AMCUS replacement class
[amcus]
enable=1
game_id=SBUZ
game_cd=GX21
am_game_ver=4.00
cacfg_game_ver=29.31
server_uri=localhost
server_host=localhost
; Controlls the xinput hooks
[xinput]
enable=1
[reader]
enable=1
access_code=00000000000000000000
; JVS config
[jvs]
enable=1
port=3
; Mappings for the najv4 IO board. To disable JVS emulation and use
; a real board, set enable to 0 in the "jvs" section.
[najv4]
test=0x24 ; "Home" key
coin=0x2D ; "Insert" key
service=0x2E ; "Delete" key
up=0x26 ; Up arrow
down=0x28 ; Down arrow
enter=0x0D ; "Enter" key
; Mappings for the gamepad. To disable gamepad eumlation and use
; a real pokken arcade controller, set enable to 0 in the "xinput" section
[gamepad]
dpad_up=0x57 ; W
dpad_down=0x53 ; A
dpad_left=0x41 ; S
dpad_right=0x44 ; D
button_a=0x4F ; O
button_b=0x4B ; K
button_x=0x49 ; I
button_y=0x4A ; J
trigger_l=0x51 ; Q
trigger_r=0x45 ; E
button_start=0xA0 ; Left Shift

11
dist/exvs2/start.bat vendored Normal file
View File

@ -0,0 +1,11 @@
@echo off
pushd %~dp0
REM Comment out this line if you intend to use the AMCUS emulator
start inject.exe -d -k exvs2hook.dll AMCUS\AMAuthd.exe
inject.exe -d -k exvs2hook.dll exvs2_exe_Release.exe
echo.
echo The game process has terminated
pause

59
dist/mkac/bananatools.ini vendored Normal file
View File

@ -0,0 +1,59 @@
; Controls the virtual file system hooks. These redirect file i/o
; requests to a folder specified below, instead of the drive.
; These are all required, even if the game doesn't use one of them.
[vfs]
path=
[dns]
default=localhost
; Security dongle emulation, disable if you have a
; real dongle connected that you want to use
[dongle]
enable=1
serial=271013020001
; Set the network environment. Most games seem to want 192.168.123.X
[netenv]
enable=1
subnet=192.168.112.0
; Graphics hook, may cause crashes in some games
[gfx]
enable=1
windowed=1
framed=0
monitor=0
; Control the AMCUS replacement class
[amcus]
enable=1
game_id=SBZB
game_cd=MK31
am_game_ver=1.10
cacfg_game_ver=18.16
server_uri=localhost
server_host=localhost
; Controlls the xinput hooks
[xinput]
enable=1
[reader]
enable=1
access_code=00000000000000000000
; JVS config
[jvs]
enable=1
port=3
; Mappings for the najv4 IO board. To disable JVS emulation and use
; a real board, set enable to 0 in the "jvs" section.
[najv4]
test=0x24 ; "Home" key
coin=0x2D ; "Insert" key
service=0x2E ; "Delete" key
up=0x26 ; Up arrow
down=0x28 ; Down arrow
enter=0x0D ; "Enter" key

10
dist/mkac/start.bat vendored Normal file
View File

@ -0,0 +1,10 @@
@echo off
pushd %~dp0
inject.exe -d -k mkachook.dll AMCUS/AMAuthd.exe
inject.exe -d -k mkachook.dll MK_AGP3_FINAL.exe
echo.
echo The game process has terminated
pause

62
dist/sao/bananatools.ini vendored Normal file
View File

@ -0,0 +1,62 @@
; Controls the virtual file system hooks. Redirects all drive to
; [path you set here]\\[drive letter]
; !! REQUIRED !!
[vfs]
path=
; Hooks for DNS (allnet, etc)
; Set 'default' to the IP/hostname of the server
; you're trying to connect to
[dns]
default=localhost
; Security dongle emulation, disable if you have a
; real dongle connected that you want to use. Some games
; validate the S/N, some don't seem to care as long as it's
; formatted like below
[dongle]
enable=1
serial=282513040001
; Set the network environment. Most games seem to want 192.168.123.X
[netenv]
enable=1
subnet=192.168.170.0
; Graphics hook, may cause crashes in some games
[gfx]
enable=0
windowed=1
framed=0
monitor=0
; Banapass reader
[reader]
enable=1
access_code=00000000000000000000
; Control the AMCUS replacement class
[amcus]
enable=0
game_id=SDEW
game_cd=SAO1
am_game_ver=1.00
cacfg_game_ver=33.11
server_uri=localhost
server_host=localhost
; Controls for USIO buttons
; Test: Home
; Service: Delete
; Coin: Insert
; Up: Up arrow
; Down: Down arrow
; Enter: Enter
[usio]
enable=1
test=0x24
service=0x2E
coin=0x2D
up=0x26
down=0x28
enter=0x0D

12
dist/sao/start.bat vendored Normal file
View File

@ -0,0 +1,12 @@
@echo off
pushd %~dp0
start inject.exe -d -k saohook.dll AMCUS/AMAuthd.exe
inject.exe -d -k saohook.dll game/link.exe -screen-fullscreen 0 -screen-width 1920 -screen-height 1080 -logfile link.log
taskkill /im AMAuthd.exe /f
echo.
echo The game process has terminated
pause

8
doc/exvs2hook.md Normal file
View File

@ -0,0 +1,8 @@
# ferrumhook
# Supported games
* EX Vs 2 v29.31
## General remarks
* Very much WIP, game currently does not work in any capacity

8
doc/saohook.md Normal file
View File

@ -0,0 +1,8 @@
# ferrumhook
# Supported games
* Sword Art Online Arcade - All Versions
## General remarks
* Very much WIP, game currently does not work in any capacity

0
exvs2hook/bngrw.c Normal file
View File

0
exvs2hook/bngrw.h Normal file
View File

36
exvs2hook/config.c Normal file
View File

@ -0,0 +1,36 @@
#include <assert.h>
#include <stddef.h>
#include "exvs2hook/config.h"
#include "platform/config.h"
void exvs2_dll_config_load(
struct exvs2_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"exvs2io",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void exvs2_hook_config_load(
struct exvs2_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
exvs2_dll_config_load(&cfg->dll, filename);
gfx_config_load(&cfg->gfx, filename);
qr_config_load(&cfg->qr, filename);
bpreader_config_load(&cfg->reader, filename);
}

28
exvs2hook/config.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <stddef.h>
#include "exvs2hook/exvs2-dll.h"
#include "platform/config.h"
#include "gfxhook/config.h"
#include "amcus/config.h"
#include "board/config.h"
struct exvs2_hook_config {
struct platform_config platform;
struct exvs2_dll_config dll;
struct gfx_config gfx;
struct amcus_config amcus;
struct qr_config qr;
struct bpreader_config reader;
};
void exvs2_dll_config_load(
struct exvs2_dll_config *cfg,
const wchar_t *filename);
void exvs2_hook_config_load(
struct exvs2_hook_config *cfg,
const wchar_t *filename);

93
exvs2hook/dllmain.c Normal file
View File

@ -0,0 +1,93 @@
#include <windows.h>
#include <stdlib.h>
#include "exvs2hook/config.h"
#include "exvs2hook/exvs2-dll.h"
#include "exvs2hook/jvs.h"
#include "amcus/amcus.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "board/bpreader.h"
#include "board/qr.h"
#include "platform/platform.h"
#include "gfxhook/gfx.h"
#include "gfxhook/dxgi.h"
#include "util/dprintf.h"
static HMODULE exvs2_hook_mod;
static process_entry_t exvs2_startup;
static struct exvs2_hook_config exvs2_hook_cfg;
static DWORD CALLBACK exvs2_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin exvs2_pre_startup ---\n");
exvs2_hook_config_load(&exvs2_hook_cfg, L".\\bananatools.ini");
serial_hook_init();
struct dongle_info dinfo;
dinfo.vid = 0x0B9A;
dinfo.pid = 0x0C20;
wcscpy_s(dinfo.manufacturer, _countof(dinfo.manufacturer), L"UFD 3.0");
wcscpy_s(dinfo.product, _countof(dinfo.product), L"Silicon-Power8G");
hr = platform_hook_init(&exvs2_hook_cfg.platform, PLATFORM_BNA1, exvs2_jvs_init, exvs2_hook_mod, dinfo);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = exvs2_dll_init(&exvs2_hook_cfg.dll, exvs2_hook_mod);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = qr_hook_init(&exvs2_hook_cfg.qr, 1);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = amcus_hook_init(&exvs2_hook_cfg.amcus);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
gfx_hook_init(&exvs2_hook_cfg.gfx);
gfx_dxgi_hook_init(&exvs2_hook_cfg.gfx, exvs2_hook_mod);
dprintf("--- End exvs2_pre_startup ---\n");
return exvs2_startup();
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
exvs2_hook_mod = mod;
hr = process_hijack_startup(exvs2_pre_startup, &exvs2_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

100
exvs2hook/exvs2-dll.c Normal file
View File

@ -0,0 +1,100 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "exvs2hook/exvs2-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym exvs2_dll_syms[] = {
{
.sym = "exvs2_io_jvs_init",
.off = offsetof(struct exvs2_dll, jvs_init),
}, {
.sym = "exvs2_io_jvs_poll",
.off = offsetof(struct exvs2_dll, jvs_poll),
}, {
.sym = "exvs2_io_jvs_read_coin_counter",
.off = offsetof(struct exvs2_dll, jvs_read_coin_counter),
}
};
struct exvs2_dll exvs2_dll;
HRESULT exvs2_dll_init(const struct exvs2_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("exvs2 IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("exvs2 IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "exvs2_io_get_api_version");
if (get_api_version != NULL) {
exvs2_dll.api_version = get_api_version();
} else {
exvs2_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose exvs2_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (exvs2_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("exvs2 IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
exvs2_dll.api_version);
goto end;
}
sym = exvs2_dll_syms;
hr = dll_bind(&exvs2_dll, src, &sym, _countof(exvs2_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("exvs2 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
exvs2hook/exvs2-dll.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <windows.h>
#include "exvs2io/exvs2io.h"
struct exvs2_dll {
uint16_t api_version;
HRESULT (*jvs_init)(void);
HRESULT (*jvs_poll)(uint8_t *opbtn, uint16_t *gamepad);
void (*jvs_read_coin_counter)(uint16_t *coins);
};
struct exvs2_dll_config {
wchar_t path[MAX_PATH];
};
extern struct exvs2_dll exvs2_dll;
HRESULT exvs2_dll_init(const struct exvs2_dll_config *cfg, HINSTANCE self);

7
exvs2hook/exvs2hook.def Normal file
View File

@ -0,0 +1,7 @@
LIBRARY exvs2hook
EXPORTS
exvs2_io_get_api_version
exvs2_io_jvs_init
exvs2_io_jvs_poll
exvs2_io_jvs_read_coin_counter

125
exvs2hook/jvs.c Normal file
View File

@ -0,0 +1,125 @@
#include <windows.h>
#include <assert.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.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"
#include "board/najv4.h"
#include "exvs2hook/jvs.h"
#include "exvs2hook/exvs2-dll.h"
static void exvs2_jvs_read_switches(void *ctx, struct najv4_switch_state *out);
static void exvs2_jvs_read_coin_counter(void *ctx, uint8_t slot_no, uint16_t *out);
static const struct najv4_ops exvs2_jvs_najv4_ops = {
.read_switches = exvs2_jvs_read_switches,
.read_coin_counter = exvs2_jvs_read_coin_counter,
};
static struct najv4 exvs2_jvs_najv4;
HRESULT exvs2_jvs_init(struct jvs_node **out)
{
HRESULT hr;
assert(out != NULL);
assert(exvs2_dll.jvs_init != NULL);
dprintf("exvs2 JVS: Starting IO backend\n");
hr = exvs2_dll.jvs_init();
if (FAILED(hr)) {
dprintf("exvs2 JVS: Backend error, I/O disconnected: %x\n", (int) hr);
return hr;
}
najv4_init(&exvs2_jvs_najv4, NULL, &exvs2_jvs_najv4_ops, NULL);
*out = najv4_to_jvs_node(&exvs2_jvs_najv4);
return S_OK;
}
static void exvs2_jvs_read_switches(void *ctx, struct najv4_switch_state *out)
{
uint8_t opbtn = 0;
uint16_t gamebtn = 0;
assert(out != NULL);
assert(exvs2_dll.jvs_poll != NULL);
exvs2_dll.jvs_poll(&opbtn, &gamebtn);
out->system = 0;
out->p1 = 0;
out->p2 = 0;
if (opbtn & EXVS2_IO_OPBTN_TEST) { // Test
out->system = 0x80;
}
if (gamebtn & EXVS2_IO_GAMEBTN_4) { // Btn4
out->p1 |= 0x40;
}
if (gamebtn & EXVS2_IO_GAMEBTN_3) { // Btn3
out->p1 |= 0x80;
}
if (gamebtn & EXVS2_IO_GAMEBTN_2) { // Btn2
out->p1 |= 0x100;
}
if (gamebtn & EXVS2_IO_GAMEBTN_1) { // Btn1
out->p1 |= 0x200;
}
if (gamebtn & EXVS2_IO_GAMEBTN_RIGHT) { // Right
out->p1 |= 0x400;
}
if (gamebtn & EXVS2_IO_GAMEBTN_LEFT) { // Left
out->p1 |= 0x800;
}
if (gamebtn & EXVS2_IO_GAMEBTN_DOWN) { // Down
out->p1 |= 0x1000;
}
if (gamebtn & EXVS2_IO_GAMEBTN_UP) { // Up
out->p1 |= 0x2000;
}
if (opbtn & EXVS2_IO_OPBTN_SERVICE) { // Service
out->p1 |= 0x4000;
}
if (gamebtn & EXVS2_IO_GAMEBTN_START) { // Start
out->p1 = 0x8000;
}
}
static void exvs2_jvs_read_coin_counter(void *ctx, uint8_t slot_no, uint16_t *out)
{
assert(out != NULL);
assert(exvs2_dll.jvs_read_coin_counter != NULL);
if (slot_no > 0) {
return;
}
exvs2_dll.jvs_read_coin_counter(out);
}

8
exvs2hook/jvs.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "jvs/jvs-bus.h"
HRESULT exvs2_jvs_init(struct jvs_node **root);

33
exvs2hook/meson.build Normal file
View File

@ -0,0 +1,33 @@
shared_library(
'exvs2hook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'exvs2hook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
],
link_with : [
exvs2io_lib,
amcus_lib,
platform_lib,
util_lib,
hooklib_lib,
gfxhook_lib,
jvs_lib,
board_lib
],
sources : [
'dllmain.c',
'config.c',
'config.h',
'exvs2-dll.c',
'exvs2-dll.h',
'jvs.c',
'jvs.h',
'bngrw.c',
'bngrw.h',
],
)

23
exvs2io/config.c Normal file
View File

@ -0,0 +1,23 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "exvs2io/config.h"
void exvs2_io_najv4_config_load(struct exvs2_najv4_config *cfg, const wchar_t *filename)
{
cfg->test = GetPrivateProfileIntW(L"najv4", L"test", VK_HOME, filename);
cfg->service = GetPrivateProfileIntW(L"najv4", L"service", VK_DELETE, filename);
cfg->coin = GetPrivateProfileIntW(L"najv4", L"coin", VK_INSERT, filename);
cfg->up = GetPrivateProfileIntW(L"najv4", L"up", VK_UP, filename);
cfg->down = GetPrivateProfileIntW(L"najv4", L"down", VK_DOWN, filename);
cfg->left = GetPrivateProfileIntW(L"najv4", L"left", VK_LEFT, filename);
cfg->right = GetPrivateProfileIntW(L"najv4", L"right", VK_RIGHT, filename);
cfg->start = GetPrivateProfileIntW(L"najv4", L"start", VK_RETURN, filename);
cfg->btn1 = GetPrivateProfileIntW(L"najv4", L"btn1", '1', filename);
cfg->btn2 = GetPrivateProfileIntW(L"najv4", L"btn2", '2', filename);
cfg->btn3 = GetPrivateProfileIntW(L"najv4", L"btn3", '3', filename);
cfg->btn4 = GetPrivateProfileIntW(L"najv4", L"btn4", '4', filename);
}

21
exvs2io/config.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
struct exvs2_najv4_config {
uint8_t test;
uint8_t service;
uint8_t coin;
uint8_t up;
uint8_t down;
uint8_t left;
uint8_t right;
uint8_t start;
uint8_t btn1;
uint8_t btn2;
uint8_t btn3;
uint8_t btn4;
};
void exvs2_io_najv4_config_load(struct exvs2_najv4_config *cfg, const wchar_t *filename);

93
exvs2io/exvs2io.c Normal file
View File

@ -0,0 +1,93 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include <stdbool.h>
#include "exvs2io/exvs2io.h"
#include "exvs2io/config.h"
#include "util/dprintf.h"
static bool exvs2_io_coin = false;
static uint16_t exvs2_coin_ct = 0;
static struct exvs2_najv4_config najv4_cfg;
uint16_t exvs2_io_get_api_version(void)
{
return 0x0100;
}
HRESULT exvs2_io_jvs_init(void)
{
dprintf("exvs2 IO: JVS Init\n");
exvs2_io_najv4_config_load(&najv4_cfg, L".\\bananatools.ini");
return S_OK;
}
HRESULT exvs2_io_jvs_poll(uint8_t *opbtn, uint16_t *gamepad)
{
*opbtn = 0;
*gamepad = 0;
if ((GetAsyncKeyState(najv4_cfg.test) & 0x8000)) {
*opbtn |= EXVS2_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(najv4_cfg.service) & 0x8000) {
*opbtn |= EXVS2_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(najv4_cfg.up) & 0x8000) {
*gamepad |= EXVS2_IO_GAMEBTN_UP;
}
if (GetAsyncKeyState(najv4_cfg.down) & 0x8000) {
*gamepad |= EXVS2_IO_GAMEBTN_DOWN;
}
if (GetAsyncKeyState(najv4_cfg.left) & 0x8000) {
*gamepad |= EXVS2_IO_GAMEBTN_LEFT;
}
if (GetAsyncKeyState(najv4_cfg.right) & 0x8000) {
*gamepad |= EXVS2_IO_GAMEBTN_RIGHT;
}
if (GetAsyncKeyState(najv4_cfg.start) & 0x8000) {
*gamepad |= EXVS2_IO_GAMEBTN_START;
}
if (GetAsyncKeyState(najv4_cfg.btn1) & 0x8000) {
*gamepad |= EXVS2_IO_GAMEBTN_1;
}
if (GetAsyncKeyState(najv4_cfg.btn1) & 0x8000) {
*gamepad |= EXVS2_IO_GAMEBTN_2;
}
if (GetAsyncKeyState(najv4_cfg.btn1) & 0x8000) {
*gamepad |= EXVS2_IO_GAMEBTN_3;
}
if (GetAsyncKeyState(najv4_cfg.btn1) & 0x8000) {
*gamepad |= EXVS2_IO_GAMEBTN_4;
}
return S_OK;
}
void exvs2_io_jvs_read_coin_counter(uint16_t *coins)
{
if (GetAsyncKeyState(najv4_cfg.coin) & 0x8000) {
if (!exvs2_io_coin) {
exvs2_io_coin = true;
exvs2_coin_ct++;
}
} else {
exvs2_io_coin = false;
}
*coins = exvs2_coin_ct;
}

7
exvs2io/exvs2io.def Normal file
View File

@ -0,0 +1,7 @@
LIBRARY exvs2hook
EXPORTS
exvs2_io_get_api_version
exvs2_io_jvs_init
exvs2_io_jvs_poll
exvs2_io_read_coin_counter

51
exvs2io/exvs2io.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <windows.h>
#include <stdint.h>
#include "exvs2io/config.h"
enum {
EXVS2_IO_OPBTN_TEST = 0x01,
EXVS2_IO_OPBTN_SERVICE = 0x02,
EXVS2_IO_OPBTN_COIN = 0x20,
};
enum {
EXVS2_IO_GAMEBTN_1 = 0x1,
EXVS2_IO_GAMEBTN_2 = 0x2,
EXVS2_IO_GAMEBTN_3 = 0x4,
EXVS2_IO_GAMEBTN_4 = 0x8,
EXVS2_IO_GAMEBTN_UP = 0x10,
EXVS2_IO_GAMEBTN_DOWN = 0x20,
EXVS2_IO_GAMEBTN_LEFT = 0x40,
EXVS2_IO_GAMEBTN_RIGHT = 0x80,
EXVS2_IO_GAMEBTN_START = 0x100,
};
/* Get the version of the Pokken 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 exvs2_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after exvs2_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT exvs2_io_jvs_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 exvs2_io_jvs_poll(uint8_t *opbtn, uint16_t *gamepad);
void exvs2_io_jvs_read_coin_counter(uint16_t *coins);

16
exvs2io/meson.build Normal file
View File

@ -0,0 +1,16 @@
exvs2io_lib = static_library(
'exvs2io',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
xinput_lib,
],
sources : [
'exvs2io.c',
'exvs2io.h',
'config.c',
'config.h',
],
)

View File

@ -56,6 +56,12 @@ subdir('amcus')
subdir('ferrumio')
subdir('taikoio')
subdir('exvs2io')
subdir('saoio')
subdir('mkacio')
subdir('taikohook')
subdir('ferrumhook')
subdir('exvs2hook')
subdir('saohook')
subdir('mkachook')

47
mkachook/config.c Normal file
View File

@ -0,0 +1,47 @@
#include <assert.h>
#include <stddef.h>
#include "mkachook/config.h"
#include "platform/config.h"
void mkac_dll_config_load(
struct mkac_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"mkacio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void mkac_xinput_config_load(
struct mkac_xinput_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"xinput", L"enable", 1, filename);
}
void mkac_hook_config_load(
struct mkac_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
mkac_dll_config_load(&cfg->dll, filename);
mkac_xinput_config_load(&cfg->xinput, filename);
gfx_config_load(&cfg->gfx, filename);
bpreader_config_load(&cfg->reader, filename);
}

34
mkachook/config.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <stddef.h>
#include "mkachook/mkac-dll.h"
#include "mkachook/xinput.h"
#include "mkachook/jvs.h"
#include "board/config.h"
#include "platform/config.h"
#include "gfxhook/config.h"
#include "amcus/config.h"
struct mkac_hook_config {
struct platform_config platform;
struct mkac_dll_config dll;
struct mkac_xinput_config xinput;
struct gfx_config gfx;
struct amcus_config amcus;
struct bpreader_config reader;
};
void mkac_dll_config_load(
struct mkac_dll_config *cfg,
const wchar_t *filename);
void mkac_xinput_config_load(
struct mkac_xinput_config *cfg,
const wchar_t *filename);
void mkac_hook_config_load(
struct mkac_hook_config *cfg,
const wchar_t *filename);

103
mkachook/dllmain.c Normal file
View File

@ -0,0 +1,103 @@
#include <windows.h>
#include <stdlib.h>
#include "mkachook/config.h"
#include "mkachook/mkac-dll.h"
#include "mkachook/xinput.h"
#include "mkachook/jvs.h"
#include "amcus/amcus.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "hooklib/debug.h"
#include "platform/platform.h"
#include "gfxhook/gfx.h"
#include "gfxhook/dxgi.h"
#include "gfxhook/d3d11.h"
#include "board/bpreader.h"
#include "util/dprintf.h"
static HMODULE mkac_hook_mod;
static process_entry_t mkac_startup;
static struct mkac_hook_config mkac_hook_cfg;
static DWORD CALLBACK mkac_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin mkac_pre_startup ---\n");
mkac_hook_config_load(&mkac_hook_cfg, L".\\bananatools.ini");
serial_hook_init();
struct dongle_info dinfo;
dinfo.vid = 0x0B9A;
dinfo.pid = 0x0C10;
wcscpy_s(dinfo.manufacturer, _countof(dinfo.manufacturer), L"BM");
wcscpy_s(dinfo.product, _countof(dinfo.product), L"RUDI04GBN-274713");
hr = platform_hook_init(&mkac_hook_cfg.platform, PLATFORM_ES3, mkac_jvs_init, mkac_hook_mod, dinfo);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = mkac_dll_init(&mkac_hook_cfg.dll, mkac_hook_mod);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = mkac_xinput_init(&mkac_hook_cfg.xinput);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = amcus_hook_init(&mkac_hook_cfg.amcus);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = bpreader_init(&mkac_hook_cfg.reader, 4);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
debug_hook_init();
gfx_hook_init(&mkac_hook_cfg.gfx);
gfx_d3d11_hook_init(&mkac_hook_cfg.gfx, mkac_hook_mod);
gfx_dxgi_hook_init(&mkac_hook_cfg.gfx, mkac_hook_mod);
dprintf("--- End mkac_pre_startup ---\n");
return mkac_startup();
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
mkac_hook_mod = mod;
hr = process_hijack_startup(mkac_pre_startup, &mkac_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

100
mkachook/jvs.c Normal file
View File

@ -0,0 +1,100 @@
#include <windows.h>
#include <assert.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.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"
#include "board/najv4.h"
#include "mkachook/jvs.h"
#include "mkachook/mkac-dll.h"
static void mkac_jvs_read_switches(void *ctx, struct najv4_switch_state *out);
static void mkac_jvs_read_coin_counter(void *ctx, uint8_t slot_no, uint16_t *out);
static const struct najv4_ops mkac_jvs_najv4_ops = {
.read_switches = mkac_jvs_read_switches,
.read_coin_counter = mkac_jvs_read_coin_counter,
};
static struct najv4 mkac_jvs_najv4;
HRESULT mkac_jvs_init(struct jvs_node **out)
{
HRESULT hr;
assert(out != NULL);
assert(mkac_dll.jvs_init != NULL);
dprintf("mkac JVS: Starting IO backend\n");
hr = mkac_dll.jvs_init();
if (FAILED(hr)) {
dprintf("mkac JVS: Backend error, I/O disconnected: %x\n", (int) hr);
return hr;
}
najv4_init(&mkac_jvs_najv4, NULL, &mkac_jvs_najv4_ops, NULL);
*out = najv4_to_jvs_node(&mkac_jvs_najv4);
return S_OK;
}
static void mkac_jvs_read_switches(void *ctx, struct najv4_switch_state *out)
{
uint8_t opbtn = 0;
//dprintf("mkac JVS: Read Switches\n");
assert(out != NULL);
assert(mkac_dll.jvs_poll != NULL);
mkac_dll.jvs_poll(&opbtn);
out->system = 0;
out->p1 = 0;
out->p2 = 0;
if (opbtn & 0x01) { // Test
out->system = 0x80;
}
if (opbtn & 0x02) { // Service
out->p1 |= 0x4000;
}
if (opbtn & 0x04) { // Up
out->p1 |= 0x2000;
}
if (opbtn & 0x08) { // Down
out->p1 |= 0x1000;
}
if (opbtn & 0x10) { // Enter
out->p1 |= 0x0200;
}
}
static void mkac_jvs_read_coin_counter(void *ctx, uint8_t slot_no, uint16_t *out)
{
//dprintf("mkac JVS: Read coin counter\n");
assert(out != NULL);
assert(mkac_dll.jvs_read_coin_counter != NULL);
if (slot_no > 0) {
return;
}
mkac_dll.jvs_read_coin_counter(out);
}

8
mkachook/jvs.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "jvs/jvs-bus.h"
HRESULT mkac_jvs_init(struct jvs_node **root);

34
mkachook/meson.build Normal file
View File

@ -0,0 +1,34 @@
shared_library(
'mkachook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'mkachook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
xinput_lib,
],
link_with : [
mkacio_lib,
amcus_lib,
platform_lib,
util_lib,
hooklib_lib,
gfxhook_lib,
jvs_lib,
board_lib,
],
sources : [
'dllmain.c',
'config.c',
'config.h',
'mkac-dll.c',
'mkac-dll.h',
'xinput.c',
'xinput.h',
'jvs.c',
'jvs.h',
],
)

106
mkachook/mkac-dll.c Normal file
View File

@ -0,0 +1,106 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "mkachook/mkac-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym mkac_dll_syms[] = {
{
.sym = "mkac_io_jvs_init",
.off = offsetof(struct mkac_dll, jvs_init),
},{
.sym = "mkac_io_gamepad_init",
.off = offsetof(struct mkac_dll, gamepad_init),
}, {
.sym = "mkac_io_jvs_poll",
.off = offsetof(struct mkac_dll, jvs_poll),
}, {
.sym = "mkac_io_gamepad_poll",
.off = offsetof(struct mkac_dll, gamepad_poll),
}, {
.sym = "mkac_io_jvs_read_coin_counter",
.off = offsetof(struct mkac_dll, jvs_read_coin_counter),
}
};
struct mkac_dll mkac_dll;
HRESULT mkac_dll_init(const struct mkac_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("mkac IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("mkac IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "mkac_io_get_api_version");
if (get_api_version != NULL) {
mkac_dll.api_version = get_api_version();
} else {
mkac_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose mkac_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (mkac_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("mkac IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
mkac_dll.api_version);
goto end;
}
sym = mkac_dll_syms;
hr = dll_bind(&mkac_dll, src, &sym, _countof(mkac_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("mkac 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;
}

22
mkachook/mkac-dll.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <windows.h>
#include "mkacio/mkacio.h"
struct mkac_dll {
uint16_t api_version;
HRESULT (*jvs_init)(void);
HRESULT (*gamepad_init)(void);
HRESULT (*jvs_poll)(uint8_t *opbtn);
HRESULT (*gamepad_poll)(uint16_t *gamebtn);
void (*jvs_read_coin_counter)(uint16_t *coins);
};
struct mkac_dll_config {
wchar_t path[MAX_PATH];
};
extern struct mkac_dll mkac_dll;
HRESULT mkac_dll_init(const struct mkac_dll_config *cfg, HINSTANCE self);

9
mkachook/mkachook.def Normal file
View File

@ -0,0 +1,9 @@
LIBRARY mkachook
EXPORTS
mkac_io_get_api_version
mkac_io_jvs_init
mkac_io_gamepad_init
mkac_io_jvs_poll
mkac_io_gamepad_poll
mkac_io_jvs_read_coin_counter

154
mkachook/xinput.c Normal file
View File

@ -0,0 +1,154 @@
#include <windows.h>
#include <xinput.h>
#include <stdint.h>
#ifdef __GNUC__
#include <ntdef.h>
#else
#include <winnt.h>
#endif
#include "hook/table.h"
#include "mkachook/xinput.h"
#include "mkachook/mkac-dll.h"
#include "util/dprintf.h"
#include "util/str.h"
static DWORD WINAPI my_XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities);
static DWORD WINAPI my_XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState);
static DWORD WINAPI my_XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration);
static DWORD my_driverUtilGetControllerUsbIdPairs(uint64_t qwUnknown, unsigned int *numControllers);
static DWORD (WINAPI *next_XInputGetCapabilities)(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities);
static DWORD (WINAPI *next_XInputGetState)(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities);
static DWORD (WINAPI *next_XInputSetState)(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities);
static DWORD (*next_driverUtilGetControllerUsbIdPairs)(uint64_t qwUnknown, unsigned int *numControllers);
uint8_t lastBtnState = 0;
DWORD packetNum = 0;
static const struct hook_symbol xinput_hook_syms[] = {
{
.ordinal = 4,
.patch = my_XInputGetCapabilities,
.link = (void **) &next_XInputGetCapabilities,
}, {
.ordinal = 2,
.patch = my_XInputGetState,
.link = (void **) &next_XInputGetState,
}, {
.ordinal = 3,
.patch = my_XInputSetState,
.link = (void **) &next_XInputSetState,
},
};
static const struct hook_symbol driverutil_hook_syms[] = {
{
.name = "driverUtilGetControllerUsbIdPairs",
.ordinal = 1,
.patch = my_driverUtilGetControllerUsbIdPairs,
.link = (void **) &next_driverUtilGetControllerUsbIdPairs,
}
};
HRESULT mkac_xinput_init(struct mkac_xinput_config *cfg)
{
if (!cfg->enable) {
dprintf("Xinput: Emulation disabled\n");
return S_OK;
}
dprintf("Xinput: init\n");
hook_table_apply(
NULL,
"XINPUT1_3.dll",
xinput_hook_syms,
_countof(xinput_hook_syms));
hook_table_apply(
NULL,
"driverUtil.dll",
driverutil_hook_syms,
_countof(driverutil_hook_syms));
return S_OK;
}
static DWORD WINAPI my_XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities)
{
//dprintf("Xinput: %s dwUserIndex %li dwFlags %li \n", __func__, dwUserIndex, dwFlags);
if (!dwUserIndex) {
HRESULT hr = mkac_dll.gamepad_init();
if (FAILED(hr)) {
return ERROR_DEVICE_NOT_CONNECTED;
}
pCapabilities->Flags = XINPUT_CAPS_VOICE_SUPPORTED;
pCapabilities->Type = XINPUT_DEVTYPE_GAMEPAD;
pCapabilities->SubType = XINPUT_DEVSUBTYPE_GAMEPAD;
pCapabilities->Gamepad.wButtons = 0xF3FF;
pCapabilities->Gamepad.bLeftTrigger = 0xFF;
pCapabilities->Gamepad.bRightTrigger = 0xFF;
pCapabilities->Gamepad.sThumbLX = (SHORT)0xFFC0;
pCapabilities->Gamepad.sThumbLY = (SHORT)0xFFC0;
pCapabilities->Gamepad.sThumbRX = (SHORT)0xFFC0;
pCapabilities->Gamepad.sThumbRY = (SHORT)0xFFC0;
pCapabilities->Vibration.wLeftMotorSpeed = 0xFF;
pCapabilities->Vibration.wRightMotorSpeed = 0xFF;
return ERROR_SUCCESS;
} else {
return ERROR_DEVICE_NOT_CONNECTED;
}
}
static DWORD WINAPI my_XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState)
{
//dprintf("Xinput: %s dwUserIndex %li\n", __func__, dwUserIndex);
assert(mkac_dll.gamepad_poll != NULL);
if (!dwUserIndex) {
uint16_t gamebtn = 0;
mkac_dll.gamepad_poll(&gamebtn);
pState->Gamepad.wButtons = gamebtn;
if (gamebtn == lastBtnState) {
pState->dwPacketNumber = packetNum;
} else {
pState->dwPacketNumber = packetNum++;
}
return ERROR_SUCCESS;
}
else {
return ERROR_DEVICE_NOT_CONNECTED;
}
}
static DWORD WINAPI my_XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration)
{
//dprintf("Xinput: %s dwUserIndex %li left %i right %i\n", __func__, dwUserIndex, pVibration->wLeftMotorSpeed, pVibration->wRightMotorSpeed);
if (!dwUserIndex)
return ERROR_SUCCESS;
else
return ERROR_DEVICE_NOT_CONNECTED;
}
static DWORD my_driverUtilGetControllerUsbIdPairs(uint64_t qwUnknown, unsigned int *numControllers)
{
dprintf("Xinput: %s hit!\n", __func__);
return 1;
}

12
mkachook/xinput.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
struct mkac_xinput_config {
bool enable;
};
HRESULT mkac_xinput_init(struct mkac_xinput_config *cfg);

32
mkacio/config.c Normal file
View File

@ -0,0 +1,32 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "mkacio/config.h"
void mkac_io_najv4_config_load(struct mkac_najv4_config *cfg, const wchar_t *filename)
{
cfg->test = GetPrivateProfileIntW(L"najv4", L"test", VK_HOME, filename);
cfg->service = GetPrivateProfileIntW(L"najv4", L"service", VK_DELETE, filename);
cfg->coin = GetPrivateProfileIntW(L"najv4", L"coin", VK_INSERT, filename);
cfg->up = GetPrivateProfileIntW(L"najv4", L"up", VK_UP, filename);
cfg->down = GetPrivateProfileIntW(L"najv4", L"down", VK_DOWN, filename);
cfg->enter = GetPrivateProfileIntW(L"najv4", L"enter", VK_RETURN, filename);
}
void mkac_io_gamepad_config_load(struct mkac_gamepad_config *cfg, const wchar_t *filename)
{
cfg->dpad_up = GetPrivateProfileIntW(L"gamepad", L"dpad_up", 'W', filename);
cfg->dpad_down = GetPrivateProfileIntW(L"gamepad", L"dpad_down", 'S', filename);
cfg->dpad_left = GetPrivateProfileIntW(L"gamepad", L"dpad_left", 'A', filename);
cfg->dpad_right = GetPrivateProfileIntW(L"gamepad", L"dpad_right", 'D', filename);
cfg->btn_a = GetPrivateProfileIntW(L"gamepad", L"button_a", 'O', filename);
cfg->btn_b = GetPrivateProfileIntW(L"gamepad", L"button_b", 'K', filename);
cfg->btn_x = GetPrivateProfileIntW(L"gamepad", L"button_x", 'I', filename);
cfg->btn_y = GetPrivateProfileIntW(L"gamepad", L"button_y", 'J', filename);
cfg->trigger_l = GetPrivateProfileIntW(L"gamepad", L"trigger_l", 'Q', filename);
cfg->trigger_r = GetPrivateProfileIntW(L"gamepad", L"trigger_r", 'E', filename);
cfg->btn_start = GetPrivateProfileIntW(L"gamepad", L"button_start", VK_LSHIFT, filename);
}

30
mkacio/config.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
struct mkac_najv4_config {
uint8_t test;
uint8_t service;
uint8_t up;
uint8_t down;
uint8_t enter;
uint8_t coin;
};
struct mkac_gamepad_config {
uint8_t dpad_up;
uint8_t dpad_down;
uint8_t dpad_left;
uint8_t dpad_right;
uint8_t btn_a;
uint8_t btn_b;
uint8_t btn_x;
uint8_t btn_y;
uint8_t trigger_l;
uint8_t trigger_r;
uint8_t btn_start;
};
void mkac_io_najv4_config_load(struct mkac_najv4_config *cfg, const wchar_t *filename);
void mkac_io_gamepad_config_load(struct mkac_gamepad_config *cfg, const wchar_t *filename);

16
mkacio/meson.build Normal file
View File

@ -0,0 +1,16 @@
mkacio_lib = static_library(
'mkacio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
xinput_lib,
],
sources : [
'mkacio.c',
'mkacio.h',
'config.c',
'config.h',
],
)

128
mkacio/mkacio.c Normal file
View File

@ -0,0 +1,128 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include <stdbool.h>
#include "mkacio/mkacio.h"
#include "mkacio/config.h"
#include "util/dprintf.h"
static bool mkac_io_coin = false;
static bool mkac_test_toggle = false;
static uint16_t mkac_coin_ct = 0;
static struct mkac_gamepad_config gamepad_cfg;
static struct mkac_najv4_config najv4_cfg;
uint16_t mkac_io_get_api_version(void)
{
return 0x0100;
}
HRESULT mkac_io_jvs_init(void)
{
dprintf("mkac IO: JVS Init\n");
mkac_io_najv4_config_load(&najv4_cfg, L".\\bananatools.ini");
return S_OK;
}
HRESULT mkac_io_gamepad_init(void)
{
dprintf("mkac IO: Gamepad Init\n");
mkac_io_gamepad_config_load(&gamepad_cfg, L".\\bananatools.ini");
return S_OK;
}
HRESULT mkac_io_jvs_poll(uint8_t *opbtn)
{
*opbtn = 0;
if ((GetAsyncKeyState(najv4_cfg.test) & 0x8000)) {
*opbtn |= mkac_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(najv4_cfg.service) & 0x8000) {
*opbtn |= mkac_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(najv4_cfg.up) & 0x8000) {
*opbtn |= mkac_IO_OPBTN_UP;
}
if (GetAsyncKeyState(najv4_cfg.down) & 0x8000) {
*opbtn |= mkac_IO_OPBTN_DOWN;
}
if (GetAsyncKeyState(najv4_cfg.enter) & 0x8000) {
*opbtn |= mkac_IO_OPBTN_ENTER;
}
return S_OK;
}
HRESULT mkac_io_gamepad_poll(uint16_t *gamepad)
{
*gamepad = 0;
if (GetAsyncKeyState(gamepad_cfg.dpad_up) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_UP;
}
if (GetAsyncKeyState(gamepad_cfg.dpad_left) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_LEFT;
}
if (GetAsyncKeyState(gamepad_cfg.dpad_down) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_DOWN;
}
if (GetAsyncKeyState(gamepad_cfg.dpad_right) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_RIGHT;
}
if (GetAsyncKeyState(gamepad_cfg.trigger_l) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_TRIGGER_L;
}
if (GetAsyncKeyState(gamepad_cfg.trigger_r) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_TRIGGER_R;
}
if (GetAsyncKeyState(gamepad_cfg.btn_a) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_A;
}
if (GetAsyncKeyState(gamepad_cfg.btn_b) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_B;
}
if (GetAsyncKeyState(gamepad_cfg.btn_x) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_X;
}
if (GetAsyncKeyState(gamepad_cfg.btn_y) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_Y;
}
if (GetAsyncKeyState(gamepad_cfg.btn_start) & 0x8000) {
*gamepad |= mkac_IO_GAMEBTN_START;
}
return S_OK;
}
void mkac_io_jvs_read_coin_counter(uint16_t *coins)
{
if (GetAsyncKeyState(VK_INSERT) & 0x8000) {
if (!mkac_io_coin) {
mkac_io_coin = true;
mkac_coin_ct++;
}
} else {
mkac_io_coin = false;
}
*coins = mkac_coin_ct;
}

9
mkacio/mkacio.def Normal file
View File

@ -0,0 +1,9 @@
LIBRARY mkachook
EXPORTS
mkac_io_get_api_version
mkac_io_jvs_init
mkac_io_gamepad_init
mkac_io_jvs_poll
mkac_io_gamepad_poll
mkac_io_read_coin_counter

68
mkacio/mkacio.h Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#include <windows.h>
#include <stdint.h>
#include "mkacio/config.h"
enum {
mkac_IO_OPBTN_TEST = 0x01,
mkac_IO_OPBTN_SERVICE = 0x02,
mkac_IO_OPBTN_UP = 0x04,
mkac_IO_OPBTN_DOWN = 0x08,
mkac_IO_OPBTN_ENTER = 0x10,
mkac_IO_OPBTN_COIN = 0x20,
};
// Chagned to match xinput masks for ease of use
enum {
mkac_IO_GAMEBTN_UP = 0x0001,
mkac_IO_GAMEBTN_DOWN = 0x0002,
mkac_IO_GAMEBTN_LEFT = 0x0004,
mkac_IO_GAMEBTN_RIGHT = 0x0008,
mkac_IO_GAMEBTN_START = 0x0010,
mkac_IO_GAMEBTN_TRIGGER_L = 0x0100,
mkac_IO_GAMEBTN_TRIGGER_R = 0x0200,
mkac_IO_GAMEBTN_A = 0x2000,
mkac_IO_GAMEBTN_B = 0x1000,
mkac_IO_GAMEBTN_X = 0x8000,
mkac_IO_GAMEBTN_Y = 0x4000,
};
/* Get the version of the Pokken 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 mkac_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after mkac_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT mkac_io_jvs_init(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after mkac_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT mkac_io_gamepad_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 mkac_io_jvs_poll(uint8_t *opbtn);
HRESULT mkac_io_gamepad_poll(uint16_t *gamebtn);
void mkac_io_jvs_read_coin_counter(uint16_t *coins);

38
saohook/config.c Normal file
View File

@ -0,0 +1,38 @@
#include <assert.h>
#include <stddef.h>
#include "saohook/config.h"
#include "platform/config.h"
void sao_dll_config_load(
struct sao_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"saoio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void sao_hook_config_load(
struct sao_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
sao_dll_config_load(&cfg->dll, filename);
gfx_config_load(&cfg->gfx, filename);
qr_config_load(&cfg->qr, filename);
bpreader_config_load(&cfg->reader, filename);
usio_config_load(&cfg->usio, filename);
}

34
saohook/config.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <stddef.h>
#include "saohook/sao-dll.h"
#include "platform/config.h"
#include "gfxhook/config.h"
#include "amcus/config.h"
#include "board/config.h"
struct sao_hook_config {
struct platform_config platform;
struct aime_config aime;
struct sao_dll_config dll;
struct gfx_config gfx;
struct amcus_config amcus;
struct qr_config qr;
struct bpreader_config reader;
struct usio_config usio;
};
void sao_dll_config_load(
struct sao_dll_config *cfg,
const wchar_t *filename);
void qr_config_load(
struct qr_config *cfg,
const wchar_t *filename);
void sao_hook_config_load(
struct sao_hook_config *cfg,
const wchar_t *filename);

107
saohook/dllmain.c Normal file
View File

@ -0,0 +1,107 @@
#include <windows.h>
#include <stdlib.h>
#include "saohook/config.h"
#include "saohook/sao-dll.h"
#include "saohook/usio.h"
#include "saohook/unity.h"
#include "amcus/amcus.h"
#include "hook/process.h"
#include "hooklib/serial.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "platform/platform.h"
#include "gfxhook/gfx.h"
#include "gfxhook/dxgi.h"
#include "gfxhook/d3d11.h"
#include "util/dprintf.h"
static HMODULE sao_hook_mod;
static process_entry_t sao_startup;
static struct sao_hook_config sao_hook_cfg;
static DWORD CALLBACK sao_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin sao_pre_startup ---\n");
sao_hook_config_load(&sao_hook_cfg, L".\\bananatools.ini");
serial_hook_init();
struct dongle_info dinfo;
dinfo.pid = 0x0C00;
dinfo.vid = 0x0B9A;
hr = platform_hook_init(&sao_hook_cfg.platform, PLATFORM_BNA1, NULL, sao_hook_mod, dinfo);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = sg_reader_hook_init(&sao_hook_cfg.aime, 1, sao_hook_mod);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = vfd_hook_init(2);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = sao_dll_init(&sao_hook_cfg.dll, sao_hook_mod);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = sao_usio_hook_init(&sao_hook_cfg.usio);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
hr = amcus_hook_init(&sao_hook_cfg.amcus);
if (FAILED(hr)) {
ExitProcess(EXIT_FAILURE);
}
unity_hook_init();
gfx_hook_init(&sao_hook_cfg.gfx);
gfx_d3d11_hook_init(&sao_hook_cfg.gfx, sao_hook_mod);
gfx_dxgi_hook_init(&sao_hook_cfg.gfx, sao_hook_mod);
dprintf("--- End sao_pre_startup ---\n");
return sao_startup();
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
sao_hook_mod = mod;
hr = process_hijack_startup(sao_pre_startup, &sao_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

33
saohook/meson.build Normal file
View File

@ -0,0 +1,33 @@
shared_library(
'saohook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'saohook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
xinput_lib,
],
link_with : [
saoio_lib,
amcus_lib,
platform_lib,
util_lib,
hooklib_lib,
gfxhook_lib,
jvs_lib,
board_lib
],
sources : [
'dllmain.c',
'config.c',
'config.h',
'sao-dll.c',
'sao-dll.h',
'usio.c',
'usio.h',
'unity.c',
],
)

103
saohook/sao-dll.c Normal file
View File

@ -0,0 +1,103 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "saohook/sao-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym sao_dll_syms[] = {
{
.sym = "sao_io_init",
.off = offsetof(struct sao_dll, init),
}, {
.sym = "sao_io_read_coin_counter",
.off = offsetof(struct sao_dll, read_coin_counter),
}, {
.sym = "sao_io_get_opbtns",
.off = offsetof(struct sao_dll, get_opbtns),
}, {
.sym = "sao_io_get_drum_analog",
.off = offsetof(struct sao_dll, get_drum_analog),
}
};
struct sao_dll sao_dll;
HRESULT sao_dll_init(const struct sao_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("Sao IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Sao IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "sao_io_get_api_version");
if (get_api_version != NULL) {
sao_dll.api_version = get_api_version();
} else {
sao_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose sao_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (sao_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Sao IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
sao_dll.api_version);
goto end;
}
sym = sao_dll_syms;
hr = dll_bind(&sao_dll, src, &sym, _countof(sao_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Sao 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;
}

22
saohook/sao-dll.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <windows.h>
#include "saoio/saoio.h"
struct sao_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*poll)(void);
void (*read_coin_counter)(uint16_t *coins, uint16_t *services);
void (*get_opbtns)(uint8_t *opbtn);
void (*get_drum_analog)(uint8_t *gamebtn);
};
struct sao_dll_config {
wchar_t path[MAX_PATH];
};
extern struct sao_dll sao_dll;
HRESULT sao_dll_init(const struct sao_dll_config *cfg, HINSTANCE self);

8
saohook/saohook.def Normal file
View File

@ -0,0 +1,8 @@
LIBRARY saohook
EXPORTS
sao_io_get_api_version
sao_io_init
sao_io_read_coin_counter
sao_io_get_drum_analog
sao_io_get_opbtns

105
saohook/unity.c Normal file
View File

@ -0,0 +1,105 @@
#include <stdbool.h>
#include <windows.h>
#include "hook/table.h"
#include "hooklib/dll.h"
#include "hooklib/path.h"
#include "amcus/amcus.h"
#include "board/usio.h"
#include "util/dprintf.h"
static void dll_hook_insert_hooks(HMODULE target);
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name);
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
static const struct hook_symbol unity_kernel32_syms[] = {
{
.name = "LoadLibraryW",
.patch = my_LoadLibraryW,
.link = (void **) &next_LoadLibraryW,
},
};
static const wchar_t *target_modules[] = {
L"mono.dll",
L"cri_ware_unity.dll",
L"aime_rw_adapter.dll",
L"AMPFServiceClient.dll",
L"bnAMPF.dll",
L"bnAMUpdater.dll",
L"bnReader.dll",
L"libamw.dll",
};
static const size_t target_modules_len = _countof(target_modules);
void unity_hook_init(void)
{
dll_hook_insert_hooks(NULL);
}
static void dll_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"kernel32.dll",
unity_kernel32_syms,
_countof(unity_kernel32_syms));
}
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
{
const wchar_t *name_end;
const wchar_t *target_module;
bool already_loaded;
HMODULE result;
size_t name_len;
size_t target_module_len;
if (name == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
// Check if the module is already loaded
already_loaded = GetModuleHandleW(name) != NULL;
// Must call the next handler so the DLL reference count is incremented
result = next_LoadLibraryW(name);
if (!already_loaded && result != NULL) {
name_len = wcslen(name);
for (size_t i = 0; i < target_modules_len; i++) {
target_module = target_modules[i];
target_module_len = wcslen(target_module);
// Check if the newly loaded library is at least the length of
// the name of the target module
if (name_len < target_module_len) {
continue;
}
name_end = &name[name_len - target_module_len];
// Check if the name of the newly loaded library is one of the
// modules the path hooks should be injected into
if (_wcsicmp(name_end, target_module) != 0) {
continue;
}
dprintf("Unity: Loaded %S\n", target_module);
dll_hook_insert_hooks(result);
path_hook_insert_hooks(result);
amcus_insert_hooks(result);
usio_hook_proc_addr(result);
}
}
return result;
}

3
saohook/unity.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void unity_hook_init(void);

82
saohook/usio.c Normal file
View File

@ -0,0 +1,82 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "board/usio.h"
#include "saohook/sao-dll.h"
#include "util/dprintf.h"
bool sao_io_coin = false;
uint16_t sao_io_coins = 0;
static HRESULT sao_usio_poll(void *ctx, struct usio_state *state);
static const struct usio_ops sao_usio_ops = {
.poll = sao_usio_poll,
};
HRESULT sao_usio_hook_init(const struct usio_config *cfg)
{
HRESULT hr;
assert(sao_dll.init != NULL);
hr = usio_hook_init(cfg, &sao_usio_ops, NULL, NULL);
if (FAILED(hr)) {
return hr;
}
dprintf("Sao USIO: Init\n");
return sao_dll.init();
}
static HRESULT sao_usio_poll(void *ctx, struct usio_state *state)
{
uint8_t opbtn_out = 0;
uint8_t analog_out = 0;
uint16_t coin_ct = 0;
uint16_t service_ct = 0;
sao_dll.get_opbtns(&opbtn_out);
sao_dll.get_drum_analog(&analog_out);
sao_dll.read_coin_counter(&coin_ct, &service_ct);
state->op_btns = 0;
state->p1_btns = 0;
state->p2_btns = 0;
if (opbtn_out & 0x01) {
state->op_btns |= 0x80; // Test
}
if (opbtn_out & 0x02) {
state->p1_btns |= 0x40; // Service
}
if (opbtn_out & 0x04) {
state->p1_btns |= 0x20; // Up
}
if (opbtn_out & 0x08) {
state->p1_btns |= 0x10; // Down
}
if (opbtn_out & 0x10) {
state->p1_btns |= 0x02; // Enter
}
for (int i = 0; i < _countof(state->analog); i++) {
if (analog_out & 1 << i) {
state->analog[i] = 0x3FFF;
} else {
state->analog[i] = 0x00;
}
}
state->coins[0].current_coin_count = coin_ct;
state->service.current_coin_count = service_ct;
return S_OK;
}

7
saohook/usio.h Normal file
View File

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

27
saoio/config.c Normal file
View File

@ -0,0 +1,27 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "saoio/config.h"
void sao_io_config_load(struct sao_input_config *cfg, const wchar_t *filename)
{
cfg->test = GetPrivateProfileIntW(L"usio", L"test", VK_HOME, filename);
cfg->service = GetPrivateProfileIntW(L"usio", L"service", VK_DELETE, filename);
cfg->coin = GetPrivateProfileIntW(L"usio", L"coin", VK_INSERT, filename);
cfg->up = GetPrivateProfileIntW(L"usio", L"up", VK_UP, filename);
cfg->down = GetPrivateProfileIntW(L"usio", L"down", VK_DOWN, filename);
cfg->enter = GetPrivateProfileIntW(L"usio", L"enter", VK_RETURN, filename);
cfg->p1_rim_l = GetPrivateProfileIntW(L"drum", L"p1_rim_l", 'Z', filename);
cfg->p1_center_l = GetPrivateProfileIntW(L"usio", L"p1_center_l", 'X', filename);
cfg->p1_center_r = GetPrivateProfileIntW(L"usio", L"p1_center_r", 'C', filename);
cfg->p1_rim_r = GetPrivateProfileIntW(L"usio", L"p1_rim_r", 'V', filename);
cfg->p2_rim_l = GetPrivateProfileIntW(L"drum", L"p2_rim_l", 'U', filename);
cfg->p2_center_l = GetPrivateProfileIntW(L"usio", L"p2_center_l", 'I', filename);
cfg->p2_center_r = GetPrivateProfileIntW(L"usio", L"p2_center_r", 'O', filename);
cfg->p2_rim_r = GetPrivateProfileIntW(L"usio", L"p2_rim_r", 'P', filename);
}

26
saoio/config.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#pragma pack(push, 1)
struct sao_input_config {
uint8_t test;
uint8_t service;
uint8_t up;
uint8_t down;
uint8_t enter;
uint8_t coin;
uint8_t p1_rim_l;
uint8_t p1_center_l;
uint8_t p1_center_r;
uint8_t p1_rim_r;
uint8_t p2_rim_l;
uint8_t p2_center_l;
uint8_t p2_center_r;
uint8_t p2_rim_r;
};
#pragma pack(pop)
void sao_io_config_load(struct sao_input_config *cfg, const wchar_t *filename);

16
saoio/meson.build Normal file
View File

@ -0,0 +1,16 @@
saoio_lib = static_library(
'saoio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
xinput_lib,
],
sources : [
'saoio.c',
'saoio.h',
'config.c',
'config.h',
],
)

123
saoio/saoio.c Normal file
View File

@ -0,0 +1,123 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include <stdbool.h>
#include "saoio/saoio.h"
#include "saoio/config.h"
#include "util/dprintf.h"
static bool sao_io_coin = false;
static bool sao_io_service = false;
static bool sao_test_toggle = false;
static bool sao_test_last_state = false;
static uint16_t sao_coin_ct = 0;
static uint16_t sao_service_ct = 0;
static struct sao_input_config cfg;
uint16_t sao_io_get_api_version(void)
{
return 0x0100;
}
HRESULT sao_io_init(void)
{
dprintf("Sao IO: Init\n");
sao_io_config_load(&cfg, L".\\bananatools.ini");
return S_OK;
}
void sao_io_get_opbtns(uint8_t *opbtn)
{
if ((GetAsyncKeyState(cfg.test) & 0x8000)) {
if (!sao_test_last_state) {
sao_test_toggle = !sao_test_toggle;
}
sao_test_last_state = true;
} else {
sao_test_last_state = false;
}
if (GetAsyncKeyState(cfg.service) & 0x8000) {
*opbtn |= SAO_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(cfg.up) & 0x8000) {
*opbtn |= SAO_IO_OPBTN_UP;
}
if (GetAsyncKeyState(cfg.down) & 0x8000) {
*opbtn |= SAO_IO_OPBTN_DOWN;
}
if (GetAsyncKeyState(cfg.enter) & 0x8000) {
*opbtn |= SAO_IO_OPBTN_ENTER;
}
if (sao_test_toggle) {
*opbtn |= SAO_IO_OPBTN_TEST;
}
}
void sao_io_get_drum_analog(uint8_t *gamebtn)
{
if (GetAsyncKeyState(cfg.p1_rim_l) & 0x8000) {
*gamebtn |= SAO_IO_P1_RIM_L;
}
if (GetAsyncKeyState(cfg.p1_center_l) & 0x8000) {
*gamebtn |= SAO_IO_P1_CENTER_L;
}
if (GetAsyncKeyState(cfg.p1_center_r) & 0x8000) {
*gamebtn |= SAO_IO_P1_CENTER_R;
}
if (GetAsyncKeyState(cfg.p1_rim_r) & 0x8000) {
*gamebtn |= SAO_IO_P1_RIM_R;
}
if (GetAsyncKeyState(cfg.p2_rim_l) & 0x8000) {
*gamebtn |= SAO_IO_P2_RIM_L;
}
if (GetAsyncKeyState(cfg.p2_center_l) & 0x8000) {
*gamebtn |= SAO_IO_P2_CENTER_L;
}
if (GetAsyncKeyState(cfg.p2_center_r) & 0x8000) {
*gamebtn |= SAO_IO_P2_CENTER_R;
}
if (GetAsyncKeyState(cfg.p2_rim_r) & 0x8000) {
*gamebtn |= SAO_IO_P2_RIM_R;
}
}
void sao_io_read_coin_counter(uint16_t *coins, uint16_t *services)
{
if (GetAsyncKeyState(cfg.coin) & 0x8000) {
if (!sao_io_coin) {
sao_io_coin = true;
sao_coin_ct++;
}
} else {
sao_io_coin = false;
}
if (GetAsyncKeyState(cfg.service) & 0x8000) {
if (!sao_io_service) {
sao_io_service = true;
sao_service_ct++;
}
} else {
sao_io_service = false;
}
*coins = sao_coin_ct;
*services = sao_service_ct;
}

8
saoio/saoio.def Normal file
View File

@ -0,0 +1,8 @@
LIBRARY saohook
EXPORTS
sao_io_get_api_version
sao_io_init
sao_io_read_coin_counter
sao_io_get_drum_analog
sao_io_get_opbtns

67
saoio/saoio.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
#include <windows.h>
#include <stdint.h>
#include "saoio/config.h"
enum {
SAO_IO_OPBTN_TEST = 0x01,
SAO_IO_OPBTN_SERVICE = 0x02,
SAO_IO_OPBTN_UP = 0x04,
SAO_IO_OPBTN_DOWN = 0x08,
SAO_IO_OPBTN_ENTER = 0x10,
};
enum {
SAO_IO_P1_RIM_L = 0x0001,
SAO_IO_P1_CENTER_L = 0x0002,
SAO_IO_P1_CENTER_R = 0x0004,
SAO_IO_P1_RIM_R = 0x0008,
SAO_IO_P2_RIM_L = 0x0100,
SAO_IO_P2_CENTER_L = 0x0200,
SAO_IO_P2_CENTER_R = 0x1000,
SAO_IO_P2_RIM_R = 0x2000,
};
/* Get the version of the Pokken 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 sao_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after sao_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT sao_io_init(void);
/* Get the state of the cabinet's operator buttons as of the last poll. See
SAO_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 sao_io_get_opbtns(uint8_t *opbtn);
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
SAO_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 sao_io_get_drum_analog(uint8_t *gamebtn);
void sao_io_read_coin_counter(uint16_t *coins, uint16_t *services);