Remove dead #if 0'd code in math.h On amd64, page_size == 4096 constant, on amd64 w/ win32, allocation_granularity == 65536. These values for x86 windows havent changed over the last 20 years so this is probably safe and gives a modest code size reduction Enable XE_USE_KUSER_SHARED. This sources host time from KUSER_SHARED instead of from QueryPerformanceCounter, which is far faster, but only has a granularity of 100 nanoseconds. In some games seemingly random crashes were happening that were hard to trace because the faulting thread was actually not the one that was misbehaving, another threads stack was underflowing into the faulting thread. Added a bunch of code to synchronize the guest stack and host stack so that if a guest longjmps the host's stack will be adjusted. Changes were also made to allow the guest to call into a piece of an existing x64 function. This synchronization might have a slight performance impact on lower end cpus, to disable it set enable_host_guest_stack_synchronization to false. It is possible it may have introduced regressions, but i dont know of any yet So far, i know the synchronization change fixes the "hub crash" in super sonic and allows the game "london 2012" to go ingame. Removed emit_useless_fpscr_updates, not emitting these updates breaks the raiden game MapGuestAddressToMachineCode now returns nullptr if no address was found, instead of the start of the function add Processor::LookupModule Add Backend::DeinitializeBackendContext Use WriteRegisterRangeFromRing_WithKnownBound<0, 0xFFFF> in WriteRegisterRangeFromRing for inlining (previously regressed on performance of ExecutePacketType0) add notes about flags that trap in XamInputGetCapabilities 0 == 3 in XamInputGetCapabilities Name arg 2 of XamInputSetState PrefetchW in critical section kernel funcs if available & doing cmpxchg Add terminated field to X_KTHREAD, set it on termination Expanded the logic of NtResumeThread/NtSuspendThread to include checking the type of the handle (in release, LookupObject doesnt seem to do anything with the type) and returning X_STATUS_OBJECT_TYPE_MISMATCH if invalid. Do termination check in NtSuspendThread. Add basic host exception messagebox, need to flesh it out more (maybe use the new stack tracking stuff if on guest thrd?) Add rdrand patching hack, mostly affects users with nvidia cards who have many threads on zen Use page_size_shift in more places Once again disable precompilation! Raiden is mostly weird ppc asm which probably breaks the precompilation. The code is still useful for running the compiler over the whole of an xex in debug to test for issues "Fix" debug console, we were checking the cvar before any cvars were loaded, and the condition it checks in AttachConsole is somehow always false Remove dead #if 0'd code in math.h On amd64, page_size == 4096 constant, on amd64 w/ win32, allocation_granularity == 65536. These values for x86 windows havent changed over the last 20 years so this is probably safe and gives a modest code size reduction Enable XE_USE_KUSER_SHARED. This sources host time from KUSER_SHARED instead of from QueryPerformanceCounter, which is far faster, but only has a granularity of 100 nanoseconds. In some games seemingly random crashes were happening that were hard to trace because the faulting thread was actually not the one that was misbehaving, another threads stack was underflowing into the faulting thread. Added a bunch of code to synchronize the guest stack and host stack so that if a guest longjmps the host's stack will be adjusted. Changes were also made to allow the guest to call into a piece of an existing x64 function. This synchronization might have a slight performance impact on lower end cpus, to disable it set enable_host_guest_stack_synchronization to false. It is possible it may have introduced regressions, but i dont know of any yet So far, i know the synchronization change fixes the "hub crash" in super sonic and allows the game "london 2012" to go ingame. Removed emit_useless_fpscr_updates, not emitting these updates breaks the raiden game MapGuestAddressToMachineCode now returns nullptr if no address was found, instead of the start of the function add Processor::LookupModule Add Backend::DeinitializeBackendContext Use WriteRegisterRangeFromRing_WithKnownBound<0, 0xFFFF> in WriteRegisterRangeFromRing for inlining (previously regressed on performance of ExecutePacketType0) add notes about flags that trap in XamInputGetCapabilities 0 == 3 in XamInputGetCapabilities Name arg 2 of XamInputSetState PrefetchW in critical section kernel funcs if available & doing cmpxchg Add terminated field to X_KTHREAD, set it on termination Expanded the logic of NtResumeThread/NtSuspendThread to include checking the type of the handle (in release, LookupObject doesnt seem to do anything with the type) and returning X_STATUS_OBJECT_TYPE_MISMATCH if invalid. Do termination check in NtSuspendThread. Add basic host exception messagebox, need to flesh it out more (maybe use the new stack tracking stuff if on guest thrd?) Add rdrand patching hack, mostly affects users with nvidia cards who have many threads on zen Use page_size_shift in more places Once again disable precompilation! Raiden is mostly weird ppc asm which probably breaks the precompilation. The code is still useful for running the compiler over the whole of an xex in debug to test for issues
251 lines
8.3 KiB
C++
251 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_ANY_USER = 1 << 30;
|
|
|
|
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);
|
|
|
|
// 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) {
|
|
unsigned flags = _flags;
|
|
//chrispy: actually, it appears that caps is never checked for null, it is memset at the start regardless
|
|
if (!caps) {
|
|
return X_ERROR_BAD_ARGUMENTS;
|
|
}
|
|
if ((flags & 0x40000000) != 0) {
|
|
//should trap
|
|
}
|
|
|
|
if ((flags & 4) != 0) {
|
|
//should trap
|
|
}
|
|
if (!flags) {
|
|
flags = 3;
|
|
}
|
|
|
|
|
|
if ((flags & 0xFF) && (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(XamInputGetCapabilities, kInput, kSketchy);
|
|
|
|
dword_result_t XamInputGetCapabilitiesEx_entry(
|
|
dword_t unk, dword_t user_index, dword_t flags,
|
|
pointer_t<X_INPUT_CAPABILITIES> caps) {
|
|
if (!caps) {
|
|
return X_ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
if ((flags & 0xFF) && (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.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 & 0xFF) && (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 & 0xFF) && (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 & 0xFF) && (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);
|