sekito: add hook

This commit is contained in:
2025-09-30 12:14:00 +02:00
parent 7dd1cd4a62
commit f33fe0f2ae
26 changed files with 1941 additions and 1 deletions

View File

@ -275,6 +275,27 @@ $(BUILD_DIR_ZIP)/ekt.zip:
$(V)strip $(BUILD_DIR_ZIP)/ekt/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/ekt ; zip -r ../ekt.zip *
$(BUILD_DIR_ZIP)/sekito.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/sekito
$(V)mkdir -p $(BUILD_DIR_ZIP)/sekito/DEVICE
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject_x86.exe \
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject_x64.exe \
$(BUILD_DIR_GAMES_64)/sekitohook/sekitohook_x64.dll \
$(BUILD_DIR_GAMES_32)/sekitohook/sekitohook_x86.dll \
$(DIST_DIR)/sekito/segatools_terminal.ini \
$(DIST_DIR)/sekito/segatools_satellite.ini \
$(DIST_DIR)/sekito/launch_terminal.bat \
$(DIST_DIR)/sekito/launch_satellite.bat \
$(DIST_DIR)/sekito/card_player.html \
$(DIST_DIR)/sekito/config_hook.json \
$(BUILD_DIR_ZIP)/sekito
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/sekito/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/sekito/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/sekito ; zip -r ../sekito.zip *
$(BUILD_DIR_ZIP)/doc.zip: \
$(DOC_DIR)/config \
$(DOC_DIR)/chunihook.md \

View File

@ -4,6 +4,7 @@
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <wtypes.h>

224
dist/sekito/card_player.html vendored Normal file
View File

@ -0,0 +1,224 @@
<!-- very basic thing, I can't do UX/CSS/design, don't blame me, I'm a network engineer lmao -->
<!DOCTYPE html>
<html>
<head>
<title>Taisen Card Field</title>
</head>
<style>
html, body {
width: 99%;
height: 99%;
}
.playfield {
width: 79%;
height: 100%;
float: left;
border: 1px solid black;
}
.card_menu {
width: 20%;
height: 100%;
border: 1px solid black;
overflow-x: hidden;
overflow-y: scroll;
}
.card {
width: 200px;
transform: rotate(-90deg);
}
#playfield .card {
position: absolute;
}
#status {
margin-bottom: 50px;
}
</style>
<script>
var VERSION = 1;
var socket;
var state = 0;
var game_id;
var cards = [];
var current_card_fetch = 0;
function send(obj){
if (!socket){ return; }
var data = JSON.stringify(obj);
console.log("Sending: " + data);
socket.send(data);
}
function connect(){
socket = new WebSocket("ws://127.0.0.1:3497/y3io");
socket.onopen = function(e) {
document.getElementById("status").innerText = "Connected. Loading information...";
state = 0;
send({
version: VERSION,
command: "get_game_id"
});
};
socket.onmessage = function(event) {
console.log("Received: " + event.data);
handle_response(JSON.parse(event.data));
};
socket.onclose = function(event) {
state = -1;
document.getElementById("status").innerHTML = "Disconnected. <a href='javascript:window.location.reload();'>Reconnect</a>";
};
socket.onerror = function(error) {
console.log(error);
};
}
function handle_response(obj){
if (!obj.success){
alert("Error receiving data while in state " + state + ": " + obj.error);
socket.close();
return;
}
if (state == 0){
game_id = obj.game_id;
document.getElementById("status").innerText = "Connected to "+game_id+". Loading cards...";
state = 1;
send({
version: VERSION,
command: "get_cards"
});
} else if (state == 1){
cards = obj.cards;
document.getElementById("status").innerText = "Connected to "+game_id+". Loading card images...";
document.getElementById("cards").innerHTML = "";
document.getElementById("playfield").innerHTML = "";
current_card_fetch = 0;
state = 2;
if (cards.length > 0){
fetch_next_card();
} else {
document.getElementById("status").innerText = "Connected to "+game_id+". No cards available.";
}
} else if (state == 2){
cards[current_card_fetch].image = obj.data;
document.getElementById("cards").innerHTML += "<img class='card' src='data:image/bmp;base64, "+obj.data+"' onclick='spawn_card("+current_card_fetch+");' />";
if (++current_card_fetch >= cards.length){
document.getElementById("status").innerText = "Connected to "+game_id+".";
state = 3;
} else {
fetch_next_card();
}
}
}
function fetch_next_card(){
var p = cards[current_card_fetch].path;
if (!p.includes("_front")){
current_card_fetch++;
fetch_next_card();
return;
}
send({
version: VERSION,
command: "get_card_image",
path: p
});
}
function spawn_card(i){
if (state != 3){
return;
}
document.getElementById("playfield").innerHTML += "<img class='card' src='data:image/bmp;base64, "+cards[i].image+"' onmousedown='startMoving(event, this, "+i+");' />";
}
function update_pos(i, x, y){
if (state != 3){
return;
}
var panelHeight = 1272;
var panelWidth = 1260;
cards[i].x = panelHeight - ((y / window.screen.height) * panelHeight);
cards[i].y = panelWidth - ((x / window.screen.width) * panelWidth);
cards[i].rotation = 0;
var list = [];
for (var j = 0; j < cards.length; j++){
var c = cards[j];
if (c.x && c.y){
list.push({
card_id: c.card_id,
x: c.x,
y: c.y,
rotation: c.rotation
});
}
}
send({
version: VERSION,
command: "set_field",
cards: list
});
}
</script>
<script>
var mousePosition;
var offset = [-75,-75];
var div;
var current_card = -1;
var isDown = false;
function startMoving(e, el, card){
div = el;
isDown = true;
current_card = card;
offset = [
div.offsetLeft - e.clientX,
div.offsetTop - e.clientY
];
}
document.addEventListener('mouseup', function() {
isDown = false;
current_card = -1;
}, true);
document.addEventListener('mousemove', function(event) {
event.preventDefault();
if (isDown) {
mousePosition = {
x : event.clientX,
y : event.clientY
};
div.style.left = (mousePosition.x + offset[0]) + 'px';
div.style.top = (mousePosition.y + offset[1]) + 'px';
update_pos(current_card, event.clientX, event.clientY);
}
}, true);
</script>
<body onload="connect();">
<div id="playfield" class="playfield">
</div>
<div class="card_menu">
<div id="status">Please wait...</div>
<div id="cards">
</div>
</div>
</body>
</html>

