forked from Dniel97/segatools
added support for tokyo
This commit is contained in:
parent
c91c7db3c7
commit
c535f18e40
17
Package.mk
17
Package.mk
@ -203,6 +203,22 @@ $(BUILD_DIR_ZIP)/cm.zip:
|
||||
$(V)strip $(BUILD_DIR_ZIP)/cm/*.{exe,dll}
|
||||
$(V)cd $(BUILD_DIR_ZIP)/cm ; zip -r ../cm.zip *
|
||||
|
||||
$(BUILD_DIR_ZIP)/tokyo.zip:
|
||||
$(V)echo ... $@
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/tokyo
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/tokyo/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/tokyohook/tokyohook.dll \
|
||||
$(DIST_DIR)/tokyo/config_hook.json \
|
||||
$(DIST_DIR)/tokyo/segatools.ini \
|
||||
$(DIST_DIR)/tokyo/start.bat \
|
||||
$(BUILD_DIR_ZIP)/tokyo
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
$(BUILD_DIR_ZIP)/tokyo/DEVICE
|
||||
$(V)strip $(BUILD_DIR_ZIP)/tokyo/*.{exe,dll}
|
||||
$(V)cd $(BUILD_DIR_ZIP)/tokyo ; zip -r ../tokyo.zip *
|
||||
|
||||
$(BUILD_DIR_ZIP)/doc.zip: \
|
||||
$(DOC_DIR)/config \
|
||||
$(DOC_DIR)/chunihook.md \
|
||||
@ -225,6 +241,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
|
||||
$(BUILD_DIR_ZIP)/mu3.zip \
|
||||
$(BUILD_DIR_ZIP)/mai2.zip \
|
||||
$(BUILD_DIR_ZIP)/cm.zip \
|
||||
$(BUILD_DIR_ZIP)/tokyo.zip \
|
||||
$(BUILD_DIR_ZIP)/fgo.zip \
|
||||
CHANGELOG.md \
|
||||
README.md \
|
||||
|
24
README.md
24
README.md
@ -1,31 +1,33 @@
|
||||
# Segatools
|
||||
|
||||
Version: `2024-03-13`
|
||||
Version: `2024-08-20`
|
||||
|
||||
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
|
||||
|
||||
## List of supported games
|
||||
|
||||
* Card Maker
|
||||
* starting from Card Maker
|
||||
* CHUNITHM
|
||||
* up to [CHUNITHM PARADISE LOST](doc/chunihook.md)
|
||||
* starting from CHUNITHM NEW!!
|
||||
* crossbeats REV.
|
||||
* up to crossbeats REV. SUNRISE
|
||||
* Fate/Grand Order
|
||||
* Fate/Grand Order Arcade
|
||||
* Hatsune Miku: Project DIVA Arcade
|
||||
* up to Future Tone
|
||||
* Initial D
|
||||
* [Initial D Arcade Stage Zero](doc/idzhook.md)
|
||||
* Initial D THE ARCADE
|
||||
* Hatsune Miku: Project DIVA Arcade
|
||||
* up to Future Tone
|
||||
* SEGA World Drivers Championship
|
||||
* SEGA World Drivers Championship 2019
|
||||
* Fate/Grand Order
|
||||
* Fate/Grand Order Arcade
|
||||
* O.N.G.E.K.I.
|
||||
* starting from O.N.G.E.K.I.
|
||||
* maimai DX
|
||||
* starting from maimai DX
|
||||
* Card Maker
|
||||
* starting from Card Maker
|
||||
* Mario & Sonic
|
||||
* Mario & Sonic at the Tokyo 2020 Olympics Arcade
|
||||
* O.N.G.E.K.I.
|
||||
* starting from O.N.G.E.K.I.
|
||||
* SEGA World Drivers Championship
|
||||
* SEGA World Drivers Championship 2019
|
||||
* WACCA
|
||||
* starting from WACCA
|
||||
|
||||
|
9
dist/tokyo/config_hook.json
vendored
Normal file
9
dist/tokyo/config_hook.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"network" :
|
||||
{
|
||||
"property" :
|
||||
{
|
||||
"dhcp" : true
|
||||
}
|
||||
}
|
||||
}
|
199
dist/tokyo/segatools.ini
vendored
Normal file
199
dist/tokyo/segatools.ini
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
; -----------------------------------------------------------------------------
|
||||
; Path settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[vfs]
|
||||
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
|
||||
amfs=
|
||||
; Insert the path to the game Option directory here (contains OPxx directories)
|
||||
option=
|
||||
; Create an empty directory somewhere and insert the path here.
|
||||
; This directory may be shared between multiple SEGA games.
|
||||
; NOTE: This has nothing to do with Windows %APPDATA%.
|
||||
appdata=
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Network settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[dns]
|
||||
; Insert the hostname or IP address of the server you wish to use here.
|
||||
; Note that 127.0.0.1, localhost etc are specifically rejected.
|
||||
default=127.0.0.1
|
||||
|
||||
[netenv]
|
||||
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
|
||||
; SEGA games are somewhat picky about their LAN environment, so leaving this
|
||||
; setting enabled is recommended.
|
||||
enable=1
|
||||
|
||||
; The final octet of the local host's IP address on the virtualized subnet (so,
|
||||
; if the keychip subnet is `192.168.149.0` and this value is set to `205`, then the
|
||||
; local host's virtualized LAN IP is `192.168.149.205`).
|
||||
addrSuffix=205
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Board settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[keychip]
|
||||
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
|
||||
; If you disable netenv then you must set this to your LAN's IP subnet, and
|
||||
; that subnet must start with 192.168.
|
||||
subnet=192.168.149.0
|
||||
|
||||
; Override the keychip's region code.
|
||||
; 1: JAPAN (ALL.Net, Japanese language, Option support enabled)
|
||||
; 4: EXPORT (Local networking only, English language, No option support)
|
||||
; 8: CHINA
|
||||
;
|
||||
; NOTE: Changing this setting causes a factory reset. The language can be
|
||||
; changed in the game settings, so it's possible to run the JAPAN region
|
||||
; with English language.
|
||||
region=1
|
||||
|
||||
[system]
|
||||
; Enable ALLS system settings.
|
||||
enable=1
|
||||
|
||||
; Enable freeplay mode. This will disable the coin slot and set the game to
|
||||
; freeplay. Keep in mind that some game modes (e.g. Freedom/Time Modes) will not
|
||||
; allow you to start a game in freeplay mode.
|
||||
freeplay=0
|
||||
|
||||
; For Mario & Sonic at the Tokyo 2020 Olympics Arcade, DipSw 1/2/3 must be set
|
||||
; as the following:
|
||||
; Cabinet ID 1 (Server): 1 0 0
|
||||
; Cabinet ID 2 (Client): 0 1 0
|
||||
; Cabinet ID 3 (Client): 0 0 1
|
||||
; Cabinet ID 4 (Client): 0 1 1
|
||||
dipsw1=1
|
||||
dipsw2=0
|
||||
dipsw3=0
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; LED settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[led15093]
|
||||
; Enable emulation of the 15093-04 controlled lights, which handle the cabinet
|
||||
; LEDs.
|
||||
enable=1
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Misc. hook settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[zinput]
|
||||
; Disables the built-in DirectInput support, which is used to support a
|
||||
; controller out of the box.
|
||||
enable=1
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Custom IO settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[tokyoio]
|
||||
; To use a custom Mario & Sonic at the Tokyo 2020 Olympics Arcade IO DLL enter
|
||||
; its path here. Leave empty if you want to use Segatools built-in keyboard/
|
||||
; gamepad input.
|
||||
path=
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Input settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal
|
||||
; (not prefixed with 0x) virtual-key codes, a list of which can be found here:
|
||||
;
|
||||
; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||
;
|
||||
; This is, admittedly, not the most user-friendly configuration method in the
|
||||
; world. An improved solution will be provided later.
|
||||
|
||||
[io4]
|
||||
; Test button virtual-key code. Default is the F1 key.
|
||||
test=0x70
|
||||
; Service button virtual-key code. Default is the F2 key.
|
||||
service=0x71
|
||||
; Keyboard button to increment coin counter. Default is the F3 key.
|
||||
coin=0x72
|
||||
|
||||
; Input API selection for IO4 input emulator.
|
||||
; Set "xinput" to use a gamepad and "keyboard" to use a keyboard.
|
||||
mode=xinput
|
||||
|
||||
; Mario & Sonic at the Tokyo 2020 Olympics Arcade Control Panel
|
||||
;
|
||||
; |--|------------------ Main-Assy ------------------|--|
|
||||
; | | YELLOW | |
|
||||
; | | --- | |
|
||||
; | | ( O ) | |
|
||||
; |--| BLUE --- RED |--|
|
||||
; | | --- PUSH CENTER --- | |
|
||||
; | | ( O ) /---------------\ ( O ) | |
|
||||
; | | --- / \ --- | |
|
||||
; | | PUSH LEFT / \ PUSH RIGHT| |
|
||||
; |--|---------/ Floor Assy \---------|--|
|
||||
; | | |JUMP SENSE JUMP SENSE| | |
|
||||
; | | |1|---------------|-|-------------->|1| | |
|
||||
; | | | | Foot Panel | | Foot Panel | | | |
|
||||
; | | |2|<- - - - - - - |-| - - - - - - - |2| | |
|
||||
; | | | | | | | | | |
|
||||
; | | |3| -FOOT SENSE - |-| - FOOT SENSE->|3| | |
|
||||
; | | | | L | | R | | | |
|
||||
; | | |4|<- - - - - - - |-| - - - - - - - |4| | |
|
||||
; | | | | | | | | | |
|
||||
; | | |5| - - - - - - - |-| - - - - - - ->|5| | |
|
||||
; | | | | | | | | | |
|
||||
; | | |6|<--------------|-|---------------|6| | |
|
||||
; | | | | | |
|
||||
; | | | | | |
|
||||
; |--|----|-------------------------------------|----|--|
|
||||
;
|
||||
|
||||
; XInput bindings
|
||||
;
|
||||
; X Push Left Blue
|
||||
; Y Push Center Yellow
|
||||
; B Push Right Red
|
||||
; D-Pad Left Push Left Blue
|
||||
; D-Pad Right Push Right Red
|
||||
; Left Trigger Foot Sense L/Jump Sense
|
||||
; Right Trigger Foot Sense R/Jump Sense
|
||||
|
||||
[keyboard]
|
||||
; Keyboard bindings
|
||||
|
||||
; Keyoard: Push button settings
|
||||
|
||||
; PUSH LEFT (BLUE) button virtual-key code. Default is the A key.
|
||||
leftBlue=0x41
|
||||
; PUSH CENTER (YELLOW) button virtual-key code. Default is the S key.
|
||||
centerYellow=0x53
|
||||
; PUSH RIGHT (RED) button virtual-key code. Default is the D key.
|
||||
rightRed=0x44
|
||||
|
||||
; Keyboard: Sensor settings
|
||||
; FOOT SENSE L (LEFT) button virtual-key code. Default is the Left Arrow key.
|
||||
footLeft=0x25
|
||||
; FOOT SENSE R (RIGHT) button virtual-key code. Default is the Right Arrow key.
|
||||
footRight=0x27
|
||||
|
||||
; Keyboard: Jump sensor settings
|
||||
; All jump sensors will also trigger the FOOT SENSE L and FOOT SENSE R buttons.
|
||||
; JUMP SENSOR 1 button virtual-key code. Default is the Z key.
|
||||
jump1=0x5A
|
||||
; JUMP SENSOR 2 button virtual-key code. Default is the X key.
|
||||
jump2=0x58
|
||||
; JUMP SENSOR 3 button virtual-key code. Default is the C key.
|
||||
jump3=0x43
|
||||
; JUMP SENSOR 4 button virtual-key code. Default is the B key.
|
||||
jump4=0x42
|
||||
; JUMP SENSOR 5 button virtual-key code. Default is the N key.
|
||||
jump5=0x4E
|
||||
; JUMP SENSOR 6 button virtual-key code. Default is the M key.
|
||||
jump6=0x4D
|
||||
|
||||
; Virtual-key code for all jump sensors. Default is the Space key.
|
||||
jumpAll=0x20
|
57
dist/tokyo/start.bat
vendored
Normal file
57
dist/tokyo/start.bat
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
@echo off
|
||||
pushd %~dp0
|
||||
|
||||
set DAEMON_WAIT_SECONDS=5
|
||||
|
||||
set AMDAEMON_CFG=config_common.json ^
|
||||
config_ch.json ^
|
||||
config_ex.json ^
|
||||
config_jp.json ^
|
||||
config_st1_ch.json ^
|
||||
config_st1_ex.json ^
|
||||
config_st1_jp.json ^
|
||||
config_st2_ch.json ^
|
||||
config_st2_ex.json ^
|
||||
config_st2_jp.json ^
|
||||
config_st3_ch.json ^
|
||||
config_st3_ex.json ^
|
||||
config_st3_jp.json ^
|
||||
config_st4_ch.json ^
|
||||
config_st4_ex.json ^
|
||||
config_st4_jp.json ^
|
||||
config_laninstall_server_ch.json ^
|
||||
config_laninstall_client1_ch.json ^
|
||||
config_laninstall_client2_ch.json ^
|
||||
config_laninstall_client3_ch.json ^
|
||||
config_laninstall_server_ex.json ^
|
||||
config_laninstall_client1_ex.json ^
|
||||
config_laninstall_client2_ex.json ^
|
||||
config_laninstall_client3_ex.json ^
|
||||
config_laninstall_server_jp.json ^
|
||||
config_laninstall_client1_jp.json ^
|
||||
config_laninstall_client2_jp.json ^
|
||||
config_laninstall_client3_jp.json ^
|
||||
config_hook.json
|
||||
|
||||
start /min "AM Daemon" inject -d -k tokyohook.dll amdaemon.exe -c %AMDAEMON_CFG%
|
||||
timeout %DAEMON_WAIT_SECONDS% > nul 2>&1
|
||||
|
||||
REM ---------------------------------------------------------------------------
|
||||
REM Set configuration
|
||||
REM ---------------------------------------------------------------------------
|
||||
|
||||
REM Configuration values to be passed to the game executable.
|
||||
REM All known values:
|
||||
REM -forceapi:11
|
||||
REM -forcehal
|
||||
REM -forcevsync:0/1
|
||||
REM -fullscreen
|
||||
REM -windowed
|
||||
REM Note: -windowed is recommended as the game looks sharper in windowed mode.
|
||||
inject -d -k tokyohook.dll app.exe -windowed
|
||||
|
||||
taskkill /f /im amdaemon.exe > nul 2>&1
|
||||
|
||||
echo.
|
||||
echo Game processes have terminated
|
||||
pause
|
@ -108,6 +108,7 @@ subdir('mai2io')
|
||||
subdir('cmio')
|
||||
subdir('mercuryio')
|
||||
subdir('cxbio')
|
||||
subdir('tokyoio')
|
||||
subdir('fgoio')
|
||||
|
||||
subdir('chunihook')
|
||||
@ -123,4 +124,5 @@ subdir('mai2hook')
|
||||
subdir('cmhook')
|
||||
subdir('mercuryhook')
|
||||
subdir('cxbhook')
|
||||
subdir('tokyohook')
|
||||
subdir('fgohook')
|
||||
|
110
tokyohook/config.c
Normal file
110
tokyohook/config.c
Normal file
@ -0,0 +1,110 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "board/config.h"
|
||||
|
||||
#include "gfxhook/config.h"
|
||||
|
||||
#include "hooklib/config.h"
|
||||
#include "hooklib/dvd.h"
|
||||
|
||||
#include "platform/config.h"
|
||||
|
||||
#include "tokyohook/config.h"
|
||||
|
||||
void tokyo_dll_config_load(
|
||||
struct tokyo_dll_config *cfg,
|
||||
const wchar_t *filename) {
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"tokyoio",
|
||||
L"path",
|
||||
L"",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
filename);
|
||||
}
|
||||
|
||||
void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
wchar_t tmpstr[16];
|
||||
|
||||
memset(cfg->board_number, ' ', sizeof(cfg->board_number));
|
||||
memset(cfg->chip_number, ' ', sizeof(cfg->chip_number));
|
||||
memset(cfg->boot_chip_number, ' ', sizeof(cfg->boot_chip_number));
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"led15093", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"led15093", L"portNo", 0, filename);
|
||||
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
|
||||
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0x90, filename);
|
||||
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xAED9, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"led15093",
|
||||
L"boardNumber",
|
||||
L"15093-04",
|
||||
tmpstr,
|
||||
_countof(tmpstr),
|
||||
filename);
|
||||
|
||||
size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number));
|
||||
for (int i = n; i < sizeof(cfg->board_number); i++)
|
||||
{
|
||||
cfg->board_number[i] = ' ';
|
||||
}
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"led15093",
|
||||
L"chipNumber",
|
||||
L"6704 ",
|
||||
tmpstr,
|
||||
_countof(tmpstr),
|
||||
filename);
|
||||
|
||||
n = wcstombs(cfg->chip_number, tmpstr, sizeof(cfg->chip_number));
|
||||
for (int i = n; i < sizeof(cfg->chip_number); i++)
|
||||
{
|
||||
cfg->chip_number[i] = ' ';
|
||||
}
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"led15093",
|
||||
L"bootChipNumber",
|
||||
L"6709 ",
|
||||
tmpstr,
|
||||
_countof(tmpstr),
|
||||
filename);
|
||||
|
||||
n = wcstombs(cfg->boot_chip_number, tmpstr, sizeof(cfg->boot_chip_number));
|
||||
for (int i = n; i < sizeof(cfg->boot_chip_number); i++)
|
||||
{
|
||||
cfg->boot_chip_number[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
void zinput_config_load(struct zinput_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"zinput", L"enable", 1, filename);
|
||||
}
|
||||
|
||||
void tokyo_hook_config_load(
|
||||
struct tokyo_hook_config *cfg,
|
||||
const wchar_t *filename) {
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
platform_config_load(&cfg->platform, filename);
|
||||
dvd_config_load(&cfg->dvd, filename);
|
||||
io4_config_load(&cfg->io4, filename);
|
||||
zinput_config_load(&cfg->zinput, filename);
|
||||
led15093_config_load(&cfg->led15093, filename);
|
||||
tokyo_dll_config_load(&cfg->dll, filename);
|
||||
}
|
30
tokyohook/config.h
Normal file
30
tokyohook/config.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/led15093.h"
|
||||
|
||||
#include "hooklib/dvd.h"
|
||||
|
||||
#include "tokyohook/tokyo-dll.h"
|
||||
#include "tokyohook/zinput.h"
|
||||
|
||||
#include "platform/config.h"
|
||||
|
||||
struct tokyo_hook_config {
|
||||
struct platform_config platform;
|
||||
struct dvd_config dvd;
|
||||
struct io4_config io4;
|
||||
struct led15093_config led15093;
|
||||
struct zinput_config zinput;
|
||||
struct tokyo_dll_config dll;
|
||||
};
|
||||
|
||||
void tokyo_dll_config_load(
|
||||
struct tokyo_dll_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
||||
void tokyo_hook_config_load(
|
||||
struct tokyo_hook_config *cfg,
|
||||
const wchar_t *filename);
|
112
tokyohook/dllmain.c
Normal file
112
tokyohook/dllmain.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
"Mario & Sonic at the Tokyo 2020 Olympics Arcade" (tokyo) hook
|
||||
|
||||
Devices
|
||||
|
||||
USB: 837-15257 "Type 4" I/O Board
|
||||
COM1: 837-15093-04 LED Controller Board
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
|
||||
#include "hook/process.h"
|
||||
|
||||
#include "hooklib/dvd.h"
|
||||
#include "hooklib/serial.h"
|
||||
#include "hooklib/spike.h"
|
||||
|
||||
#include "tokyohook/config.h"
|
||||
#include "tokyohook/io4.h"
|
||||
#include "tokyohook/tokyo-dll.h"
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HMODULE tokyo_hook_mod;
|
||||
static process_entry_t tokyo_startup;
|
||||
static struct tokyo_hook_config tokyo_hook_cfg;
|
||||
|
||||
static DWORD CALLBACK tokyo_pre_startup(void)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("--- Begin tokyo_pre_startup ---\n");
|
||||
|
||||
/* Load config */
|
||||
|
||||
tokyo_hook_config_load(&tokyo_hook_cfg, L".\\segatools.ini");
|
||||
|
||||
/* Hook Win32 APIs */
|
||||
|
||||
dvd_hook_init(&tokyo_hook_cfg.dvd, tokyo_hook_mod);
|
||||
zinput_hook_init(&tokyo_hook_cfg.zinput);
|
||||
serial_hook_init();
|
||||
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
hr = platform_hook_init(
|
||||
&tokyo_hook_cfg.platform,
|
||||
"SDFV",
|
||||
"ACA1",
|
||||
tokyo_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = tokyo_dll_init(&tokyo_hook_cfg.dll, tokyo_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = led15093_hook_init(&tokyo_hook_cfg.led15093,
|
||||
tokyo_dll.led_init, tokyo_dll.led_set_leds, 1, 1, 1, 2);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = tokyo_io4_hook_init(&tokyo_hook_cfg.io4);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Initialize debug helpers */
|
||||
|
||||
spike_hook_init(L".\\segatools.ini");
|
||||
|
||||
dprintf("--- End tokyo_pre_startup ---\n");
|
||||
|
||||
/* Jump to EXE start address */
|
||||
|
||||
return tokyo_startup();
|
||||
|
||||
fail:
|
||||
ExitProcess(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (cause != DLL_PROCESS_ATTACH) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
tokyo_hook_mod = mod;
|
||||
|
||||
hr = process_hijack_startup(tokyo_pre_startup, &tokyo_startup);
|
||||
|
||||
if (!SUCCEEDED(hr)) {
|
||||
dprintf("Failed to hijack process startup: %x\n", (int) hr);
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
163
tokyohook/io4.c
Normal file
163
tokyohook/io4.c
Normal file
@ -0,0 +1,163 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
|
||||
#include "tokyohook/tokyo-dll.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT tokyo_io4_poll(void *ctx, struct io4_state *state);
|
||||
static HRESULT tokyo_io4_write_gpio(uint8_t* payload, size_t len);
|
||||
|
||||
static uint16_t coins;
|
||||
|
||||
static const struct io4_ops tokyo_io4_ops = {
|
||||
.poll = tokyo_io4_poll,
|
||||
.write_gpio = tokyo_io4_write_gpio,
|
||||
};
|
||||
|
||||
HRESULT tokyo_io4_hook_init(const struct io4_config *cfg)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(tokyo_dll.init != NULL);
|
||||
|
||||
hr = io4_hook_init(cfg, &tokyo_io4_ops, NULL);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return tokyo_dll.init();
|
||||
}
|
||||
|
||||
static HRESULT tokyo_io4_poll(void *ctx, struct io4_state *state)
|
||||
{
|
||||
uint8_t opbtn;
|
||||
uint8_t gamebtn;
|
||||
uint8_t sense;
|
||||
HRESULT hr;
|
||||
|
||||
assert(tokyo_dll.get_opbtns != NULL);
|
||||
assert(tokyo_dll.get_gamebtns != NULL);
|
||||
assert(tokyo_dll.get_sensors != NULL);
|
||||
|
||||
memset(state, 0, sizeof(*state));
|
||||
|
||||
opbtn = 0;
|
||||
gamebtn = 0;
|
||||
sense = 0;
|
||||
|
||||
tokyo_dll.get_opbtns(&opbtn);
|
||||
tokyo_dll.get_gamebtns(&gamebtn);
|
||||
tokyo_dll.get_sensors(&sense);
|
||||
|
||||
if (opbtn & TOKYO_IO_OPBTN_TEST) {
|
||||
state->buttons[0] |= IO4_BUTTON_TEST;
|
||||
}
|
||||
|
||||
if (opbtn & TOKYO_IO_OPBTN_SERVICE) {
|
||||
state->buttons[0] |= IO4_BUTTON_SERVICE;
|
||||
}
|
||||
|
||||
if (opbtn & TOKYO_IO_OPBTN_COIN) {
|
||||
coins++;
|
||||
}
|
||||
state->chutes[0] = coins << 8;
|
||||
|
||||
/* Update gamebtns */
|
||||
|
||||
if (gamebtn & TOKYO_IO_GAMEBTN_BLUE) {
|
||||
state->buttons[0] |= 1 << 1;
|
||||
}
|
||||
|
||||
if (gamebtn & TOKYO_IO_GAMEBTN_YELLOW) {
|
||||
state->buttons[0] |= 1 << 0;
|
||||
}
|
||||
|
||||
if (gamebtn & TOKYO_IO_GAMEBTN_RED) {
|
||||
state->buttons[0] |= 1 << 15;
|
||||
}
|
||||
|
||||
/* Update sensors */
|
||||
|
||||
// Invert the logic so that it's active high
|
||||
if (!(sense & TOKYO_IO_SENSE_FOOT_LEFT)) {
|
||||
state->buttons[0] |= 1 << 13;
|
||||
}
|
||||
|
||||
if (!(sense & TOKYO_IO_SENSE_FOOT_RIGHT)) {
|
||||
state->buttons[1] |= 1 << 13;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_1) {
|
||||
state->buttons[0] |= 1 << 12;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_2) {
|
||||
state->buttons[1] |= 1 << 12;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_3) {
|
||||
state->buttons[0] |= 1 << 11;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_4) {
|
||||
state->buttons[1] |= 1 << 11;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_5) {
|
||||
state->buttons[0] |= 1 << 10;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_6) {
|
||||
state->buttons[1] |= 1 << 10;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT tokyo_io4_write_gpio(uint8_t* payload, size_t len)
|
||||
{
|
||||
// Just fast fail if there aren't enough bytes in the payload
|
||||
if (len < 3)
|
||||
return S_OK;
|
||||
|
||||
// This command is used for lights in Mario & Sonic at the Tokyo 2020 Olympics
|
||||
// Arcade, but it only contains button lights, and only in the first 3 bytes of
|
||||
// the payload; everything else is padding to make the payload 62 bytes. The
|
||||
// rest of the cabinet lights and the side button lights are handled separately,
|
||||
// by the 15093 lights controller.
|
||||
uint32_t lights_data = (uint32_t) ((uint8_t)(payload[0]) << 24 |
|
||||
(uint8_t)(payload[1]) << 16 |
|
||||
(uint8_t)(payload[2]) << 8);
|
||||
|
||||
// Since Sega uses an odd ordering for the first part of the bitfield,
|
||||
// let's normalize the data and just send over bytes for the receiver
|
||||
// to interpret as RGB values.
|
||||
uint8_t rgb_out[5 * 3] = {
|
||||
lights_data & TOKYO_IO_LED_LEFT_BLUE ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CENTER_YELLOW ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_RIGHT_RED ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_LEFT_R ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_LEFT_G ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_LEFT_B ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_RIGHT_R ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_RIGHT_G ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_RIGHT_B ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_LEFT_R ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_LEFT_G ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_LEFT_B ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_RIGHT_R ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_RIGHT_G ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_RIGHT_B ? 0xFF : 0x00,
|
||||
};
|
||||
|
||||
tokyo_dll.led_set_leds(1, rgb_out);
|
||||
|
||||
return S_OK;
|
||||
}
|
7
tokyohook/io4.h
Normal file
7
tokyohook/io4.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
|
||||
HRESULT tokyo_io4_hook_init(const struct io4_config *cfg);
|
32
tokyohook/meson.build
Normal file
32
tokyohook/meson.build
Normal file
@ -0,0 +1,32 @@
|
||||
shared_library(
|
||||
'tokyohook',
|
||||
name_prefix : '',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
vs_module_defs : 'tokyohook.def',
|
||||
c_pch : '../precompiled.h',
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
capnhook.get_variable('hooklib_dep'),
|
||||
xinput_lib,
|
||||
],
|
||||
link_with : [
|
||||
aimeio_lib,
|
||||
board_lib,
|
||||
hooklib_lib,
|
||||
tokyoio_lib,
|
||||
platform_lib,
|
||||
util_lib,
|
||||
],
|
||||
sources : [
|
||||
'config.c',
|
||||
'config.h',
|
||||
'dllmain.c',
|
||||
'io4.c',
|
||||
'io4.h',
|
||||
'zinput.c',
|
||||
'zinput.h',
|
||||
'tokyo-dll.c',
|
||||
'tokyo-dll.h',
|
||||
],
|
||||
)
|
115
tokyohook/tokyo-dll.c
Normal file
115
tokyohook/tokyo-dll.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tokyohook/tokyo-dll.h"
|
||||
|
||||
#include "util/dll-bind.h"
|
||||
#include "util/dprintf.h"
|
||||
|
||||
const struct dll_bind_sym tokyo_dll_syms[] = {
|
||||
{
|
||||
.sym = "tokyo_io_init",
|
||||
.off = offsetof(struct tokyo_dll, init),
|
||||
}, {
|
||||
.sym = "tokyo_io_get_opbtns",
|
||||
.off = offsetof(struct tokyo_dll, get_opbtns),
|
||||
}, {
|
||||
.sym = "tokyo_io_get_gamebtns",
|
||||
.off = offsetof(struct tokyo_dll, get_gamebtns),
|
||||
}, {
|
||||
.sym = "tokyo_io_get_sensors",
|
||||
.off = offsetof(struct tokyo_dll, get_sensors),
|
||||
}, {
|
||||
.sym = "tokyo_io_led_init",
|
||||
.off = offsetof(struct tokyo_dll, led_init),
|
||||
}, {
|
||||
.sym = "tokyo_io_led_set_colors",
|
||||
.off = offsetof(struct tokyo_dll, led_set_leds),
|
||||
}
|
||||
};
|
||||
|
||||
struct tokyo_dll tokyo_dll;
|
||||
|
||||
// Copypasta DLL binding and diagnostic message boilerplate.
|
||||
// Not much of this lends itself to being easily factored out. Also there
|
||||
// will be a lot of API-specific branching code here eventually as new API
|
||||
// versions get defined, so even though these functions all look the same
|
||||
// now this won't remain the case forever.
|
||||
|
||||
HRESULT tokyo_dll_init(const struct tokyo_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("Tokyo IO: Failed to load IO DLL: %lx: %S\n",
|
||||
hr,
|
||||
cfg->path);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
dprintf("Tokyo IO: Using custom IO DLL: %S\n", cfg->path);
|
||||
src = owned;
|
||||
} else {
|
||||
owned = NULL;
|
||||
src = self;
|
||||
}
|
||||
|
||||
get_api_version = (void *) GetProcAddress(src, "tokyo_io_get_api_version");
|
||||
|
||||
if (get_api_version != NULL) {
|
||||
tokyo_dll.api_version = get_api_version();
|
||||
} else {
|
||||
tokyo_dll.api_version = 0x0100;
|
||||
dprintf("Custom IO DLL does not expose tokyo_io_get_api_version, "
|
||||
"assuming API version 1.0.\n"
|
||||
"Please ask the developer to update their DLL.\n");
|
||||
}
|
||||
|
||||
if (tokyo_dll.api_version >= 0x0200) {
|
||||
hr = E_NOTIMPL;
|
||||
dprintf("Tokyo IO: Custom IO DLL implements an unsupported "
|
||||
"API version (%#04x). Please update Segatools.\n",
|
||||
tokyo_dll.api_version);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
sym = tokyo_dll_syms;
|
||||
hr = dll_bind(&tokyo_dll, src, &sym, _countof(tokyo_dll_syms));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (src != self) {
|
||||
dprintf("Tokyo 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;
|
||||
}
|
24
tokyohook/tokyo-dll.h
Normal file
24
tokyohook/tokyo-dll.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "tokyoio/tokyoio.h"
|
||||
|
||||
struct tokyo_dll {
|
||||
uint16_t api_version;
|
||||
HRESULT (*init)(void);
|
||||
void (*get_opbtns)(uint8_t *opbtn);
|
||||
void (*get_gamebtns)(uint8_t *gamebtn);
|
||||
void (*get_sensors)(uint8_t *sense);
|
||||
HRESULT (*gpio_out)(uint32_t state);
|
||||
HRESULT (*led_init)(void);
|
||||
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
|
||||
};
|
||||
|
||||
struct tokyo_dll_config {
|
||||
wchar_t path[MAX_PATH];
|
||||
};
|
||||
|
||||
extern struct tokyo_dll tokyo_dll;
|
||||
|
||||
HRESULT tokyo_dll_init(const struct tokyo_dll_config *cfg, HINSTANCE self);
|
20
tokyohook/tokyohook.def
Normal file
20
tokyohook/tokyohook.def
Normal file
@ -0,0 +1,20 @@
|
||||
LIBRARY tokyohook
|
||||
|
||||
EXPORTS
|
||||
aime_io_get_api_version
|
||||
aime_io_init
|
||||
aime_io_led_set_color
|
||||
aime_io_nfc_get_aime_id
|
||||
aime_io_nfc_get_felica_id
|
||||
aime_io_nfc_poll
|
||||
amDllVideoClose @2
|
||||
amDllVideoGetVBiosVersion @4
|
||||
amDllVideoOpen @1
|
||||
amDllVideoSetResolution @3
|
||||
tokyo_io_get_api_version
|
||||
tokyo_io_init
|
||||
tokyo_io_get_opbtns
|
||||
tokyo_io_get_gamebtns
|
||||
tokyo_io_get_sensors
|
||||
tokyo_io_led_init
|
||||
tokyo_io_led_set_colors
|
118
tokyohook/zinput.c
Normal file
118
tokyohook/zinput.c
Normal file
@ -0,0 +1,118 @@
|
||||
#include <windows.h>
|
||||
#include <shlwapi.h>
|
||||
#include <dinput.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tokyohook/config.h"
|
||||
#include "tokyohook/zinput.h"
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/lib.h"
|
||||
#include "util/dprintf.h"
|
||||
|
||||
HRESULT WINAPI hook_DirectInput8Create(
|
||||
HINSTANCE hinst,
|
||||
DWORD dwVersion,
|
||||
REFIID riidltf,
|
||||
LPVOID *ppvOut,
|
||||
LPUNKNOWN punkOuter);
|
||||
|
||||
static HRESULT WINAPI hook_EnumDevices(
|
||||
IDirectInput8W *self,
|
||||
DWORD dwDevType,
|
||||
LPDIENUMDEVICESCALLBACKW lpCallback,
|
||||
LPVOID pvRef,
|
||||
DWORD dwFlags);
|
||||
|
||||
static unsigned long WINAPI hook_AddRef(IUnknown *self);
|
||||
static unsigned long WINAPI hook_Release(IUnknown *self);
|
||||
|
||||
static const IDirectInput8WVtbl api_vtbl = {
|
||||
.EnumDevices = hook_EnumDevices,
|
||||
.AddRef = (void *) hook_AddRef,
|
||||
.Release = (void *) hook_Release,
|
||||
};
|
||||
|
||||
static const IDirectInput8W api = { (void *) &api_vtbl };
|
||||
|
||||
static const struct hook_symbol zinput_hook_syms[] = {
|
||||
{
|
||||
.name = "DirectInput8Create",
|
||||
.patch = hook_DirectInput8Create,
|
||||
.link = NULL,
|
||||
}
|
||||
};
|
||||
|
||||
HRESULT zinput_hook_init(struct zinput_config *cfg)
|
||||
{
|
||||
wchar_t *module_path;
|
||||
wchar_t *file_name;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
module_path = module_file_name(NULL);
|
||||
|
||||
if (module_path != NULL) {
|
||||
file_name = PathFindFileNameW(module_path);
|
||||
|
||||
free(module_path);
|
||||
module_path = NULL;
|
||||
|
||||
_wcslwr(file_name);
|
||||
|
||||
if (wcsstr(file_name, L"amdaemon") != NULL) {
|
||||
// dprintf("Executable filename contains 'amdaemon', disabling zinput\n");
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"dinput8.dll",
|
||||
zinput_hook_syms,
|
||||
_countof(zinput_hook_syms));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WINAPI hook_DirectInput8Create(
|
||||
HINSTANCE hinst,
|
||||
DWORD dwVersion,
|
||||
REFIID riidltf,
|
||||
LPVOID *ppvOut,
|
||||
LPUNKNOWN punkOuter)
|
||||
{
|
||||
dprintf("ZInput: Blocking built-in DirectInput support\n");
|
||||
*ppvOut = (void *) &api;
|
||||
|
||||
return DI_OK;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI hook_EnumDevices(
|
||||
IDirectInput8W *self,
|
||||
DWORD dwDevType,
|
||||
LPDIENUMDEVICESCALLBACKW lpCallback,
|
||||
LPVOID pvRef,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
dprintf("ZInput: %s\n", __func__);
|
||||
|
||||
return DI_OK;
|
||||
}
|
||||
|
||||
static unsigned long WINAPI hook_AddRef(IUnknown *self)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned long WINAPI hook_Release(IUnknown *self)
|
||||
{
|
||||
return 1;
|
||||
}
|
11
tokyohook/zinput.h
Normal file
11
tokyohook/zinput.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct zinput_config {
|
||||
bool enable;
|
||||
};
|
||||
|
||||
HRESULT zinput_hook_init(struct zinput_config *cfg);
|
10
tokyoio/backend.h
Normal file
10
tokyoio/backend.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tokyoio/tokyoio.h"
|
||||
|
||||
struct tokyo_io_backend {
|
||||
void (*get_gamebtns)(uint8_t *gamebtn);
|
||||
void (*get_sensors)(uint8_t *sense);
|
||||
};
|
54
tokyoio/config.c
Normal file
54
tokyoio/config.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "tokyoio/config.h"
|
||||
|
||||
|
||||
void tokyo_kb_config_load(
|
||||
struct tokyo_kb_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
/* Load game button keyboard bindings */
|
||||
cfg->vk_push_left_b = GetPrivateProfileIntW(L"keyboard", L"leftBlue", 'A', filename);
|
||||
cfg->vk_push_center_y = GetPrivateProfileIntW(L"keyboard", L"centerYellow", 'S', filename);
|
||||
cfg->vk_push_right_r = GetPrivateProfileIntW(L"keyboard", L"rightRed", 'D', filename);
|
||||
|
||||
/* Load sensor keyboard bindings */
|
||||
cfg->vk_foot_l = GetPrivateProfileIntW(L"keyboard", L"footLeft", VK_LEFT, filename);
|
||||
cfg->vk_foot_r = GetPrivateProfileIntW(L"keyboard", L"footRight", VK_RIGHT, filename);
|
||||
cfg->vk_jump_1 = GetPrivateProfileIntW(L"keyboard", L"jump1", 'Z', filename);
|
||||
cfg->vk_jump_2 = GetPrivateProfileIntW(L"keyboard", L"jump2", 'X', filename);
|
||||
cfg->vk_jump_3 = GetPrivateProfileIntW(L"keyboard", L"jump3", 'C', filename);
|
||||
cfg->vk_jump_4 = GetPrivateProfileIntW(L"keyboard", L"jump4", 'B', filename);
|
||||
cfg->vk_jump_5 = GetPrivateProfileIntW(L"keyboard", L"jump5", 'N', filename);
|
||||
cfg->vk_jump_6 = GetPrivateProfileIntW(L"keyboard", L"jump6", 'M', filename);
|
||||
cfg->vk_jump_all = GetPrivateProfileIntW(L"keyboard", L"jumpAll", VK_SPACE, filename);
|
||||
}
|
||||
|
||||
void tokyo_io_config_load(
|
||||
struct tokyo_io_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", '1', filename);
|
||||
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename);
|
||||
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", '3', filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"io4",
|
||||
L"mode",
|
||||
L"xinput",
|
||||
cfg->mode,
|
||||
_countof(cfg->mode),
|
||||
filename);
|
||||
|
||||
tokyo_kb_config_load(&cfg->kb, filename);
|
||||
}
|
34
tokyoio/config.h
Normal file
34
tokyoio/config.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct tokyo_kb_config {
|
||||
uint8_t vk_push_left_b;
|
||||
uint8_t vk_push_center_y;
|
||||
uint8_t vk_push_right_r;
|
||||
uint8_t vk_foot_l;
|
||||
uint8_t vk_foot_r;
|
||||
uint8_t vk_jump_1;
|
||||
uint8_t vk_jump_2;
|
||||
uint8_t vk_jump_3;
|
||||
uint8_t vk_jump_4;
|
||||
uint8_t vk_jump_5;
|
||||
uint8_t vk_jump_6;
|
||||
uint8_t vk_jump_all;
|
||||
};
|
||||
|
||||
struct tokyo_io_config {
|
||||
uint8_t vk_test;
|
||||
uint8_t vk_service;
|
||||
uint8_t vk_coin;
|
||||
wchar_t mode[9];
|
||||
|
||||
struct tokyo_kb_config kb;
|
||||
};
|
||||
|
||||
void tokyo_io_config_load(
|
||||
struct tokyo_io_config *cfg,
|
||||
const wchar_t *filename);
|
135
tokyoio/dllmain.c
Normal file
135
tokyoio/dllmain.c
Normal file
@ -0,0 +1,135 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tokyoio/backend.h"
|
||||
#include "tokyoio/config.h"
|
||||
#include "tokyoio/kb.h"
|
||||
#include "tokyoio/tokyoio.h"
|
||||
#include "tokyoio/xi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static struct tokyo_io_config tokyo_io_cfg;
|
||||
static const struct tokyo_io_backend *tokyo_io_backend;
|
||||
static bool tokyo_io_coin;
|
||||
|
||||
uint16_t tokyo_io_get_api_version(void)
|
||||
{
|
||||
return 0x0100;
|
||||
}
|
||||
|
||||
HRESULT tokyo_io_init(void)
|
||||
{
|
||||
HINSTANCE inst;
|
||||
HRESULT hr;
|
||||
|
||||
assert(tokyo_io_backend == NULL);
|
||||
|
||||
inst = GetModuleHandleW(NULL);
|
||||
|
||||
if (inst == NULL) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("GetModuleHandleW failed: %lx\n", hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
tokyo_io_config_load(&tokyo_io_cfg, L".\\segatools.ini");
|
||||
|
||||
if (wstr_ieq(tokyo_io_cfg.mode, L"keyboard")) {
|
||||
hr = tokyo_kb_init(&tokyo_io_cfg.kb, &tokyo_io_backend);
|
||||
} else if (wstr_ieq(tokyo_io_cfg.mode, L"xinput")) {
|
||||
hr = tokyo_xi_init(&tokyo_io_backend);
|
||||
} else {
|
||||
hr = E_INVALIDARG;
|
||||
dprintf("IDAC IO: Invalid IO mode \"%S\", use keyboard or xinput\n",
|
||||
tokyo_io_cfg.mode);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
void tokyo_io_get_opbtns(uint8_t *opbtn_out)
|
||||
{
|
||||
uint8_t opbtn;
|
||||
|
||||
assert(tokyo_io_backend != NULL);
|
||||
assert(opbtn_out != NULL);
|
||||
|
||||
opbtn = 0;
|
||||
|
||||
/* Common operator buttons, not backend-specific */
|
||||
|
||||
if (GetAsyncKeyState(tokyo_io_cfg.vk_test) & 0x8000) {
|
||||
opbtn |= TOKYO_IO_OPBTN_TEST;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_io_cfg.vk_service) & 0x8000) {
|
||||
opbtn |= TOKYO_IO_OPBTN_SERVICE;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_io_cfg.vk_coin) & 0x8000) {
|
||||
if (!tokyo_io_coin) {
|
||||
tokyo_io_coin = true;
|
||||
opbtn |= TOKYO_IO_OPBTN_COIN;
|
||||
}
|
||||
} else {
|
||||
tokyo_io_coin = false;
|
||||
}
|
||||
|
||||
*opbtn_out = opbtn;
|
||||
}
|
||||
|
||||
|
||||
void tokyo_io_get_gamebtns(uint8_t *gamebtn_out)
|
||||
{
|
||||
assert(tokyo_io_backend != NULL);
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
tokyo_io_backend->get_gamebtns(gamebtn_out);
|
||||
}
|
||||
|
||||
void tokyo_io_get_sensors(uint8_t *sense_out)
|
||||
{
|
||||
assert(sense_out != NULL);
|
||||
assert(tokyo_io_backend != NULL);
|
||||
|
||||
tokyo_io_backend->get_sensors(sense_out);
|
||||
}
|
||||
|
||||
HRESULT tokyo_io_led_init(void)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void tokyo_io_led_set_colors(uint8_t board, uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
if (board == 0) {
|
||||
dprintf("Board 0:\n");
|
||||
// Change GRB order to RGB order
|
||||
for (int i = 0; i < 27; i++) {
|
||||
dprintf("Tokyo LED: MONITOR LEFT: %02X %02X %02X\n", rgb[i * 3 + 1], rgb[i * 3], rgb[i * 3 + 2]);
|
||||
}
|
||||
|
||||
for (int i = 27; i < 54; i++) {
|
||||
dprintf("Tokyo LED: MONITOR RIGHT: %d, %02X %02X %02X\n", i, rgb[i * 3 + 1], rgb[i * 3], rgb[i * 3 + 2]);
|
||||
}
|
||||
} else {
|
||||
dprintf("Board 1:\n");
|
||||
dprintf("Tokyo LED: LEFT BLUE: %02X\n", rgb[0]);
|
||||
dprintf("Tokyo LED: CENTER YELLOW: %02X\n", rgb[1]);
|
||||
dprintf("Tokyo LED: RIGHT RED: %02X\n", rgb[2]);
|
||||
dprintf("Tokyo LED: CONTROL LEFT: %02X %02X %02X\n", rgb[3], rgb[4], rgb[5]);
|
||||
dprintf("Tokyo LED: CONTROL RIGHT: %02X %02X %02X\n", rgb[6], rgb[7], rgb[8]);
|
||||
dprintf("Tokyo LED: FLOOR LEFT: %02X %02X %02X\n", rgb[9], rgb[10], rgb[11]);
|
||||
dprintf("Tokyo LED: FLOOR RIGHT: %02X %02X %02X\n", rgb[12], rgb[13], rgb[14]);
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
135
tokyoio/kb.c
Normal file
135
tokyoio/kb.c
Normal file
@ -0,0 +1,135 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tokyoio/backend.h"
|
||||
#include "tokyoio/config.h"
|
||||
#include "tokyoio/kb.h"
|
||||
#include "tokyoio/tokyoio.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static HRESULT tokyo_kb_config_apply(const struct tokyo_kb_config *cfg);
|
||||
|
||||
static void tokyo_kb_get_gamebtns(uint8_t *gamebtn_out);
|
||||
static void tokyo_kb_get_sensors(uint8_t *sense_out);
|
||||
|
||||
static const struct tokyo_io_backend tokyo_kb_backend = {
|
||||
.get_gamebtns = tokyo_kb_get_gamebtns,
|
||||
.get_sensors = tokyo_kb_get_sensors,
|
||||
};
|
||||
|
||||
static struct tokyo_kb_config tokyo_kb_cfg;
|
||||
|
||||
HRESULT tokyo_kb_init(const struct tokyo_kb_config *cfg, const struct tokyo_io_backend **backend)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(backend != NULL);
|
||||
|
||||
hr = tokyo_kb_config_apply(cfg);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("TokyoIO: Using keyboard input\n");
|
||||
*backend = &tokyo_kb_backend;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT tokyo_kb_config_apply(const struct tokyo_kb_config *cfg)
|
||||
{
|
||||
tokyo_kb_cfg = *cfg;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void tokyo_kb_get_gamebtns(uint8_t *gamebtn_out)
|
||||
{
|
||||
uint8_t gamebtn;
|
||||
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
gamebtn = 0;
|
||||
|
||||
/* PUSH BUTTON inputs */
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_push_left_b) & 0x8000) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_BLUE;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_push_center_y) & 0x8000) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_YELLOW;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_push_right_r) & 0x8000) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_RED;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
||||
|
||||
static void tokyo_kb_get_sensors(uint8_t *sense_out)
|
||||
{
|
||||
uint8_t sense;
|
||||
|
||||
assert(sense_out != NULL);
|
||||
|
||||
sense = 0;
|
||||
|
||||
/* FOOT SENSOR inputs */
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_foot_l) & 0x8000) {
|
||||
sense |= TOKYO_IO_SENSE_FOOT_LEFT;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_foot_r) & 0x8000) {
|
||||
sense |= TOKYO_IO_SENSE_FOOT_RIGHT;
|
||||
}
|
||||
|
||||
/* JUMP SENSOR inputs */
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_1) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_1);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_2) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_2);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_3) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_3);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_4) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_4);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_5) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_5);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_6) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_6);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_all) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT+ TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_1 + TOKYO_IO_SENSE_JUMP_2 +
|
||||
TOKYO_IO_SENSE_JUMP_3 + TOKYO_IO_SENSE_JUMP_4 +
|
||||
TOKYO_IO_SENSE_JUMP_5 + TOKYO_IO_SENSE_JUMP_6);
|
||||
}
|
||||
|
||||
*sense_out = sense;
|
||||
}
|
10
tokyoio/kb.h
Normal file
10
tokyoio/kb.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "tokyoio/backend.h"
|
||||
#include "tokyoio/config.h"
|
||||
|
||||
HRESULT tokyo_kb_init(
|
||||
const struct tokyo_kb_config *cfg,
|
||||
const struct tokyo_io_backend **backend);
|
21
tokyoio/meson.build
Normal file
21
tokyoio/meson.build
Normal file
@ -0,0 +1,21 @@
|
||||
tokyoio_lib = static_library(
|
||||
'tokyoio',
|
||||
name_prefix : '',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
c_pch : '../precompiled.h',
|
||||
dependencies : [
|
||||
xinput_lib,
|
||||
],
|
||||
sources : [
|
||||
'backend.h',
|
||||
'config.c',
|
||||
'config.h',
|
||||
'dllmain.c',
|
||||
'tokyoio.h',
|
||||
'kb.c',
|
||||
'kb.h',
|
||||
'xi.c',
|
||||
'xi.h',
|
||||
],
|
||||
)
|
139
tokyoio/tokyoio.h
Normal file
139
tokyoio/tokyoio.h
Normal file
@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
TOKYO_IO_OPBTN_TEST = 0x01,
|
||||
TOKYO_IO_OPBTN_SERVICE = 0x02,
|
||||
TOKYO_IO_OPBTN_COIN = 0x04,
|
||||
};
|
||||
|
||||
enum {
|
||||
TOKYO_IO_GAMEBTN_BLUE = 0x01,
|
||||
TOKYO_IO_GAMEBTN_YELLOW = 0x02,
|
||||
TOKYO_IO_GAMEBTN_RED = 0x04,
|
||||
};
|
||||
|
||||
enum {
|
||||
TOKYO_IO_SENSE_FOOT_LEFT = 0x01,
|
||||
TOKYO_IO_SENSE_FOOT_RIGHT = 0x02,
|
||||
TOKYO_IO_SENSE_JUMP_1 = 0x04,
|
||||
TOKYO_IO_SENSE_JUMP_2 = 0x08,
|
||||
TOKYO_IO_SENSE_JUMP_3 = 0x10,
|
||||
TOKYO_IO_SENSE_JUMP_4 = 0x20,
|
||||
TOKYO_IO_SENSE_JUMP_5 = 0x40,
|
||||
TOKYO_IO_SENSE_JUMP_6 = 0x80,
|
||||
};
|
||||
|
||||
enum {
|
||||
/* These are the bitmasks to use when checking which
|
||||
lights are triggered on incoming IO4 GPIO writes. */
|
||||
TOKYO_IO_LED_LEFT_BLUE = 1 << 31,
|
||||
TOKYO_IO_LED_CENTER_YELLOW = 1 << 30,
|
||||
TOKYO_IO_LED_RIGHT_RED = 1 << 29,
|
||||
TOKYO_IO_LED_CONTROL_LEFT_R = 1 << 25,
|
||||
TOKYO_IO_LED_CONTROL_LEFT_G = 1 << 24,
|
||||
TOKYO_IO_LED_CONTROL_LEFT_B = 1 << 23,
|
||||
TOKYO_IO_LED_CONTROL_RIGHT_R = 1 << 22,
|
||||
TOKYO_IO_LED_CONTROL_RIGHT_G = 1 << 21,
|
||||
TOKYO_IO_LED_CONTROL_RIGHT_B = 1 << 20,
|
||||
TOKYO_IO_LED_FLOOR_LEFT_R = 1 << 19,
|
||||
TOKYO_IO_LED_FLOOR_LEFT_G = 1 << 18,
|
||||
TOKYO_IO_LED_FLOOR_LEFT_B = 1 << 17,
|
||||
TOKYO_IO_LED_FLOOR_RIGHT_R = 1 << 16,
|
||||
TOKYO_IO_LED_FLOOR_RIGHT_G = 1 << 15,
|
||||
TOKYO_IO_LED_FLOOR_RIGHT_B = 1 << 14,
|
||||
};
|
||||
|
||||
/* Get the version of the Mario & Sonic at the Olympic Games Tokyo 2020 Arcade
|
||||
Edition 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 tokyo_io_get_api_version(void);
|
||||
|
||||
/* Initialize the IO DLL. This is the second function that will be called on
|
||||
your DLL, after tokyo_io_get_api_version.
|
||||
|
||||
All subsequent calls to this API may originate from arbitrary threads.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
HRESULT tokyo_io_init(void);
|
||||
|
||||
/* Send any queued outputs (of which there are currently none, though this may
|
||||
change in subsequent API versions) and retrieve any new inputs.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
HRESULT tokyo_io_poll(void);
|
||||
|
||||
/* Get the state of the cabinet's operator buttons as of the last poll. See
|
||||
TOKYO_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 tokyo_io_get_opbtns(uint8_t *opbtn);
|
||||
|
||||
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
|
||||
TOKYO_IO_GAMEBTN enum above: this contains bit mask definitions for button
|
||||
states returned in *gamebtn. All buttons are active-high.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void tokyo_io_get_gamebtns(uint8_t *gamebtn);
|
||||
|
||||
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
|
||||
TOKYO_IO_SENSE enum above: this contains bit mask definitions for button
|
||||
states returned in *sense. All buttons are active-high.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void tokyo_io_get_sensors(uint8_t *sense);
|
||||
|
||||
/* Initialize LED emulation. This function will be called before any
|
||||
other tokyo_io_led_*() function calls.
|
||||
|
||||
All subsequent calls may originate from arbitrary threads and some may
|
||||
overlap with each other. Ensuring synchronization inside your IO DLL is
|
||||
your responsibility.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
HRESULT tokyo_io_led_init(void);
|
||||
|
||||
/* Update the RGB LEDs. rgb is a pointer to an array of up to 54 * 3 = 162 bytes.
|
||||
|
||||
Mario & Sonic at the Tokyo 2020 Olympics Arcade uses one board with 15 LEDs for
|
||||
all buttons, control panel, and floor LEDs. Board 1 is just used for the
|
||||
left and right monitor LEDs.
|
||||
|
||||
Board 0 has 54 LEDs (GRB order):
|
||||
[0]-[26]: left monitor LEDs
|
||||
[27]-[53]: right monitor LEDs
|
||||
|
||||
Board 1 has 15 LEDs (RGB order):
|
||||
[0]: left blue LED
|
||||
[1]: center yellow LED
|
||||
[2]: right red LED
|
||||
[3]-[5]: left control panel LEDs
|
||||
[6]-[8]: right control panel LEDs
|
||||
[9]-[11]: left floor LEDs
|
||||
[12]-[14]: right floor LEDs
|
||||
|
||||
Each rgb value is comprised of 3 bytes in G,R,B order for board 0 and R,G,B
|
||||
order for board 1. The tricky part is that the board 0 is called from app and
|
||||
the board 1 is called from amdaemon. So the library must be able to handle both
|
||||
calls, using shared memory f.e. This is up to the developer to decide how to
|
||||
handle this, recommended way is to use the amdaemon process as the main one
|
||||
and the app process as a sub one.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void tokyo_io_led_set_colors(uint8_t board, uint8_t *rgb);
|
130
tokyoio/xi.c
Normal file
130
tokyoio/xi.c
Normal file
@ -0,0 +1,130 @@
|
||||
#include <windows.h>
|
||||
#include <xinput.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tokyoio/backend.h"
|
||||
#include "tokyoio/config.h"
|
||||
#include "tokyoio/tokyoio.h"
|
||||
#include "tokyoio/xi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static void tokyo_xi_get_gamebtns(uint8_t *gamebtn_out);
|
||||
static void tokyo_xi_get_sensors(uint8_t *sense_out);
|
||||
|
||||
static const struct tokyo_io_backend tokyo_xi_backend = {
|
||||
.get_gamebtns = tokyo_xi_get_gamebtns,
|
||||
.get_sensors = tokyo_xi_get_sensors,
|
||||
};
|
||||
|
||||
HRESULT tokyo_xi_init(const struct tokyo_io_backend **backend)
|
||||
{
|
||||
wchar_t dll_path[MAX_PATH];
|
||||
HMODULE xinput;
|
||||
HRESULT hr;
|
||||
UINT path_pos;
|
||||
|
||||
assert(backend != NULL);
|
||||
|
||||
dprintf("TokyoIO: IO4: Using XInput controller\n");
|
||||
*backend = &tokyo_xi_backend;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void tokyo_xi_get_gamebtns(uint8_t *gamebtn_out)
|
||||
{
|
||||
uint8_t gamebtn;
|
||||
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
gamebtn = 0;
|
||||
|
||||
XINPUT_STATE xi;
|
||||
WORD xb;
|
||||
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
XInputGetState(0, &xi);
|
||||
xb = xi.Gamepad.wButtons;
|
||||
|
||||
/* PUSH BUTTON inputs */
|
||||
|
||||
if ((xb & XINPUT_GAMEPAD_X) || (xb & XINPUT_GAMEPAD_DPAD_LEFT)) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_BLUE;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_Y || (xb & XINPUT_GAMEPAD_A)) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_YELLOW;
|
||||
}
|
||||
|
||||
if ((xb & XINPUT_GAMEPAD_B) || (xb & XINPUT_GAMEPAD_DPAD_RIGHT)) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_RED;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
||||
|
||||
static void tokyo_xi_get_sensors(uint8_t *sense_out)
|
||||
{
|
||||
uint8_t sense;
|
||||
|
||||
XINPUT_STATE xi;
|
||||
WORD xb;
|
||||
BYTE xt_l;
|
||||
BYTE xt_r;
|
||||
|
||||
assert(sense_out != NULL);
|
||||
|
||||
sense = 0;
|
||||
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
XInputGetState(0, &xi);
|
||||
xb = xi.Gamepad.wButtons;
|
||||
xt_l = xi.Gamepad.bLeftTrigger;
|
||||
xt_r = xi.Gamepad.bRightTrigger;
|
||||
|
||||
float xt_l_f = xt_l / 255.0f;
|
||||
float xt_r_f = xt_r / 255.0f;
|
||||
|
||||
// Normalize both triggers to 0..1 and find the max directly
|
||||
float trigger = fmaxf(xt_l_f, xt_r_f);
|
||||
|
||||
const int max_jump_levels = 6;
|
||||
float jump_threshold = 1.0f / max_jump_levels;
|
||||
|
||||
/* FOOT SENSOR inputs */
|
||||
|
||||
// Determine if both foot sensors should be set
|
||||
bool left_active = xt_l_f > jump_threshold;
|
||||
bool right_active = xt_r_f > jump_threshold;
|
||||
|
||||
// Set foot sensors based on individual trigger activity
|
||||
if (left_active) {
|
||||
sense |= TOKYO_IO_SENSE_FOOT_LEFT;
|
||||
}
|
||||
if (right_active) {
|
||||
sense |= TOKYO_IO_SENSE_FOOT_RIGHT;
|
||||
}
|
||||
|
||||
/* JUMP SENSOR inputs */
|
||||
|
||||
// If both triggers are active, set jump levels and both foot sensors
|
||||
if (left_active && right_active) {
|
||||
float trigger_avg = (xt_l_f + xt_r_f) / 2.0f;
|
||||
|
||||
// Calculate the appropriate jump level
|
||||
for (int i = 1; i <= max_jump_levels; ++i) {
|
||||
if (trigger_avg >= i * jump_threshold) {
|
||||
sense |= (TOKYO_IO_SENSE_JUMP_1 << (i - 1));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*sense_out = sense;
|
||||
}
|
8
tokyoio/xi.h
Normal file
8
tokyoio/xi.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "tokyoio/backend.h"
|
||||
#include "tokyoio/config.h"
|
||||
|
||||
HRESULT tokyo_xi_init(const struct tokyo_io_backend **backend);
|
Loading…
Reference in New Issue
Block a user