forked from Hay1tsme/segatools
merge upstream
This commit is contained in:
17
Package.mk
17
Package.mk
@ -240,6 +240,22 @@ $(BUILD_DIR_ZIP)/kemono.zip:
|
||||
for x in exe dll; do strip $(BUILD_DIR_ZIP)/kemono/*.$$x; done
|
||||
$(V)cd $(BUILD_DIR_ZIP)/kemono ; zip -r ../kemono.zip *
|
||||
|
||||
$(BUILD_DIR_ZIP)/apm3.zip:
|
||||
$(V)echo ... $@
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/apm3
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/apm3/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_GAMES_64)/apm3hook/apm3hook.dll \
|
||||
$(DIST_DIR)/apm3/segatools.ini \
|
||||
$(DIST_DIR)/apm3/launch.bat \
|
||||
$(DIST_DIR)/apm3/config_hook.json \
|
||||
$(BUILD_DIR_ZIP)/apm3
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
$(BUILD_DIR_ZIP)/apm3/DEVICE
|
||||
$(V)strip $(BUILD_DIR_ZIP)/apm3/*.{exe,dll}
|
||||
$(V)cd $(BUILD_DIR_ZIP)/apm3 ; zip -r ../apm3.zip *
|
||||
|
||||
$(BUILD_DIR_ZIP)/doc.zip: \
|
||||
$(DOC_DIR)/config \
|
||||
$(DOC_DIR)/chunihook.md \
|
||||
@ -265,6 +281,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
|
||||
$(BUILD_DIR_ZIP)/tokyo.zip \
|
||||
$(BUILD_DIR_ZIP)/fgo.zip \
|
||||
$(BUILD_DIR_ZIP)/kemono.zip \
|
||||
$(BUILD_DIR_ZIP)/apm3.zip \
|
||||
CHANGELOG.md \
|
||||
README.md \
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Segatools
|
||||
|
||||
Version: `2024-09-30`
|
||||
Version: `2025-07-20`
|
||||
|
||||
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
|
||||
|
||||
@ -32,6 +32,8 @@ Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platfo
|
||||
* starting from WACCA
|
||||
* Kemono Friends
|
||||
* Kemono Friends 3: Planet Tours
|
||||
* ALL.Net P-ras MULTI Version 3
|
||||
* starting from ALL.Net P-ras MULTI Version 3 1.01
|
||||
|
||||
## End-users
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
static void path_hook_init(void);
|
||||
static BOOL path_transform_a(char **out, const char *src);
|
||||
static BOOL path_transform_w(wchar_t **out, const wchar_t *src);
|
||||
|
||||
/* API hooks */
|
||||
|
||||
@ -490,7 +489,7 @@ end:
|
||||
return ok;
|
||||
}
|
||||
|
||||
static BOOL path_transform_w(wchar_t **out, const wchar_t *src)
|
||||
BOOL path_transform_w(wchar_t **out, const wchar_t *src)
|
||||
{
|
||||
BOOL ok;
|
||||
HRESULT hr;
|
||||
|
@ -13,6 +13,7 @@ typedef HRESULT (*path_hook_t)(
|
||||
HRESULT path_hook_push(path_hook_t hook);
|
||||
void path_hook_insert_hooks(HMODULE target);
|
||||
int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count);
|
||||
BOOL path_transform_w(wchar_t **out, const wchar_t *src);
|
||||
|
||||
static inline bool path_is_separator_w(wchar_t c)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@ platform_lib = static_library(
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
shlwapi_lib,
|
||||
bcrypt_lib,
|
||||
],
|
||||
sources : [
|
||||
'amvideo.c',
|
||||
@ -31,6 +32,8 @@ platform_lib = static_library(
|
||||
'pcbid.h',
|
||||
'platform.c',
|
||||
'platform.h',
|
||||
'security.c',
|
||||
'security.h',
|
||||
'vfs.c',
|
||||
'vfs.h',
|
||||
'system.c',
|
||||
|
210
common/platform/security.c
Normal file
210
common/platform/security.c
Normal file
@ -0,0 +1,210 @@
|
||||
#include <windows.h>
|
||||
#include <bcrypt.h>
|
||||
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000)
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "platform/security.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
static NTSTATUS __stdcall my_BCryptDecrypt(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags);
|
||||
|
||||
static NTSTATUS (__stdcall *next_BCryptDecrypt)(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags);
|
||||
|
||||
static NTSTATUS __stdcall my_BCryptEncrypt(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags);
|
||||
|
||||
static NTSTATUS (__stdcall *next_BCryptEncrypt)(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags);
|
||||
|
||||
static const struct hook_symbol security_hook_syms[] = {
|
||||
{
|
||||
.name = "BCryptDecrypt",
|
||||
.patch = my_BCryptDecrypt,
|
||||
.link = (void **) &next_BCryptDecrypt,
|
||||
},
|
||||
{
|
||||
.name = "BCryptEncrypt",
|
||||
.patch = my_BCryptEncrypt,
|
||||
.link = (void **) &next_BCryptEncrypt,
|
||||
}
|
||||
};
|
||||
|
||||
void LogKeyProperties(BCRYPT_KEY_HANDLE hKey) {
|
||||
NTSTATUS status;
|
||||
ULONG cbData;
|
||||
|
||||
// Retrieve the algorithm name
|
||||
WCHAR algorithmName[256];
|
||||
ULONG cbAlgorithmName = sizeof(algorithmName);
|
||||
|
||||
status = BCryptGetProperty(hKey, BCRYPT_ALGORITHM_NAME, (PUCHAR) algorithmName, cbAlgorithmName * sizeof(WCHAR),
|
||||
&cbData, 0);
|
||||
if (status == STATUS_SUCCESS) {
|
||||
dprintf("Algorithm: %ls\n", algorithmName);
|
||||
} else {
|
||||
dprintf("Algorithm: failed to retrieve algorithm name (0x%08lX)\n", status);
|
||||
}
|
||||
|
||||
// Export the key value
|
||||
DWORD keyBlobSize = 0;
|
||||
status = BCryptExportKey(hKey, NULL, BCRYPT_KEY_DATA_BLOB, NULL, 0, &keyBlobSize, 0);
|
||||
if (status != STATUS_SUCCESS) {
|
||||
dprintf("KEY: failed to determine key blob size (0x%08lX)\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
PUCHAR keyBlob = (PUCHAR) malloc(keyBlobSize);
|
||||
if (!keyBlob) {
|
||||
dprintf("KEY: failed the memory allocation for key blob.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
status = BCryptExportKey(hKey, NULL, BCRYPT_KEY_DATA_BLOB, keyBlob, keyBlobSize, &keyBlobSize, 0);
|
||||
if (status == STATUS_SUCCESS) {
|
||||
int headerSize = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER);
|
||||
dprintf("KEY (%lu bytes):\n", keyBlobSize - headerSize);
|
||||
dump(keyBlob + headerSize, keyBlobSize - headerSize);
|
||||
} else {
|
||||
dprintf("KEY: failed to export key value (0x%08lX)\n", status);
|
||||
}
|
||||
|
||||
free(keyBlob);
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS __stdcall my_BCryptDecrypt(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags) {
|
||||
dprintf("BCrypt: Decrypt\n");
|
||||
dprintf("FLAGS: %lx\n", dwFlags);
|
||||
LogKeyProperties(hKey);
|
||||
dprintf("IV:\n");
|
||||
dump(pbIV, cbIV);
|
||||
dprintf("ENCRYPTED:\n");
|
||||
dump(pbInput, cbInput);
|
||||
NTSTATUS ret = next_BCryptDecrypt(hKey, pbInput, cbInput, pPaddingInfo, pbIV, cbIV, pbOutput, cbOutput, pcbResult,
|
||||
dwFlags);
|
||||
dprintf("Operation Result: %lx\n", ret);
|
||||
if (ret == 0) {
|
||||
dprintf("Decrypted (%ld, max buf: %ld):\n", *pcbResult, cbOutput);
|
||||
if (pbOutput != NULL) {
|
||||
dump(pbOutput, *pcbResult);
|
||||
} else {
|
||||
dprintf("pbOutput == NULL!\n");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS __stdcall my_BCryptEncrypt(
|
||||
BCRYPT_KEY_HANDLE hKey,
|
||||
PUCHAR pbInput,
|
||||
ULONG cbInput,
|
||||
void* pPaddingInfo,
|
||||
PUCHAR pbIV,
|
||||
ULONG cbIV,
|
||||
PUCHAR pbOutput,
|
||||
ULONG cbOutput,
|
||||
ULONG* pcbResult,
|
||||
ULONG dwFlags) {
|
||||
dprintf("BCrypt: Encrypt\n");
|
||||
dprintf("FLAGS: %lx\n", dwFlags);
|
||||
LogKeyProperties(hKey);
|
||||
dprintf("IV:\n");
|
||||
dump(pbIV, cbIV);
|
||||
dprintf("Input data (%ld):\n", cbInput);
|
||||
dump(pbOutput, *pcbResult);
|
||||
NTSTATUS ret = next_BCryptEncrypt(hKey, pbInput, cbInput, pPaddingInfo, pbIV, cbIV, pbOutput, cbOutput, pcbResult,
|
||||
dwFlags);
|
||||
dprintf("Operation Result: %lx\n", ret);
|
||||
if (ret == 0) {
|
||||
dprintf("First 16 bytes of encrypted data (%ld, max buf: %ld):\n", *pcbResult, cbOutput);
|
||||
if (pbOutput != NULL) {
|
||||
dump(pbOutput, min(16, *pcbResult));
|
||||
} else {
|
||||
dprintf("pbOutput == NULL!\n");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
HRESULT security_hook_init() {
|
||||
security_hook_insert_hooks(NULL);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void security_hook_insert_hooks(HMODULE mod) {
|
||||
|
||||
char value[8];
|
||||
if (!GetEnvironmentVariableA("BCRYPT_DUMP_ENABLED", value, 8)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value[0] != '1') {
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("BCrypt: dumping enabled\n");
|
||||
|
||||
hook_table_apply(
|
||||
mod,
|
||||
"BCrypt.dll",
|
||||
security_hook_syms,
|
||||
_countof(security_hook_syms));
|
||||
}
|
9
common/platform/security.h
Normal file
9
common/platform/security.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
HRESULT security_hook_init();
|
||||
void security_hook_insert_hooks(HMODULE mod);
|
@ -31,6 +31,10 @@ static HRESULT vfs_path_hook_option(
|
||||
const wchar_t *src,
|
||||
wchar_t *dest,
|
||||
size_t *count);
|
||||
static HRESULT vfs_path_hook_apm(
|
||||
const wchar_t *src,
|
||||
wchar_t *dest,
|
||||
size_t *count);
|
||||
static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes);
|
||||
static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes);
|
||||
|
||||
@ -64,6 +68,9 @@ static const size_t vfs_w10home_len = _countof(vfs_w10home) - 1;
|
||||
static const wchar_t vfs_option[] = L"C:\\Mount\\Option";
|
||||
static const size_t vfs_option_len = _countof(vfs_option) - 1;
|
||||
|
||||
static const wchar_t vfs_apm3[] = L"C:\\Mount\\Apm";
|
||||
static const size_t vfs_apm3_len = _countof(vfs_apm3) - 1;
|
||||
|
||||
static const struct reg_hook_val vfs_reg_vals[] = {
|
||||
{
|
||||
.name = L"AMFS",
|
||||
@ -199,6 +206,11 @@ HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id)
|
||||
if (vfs_config.option[0] != L'\0') {
|
||||
hr = path_hook_push(vfs_path_hook_option);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
hr = path_hook_push(vfs_path_hook_apm);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
@ -514,6 +526,53 @@ static HRESULT vfs_path_hook_option(
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT vfs_path_hook_apm(
|
||||
const wchar_t *src,
|
||||
wchar_t *dest,
|
||||
size_t *count)
|
||||
{
|
||||
size_t required;
|
||||
size_t redir_len;
|
||||
size_t shift;
|
||||
|
||||
assert(src != NULL);
|
||||
assert(count != NULL);
|
||||
|
||||
/* Case-insensitive check to see if src starts with vfs_apm */
|
||||
|
||||
if (path_compare_w(src, vfs_apm3, vfs_apm3_len) != 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
/* Check if the character after vfs_nthome is a separator or the end of
|
||||
the string */
|
||||
|
||||
if (!path_is_separator_w(src[vfs_apm3_len]) &&
|
||||
src[vfs_apm3_len] != L'\0')
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
/* Cut off the matched <prefix>\, add the replaced prefix, count NUL */
|
||||
|
||||
shift = path_is_separator_w(src[vfs_apm3_len]) ? 1 : 0;
|
||||
redir_len = wcslen(vfs_config.option);
|
||||
required = wcslen(src) - vfs_apm3_len - shift + redir_len + 1;
|
||||
|
||||
if (dest != NULL) {
|
||||
if (required > *count) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
wcscpy_s(dest, *count, vfs_config.option);
|
||||
wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_apm3_len + shift);
|
||||
}
|
||||
|
||||
*count = required;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes)
|
||||
{
|
||||
return reg_hook_read_wstr(bytes, nbytes, L"E:\\");
|
||||
|
@ -34,7 +34,11 @@ static const wchar_t *target_modules[] = {
|
||||
L"C300FWDLusb.dll",
|
||||
L"apmled.dll",
|
||||
L"HKBSys_api.dll",
|
||||
L"amptw.dll"
|
||||
L"amptw.dll",
|
||||
L"apmmount.dll",
|
||||
L"abaasgs.dll",
|
||||
L"AVProVideo.dll",
|
||||
L"Audio360.dll",
|
||||
};
|
||||
|
||||
static const size_t target_modules_len = _countof(target_modules);
|
||||
|
10
dist/apm3/config_hook.json
vendored
Normal file
10
dist/apm3/config_hook.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"common": {
|
||||
"language": "english"
|
||||
},
|
||||
"aime": {
|
||||
"firmware_path": [
|
||||
"aime_firm\\update_15396_6728_94.bin"
|
||||
]
|
||||
}
|
||||
}
|
61
dist/apm3/launch.bat
vendored
Normal file
61
dist/apm3/launch.bat
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
@echo off
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
set PATH=%~dp0lib;%~dp0;X:\;%PATH%
|
||||
|
||||
rem remove the reboot flag and show the copyright screen
|
||||
|
||||
if exist %tmp%\APMv3SystemReboot (
|
||||
del %tmp%\APMv3SystemReboot
|
||||
)
|
||||
|
||||
if exist %tmp%\SequenceSetting.json (
|
||||
del %tmp%\SequenceSetting.json
|
||||
)
|
||||
|
||||
:BEGIN
|
||||
pushd %~dp0
|
||||
|
||||
qprocess amdaemon.exe > NUL
|
||||
IF %ERRORLEVEL% NEQ 0 start /min "AM Daemon" inject -d -k apm3hook.dll amdaemon.exe -c daemon_config\common.json daemon_config\server.json config_hook.json
|
||||
|
||||
inject -d -k apm3hook.dll APMV3System -screen-fullscreen 0 -screen-width 1920 -screen-height 1080 -popupWindow -logFile output_log.txt
|
||||
|
||||
if exist %tmp%\segaboot (
|
||||
del %tmp%\segaboot
|
||||
goto END
|
||||
)
|
||||
|
||||
if exist %tmp%\app (
|
||||
del %tmp%\app
|
||||
goto APP
|
||||
)
|
||||
|
||||
if exist %tmp%\apptest (
|
||||
del %tmp%\apptest
|
||||
goto APPTEST
|
||||
)
|
||||
|
||||
goto END
|
||||
|
||||
:APP
|
||||
|
||||
call W:\game.bat
|
||||
taskkill /f /im emoneyUI.exe > nul 2>&1
|
||||
goto BEGIN
|
||||
|
||||
:APPTEST
|
||||
call W:\gametest.bat
|
||||
goto BEGIN
|
||||
|
||||
:END
|
||||
|
||||
taskkill /f /im emoneyUI.exe > nul 2>&1
|
||||
taskkill /f /im amdaemon.exe > nul 2>&1
|
||||
|
||||
rundll32 apm3hook.dll,UnmountApmDrives
|
||||
|
||||
echo.
|
||||
echo Game processes have terminated
|
||||
pause
|
244
dist/apm3/segatools.ini
vendored
Normal file
244
dist/apm3/segatools.ini
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
; -----------------------------------------------------------------------------
|
||||
; 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
|
||||
|
||||
[unity]
|
||||
; Enable Unity hook. This will allow you to run custom .NET code before the game
|
||||
enable=1
|
||||
|
||||
; Path to a .NET DLL that should run before the game. Useful for loading
|
||||
; modding frameworks such as BepInEx.
|
||||
targetAssembly=
|
||||
|
||||
[mount]
|
||||
; Enables W: drive mapping instead of using .vhd files
|
||||
enable=1
|
||||
; Delays the mount operation by a slight bit, so the launch sound effect isn't cut off.
|
||||
delay=1
|
||||
|
||||
[epay]
|
||||
; Enables the Thinca emulation. This will allow you to enable E-Money on compatible servers.
|
||||
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=
|
||||
|
||||
[apm3io]
|
||||
; To use a custom APM3 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
|
||||
|
||||
; Button mappings for the 8 vewlix arcade stick buttons.
|
||||
;
|
||||
; :=**#*+-. :+#@@@@#*- -*#@@@@#+:
|
||||
; =%@@@@@@@@@#: -%@@@@@@@@@@%- -%@@@@@@@@@@%-
|
||||
; #@@@@@@@@@@@@@+ +@@@@@@@@@@@@@@+ +@@@@@@@@@@@@@@=
|
||||
; :::: *@@@@@ @@@@@- .@@@@@ @@@@@@. .@@@@@ @@@@@@
|
||||
; :+%@@@@@@%*: %@@@@@ [2] @@@@@+ :@@@@@ [3] @@@@@@: :@@@@@ [4] @@@@@@
|
||||
; #@@@@@@@@@@@@# +@@@@@ @@@@@: #@@@@ @@@@@# #@@@@ @@@@@*
|
||||
; #@@@@ @@@@@# *@@@@@@@@@@@@@= *@@@@@@@@@@@@* *@@@@@@@@@@@@*
|
||||
; @@@@@ [1] @@@@@@: -#@@@@@@@@@*. .+%@@@@@@%+. :*%@@@@@@%+.
|
||||
; @@@@@ @@@@@@. :=++=-: .::. .::.
|
||||
; =@@@@@@@@@@@@@@+
|
||||
; -%@@@@@@@@@@%- =*%@@@%#=. :+#%@@@%*- .=#@@@@@%*-
|
||||
; :+#%@@%#+- *@@@@@@@@@@@#. :#@@@@@@@@@@@+ +@@@@@@@@@@@%-
|
||||
; %@@@@@@@@@@@@@@. -@@@@@@@@@@@@@@% #@@@@@@@@@@@@@@-
|
||||
; .:::. +@@@@@ @@@@@# %@@@@@ @@@@@- -@@@@@@ @@@@%
|
||||
; =#@@@@@@@#- +@@@@@ [6] @@@@@# @@@@@@ [7] @@@@@= -@@@@@@ [8] @@@@@
|
||||
; =@@@@@@@@@@@@%. .@@@@@ @@@@@- =@@@@@ @@@@@. %@@@@@ @@@@=
|
||||
; +@@@@@ @@@@@. :%@@@@@@@@@@@@- =@@@@@@@@@@@@#. *@@@@@@@@@@@@=
|
||||
; @@@@@@ [5] @@@@@+ -*@@@@@@@#= =#@@@@@@%*: :+#@@@@@@*=
|
||||
; %@@@@@ @@@@@= .:. ..:. ..
|
||||
; :@@@@@@@@@@@@@@#
|
||||
; .#@@@@@@@@@@@+
|
||||
; .::.
|
||||
|
||||
|
||||
[keyboard]
|
||||
; START button (default: P)
|
||||
start=0x50
|
||||
; HOME button (default: O)
|
||||
home=0x4F
|
||||
|
||||
; Buttons for the fight stick (default: arrows)
|
||||
up=0x26
|
||||
right=0x27
|
||||
down=0x28
|
||||
left=0x25
|
||||
|
||||
; Defaults:
|
||||
; Q W E R
|
||||
; A S D F
|
||||
button1=0x51
|
||||
button2=0x57
|
||||
button3=0x45
|
||||
button4=0x52
|
||||
button5=0x41
|
||||
button6=0x53
|
||||
button7=0x44
|
||||
button8=0x46
|
||||
|
||||
[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
|
||||
|
||||
button1=1
|
||||
button2=4
|
||||
button3=6
|
||||
button4=5
|
||||
button5=2
|
||||
button6=3
|
||||
button7=8
|
||||
button8=7
|
6
dist/carol/segatools.ini
vendored
6
dist/carol/segatools.ini
vendored
@ -23,6 +23,12 @@ appdata=
|
||||
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
|
||||
; -----------------------------------------------------------------------------
|
||||
|
6
dist/chuni/segatools.ini
vendored
6
dist/chuni/segatools.ini
vendored
@ -22,6 +22,12 @@ appdata=
|
||||
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
|
||||
; -----------------------------------------------------------------------------
|
||||
|
6
dist/chusan/segatools.ini
vendored
6
dist/chusan/segatools.ini
vendored
@ -24,6 +24,12 @@ aimePath=DEVICE\aime.txt
|
||||
; Enable high baud rate.
|
||||
;highBaud=1
|
||||
|
||||
; 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.
|
||||
|
6
dist/cm/segatools.ini
vendored
6
dist/cm/segatools.ini
vendored
@ -22,6 +22,12 @@ appdata=
|
||||
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.
|
||||
|
6
dist/cxb/segatools.ini
vendored
6
dist/cxb/segatools.ini
vendored
@ -24,6 +24,12 @@ enable=1
|
||||
; will load the file from "resource\DEVICE\aime.txt".
|
||||
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
|
||||
|
||||
[led]
|
||||
; Emulation for the LED board. Currently it's just dummy responses,
|
||||
; but if somebody wants to make their keyboard or whatever light
|
||||
|
6
dist/diva/segatools.ini
vendored
6
dist/diva/segatools.ini
vendored
@ -22,6 +22,12 @@ appdata=
|
||||
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
|
||||
; -----------------------------------------------------------------------------
|
||||
|
6
dist/fgo/segatools.ini
vendored
6
dist/fgo/segatools.ini
vendored
@ -22,6 +22,12 @@ appdata=
|
||||
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.
|
||||
|
6
dist/idac/segatools.ini
vendored
6
dist/idac/segatools.ini
vendored
@ -21,6 +21,12 @@ appdata=
|
||||
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
|
||||
; -----------------------------------------------------------------------------
|
||||
|
5
dist/idz/launch.bat
vendored
5
dist/idz/launch.bat
vendored
@ -6,9 +6,10 @@ start "AM Daemon" /min inject -d -k idzhook.dll amdaemon.exe -c configDHCP_Final
|
||||
|
||||
rem Set dipsw1=0 and uncomment the ServerBox for in store battle?
|
||||
rem inject -k idzhook.dll ServerBoxD8_Nu_x64.exe
|
||||
inject -d -k idzhook.dll InitialD0_DX11_Nu.exe
|
||||
inject -d -k idzhook.dll InitialD0_DX11_Nu.exe -m
|
||||
|
||||
taskkill /im ServerBoxD8_Nu_x64.exe > nul 2>&1
|
||||
taskkill /f /im ServerBoxD8_Nu_x64.exe > nul 2>&1
|
||||
taskkill /f /im amdaemon.exe > nul 2>&1
|
||||
|
||||
echo.
|
||||
echo Game processes have terminated
|
||||
|
12
dist/idz/segatools.ini
vendored
12
dist/idz/segatools.ini
vendored
@ -24,6 +24,12 @@ aimePath=DEVICE\aime.txt
|
||||
felicaGen=0
|
||||
aimeGen=1
|
||||
|
||||
; 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
|
||||
; -----------------------------------------------------------------------------
|
||||
@ -55,8 +61,10 @@ 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.158.0
|
||||
; that subnet must start with 192.168. The official keychip subnet is actually
|
||||
; 192.168.158.0, but most dumps use 192.168.100.0 instead.
|
||||
;subnet=192.168.158.0
|
||||
subnet=192.168.100.0
|
||||
|
||||
[pcbid]
|
||||
; Set the Windows host name. This should be an ALLS MAIN ID, without the
|
||||
|
6
dist/kemono/segatools.ini
vendored
6
dist/kemono/segatools.ini
vendored
@ -22,6 +22,12 @@ appdata=
|
||||
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 (currently just stubbed). Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
|
6
dist/mai2/segatools.ini
vendored
6
dist/mai2/segatools.ini
vendored
@ -22,6 +22,12 @@ appdata=
|
||||
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.
|
||||
|
6
dist/mercury/segatools.ini
vendored
6
dist/mercury/segatools.ini
vendored
@ -22,6 +22,12 @@ appdata=
|
||||
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.
|
||||
|
6
dist/mu3/segatools.ini
vendored
6
dist/mu3/segatools.ini
vendored
@ -22,6 +22,12 @@ appdata=
|
||||
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.
|
||||
|
6
dist/swdc/segatools.ini
vendored
6
dist/swdc/segatools.ini
vendored
@ -22,6 +22,12 @@ appdata=appdata
|
||||
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.
|
||||
|
115
games/apm3hook/apm3-dll.c
Normal file
115
games/apm3hook/apm3-dll.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "apm3hook/apm3-dll.h"
|
||||
|
||||
#include "util/dll-bind.h"
|
||||
#include "util/dprintf.h"
|
||||
|
||||
const struct dll_bind_sym apm3_dll_syms[] = {
|
||||
{
|
||||
.sym = "apm3_io_init",
|
||||
.off = offsetof(struct apm3_dll, init),
|
||||
}, {
|
||||
.sym = "apm3_io_poll",
|
||||
.off = offsetof(struct apm3_dll, poll),
|
||||
}, {
|
||||
.sym = "apm3_io_get_opbtns",
|
||||
.off = offsetof(struct apm3_dll, get_opbtns),
|
||||
}, {
|
||||
.sym = "apm3_io_get_gamebtns",
|
||||
.off = offsetof(struct apm3_dll, get_gamebtns),
|
||||
}, {
|
||||
.sym = "apm3_io_led_init",
|
||||
.off = offsetof(struct apm3_dll, led_init),
|
||||
}, {
|
||||
.sym = "apm3_io_led_set_colors",
|
||||
.off = offsetof(struct apm3_dll, led_set_leds),
|
||||
}
|
||||
};
|
||||
|
||||
struct apm3_dll apm3_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 apm3_dll_init(const struct apm3_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("APM IO: Failed to load IO DLL: %lx: %S\n",
|
||||
hr,
|
||||
cfg->path);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
dprintf("APM IO: Using custom IO DLL: %S\n", cfg->path);
|
||||
src = owned;
|
||||
} else {
|
||||
owned = NULL;
|
||||
src = self;
|
||||
}
|
||||
|
||||
get_api_version = (void *) GetProcAddress(src, "apm3_io_get_api_version");
|
||||
|
||||
if (get_api_version != NULL) {
|
||||
apm3_dll.api_version = get_api_version();
|
||||
} else {
|
||||
apm3_dll.api_version = 0x0100;
|
||||
dprintf("Custom IO DLL does not expose apm3_io_get_api_version, "
|
||||
"assuming API version 1.0.\n"
|
||||
"Please ask the developer to update their DLL.\n");
|
||||
}
|
||||
|
||||
if (apm3_dll.api_version >= 0x0200) {
|
||||
hr = E_NOTIMPL;
|
||||
dprintf("APM IO: Custom IO DLL implements an unsupported "
|
||||
"API version (%#04x). Please update Segatools.\n",
|
||||
apm3_dll.api_version);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
sym = apm3_dll_syms;
|
||||
hr = dll_bind(&apm3_dll, src, &sym, _countof(apm3_dll_syms));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (src != self) {
|
||||
dprintf("APM 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
games/apm3hook/apm3-dll.h
Normal file
23
games/apm3hook/apm3-dll.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "apm3io/apm3io.h"
|
||||
|
||||
struct apm3_dll {
|
||||
uint16_t api_version;
|
||||
HRESULT (*init)(void);
|
||||
HRESULT (*poll)(void);
|
||||
void (*get_opbtns)(uint8_t *opbtn);
|
||||
void (*get_gamebtns)(uint32_t *gamebtn);
|
||||
HRESULT (*led_init)(void);
|
||||
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
|
||||
};
|
||||
|
||||
struct apm3_dll_config {
|
||||
wchar_t path[MAX_PATH];
|
||||
};
|
||||
|
||||
extern struct apm3_dll apm3_dll;
|
||||
|
||||
HRESULT apm3_dll_init(const struct apm3_dll_config *cfg, HINSTANCE self);
|
21
games/apm3hook/apm3hook.def
Normal file
21
games/apm3hook/apm3hook.def
Normal file
@ -0,0 +1,21 @@
|
||||
LIBRARY apm3hook
|
||||
|
||||
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
|
||||
apm3_io_get_api_version
|
||||
apm3_io_get_gamebtns
|
||||
apm3_io_get_opbtns
|
||||
apm3_io_init
|
||||
apm3_io_poll
|
||||
apm3_io_led_init
|
||||
apm3_io_led_set_colors
|
||||
UnmountApmDrives
|
113
games/apm3hook/config.c
Normal file
113
games/apm3hook/config.c
Normal file
@ -0,0 +1,113 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/config.h"
|
||||
|
||||
#include "hooklib/config.h"
|
||||
#include "hooklib/dvd.h"
|
||||
|
||||
#include "apm3hook/config.h"
|
||||
|
||||
#include "platform/config.h"
|
||||
|
||||
void apm3_dll_config_load(
|
||||
struct apm3_dll_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"apm3io",
|
||||
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"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 mount_config_load(struct mount_config *cfg, const wchar_t *filename) {
|
||||
cfg->enable = GetPrivateProfileIntW(L"mount", L"enable", 1, filename);
|
||||
cfg->delay = GetPrivateProfileIntW(L"mount", L"delay", 1, filename);
|
||||
}
|
||||
|
||||
void apm3_hook_config_load(
|
||||
struct apm3_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);
|
||||
dvd_config_load(&cfg->dvd, filename);
|
||||
io4_config_load(&cfg->io4, filename);
|
||||
vfd_config_load(&cfg->vfd, filename);
|
||||
touch_screen_config_load(&cfg->touch, filename);
|
||||
led15093_config_load(&cfg->led15093, filename);
|
||||
apm3_dll_config_load(&cfg->dll, filename);
|
||||
unity_config_load(&cfg->unity, filename);
|
||||
mount_config_load(&cfg->mount, filename);
|
||||
}
|
43
games/apm3hook/config.h
Normal file
43
games/apm3hook/config.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/led15093.h"
|
||||
|
||||
#include "hooklib/dvd.h"
|
||||
#include "hooklib/touch.h"
|
||||
#include "hooklib/printer.h"
|
||||
|
||||
#include "gfxhook/config.h"
|
||||
|
||||
#include "apm3hook/apm3-dll.h"
|
||||
|
||||
#include "platform/config.h"
|
||||
#include "unityhook/config.h"
|
||||
|
||||
struct mount_config {
|
||||
bool enable;
|
||||
bool delay;
|
||||
};
|
||||
|
||||
struct apm3_hook_config {
|
||||
struct platform_config platform;
|
||||
struct aime_config aime;
|
||||
struct dvd_config dvd;
|
||||
struct io4_config io4;
|
||||
struct vfd_config vfd;
|
||||
struct touch_screen_config touch;
|
||||
struct led15093_config led15093;
|
||||
struct apm3_dll_config dll;
|
||||
struct unity_config unity;
|
||||
struct mount_config mount;
|
||||
};
|
||||
|
||||
void apm3_dll_config_load(
|
||||
struct apm3_dll_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
||||
void apm3_hook_config_load(
|
||||
struct apm3_hook_config *cfg,
|
||||
const wchar_t *filename);
|
170
games/apm3hook/dllmain.c
Normal file
170
games/apm3hook/dllmain.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
"ALL.Net P-ras MULTI Version 3" (apm3) hook
|
||||
|
||||
Devices
|
||||
|
||||
USB: 837-15257 "Type 4" I/O Board
|
||||
COM1: 200-6275 VFD GP1232A02A FUTABA Board
|
||||
COM2: 837-15093-06 LED Controller Board
|
||||
COM3: 837-15396 "Gen 3" Aime Reader
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "io4.h"
|
||||
#include "mount.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
#include "hook/process.h"
|
||||
|
||||
#include "hooklib/serial.h"
|
||||
#include "hooklib/spike.h"
|
||||
|
||||
#include "platform/clock.h"
|
||||
#include "platform/config.h"
|
||||
#include "platform/nusec.h"
|
||||
#include "platform/security.h"
|
||||
|
||||
#include "unityhook/hook.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/env.h"
|
||||
|
||||
static HMODULE apm3_hook_mod;
|
||||
static process_entry_t apm3_startup;
|
||||
static struct apm3_hook_config apm3_hook_cfg;
|
||||
|
||||
static const wchar_t *target_modules[] = {
|
||||
L"apmled.dll",
|
||||
};
|
||||
|
||||
static const size_t target_modules_len = _countof(target_modules);
|
||||
|
||||
void unity_hook_callback(HMODULE hmodule, const wchar_t* p) {
|
||||
dprintf("Unity: Hook callback: %ls\n", p);
|
||||
|
||||
for (size_t i = 0; i < target_modules_len; i++) {
|
||||
if (_wcsicmp(p, target_modules[i]) == 0) {
|
||||
serial_hook_apply_hooks(hmodule);
|
||||
iohook_apply_hooks(hmodule);
|
||||
}
|
||||
}
|
||||
security_hook_insert_hooks(hmodule);
|
||||
touch_hook_insert_hooks(hmodule);
|
||||
// mount_hook_apply_hooks(&apm3_hook_cfg.mount, hmodule);
|
||||
}
|
||||
|
||||
static DWORD CALLBACK apm3_pre_startup(void)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("--- Begin %s ---\n", __func__);
|
||||
|
||||
/* Load config */
|
||||
|
||||
apm3_hook_config_load(&apm3_hook_cfg, get_config_path());
|
||||
|
||||
/* Hook Win32 APIs */
|
||||
|
||||
dvd_hook_init(&apm3_hook_cfg.dvd, apm3_hook_mod);
|
||||
touch_screen_hook_init(&apm3_hook_cfg.touch, apm3_hook_mod);
|
||||
serial_hook_init();
|
||||
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
hr = platform_hook_init(
|
||||
&apm3_hook_cfg.platform,
|
||||
"SDEM",
|
||||
"ACA1",
|
||||
apm3_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&apm3_hook_cfg.aime, 3, 3, apm3_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = vfd_hook_init(&apm3_hook_cfg.vfd, 1);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = apm3_dll_init(&apm3_hook_cfg.dll, apm3_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = apm3_io4_hook_init(&apm3_hook_cfg.io4);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
unsigned int led_port_no[2] = {2, 0};
|
||||
hr = led15093_hook_init(&apm3_hook_cfg.led15093,
|
||||
apm3_dll.led_init, apm3_dll.led_set_leds, led_port_no);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = security_hook_init();
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
touch_screen_hook_init(&apm3_hook_cfg.touch, apm3_hook_mod);
|
||||
|
||||
mount_hook_init(&apm3_hook_cfg.platform.vfs, &apm3_hook_cfg.mount);
|
||||
|
||||
security_hook_insert_hooks(NULL);
|
||||
|
||||
mount_hook_apply_hooks(NULL);
|
||||
|
||||
unity_hook_init(&apm3_hook_cfg.unity, apm3_hook_mod, unity_hook_callback);
|
||||
|
||||
/* Initialize debug helpers */
|
||||
|
||||
spike_hook_init(get_config_path());
|
||||
|
||||
dprintf("--- End %s ---\n", __func__);
|
||||
|
||||
return apm3_startup();
|
||||
|
||||
fail:
|
||||
ExitProcess(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (cause != DLL_PROCESS_ATTACH) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
wchar_t szFileName[MAX_PATH];
|
||||
GetModuleFileNameW(NULL, szFileName, MAX_PATH);
|
||||
if (wcsstr(szFileName, L"rundll32") != NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
apm3_hook_mod = mod;
|
||||
hr = process_hijack_startup(apm3_pre_startup, &apm3_startup);
|
||||
|
||||
if (!SUCCEEDED(hr)) {
|
||||
dprintf("Failed to hijack process startup: %x\n", (int) hr);
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
129
games/apm3hook/io4.c
Normal file
129
games/apm3hook/io4.c
Normal file
@ -0,0 +1,129 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
|
||||
#include "apm3hook/apm3-dll.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT apm3_io4_poll(void *ctx, struct io4_state *state);
|
||||
static uint16_t coins;
|
||||
|
||||
static const struct io4_ops apm3_io4_ops = {
|
||||
.poll = apm3_io4_poll,
|
||||
};
|
||||
|
||||
HRESULT apm3_io4_hook_init(const struct io4_config *cfg)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(apm3_dll.init != NULL);
|
||||
|
||||
hr = io4_hook_init(cfg, &apm3_io4_ops, NULL);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return apm3_dll.init();
|
||||
}
|
||||
|
||||
static HRESULT apm3_io4_poll(void *ctx, struct io4_state *state)
|
||||
{
|
||||
uint8_t opbtn;
|
||||
uint32_t gamebtn;
|
||||
HRESULT hr;
|
||||
|
||||
assert(apm3_dll.poll != NULL);
|
||||
assert(apm3_dll.get_opbtns != NULL);
|
||||
assert(apm3_dll.get_gamebtns != NULL);
|
||||
|
||||
memset(state, 0, sizeof(*state));
|
||||
|
||||
hr = apm3_dll.poll();
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
opbtn = 0;
|
||||
gamebtn = 0;
|
||||
|
||||
apm3_dll.get_opbtns(&opbtn);
|
||||
apm3_dll.get_gamebtns(&gamebtn);
|
||||
|
||||
if (opbtn & APM3_IO_OPBTN_TEST) {
|
||||
state->buttons[0] |= IO4_BUTTON_TEST;
|
||||
}
|
||||
|
||||
if (opbtn & APM3_IO_OPBTN_SERVICE) {
|
||||
state->buttons[0] |= IO4_BUTTON_SERVICE;
|
||||
}
|
||||
|
||||
if (opbtn & APM3_IO_OPBTN_COIN) {
|
||||
coins++;
|
||||
}
|
||||
state->chutes[0] = coins << 8;
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_HOME) {
|
||||
state->buttons[1] |= 1 << 12;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_START) {
|
||||
state->buttons[0] |= 1 << 7;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_UP) {
|
||||
state->buttons[0] |= 1 << 5;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_RIGHT) {
|
||||
state->buttons[0] |= 1 << 2;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_DOWN) {
|
||||
state->buttons[0] |= 1 << 4;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_LEFT) {
|
||||
state->buttons[0] |= 1 << 3;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_B1) {
|
||||
state->buttons[0] |= 1 << 1;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_B2) {
|
||||
state->buttons[0] |= 1 << 0;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_B3) {
|
||||
state->buttons[0] |= 1 << 15;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_B4) {
|
||||
state->buttons[0] |= 1 << 14;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_B5) {
|
||||
state->buttons[0] |= 1 << 13;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_B6) {
|
||||
state->buttons[0] |= 1 << 12;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_B7) {
|
||||
state->buttons[0] |= 1 << 11;
|
||||
}
|
||||
|
||||
if (gamebtn & APM3_IO_GAMEBTN_B8) {
|
||||
state->buttons[0] |= 1 << 10;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
7
games/apm3hook/io4.h
Normal file
7
games/apm3hook/io4.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
|
||||
HRESULT apm3_io4_hook_init(const struct io4_config *cfg);
|
32
games/apm3hook/meson.build
Normal file
32
games/apm3hook/meson.build
Normal file
@ -0,0 +1,32 @@
|
||||
shared_library(
|
||||
'apm3hook',
|
||||
name_prefix : '',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
vs_module_defs : 'apm3hook.def',
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
capnhook.get_variable('hooklib_dep'),
|
||||
],
|
||||
link_with : [
|
||||
aimeio_lib,
|
||||
apm3io_lib,
|
||||
board_lib,
|
||||
gfxhook_lib,
|
||||
hooklib_lib,
|
||||
platform_lib,
|
||||
unityhook_lib,
|
||||
util_lib,
|
||||
],
|
||||
sources : [
|
||||
'apm3-dll.c',
|
||||
'apm3-dll.h',
|
||||
'config.c',
|
||||
'config.h',
|
||||
'dllmain.c',
|
||||
'io4.c',
|
||||
'io4.h',
|
||||
'mount.c',
|
||||
'mount.h',
|
||||
],
|
||||
)
|
174
games/apm3hook/mount.c
Normal file
174
games/apm3hook/mount.c
Normal file
@ -0,0 +1,174 @@
|
||||
#include "board/aime-dll.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "mount.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <pathcch.h>
|
||||
#include <shlwapi.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hooklib/path.h"
|
||||
#include "hook/procaddr.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
/* API hooks */
|
||||
|
||||
static bool hook_ApmSys_mountVhd(const wchar_t* vhdOriginalPath, const wchar_t* vhdPatchPath, char mountDriveLetter);
|
||||
static bool hook_ApmSys_mountFscrypt(const wchar_t* fscryptImagePath, const wchar_t* mountFolderPath, const wchar_t* subGameId);
|
||||
static bool hook_ApmSys_unmountVhd(char mountDriveLetter);
|
||||
static bool hook_ApmSys_unmountFscrypt(const wchar_t* mountFolderPath);
|
||||
|
||||
static const struct hook_symbol mount_hooks[] = {
|
||||
{
|
||||
.name = "ApmSys_mountVhd",
|
||||
.patch = hook_ApmSys_mountVhd,
|
||||
.ordinal = 2,
|
||||
},
|
||||
{
|
||||
.name = "ApmSys_unmountVhd",
|
||||
.patch = hook_ApmSys_unmountVhd,
|
||||
.ordinal = 4,
|
||||
},
|
||||
{
|
||||
.name = "ApmSys_mountFscrypt",
|
||||
.patch = hook_ApmSys_mountFscrypt,
|
||||
.ordinal = 1,
|
||||
},
|
||||
{
|
||||
.name = "ApmSys_unmountFscrypt",
|
||||
.patch = hook_ApmSys_unmountFscrypt,
|
||||
.ordinal = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static struct vfs_config* vcfg;
|
||||
static struct mount_config* mcfg;
|
||||
|
||||
static wchar_t game_directory[MAX_PATH];
|
||||
|
||||
void mount_hook_init(struct vfs_config* vfs_cfg, struct mount_config* mount_cfg) {
|
||||
|
||||
assert(vfs_cfg != NULL);
|
||||
assert(mount_cfg != NULL);
|
||||
|
||||
vcfg = vfs_cfg;
|
||||
mcfg = mount_cfg;
|
||||
|
||||
if (!mcfg->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("Mount: Hook enabled\n");
|
||||
}
|
||||
|
||||
void mount_hook_apply_hooks(HMODULE module) {
|
||||
if (!mcfg->enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
proc_addr_table_push(module, "apmmount.dll", mount_hooks, _countof(mount_hooks));
|
||||
}
|
||||
|
||||
|
||||
bool hook_ApmSys_mountFscrypt(const wchar_t* fscryptImagePath, const wchar_t* mountFolderPath, const wchar_t* subGameId) {
|
||||
dprintf("Mount: Mount FsCrypt (%ls, %ls, %ls)\n", fscryptImagePath, mountFolderPath, subGameId);
|
||||
|
||||
wcscpy_s(game_directory, MAX_PATH, fscryptImagePath);
|
||||
PathCchRemoveFileSpec(game_directory, MAX_PATH);
|
||||
wnsprintfW(game_directory, MAX_PATH, L"%s\\App", game_directory);
|
||||
|
||||
wchar_t *trans;
|
||||
BOOL ok;
|
||||
|
||||
ok = path_transform_w(&trans, game_directory);
|
||||
|
||||
if (!ok) {
|
||||
dprintf("Mount: Path transformation error\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (trans != NULL) {
|
||||
wcscpy_s(game_directory, MAX_PATH, trans);
|
||||
}
|
||||
|
||||
free(trans);
|
||||
|
||||
dprintf("Mount: Target Path: %ls\n", game_directory);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool hook_ApmSys_mountVhd(const wchar_t* vhdOriginalPath, const wchar_t* vhdPatchPath, char mountDriveLetter) {
|
||||
dprintf("Mount: Mount VHD (%ls, %ls, %c)\n", vhdOriginalPath, vhdPatchPath, mountDriveLetter);
|
||||
|
||||
if (game_directory[0] == '\0') {
|
||||
dprintf("Mount: Target directory is unset!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Game drive
|
||||
wchar_t device[3];
|
||||
device[0] = mountDriveLetter;
|
||||
device[1] = ':';
|
||||
device[2] = '\0';
|
||||
|
||||
dprintf("Mount: Mapping %ls to %ls\n", device, game_directory);
|
||||
if (!DefineDosDeviceW(0, device, game_directory)) {
|
||||
dprintf("DefineDosDevice failed: %lx\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// APM root drive
|
||||
device[0] = 'X';
|
||||
|
||||
wchar_t self_directory[MAX_PATH];
|
||||
GetCurrentDirectoryW(MAX_PATH, self_directory);
|
||||
|
||||
dprintf("Mount: Mapping %ls to %ls\n", device, self_directory);
|
||||
if (!DefineDosDeviceW(0, device, self_directory)) {
|
||||
dprintf("DefineDosDevice failed: %lx\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mcfg->delay) {
|
||||
Sleep(1500);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
bool hook_ApmSys_unmountVhd(char mountDriveLetter) {
|
||||
dprintf("Mount: Unmount VHD (%c)\n", mountDriveLetter);
|
||||
|
||||
char device[3];
|
||||
device[0] = mountDriveLetter;
|
||||
device[1] = ':';
|
||||
device[2] = '\0';
|
||||
|
||||
// Game drive
|
||||
if (!DefineDosDevice(DDD_REMOVE_DEFINITION, device, NULL)) {
|
||||
dprintf("DefineDosDevice failed: %lx\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// APM root drive
|
||||
device[0] = 'X';
|
||||
if (!DefineDosDevice(DDD_REMOVE_DEFINITION, device, NULL)) {
|
||||
dprintf("DefineDosDevice failed: %lx\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CALLBACK UnmountApmDrives(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) {
|
||||
hook_ApmSys_unmountVhd('W');
|
||||
hook_ApmSys_unmountVhd('X');
|
||||
}
|
||||
|
||||
bool hook_ApmSys_unmountFscrypt(const wchar_t* mountFolderPath) {
|
||||
dprintf("Mount: Unmount FsCrypt (%ls)\n", mountFolderPath);
|
||||
memset(game_directory, 0, MAX_PATH * sizeof(wchar_t));
|
||||
return 0;
|
||||
}
|
7
games/apm3hook/mount.h
Normal file
7
games/apm3hook/mount.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/aime-dll.h"
|
||||
#include "config.h"
|
||||
|
||||
void mount_hook_apply_hooks(HMODULE module);
|
||||
void mount_hook_init(struct vfs_config* vfs_cfg, struct mount_config* mount_cfg);
|
85
games/apm3io/apm3io.h
Normal file
85
games/apm3io/apm3io.h
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
APM3_IO_OPBTN_TEST = 0x01,
|
||||
APM3_IO_OPBTN_SERVICE = 0x02,
|
||||
APM3_IO_OPBTN_COIN = 0x04,
|
||||
};
|
||||
|
||||
enum {
|
||||
APM3_IO_GAMEBTN_HOME = 0x01,
|
||||
APM3_IO_GAMEBTN_START = 0x02,
|
||||
APM3_IO_GAMEBTN_UP = 0x04,
|
||||
APM3_IO_GAMEBTN_RIGHT = 0x08,
|
||||
APM3_IO_GAMEBTN_DOWN = 0x10,
|
||||
APM3_IO_GAMEBTN_LEFT = 0x20,
|
||||
|
||||
APM3_IO_GAMEBTN_B1 = 0x40,
|
||||
APM3_IO_GAMEBTN_B2 = 0x80,
|
||||
APM3_IO_GAMEBTN_B3 = 0x100,
|
||||
APM3_IO_GAMEBTN_B4 = 0x200,
|
||||
APM3_IO_GAMEBTN_B5 = 0x400,
|
||||
APM3_IO_GAMEBTN_B6 = 0x800,
|
||||
APM3_IO_GAMEBTN_B7 = 0x1000,
|
||||
APM3_IO_GAMEBTN_B8 = 0x2000,
|
||||
};
|
||||
|
||||
/* Get the version of the APMv3 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 apm3_io_get_api_version(void);
|
||||
|
||||
/* Initialize the IO DLL. This is the second function that will be called on
|
||||
your DLL, after apm3_io_get_api_version.
|
||||
|
||||
All subsequent calls to this API may originate from arbitrary threads.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
HRESULT apm3_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 apm3_io_poll(void);
|
||||
|
||||
/* Get the state of the cabinet's operator buttons as of the last poll. See
|
||||
APM3_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 apm3_io_get_opbtns(uint8_t *opbtn);
|
||||
|
||||
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
|
||||
APM3_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 apm3_io_get_gamebtns(uint16_t *gamebtn);
|
||||
|
||||
/* Initialize LED emulation. This function will be called before any
|
||||
other apm3_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 apm3_io_led_init(void);
|
||||
|
||||
/* Update the RGB LEDs.
|
||||
|
||||
Exact layout is TBD. */
|
||||
|
||||
void apm3_io_led_set_colors(uint8_t board, uint8_t *rgb);
|
10
games/apm3io/backend.h
Normal file
10
games/apm3io/backend.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "apm3io/apm3io.h"
|
||||
|
||||
struct apm3_io_backend {
|
||||
void (*get_opbtns)(uint8_t *opbtn);
|
||||
void (*get_gamebtns)(uint16_t *gamebtn);
|
||||
};
|
94
games/apm3io/config.c
Normal file
94
games/apm3io/config.c
Normal file
@ -0,0 +1,94 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "apm3io/config.h"
|
||||
|
||||
const int BUTTON_DEFAULTS[] = {'Q','W','E','R','A','S','D','F'};
|
||||
|
||||
void apm3_kb_config_load(
|
||||
struct apm3_kb_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->vk_start = GetPrivateProfileIntW(L"keyboard", L"start", 'P', filename);
|
||||
cfg->vk_home = GetPrivateProfileIntW(L"keyboard", L"home", 'O', filename);
|
||||
|
||||
cfg->vk_up = GetPrivateProfileIntW(L"keyboard", L"up", VK_UP, filename);
|
||||
cfg->vk_right = GetPrivateProfileIntW(L"keyboard", L"right", VK_RIGHT, filename);
|
||||
cfg->vk_down = GetPrivateProfileIntW(L"keyboard", L"down", VK_DOWN, filename);
|
||||
cfg->vk_left = GetPrivateProfileIntW(L"keyboard", L"left", VK_LEFT, filename);
|
||||
|
||||
wchar_t tmp[16];
|
||||
for (int i = 0; i < APM3_BUTTON_COUNT; i++) {
|
||||
swprintf_s(tmp, 32, L"button%d", i + 1);
|
||||
cfg->vk_buttons[i] = GetPrivateProfileIntW(L"keyboard", tmp, BUTTON_DEFAULTS[i], filename);
|
||||
}
|
||||
}
|
||||
|
||||
void apm3_di_config_load(struct apm3_di_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
wchar_t key[8];
|
||||
int i;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"dinput",
|
||||
L"deviceName",
|
||||
L"",
|
||||
cfg->device_name,
|
||||
_countof(cfg->device_name),
|
||||
filename);
|
||||
|
||||
cfg->home = GetPrivateProfileIntW(L"dinput", L"home", 0, filename);
|
||||
cfg->start = GetPrivateProfileIntW(L"dinput", L"start", 0, filename);
|
||||
|
||||
for (i = 0 ; i < 8 ; i++) {
|
||||
swprintf_s(key, _countof(key), L"button%i", i + 1);
|
||||
cfg->button[i] = GetPrivateProfileIntW(L"dinput", key, i + 1, filename);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void apm3_xi_config_load(struct apm3_xi_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->analog_stick_enabled = GetPrivateProfileIntW(
|
||||
L"xinput",
|
||||
L"analogStickEnabled",
|
||||
1,
|
||||
filename);
|
||||
}
|
||||
|
||||
void apm3_io_config_load(
|
||||
struct apm3_io_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", VK_F1, filename);
|
||||
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", VK_F2, filename);
|
||||
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", VK_F3, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"io4",
|
||||
L"mode",
|
||||
L"",
|
||||
cfg->mode,
|
||||
_countof(cfg->mode),
|
||||
filename);
|
||||
|
||||
apm3_kb_config_load(&cfg->kb, filename);
|
||||
apm3_di_config_load(&cfg->di, filename);
|
||||
apm3_xi_config_load(&cfg->xi, filename);
|
||||
}
|
48
games/apm3io/config.h
Normal file
48
games/apm3io/config.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define APM3_BUTTON_COUNT 8
|
||||
|
||||
struct apm3_di_config {
|
||||
wchar_t device_name[64];
|
||||
uint8_t start;
|
||||
uint8_t home;
|
||||
uint8_t button[8];
|
||||
};
|
||||
|
||||
struct apm3_xi_config {
|
||||
bool analog_stick_enabled;
|
||||
};
|
||||
|
||||
struct apm3_kb_config {
|
||||
uint8_t vk_start;
|
||||
uint8_t vk_home;
|
||||
|
||||
uint8_t vk_up;
|
||||
uint8_t vk_right;
|
||||
uint8_t vk_down;
|
||||
uint8_t vk_left;
|
||||
|
||||
uint8_t vk_buttons[APM3_BUTTON_COUNT];
|
||||
};
|
||||
|
||||
struct apm3_io_config {
|
||||
uint8_t vk_test;
|
||||
uint8_t vk_service;
|
||||
uint8_t vk_coin;
|
||||
|
||||
wchar_t mode[9];
|
||||
|
||||
struct apm3_kb_config kb;
|
||||
struct apm3_di_config di;
|
||||
struct apm3_xi_config xi;
|
||||
};
|
||||
|
||||
void apm3_io_config_load(struct apm3_io_config *cfg, const wchar_t *filename);
|
||||
|
||||
void apm3_kb_config_load(struct apm3_kb_config *cfg, const wchar_t *filename);
|
||||
void apm3_di_config_load(struct apm3_di_config *cfg, const wchar_t *filename);
|
||||
void apm3_xi_config_load(struct apm3_xi_config *cfg, const wchar_t *filename);
|
83
games/apm3io/di-dev.c
Normal file
83
games/apm3io/di-dev.c
Normal file
@ -0,0 +1,83 @@
|
||||
#include <windows.h>
|
||||
#include <dinput.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "apm3io/di-dev.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
HRESULT apm3_di_dev_start(IDirectInputDevice8W *dev, HWND wnd)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(dev != NULL);
|
||||
assert(wnd != NULL);
|
||||
|
||||
hr = IDirectInputDevice8_SetCooperativeLevel(
|
||||
dev,
|
||||
wnd,
|
||||
DISCL_BACKGROUND | DISCL_EXCLUSIVE);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: SetCooperativeLevel failed: %08x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = IDirectInputDevice8_SetDataFormat(dev, &c_dfDIJoystick);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: SetDataFormat failed: %08x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = IDirectInputDevice8_Acquire(dev);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: Acquire failed: %08x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
HRESULT apm3_di_dev_poll(
|
||||
IDirectInputDevice8W *dev,
|
||||
HWND wnd,
|
||||
union apm3_di_state *out)
|
||||
{
|
||||
HRESULT hr;
|
||||
MSG msg;
|
||||
|
||||
assert(dev != NULL);
|
||||
assert(wnd != NULL);
|
||||
assert(out != NULL);
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
/* Pump our dummy window's message queue just in case DirectInput or an
|
||||
IHV DirectInput driver somehow relies on it */
|
||||
|
||||
while (PeekMessageW(&msg, wnd, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
hr = IDirectInputDevice8_GetDeviceState(
|
||||
dev,
|
||||
sizeof(out->st),
|
||||
&out->st);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: GetDeviceState error: %08x\n", (int) hr);
|
||||
}
|
||||
|
||||
/* JVS lacks a protocol for reporting hardware errors from poll command
|
||||
responses, so this ends up returning zeroed input state instead. */
|
||||
|
||||
return hr;
|
||||
}
|
18
games/apm3io/di-dev.h
Normal file
18
games/apm3io/di-dev.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <dinput.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
union apm3_di_state {
|
||||
DIJOYSTATE st;
|
||||
uint8_t bytes[sizeof(DIJOYSTATE)];
|
||||
};
|
||||
|
||||
HRESULT apm3_di_dev_start(IDirectInputDevice8W *dev, HWND wnd);
|
||||
HRESULT apm3_di_dev_poll(
|
||||
IDirectInputDevice8W *dev,
|
||||
HWND wnd,
|
||||
union apm3_di_state *out);
|
||||
|
257
games/apm3io/di.c
Normal file
257
games/apm3io/di.c
Normal file
@ -0,0 +1,257 @@
|
||||
#include <windows.h>
|
||||
#include <dinput.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "apm3io/backend.h"
|
||||
#include "apm3io/config.h"
|
||||
#include "apm3io/di.h"
|
||||
#include "apm3io/di-dev.h"
|
||||
#include "apm3io/apm3io.h"
|
||||
#include "apm3io/wnd.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
struct apm3_di_axis {
|
||||
wchar_t name[4];
|
||||
size_t off;
|
||||
};
|
||||
|
||||
static HRESULT apm3_di_config_apply(const struct apm3_di_config *cfg);
|
||||
static BOOL CALLBACK apm3_di_enum_callback(
|
||||
const DIDEVICEINSTANCEW *dev,
|
||||
void *ctx);
|
||||
static BOOL CALLBACK apm3_di_enum_callback_shifter(
|
||||
const DIDEVICEINSTANCEW *dev,
|
||||
void *ctx);
|
||||
static void apm3_di_get_gamebtns(uint16_t *gamebtn_out);
|
||||
static uint8_t apm3_di_decode_pov(DWORD pov);
|
||||
|
||||
|
||||
static const struct apm3_io_backend apm3_di_backend = {
|
||||
.get_gamebtns = apm3_di_get_gamebtns,
|
||||
};
|
||||
|
||||
static HWND apm3_di_wnd;
|
||||
static IDirectInput8W *apm3_di_api;
|
||||
static IDirectInputDevice8W *apm3_di_dev;
|
||||
static IDirectInputEffect *apm3_di_fx;
|
||||
static uint8_t apm3_di_home;
|
||||
static uint8_t apm3_di_start;
|
||||
static uint8_t apm3_di_button[8];
|
||||
|
||||
HRESULT apm3_di_init(
|
||||
const struct apm3_di_config *cfg,
|
||||
HINSTANCE inst,
|
||||
const struct apm3_io_backend **backend)
|
||||
{
|
||||
HRESULT hr;
|
||||
HMODULE dinput8;
|
||||
HRESULT (WINAPI *api_entry)(HINSTANCE,DWORD,REFIID,LPVOID *,LPUNKNOWN);
|
||||
wchar_t dll_path[MAX_PATH];
|
||||
UINT path_pos;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(backend != NULL);
|
||||
|
||||
*backend = NULL;
|
||||
|
||||
hr = apm3_di_config_apply(cfg);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = apm3_io_wnd_create(inst, &apm3_di_wnd);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = IDirectInput8_EnumDevices(
|
||||
apm3_di_api,
|
||||
DI8DEVCLASS_GAMECTRL,
|
||||
apm3_di_enum_callback,
|
||||
(void *) cfg,
|
||||
DIEDFL_ATTACHEDONLY);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("DirectInput: EnumDevices failed: %08x\n", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (apm3_di_dev == NULL) {
|
||||
dprintf("Stick: Controller not found\n");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
hr = apm3_di_dev_start(apm3_di_dev, apm3_di_wnd);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("DirectInput: Controller initialized\n");
|
||||
|
||||
*backend = &apm3_di_backend;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT apm3_di_config_apply(const struct apm3_di_config *cfg)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cfg->start > 32) {
|
||||
dprintf("Stick: Invalid start button: %i\n", cfg->start);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->home > 32) {
|
||||
dprintf("Stick: Invalid home button: %i\n", cfg->home);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
/* Check all 8 defined buttons */
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (cfg->button[i] > 32) {
|
||||
dprintf("Stick: Invalid button %i: %i\n", i, cfg->button[i]);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print some debug output to make sure config works... */
|
||||
|
||||
dprintf("Stick: --- Begin configuration ---\n");
|
||||
dprintf("Stick: Device name . . . . : Contains \"%S\"\n",
|
||||
cfg->device_name);
|
||||
dprintf("Stick: Home button . . . : %i\n", cfg->home);
|
||||
dprintf("Stick: Start button . . . : %i\n", cfg->start);
|
||||
|
||||
/* Print the configuration for all 8 buttons */
|
||||
for (i = 0; i < 8; i++) {
|
||||
dprintf("Stick: Button %i . . . . . : %i\n", i, cfg->button[i]);
|
||||
}
|
||||
|
||||
dprintf("Stick: --- End configuration ---\n");
|
||||
|
||||
apm3_di_start = cfg->start;
|
||||
apm3_di_home = cfg->home;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
apm3_di_button[i] = cfg->button[i];
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK apm3_di_enum_callback(
|
||||
const DIDEVICEINSTANCEW *dev,
|
||||
void *ctx)
|
||||
{
|
||||
const struct apm3_di_config *cfg;
|
||||
HRESULT hr;
|
||||
|
||||
cfg = ctx;
|
||||
|
||||
if (wcsstr(dev->tszProductName, cfg->device_name) == NULL) {
|
||||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
dprintf("Stick: Using DirectInput device \"%S\"\n", dev->tszProductName);
|
||||
|
||||
hr = IDirectInput8_CreateDevice(
|
||||
apm3_di_api,
|
||||
&dev->guidInstance,
|
||||
&apm3_di_dev,
|
||||
NULL);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("Stick: CreateDevice failed: %08x\n", (int) hr);
|
||||
}
|
||||
|
||||
return DIENUM_STOP;
|
||||
}
|
||||
|
||||
static void apm3_di_get_gamebtns(uint16_t *gamebtn_out)
|
||||
{
|
||||
union apm3_di_state state;
|
||||
uint16_t gamebtn;
|
||||
HRESULT hr;
|
||||
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
hr = apm3_di_dev_poll(apm3_di_dev, apm3_di_wnd, &state);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gamebtn = apm3_di_decode_pov(state.st.rgdwPOV[0]);
|
||||
|
||||
if (apm3_di_start && state.st.rgbButtons[apm3_di_start - 1]) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_START;
|
||||
}
|
||||
|
||||
if (apm3_di_home && state.st.rgbButtons[apm3_di_home - 1]) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_HOME;
|
||||
}
|
||||
|
||||
if (apm3_di_button[0] && state.st.rgbButtons[apm3_di_button[0] - 1]) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B1;
|
||||
}
|
||||
|
||||
if (apm3_di_button[1] && state.st.rgbButtons[apm3_di_button[1] - 1]) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B2;
|
||||
}
|
||||
|
||||
if (apm3_di_button[2] && state.st.rgbButtons[apm3_di_button[2] - 1]) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B3;
|
||||
}
|
||||
|
||||
if (apm3_di_button[3] && state.st.rgbButtons[apm3_di_button[3] - 1]) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B4;
|
||||
}
|
||||
|
||||
if (apm3_di_button[4] && state.st.rgbButtons[apm3_di_button[4] - 1]) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B5;
|
||||
}
|
||||
|
||||
if (apm3_di_button[5] && state.st.rgbButtons[apm3_di_button[5] - 1]) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B6;
|
||||
}
|
||||
|
||||
if (apm3_di_button[6] && state.st.rgbButtons[apm3_di_button[6] - 1]) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B7;
|
||||
}
|
||||
|
||||
if (apm3_di_button[7] && state.st.rgbButtons[apm3_di_button[7] - 1]) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B8;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
||||
|
||||
static uint8_t apm3_di_decode_pov(DWORD pov)
|
||||
{
|
||||
switch (pov) {
|
||||
case 0: return APM3_IO_GAMEBTN_UP;
|
||||
case 4500: return APM3_IO_GAMEBTN_UP | APM3_IO_GAMEBTN_RIGHT;
|
||||
case 9000: return APM3_IO_GAMEBTN_RIGHT;
|
||||
case 13500: return APM3_IO_GAMEBTN_RIGHT | APM3_IO_GAMEBTN_DOWN;
|
||||
case 18000: return APM3_IO_GAMEBTN_DOWN;
|
||||
case 22500: return APM3_IO_GAMEBTN_DOWN | APM3_IO_GAMEBTN_LEFT;
|
||||
case 27000: return APM3_IO_GAMEBTN_LEFT;
|
||||
case 31500: return APM3_IO_GAMEBTN_LEFT | APM3_IO_GAMEBTN_UP;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
7
games/apm3io/di.h
Normal file
7
games/apm3io/di.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "apm3io/backend.h"
|
||||
#include "apm3io/config.h"
|
||||
|
||||
HRESULT apm3_di_init(const struct apm3_di_config *cfg, HINSTANCE inst,
|
||||
const struct apm3_io_backend **backend);
|
105
games/apm3io/dllmain.c
Normal file
105
games/apm3io/dllmain.c
Normal file
@ -0,0 +1,105 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "apm3io/backend.h"
|
||||
#include "apm3io/config.h"
|
||||
#include "apm3io/apm3io.h"
|
||||
#include "apm3io/kb.h"
|
||||
#include "apm3io/di.h"
|
||||
#include "apm3io/xi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static struct apm3_io_config apm3_io_cfg;
|
||||
static const struct apm3_io_backend *apm3_io_backend;
|
||||
static bool apm3_io_coin;
|
||||
|
||||
uint16_t apm3_io_get_api_version(void)
|
||||
{
|
||||
return 0x0100;
|
||||
}
|
||||
|
||||
HRESULT apm3_io_init(void)
|
||||
{
|
||||
HINSTANCE inst;
|
||||
HRESULT hr;
|
||||
|
||||
assert(apm3_io_backend == NULL);
|
||||
|
||||
inst = GetModuleHandleW(NULL);
|
||||
|
||||
if (inst == NULL) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("GetModuleHandleW failed: %lx\n", hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
apm3_io_config_load(&apm3_io_cfg, L".\\segatools.ini");
|
||||
|
||||
if (wstr_ieq(apm3_io_cfg.mode, L"keyboard")) {
|
||||
hr = apm3_kb_init(&apm3_io_cfg.kb, &apm3_io_backend);
|
||||
} else if (wstr_ieq(apm3_io_cfg.mode, L"dinput")) {
|
||||
hr = apm3_di_init(&apm3_io_cfg.di, inst, &apm3_io_backend);
|
||||
} else if (wstr_ieq(apm3_io_cfg.mode, L"xinput")) {
|
||||
hr = apm3_xi_init(&apm3_io_cfg.xi, &apm3_io_backend);
|
||||
} else {
|
||||
hr = E_INVALIDARG;
|
||||
dprintf("APM3 IO: Invalid IO mode \"%S\", use keyboard, dinput or xinput\n",
|
||||
apm3_io_cfg.mode);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
void apm3_io_get_opbtns(uint8_t *opbtn_out)
|
||||
{
|
||||
uint8_t opbtn;
|
||||
|
||||
assert(apm3_io_backend != NULL);
|
||||
assert(opbtn_out != NULL);
|
||||
|
||||
opbtn = 0;
|
||||
|
||||
if (GetAsyncKeyState(apm3_io_cfg.vk_test) & 0x8000) {
|
||||
opbtn |= APM3_IO_OPBTN_TEST;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_io_cfg.vk_service) & 0x8000) {
|
||||
opbtn |= APM3_IO_OPBTN_SERVICE;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_io_cfg.vk_coin) & 0x8000) {
|
||||
if (!apm3_io_coin) {
|
||||
apm3_io_coin = true;
|
||||
opbtn |= APM3_IO_OPBTN_COIN;
|
||||
}
|
||||
} else {
|
||||
apm3_io_coin = false;
|
||||
}
|
||||
|
||||
*opbtn_out = opbtn;
|
||||
}
|
||||
|
||||
|
||||
void apm3_io_get_gamebtns(uint16_t *gamebtn_out)
|
||||
{
|
||||
assert(apm3_io_backend != NULL);
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
apm3_io_backend->get_gamebtns(gamebtn_out);
|
||||
}
|
||||
|
||||
HRESULT apm3_io_led_init(void)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void apm3_io_led_set_colors(uint8_t board, uint8_t *rgb)
|
||||
{
|
||||
return;
|
||||
}
|
184
games/apm3io/kb.c
Normal file
184
games/apm3io/kb.c
Normal file
@ -0,0 +1,184 @@
|
||||
#include <windows.h>
|
||||
#include <xinput.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "apm3io/apm3io.h"
|
||||
#include "apm3io/config.h"
|
||||
#include "apm3io/kb.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/env.h"
|
||||
|
||||
static uint8_t apm3_kb_home;
|
||||
static uint8_t apm3_kb_start;
|
||||
static uint8_t apm3_kb_up;
|
||||
static uint8_t apm3_kb_right;
|
||||
static uint8_t apm3_kb_down;
|
||||
static uint8_t apm3_kb_left;
|
||||
static uint8_t apm3_kb_button[8];
|
||||
|
||||
static void apm3_kb_get_gamebtns(uint16_t *gamebtn_out);
|
||||
|
||||
static HRESULT apm3_kb_config_apply(const struct apm3_kb_config *cfg);
|
||||
|
||||
static const struct apm3_io_backend apm3_kb_backend = {
|
||||
.get_gamebtns = apm3_kb_get_gamebtns,
|
||||
};
|
||||
|
||||
HRESULT apm3_kb_init(const struct apm3_kb_config *cfg,
|
||||
const struct apm3_io_backend **backend) {
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(backend != NULL);
|
||||
|
||||
*backend = NULL;
|
||||
|
||||
hr = apm3_kb_config_apply(cfg);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("Keyboard: Using keyboard input\n");
|
||||
*backend = &apm3_kb_backend;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT apm3_kb_config_apply(const struct apm3_kb_config *cfg) {
|
||||
int i;
|
||||
|
||||
if (cfg->vk_start > 255) {
|
||||
dprintf("Keyboard: Invalid start key configuration: %u\n", cfg->vk_start);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->vk_home > 255) {
|
||||
dprintf("Keyboard: Invalid home key configuration: %u\n", cfg->vk_home);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->vk_up > 255) {
|
||||
dprintf("Keyboard: Invalid up key configuration: %u\n", cfg->vk_up);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->vk_right > 255) {
|
||||
dprintf("Keyboard: Invalid right key configuration: %u\n", cfg->vk_right);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->vk_down > 255) {
|
||||
dprintf("Keyboard: Invalid down key configuration: %u\n", cfg->vk_down);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (cfg->vk_left > 255) {
|
||||
dprintf("Keyboard: Invalid left key configuration: %u\n", cfg->vk_left);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
for (i = 0; i < APM3_BUTTON_COUNT; i++) {
|
||||
if (cfg->vk_buttons[i] > 255) {
|
||||
dprintf("Keyboard: Invalid button %i configuration\n", i);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
apm3_kb_button[i] = cfg->vk_buttons[i];
|
||||
}
|
||||
|
||||
/* Apply the configuration */
|
||||
apm3_kb_home = cfg->vk_home;
|
||||
apm3_kb_start = cfg->vk_start;
|
||||
apm3_kb_up = cfg->vk_up;
|
||||
apm3_kb_right = cfg->vk_right;
|
||||
apm3_kb_down = cfg->vk_down;
|
||||
apm3_kb_left = cfg->vk_left;
|
||||
|
||||
/* Print some debug output to make sure config works... */
|
||||
dprintf("Keyboard: --- Begin configuration ---\n");
|
||||
dprintf("Keyboard: Home key . . . . : %u\n", apm3_kb_home);
|
||||
dprintf("Keyboard: Start key . . . : %u\n", apm3_kb_start);
|
||||
|
||||
dprintf("Keyboard: Down key . . . . : %u\n", cfg->vk_down);
|
||||
dprintf("Keyboard: Left key . . . . : %u\n", cfg->vk_left);
|
||||
|
||||
/* Print the configuration for all 8 buttons */
|
||||
for (i = 0; i < APM3_BUTTON_COUNT; i++) {
|
||||
dprintf("Keyboard: Button %i . . . . : %u\n", i + 1, apm3_kb_button[i]);
|
||||
}
|
||||
|
||||
dprintf("Keyboard: --- End configuration ---\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void apm3_kb_get_gamebtns(uint16_t* gamebtn_out) {
|
||||
uint16_t gamebtn;
|
||||
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
gamebtn = 0;
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_home) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_HOME;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_start) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_START;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_up) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_UP;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_right) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_RIGHT;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_down) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_DOWN;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_left) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_LEFT;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_button[0]) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B1;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_button[1]) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B2;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_button[2]) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B3;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_button[3]) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B4;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_button[4]) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B5;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_button[5]) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B6;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_button[6]) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B7;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(apm3_kb_button[7]) & 0x8000) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B8;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
9
games/apm3io/kb.h
Normal file
9
games/apm3io/kb.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "apm3io/backend.h"
|
||||
#include "apm3io/config.h"
|
||||
|
||||
HRESULT apm3_kb_init(const struct apm3_kb_config *cfg,
|
||||
const struct apm3_io_backend **backend);
|
31
games/apm3io/meson.build
Normal file
31
games/apm3io/meson.build
Normal file
@ -0,0 +1,31 @@
|
||||
apm3io_lib = static_library(
|
||||
'apm3io',
|
||||
name_prefix : '',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
dependencies : [
|
||||
dinput8_lib,
|
||||
dxguid_lib,
|
||||
xinput_lib,
|
||||
],
|
||||
link_with : [
|
||||
util_lib,
|
||||
],
|
||||
sources : [
|
||||
'apm3io.h',
|
||||
'backend.h',
|
||||
'config.c',
|
||||
'config.h',
|
||||
'di.c',
|
||||
'di.h',
|
||||
'di-dev.c',
|
||||
'di-dev.h',
|
||||
'dllmain.c',
|
||||
'kb.c',
|
||||
'kb.h',
|
||||
'wnd.c',
|
||||
'wnd.h',
|
||||
'xi.c',
|
||||
'xi.h',
|
||||
],
|
||||
)
|
86
games/apm3io/wnd.c
Normal file
86
games/apm3io/wnd.c
Normal file
@ -0,0 +1,86 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
/* DirectInput requires a window for correct initialization (and also force
|
||||
feedback), so this source file provides some utilities for creating a
|
||||
generic message-only window. */
|
||||
|
||||
static LRESULT WINAPI apm3_io_wnd_proc(
|
||||
HWND hwnd,
|
||||
UINT msg,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
|
||||
HRESULT apm3_io_wnd_create(HINSTANCE inst, HWND *out)
|
||||
{
|
||||
HRESULT hr;
|
||||
WNDCLASSEXW wcx;
|
||||
ATOM atom;
|
||||
HWND hwnd;
|
||||
|
||||
assert(inst != NULL); /* We are not an EXE */
|
||||
assert(out != NULL);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
memset(&wcx, 0, sizeof(wcx));
|
||||
wcx.cbSize = sizeof(wcx);
|
||||
wcx.lpfnWndProc = apm3_io_wnd_proc;
|
||||
wcx.hInstance = inst;
|
||||
wcx.lpszClassName = L"apm3IO";
|
||||
|
||||
atom = RegisterClassExW(&wcx);
|
||||
|
||||
if (atom == 0) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("apm3IO: RegisterClassExW failed: %08x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hwnd = CreateWindowExW(
|
||||
0,
|
||||
(wchar_t *) (intptr_t) atom,
|
||||
L"",
|
||||
0,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
HWND_MESSAGE,
|
||||
NULL,
|
||||
inst,
|
||||
NULL);
|
||||
|
||||
if (hwnd == NULL) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("apm3IO: CreateWindowExW failed: %08x\n", (int) hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*out = hwnd;
|
||||
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
UnregisterClassW((wchar_t *) (intptr_t) atom, inst);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static LRESULT WINAPI apm3_io_wnd_proc(
|
||||
HWND hwnd,
|
||||
UINT msg,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
switch (msg) {
|
||||
default:
|
||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
}
|
5
games/apm3io/wnd.h
Normal file
5
games/apm3io/wnd.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
HRESULT apm3_io_wnd_create(HINSTANCE inst, HWND *out);
|
167
games/apm3io/xi.c
Normal file
167
games/apm3io/xi.c
Normal file
@ -0,0 +1,167 @@
|
||||
#include <windows.h>
|
||||
#include <xinput.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "apm3io/backend.h"
|
||||
#include "apm3io/config.h"
|
||||
#include "apm3io/apm3io.h"
|
||||
#include "apm3io/xi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static void apm3_xi_get_gamebtns(uint16_t *gamebtn_out);
|
||||
|
||||
static HRESULT apm3_xi_config_apply(const struct apm3_xi_config *cfg);
|
||||
|
||||
static const struct apm3_io_backend apm3_xi_backend = {
|
||||
.get_gamebtns = apm3_xi_get_gamebtns,
|
||||
};
|
||||
|
||||
static bool apm3_xi_analog_stick_enabled;
|
||||
|
||||
HRESULT apm3_xi_init(const struct apm3_xi_config *cfg, const struct apm3_io_backend **backend)
|
||||
{
|
||||
HRESULT hr;
|
||||
assert(cfg != NULL);
|
||||
assert(backend != NULL);
|
||||
|
||||
hr = apm3_xi_config_apply(cfg);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("XInput: Using XInput controller\n");
|
||||
*backend = &apm3_xi_backend;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT apm3_io_poll(void)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT apm3_xi_config_apply(const struct apm3_xi_config *cfg)
|
||||
{
|
||||
dprintf("XInput: --- Begin configuration ---\n");
|
||||
dprintf("XInput: Analog Stick : %i\n", cfg->analog_stick_enabled);
|
||||
dprintf("XInput: --- End configuration ---\n");
|
||||
|
||||
apm3_xi_analog_stick_enabled = cfg->analog_stick_enabled;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void apm3_xi_get_gamebtns(uint16_t *gamebtn_out)
|
||||
{
|
||||
uint16_t gamebtn;
|
||||
XINPUT_STATE xi;
|
||||
WORD xb;
|
||||
int left_x, left_y;
|
||||
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
gamebtn = 0;
|
||||
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
XInputGetState(0, &xi);
|
||||
xb = xi.Gamepad.wButtons;
|
||||
|
||||
/* Use the left analog stick as a D Pad if enabled */
|
||||
if (apm3_xi_analog_stick_enabled) {
|
||||
left_x = xi.Gamepad.sThumbLX;
|
||||
left_y = xi.Gamepad.sThumbLY;
|
||||
|
||||
if (left_x < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE * 2) {
|
||||
left_x += XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE * 2;
|
||||
} else if (left_x > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE * 2) {
|
||||
left_x -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE * 2;
|
||||
} else {
|
||||
left_x = 0;
|
||||
}
|
||||
|
||||
if (left_y < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE * 2) {
|
||||
left_y += XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE * 2;
|
||||
} else if (left_y > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE * 2) {
|
||||
left_y -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE * 2;
|
||||
} else {
|
||||
left_y = 0;
|
||||
}
|
||||
|
||||
if (left_y < 0) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_DOWN;
|
||||
} else if (left_y > 0) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_UP;
|
||||
}
|
||||
|
||||
if (left_x < 0) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_LEFT;
|
||||
} else if (left_x > 0) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Normal game button controls */
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_DPAD_UP) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_UP;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_DPAD_DOWN) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_DOWN;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_DPAD_LEFT) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_LEFT;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_DPAD_RIGHT) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_RIGHT;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_START) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_START;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_BACK) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_HOME;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_A) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B1;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_B) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B2;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_X) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B5;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_Y) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B6;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_LEFT_SHOULDER) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B3;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_RIGHT_SHOULDER) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B4;
|
||||
}
|
||||
|
||||
if (xi.Gamepad.bLeftTrigger > 64) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B7;
|
||||
}
|
||||
|
||||
if (xi.Gamepad.bRightTrigger > 64) {
|
||||
gamebtn |= APM3_IO_GAMEBTN_B8;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
11
games/apm3io/xi.h
Normal file
11
games/apm3io/xi.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
/* Can't call this xinput.h or it will conflict with <xinput.h> */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "apm3io/backend.h"
|
||||
#include "apm3io/config.h"
|
||||
|
||||
HRESULT apm3_xi_init(const struct apm3_xi_config *cfg,
|
||||
const struct apm3_io_backend **backend);
|
@ -87,7 +87,7 @@ static DWORD CALLBACK cm_pre_startup(void)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&cm_hook_cfg.aime, 1, 1, cm_hook_mod);
|
||||
hr = sg_reader_hook_init(&cm_hook_cfg.aime, 1, 3, cm_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
@ -82,7 +82,7 @@ static DWORD CALLBACK mercury_pre_startup(void)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&mercury_hook_cfg.aime, 1, 1, mercury_hook_mod);
|
||||
hr = sg_reader_hook_init(&mercury_hook_cfg.aime, 1, 3, mercury_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
@ -97,7 +97,7 @@ static DWORD CALLBACK mu3_pre_startup(void)
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1, 1, mu3_hook_mod);
|
||||
hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1, 3, mu3_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
|
@ -93,6 +93,7 @@ xinput_lib = cc.find_library('xinput')
|
||||
pathcch_lib = cc.find_library('pathcch')
|
||||
imagehlp_lib = cc.find_library('imagehlp')
|
||||
ws2_32_lib = cc.find_library('ws2_32')
|
||||
bcrypt_lib = cc.find_library('bcrypt')
|
||||
|
||||
inc = include_directories('common', 'games')
|
||||
capnhook = subproject('capnhook')
|
||||
@ -123,6 +124,7 @@ subdir('games/cxbio')
|
||||
subdir('games/tokyoio')
|
||||
subdir('games/fgoio')
|
||||
subdir('games/kemonoio')
|
||||
subdir('games/apm3io')
|
||||
|
||||
subdir('games/chunihook')
|
||||
subdir('games/divahook')
|
||||
@ -139,3 +141,4 @@ subdir('games/cxbhook')
|
||||
subdir('games/tokyohook')
|
||||
subdir('games/fgohook')
|
||||
subdir('games/kemonohook')
|
||||
subdir('games/apm3hook')
|
||||
|
Reference in New Issue
Block a user