mirror of
https://github.com/izzy2lost/xenia-edge.git
synced 2026-07-06 00:20:26 -07:00
Merge remote-tracking branch 'upstream/canary_experimental' into edge
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user