[Kernel] Removed XAM specific entities from KernelState
This commit is contained in:
parent
6f65ec382c
commit
e409a384ec
|
@ -37,10 +37,6 @@
|
||||||
|
|
||||||
DEFINE_bool(apply_title_update, true, "Apply title updates.", "Kernel");
|
DEFINE_bool(apply_title_update, true, "Apply title updates.", "Kernel");
|
||||||
|
|
||||||
DEFINE_uint32(max_signed_profiles, 4,
|
|
||||||
"Limits how many profiles can be assigned. Possible values: 1-4",
|
|
||||||
"Kernel");
|
|
||||||
|
|
||||||
DEFINE_uint32(kernel_build_version, 1888, "Define current kernel version",
|
DEFINE_uint32(kernel_build_version, 1888, "Define current kernel version",
|
||||||
"Kernel");
|
"Kernel");
|
||||||
|
|
||||||
|
@ -68,20 +64,11 @@ KernelState::KernelState(Emulator* emulator)
|
||||||
shared_kernel_state_ = this;
|
shared_kernel_state_ = this;
|
||||||
processor_ = emulator->processor();
|
processor_ = emulator->processor();
|
||||||
file_system_ = emulator->file_system();
|
file_system_ = emulator->file_system();
|
||||||
|
xam_state_ = std::make_unique<xam::XamState>(emulator, this);
|
||||||
app_manager_ = std::make_unique<xam::AppManager>();
|
|
||||||
achievement_manager_ = std::make_unique<AchievementManager>();
|
|
||||||
user_profiles_.emplace(0, std::make_unique<xam::UserProfile>(0));
|
|
||||||
|
|
||||||
InitializeKernelGuestGlobals();
|
InitializeKernelGuestGlobals();
|
||||||
kernel_version_ = KernelVersion(cvars::kernel_build_version);
|
kernel_version_ = KernelVersion(cvars::kernel_build_version);
|
||||||
|
|
||||||
auto content_root = emulator_->content_root();
|
|
||||||
if (!content_root.empty()) {
|
|
||||||
content_root = std::filesystem::absolute(content_root);
|
|
||||||
}
|
|
||||||
content_manager_ = std::make_unique<xam::ContentManager>(this, content_root);
|
|
||||||
|
|
||||||
// Hardcoded maximum of 2048 TLS slots.
|
// Hardcoded maximum of 2048 TLS slots.
|
||||||
tls_bitmap_.Resize(2048);
|
tls_bitmap_.Resize(2048);
|
||||||
|
|
||||||
|
@ -92,8 +79,6 @@ KernelState::KernelState(Emulator* emulator)
|
||||||
kMemoryProtectRead | kMemoryProtectWrite);
|
kMemoryProtectRead | kMemoryProtectWrite);
|
||||||
|
|
||||||
xenia_assert(fixed_alloc_worked);
|
xenia_assert(fixed_alloc_worked);
|
||||||
|
|
||||||
xam::AppManager::RegisterApps(this, app_manager_.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelState::~KernelState() {
|
KernelState::~KernelState() {
|
||||||
|
@ -112,8 +97,7 @@ KernelState::~KernelState() {
|
||||||
// Delete all objects.
|
// Delete all objects.
|
||||||
object_table_.Reset();
|
object_table_.Reset();
|
||||||
|
|
||||||
// Shutdown apps.
|
xam_state_.reset();
|
||||||
app_manager_.reset();
|
|
||||||
|
|
||||||
assert_true(shared_kernel_state_ == this);
|
assert_true(shared_kernel_state_ == this);
|
||||||
shared_kernel_state_ = nullptr;
|
shared_kernel_state_ = nullptr;
|
||||||
|
@ -592,8 +576,8 @@ std::vector<xam::XCONTENT_AGGREGATE_DATA> KernelState::FindTitleUpdate(
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return content_manager()->ListContent(1, xe::XContentType::kInstaller,
|
return xam_state_->content_manager()->ListContent(
|
||||||
title_id);
|
1, xe::XContentType::kInstaller, title_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const object_ref<UserModule> KernelState::LoadTitleUpdate(
|
const object_ref<UserModule> KernelState::LoadTitleUpdate(
|
||||||
|
@ -1252,24 +1236,6 @@ void KernelState::EmulateCPInterruptDPC(uint32_t interrupt_callback,
|
||||||
EndDPCImpersonation(current_context, dpc_scope);
|
EndDPCImpersonation(current_context, dpc_scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelState::UpdateUsedUserProfiles() {
|
|
||||||
const std::bitset<4> used_slots = GetConnectedUsers();
|
|
||||||
|
|
||||||
for (uint32_t i = 1; i < cvars::max_signed_profiles; i++) {
|
|
||||||
bool is_used = used_slots.test(i);
|
|
||||||
|
|
||||||
if (IsUserSignedIn(i) && !is_used) {
|
|
||||||
user_profiles_.erase(i);
|
|
||||||
BroadcastNotification(kXNotificationIDSystemInputDevicesChanged, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsUserSignedIn(i) && is_used) {
|
|
||||||
user_profiles_.emplace(i, std::make_unique<xam::UserProfile>(i));
|
|
||||||
BroadcastNotification(kXNotificationIDSystemInputDevicesChanged, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void KernelState::InitializeProcess(X_KPROCESS* process, uint32_t type,
|
void KernelState::InitializeProcess(X_KPROCESS* process, uint32_t type,
|
||||||
char unk_18, char unk_19, char unk_1A) {
|
char unk_18, char unk_19, char unk_1A) {
|
||||||
uint32_t guest_kprocess = memory()->HostToGuestVirtual(process);
|
uint32_t guest_kprocess = memory()->HostToGuestVirtual(process);
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "achievement_manager.h"
|
|
||||||
#include "xenia/base/bit_map.h"
|
#include "xenia/base/bit_map.h"
|
||||||
#include "xenia/base/cvar.h"
|
#include "xenia/base/cvar.h"
|
||||||
#include "xenia/base/mutex.h"
|
#include "xenia/base/mutex.h"
|
||||||
|
@ -28,9 +27,11 @@
|
||||||
#include "xenia/kernel/util/native_list.h"
|
#include "xenia/kernel/util/native_list.h"
|
||||||
#include "xenia/kernel/util/object_table.h"
|
#include "xenia/kernel/util/object_table.h"
|
||||||
#include "xenia/kernel/util/xdbf_utils.h"
|
#include "xenia/kernel/util/xdbf_utils.h"
|
||||||
|
#include "xenia/kernel/xam/achievement_manager.h"
|
||||||
#include "xenia/kernel/xam/app_manager.h"
|
#include "xenia/kernel/xam/app_manager.h"
|
||||||
#include "xenia/kernel/xam/content_manager.h"
|
#include "xenia/kernel/xam/content_manager.h"
|
||||||
#include "xenia/kernel/xam/user_profile.h"
|
#include "xenia/kernel/xam/user_profile.h"
|
||||||
|
#include "xenia/kernel/xam/xam_state.h"
|
||||||
#include "xenia/kernel/xevent.h"
|
#include "xenia/kernel/xevent.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
#include "xenia/vfs/virtual_file_system.h"
|
#include "xenia/vfs/virtual_file_system.h"
|
||||||
|
@ -186,41 +187,17 @@ class KernelState {
|
||||||
util::XdbfGameData title_xdbf() const;
|
util::XdbfGameData title_xdbf() const;
|
||||||
util::XdbfGameData module_xdbf(object_ref<UserModule> exec_module) const;
|
util::XdbfGameData module_xdbf(object_ref<UserModule> exec_module) const;
|
||||||
|
|
||||||
AchievementManager* achievement_manager() const {
|
xam::XamState* xam_state() const { return xam_state_.get(); }
|
||||||
return achievement_manager_.get();
|
|
||||||
|
xam::AchievementManager* achievement_manager() const {
|
||||||
|
return xam_state()->achievement_manager();
|
||||||
}
|
}
|
||||||
xam::AppManager* app_manager() const { return app_manager_.get(); }
|
xam::AppManager* app_manager() const { return xam_state()->app_manager(); }
|
||||||
xam::ContentManager* content_manager() const {
|
xam::ContentManager* content_manager() const {
|
||||||
return content_manager_.get();
|
return xam_state()->content_manager();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::bitset<4> GetConnectedUsers() const;
|
std::bitset<4> GetConnectedUsers() const;
|
||||||
void UpdateUsedUserProfiles();
|
|
||||||
|
|
||||||
bool IsUserSignedIn(uint32_t index) const {
|
|
||||||
return user_profiles_.find(index) != user_profiles_.cend();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsUserSignedIn(uint64_t xuid) const {
|
|
||||||
return user_profile(xuid) != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
xam::UserProfile* user_profile(uint32_t index) const {
|
|
||||||
if (!IsUserSignedIn(index)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return user_profiles_.at(index).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
xam::UserProfile* user_profile(uint64_t xuid) const {
|
|
||||||
for (const auto& [key, value] : user_profiles_) {
|
|
||||||
if (value->xuid() == xuid) {
|
|
||||||
return user_profiles_.at(key).get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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_; }
|
||||||
|
@ -368,11 +345,7 @@ class KernelState {
|
||||||
Memory* memory_;
|
Memory* memory_;
|
||||||
cpu::Processor* processor_;
|
cpu::Processor* processor_;
|
||||||
vfs::VirtualFileSystem* file_system_;
|
vfs::VirtualFileSystem* file_system_;
|
||||||
|
std::unique_ptr<xam::XamState> xam_state_;
|
||||||
std::unique_ptr<xam::AppManager> app_manager_;
|
|
||||||
std::unique_ptr<xam::ContentManager> content_manager_;
|
|
||||||
std::map<uint8_t, std::unique_ptr<xam::UserProfile>> user_profiles_;
|
|
||||||
std::unique_ptr<AchievementManager> achievement_manager_;
|
|
||||||
|
|
||||||
KernelVersion kernel_version_;
|
KernelVersion kernel_version_;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ DECLARE_int32(user_language);
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
namespace xam {
|
||||||
|
|
||||||
AchievementManager::AchievementManager() { unlocked_achievements.clear(); };
|
AchievementManager::AchievementManager() { unlocked_achievements.clear(); };
|
||||||
|
|
||||||
|
@ -85,5 +86,6 @@ uint64_t AchievementManager::GetAchievementUnlockTime(uint32_t achievement_id) {
|
||||||
return itr->second;
|
return itr->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace xam
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
|
@ -7,8 +7,8 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef XENIA_KERNEL_ACHIEVEMENT_MANAGER_H_
|
#ifndef XENIA_KERNEL_XAM_ACHIEVEMENT_MANAGER_H_
|
||||||
#define XENIA_KERNEL_ACHIEVEMENT_MANAGER_H_
|
#define XENIA_KERNEL_XAM_ACHIEVEMENT_MANAGER_H_
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
namespace xam {
|
||||||
|
|
||||||
// TODO(gibbed): probably a FILETIME/LARGE_INTEGER, unknown currently
|
// TODO(gibbed): probably a FILETIME/LARGE_INTEGER, unknown currently
|
||||||
struct X_ACHIEVEMENT_UNLOCK_TIME {
|
struct X_ACHIEVEMENT_UNLOCK_TIME {
|
||||||
|
@ -55,7 +56,8 @@ class AchievementManager {
|
||||||
// void Save();
|
// void Save();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace xam
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_ACHIEVEMENT_MANAGER_H_
|
#endif // XENIA_KERNEL_XAM_ACHIEVEMENT_MANAGER_H_
|
|
@ -54,7 +54,8 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||||
XELOGD("XGIUserSetContextEx: {} - Set to value: {}", desc,
|
XELOGD("XGIUserSetContextEx: {} - Set to value: {}", desc,
|
||||||
context_value);
|
context_value);
|
||||||
|
|
||||||
UserProfile* user_profile = kernel_state_->user_profile(user_index);
|
UserProfile* user_profile =
|
||||||
|
kernel_state_->xam_state()->GetUserProfile(user_index);
|
||||||
if (user_profile) {
|
if (user_profile) {
|
||||||
user_profile->contexts_[context_id] = context_value;
|
user_profile->contexts_[context_id] = context_value;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +82,7 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||||
Property(property_id, value_size,
|
Property(property_id, value_size,
|
||||||
memory_->TranslateVirtual<uint8_t*>(value_ptr));
|
memory_->TranslateVirtual<uint8_t*>(value_ptr));
|
||||||
|
|
||||||
auto user = kernel_state_->user_profile(user_index);
|
auto user = kernel_state_->xam_state()->GetUserProfile(user_index);
|
||||||
if (user) {
|
if (user) {
|
||||||
user->AddProperty(&property);
|
user->AddProperty(&property);
|
||||||
}
|
}
|
||||||
|
@ -197,7 +198,8 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||||
context_ptr, context_id);
|
context_ptr, context_id);
|
||||||
uint32_t value = 0;
|
uint32_t value = 0;
|
||||||
if (context) {
|
if (context) {
|
||||||
UserProfile* user_profile = kernel_state_->user_profile(user_index);
|
UserProfile* user_profile =
|
||||||
|
kernel_state_->xam_state()->GetUserProfile(user_index);
|
||||||
if (user_profile) {
|
if (user_profile) {
|
||||||
if (user_profile->contexts_.find(context_id) !=
|
if (user_profile->contexts_.find(context_id) !=
|
||||||
user_profile->contexts_.cend()) {
|
user_profile->contexts_.cend()) {
|
||||||
|
|
|
@ -275,7 +275,9 @@ X_RESULT ContentManager::ReadContentHeaderFile(const std::string_view file_name,
|
||||||
// usually requires title_id to be provided
|
// usually requires title_id to be provided
|
||||||
// Kinda simple workaround for that, but still assumption
|
// Kinda simple workaround for that, but still assumption
|
||||||
data.title_id = title_id;
|
data.title_id = title_id;
|
||||||
data.unk134 = kernel_state_->user_profile(uint32_t(0))->xuid();
|
data.xuid = kernel_state_->xam_state()
|
||||||
|
->GetUserProfile(static_cast<uint32_t>(0))
|
||||||
|
->xuid();
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
return X_STATUS_NO_SUCH_FILE;
|
return X_STATUS_NO_SUCH_FILE;
|
||||||
|
@ -400,8 +402,9 @@ 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 =
|
auto user_name = xe::to_path(kernel_state_->xam_state()
|
||||||
xe::to_path(kernel_state_->user_profile(uint32_t(0))->name());
|
->GetUserProfile(static_cast<uint32_t>(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
|
||||||
|
|
|
@ -94,7 +94,7 @@ struct XCONTENT_DATA {
|
||||||
static_assert_size(XCONTENT_DATA, 0x134);
|
static_assert_size(XCONTENT_DATA, 0x134);
|
||||||
|
|
||||||
struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA {
|
struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA {
|
||||||
be<uint64_t> unk134; // some titles store XUID here?
|
be<uint64_t> xuid; // some titles store XUID here?
|
||||||
be<uint32_t> title_id;
|
be<uint32_t> title_id;
|
||||||
|
|
||||||
XCONTENT_AGGREGATE_DATA() = default;
|
XCONTENT_AGGREGATE_DATA() = default;
|
||||||
|
@ -104,7 +104,7 @@ struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA {
|
||||||
set_display_name(other.display_name());
|
set_display_name(other.display_name());
|
||||||
set_file_name(other.file_name());
|
set_file_name(other.file_name());
|
||||||
padding[0] = padding[1] = 0;
|
padding[0] = padding[1] = 0;
|
||||||
unk134 = 0;
|
xuid = 0;
|
||||||
title_id = kCurrentlyRunningTitleId;
|
title_id = kCurrentlyRunningTitleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -334,8 +334,9 @@ 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) {
|
||||||
if (kernel_state()->IsUserSignedIn(user_index)) {
|
if (kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile = kernel_state()->user_profile(user_index);
|
const auto& user_profile =
|
||||||
|
kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||||
*creator_xuid_ptr = user_profile->xuid();
|
*creator_xuid_ptr = user_profile->xuid();
|
||||||
} else {
|
} else {
|
||||||
result = X_ERROR_NO_SUCH_USER;
|
result = X_ERROR_NO_SUCH_USER;
|
||||||
|
|
|
@ -234,7 +234,7 @@ X_HRESULT_result_t XamUserGetDeviceContext_entry(dword_t user_index,
|
||||||
// If this function fails they assume zero, so let's fail AND
|
// If this function fails they assume zero, so let's fail AND
|
||||||
// set zero just to be safe.
|
// set zero just to be safe.
|
||||||
*out_ptr = 0;
|
*out_ptr = 0;
|
||||||
if (kernel_state()->IsUserSignedIn(user_index) ||
|
if (kernel_state()->xam_state()->IsUserSignedIn(user_index) ||
|
||||||
(user_index & 0xFF) == 0xFF) {
|
(user_index & 0xFF) == 0xFF) {
|
||||||
*out_ptr = (uint32_t)user_index;
|
*out_ptr = (uint32_t)user_index;
|
||||||
return X_E_SUCCESS;
|
return X_E_SUCCESS;
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2024 Xenia Canary. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xenia/kernel/xam/xam_state.h"
|
||||||
|
#include "xenia/emulator.h"
|
||||||
|
|
||||||
|
DEFINE_uint32(max_signed_profiles, 4,
|
||||||
|
"Limits how many profiles can be assigned. Possible values: 1-4",
|
||||||
|
"Kernel");
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
namespace xam {
|
||||||
|
|
||||||
|
XamState::XamState(Emulator* emulator, KernelState* kernel_state)
|
||||||
|
: kernel_state_(kernel_state) {
|
||||||
|
app_manager_ = std::make_unique<AppManager>();
|
||||||
|
|
||||||
|
auto content_root = emulator->content_root();
|
||||||
|
if (!content_root.empty()) {
|
||||||
|
content_root = std::filesystem::absolute(content_root);
|
||||||
|
}
|
||||||
|
content_manager_ =
|
||||||
|
std::make_unique<ContentManager>(kernel_state, content_root);
|
||||||
|
|
||||||
|
user_profiles_.emplace(0, std::make_unique<xam::UserProfile>(0));
|
||||||
|
|
||||||
|
achievement_manager_ = std::make_unique<AchievementManager>();
|
||||||
|
|
||||||
|
AppManager::RegisterApps(kernel_state, app_manager_.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
XamState::~XamState() {
|
||||||
|
app_manager_.reset();
|
||||||
|
achievement_manager_.reset();
|
||||||
|
content_manager_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserProfile* XamState::GetUserProfile(uint32_t user_index) const {
|
||||||
|
if (!IsUserSignedIn(user_index)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return user_profiles_.at(user_index).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserProfile* XamState::GetUserProfile(uint64_t xuid) const {
|
||||||
|
for (const auto& [key, value] : user_profiles_) {
|
||||||
|
if (value->xuid() == xuid) {
|
||||||
|
return user_profiles_.at(key).get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XamState::UpdateUsedUserProfiles() {
|
||||||
|
const std::bitset<4> used_slots = kernel_state_->GetConnectedUsers();
|
||||||
|
|
||||||
|
const uint32_t signed_profile_count =
|
||||||
|
std::max(static_cast<uint32_t>(1),
|
||||||
|
std::min(static_cast<uint32_t>(4), cvars::max_signed_profiles));
|
||||||
|
|
||||||
|
for (uint32_t i = 1; i < signed_profile_count; i++) {
|
||||||
|
bool is_used = used_slots.test(i);
|
||||||
|
|
||||||
|
if (IsUserSignedIn(i) && !is_used) {
|
||||||
|
user_profiles_.erase(i);
|
||||||
|
kernel_state_->BroadcastNotification(
|
||||||
|
kXNotificationIDSystemInputDevicesChanged, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsUserSignedIn(i) && is_used) {
|
||||||
|
user_profiles_.emplace(i, std::make_unique<xam::UserProfile>(i));
|
||||||
|
kernel_state_->BroadcastNotification(
|
||||||
|
kXNotificationIDSystemInputDevicesChanged, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XamState::IsUserSignedIn(uint32_t user_index) const {
|
||||||
|
return user_profiles_.find(user_index) != user_profiles_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XamState::IsUserSignedIn(uint64_t xuid) const {
|
||||||
|
return GetUserProfile(xuid) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xam
|
||||||
|
} // namespace kernel
|
||||||
|
} // namespace xe
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2024 Xenia Canary. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_KERNEL_XAM_XAM_STATE_H_
|
||||||
|
#define XENIA_KERNEL_XAM_XAM_STATE_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "xenia/kernel/xam/achievement_manager.h"
|
||||||
|
#include "xenia/kernel/xam/app_manager.h"
|
||||||
|
#include "xenia/kernel/xam/content_manager.h"
|
||||||
|
#include "xenia/kernel/xam/user_profile.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
class Emulator;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
class KernelState;
|
||||||
|
}
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
namespace xam {
|
||||||
|
|
||||||
|
class XamState {
|
||||||
|
public:
|
||||||
|
XamState(Emulator* emulator, KernelState* kernel_state);
|
||||||
|
~XamState();
|
||||||
|
|
||||||
|
AppManager* app_manager() const { return app_manager_.get(); }
|
||||||
|
ContentManager* content_manager() const { return content_manager_.get(); }
|
||||||
|
AchievementManager* achievement_manager() const {
|
||||||
|
return achievement_manager_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserProfile* GetUserProfile(uint32_t user_index) const;
|
||||||
|
UserProfile* GetUserProfile(uint64_t xuid) const;
|
||||||
|
|
||||||
|
void UpdateUsedUserProfiles();
|
||||||
|
|
||||||
|
bool IsUserSignedIn(uint32_t user_index) const;
|
||||||
|
bool IsUserSignedIn(uint64_t xuid) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
KernelState* kernel_state_;
|
||||||
|
|
||||||
|
std::unique_ptr<AppManager> app_manager_;
|
||||||
|
std::unique_ptr<ContentManager> content_manager_;
|
||||||
|
std::unique_ptr<AchievementManager> achievement_manager_;
|
||||||
|
|
||||||
|
std::map<uint8_t, std::unique_ptr<UserProfile>> user_profiles_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xam
|
||||||
|
} // namespace kernel
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif
|
|
@ -574,7 +574,8 @@ dword_result_t XamShowDeviceSelectorUI_entry(
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_index != 0xFF && !kernel_state()->IsUserSignedIn(user_index)) {
|
if (user_index != 0xFF &&
|
||||||
|
!kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
kernel_state()->CompleteOverlappedImmediate(overlapped,
|
kernel_state()->CompleteOverlappedImmediate(overlapped,
|
||||||
X_ERROR_NO_SUCH_USER);
|
X_ERROR_NO_SUCH_USER);
|
||||||
return X_ERROR_IO_PENDING;
|
return X_ERROR_IO_PENDING;
|
||||||
|
@ -682,7 +683,7 @@ dword_result_t XamShowMarketplaceUI_entry(dword_t user_index, dword_t ui_type,
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
if (!kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,7 +759,7 @@ dword_result_t XamShowMarketplaceDownloadItemsUI_entry(
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
if (!kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
if (overlapped) {
|
if (overlapped) {
|
||||||
kernel_state()->CompleteOverlappedImmediate(overlapped,
|
kernel_state()->CompleteOverlappedImmediate(overlapped,
|
||||||
X_ERROR_NO_SUCH_USER);
|
X_ERROR_NO_SUCH_USER);
|
||||||
|
|
|
@ -36,8 +36,9 @@ 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 (kernel_state()->IsUserSignedIn(user_index)) {
|
if (kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile = kernel_state()->user_profile(user_index);
|
const auto& user_profile =
|
||||||
|
kernel_state()->xam_state()->GetUserProfile(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?
|
||||||
|
@ -62,8 +63,9 @@ 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 (kernel_state()->IsUserSignedIn(user_index)) {
|
if (kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile = kernel_state()->user_profile(user_index);
|
const auto& user_profile =
|
||||||
|
kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||||
signin_state = user_profile->signin_state();
|
signin_state = user_profile->signin_state();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,10 +95,11 @@ X_HRESULT_result_t XamUserGetSigninInfo_entry(
|
||||||
return X_E_NO_SUCH_USER;
|
return X_E_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel_state()->UpdateUsedUserProfiles();
|
kernel_state()->xam_state()->UpdateUsedUserProfiles();
|
||||||
|
|
||||||
if (kernel_state()->IsUserSignedIn(user_index)) {
|
if (kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile = kernel_state()->user_profile(user_index);
|
const auto& user_profile =
|
||||||
|
kernel_state()->xam_state()->GetUserProfile(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(),
|
||||||
|
@ -114,8 +117,9 @@ dword_result_t XamUserGetName_entry(dword_t user_index, lpstring_t buffer,
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kernel_state()->IsUserSignedIn(user_index)) {
|
if (kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile = kernel_state()->user_profile(user_index);
|
const auto& user_profile =
|
||||||
|
kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||||
const auto& user_name = user_profile->name();
|
const auto& user_name = user_profile->name();
|
||||||
xe::string_util::copy_truncating(
|
xe::string_util::copy_truncating(
|
||||||
buffer, user_name, std::min(buffer_len.value(), uint32_t(16)));
|
buffer, user_name, std::min(buffer_len.value(), uint32_t(16)));
|
||||||
|
@ -138,11 +142,12 @@ dword_result_t XamUserGetGamerTag_entry(dword_t user_index,
|
||||||
return X_E_INVALIDARG;
|
return X_E_INVALIDARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
if (!kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
return X_E_INVALIDARG;
|
return X_E_INVALIDARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& user_profile = kernel_state()->user_profile(user_index);
|
const auto& user_profile =
|
||||||
|
kernel_state()->xam_state()->GetUserProfile(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)));
|
||||||
|
@ -173,8 +178,9 @@ uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
|
||||||
// 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)) {
|
if (kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile = kernel_state()->user_profile(user_index);
|
const auto& user_profile =
|
||||||
|
kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||||
assert_true(static_cast<uint64_t>(xuids[0]) == user_profile->xuid());
|
assert_true(static_cast<uint64_t>(xuids[0]) == user_profile->xuid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,7 +233,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) && !xuids) {
|
if (!kernel_state()->xam_state()->IsUserSignedIn(user_index) && !xuids) {
|
||||||
if (overlapped) {
|
if (overlapped) {
|
||||||
kernel_state()->CompleteOverlappedImmediate(
|
kernel_state()->CompleteOverlappedImmediate(
|
||||||
kernel_state()->memory()->HostToGuestVirtual(overlapped),
|
kernel_state()->memory()->HostToGuestVirtual(overlapped),
|
||||||
|
@ -237,11 +243,11 @@ uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto user_profile = kernel_state()->user_profile(user_index);
|
auto user_profile = kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||||
|
|
||||||
if (xuids) {
|
if (xuids) {
|
||||||
uint64_t user_xuid = static_cast<uint64_t>(xuids[0]);
|
uint64_t user_xuid = static_cast<uint64_t>(xuids[0]);
|
||||||
if (!kernel_state()->IsUserSignedIn(user_xuid)) {
|
if (!kernel_state()->xam_state()->IsUserSignedIn(user_xuid)) {
|
||||||
if (overlapped) {
|
if (overlapped) {
|
||||||
kernel_state()->CompleteOverlappedImmediate(
|
kernel_state()->CompleteOverlappedImmediate(
|
||||||
kernel_state()->memory()->HostToGuestVirtual(overlapped),
|
kernel_state()->memory()->HostToGuestVirtual(overlapped),
|
||||||
|
@ -250,7 +256,7 @@ uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
|
||||||
}
|
}
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
user_profile = kernel_state()->user_profile(user_xuid);
|
user_profile = kernel_state()->xam_state()->GetUserProfile(user_xuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First call asks for size (fill buffer_size_ptr).
|
// First call asks for size (fill buffer_size_ptr).
|
||||||
|
@ -361,7 +367,8 @@ dword_result_t XamUserWriteProfileSettings_entry(
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
// Update and save settings.
|
// Update and save settings.
|
||||||
const auto& user_profile = kernel_state()->user_profile(user_index);
|
const auto& user_profile =
|
||||||
|
kernel_state()->xam_state()->GetUserProfile(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];
|
||||||
|
@ -429,7 +436,7 @@ dword_result_t XamUserCheckPrivilege_entry(dword_t user_index, dword_t mask,
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
if (!kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,7 +449,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 (!kernel_state()->IsUserSignedIn(user_index)) {
|
if (!kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,7 +463,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 (!kernel_state()->IsUserSignedIn(user_index)) {
|
if (!kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
return X_ERROR_NO_SUCH_USER;
|
return X_ERROR_NO_SUCH_USER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,7 +498,7 @@ dword_result_t XamUserGetMembershipTier_entry(dword_t user_index) {
|
||||||
return X_ERROR_INVALID_PARAMETER;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!kernel_state()->IsUserSignedIn(user_index)) {
|
if (!kernel_state()->xam_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 */;
|
||||||
|
@ -507,8 +514,9 @@ 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 (kernel_state()->IsUserSignedIn(user_index)) {
|
if (kernel_state()->xam_state()->IsUserSignedIn(user_index)) {
|
||||||
const auto& user_profile = kernel_state()->user_profile(user_index);
|
const auto& user_profile =
|
||||||
|
kernel_state()->xam_state()->GetUserProfile(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 {
|
||||||
|
@ -545,7 +553,7 @@ DECLARE_XAM_EXPORT1(XamUserAreUsersFriends, kUserProfiles, kStub);
|
||||||
dword_result_t XamShowSigninUI_entry(dword_t users_needed, dword_t unk_mask) {
|
dword_result_t XamShowSigninUI_entry(dword_t users_needed, dword_t unk_mask) {
|
||||||
// XN_SYS_UI (on)
|
// XN_SYS_UI (on)
|
||||||
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, 1);
|
kernel_state()->BroadcastNotification(kXNotificationIDSystemUI, 1);
|
||||||
kernel_state()->UpdateUsedUserProfiles();
|
kernel_state()->xam_state()->UpdateUsedUserProfiles();
|
||||||
// Mask values vary. Probably matching user types? Local/remote?
|
// Mask values vary. Probably matching user types? Local/remote?
|
||||||
// Games seem to sit and loop until we trigger this notification:
|
// Games seem to sit and loop until we trigger this notification:
|
||||||
|
|
||||||
|
@ -554,7 +562,7 @@ dword_result_t XamShowSigninUI_entry(dword_t users_needed, dword_t unk_mask) {
|
||||||
uint32_t active_users = 0;
|
uint32_t active_users = 0;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < 4; i++) {
|
for (uint32_t i = 0; i < 4; i++) {
|
||||||
if (kernel_state()->IsUserSignedIn(i)) {
|
if (kernel_state()->xam_state()->IsUserSignedIn(i)) {
|
||||||
user_mask |= (1 << i);
|
user_mask |= (1 << i);
|
||||||
active_users++;
|
active_users++;
|
||||||
if (active_users >= users_needed) break;
|
if (active_users >= users_needed) break;
|
||||||
|
|
|
@ -88,12 +88,13 @@ uint32_t XAchievementEnumerator::WriteItems(uint32_t buffer_ptr,
|
||||||
|
|
||||||
size_t size = count * item_size();
|
size_t size = count * item_size();
|
||||||
|
|
||||||
auto details = reinterpret_cast<X_ACHIEVEMENT_DETAILS*>(buffer_data);
|
auto details = reinterpret_cast<xam::X_ACHIEVEMENT_DETAILS*>(buffer_data);
|
||||||
size_t string_offset = items_per_enumerate() * sizeof(X_ACHIEVEMENT_DETAILS);
|
size_t string_offset =
|
||||||
|
items_per_enumerate() * sizeof(xam::X_ACHIEVEMENT_DETAILS);
|
||||||
auto string_buffer =
|
auto string_buffer =
|
||||||
StringBuffer{buffer_ptr + static_cast<uint32_t>(string_offset),
|
StringBuffer{buffer_ptr + static_cast<uint32_t>(string_offset),
|
||||||
&buffer_data[string_offset],
|
&buffer_data[string_offset],
|
||||||
count * X_ACHIEVEMENT_DETAILS::kStringBufferSize};
|
count * xam::X_ACHIEVEMENT_DETAILS::kStringBufferSize};
|
||||||
for (size_t i = 0, o = current_item_; i < count; ++i, ++current_item_) {
|
for (size_t i = 0, o = current_item_; i < count; ++i, ++current_item_) {
|
||||||
const auto& item = items_[current_item_];
|
const auto& item = items_[current_item_];
|
||||||
details[i].id = item.id;
|
details[i].id = item.id;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/kernel/achievement_manager.h"
|
#include "xenia/kernel/xam/achievement_manager.h"
|
||||||
#include "xenia/kernel/xobject.h"
|
#include "xenia/kernel/xobject.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
|
@ -134,8 +134,9 @@ class XAchievementEnumerator : public XEnumerator {
|
||||||
uint32_t flags)
|
uint32_t flags)
|
||||||
: XEnumerator(
|
: XEnumerator(
|
||||||
kernel_state, items_per_enumerate,
|
kernel_state, items_per_enumerate,
|
||||||
sizeof(X_ACHIEVEMENT_DETAILS) +
|
sizeof(xam::X_ACHIEVEMENT_DETAILS) +
|
||||||
(!!(flags & 7) ? X_ACHIEVEMENT_DETAILS::kStringBufferSize : 0)),
|
(!!(flags & 7) ? xam::X_ACHIEVEMENT_DETAILS::kStringBufferSize
|
||||||
|
: 0)),
|
||||||
flags_(flags) {}
|
flags_(flags) {}
|
||||||
|
|
||||||
void AppendItem(AchievementDetails item) {
|
void AppendItem(AchievementDetails item) {
|
||||||
|
|
Loading…
Reference in New Issue