soul: Add initial code by Dniel97

Co-authored-by: Dniel97 <Dniel97@noreply.gitea.tendokyu.moe>
This commit is contained in:
Wither OrNot
2026-01-29 16:56:57 -05:00
co-authored by Dniel97
parent 966eeeeb87
commit 7ab2439955
19 changed files with 986 additions and 0 deletions
+17
View File
@@ -256,6 +256,22 @@ $(BUILD_DIR_ZIP)/apm3.zip:
$(V)strip $(BUILD_DIR_ZIP)/apm3/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/apm3 ; zip -r ../apm3.zip *
$(BUILD_DIR_ZIP)/soul.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/soul
$(V)mkdir -p $(BUILD_DIR_ZIP)/soul/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_GAMES_64)/soulhook/soulhook.dll \
$(DIST_DIR)/soul/segatools.ini \
$(DIST_DIR)/soul/launch.bat \
$(DIST_DIR)/soul/config_hook.json \
$(BUILD_DIR_ZIP)/soul
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/soul/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/soul/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/soul ; zip -r ../soul.zip *
$(BUILD_DIR_ZIP)/doc.zip: \
$(DOC_DIR)/config \
$(DOC_DIR)/chunihook.md \
@@ -282,6 +298,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
$(BUILD_DIR_ZIP)/fgo.zip \
$(BUILD_DIR_ZIP)/kemono.zip \
$(BUILD_DIR_ZIP)/apm3.zip \
$(BUILD_DIR_ZIP)/soul.zip
CHANGELOG.md \
README.md \
+7
View File
@@ -0,0 +1,7 @@
{
"aime": {
"firmware_path": [
"aime_firm\\update_15396_6728_94.bin"
]
}
}
+13
View File
@@ -0,0 +1,13 @@
@echo off
pushd %~dp0
start "AM Daemon" /min inject -d -k soulhook.dll .\tools\amdaemon\amdaemon.exe -c .\tools\amdaemon\config_common.json .\tools\amdaemon\config_emoney_satellite_pro.json .\tools\amdaemon\config_satellite.json
inject -d -k soulhook.dll fbb.exe
taskkill /f /im amdaemon.exe > nul 2>&1
echo.
echo Game processes have terminated
pause
+178
View File
@@ -0,0 +1,178 @@
; -----------------------------------------------------------------------------
; 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 Axxx directories)
option=
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=
; -----------------------------------------------------------------------------
; Device settings
; -----------------------------------------------------------------------------
[aime]
; Enable Aime card reader assembly emulation. Disable to use a real SEGA Aime
; reader.
enable=1
aimePath=DEVICE\aime.txt
; Virtual-key code. If this button is **held** then the emulated IC card reader
; emulates an IC card in its proximity. A variety of different IC cards can be
; emulated; the exact choice of card that is emulated depends on the presence or
; absence of the configured card ID files. Default is the Return key.
scan=0x0D
[vfd]
; Enable VFD emulation. Disable to use a real VFD
; GP1232A02A FUTABA assembly.
enable=1
[led15093]
; 837-15093-06 LED board emulation setting.
enable=1
; COM port number for the LED board.
portNo1=2
; -----------------------------------------------------------------------------
; 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.32.0` and this value is set to `11`, then the
; local host's virtualized LAN IP is `192.168.32.11`).
addrSuffix=11
; -----------------------------------------------------------------------------
; Board settings
; -----------------------------------------------------------------------------
[keychip]
; Keychip serial number. Keychip serials observed in the wild follow this
; pattern: `A\d{2}(E|X)-(01|20)[ABCDU]\d{8}`.
id=A69E-01A88888888
; 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.168.0
; Override the game's four-character platform code (e.g. `AAV2` for Nu 2). This
; is actually supposed to be a separate three-character `platformId` and
; integer `modelType` setting, but they are combined here for convenience.
; Use `ACA1` for the Server/Client, depending on the dipsw1
; Use `ACA5` for the Standalone mode
platformId=ACA1
[pcbid]
; Set the Windows host name. This should be an ALLS MAIN ID, without the
; hyphen (which is not a valid character in a Windows host name).
serialNo=ACAE01A99999999
[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
; LAN Install: If multiple machines are present on the same LAN then set
; this to 1 on exactly one machine and set this to 0 on all others.
dipsw1=1
; -----------------------------------------------------------------------------
; Misc. hook settings
; -----------------------------------------------------------------------------
[touch]
; WinTouch emulation setting.
enable=1
remap=1
cursor=1
; -----------------------------------------------------------------------------
; Custom IO settings
; -----------------------------------------------------------------------------
[aimeio]
; To use a custom card reader IO DLL enter its path here.
; Leave empty if you want to use Segatools built-in keyboard input.
path=
[soulio]
; To use a custom Soul IO DLL enter its path here.
; Leave empty if you want to use Segatools built-in 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 "keyboard" to use keyboard input, "xinput" to use a gamepad, "dinput" to
; use an arcade stick.
mode=keyboard
[keyboard]
; START button (default: P)
start=0x50
; HOME button (default: O)
a=0x4a
b=0x4b
c=0x4c
[xinput]
; Defaults:
; A B LB RB
; X Y LT RT
; Use the left thumbstick for directional controls on XInput controllers.
analogStickEnabled=1
[dinput]
; Name of the DirectInput stick to use (or any text that occurs in its name)
; Example: TE2+
;
; If this is left blank then the first DirectInput device will be used.
deviceName=
; DirectInput button numbers to map to menu inputs. Note that buttons are
; numbered from 1; some software numbers buttons from 0.
start=9
home=10
a=1
b=4
c=6
+109
View File
@@ -0,0 +1,109 @@
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include "board/config.h"
#include "amex/amex.h"
#include "amex/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "soulhook/config.h"
#include "platform/config.h"
void soul_dll_config_load(
struct soul_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"soulio",
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[0] = GetPrivateProfileIntW(L"led15070", L"portNo1", 0, filename);
cfg->port_no[1] = GetPrivateProfileIntW(L"led15070", L"portNo2", 0, filename);
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0xA0, filename);
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xAA53, filename);
GetPrivateProfileStringW(
L"led15093",
L"boardNumber",
L"15093-06",
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"6710A",
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 soul_hook_config_load(
struct soul_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
io4_config_load(&cfg->io4, filename);
aime_config_load(&cfg->aime, filename);
dvd_config_load(&cfg->dvd, filename);
vfd_config_load(&cfg->vfd, filename);
touch_screen_config_load(&cfg->touch, filename);
led15093_config_load(&cfg->led15093, filename);
soul_dll_config_load(&cfg->dll, filename);
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <stddef.h>
#include "board/config.h"
#include "board/led15093.h"
#include "hooklib/dvd.h"
#include "hooklib/touch.h"
#include "soulhook/soul-dll.h"
#include "platform/config.h"
struct soul_hook_config {
struct platform_config platform;
struct io4_config io4;
struct aime_config aime;
struct dvd_config dvd;
struct vfd_config vfd;
struct touch_screen_config touch;
struct led15093_config led15093;
struct soul_dll_config dll;
};
void soul_dll_config_load(
struct soul_dll_config *cfg,
const wchar_t *filename);
void soul_hook_config_load(
struct soul_hook_config *cfg,
const wchar_t *filename);
+120
View File
@@ -0,0 +1,120 @@
#include <windows.h>
#include <stdlib.h>
#include "board/io4.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "hook/process.h"
#include "hooklib/dvd.h"
#include "hooklib/touch.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "soulhook/config.h"
#include "soulhook/io4.h"
#include "soulhook/soul-dll.h"
#include "platform/platform.h"
#include "util/dprintf.h"
#include "util/env.h"
static HMODULE soul_hook_mod;
static process_entry_t soul_startup;
static struct soul_hook_config soul_hook_cfg;
static DWORD CALLBACK soul_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin soul_pre_startup ---\n");
/* Load config */
soul_hook_config_load(&soul_hook_cfg, get_config_path());
/* Hook Win32 APIs */
dvd_hook_init(&soul_hook_cfg.dvd, soul_hook_mod);
touch_screen_hook_init(&soul_hook_cfg.touch, soul_hook_mod);
serial_hook_init();
/* Initialize emulation hooks */
hr = platform_hook_init(
&soul_hook_cfg.platform,
"SDDP",
"ACA2",
soul_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = soul_dll_init(&soul_hook_cfg.dll, soul_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&soul_hook_cfg.aime, 3, 3, soul_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(&soul_hook_cfg.vfd, 1);
if (FAILED(hr)) {
goto fail;
}
hr = soul_io4_hook_init(&soul_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
unsigned int led_port_no[2] = {2, 0};
hr = led15093_hook_init(&soul_hook_cfg.led15093,
soul_dll.led_init, soul_dll.led_set_leds, led_port_no);
if (FAILED(hr)) {
goto fail;
}
/* Initialize debug helpers */
spike_hook_init(get_config_path());
dprintf("--- End soul_pre_startup ---\n");
/* Jump to EXE start address */
return soul_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
soul_hook_mod = mod;
hr = process_hijack_startup(soul_pre_startup, &soul_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}
+89
View File
@@ -0,0 +1,89 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/io4.h"
#include "soulhook/soul-dll.h"
#include "util/dprintf.h"
static HRESULT soul_io4_poll(void *ctx, struct io4_state *state);
static uint16_t coins;
static const struct io4_ops soul_io4_ops = {
.poll = soul_io4_poll,
};
HRESULT soul_io4_hook_init(const struct io4_config *cfg)
{
HRESULT hr;
assert(soul_dll.init != NULL);
hr = io4_hook_init(cfg, &soul_io4_ops, NULL, L"Soul Reverse", false);
if (FAILED(hr)) {
return hr;
}
return soul_dll.init();
}
static HRESULT soul_io4_poll(void *ctx, struct io4_state *state)
{
uint8_t opbtn;
uint8_t left;
uint8_t right;
int16_t lever;
HRESULT hr;
assert(soul_dll.poll != NULL);
assert(soul_dll.get_opbtns != NULL);
assert(soul_dll.get_gamebtns != NULL);
memset(state, 0, sizeof(*state));
hr = soul_dll.poll();
if (FAILED(hr)) {
return hr;
}
opbtn = 0;
left = 0;
right = 0;
lever = 0;
soul_dll.get_opbtns(&opbtn);
soul_dll.get_gamebtns(&left, &right);
if (opbtn & SOUL_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & SOUL_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
if (opbtn & SOUL_IO_OPBTN_COIN) {
coins++;
}
state->chutes[0] = coins << 8;
if (left & SOUL_IO_GAMEBTN_A) {
state->buttons[0] |= 1 << 0;
}
if (left & SOUL_IO_GAMEBTN_B) {
state->buttons[0] |= 1 << 5;
}
if (left & SOUL_IO_GAMEBTN_C) {
state->buttons[0] |= 1 << 7;
}
return S_OK;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "board/io4.h"
HRESULT soul_io4_hook_init(const struct io4_config *cfg);
+30
View File
@@ -0,0 +1,30 @@
shared_library(
'soulhook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'soulhook.def',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
xinput_lib,
],
link_with : [
aimeio_lib,
amex_lib,
board_lib,
hooklib_lib,
soulio_lib,
platform_lib,
util_lib,
],
sources : [
'config.c',
'config.h',
'dllmain.c',
'io4.c',
'io4.h',
'soul-dll.c',
'soul-dll.h',
],
)
+115
View File
@@ -0,0 +1,115 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "soulhook/soul-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym soul_dll_syms[] = {
{
.sym = "soul_io_init",
.off = offsetof(struct soul_dll, init),
}, {
.sym = "soul_io_poll",
.off = offsetof(struct soul_dll, poll),
}, {
.sym = "soul_io_get_opbtns",
.off = offsetof(struct soul_dll, get_opbtns),
}, {
.sym = "soul_io_get_gamebtns",
.off = offsetof(struct soul_dll, get_gamebtns),
}, {
.sym = "soul_io_led_init",
.off = offsetof(struct soul_dll, led_init),
}, {
.sym = "soul_io_led_set_colors",
.off = offsetof(struct soul_dll, led_set_leds),
}
};
struct soul_dll soul_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 soul_dll_init(const struct soul_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("Soul Reverse IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Soul Reverse IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "soul_io_get_api_version");
if (get_api_version != NULL) {
soul_dll.api_version = get_api_version();
} else {
soul_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose soul_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (soul_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Soul Reverse IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
soul_dll.api_version);
goto end;
}
sym = soul_dll_syms;
hr = dll_bind(&soul_dll, src, &sym, _countof(soul_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Soul Reverse 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;
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include <windows.h>
#include "soulio/soulio.h"
struct soul_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*poll)(void);
void (*get_opbtns)(uint8_t *opbtn);
void (*get_gamebtns)(uint8_t *left, uint8_t *right);
HRESULT (*led_init)(void);
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
};
struct soul_dll_config {
wchar_t path[MAX_PATH];
};
extern struct soul_dll soul_dll;
HRESULT soul_dll_init(const struct soul_dll_config *cfg, HINSTANCE self);
+22
View File
@@ -0,0 +1,22 @@
LIBRARY soulhook
EXPORTS
aime_io_get_api_version
aime_io_init
aime_io_led_set_color
aime_io_vfd_set_text
aime_io_vfd_set_state
aime_io_nfc_get_aime_id
aime_io_nfc_get_felica_id
aime_io_nfc_poll
amDllVideoClose @2
amDllVideoGetVBiosVersion @4
amDllVideoOpen @1
amDllVideoSetResolution @3
soul_io_get_api_version
soul_io_get_gamebtns
soul_io_get_opbtns
soul_io_init
soul_io_poll
soul_io_led_init
soul_io_led_set_colors
+20
View File
@@ -0,0 +1,20 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "soulio/config.h"
void soul_io_config_load(
struct soul_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);
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
struct soul_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
};
void soul_io_config_load(
struct soul_io_config *cfg,
const wchar_t *filename);
+12
View File
@@ -0,0 +1,12 @@
soulio_lib = static_library(
'soulio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
sources : [
'soulio.c',
'soulio.h',
'config.c',
'config.h',
],
)
+97
View File
@@ -0,0 +1,97 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include "soulio/soulio.h"
#include "soulio/config.h"
#include "util/dprintf.h"
#include "util/env.h"
static uint8_t soul_opbtn;
static uint8_t soul_gamebtn;
static struct soul_io_config soul_io_cfg;
static bool soul_io_coin;
uint16_t soul_io_get_api_version(void)
{
return 0x0100;
}
HRESULT soul_io_init(void)
{
soul_io_config_load(&soul_io_cfg, get_config_path());
return S_OK;
}
HRESULT soul_io_poll(void)
{
int lever;
int xlever;
XINPUT_STATE xi;
WORD xb;
soul_opbtn = 0;
soul_gamebtn = 0;
if (GetAsyncKeyState(soul_io_cfg.vk_test) & 0x8000) {
soul_opbtn |= SOUL_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(soul_io_cfg.vk_service) & 0x8000) {
soul_opbtn |= SOUL_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(soul_io_cfg.vk_coin) & 0x8000) {
if (!soul_io_coin) {
soul_io_coin = true;
soul_opbtn |= SOUL_IO_OPBTN_COIN;
}
} else {
soul_io_coin = false;
}
memset(&xi, 0, sizeof(xi));
XInputGetState(0, &xi);
xb = xi.Gamepad.wButtons;
if (xb & XINPUT_GAMEPAD_X) {
soul_gamebtn |= SOUL_IO_GAMEBTN_A;
}
if (xb & XINPUT_GAMEPAD_A) {
soul_gamebtn |= SOUL_IO_GAMEBTN_B;
}
if (xb & XINPUT_GAMEPAD_B) {
soul_gamebtn |= SOUL_IO_GAMEBTN_C;
}
return S_OK;
}
void soul_io_get_opbtns(uint8_t *opbtn)
{
if (opbtn != NULL) {
*opbtn = soul_opbtn;
}
}
void soul_io_get_gamebtns(uint8_t *gamebtn)
{
if (gamebtn != NULL) {
*gamebtn = soul_gamebtn;
}
}
HRESULT soul_io_led_init(void)
{
return S_OK;
}
void soul_io_led_set_colors(uint8_t board, uint8_t *rgb)
{
return;
}
+77
View File
@@ -0,0 +1,77 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
SOUL_IO_OPBTN_TEST = 0x01,
SOUL_IO_OPBTN_SERVICE = 0x02,
SOUL_IO_OPBTN_COIN = 0x04,
};
enum {
SOUL_IO_GAMEBTN_A = 0x01,
SOUL_IO_GAMEBTN_B = 0x02,
SOUL_IO_GAMEBTN_C = 0x04,
};
/* Get the version of the Soul Reverse 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 soul_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after soul_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT soul_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 soul_io_poll(void);
/* Get the state of the cabinet's operator buttons as of the last poll. See
SOUL_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 soul_io_get_opbtns(uint8_t *opbtn);
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
SOUL_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 soul_io_get_gamebtns(uint8_t *gamebtn);
/* Initialize LED emulation. This function will be called before any
other soul_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. */
HRESULT soul_io_led_init(void);
/* Update the RGB LEDs.
Exact layout is TBD. */
void soul_io_led_set_colors(uint8_t board, uint8_t *rgb);
+2
View File
@@ -128,6 +128,7 @@ subdir('games/tokyoio')
subdir('games/fgoio')
subdir('games/kemonoio')
subdir('games/apm3io')
subdir('games/soulio')
subdir('games/chunihook')
subdir('games/divahook')
@@ -145,3 +146,4 @@ subdir('games/tokyohook')
subdir('games/fgohook')
subdir('games/kemonohook')
subdir('games/apm3hook')
subdir('games/soulhook')