Files
kyoubate-haruka d29a3926ab apm: Fix distribution server handling, abaas log symlinks, add extra buttons for dinput (#123)
Requires https://gitea.tendokyu.moe/TeamTofuShop/capnhook/pulls/8

This adds the following:

1. Fixes `am::abaas::FileSearchSuite::isAvailableAbsolutePath: Line259  ERROR: folder is unavailable (Y:\SDEM\log\\SDFU)` by hooking `DeviceIoControl` with `FSCTL_SET_REPARSE_POINT`.

Sega decided to use the most bizzare way possible of creating symbolic links. These are created in the AbaaS log directory inside SDEM and point to the appdata directories for the sub-games.

Magic numbers have been taken from amdaemon decompilation. That whole API is very sparsely documented on the Microsoft side.

2. Fixes distribution server settings not applying.

Unlike most other games, the daemon config files do not have dipswitch filters in them. Instead the distribution setting is determined by running `hwvalue.exe` twice and checking it's exit code. This has been added to `launch.bat` and requires the linked addition for inject.

3. Implemented `get_opbtns` and added extra buttons for dinput.

* `get_opbtns` existed but was never implemented. Added the ability to bind dinput buttons for test/service.
* DirectInput is weird. The controller I'm using is sending directional keys as buttons rather than a D-Pad, so I was unable to bind it. These extra directional buttons can be bound in addition to the bindings that exist for a D-Pad.

4. Fixes audio in Rolling Gunner by setting the CWD to the root of the C:\ drive before starting a game.

lol. lmao even.

5. Add a `DefineDosDevice` for Y:\ to fix Blazblue crashing

They hardcoded the damn path to appdata instead of retrieving it from the apm.dll...

Reviewed-on: https://gitea.tendokyu.moe/TeamTofuShop/segatools/pulls/123
Co-authored-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
Co-committed-by: kyoubate-haruka <46010460+kyoubate-haruka@users.noreply.github.com>
2026-07-19 08:25:54 +00:00

305 lines
7.7 KiB
C

#include <windows.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 void apm3_di_get_gamebtns(uint16_t *gamebtn_out);
static void apm3_di_get_opbtns(uint8_t *opbtn_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,
.get_opbtns = apm3_di_get_opbtns
};
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_service;
static uint8_t apm3_di_test;
static uint8_t apm3_di_extra_up;
static uint8_t apm3_di_extra_right;
static uint8_t apm3_di_extra_down;
static uint8_t apm3_di_extra_left;
static uint8_t apm3_di_button[APM3_BUTTON_COUNT];
static union apm3_di_state state = {0};
HRESULT apm3_di_init(
const struct apm3_di_config *cfg,
HINSTANCE inst,
const struct apm3_io_backend **backend)
{
HRESULT hr;
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 = DirectInput8Create(
inst,
DIRECTINPUT_VERSION,
&IID_IDirectInput8W,
(void**)&apm3_di_api,
NULL);
if (FAILED(hr)) {
dprintf("DirectInput: DirectInput8Create failed: %08x\n", (int)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);
dprintf("Stick: Service button . . : %i\n", cfg->service);
dprintf("Stick: Test button . . . : %i\n", cfg->test);
/* Print the configuration for all 8 buttons */
for (i = 0; i < APM3_BUTTON_COUNT; 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;
apm3_di_service = cfg->service;
apm3_di_test = cfg->test;
apm3_di_extra_up = cfg->up;
apm3_di_extra_right = cfg->right;
apm3_di_extra_down = cfg->down;
apm3_di_extra_left = cfg->left;
for (i = 0; i < APM3_BUTTON_COUNT; 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)
{
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_extra_up && state.st.rgbButtons[apm3_di_extra_up - 1]) {
gamebtn |= APM3_IO_GAMEBTN_UP;
}
if (apm3_di_extra_right && state.st.rgbButtons[apm3_di_extra_right - 1]) {
gamebtn |= APM3_IO_GAMEBTN_RIGHT;
}
if (apm3_di_extra_down && state.st.rgbButtons[apm3_di_extra_down - 1]) {
gamebtn |= APM3_IO_GAMEBTN_DOWN;
}
if (apm3_di_extra_left && state.st.rgbButtons[apm3_di_extra_left - 1]) {
gamebtn |= APM3_IO_GAMEBTN_LEFT;
}
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;
}
}
static void apm3_di_get_opbtns(uint8_t *opbtn_out) {
uint8_t opbtn = 0;
if (apm3_di_service && state.st.rgbButtons[apm3_di_service - 1]) {
opbtn |= APM3_IO_OPBTN_SERVICE;
}
if (apm3_di_test && state.st.rgbButtons[apm3_di_test - 1]) {
opbtn |= APM3_IO_OPBTN_TEST;
}
*opbtn_out = opbtn;
}