13
dist/sekito/config_hook.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
"common": {
"language": "english"
},
"network": {
"property": {
"dhcp": true
}
},
"allnet_auth": {
"type": "1.0"
}
}

15
dist/sekito/launch_satellite.bat vendored Normal file
View File

@ -0,0 +1,15 @@
@echo off
set SEGATOOLS_CONFIG_PATH=.\segatools_satellite.ini
pushd %~dp0
start "AM Daemon" /min inject_x64 -d -k sekitohook_x64.dll amdaemon.exe -c config_new.json -c config_video_single.json -c config_video_multi.json -c config_input_sate.json -c config_input_terminal.json -c config_input_terminal_exp.json -c config_hook.json
inject_x86 -d -k sekitohook_x86.dll appSate.exe
taskkill /f /im appSate.exe > nul 2>&1
taskkill /f /im amdaemon.exe > nul 2>&1
echo.
echo Game processes have terminated
pause

19
dist/sekito/launch_terminal.bat vendored Normal file
View File

@ -0,0 +1,19 @@
@echo off
set SEGATOOLS_CONFIG_PATH=.\segatools_satellite.ini
pushd %~dp0
start "AM Daemon" /min inject_x64 -d -k sekitohook_x64.dll amdaemon.exe -c config_new.json -c config_video_single.json -c config_video_multi.json -c config_input_sate.json -c config_input_terminal.json -c config_input_terminal_exp.json -c config_hook.json
call server\server_start.bat
inject_x86 -d -k sekitohook_x86.dll appTerminal.exe
taskkill /f /im appTerminal.exe > nul 2>&1
taskkill /f /im amdaemon.exe > nul 2>&1
call server\server_stop.bat
echo.
echo Game processes have terminated
pause

174
dist/sekito/segatools_satellite.ini vendored Normal file
View File

@ -0,0 +1,174 @@
; -----------------------------------------------------------------------------
; 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
; -----------------------------------------------------------------------------
; 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
; -----------------------------------------------------------------------------
; 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.189.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.
; 1 = Terminal (TM)
; 2 = Satellite (ST)
platformId=AAV2
[system]
; Enable ALLS system settings.
enable=1
; LAN Install: If multiple machines are present on the same LAN then set
; this to 0 on exactly one machine and set this to 1 on all others.
dipsw1=0
; -----------------------------------------------------------------------------
; Misc. hooks settings
; -----------------------------------------------------------------------------
[flatPanelReader]
; Enable the Y3 board emulation.
enable=1
[y3ws]
; Enable the Y3 websocket server.
enable=1
; Set the TCP port on which the Y3 websocket server runs.
port=3594
; -----------------------------------------------------------------------------
; LED settings
; -----------------------------------------------------------------------------
[led15093]
; Enable the 837-15093-06 board emulation.
enable=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=
[ektio]
; To use a custom Eiketsu Taisen IO DLL enter its path here.
; Leave empty if you want to use Segatools built-in keyboard/gamepad input.
path=
[y3io]
; To use a custom Y3 IO DLL enter its path here.
; Leave empty if you want to use ... TBA ...
path=
; -----------------------------------------------------------------------------
; Input settings
; -----------------------------------------------------------------------------
; Keyboard bindings are 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
; SW1. Default is the 4 key.
sw1=0x34
; SW2. Default is the 5 key.
sw2=0x35
; Input API selection for IO4 input emulator.
; For now only "keyboard" is supported.
mode=keyboard
[keyboard]
menu=0x41
start=0x42
stratagem=0x43
stratagem_lock=0x44
hougu=0x45
ryuuha=0x46
tenkey_0=0x60
tenkey_1=0x61
tenkey_2=0x62
tenkey_3=0x63
tenkey_4=0x64
tenkey_5=0x65
tenkey_6=0x66
tenkey_7=0x67
tenkey_8=0x68
tenkey_9=0x69
tenkey_clear=0x6E
tenkey_enter=0x0D
vol_up=0x21
vol_down=0x22
trackball_up=0x26
trackball_right=0x27
trackball_down=0x28
trackball_left=0x25
speed_modifier=10

137
dist/sekito/segatools_terminal.ini vendored Normal file
View File

@ -0,0 +1,137 @@
; -----------------------------------------------------------------------------
; 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
; -----------------------------------------------------------------------------
; 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
; -----------------------------------------------------------------------------
; 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.189.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.
; 1 = Terminal (TM)
; 2 = Satellite (ST)
platformId=AAV1
[system]
; Enable ALLS system settings.
enable=1
; LAN Install: If multiple machines are present on the same LAN then set
; this to 0 on exactly one machine and set this to 1 on all others.
dipsw1=0
; -----------------------------------------------------------------------------
; LED settings
; -----------------------------------------------------------------------------
[led15093]
; Enable the 837-15093-06 board emulation.
enable=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=
[ektio]
; To use a custom Eiketsu Taisen 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 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
; SW1. Default is the 4 key.
sw1=0x34
; SW2. Default is the 5 key.
sw2=0x35
; Input API selection for IO4 input emulator.
; For now only "keyboard" is supported.
mode=keyboard
[keyboard]
cancel=0x53
decide=0x41
up=0x26
right=0x27
down=0x28
left=0x25
left_2=0x4F
right_2=0x57

