Merge remote-tracking branch 'GliniakRepo/local_multiplayer' into canary_experimental
This commit is contained in:
commit
7c2cd16548
|
@ -28,6 +28,18 @@ void InputSystem::AddDriver(std::unique_ptr<InputDriver> driver) {
|
||||||
drivers_.push_back(std::move(driver));
|
drivers_.push_back(std::move(driver));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputSystem::UpdateUsedSlot(uint8_t slot, bool connected) {
|
||||||
|
if (slot == 0xFF) {
|
||||||
|
slot = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connected) {
|
||||||
|
connected_slot |= (1 << slot);
|
||||||
|
} else {
|
||||||
|
connected_slot &= ~(1 << slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
X_RESULT InputSystem::GetCapabilities(uint32_t user_index, uint32_t flags,
|
X_RESULT InputSystem::GetCapabilities(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_CAPABILITIES* out_caps) {
|
X_INPUT_CAPABILITIES* out_caps) {
|
||||||
SCOPE_profile_cpu_f("hid");
|
SCOPE_profile_cpu_f("hid");
|
||||||
|
@ -39,9 +51,11 @@ X_RESULT InputSystem::GetCapabilities(uint32_t user_index, uint32_t flags,
|
||||||
any_connected = true;
|
any_connected = true;
|
||||||
}
|
}
|
||||||
if (result == X_ERROR_SUCCESS) {
|
if (result == X_ERROR_SUCCESS) {
|
||||||
|
UpdateUsedSlot(user_index, any_connected);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UpdateUsedSlot(user_index, any_connected);
|
||||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,9 +69,11 @@ X_RESULT InputSystem::GetState(uint32_t user_index, X_INPUT_STATE* out_state) {
|
||||||
any_connected = true;
|
any_connected = true;
|
||||||
}
|
}
|
||||||
if (result == X_ERROR_SUCCESS) {
|
if (result == X_ERROR_SUCCESS) {
|
||||||
|
UpdateUsedSlot(user_index, any_connected);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UpdateUsedSlot(user_index, any_connected);
|
||||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,9 +88,11 @@ X_RESULT InputSystem::SetState(uint32_t user_index,
|
||||||
any_connected = true;
|
any_connected = true;
|
||||||
}
|
}
|
||||||
if (result == X_ERROR_SUCCESS) {
|
if (result == X_ERROR_SUCCESS) {
|
||||||
|
UpdateUsedSlot(user_index, any_connected);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UpdateUsedSlot(user_index, any_connected);
|
||||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,9 +107,11 @@ X_RESULT InputSystem::GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
any_connected = true;
|
any_connected = true;
|
||||||
}
|
}
|
||||||
if (result == X_ERROR_SUCCESS || result == X_ERROR_EMPTY) {
|
if (result == X_ERROR_SUCCESS || result == X_ERROR_EMPTY) {
|
||||||
|
UpdateUsedSlot(user_index, any_connected);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UpdateUsedSlot(user_index, any_connected);
|
||||||
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,8 @@ class InputSystem {
|
||||||
X_INPUT_KEYSTROKE* out_keystroke);
|
X_INPUT_KEYSTROKE* out_keystroke);
|
||||||
|
|
||||||
void ToggleVibration();
|
void ToggleVibration();
|
||||||
|
void UpdateUsedSlot(uint8_t slot, bool connected);
|
||||||
|
uint8_t GetConnectedSlots() const { return connected_slot; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xe::ui::Window* window_ = nullptr;
|
xe::ui::Window* window_ = nullptr;
|
||||||
|
@ -52,6 +54,7 @@ class InputSystem {
|
||||||
std::vector<std::unique_ptr<InputDriver>> drivers_;
|
std::vector<std::unique_ptr<InputDriver>> drivers_;
|
||||||
|
|
||||||
X_INPUT_VIBRATION ModifyVibrationLevel(X_INPUT_VIBRATION* vibration);
|
X_INPUT_VIBRATION ModifyVibrationLevel(X_INPUT_VIBRATION* vibration);
|
||||||
|
uint8_t connected_slot = 0b0001;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace hid
|
} // namespace hid
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
|
#include "xenia/hid/input_system.h"
|
||||||
#include "xenia/kernel/user_module.h"
|
#include "xenia/kernel/user_module.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xam/xam_module.h"
|
#include "xenia/kernel/xam/xam_module.h"
|
||||||
|
@ -49,7 +50,7 @@ KernelState::KernelState(Emulator* emulator)
|
||||||
file_system_ = emulator->file_system();
|
file_system_ = emulator->file_system();
|
||||||
|
|
||||||
app_manager_ = std::make_unique<xam::AppManager>();
|
app_manager_ = std::make_unique<xam::AppManager>();
|
||||||
user_profile_ = std::make_unique<xam::UserProfile>();
|
user_profiles_.emplace(0, std::make_unique<xam::UserProfile>(0));
|
||||||
|
|
||||||
auto content_root = emulator_->content_root();
|
auto content_root = emulator_->content_root();
|
||||||
if (!content_root.empty()) {
|
if (!content_root.empty()) {
|
||||||
|
@ -886,5 +887,27 @@ bool KernelState::Restore(ByteStream* stream) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t KernelState::GetConnectedUsers() const {
|
||||||
|
return emulator_->input_system()->GetConnectedSlots();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KernelState::UpdateUsedUserProfiles() {
|
||||||
|
const uint8_t used_slots_bitmask = GetConnectedUsers();
|
||||||
|
|
||||||
|
for (uint8_t i = 1; i < 4; i++) {
|
||||||
|
bool is_used = used_slots_bitmask & (1 << i);
|
||||||
|
|
||||||
|
if (IsUserSignedIn(i) && !is_used) {
|
||||||
|
user_profiles_.erase(i);
|
||||||
|
BroadcastNotification(0x12, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsUserSignedIn(i) && is_used) {
|
||||||
|
user_profiles_.emplace(i, std::make_unique<xam::UserProfile>(i));
|
||||||
|
BroadcastNotification(0x12, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -104,7 +104,17 @@ class KernelState {
|
||||||
xam::ContentManager* content_manager() const {
|
xam::ContentManager* content_manager() const {
|
||||||
return content_manager_.get();
|
return content_manager_.get();
|
||||||
}
|
}
|
||||||
xam::UserProfile* user_profile() const { return user_profile_.get(); }
|
|
||||||
|
uint8_t GetConnectedUsers() const;
|
||||||
|
void UpdateUsedUserProfiles();
|
||||||
|
|
||||||
|
bool IsUserSignedIn(uint8_t index) const {
|
||||||
|
return user_profiles_.find(index) != user_profiles_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
xam::UserProfile* user_profile(uint8_t index) const {
|
||||||
|
return user_profiles_.at(index).get();
|
||||||
|
}
|
||||||
|
|
||||||
// Access must be guarded by the global critical region.
|
// Access must be guarded by the global critical region.
|
||||||
util::ObjectTable* object_table() { return &object_table_; }
|
util::ObjectTable* object_table() { return &object_table_; }
|
||||||
|
@ -206,7 +216,7 @@ class KernelState {
|
||||||
|
|
||||||
std::unique_ptr<xam::AppManager> app_manager_;
|
std::unique_ptr<xam::AppManager> app_manager_;
|
||||||
std::unique_ptr<xam::ContentManager> content_manager_;
|
std::unique_ptr<xam::ContentManager> content_manager_;
|
||||||
std::unique_ptr<xam::UserProfile> user_profile_;
|
std::map<uint8_t, std::unique_ptr<xam::UserProfile>> user_profiles_;
|
||||||
|
|
||||||
xe::global_critical_region global_critical_region_;
|
xe::global_critical_region global_critical_region_;
|
||||||
|
|
||||||
|
|
|
@ -312,7 +312,7 @@ X_RESULT ContentManager::DeleteContent(const XCONTENT_AGGREGATE_DATA& data) {
|
||||||
|
|
||||||
std::filesystem::path ContentManager::ResolveGameUserContentPath() {
|
std::filesystem::path ContentManager::ResolveGameUserContentPath() {
|
||||||
auto title_id = fmt::format("{:08X}", kernel_state_->title_id());
|
auto title_id = fmt::format("{:08X}", kernel_state_->title_id());
|
||||||
auto user_name = xe::to_path(kernel_state_->user_profile()->name());
|
auto user_name = xe::to_path(kernel_state_->user_profile(0)->name());
|
||||||
|
|
||||||
// Per-game per-profile data location:
|
// Per-game per-profile data location:
|
||||||
// content_root/title_id/profile/user_name
|
// content_root/title_id/profile/user_name
|
||||||
|
|
|
@ -19,12 +19,15 @@ namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace xam {
|
namespace xam {
|
||||||
|
|
||||||
UserProfile::UserProfile() {
|
UserProfile::UserProfile(uint8_t index) {
|
||||||
// 58410A1F checks the user XUID against a mask of 0x00C0000000000000 (3<<54),
|
// 58410A1F checks the user XUID against a mask of 0x00C0000000000000 (3<<54),
|
||||||
// if non-zero, it prevents the user from playing the game.
|
// if non-zero, it prevents the user from playing the game.
|
||||||
// "You do not have permissions to perform this operation."
|
// "You do not have permissions to perform this operation."
|
||||||
xuid_ = 0xB13EBABEBABEBABE;
|
xuid_ = 0xB13EBABEBABEBABE + index;
|
||||||
name_ = "User";
|
name_ = "User";
|
||||||
|
if (index) {
|
||||||
|
name_ = "User_" + std::to_string(index);
|
||||||
|
}
|
||||||
|
|
||||||
// https://cs.rin.ru/forum/viewtopic.php?f=38&t=60668&hilit=gfwl+live&start=195
|
// https://cs.rin.ru/forum/viewtopic.php?f=38&t=60668&hilit=gfwl+live&start=195
|
||||||
// https://github.com/arkem/py360/blob/master/py360/constants.py
|
// https://github.com/arkem/py360/blob/master/py360/constants.py
|
||||||
|
|
|
@ -222,7 +222,7 @@ class UserProfile {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
UserProfile();
|
UserProfile(uint8_t index);
|
||||||
|
|
||||||
uint64_t xuid() const { return xuid_; }
|
uint64_t xuid() const { return xuid_; }
|
||||||
std::string name() const { return name_; }
|
std::string name() const { return name_; }
|
||||||
|
|
|
@ -322,7 +322,12 @@ dword_result_t XamContentGetCreator_entry(dword_t user_index,
|
||||||
// User always creates saves.
|
// User always creates saves.
|
||||||
*is_creator_ptr = 1;
|
*is_creator_ptr = 1;
|
||||||
if (creator_xuid_ptr) {
|
if (creator_xuid_ptr) {
|
||||||
*creator_xuid_ptr = kernel_state()->user_profile()->xuid();
|
if (kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
|
const auto& user_profile = kernel_state()->user_profile(user_index);
|
||||||
|
*creator_xuid_ptr = user_profile->xuid();
|
||||||
|
} else {
|
||||||
|
result = X_ERROR_NO_SUCH_USER;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*is_creator_ptr = 0;
|
*is_creator_ptr = 0;
|
||||||
|
|
|
@ -34,8 +34,8 @@ X_HRESULT_result_t XamUserGetXUID_entry(dword_t user_index, dword_t type_mask,
|
||||||
uint32_t result = X_E_NO_SUCH_USER;
|
uint32_t result = X_E_NO_SUCH_USER;
|
||||||
uint64_t xuid = 0;
|
uint64_t xuid = 0;
|
||||||
if (user_index < 4) {
|
if (user_index < 4) {
|
||||||
if (user_index == 0) {
|
if (kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile = kernel_state()->user_profile();
|
const auto& user_profile = kernel_state()->user_profile(user_index);
|
||||||
auto type = user_profile->type() & type_mask;
|
auto type = user_profile->type() & type_mask;
|
||||||
if (type & (2 | 4)) {
|
if (type & (2 | 4)) {
|
||||||
// maybe online profile?
|
// maybe online profile?
|
||||||
|
@ -60,8 +60,8 @@ dword_result_t XamUserGetSigninState_entry(dword_t user_index) {
|
||||||
xe::threading::MaybeYield();
|
xe::threading::MaybeYield();
|
||||||
uint32_t signin_state = 0;
|
uint32_t signin_state = 0;
|
||||||
if (user_index < 4) {
|
if (user_index < 4) {
|
||||||
if (user_index == 0) {
|
if (kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile = kernel_state()->user_profile();
|
const auto& user_profile = kernel_state()->user_profile(user_index);
|
||||||
signin_state = user_profile->signin_state();
|
signin_state = user_profile->signin_state();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,15 +87,21 @@ X_HRESULT_result_t XamUserGetSigninInfo_entry(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memset(info, 0, sizeof(X_USER_SIGNIN_INFO));
|
std::memset(info, 0, sizeof(X_USER_SIGNIN_INFO));
|
||||||
if (user_index) {
|
if (user_index > 3) {
|
||||||
return X_E_NO_SUCH_USER;
|
return X_E_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& user_profile = kernel_state()->user_profile();
|
kernel_state()->UpdateUsedUserProfiles();
|
||||||
|
|
||||||
|
if (kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
|
const auto& user_profile = kernel_state()->user_profile(user_index);
|
||||||
info->xuid = user_profile->xuid();
|
info->xuid = user_profile->xuid();
|
||||||
info->signin_state = user_profile->signin_state();
|
info->signin_state = user_profile->signin_state();
|
||||||
xe::string_util::copy_truncating(info->name, user_profile->name(),
|
xe::string_util::copy_truncating(info->name, user_profile->name(),
|
||||||
xe::countof(info->name));
|
xe::countof(info->name));
|
||||||
|
} else {
|
||||||
|
return X_E_NO_SUCH_USER;
|
||||||
|
}
|
||||||
return X_E_SUCCESS;
|
return X_E_SUCCESS;
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamUserGetSigninInfo, kUserProfiles, kImplemented);
|
DECLARE_XAM_EXPORT1(XamUserGetSigninInfo, kUserProfiles, kImplemented);
|
||||||
|
@ -106,14 +112,14 @@ dword_result_t XamUserGetName_entry(dword_t user_index, lpstring_t buffer,
|
||||||
return X_E_INVALIDARG;
|
return X_E_INVALIDARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_index) {
|
if (kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
|
const auto& user_profile = kernel_state()->user_profile(user_index);
|
||||||
|
const auto& user_name = user_profile->name();
|
||||||
|
xe::string_util::copy_truncating(
|
||||||
|
buffer, user_name, std::min(buffer_len.value(), uint32_t(16)));
|
||||||
|
} else {
|
||||||
return X_E_NO_SUCH_USER;
|
return X_E_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& user_profile = kernel_state()->user_profile();
|
|
||||||
const auto& user_name = user_profile->name();
|
|
||||||
xe::string_util::copy_truncating(buffer, user_name,
|
|
||||||
std::min(buffer_len.value(), uint32_t(16)));
|
|
||||||
return X_E_SUCCESS;
|
return X_E_SUCCESS;
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamUserGetName, kUserProfiles, kImplemented);
|
DECLARE_XAM_EXPORT1(XamUserGetName, kUserProfiles, kImplemented);
|
||||||
|
@ -125,15 +131,15 @@ dword_result_t XamUserGetGamerTag_entry(dword_t user_index,
|
||||||
return X_E_INVALIDARG;
|
return X_E_INVALIDARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_index) {
|
|
||||||
return X_E_NO_SUCH_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!buffer || buffer_len < 16) {
|
if (!buffer || buffer_len < 16) {
|
||||||
return X_E_INVALIDARG;
|
return X_E_INVALIDARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& user_profile = kernel_state()->user_profile();
|
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
|
return X_E_INVALIDARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& user_profile = kernel_state()->user_profile(user_index);
|
||||||
auto user_name = xe::to_utf16(user_profile->name());
|
auto user_name = xe::to_utf16(user_profile->name());
|
||||||
xe::string_util::copy_and_swap_truncating(
|
xe::string_util::copy_and_swap_truncating(
|
||||||
buffer, user_name, std::min(buffer_len.value(), uint32_t(16)));
|
buffer, user_name, std::min(buffer_len.value(), uint32_t(16)));
|
||||||
|
@ -161,11 +167,13 @@ uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
|
||||||
assert_true(xuid_count == 1);
|
assert_true(xuid_count == 1);
|
||||||
assert_not_null(xuids);
|
assert_not_null(xuids);
|
||||||
// TODO(gibbed): allow proper lookup of arbitrary XUIDs
|
// TODO(gibbed): allow proper lookup of arbitrary XUIDs
|
||||||
const auto& user_profile = kernel_state()->user_profile();
|
|
||||||
assert_true(static_cast<uint64_t>(xuids[0]) == user_profile->xuid());
|
|
||||||
// TODO(gibbed): we assert here, but in case a title passes xuid_count > 1
|
// TODO(gibbed): we assert here, but in case a title passes xuid_count > 1
|
||||||
// until it's implemented for release builds...
|
// until it's implemented for release builds...
|
||||||
xuid_count = 1;
|
xuid_count = 1;
|
||||||
|
if (kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
|
const auto& user_profile = kernel_state()->user_profile(user_index);
|
||||||
|
assert_true(static_cast<uint64_t>(xuids[0]) == user_profile->xuid());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
assert_zero(unk); // probably flags
|
assert_zero(unk); // probably flags
|
||||||
|
|
||||||
|
@ -216,9 +224,7 @@ uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
|
||||||
|
|
||||||
// Title ID = 0 means us.
|
// Title ID = 0 means us.
|
||||||
// 0xfffe07d1 = profile?
|
// 0xfffe07d1 = profile?
|
||||||
|
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
if (!xuids && user_index) {
|
|
||||||
// Only support user 0.
|
|
||||||
if (overlapped) {
|
if (overlapped) {
|
||||||
kernel_state()->CompleteOverlappedImmediate(
|
kernel_state()->CompleteOverlappedImmediate(
|
||||||
kernel_state()->memory()->HostToGuestVirtual(overlapped),
|
kernel_state()->memory()->HostToGuestVirtual(overlapped),
|
||||||
|
@ -228,7 +234,7 @@ uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& user_profile = kernel_state()->user_profile();
|
const auto& user_profile = kernel_state()->user_profile(user_index);
|
||||||
|
|
||||||
// First call asks for size (fill buffer_size_ptr).
|
// First call asks for size (fill buffer_size_ptr).
|
||||||
// Second call asks for buffer contents with that size.
|
// Second call asks for buffer contents with that size.
|
||||||
|
@ -329,18 +335,18 @@ dword_result_t XamUserWriteProfileSettings_entry(
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_index) {
|
// Skip writing data about users with id != 0 they're not supported
|
||||||
// Only support user 0.
|
if (user_index > 0) {
|
||||||
if (overlapped) {
|
if (overlapped) {
|
||||||
kernel_state()->CompleteOverlappedImmediate(overlapped,
|
kernel_state()->CompleteOverlappedImmediate(
|
||||||
|
kernel_state()->memory()->HostToGuestVirtual(overlapped),
|
||||||
X_ERROR_NO_SUCH_USER);
|
X_ERROR_NO_SUCH_USER);
|
||||||
return X_ERROR_IO_PENDING;
|
return X_ERROR_IO_PENDING;
|
||||||
}
|
}
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update and save settings.
|
// Update and save settings.
|
||||||
const auto& user_profile = kernel_state()->user_profile();
|
const auto& user_profile = kernel_state()->user_profile(user_index);
|
||||||
|
|
||||||
for (uint32_t n = 0; n < setting_count; ++n) {
|
for (uint32_t n = 0; n < setting_count; ++n) {
|
||||||
const X_USER_PROFILE_SETTING& setting = settings[n];
|
const X_USER_PROFILE_SETTING& setting = settings[n];
|
||||||
|
@ -405,7 +411,7 @@ dword_result_t XamUserCheckPrivilege_entry(dword_t user_index, dword_t mask,
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_index) {
|
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,7 +424,7 @@ DECLARE_XAM_EXPORT1(XamUserCheckPrivilege, kUserProfiles, kStub);
|
||||||
|
|
||||||
dword_result_t XamUserContentRestrictionGetFlags_entry(dword_t user_index,
|
dword_result_t XamUserContentRestrictionGetFlags_entry(dword_t user_index,
|
||||||
lpdword_t out_flags) {
|
lpdword_t out_flags) {
|
||||||
if (user_index) {
|
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +438,7 @@ dword_result_t XamUserContentRestrictionGetRating_entry(dword_t user_index,
|
||||||
dword_t unk1,
|
dword_t unk1,
|
||||||
lpdword_t out_unk2,
|
lpdword_t out_unk2,
|
||||||
lpdword_t out_unk3) {
|
lpdword_t out_unk3) {
|
||||||
if (user_index) {
|
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +472,8 @@ dword_result_t XamUserGetMembershipTier_entry(dword_t user_index) {
|
||||||
if (user_index >= 4) {
|
if (user_index >= 4) {
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
if (user_index) {
|
|
||||||
|
if (kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
return 6 /* 6 appears to be Gold */;
|
return 6 /* 6 appears to be Gold */;
|
||||||
|
@ -482,8 +489,8 @@ dword_result_t XamUserAreUsersFriends_entry(dword_t user_index, dword_t unk1,
|
||||||
if (user_index >= 4) {
|
if (user_index >= 4) {
|
||||||
result = X_ERROR_INVALID_PARAMETER;
|
result = X_ERROR_INVALID_PARAMETER;
|
||||||
} else {
|
} else {
|
||||||
if (user_index == 0) {
|
if (kernel_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile = kernel_state()->user_profile();
|
const auto& user_profile = kernel_state()->user_profile(user_index);
|
||||||
if (user_profile->signin_state() == 0) {
|
if (user_profile->signin_state() == 0) {
|
||||||
result = X_ERROR_NOT_LOGGED_ON;
|
result = X_ERROR_NOT_LOGGED_ON;
|
||||||
} else {
|
} else {
|
||||||
|
@ -518,13 +525,16 @@ dword_result_t XamUserAreUsersFriends_entry(dword_t user_index, dword_t unk1,
|
||||||
DECLARE_XAM_EXPORT1(XamUserAreUsersFriends, kUserProfiles, kStub);
|
DECLARE_XAM_EXPORT1(XamUserAreUsersFriends, kUserProfiles, kStub);
|
||||||
|
|
||||||
dword_result_t XamShowSigninUI_entry(dword_t unk, dword_t unk_mask) {
|
dword_result_t XamShowSigninUI_entry(dword_t unk, dword_t unk_mask) {
|
||||||
|
kernel_state()->UpdateUsedUserProfiles();
|
||||||
// Mask values vary. Probably matching user types? Local/remote?
|
// Mask values vary. Probably matching user types? Local/remote?
|
||||||
|
|
||||||
// To fix game modes that display a 4 profile signin UI (even if playing
|
|
||||||
// alone):
|
|
||||||
// XN_SYS_SIGNINCHANGED
|
|
||||||
kernel_state()->BroadcastNotification(0x0000000A, 1);
|
|
||||||
// Games seem to sit and loop until we trigger this notification:
|
// Games seem to sit and loop until we trigger this notification:
|
||||||
|
|
||||||
|
for (uint8_t i = 1; i < 4; i++) {
|
||||||
|
if (kernel_state()->IsUserSignedIn(i)) {
|
||||||
|
// XN_SYS_SIGNINCHANGED
|
||||||
|
kernel_state()->BroadcastNotification(0xA, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
// XN_SYS_UI (off)
|
// XN_SYS_UI (off)
|
||||||
kernel_state()->BroadcastNotification(0x00000009, 0);
|
kernel_state()->BroadcastNotification(0x00000009, 0);
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
|
|
Loading…
Reference in New Issue