add games
This commit is contained in:
parent
f7cd4f9d28
commit
8ff356b84c
38
Package.mk
38
Package.mk
@ -20,9 +20,44 @@ $(BUILD_DIR_ZIP)/taiko.zip:
|
|||||||
$(V)strip $(BUILD_DIR_ZIP)/taiko/*.{exe,dll}
|
$(V)strip $(BUILD_DIR_ZIP)/taiko/*.{exe,dll}
|
||||||
$(V)cd $(BUILD_DIR_ZIP)/taiko ; zip -r ../taiko.zip *
|
$(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: \
|
$(BUILD_DIR_ZIP)/doc.zip: \
|
||||||
$(DOC_DIR)/ferrumhook.md \
|
$(DOC_DIR)/ferrumhook.md \
|
||||||
$(DOC_DIR)/taikohook.md \
|
$(DOC_DIR)/taikohook.md \
|
||||||
|
$(DOC_DIR)/exvs2hook.md \
|
||||||
|
$(DOC_DIR)/saohook.md \
|
||||||
| $(zipdir)/
|
| $(zipdir)/
|
||||||
$(V)echo ... $@
|
$(V)echo ... $@
|
||||||
$(V)zip -r $@ $^
|
$(V)zip -r $@ $^
|
||||||
@ -30,6 +65,9 @@ $(BUILD_DIR_ZIP)/doc.zip: \
|
|||||||
$(BUILD_DIR_ZIP)/bananatools.zip: \
|
$(BUILD_DIR_ZIP)/bananatools.zip: \
|
||||||
$(BUILD_DIR_ZIP)/ferrum.zip \
|
$(BUILD_DIR_ZIP)/ferrum.zip \
|
||||||
$(BUILD_DIR_ZIP)/taiko.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 \
|
$(BUILD_DIR_ZIP)/doc.zip \
|
||||||
README.md \
|
README.md \
|
||||||
|
|
||||||
|
77
dist/exvs2/bananatools.ini
vendored
Normal file
77
dist/exvs2/bananatools.ini
vendored
Normal 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
11
dist/exvs2/start.bat
vendored
Normal 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
59
dist/mkac/bananatools.ini
vendored
Normal 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
10
dist/mkac/start.bat
vendored
Normal 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
62
dist/sao/bananatools.ini
vendored
Normal 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
12
dist/sao/start.bat
vendored
Normal 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
8
doc/exvs2hook.md
Normal 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
8
doc/saohook.md
Normal 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
0
exvs2hook/bngrw.c
Normal file
0
exvs2hook/bngrw.h
Normal file
0
exvs2hook/bngrw.h
Normal file
36
exvs2hook/config.c
Normal file
36
exvs2hook/config.c
Normal 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
28
exvs2hook/config.h
Normal 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
93
exvs2hook/dllmain.c
Normal 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
100
exvs2hook/exvs2-dll.c
Normal 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
20
exvs2hook/exvs2-dll.h
Normal 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
7
exvs2hook/exvs2hook.def
Normal 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
125
exvs2hook/jvs.c
Normal 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
8
exvs2hook/jvs.h
Normal 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
33
exvs2hook/meson.build
Normal 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
23
exvs2io/config.c
Normal 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
21
exvs2io/config.h
Normal 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
93
exvs2io/exvs2io.c
Normal 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
7
exvs2io/exvs2io.def
Normal 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
51
exvs2io/exvs2io.h
Normal 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
16
exvs2io/meson.build
Normal 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',
|
||||||
|
],
|
||||||
|
)
|
@ -56,6 +56,12 @@ subdir('amcus')
|
|||||||
|
|
||||||
subdir('ferrumio')
|
subdir('ferrumio')
|
||||||
subdir('taikoio')
|
subdir('taikoio')
|
||||||
|
subdir('exvs2io')
|
||||||
|
subdir('saoio')
|
||||||
|
subdir('mkacio')
|
||||||
|
|
||||||
subdir('taikohook')
|
subdir('taikohook')
|
||||||
subdir('ferrumhook')
|
subdir('ferrumhook')
|
||||||
|
subdir('exvs2hook')
|
||||||
|
subdir('saohook')
|
||||||
|
subdir('mkachook')
|
47
mkachook/config.c
Normal file
47
mkachook/config.c
Normal 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
34
mkachook/config.h
Normal 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
103
mkachook/dllmain.c
Normal 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
100
mkachook/jvs.c
Normal 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
8
mkachook/jvs.h
Normal 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
34
mkachook/meson.build
Normal 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
106
mkachook/mkac-dll.c
Normal 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
22
mkachook/mkac-dll.h
Normal 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
9
mkachook/mkachook.def
Normal 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
154
mkachook/xinput.c
Normal 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
12
mkachook/xinput.h
Normal 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
32
mkacio/config.c
Normal 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
30
mkacio/config.h
Normal 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
16
mkacio/meson.build
Normal 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
128
mkacio/mkacio.c
Normal 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
9
mkacio/mkacio.def
Normal 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
68
mkacio/mkacio.h
Normal 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
38
saohook/config.c
Normal 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
34
saohook/config.h
Normal 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
107
saohook/dllmain.c
Normal 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
33
saohook/meson.build
Normal 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
103
saohook/sao-dll.c
Normal 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
22
saohook/sao-dll.h
Normal 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
8
saohook/saohook.def
Normal 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
105
saohook/unity.c
Normal 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
3
saohook/unity.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void unity_hook_init(void);
|
82
saohook/usio.c
Normal file
82
saohook/usio.c
Normal 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
7
saohook/usio.h
Normal 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
27
saoio/config.c
Normal 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
26
saoio/config.h
Normal 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
16
saoio/meson.build
Normal 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
123
saoio/saoio.c
Normal 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
8
saoio/saoio.def
Normal 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
67
saoio/saoio.h
Normal 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);
|
Loading…
Reference in New Issue
Block a user