107
games/sekitohook/config.c Normal file
View File

@ -0,0 +1,107 @@
#include <assert.h>
#include <stdlib.h>
#include "board/config.h"
#include "sekitohook/config.h"
#include "sekitohook/sekito-dll.h"
#include "hooklib/config.h"
#include "platform/config.h"
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"led15093", L"portNo1", 0, filename);
cfg->port_no[1] = GetPrivateProfileIntW(L"led15093", 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 sekito_dll_config_load(
struct sekito_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"sekitoio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void sekito_hook_config_load(
struct sekito_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
aime_config_load(&cfg->aime, filename);
io4_config_load(&cfg->io4, filename);
dvd_config_load(&cfg->dvd, filename);
led15093_config_load(&cfg->led15093, filename);
y3_config_load(&cfg->y3, filename);
printer_chc_config_load(&cfg->printer, filename);
unity_config_load(&cfg->unity, filename);
sekito_dll_config_load(&cfg->dll, filename);
}

37
games/sekitohook/config.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <stddef.h>
#include "board/sg-reader.h"
#include "board/config.h"
#include "board/led15093.h"
#include "sekitohook/sekito-dll.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "hooklib/printer_chc.h"
#include "platform/config.h"
#include "unityhook/config.h"
struct sekito_hook_config {
struct platform_config platform;
struct aime_config aime;
struct io4_config io4;
struct dvd_config dvd;
struct led15093_config led15093;
struct y3_config y3;
struct sekito_dll_config dll;
struct unity_config unity;
struct printer_chc_config printer;
};
void sekito_dll_config_load(
struct sekito_dll_config *cfg,
const wchar_t *filename);
void sekito_hook_config_load(
struct sekito_hook_config *cfg,
const wchar_t *filename);

178
games/sekitohook/dllmain.c Normal file
View File

@ -0,0 +1,178 @@
/*
"Sangokushi Taisen" (sekito) hook
Devices
USB: 837-15257-01 "Type 4" I/O Board
COM12: 837-15396 "Gen 3" Aime Reader
[Satellite]
USB: Printer
COM1: 837-15093-06 LED Controller Board
COM11: Printer Camera
[Terminal]
COM1: 837-15396 "Gen 3" Aime Reader
COM3: 837-15093-06 LED Controller Board
*/
#include <windows.h>
#include <stdlib.h>
#include "sekito-dll.h"
#include "board/sg-reader.h"
#include "board/led15093.h"
#include "hook/process.h"
#include "hook/iohook.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "sekitohook/config.h"
#include "sekitohook/io4.h"
#include "hooklib/printer_cx.h"
#include "platform/platform.h"
#include "unityhook/hook.h"
#include "util/dprintf.h"
#include "util/env.h"
#include "hooklib/y3-dll.h"
#include "hooklib/y3.h"
static HMODULE sekito_hook_mod;
static process_entry_t sekito_startup;
static struct sekito_hook_config sekito_hook_cfg;
static void unity_hook_callback(HMODULE hmodule, const wchar_t* p) {
netenv_hook_apply_hooks(hmodule);
}
static DWORD CALLBACK sekito_pre_startup(void)
{
HRESULT hr;
bool is_terminal;
dprintf("--- Begin sekito_pre_startup ---\n");
/* Load config */
sekito_hook_config_load(&sekito_hook_cfg, get_config_path());
/* Hook Win32 APIs */
dvd_hook_init(&sekito_hook_cfg.dvd, sekito_hook_mod);
serial_hook_init();
/* Hook external DLL APIs */
hr = y3_hook_init(&sekito_hook_cfg.y3, sekito_hook_mod, get_config_path());
if (FAILED(hr)) {
goto fail;
}
printer_chc_hook_init(&sekito_hook_cfg.printer, 0, sekito_hook_mod);
/* Initialize emulation hooks */
hr = platform_hook_init(
&sekito_hook_cfg.platform,
"SDDD",
"AAV2",
sekito_hook_mod);
if (FAILED(hr)) {
goto fail;
}
/* Initialize Terminal/Satellite hooks */
if (strncmp(sekito_hook_cfg.platform.nusec.platform_id, "AAV1", 4) == 0) {
// Terminal
is_terminal = true;
} else if (strncmp(sekito_hook_cfg.platform.nusec.platform_id, "AAV2", 4) == 0) {
// Satellite
is_terminal = false;
} else {
// Unknown
dprintf("Unknown platform ID: %s\n", sekito_hook_cfg.platform.nusec.platform_id);
goto fail;
}
// LED: terminal uses COM 3 and satellite use COM 2
unsigned int led_port_no[2] = {is_terminal ? 3 : 2, 0};
hr = sekito_dll_init(&sekito_hook_cfg.dll, sekito_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sekito_io4_hook_init(&sekito_hook_cfg.io4, is_terminal);
if (FAILED(hr)) {
goto fail;
}
hr = led15093_hook_init(&sekito_hook_cfg.led15093,
sekito_dll.led_init, sekito_dll.led_set_leds, led_port_no);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&sekito_hook_cfg.aime, 12, 3,
sekito_hook_mod);
if (FAILED(hr)) {
goto fail;
}
if (is_terminal) {
hr = sg_reader_hook_init(&sekito_hook_cfg.aime, 1, 3,
sekito_hook_mod);
if (FAILED(hr)) {
goto fail;
}
}
/* Initialize debug helpers */
spike_hook_init(get_config_path());
dprintf("--- End sekito_pre_startup ---\n");
/* Jump to EXE start address */
return sekito_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
sekito_hook_mod = mod;
hr = process_hijack_startup(sekito_pre_startup, &sekito_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

212
games/sekitohook/io4.c Normal file
View File

@ -0,0 +1,212 @@
#include "io4.h"
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/io4.h"
#include "sekitohook/sekito-dll.h"
#include "util/dprintf.h"
static HRESULT sekito_io4_poll(void *ctx, struct io4_state *state);
static uint16_t coins;
static const struct io4_ops sekito_io4_ops = {
.poll = sekito_io4_poll,
};
static bool io_is_terminal;
HRESULT sekito_io4_hook_init(const struct io4_config *cfg, bool is_terminal)
{
HRESULT hr;
assert(sekito_dll.init != NULL);
hr = io4_hook_init(cfg, &sekito_io4_ops, NULL);
io_is_terminal = is_terminal;
if (FAILED(hr)) {
return hr;
}
return sekito_dll.init();
}
static HRESULT sekito_io4_poll(void *ctx, struct io4_state *state)
{
uint8_t opbtn;
uint16_t x, y;
uint32_t gamebtn;
HRESULT hr;
assert(sekito_dll.poll != NULL);
assert(sekito_dll.get_opbtns != NULL);
assert(sekito_dll.get_gamebtns != NULL);
assert(sekito_dll.get_trackball_position != NULL);
memset(state, 0, sizeof(*state));
hr = sekito_dll.poll();
if (FAILED(hr)) {
return hr;
}
opbtn = 0;
gamebtn = 0;
x = 0;
y = 0;
sekito_dll.get_opbtns(&opbtn);
sekito_dll.get_gamebtns(&gamebtn);
sekito_dll.get_trackball_position(&x, &y);
if (opbtn & SEKITO_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & SEKITO_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
if (opbtn & SEKITO_IO_OPBTN_SW1) {
state->buttons[0] |= 1 << 10;
}
if (opbtn & SEKITO_IO_OPBTN_SW2) {
state->buttons[0] |= 1 << 11;
}
if (opbtn & SEKITO_IO_OPBTN_COIN) {
coins++;
}
state->chutes[0] = coins << 8;
if (!io_is_terminal) {
if (gamebtn & SEKITO_IO_GAMEBTN_HOUGU) {
state->buttons[1] |= 1 << 6;
}
if (gamebtn & SEKITO_IO_GAMEBTN_MENU) {
state->buttons[1] |= 1 << 4;
}
if (gamebtn & SEKITO_IO_GAMEBTN_START) {
state->buttons[0] |= 1 << 15;
}
if (gamebtn & SEKITO_IO_GAMEBTN_STRATAGEM) {
state->buttons[1] |= 1 << 7;
}
if (gamebtn & SEKITO_IO_GAMEBTN_STRATAGEM_LOCK) {
state->buttons[1] |= 1 << 5;
}
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_0) {
state->buttons[0] |= SEKITO_NUMPAD_C2;
state->buttons[0] |= SEKITO_NUMPAD_R4;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_1) {
state->buttons[0] |= SEKITO_NUMPAD_C1;
state->buttons[0] |= SEKITO_NUMPAD_R1;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_2) {
state->buttons[0] |= SEKITO_NUMPAD_C2;
state->buttons[0] |= SEKITO_NUMPAD_R1;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_3) {
state->buttons[0] |= SEKITO_NUMPAD_C3;
state->buttons[0] |= SEKITO_NUMPAD_R1;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_4) {
state->buttons[0] |= SEKITO_NUMPAD_C1;
state->buttons[0] |= SEKITO_NUMPAD_R2;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_5) {
state->buttons[0] |= SEKITO_NUMPAD_C2;
state->buttons[0] |= SEKITO_NUMPAD_R2;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_6) {
state->buttons[0] |= SEKITO_NUMPAD_C3;
state->buttons[0] |= SEKITO_NUMPAD_R2;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_7) {
state->buttons[0] |= SEKITO_NUMPAD_C1;
state->buttons[0] |= SEKITO_NUMPAD_R3;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_8) {
state->buttons[0] |= SEKITO_NUMPAD_C2;
state->buttons[0] |= SEKITO_NUMPAD_R3;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_9) {
state->buttons[0] |= SEKITO_NUMPAD_C3;
state->buttons[0] |= SEKITO_NUMPAD_R3;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_CLEAR) {
state->buttons[0] |= SEKITO_NUMPAD_C1;
state->buttons[0] |= SEKITO_NUMPAD_R4;
}
if (gamebtn & SEKITO_IO_GAMEBTN_NUMPAD_ENTER) {
state->buttons[0] |= SEKITO_NUMPAD_C3;
state->buttons[0] |= SEKITO_NUMPAD_R4;
}
if (io_is_terminal) {
if (gamebtn & SEKITO_IO_GAMEBTN_TERMINAL_CANCEL) {
state->buttons[1] |= 1 << 0;
}
if (gamebtn & SEKITO_IO_GAMEBTN_TERMINAL_DECIDE) {
state->buttons[1] |= 1 << 1;
}
if (gamebtn & SEKITO_IO_GAMEBTN_TERMINAL_LEFT) {
state->buttons[0] |= 1 << 3;
}
if (gamebtn & SEKITO_IO_GAMEBTN_TERMINAL_UP) {
state->buttons[0] |= 1 << 5;
}
if (gamebtn & SEKITO_IO_GAMEBTN_TERMINAL_RIGHT) {
state->buttons[0] |= 1 << 2;
}
if (gamebtn & SEKITO_IO_GAMEBTN_TERMINAL_DOWN) {
state->buttons[0] |= 1 << 4;
}
if (gamebtn & SEKITO_IO_GAMEBTN_TERMINAL_LEFT_2) {
state->buttons[1] |= 1 << 3;
}
if (gamebtn & SEKITO_IO_GAMEBTN_TERMINAL_RIGHT_2) {
state->buttons[1] |= 1 << 2;
}
}
state->spinners[2] = x;
state->spinners[3] = y;
return S_OK;
}

17
games/sekitohook/io4.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <windows.h>
#include "board/io4.h"
enum {
SEKITO_NUMPAD_R1 = 1 << 9,
SEKITO_NUMPAD_R2 = 1 << 8,
SEKITO_NUMPAD_R3 = 1 << 7,
SEKITO_NUMPAD_R4 = 1 << 6,
SEKITO_NUMPAD_C1 = 1 << 5,
SEKITO_NUMPAD_C2 = 1 << 4,
SEKITO_NUMPAD_C3 = 1 << 3
};
HRESULT sekito_io4_hook_init(const struct io4_config *cfg, bool is_terminal);

View File

@ -0,0 +1,31 @@
shared_library(
'sekitohook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'sekitohook.def',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep')
],
link_with : [
aimeio_lib,
board_lib,
sekitoio_lib,
hooklib_lib,
jvs_lib,
platform_lib,
unityhook_lib,
util_lib,
y3io_lib,
],
sources : [
'config.c',
'config.h',
'dllmain.c',
'io4.c',
'io4.h',
'sekito-dll.c',
'sekito-dll.h',
],
)

View File

@ -0,0 +1,118 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "sekitohook/sekito-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym sekito_dll_syms[] = {
{
.sym = "sekito_io_init",
.off = offsetof(struct sekito_dll, init),
}, {
.sym = "sekito_io_poll",
.off = offsetof(struct sekito_dll, poll),
}, {
.sym = "sekito_io_get_opbtns",
.off = offsetof(struct sekito_dll, get_opbtns),
}, {
.sym = "sekito_io_get_gamebtns",
.off = offsetof(struct sekito_dll, get_gamebtns),
}, {
.sym = "sekito_io_get_trackball_position",
.off = offsetof(struct sekito_dll, get_trackball_position),
}, {
.sym = "sekito_io_led_init",
.off = offsetof(struct sekito_dll, led_init),
}, {
.sym = "sekito_io_led_set_colors",
.off = offsetof(struct sekito_dll, led_set_leds),
}
};
struct sekito_dll sekito_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 sekito_dll_init(const struct sekito_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("Sekito IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Sekito IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "sekito_io_get_api_version");
if (get_api_version != NULL) {
sekito_dll.api_version = get_api_version();
} else {
sekito_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose sekito_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (sekito_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Sekito IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
sekito_dll.api_version);
goto end;
}
sym = sekito_dll_syms;
hr = dll_bind(&sekito_dll, src, &sym, _countof(sekito_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Sekito IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
goto end;
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}
}
owned = NULL;
end:
if (owned != NULL) {
FreeLibrary(owned);
}
return hr;
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <windows.h>
#include "sekitoio/sekitoio.h"
struct sekito_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*poll)(void);
void (*get_opbtns)(uint8_t *opbtn);
void (*get_gamebtns)(uint32_t *gamebtn);
void (*get_trackball_position)(uint16_t *x, uint16_t *y);
HRESULT (*led_init)(void);
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
};
struct sekito_dll_config {
wchar_t path[MAX_PATH];
};
extern struct sekito_dll sekito_dll;
HRESULT sekito_dll_init(const struct sekito_dll_config *cfg, HINSTANCE self);

View File

@ -0,0 +1,61 @@
LIBRARY taisenhook
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
sekito_io_get_api_version
sekito_io_get_gamebtns
sekito_io_get_opbtns
sekito_io_get_trackball_position
sekito_io_init
sekito_io_poll
sekito_io_led_init
sekito_io_led_set_colors
y3_io_get_api_version
y3_io_init
y3_io_close
y3_io_get_cards
API_DLLVersion @1
API_GetLastError @2
API_GetErrorMessage @3
API_Connect @4
API_Close @5
API_Start @6
API_Stop @7
API_GetFirmVersion @8
API_GetFirmName @9
API_GetTargetCode @10
API_GetStatus @11
API_GetCounter @12
API_ClearError @13
API_Reset @14
API_GetCardInfo @15
API_GetCardInfoCharSize @16
API_SetDevice @17
API_SetCommand @18
API_FirmwareUpdate @19
API_Calibration @20
API_GetCalibrationResult @21
API_GetProcTime @22
API_GetMemStatus @23
API_GetMemCounter @24
API_SetSysControl @25
API_GetSysControl @26
API_SetParameter @27
API_GetParameter @28
API_TestReset @29
API_DebugReset @30
API_GetBoardType @31
API_GetCardDataSize @32
API_GetFirmDate @33
API_SystemCommand @34
API_CalcCheckSum @35
API_GetCheckSumResult @36
API_BlockRead @37
API_GetBlockReadResult @38
API_BlockWrite @39
API_GetDebugParam @40

10
games/sekitoio/backend.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
#include "sekitoio/sekitoio.h"
struct sekito_io_backend {
void (*get_gamebtns)(uint32_t *gamebtn);
void (*get_trackball)(uint16_t *x, uint16_t *y);
};

77
games/sekitoio/config.c Normal file
View File

@ -0,0 +1,77 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "sekitoio/config.h"
#include <xinput.h>
void sekito_kb_config_load(
struct sekito_kb_config *cfg,
const wchar_t *filename) {
cfg->vk_menu = GetPrivateProfileIntW(L"keyboard", L"menu", 'A', filename);
cfg->vk_start = GetPrivateProfileIntW(L"keyboard", L"start", 'S', filename);
cfg->vk_stratagem = GetPrivateProfileIntW(L"keyboard", L"stratagem", 'D', filename);
cfg->vk_stratagem_lock = GetPrivateProfileIntW(L"keyboard", L"stratagem_lock", 'F', filename);
cfg->vk_hougu = GetPrivateProfileIntW(L"keyboard", L"hougu", 'G', filename);
cfg->vk_ryuuha = GetPrivateProfileIntW(L"keyboard", L"ryuuha", 'H', filename);
cfg->vk_tenkey_0 = GetPrivateProfileIntW(L"keyboard", L"tenkey_0", VK_NUMPAD0, filename);
cfg->vk_tenkey_1 = GetPrivateProfileIntW(L"keyboard", L"tenkey_1", VK_NUMPAD1, filename);
cfg->vk_tenkey_2 = GetPrivateProfileIntW(L"keyboard", L"tenkey_2", VK_NUMPAD2, filename);
cfg->vk_tenkey_3 = GetPrivateProfileIntW(L"keyboard", L"tenkey_3", VK_NUMPAD3, filename);
cfg->vk_tenkey_4 = GetPrivateProfileIntW(L"keyboard", L"tenkey_4", VK_NUMPAD4, filename);
cfg->vk_tenkey_5 = GetPrivateProfileIntW(L"keyboard", L"tenkey_5", VK_NUMPAD5, filename);
cfg->vk_tenkey_6 = GetPrivateProfileIntW(L"keyboard", L"tenkey_6", VK_NUMPAD6, filename);
cfg->vk_tenkey_7 = GetPrivateProfileIntW(L"keyboard", L"tenkey_7", VK_NUMPAD7, filename);
cfg->vk_tenkey_8 = GetPrivateProfileIntW(L"keyboard", L"tenkey_8", VK_NUMPAD8, filename);
cfg->vk_tenkey_9 = GetPrivateProfileIntW(L"keyboard", L"tenkey_9", VK_NUMPAD9, filename);
cfg->vk_tenkey_clear = GetPrivateProfileIntW(L"keyboard", L"tenkey_clear", VK_DECIMAL, filename);
cfg->vk_tenkey_enter = GetPrivateProfileIntW(L"keyboard", L"tenkey_enter", VK_RETURN, filename);
cfg->vk_vol_down = GetPrivateProfileIntW(L"keyboard", L"vol_down", VK_NEXT, filename);
cfg->vk_vol_up = GetPrivateProfileIntW(L"keyboard", L"vol_up", VK_PRIOR, filename);
cfg->vk_terminal_decide = GetPrivateProfileIntW(L"keyboard", L"decide", 'A', filename);
cfg->vk_terminal_cancel = GetPrivateProfileIntW(L"keyboard", L"cancel", 'S', filename);
cfg->vk_terminal_up = GetPrivateProfileIntW(L"keyboard", L"up", VK_UP, filename);
cfg->vk_terminal_right = GetPrivateProfileIntW(L"keyboard", L"right", VK_RIGHT, filename);
cfg->vk_terminal_down = GetPrivateProfileIntW(L"keyboard", L"down", VK_DOWN, filename);
cfg->vk_terminal_left = GetPrivateProfileIntW(L"keyboard", L"left", VK_LEFT, filename);
cfg->vk_terminal_left_2 = GetPrivateProfileIntW(L"keyboard", L"left2", 'Q', filename);
cfg->vk_terminal_right_2 = GetPrivateProfileIntW(L"keyboard", L"right2", 'W', filename);
cfg->x_down = GetPrivateProfileIntW(L"keyboard", L"trackball_left", VK_LEFT, filename);
cfg->x_up = GetPrivateProfileIntW(L"keyboard", L"trackball_right", VK_RIGHT, filename);
cfg->y_down = GetPrivateProfileIntW(L"keyboard", L"trackball_up", VK_UP, filename);
cfg->y_up = GetPrivateProfileIntW(L"keyboard", L"trackball_down", VK_DOWN, filename);
cfg->speed = GetPrivateProfileIntW(L"keyboard", L"speed_modifier", 1, filename);
}
void sekito_io_config_load(
struct sekito_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);
cfg->vk_sw1 = GetPrivateProfileIntW(L"io4", L"sw1", '4', filename);
cfg->vk_sw2 = GetPrivateProfileIntW(L"io4", L"sw2", '5', filename);
GetPrivateProfileStringW(
L"io4",
L"mode",
L"keyboard",
cfg->mode,
_countof(cfg->mode),
filename);
sekito_kb_config_load(&cfg->kb, filename);
}

