APMv3: add hook (#73)

This adds support for APMv3 I/O, menus and the launcher.

* Added a apm3hook dll and I/O based on the usual layout.
* Added C:\Mount\Apm to vfs.
* Added the relevant .dlls to unityhook.
* Added a hook for apmmount.dll that uses `CreateDosDevice` to mount decrypted data to the locations the launcher and games expect files to be. This will conflict with anything that is already at W:\ and X:\, but I do not have better solutions for this.
* `launch.bat` is a bit more involved as it simulates the launcher loop. It can be broken by alt+f4ing or closing the launcher with "X".
* An extra export was added, so rundll32 can be used to get rid of the dosdevices after the launcher was killed.
* Since all the games do everything via `X:\lib\apm.dll`, no game hooks were needed in testing, therefore, `game.bat` files can be used as is.
* Path hooks are applied correctly, so you can go correctly between games, launcher, sub system test mode and game test modes.

A setup guide (some stuff specific to my server) can be found here:
https://gmg.hopto.org:82/gmg/wiki/index.php/All.Net_P-ras_Multi_Menu

Tested with the 2 APM sample apps, Blazblue, Puyo, Guilty Gear and some weird unity puzzle game whose name I forgot.

![Apmv3System_yLRityJVpm.png](/attachments/3d645e71-81e6-42e6-acd4-63c537cda59e)
![puyoe_hJNhnJGFnd.png](/attachments/01664049-71fe-4c38-9c99-39649ab21e56)

Reviewed-on: https://gitea.tendokyu.moe/TeamTofuShop/segatools/pulls/73
Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
This commit is contained in:
kyoubate-haruka
2025-07-20 09:43:56 +00:00
committed by Dniel97
parent 03513e7b0c
commit e2e4b37e3f
28 changed files with 1658 additions and 3 deletions
+115
View 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
View 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
View 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
View 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"apmio",
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
View 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);
+173
View File
@@ -0,0 +1,173 @@
/*
"ALL.Net P-ras multi Ver.3" (apm) 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 "amex/ds.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;
void unity_hook_callback(HMODULE hmodule, const wchar_t* p) {
dprintf("Unity: Hook callback: %ls\n", p);
serial_hook_apply_hooks(hmodule);
security_hook_insert_hooks(hmodule);
touch_hook_insert_hooks(hmodule);
//mount_hook_apply_hooks(&apm3_hook_cfg.mount, hmodule);
}
void apm3_extra_hooks_init(void) {
HMODULE module = LoadLibraryA("Apmv3System_Data/Plugins/x86_64/apmmount.dll"); // HACK??
if (module != NULL){
dprintf("APM: Successfully pre-loaded apmmount\n");
}
module = LoadLibraryA("Apmv3System_Data/Plugins/x86_64/apmled.dll"); // HACK??
if (module != NULL){
dprintf("APM: Successfully pre-loaded apmled\n");
}
}
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());
/* Pin stuff */
apm3_extra_hooks_init();
/* 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
View 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
View 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
View 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
View 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
View 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);
+128
View File
@@ -0,0 +1,128 @@
#include <windows.h>
#include <xinput.h>
#include <math.h>
#include <limits.h>
#include <stdint.h>
#include "apm3io/apm3io.h"
#include "apm3io/config.h"
#include "util/dprintf.h"
#include "util/env.h"
static uint8_t apm3_opbtn;
static uint32_t apm3_gamebtn;
static int16_t apm3_stick_x;
static int16_t apm3_stick_y;
static struct apm3_io_config apm3_io_cfg;
static bool apm3_io_coin;
uint16_t apm3_io_get_api_version(void) {
return 0x0100;
}
HRESULT apm3_io_init(void) {
apm3_io_config_load(&apm3_io_cfg, get_config_path());
return S_OK;
}
HRESULT apm3_io_poll(void) {
apm3_opbtn = 0;
apm3_gamebtn = 0;
if (GetAsyncKeyState(apm3_io_cfg.vk_test) & 0x8000) {
apm3_opbtn |= APM3_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_service) & 0x8000) {
apm3_opbtn |= APM3_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_coin) & 0x8000) {
if (!apm3_io_coin) {
apm3_io_coin = true;
apm3_opbtn |= APM3_IO_OPBTN_COIN;
}
} else {
apm3_io_coin = false;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_home) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_HOME;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_start) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_START;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_up) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_UP;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_right) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_RIGHT;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_down) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_DOWN;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_left) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_LEFT;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_buttons[0]) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_B1;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_buttons[1]) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_B2;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_buttons[2]) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_B3;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_buttons[3]) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_B4;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_buttons[4]) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_B5;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_buttons[5]) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_B6;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_buttons[6]) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_B7;
}
if (GetAsyncKeyState(apm3_io_cfg.vk_buttons[7]) & 0x8000) {
apm3_gamebtn |= APM3_IO_GAMEBTN_B8;
}
return S_OK;
}
void apm3_io_get_opbtns(uint8_t* opbtn) {
if (opbtn != NULL) {
*opbtn = apm3_opbtn;
}
}
void apm3_io_get_gamebtns(uint32_t* btn) {
if (btn != NULL) {
*btn = apm3_gamebtn;
}
}
HRESULT apm3_io_led_init(void) {
return S_OK;
}
void apm3_io_led_set_colors(uint8_t board, uint8_t* rgb) {
return;
}
+84
View File
@@ -0,0 +1,84 @@
#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(uint32_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);
+36
View File
@@ -0,0 +1,36 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "apm3io/config.h"
const int BUTTON_DEFAULTS[] = {'Q','W','E','R','A','S','D','F'};
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", '1', filename);
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", '3', filename);
cfg->vk_start = GetPrivateProfileIntW(L"io4", L"start", 'P', filename);
cfg->vk_home = GetPrivateProfileIntW(L"io4", L"home", 'O', filename);
cfg->vk_up = GetPrivateProfileIntW(L"io4", L"up", VK_UP, filename);
cfg->vk_right = GetPrivateProfileIntW(L"io4", L"right", VK_RIGHT, filename);
cfg->vk_down = GetPrivateProfileIntW(L"io4", L"down", VK_DOWN, filename);
cfg->vk_left = GetPrivateProfileIntW(L"io4", 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"io4", tmp, BUTTON_DEFAULTS[i], filename);
}
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#define APM3_BUTTON_COUNT 8
struct apm3_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
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];
};
void apm3_io_config_load(
struct apm3_io_config *cfg,
const wchar_t *filename);
+15
View File
@@ -0,0 +1,15 @@
apm3io_lib = static_library(
'apm3io',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
dependencies : [
xinput_lib,
],
sources : [
'apm3io.c',
'apm3io.h',
'config.c',
'config.h',
],
)