forked from TeamTofuShop/segatools
[apm3] add DInput and XInput support
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
# Segatools
|
# Segatools
|
||||||
|
|
||||||
Version: `2024-09-30`
|
Version: `2025-07-20`
|
||||||
|
|
||||||
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
|
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
|
* starting from WACCA
|
||||||
* Kemono Friends
|
* Kemono Friends
|
||||||
* Kemono Friends 3: Planet Tours
|
* 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
|
## End-users
|
||||||
|
|
||||||
|
97
dist/apm3/segatools.ini
vendored
97
dist/apm3/segatools.ini
vendored
@ -10,7 +10,7 @@ option=
|
|||||||
; Create an empty directory somewhere and insert the path here.
|
; Create an empty directory somewhere and insert the path here.
|
||||||
; This directory may be shared between multiple SEGA games.
|
; This directory may be shared between multiple SEGA games.
|
||||||
; NOTE: This has nothing to do with Windows %APPDATA%.
|
; NOTE: This has nothing to do with Windows %APPDATA%.
|
||||||
appdata=appdata
|
appdata=
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
; Device settings
|
; Device settings
|
||||||
@ -22,6 +22,12 @@ appdata=appdata
|
|||||||
enable=1
|
enable=1
|
||||||
aimePath=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
|
||||||
|
|
||||||
[vfd]
|
[vfd]
|
||||||
; Enable VFD emulation. Disable to use a real VFD
|
; Enable VFD emulation. Disable to use a real VFD
|
||||||
; GP1232A02A FUTABA assembly.
|
; GP1232A02A FUTABA assembly.
|
||||||
@ -57,11 +63,20 @@ addrSuffix=11
|
|||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
|
|
||||||
[keychip]
|
[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.
|
; 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
|
; If you disable netenv then you must set this to your LAN's IP subnet, and
|
||||||
; that subnet must start with 192.168.
|
; that subnet must start with 192.168.
|
||||||
subnet=192.168.168.0
|
subnet=192.168.168.0
|
||||||
|
|
||||||
|
[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]
|
[system]
|
||||||
; Enable ALLS system settings.
|
; Enable ALLS system settings.
|
||||||
enable=1
|
enable=1
|
||||||
@ -108,8 +123,8 @@ enable=0
|
|||||||
; Leave empty if you want to use Segatools built-in keyboard input.
|
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||||
path=
|
path=
|
||||||
|
|
||||||
[apmio]
|
[apm3io]
|
||||||
; To use a custom APM IO DLL enter its path here.
|
; To use a custom APM3 IO DLL enter its path here.
|
||||||
; Leave empty if you want to use Segatools built-in gamepad input.
|
; Leave empty if you want to use Segatools built-in gamepad input.
|
||||||
path=
|
path=
|
||||||
|
|
||||||
@ -126,13 +141,45 @@ path=
|
|||||||
; world. An improved solution will be provided later.
|
; world. An improved solution will be provided later.
|
||||||
|
|
||||||
[io4]
|
[io4]
|
||||||
; Test button virtual-key code. Default is the 1 key.
|
; Test button virtual-key code. Default is the F1 key.
|
||||||
test=0x31
|
test=0x70
|
||||||
; Service button virtual-key code. Default is the 2 key.
|
; Service button virtual-key code. Default is the F2 key.
|
||||||
service=0x32
|
service=0x71
|
||||||
; Keyboard button to increment coin counter. Default is the 3 key.
|
; Keyboard button to increment coin counter. Default is the F3 key.
|
||||||
coin=0x33
|
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 button (default: P)
|
||||||
start=0x50
|
start=0x50
|
||||||
; HOME button (default: O)
|
; HOME button (default: O)
|
||||||
@ -144,10 +191,6 @@ right=0x27
|
|||||||
down=0x28
|
down=0x28
|
||||||
left=0x25
|
left=0x25
|
||||||
|
|
||||||
; Main buttons
|
|
||||||
; The arrangement is as following:
|
|
||||||
; 1 2 3 4
|
|
||||||
; 5 6 7 8
|
|
||||||
; Defaults:
|
; Defaults:
|
||||||
; Q W E R
|
; Q W E R
|
||||||
; A S D F
|
; A S D F
|
||||||
@ -159,3 +202,31 @@ button5=0x41
|
|||||||
button6=0x53
|
button6=0x53
|
||||||
button7=0x44
|
button7=0x44
|
||||||
button8=0x46
|
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
|
||||||
|
@ -19,7 +19,7 @@ void apm3_dll_config_load(
|
|||||||
assert(filename != NULL);
|
assert(filename != NULL);
|
||||||
|
|
||||||
GetPrivateProfileStringW(
|
GetPrivateProfileStringW(
|
||||||
L"apmio",
|
L"apm3io",
|
||||||
L"path",
|
L"path",
|
||||||
L"",
|
L"",
|
||||||
cfg->path,
|
cfg->path,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
"ALL.Net P-ras multi Ver.3" (apm) hook
|
"ALL.Net P-ras MULTI Version 3" (apm3) hook
|
||||||
|
|
||||||
Devices
|
Devices
|
||||||
|
|
||||||
@ -17,17 +17,16 @@
|
|||||||
#include "io4.h"
|
#include "io4.h"
|
||||||
#include "mount.h"
|
#include "mount.h"
|
||||||
|
|
||||||
#include "amex/ds.h"
|
|
||||||
|
|
||||||
#include "hook/process.h"
|
#include "hook/process.h"
|
||||||
#include "hooklib/serial.h"
|
|
||||||
|
|
||||||
|
#include "hooklib/serial.h"
|
||||||
#include "hooklib/spike.h"
|
#include "hooklib/spike.h"
|
||||||
|
|
||||||
#include "platform/clock.h"
|
#include "platform/clock.h"
|
||||||
#include "platform/config.h"
|
#include "platform/config.h"
|
||||||
#include "platform/nusec.h"
|
#include "platform/nusec.h"
|
||||||
#include "platform/security.h"
|
#include "platform/security.h"
|
||||||
|
|
||||||
#include "unityhook/hook.h"
|
#include "unityhook/hook.h"
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
#include "util/dprintf.h"
|
||||||
@ -43,7 +42,7 @@ void unity_hook_callback(HMODULE hmodule, const wchar_t* p) {
|
|||||||
serial_hook_apply_hooks(hmodule);
|
serial_hook_apply_hooks(hmodule);
|
||||||
security_hook_insert_hooks(hmodule);
|
security_hook_insert_hooks(hmodule);
|
||||||
touch_hook_insert_hooks(hmodule);
|
touch_hook_insert_hooks(hmodule);
|
||||||
//mount_hook_apply_hooks(&apm3_hook_cfg.mount, hmodule);
|
// mount_hook_apply_hooks(&apm3_hook_cfg.mount, hmodule);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apm3_extra_hooks_init(void) {
|
void apm3_extra_hooks_init(void) {
|
||||||
|
@ -1,128 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
@ -17,6 +17,7 @@ enum {
|
|||||||
APM3_IO_GAMEBTN_RIGHT = 0x08,
|
APM3_IO_GAMEBTN_RIGHT = 0x08,
|
||||||
APM3_IO_GAMEBTN_DOWN = 0x10,
|
APM3_IO_GAMEBTN_DOWN = 0x10,
|
||||||
APM3_IO_GAMEBTN_LEFT = 0x20,
|
APM3_IO_GAMEBTN_LEFT = 0x20,
|
||||||
|
|
||||||
APM3_IO_GAMEBTN_B1 = 0x40,
|
APM3_IO_GAMEBTN_B1 = 0x40,
|
||||||
APM3_IO_GAMEBTN_B2 = 0x80,
|
APM3_IO_GAMEBTN_B2 = 0x80,
|
||||||
APM3_IO_GAMEBTN_B3 = 0x100,
|
APM3_IO_GAMEBTN_B3 = 0x100,
|
||||||
@ -66,7 +67,7 @@ void apm3_io_get_opbtns(uint8_t *opbtn);
|
|||||||
|
|
||||||
Minimum API version: 0x0100 */
|
Minimum API version: 0x0100 */
|
||||||
|
|
||||||
void apm3_io_get_gamebtns(uint32_t *gamebtn);
|
void apm3_io_get_gamebtns(uint16_t *gamebtn);
|
||||||
|
|
||||||
/* Initialize LED emulation. This function will be called before any
|
/* Initialize LED emulation. This function will be called before any
|
||||||
other apm3_io_led_*() function calls.
|
other apm3_io_led_*() function calls.
|
||||||
|
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);
|
||||||
|
};
|
@ -3,11 +3,72 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "apm3io/config.h"
|
#include "apm3io/config.h"
|
||||||
|
|
||||||
const int BUTTON_DEFAULTS[] = {'Q','W','E','R','A','S','D','F'};
|
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(
|
void apm3_io_config_load(
|
||||||
struct apm3_io_config *cfg,
|
struct apm3_io_config *cfg,
|
||||||
const wchar_t *filename)
|
const wchar_t *filename)
|
||||||
@ -15,22 +76,19 @@ void apm3_io_config_load(
|
|||||||
assert(cfg != NULL);
|
assert(cfg != NULL);
|
||||||
assert(filename != NULL);
|
assert(filename != NULL);
|
||||||
|
|
||||||
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", '1', filename);
|
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", VK_F1, filename);
|
||||||
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename);
|
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", VK_F2, filename);
|
||||||
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", '3', filename);
|
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", VK_F3, filename);
|
||||||
|
|
||||||
cfg->vk_start = GetPrivateProfileIntW(L"io4", L"start", 'P', filename);
|
GetPrivateProfileStringW(
|
||||||
cfg->vk_home = GetPrivateProfileIntW(L"io4", L"home", 'O', filename);
|
L"io4",
|
||||||
|
L"mode",
|
||||||
cfg->vk_up = GetPrivateProfileIntW(L"io4", L"up", VK_UP, filename);
|
L"",
|
||||||
cfg->vk_right = GetPrivateProfileIntW(L"io4", L"right", VK_RIGHT, filename);
|
cfg->mode,
|
||||||
cfg->vk_down = GetPrivateProfileIntW(L"io4", L"down", VK_DOWN, filename);
|
_countof(cfg->mode),
|
||||||
cfg->vk_left = GetPrivateProfileIntW(L"io4", L"left", VK_LEFT, filename);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
apm3_kb_config_load(&cfg->kb, filename);
|
||||||
|
apm3_di_config_load(&cfg->di, filename);
|
||||||
|
apm3_xi_config_load(&cfg->xi, filename);
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#define APM3_BUTTON_COUNT 8
|
#define APM3_BUTTON_COUNT 8
|
||||||
|
|
||||||
struct apm3_io_config {
|
struct apm3_di_config {
|
||||||
uint8_t vk_test;
|
wchar_t device_name[64];
|
||||||
uint8_t vk_service;
|
uint8_t start;
|
||||||
uint8_t vk_coin;
|
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_start;
|
||||||
uint8_t vk_home;
|
uint8_t vk_home;
|
||||||
|
|
||||||
@ -23,6 +29,20 @@ struct apm3_io_config {
|
|||||||
uint8_t vk_buttons[APM3_BUTTON_COUNT];
|
uint8_t vk_buttons[APM3_BUTTON_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
void apm3_io_config_load(
|
struct apm3_io_config {
|
||||||
struct apm3_io_config *cfg,
|
uint8_t vk_test;
|
||||||
const wchar_t *filename);
|
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);
|
@ -4,12 +4,28 @@ apm3io_lib = static_library(
|
|||||||
include_directories : inc,
|
include_directories : inc,
|
||||||
implicit_include_directories : false,
|
implicit_include_directories : false,
|
||||||
dependencies : [
|
dependencies : [
|
||||||
|
dinput8_lib,
|
||||||
|
dxguid_lib,
|
||||||
xinput_lib,
|
xinput_lib,
|
||||||
],
|
],
|
||||||
|
link_with : [
|
||||||
|
util_lib,
|
||||||
|
],
|
||||||
sources : [
|
sources : [
|
||||||
'apm3io.c',
|
|
||||||
'apm3io.h',
|
'apm3io.h',
|
||||||
|
'backend.h',
|
||||||
'config.c',
|
'config.c',
|
||||||
'config.h',
|
'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);
|
Reference in New Issue
Block a user