62
games/sekitoio/config.h Normal file
View File

@ -0,0 +1,62 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
struct sekito_kb_config {
uint8_t vk_menu;
uint8_t vk_start;
uint8_t vk_stratagem;
uint8_t vk_stratagem_lock;
uint8_t vk_hougu;
uint8_t vk_ryuuha;
uint8_t vk_tenkey_0;
uint8_t vk_tenkey_1;
uint8_t vk_tenkey_2;
uint8_t vk_tenkey_3;
uint8_t vk_tenkey_4;
uint8_t vk_tenkey_5;
uint8_t vk_tenkey_6;
uint8_t vk_tenkey_7;
uint8_t vk_tenkey_8;
uint8_t vk_tenkey_9;
uint8_t vk_tenkey_clear;
uint8_t vk_tenkey_enter;
uint8_t vk_vol_down;
uint8_t vk_vol_up;
uint8_t vk_terminal_up;
uint8_t vk_terminal_right;
uint8_t vk_terminal_down;
uint8_t vk_terminal_left;
uint8_t vk_terminal_left_2;
uint8_t vk_terminal_right_2;
uint8_t vk_terminal_cancel;
uint8_t vk_terminal_decide;
uint8_t x_down;
uint8_t x_up;
uint8_t y_down;
uint8_t y_up;
uint8_t speed;
};
struct sekito_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
uint8_t vk_sw1;
uint8_t vk_sw2;
wchar_t mode[12];
struct sekito_kb_config kb;
};
void sekito_kb_config_load(struct sekito_kb_config *cfg, const wchar_t *filename);
void sekito_io_config_load(
struct sekito_io_config *cfg,
const wchar_t *filename);

