252 lines
8.3 KiB
C++
252 lines
8.3 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2022 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "xenia/base/logging.h"
|
|
#include "xenia/emulator.h"
|
|
#include "xenia/hid/input.h"
|
|
#include "xenia/hid/input_system.h"
|
|
#include "xenia/kernel/kernel_state.h"
|
|
#include "xenia/kernel/util/shim_utils.h"
|
|
#include "xenia/kernel/xam/xam_private.h"
|
|
#include "xenia/xbox.h"
|
|
|
|
namespace xe {
|
|
namespace kernel {
|
|
namespace xam {
|
|
|
|
using xe::hid::X_INPUT_CAPABILITIES;
|
|
using xe::hid::X_INPUT_KEYSTROKE;
|
|
using xe::hid::X_INPUT_STATE;
|
|
using xe::hid::X_INPUT_VIBRATION;
|
|
|
|
constexpr uint32_t XINPUT_FLAG_GAMEPAD = 0x01;
|
|
constexpr uint32_t XINPUT_FLAG_KEYBOARD = 0x02;
|
|
constexpr uint32_t XINPUT_FLAG_MIC = 0x20; // Based on "karaoke" titles
|
|
constexpr uint32_t XINPUT_FLAG_ANYDEVICE = 0xFF;
|
|
constexpr uint32_t XINPUT_FLAG_ANY_USER = 1 << 30;
|
|
|
|
dword_result_t XAutomationpUnbindController_entry(dword_t user_index) {
|
|
if (user_index > 4) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
DECLARE_XAM_EXPORT1(XAutomationpUnbindController, kInput, kStub);
|
|
|
|
void XamResetInactivity_entry() {
|
|
// Do we need to do anything?
|
|
}
|
|
DECLARE_XAM_EXPORT1(XamResetInactivity, kInput, kStub);
|
|
|
|
dword_result_t XamEnableInactivityProcessing_entry(dword_t unk,
|
|
dword_t enable) {
|
|
return X_ERROR_SUCCESS;
|
|
}
|
|
DECLARE_XAM_EXPORT1(XamEnableInactivityProcessing, kInput, kStub);
|
|
|
|
dword_result_t XamInputGetCapabilitiesEx_entry(
|
|
dword_t unk, dword_t user_index, dword_t flags,
|
|
pointer_t<X_INPUT_CAPABILITIES> caps) {
|
|
if (unk > 1) {
|
|
return X_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
// Fail-safe check
|
|
if (!caps) {
|
|
return X_ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
caps.Zero();
|
|
|
|
if ((flags & XINPUT_FLAG_ANY_USER) != 0) {
|
|
// should trap
|
|
}
|
|
|
|
if ((flags & 4) != 0) {
|
|
// should trap
|
|
}
|
|
|
|
if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
|
|
// Ignore any query for other types of devices.
|
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
uint32_t actual_user_index = user_index;
|
|
if ((actual_user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) {
|
|
// Always pin user to 0.
|
|
actual_user_index = 0;
|
|
}
|
|
|
|
auto input_system = kernel_state()->emulator()->input_system();
|
|
auto lock = input_system->lock();
|
|
return input_system->GetCapabilities(actual_user_index, flags, caps);
|
|
}
|
|
DECLARE_XAM_EXPORT1(XamInputGetCapabilitiesEx, kInput, kSketchy);
|
|
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetcapabilities(v=vs.85).aspx
|
|
dword_result_t XamInputGetCapabilities_entry(
|
|
dword_t user_index, dword_t flags, pointer_t<X_INPUT_CAPABILITIES> caps) {
|
|
// chrispy: actually, it appears that caps is never checked for null, it is
|
|
// memset at the start regardless
|
|
return XamInputGetCapabilitiesEx_entry(1, user_index, flags, caps);
|
|
}
|
|
DECLARE_XAM_EXPORT1(XamInputGetCapabilities, kInput, kSketchy);
|
|
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetstate(v=vs.85).aspx
|
|
dword_result_t XamInputGetState_entry(dword_t user_index, dword_t flags,
|
|
pointer_t<X_INPUT_STATE> input_state) {
|
|
if (input_state) {
|
|
memset((void*)input_state.host_address(), 0, sizeof(X_INPUT_STATE));
|
|
}
|
|
if (user_index >= 4) {
|
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
// Games call this with a NULL state ptr, probably as a query.
|
|
|
|
if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
|
|
// Ignore any query for other types of devices.
|
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
uint32_t actual_user_index = user_index;
|
|
// chrispy: change this, logic is not right
|
|
if ((actual_user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) {
|
|
// Always pin user to 0.
|
|
actual_user_index = 0;
|
|
}
|
|
|
|
auto input_system = kernel_state()->emulator()->input_system();
|
|
auto lock = input_system->lock();
|
|
return input_system->GetState(user_index, input_state);
|
|
}
|
|
DECLARE_XAM_EXPORT2(XamInputGetState, kInput, kImplemented, kHighFrequency);
|
|
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputsetstate(v=vs.85).aspx
|
|
dword_result_t XamInputSetState_entry(
|
|
dword_t user_index,
|
|
dword_t flags, /* flags, as far as i can see, is not used*/
|
|
pointer_t<X_INPUT_VIBRATION> vibration) {
|
|
if (user_index >= 4) {
|
|
return X_E_DEVICE_NOT_CONNECTED;
|
|
}
|
|
if (!vibration) {
|
|
return X_ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
uint32_t actual_user_index = user_index;
|
|
if ((user_index & 0xFF) == 0xFF) {
|
|
// Always pin user to 0.
|
|
actual_user_index = 0;
|
|
}
|
|
|
|
auto input_system = kernel_state()->emulator()->input_system();
|
|
auto lock = input_system->lock();
|
|
return input_system->SetState(user_index, vibration);
|
|
}
|
|
DECLARE_XAM_EXPORT1(XamInputSetState, kInput, kImplemented);
|
|
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetkeystroke(v=vs.85).aspx
|
|
dword_result_t XamInputGetKeystroke_entry(
|
|
dword_t user_index, dword_t flags, pointer_t<X_INPUT_KEYSTROKE> keystroke) {
|
|
// https://github.com/CodeAsm/ffplay360/blob/master/Common/AtgXime.cpp
|
|
// user index = index or XUSER_INDEX_ANY
|
|
// flags = XINPUT_FLAG_GAMEPAD (| _ANYUSER | _ANYDEVICE)
|
|
|
|
if (!keystroke) {
|
|
return X_ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
|
|
// Ignore any query for other types of devices.
|
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
uint32_t actual_user_index = user_index;
|
|
if ((actual_user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) {
|
|
// Always pin user to 0.
|
|
actual_user_index = 0;
|
|
}
|
|
|
|
auto input_system = kernel_state()->emulator()->input_system();
|
|
auto lock = input_system->lock();
|
|
return input_system->GetKeystroke(user_index, flags, keystroke);
|
|
}
|
|
DECLARE_XAM_EXPORT1(XamInputGetKeystroke, kInput, kImplemented);
|
|
|
|
// Same as non-ex, just takes a pointer to user index.
|
|
dword_result_t XamInputGetKeystrokeEx_entry(
|
|
lpdword_t user_index_ptr, dword_t flags,
|
|
pointer_t<X_INPUT_KEYSTROKE> keystroke) {
|
|
if (!keystroke) {
|
|
return X_ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) {
|
|
// Ignore any query for other types of devices.
|
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
uint32_t user_index = *user_index_ptr;
|
|
auto input_system = kernel_state()->emulator()->input_system();
|
|
auto lock = input_system->lock();
|
|
if ((user_index & 0xFF) == 0xFF) {
|
|
// Always pin user to 0.
|
|
user_index = 0;
|
|
}
|
|
|
|
if (flags & XINPUT_FLAG_ANY_USER) {
|
|
// That flag means we should iterate over every connected controller and
|
|
// check which one have pending request.
|
|
auto result = X_ERROR_DEVICE_NOT_CONNECTED;
|
|
for (uint32_t i = 0; i < 4; i++) {
|
|
auto result = input_system->GetKeystroke(i, flags, keystroke);
|
|
|
|
// Return result from first user that have pending request
|
|
if (result == X_ERROR_SUCCESS) {
|
|
*user_index_ptr = keystroke->user_index;
|
|
return result;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto result = input_system->GetKeystroke(user_index, flags, keystroke);
|
|
|
|
if (XSUCCEEDED(result)) {
|
|
*user_index_ptr = keystroke->user_index;
|
|
}
|
|
return result;
|
|
}
|
|
DECLARE_XAM_EXPORT1(XamInputGetKeystrokeEx, kInput, kImplemented);
|
|
|
|
X_HRESULT_result_t XamUserGetDeviceContext_entry(dword_t user_index,
|
|
dword_t unk,
|
|
lpdword_t out_ptr) {
|
|
// Games check the result - usually with some masking.
|
|
// If this function fails they assume zero, so let's fail AND
|
|
// set zero just to be safe.
|
|
*out_ptr = 0;
|
|
if (kernel_state()->IsUserSignedIn(user_index) ||
|
|
(user_index & 0xFF) == 0xFF) {
|
|
*out_ptr = (uint32_t)user_index;
|
|
return X_E_SUCCESS;
|
|
} else {
|
|
return X_E_DEVICE_NOT_CONNECTED;
|
|
}
|
|
}
|
|
DECLARE_XAM_EXPORT1(XamUserGetDeviceContext, kInput, kStub);
|
|
|
|
} // namespace xam
|
|
} // namespace kernel
|
|
} // namespace xe
|
|
|
|
DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Input);
|