Merge remote-tracking branch 'upstream/canary_experimental' into edge

This commit is contained in:
Herman S.
2026-01-07 11:17:34 +09:00
8 changed files with 140 additions and 72 deletions
+48 -28
View File
@@ -85,19 +85,19 @@ std::string GameInfoDatabase::GetLocalizedString(const uint32_t id,
spa_gamedata_->GetExistingLanguage(language), id);
}
GameInfoDatabase::Context GameInfoDatabase::GetContext(
std::optional<GameInfoDatabase::Context> GameInfoDatabase::GetContext(
const uint32_t id) const {
Context context = {.id = xam::kInvalidContextId};
if (!is_valid_) {
return context;
return std::nullopt;
}
const auto xdbf_context = spa_gamedata_->GetContext(id);
if (!xdbf_context) {
return context;
return std::nullopt;
}
Context context = {};
context.id = xdbf_context->id;
context.default_value = xdbf_context->default_value;
context.max_value = xdbf_context->max_value;
@@ -109,19 +109,19 @@ GameInfoDatabase::Context GameInfoDatabase::GetContext(
return context;
}
GameInfoDatabase::Property GameInfoDatabase::GetProperty(
std::optional<GameInfoDatabase::Property> GameInfoDatabase::GetProperty(
const uint32_t id) const {
Property property = {.id = xam::kInvalidPropertyId};
if (!is_valid_) {
return property;
return std::nullopt;
}
const auto xdbf_property = spa_gamedata_->GetProperty(id);
if (!xdbf_property) {
return property;
return std::nullopt;
}
Property property = {};
property.id = xdbf_property->id;
property.data_size = xdbf_property->data_size;
property.is_system = xam::UserData::is_system_property(xdbf_property->id);
@@ -132,14 +132,14 @@ GameInfoDatabase::Property GameInfoDatabase::GetProperty(
return property;
}
GameInfoDatabase::Achievement GameInfoDatabase::GetAchievement(
std::optional<GameInfoDatabase::Achievement> GameInfoDatabase::GetAchievement(
const uint32_t id) const {
Achievement achievement = {};
if (!is_valid_) {
return achievement;
return std::nullopt;
}
Achievement achievement = {};
const auto xdbf_achievement = spa_gamedata_->GetAchievement(id);
if (!xdbf_achievement) {
return achievement;
@@ -190,20 +190,20 @@ GameInfoDatabase::Field GameInfoDatabase::GetField(
return field;
}
GameInfoDatabase::StatsView GameInfoDatabase::GetStatsView(
std::optional<GameInfoDatabase::StatsView> GameInfoDatabase::GetStatsView(
const uint32_t id) const {
StatsView stats_view = {};
if (!is_valid_) {
return stats_view;
return std::nullopt;
}
const auto xdbf_stats_view = spa_gamedata_->GetStatsView(id);
if (!xdbf_stats_view.has_value()) {
return stats_view;
return std::nullopt;
}
StatsView stats_view = {};
stats_view.view.id = xdbf_stats_view->view_entry.id;
stats_view.view.arbitrated =
@@ -252,14 +252,14 @@ GameInfoDatabase::Presence GameInfoDatabase::GetPresence() const {
return presence;
}
GameInfoDatabase::PresenceMode GameInfoDatabase::GetPresenceMode(
std::optional<GameInfoDatabase::PresenceMode> GameInfoDatabase::GetPresenceMode(
const uint32_t context_value) const {
PresenceMode presence_mode = {};
if (!is_valid_) {
return presence_mode;
return std::nullopt;
}
PresenceMode presence_mode = {};
const auto xdbf_presence_mode = spa_gamedata_->GetPresenceMode(context_value);
if (!xdbf_presence_mode.has_value()) {
@@ -369,7 +369,11 @@ std::vector<GameInfoDatabase::Context> GameInfoDatabase::GetContexts() const {
const auto xdbf_contexts = spa_gamedata_->GetContexts();
for (const auto& entry : xdbf_contexts) {
contexts.push_back(GetContext(entry->id));
const auto context = GetContext(entry->id);
if (context.has_value()) {
contexts.push_back(context.value());
}
}
return contexts;
@@ -385,7 +389,11 @@ std::vector<GameInfoDatabase::Property> GameInfoDatabase::GetProperties()
const auto xdbf_properties = spa_gamedata_->GetProperties();
for (const auto& entry : xdbf_properties) {
properties.push_back(GetProperty(entry->id));
const auto property = GetProperty(entry->id);
if (property.has_value()) {
properties.push_back(property.value());
}
}
return properties;
@@ -401,7 +409,11 @@ std::vector<GameInfoDatabase::Achievement> GameInfoDatabase::GetAchievements()
const auto xdbf_achievements = spa_gamedata_->GetAchievements();
for (const auto& entry : xdbf_achievements) {
achievements.push_back(GetAchievement(entry->id));
auto achievement = GetAchievement(entry->id);
if (achievement.has_value()) {
achievements.push_back(achievement.value());
}
}
return achievements;
@@ -418,7 +430,11 @@ std::vector<GameInfoDatabase::StatsView> GameInfoDatabase::GetStatsViews()
const auto xdbf_stats_views = spa_gamedata_->GetStatsViews();
for (const auto& entry : *xdbf_stats_views) {
stats_views.push_back(GetStatsView(entry.view_entry.id));
auto stats_view = GetStatsView(entry.view_entry.id);
if (stats_view.has_value()) {
stats_views.push_back(stats_view.value());
}
}
return stats_views;
@@ -437,7 +453,11 @@ std::vector<GameInfoDatabase::PresenceMode> GameInfoDatabase::GetPresenceModes()
for (uint32_t context_value = 0; context_value < xdbf_presence_modes.size();
context_value++) {
presence_modes.push_back(GetPresenceMode(context_value));
const auto presence_mode = GetPresenceMode(context_value);
if (presence_mode.has_value()) {
presence_modes.push_back(presence_mode.value());
}
}
return presence_modes;
+9 -5
View File
@@ -144,14 +144,18 @@ class GameInfoDatabase {
std::vector<uint8_t> GetIcon() const;
Context GetContext(const uint32_t id) const;
Property GetProperty(const uint32_t id) const;
Achievement GetAchievement(const uint32_t id) const;
std::optional<GameInfoDatabase::Context> GetContext(const uint32_t id) const;
std::optional<GameInfoDatabase::Property> GetProperty(
const uint32_t id) const;
std::optional<GameInfoDatabase::Achievement> GetAchievement(
const uint32_t id) const;
PropertyBag GetPropertyBag(const xam::PropertyBag& property_bag) const;
Field GetField(const xam::ViewFieldEntry& field_entry) const;
StatsView GetStatsView(const uint32_t id) const;
std::optional<GameInfoDatabase::StatsView> GetStatsView(
const uint32_t id) const;
Presence GetPresence() const;
PresenceMode GetPresenceMode(const uint32_t context_value) const;
std::optional<GameInfoDatabase::PresenceMode> GetPresenceMode(
const uint32_t context_value) const;
std::vector<PresenceMode> GetPresenceModes(
const std::vector<xam::PropertyBag> property_bags) const;
+25
View File
@@ -222,6 +222,22 @@ static_assert_size(X_PASSPORT_SESSION_TOKEN, 0x1C);
#pragma pack(pop)
struct X_USER_SIGNIN_INFO {
xe::be<uint64_t> xuid;
xe::be<uint32_t> flags;
xe::be<uint32_t> signin_state;
xe::be<uint32_t> guest_num;
xe::be<uint32_t> sponsor_user_index;
char name[16];
};
static_assert_size(X_USER_SIGNIN_INFO, 40);
struct X_USER_READ_PROFILE_SETTINGS {
xe::be<uint32_t> setting_count;
xe::be<uint32_t> settings_ptr;
};
static_assert_size(X_USER_READ_PROFILE_SETTINGS, 8);
// clang-format off
#define XMBox_NOICON 0x00000000
#define XMBox_ERRORICON 0x00000001
@@ -343,6 +359,15 @@ struct XMP_USER_PLAYLIST_INFO {
};
static_assert_size(XMP_USER_PLAYLIST_INFO, 0x334);
constexpr uint8_t kStatsMaxAmount = 64;
struct X_STATS_DETAILS {
xe::be<uint32_t> id;
xe::be<uint32_t> stats_amount;
xe::be<uint16_t> stats[kStatsMaxAmount];
};
static_assert_size(X_STATS_DETAILS, 8 + kStatsMaxAmount * 2);
} // namespace xam
} // namespace kernel
} // namespace xe
+13 -24
View File
@@ -107,16 +107,6 @@ dword_result_t XamUserGetSigninState_entry(dword_t user_index) {
DECLARE_XAM_EXPORT2(XamUserGetSigninState, kUserProfiles, kImplemented,
kHighFrequency);
typedef struct {
xe::be<uint64_t> xuid;
xe::be<uint32_t> flags;
xe::be<uint32_t> signin_state;
xe::be<uint32_t> guest_num;
xe::be<uint32_t> sponsor_user_index;
char name[16];
} X_USER_SIGNIN_INFO;
static_assert_size(X_USER_SIGNIN_INFO, 40);
X_HRESULT_result_t XamUserGetSigninInfo_entry(
dword_t user_index, dword_t flags, pointer_t<X_USER_SIGNIN_INFO> info) {
if (!info) {
@@ -203,12 +193,6 @@ dword_result_t XamUserGetGamerTag_entry(dword_t user_index, dword_t buffer,
}
DECLARE_XAM_EXPORT1(XamUserGetGamerTag, kUserProfiles, kImplemented);
typedef struct {
xe::be<uint32_t> setting_count;
xe::be<uint32_t> settings_ptr;
} X_USER_READ_PROFILE_SETTINGS;
static_assert_size(X_USER_READ_PROFILE_SETTINGS, 8);
// https://github.com/oukiar/freestyledash/blob/master/Freestyle/Tools/Generic/xboxtools.cpp
uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
uint32_t xuid_count, be<uint64_t>* xuids,
@@ -1030,14 +1014,19 @@ dword_result_t XamUserGetOnlineCountryFromXUID_entry(qword_t xuid) {
DECLARE_XAM_EXPORT1(XamUserGetOnlineCountryFromXUID, kUserProfiles,
kImplemented);
constexpr uint8_t kStatsMaxAmount = 64;
struct X_STATS_DETAILS {
xe::be<uint32_t> id;
xe::be<uint32_t> stats_amount;
xe::be<uint16_t> stats[kStatsMaxAmount];
};
static_assert_size(X_STATS_DETAILS, 8 + kStatsMaxAmount * 2);
dword_result_t XamUserIsParentalControlled_entry(dword_t user_index) {
/* Notes:
- if (data_address < 1 || XamExecutingOnBehalfOfTitle == 0 || title id ==
kDashboardID || user_type != offline) (type mask used in XamUserGetXUID) go
forward else return false
*/
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
if (!user || !user->IsParentalControlled()) {
return false;
}
return true;
}
DECLARE_XAM_EXPORT1(XamUserIsParentalControlled, kUserProfiles, kImplemented);
dword_result_t XamUserCreateStatsEnumerator_entry(
dword_t title_id, dword_t user_index, dword_t count, dword_t flags,
+27 -3
View File
@@ -10,6 +10,7 @@
#include "xenia/memory.h"
#include <cstring>
#include <random>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h"
@@ -30,7 +31,12 @@ DEFINE_bool(protect_zero, true, "Protect the zero page from reads and writes.",
DEFINE_bool(protect_on_release, false,
"Protect released memory to prevent accesses.", "Memory");
DEFINE_bool(scribble_heap, false,
"Scribble 0xCD into all allocated heap memory.", "Memory");
"Scribble specific or random value into all allocated heap memory.",
"Memory");
DEFINE_int32(scribble_heap_value, 0,
"Value used to fill all allocated heap memory. 0 - Random value. "
"Valid range: [1-255]",
"Memory");
namespace xe {
uint32_t get_page_count(uint32_t value, uint32_t page_size) {
@@ -96,6 +102,24 @@ xe::memory::PageAccess ToPageAccess(uint32_t protect) {
}
}
void RandomizeMemory(void* range_start, uint32_t size) {
if (!cvars::scribble_heap) {
return;
}
if (!cvars::scribble_heap_value) {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, std::numeric_limits<uint8_t>::max());
std::generate(static_cast<char*>(range_start),
static_cast<char*>(range_start) + size,
[&]() { return dis(gen); });
} else {
std::memset(range_start, cvars::scribble_heap_value, size);
}
}
Memory::Memory() {
system_page_size_ = uint32_t(xe::memory::page_size());
system_allocation_granularity_ =
@@ -973,7 +997,7 @@ bool BaseHeap::AllocFixed(uint32_t base_address, uint32_t size,
}
if (cvars::scribble_heap && protect & kMemoryProtectWrite) {
std::memset(result, 0xCD, page_count * page_size_);
RandomizeMemory(result, page_count * page_size_);
}
}
@@ -1135,7 +1159,7 @@ bool BaseHeap::AllocRange(uint32_t low_address, uint32_t high_address,
}
if (cvars::scribble_heap && (protect & kMemoryProtectWrite)) {
std::memset(result, 0xCD, page_count << page_size_shift_);
RandomizeMemory(result, page_count << page_size_shift_);
}
}
+6 -1
View File
@@ -272,7 +272,8 @@ std::unique_ptr<ImmediateTexture> ImGuiDrawer::LoadImGuiIcon(
width, height, ImmediateTextureFilter::kLinear, true,
reinterpret_cast<uint8_t*>(image_data));
stbi_image_free(image_data); // Free the image data after creating texture
stbi_image_free(image_data);
return texture;
}
@@ -310,6 +311,8 @@ void ImGuiDrawer::SetupNotificationTextures() {
notification_icon_textures_.push_back(immediate_drawer_->CreateTexture(
width, height, ImmediateTextureFilter::kLinear, true,
reinterpret_cast<uint8_t*>(image_data)));
stbi_image_free(image_data);
}
}
@@ -550,6 +553,8 @@ void ImGuiDrawer::SetImmediateDrawer(ImmediateDrawer* new_immediate_drawer) {
locked_achievement_icon_ = immediate_drawer_->CreateTexture(
width, height, ImmediateTextureFilter::kLinear, true,
reinterpret_cast<uint8_t*>(image_data));
stbi_image_free(image_data);
}
}
@@ -93,7 +93,7 @@ XContentContainerDevice::Result SvodContainerDevice::Read() {
static_assert_size(root_data, 0x10);
if (fread(&root_data, sizeof(root_data), 1, svod_header) != 1) {
XELOGE("ReadSVOD failed to read root block data at 0x{X}",
XELOGE("ReadSVOD failed to read root block data at 0x{:016X}",
magic_offset + 0x14);
return Result::kReadError;
}
@@ -335,9 +335,11 @@ XContentContainerDevice::Result SvodContainerDevice::SetNormalLayout(
FILE* header, size_t& magic_offset) {
uint8_t magic_buf[20];
xe::filesystem::Seek(header, 0xD000, SEEK_SET);
const uint32_t magic_pos =
header_->content_metadata.data_file_count == 1 ? 0xD000 : 0x2000;
xe::filesystem::Seek(header, magic_pos, SEEK_SET);
if (fread(magic_buf, 1, countof(magic_buf), header) != countof(magic_buf)) {
XELOGE("ReadSVOD failed to read SVOD magic at 0xD000");
XELOGE("ReadSVOD failed to read SVOD magic at 0x{:04X}", magic_pos);
return Result::kReadError;
}
@@ -350,18 +352,16 @@ XContentContainerDevice::Result SvodContainerDevice::SetNormalLayout(
// is a single-file system. The STFS Header is 0xB000 bytes and the
// remaining 0x2000 is from hash tables. In most cases, these will be
// STFS, not SVOD.
svod_base_offset_ = 0xB000;
magic_offset = 0xD000;
// Check for single file system
if (header_->content_metadata.data_file_count == 1) {
svod_base_offset_ = 0xB000;
svod_layout_ = SvodLayoutType::kSingleFile;
XELOGI("SVOD is a single file. Magic block present at 0xD000.");
magic_offset = 0xD000;
} else {
svod_layout_ = SvodLayoutType::kUnknown;
XELOGE(
"SVOD is not a single file, but the magic block was found at "
"0xD000.");
svod_base_offset_ = 0x0000;
svod_layout_ = SvodLayoutType::kMultipleFiles;
XELOGI("SVOD is a multiple files. Magic block present at 0x2000.");
magic_offset = 0x2000;
}
return Result::kSuccess;
}
@@ -41,6 +41,7 @@ class SvodContainerDevice : public XContentContainerDevice {
kEnhancedGDF = 0x1,
kXSF = 0x2,
kSingleFile = 0x4,
kMultipleFiles = 0x8,
};
const char* MEDIA_MAGIC = "MICROSOFT*XBOX*MEDIA";