164
games/sekitoio/keyboard.c Normal file
View File

@ -0,0 +1,164 @@
#include <windows.h>
#include <math.h>
#include <assert.h>
#include <stdint.h>
#include <limits.h>
#include "sekitoio/backend.h"
#include "sekitoio/config.h"
#include "sekitoio/sekitoio.h"
#include "sekitoio/keyboard.h"
#include "util/dprintf.h"
static void sekito_kb_get_gamebtns(uint32_t* gamebtn_out);
static void sekito_kb_get_trackball(uint16_t* x, uint16_t* y);
static const struct sekito_io_backend sekito_kb_backend = {
.get_gamebtns = sekito_kb_get_gamebtns,
.get_trackball = sekito_kb_get_trackball
};
static uint16_t current_x;
static uint16_t current_y;
static struct sekito_kb_config config;
HRESULT sekito_kb_init(const struct sekito_kb_config* cfg, const struct sekito_io_backend** backend) {
assert(cfg != NULL);
assert(backend != NULL);
dprintf("Keyboard: Using keyboard input\n");
*backend = &sekito_kb_backend;
config = *cfg;
return S_OK;
}
static void sekito_kb_get_gamebtns(uint32_t* gamebtn_out) {
assert(gamebtn_out != NULL);
uint32_t gamebtn = 0;
if (GetAsyncKeyState(config.vk_hougu) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_HOUGU;
}
if (GetAsyncKeyState(config.vk_menu) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_MENU;
}
if (GetAsyncKeyState(config.vk_start) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_START;
}
if (GetAsyncKeyState(config.vk_stratagem) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_STRATAGEM;
}
if (GetAsyncKeyState(config.vk_stratagem_lock) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_STRATAGEM_LOCK;
}
if (GetAsyncKeyState(config.vk_tenkey_0) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_0;
}
if (GetAsyncKeyState(config.vk_tenkey_1) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_1;
}
if (GetAsyncKeyState(config.vk_tenkey_2) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_2;
}
if (GetAsyncKeyState(config.vk_tenkey_3) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_3;
}
if (GetAsyncKeyState(config.vk_tenkey_4) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_4;
}
if (GetAsyncKeyState(config.vk_tenkey_5) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_5;
}
if (GetAsyncKeyState(config.vk_tenkey_6) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_6;
}
if (GetAsyncKeyState(config.vk_tenkey_7) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_7;
}
if (GetAsyncKeyState(config.vk_tenkey_8) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_8;
}
if (GetAsyncKeyState(config.vk_tenkey_9) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_9;
}
if (GetAsyncKeyState(config.vk_tenkey_clear) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_CLEAR;
}
if (GetAsyncKeyState(config.vk_tenkey_enter) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_NUMPAD_ENTER;
}
if (GetAsyncKeyState(config.vk_terminal_cancel) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_TERMINAL_CANCEL;
}
if (GetAsyncKeyState(config.vk_terminal_decide) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_TERMINAL_DECIDE;
}
if (GetAsyncKeyState(config.vk_terminal_up) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_TERMINAL_UP;
}
if (GetAsyncKeyState(config.vk_terminal_right) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_TERMINAL_RIGHT;
}
if (GetAsyncKeyState(config.vk_terminal_down) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_TERMINAL_DOWN;
}
if (GetAsyncKeyState(config.vk_terminal_left) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_TERMINAL_LEFT;
}
if (GetAsyncKeyState(config.vk_terminal_left_2) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_TERMINAL_LEFT_2;
}
if (GetAsyncKeyState(config.vk_terminal_right_2) & 0x8000) {
gamebtn |= SEKITO_IO_GAMEBTN_TERMINAL_RIGHT_2;
}
*gamebtn_out = gamebtn;
}
static void sekito_kb_get_trackball(uint16_t* x, uint16_t* y) {
assert(x != NULL);
assert(y != NULL);
if (GetAsyncKeyState(config.x_down) & 0x8000) {
current_x -= config.speed;
} else if (GetAsyncKeyState(config.x_up) & 0x8000) {
current_x += config.speed;
}
if (GetAsyncKeyState(config.y_down) & 0x8000) {
current_y += config.speed;
} else if (GetAsyncKeyState(config.y_up) & 0x8000) {
current_y -= config.speed;
}
*x = current_x;
*y = current_y;
}

View File

@ -0,0 +1,8 @@
#pragma once
#include <windows.h>
#include "sekitoio/backend.h"
#include "sekitoio/config.h"
HRESULT sekito_kb_init(const struct sekito_kb_config *cfg, const struct sekito_io_backend **backend);

View File

@ -0,0 +1,18 @@
sekitoio_lib = static_library(
'sekitoio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
dependencies : [
xinput_lib,
],
sources : [
'config.c',
'config.h',
'backend.h',
'keyboard.c',
'keyboard.h',
'sekitoio.c',
'sekitoio.h',
],
)

108
games/sekitoio/sekitoio.c Normal file
View File

@ -0,0 +1,108 @@
#include <windows.h>
#include <xinput.h>
#include <math.h>
#include <stdint.h>
#include "sekitoio/sekitoio.h"
#include <assert.h>
#include "keyboard.h"
#include "sekitoio/config.h"
#include "util/dprintf.h"
#include "util/env.h"
#include "util/str.h"
static uint8_t sekito_opbtn;
static uint32_t sekito_gamebtn;
static uint8_t sekito_stick_x;
static uint8_t sekito_stick_y;
static struct sekito_io_config sekito_io_cfg;
static const struct sekito_io_backend* sekito_io_backend;
static bool sekito_io_coin;
uint16_t sekito_io_get_api_version(void) {
return 0x0100;
}
HRESULT sekito_io_init(void) {
sekito_io_config_load(&sekito_io_cfg, get_config_path());
HRESULT hr;
if (wstr_ieq(sekito_io_cfg.mode, L"keyboard")) {
hr = sekito_kb_init(&sekito_io_cfg.kb, &sekito_io_backend);
} else {
hr = E_INVALIDARG;
dprintf("Sekito IO: Invalid IO mode \"%S\", use keyboard\n",
sekito_io_cfg.mode);
}
return hr;
}
HRESULT sekito_io_poll(void) {
assert(sekito_io_backend != NULL);
sekito_opbtn = 0;
sekito_gamebtn = 0;
sekito_stick_x = 0;
sekito_stick_y = 0;
if (GetAsyncKeyState(sekito_io_cfg.vk_test) & 0x8000) {
sekito_opbtn |= SEKITO_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(sekito_io_cfg.vk_service) & 0x8000) {
sekito_opbtn |= SEKITO_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(sekito_io_cfg.vk_sw1) & 0x8000) {
sekito_opbtn |= SEKITO_IO_OPBTN_SW1;
}
if (GetAsyncKeyState(sekito_io_cfg.vk_sw2) & 0x8000) {
sekito_opbtn |= SEKITO_IO_OPBTN_SW2;
}
if (GetAsyncKeyState(sekito_io_cfg.vk_coin) & 0x8000) {
if (!sekito_io_coin) {
sekito_io_coin = true;
sekito_opbtn |= SEKITO_IO_OPBTN_COIN;
}
} else {
sekito_io_coin = false;
}
return S_OK;
}
void sekito_io_get_opbtns(uint8_t* opbtn) {
if (opbtn != NULL) {
*opbtn = sekito_opbtn;
}
}
void sekito_io_get_gamebtns(uint32_t* btn) {
assert(sekito_io_backend != NULL);
assert(btn != NULL);
sekito_io_backend->get_gamebtns(btn);
}
void sekito_io_get_trackball_position(uint16_t* stick_x, uint16_t* stick_y) {
assert(sekito_io_backend != NULL);
assert(stick_x != NULL);
assert(stick_y != NULL);
sekito_io_backend->get_trackball(stick_x, stick_y);
}
HRESULT sekito_io_led_init(void) {
return S_OK;
}
void sekito_io_led_set_colors(uint8_t board, uint8_t* rgb) {
return;
}

103
games/sekitoio/sekitoio.h Normal file
View File

@ -0,0 +1,103 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
SEKITO_IO_OPBTN_TEST = 0x01,
SEKITO_IO_OPBTN_SERVICE = 0x02,
SEKITO_IO_OPBTN_COIN = 0x04,
SEKITO_IO_OPBTN_SW1 = 0x08,
SEKITO_IO_OPBTN_SW2 = 0x10,
};
enum {
SEKITO_IO_GAMEBTN_MENU = 0x01,
SEKITO_IO_GAMEBTN_START = 0x02,
SEKITO_IO_GAMEBTN_STRATAGEM = 0x04,
SEKITO_IO_GAMEBTN_STRATAGEM_LOCK = 0x08,
SEKITO_IO_GAMEBTN_HOUGU = 0x10,
SEKITO_IO_GAMEBTN_NUMPAD_0 = 0x100,
SEKITO_IO_GAMEBTN_NUMPAD_1 = 0x200,
SEKITO_IO_GAMEBTN_NUMPAD_2 = 0x400,
SEKITO_IO_GAMEBTN_NUMPAD_3 = 0x800,
SEKITO_IO_GAMEBTN_NUMPAD_4 = 0x1000,
SEKITO_IO_GAMEBTN_NUMPAD_5 = 0x2000,
SEKITO_IO_GAMEBTN_NUMPAD_6 = 0x4000,
SEKITO_IO_GAMEBTN_NUMPAD_7 = 0x8000,
SEKITO_IO_GAMEBTN_NUMPAD_8 = 0x10000,
SEKITO_IO_GAMEBTN_NUMPAD_9 = 0x20000,
SEKITO_IO_GAMEBTN_NUMPAD_CLEAR = 0x40000,
SEKITO_IO_GAMEBTN_NUMPAD_ENTER = 0x80000,
SEKITO_IO_GAMEBTN_TERMINAL_LEFT = 0x400000,
SEKITO_IO_GAMEBTN_TERMINAL_UP = 0x800000,
SEKITO_IO_GAMEBTN_TERMINAL_RIGHT = 0x1000000,
SEKITO_IO_GAMEBTN_TERMINAL_DOWN = 0x2000000,
SEKITO_IO_GAMEBTN_TERMINAL_LEFT_2 = 0x4000000,
SEKITO_IO_GAMEBTN_TERMINAL_RIGHT_2 = 0x8000000,
SEKITO_IO_GAMEBTN_TERMINAL_DECIDE = 0x10000000,
SEKITO_IO_GAMEBTN_TERMINAL_CANCEL = 0x20000000,
};
/* Get the version of the Eiketsu Taisen 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 sekito_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after sekito_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT sekito_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 sekito_io_poll(void);
/* Get the state of the cabinet's operator buttons as of the last poll. See
SEKITO_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 sekito_io_get_opbtns(uint8_t *opbtn);
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
SEKITO_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 sekito_io_get_gamebtns(uint32_t *gamebtn);
/* Get the position of the trackball as of the last poll.
Minimum API version: 0x0100 */
void sekito_io_get_trackball_position(uint16_t *stick_x, uint16_t *stick_y);
/* Initialize LED emulation. This function will be called before any
other sekito_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 sekito_io_led_init(void);
/* Update the RGB LEDs.
Exact layout is TBD. */
void sekito_io_led_set_colors(uint8_t board, uint8_t *rgb);

View File

@ -127,6 +127,7 @@ subdir('games/tokyoio')
subdir('games/fgoio')
subdir('games/kemonoio')
subdir('games/apm3io')
subdir('games/sekitoio')
subdir('games/ektio')
subdir('games/chunihook')
@ -145,4 +146,4 @@ subdir('games/tokyohook')
subdir('games/fgohook')
subdir('games/kemonohook')
subdir('games/apm3hook')
subdir('games/ekthook')
subdir('games/sekitohook')