This commit is contained in:
The-Little-Wolf 2025-07-23 21:49:38 +00:00 committed by GitHub
commit 03a081c4c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 347 additions and 186 deletions

View File

@ -587,7 +587,7 @@ X_RESULT KernelState::ApplyTitleUpdate(
return ApplyTitleUpdate(title_module, patch_module);
}
std::vector<xam::XCONTENT_AGGREGATE_DATA> KernelState::FindTitleUpdate(
std::vector<xam::XCONTENT_DATA_INTERNAL> KernelState::FindTitleUpdate(
const uint32_t title_id) const {
if (!cvars::apply_title_update) {
return {};
@ -598,7 +598,7 @@ std::vector<xam::XCONTENT_AGGREGATE_DATA> KernelState::FindTitleUpdate(
}
const object_ref<UserModule> KernelState::LoadTitleUpdate(
const xam::XCONTENT_AGGREGATE_DATA* title_update,
const xam::XCONTENT_DATA_INTERNAL* title_update,
const object_ref<UserModule> module) {
uint32_t disc_number = -1;
if (module->is_multi_disc_title()) {

View File

@ -329,10 +329,10 @@ class KernelState {
int tls_static_data_address);
void InitializeKernelGuestGlobals();
std::vector<xam::XCONTENT_AGGREGATE_DATA> FindTitleUpdate(
std::vector<xam::XCONTENT_DATA_INTERNAL> FindTitleUpdate(
const uint32_t title_id) const;
const object_ref<UserModule> LoadTitleUpdate(
const xam::XCONTENT_AGGREGATE_DATA* title_update,
const xam::XCONTENT_DATA_INTERNAL* title_update,
const object_ref<UserModule> module);
bool IsPatchSignatureProper(const object_ref<UserModule> title_module,
const object_ref<UserModule> patch_module) const;

View File

@ -34,7 +34,7 @@ static int content_device_id_ = 0;
ContentPackage::ContentPackage(KernelState* kernel_state,
const std::string_view root_name,
const XCONTENT_AGGREGATE_DATA& data,
const XCONTENT_DATA_INTERNAL& data,
const std::filesystem::path& package_path)
: kernel_state_(kernel_state),
root_name_(root_name),
@ -64,12 +64,12 @@ void ContentPackage::LoadPackageLicenseMask(
auto file = xe::filesystem::OpenFile(header_path, "rb");
auto file_size = std::filesystem::file_size(header_path);
if (file_size < sizeof(XCONTENT_AGGREGATE_DATA) + sizeof(license_)) {
if (file_size < sizeof(XCONTENT_DATA_INTERNAL) + sizeof(license_)) {
fclose(file);
return;
}
fseek(file, sizeof(XCONTENT_AGGREGATE_DATA), SEEK_SET);
fseek(file, sizeof(XCONTENT_DATA_INTERNAL), SEEK_SET);
size_t result = fread(&license_, 1, sizeof(license_), file);
fclose(file);
}
@ -97,7 +97,7 @@ std::filesystem::path ContentManager::ResolvePackageRoot(
}
std::filesystem::path ContentManager::ResolvePackagePath(
const uint64_t xuid, const XCONTENT_AGGREGATE_DATA& data,
const uint64_t xuid, const XCONTENT_DATA_INTERNAL& data,
const uint32_t disc_number) {
// Content path:
// content_root/title_id/content_type/data_file_name/
@ -198,10 +198,10 @@ std::unordered_set<uint32_t> ContentManager::FindPublisherTitleIds(
return title_ids;
}
std::vector<XCONTENT_AGGREGATE_DATA> ContentManager::ListContent(
std::vector<XCONTENT_DATA_INTERNAL> ContentManager::ListContent(
const uint32_t device_id, const uint64_t xuid, const uint32_t title_id,
const XContentType content_type) const {
std::vector<XCONTENT_AGGREGATE_DATA> result;
std::vector<XCONTENT_DATA_INTERNAL> result;
std::unordered_set<uint32_t> title_ids = {title_id};
@ -221,7 +221,7 @@ std::vector<XCONTENT_AGGREGATE_DATA> ContentManager::ListContent(
continue;
}
XCONTENT_AGGREGATE_DATA content_data;
XCONTENT_DATA_INTERNAL content_data;
if (XSUCCEEDED(ReadContentHeaderFile(xe::path_to_utf8(file_info.name),
xuid, title_id, content_type,
content_data))) {
@ -240,10 +240,10 @@ std::vector<XCONTENT_AGGREGATE_DATA> ContentManager::ListContent(
return result;
}
std::vector<XCONTENT_AGGREGATE_DATA> ContentManager::ListContentODD(
std::vector<XCONTENT_DATA_INTERNAL> ContentManager::ListContentODD(
const uint32_t device_id, const uint64_t xuid, const uint32_t title_id,
const XContentType content_type) const {
std::vector<XCONTENT_AGGREGATE_DATA> result;
std::vector<XCONTENT_DATA_INTERNAL> result;
auto xuid_str = fmt::format("{:016X}", xuid);
auto title_id_str = fmt::format("{:08X}", title_id);
@ -262,7 +262,7 @@ std::vector<XCONTENT_AGGREGATE_DATA> ContentManager::ListContentODD(
}
for (const auto& child : entry->children()) {
XCONTENT_AGGREGATE_DATA content_data;
XCONTENT_DATA_INTERNAL content_data;
content_data.device_id = device_id;
content_data.content_type = content_type;
@ -278,7 +278,7 @@ std::vector<XCONTENT_AGGREGATE_DATA> ContentManager::ListContentODD(
std::unique_ptr<ContentPackage> ContentManager::ResolvePackage(
const std::string_view root_name, const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data, const uint32_t disc_number) {
const XCONTENT_DATA_INTERNAL& data, const uint32_t disc_number) {
auto package_path = ResolvePackagePath(xuid, data, disc_number);
if (!std::filesystem::exists(package_path)) {
return nullptr;
@ -292,13 +292,13 @@ std::unique_ptr<ContentPackage> ContentManager::ResolvePackage(
}
bool ContentManager::ContentExists(const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data) {
const XCONTENT_DATA_INTERNAL& data) {
auto path = ResolvePackagePath(xuid, data);
return std::filesystem::exists(path);
}
X_RESULT ContentManager::WriteContentHeaderFile(const uint64_t xuid,
XCONTENT_AGGREGATE_DATA data) {
XCONTENT_DATA_INTERNAL data) {
if (data.title_id == -1) {
data.title_id = kernel_state_->title_id();
}
@ -322,7 +322,7 @@ X_RESULT ContentManager::WriteContentHeaderFile(const uint64_t xuid,
if (std::filesystem::exists(header_path)) {
auto file = xe::filesystem::OpenFile(header_path, "wb");
fwrite(&data, 1, sizeof(XCONTENT_AGGREGATE_DATA), file);
fwrite(&data, 1, sizeof(XCONTENT_DATA_INTERNAL), file);
fclose(file);
return X_STATUS_SUCCESS;
}
@ -332,10 +332,10 @@ X_RESULT ContentManager::WriteContentHeaderFile(const uint64_t xuid,
X_RESULT ContentManager::ReadContentHeaderFile(
const std::string_view file_name, const uint64_t xuid,
const uint32_t title_id, XContentType content_type,
XCONTENT_AGGREGATE_DATA& data) const {
XCONTENT_DATA_INTERNAL& data) const {
auto header_file_path =
ResolvePackageHeaderPath(file_name, xuid, title_id, content_type);
constexpr uint32_t header_size = sizeof(XCONTENT_AGGREGATE_DATA);
constexpr uint32_t header_size = sizeof(XCONTENT_DATA_INTERNAL);
if (std::filesystem::exists(header_file_path)) {
auto file = xe::filesystem::OpenFile(header_file_path, "rb");
@ -363,7 +363,7 @@ X_RESULT ContentManager::ReadContentHeaderFile(
X_RESULT ContentManager::CreateContent(const std::string_view root_name,
const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data) {
const XCONTENT_DATA_INTERNAL& data) {
auto global_lock = global_critical_region_.Acquire();
if (open_packages_.count(string_key(root_name))) {
@ -391,7 +391,7 @@ X_RESULT ContentManager::CreateContent(const std::string_view root_name,
X_RESULT ContentManager::OpenContent(const std::string_view root_name,
const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data,
const XCONTENT_DATA_INTERNAL& data,
uint32_t& content_license,
const uint32_t disc_number) {
auto global_lock = global_critical_region_.Acquire();
@ -446,9 +446,9 @@ X_RESULT ContentManager::CloseContent(const std::string_view root_name) {
return X_ERROR_SUCCESS;
}
X_RESULT ContentManager::GetContentThumbnail(
const uint64_t xuid, const XCONTENT_AGGREGATE_DATA& data,
std::vector<uint8_t>* buffer) {
X_RESULT ContentManager::GetContentThumbnail(const uint64_t xuid,
const XCONTENT_DATA_INTERNAL& data,
std::vector<uint8_t>* buffer) {
auto global_lock = global_critical_region_.Acquire();
auto package_path = ResolvePackagePath(xuid, data);
@ -465,9 +465,9 @@ X_RESULT ContentManager::GetContentThumbnail(
}
}
X_RESULT ContentManager::SetContentThumbnail(
const uint64_t xuid, const XCONTENT_AGGREGATE_DATA& data,
std::vector<uint8_t> buffer) {
X_RESULT ContentManager::SetContentThumbnail(const uint64_t xuid,
const XCONTENT_DATA_INTERNAL& data,
std::vector<uint8_t> buffer) {
auto global_lock = global_critical_region_.Acquire();
auto package_path = ResolvePackagePath(xuid, data);
std::filesystem::create_directories(package_path);
@ -483,7 +483,7 @@ X_RESULT ContentManager::SetContentThumbnail(
}
X_RESULT ContentManager::DeleteContent(const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data) {
const XCONTENT_DATA_INTERNAL& data) {
auto global_lock = global_critical_region_.Acquire();
if (IsContentOpen(data)) {
@ -508,7 +508,7 @@ std::filesystem::path ContentManager::ResolveGameUserContentPath(
title_id;
}
bool ContentManager::IsContentOpen(const XCONTENT_AGGREGATE_DATA& data) const {
bool ContentManager::IsContentOpen(const XCONTENT_DATA_INTERNAL& data) const {
return std::any_of(open_packages_.cbegin(), open_packages_.cend(),
[data](std::pair<string_key, ContentPackage*> content) {
return data == content.second->GetPackageContentData();

View File

@ -32,28 +32,28 @@ namespace xe {
namespace kernel {
namespace xam {
// If set in XCONTENT_AGGREGATE_DATA, will be substituted with the running
// If set in XCONTENT_DATA_INTERNAL, will be substituted with the running
// titles ID
// TODO: check if actual x360 kernel/xam has a value similar to this
constexpr uint32_t kCurrentlyRunningTitleId = 0xFFFFFFFF;
struct XCONTENT_DATA {
be<uint32_t> device_id;
be<XContentType> content_type;
be<uint32_t> device_id; // 0x0 sz:0x4
be<XContentType> content_type; // 0x4 sz:0x4
union {
// this should be be<uint16_t>, but that stops copy constructor being
// generated...
uint16_t uint[128];
char16_t chars[128];
} display_name_raw;
} display_name_raw; // 0x8 sz:0x100
char file_name_raw[42];
char file_name_raw[42]; // 0x108 sz:0x2A
// Some games use this padding field as a null-terminator, as eg.
// DLC packages usually fill the entire file_name_raw array
// Not every game sets it to 0 though, so make sure any file_name_raw reads
// only go up to 42 chars!
uint8_t padding[2];
uint8_t padding[2]; // 0x132 sz: 0x2
bool operator==(const XCONTENT_DATA& other) const {
// Package is located via device_id/content_type/file_name, so only need to
@ -93,12 +93,50 @@ struct XCONTENT_DATA {
};
static_assert_size(XCONTENT_DATA, 0x134);
struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA {
be<uint64_t> xuid; // some titles store XUID here?
be<uint32_t> title_id;
struct XCONTENT_DATA_MEDIA {
be<uint8_t> series_id[0x10]; // 0x0 sz:0x10
be<uint8_t> seasonid[0x10]; // 0x10 sz:0x10
be<uint16_t> season_number; // 0x20 sz:0x2
be<uint16_t> episode_number; // 0x22 sz:0x2
};
static_assert_size(XCONTENT_DATA_MEDIA, 0x24);
XCONTENT_AGGREGATE_DATA() = default;
XCONTENT_AGGREGATE_DATA(const XCONTENT_DATA& other) {
struct XCONTENT_DATA_AVATAR_ASSET {
be<uint32_t> subcategory; // 0x0 sz:0x4
be<int32_t> colorizable; // 0x4 sz:0x4
be<uint8_t> asset_id[0x10]; // 0x8 sz:0x10
be<uint8_t> skeleton_version_mask; // 0x18 sz:0x1
};
static_assert_size(XCONTENT_DATA_AVATAR_ASSET, 0x1C);
struct XCONTENT_DATA_INTERNAL : XCONTENT_DATA { // 0x0 sz:0x134
be<uint32_t> category; // 0x134 sz:0x4
be<uint64_t> xuid; // 0x138 sz:0x8
be<uint32_t> title_id; // 0x140 sz:0x4
be<uint32_t> license_mask; // 0x144 sz:0x4
// static_assert_size(XCONTENT_DATA_INTERNAL, 0x148);
// added in V4532 0x148
be<uint64_t> content_size; // 0x148 sz:0x8
// static_assert_size(XCONTENT_DATA_INTERNAL, 0x150);
// added in NXE
be<uint64_t> creation_time; // 0x150 sz:0x8 FILETIME
char16_t title_name[0x40]; // 0x158 sz:0x80
// compiler does not like
// union {
// XCONTENT_DATA_AVATAR_ASSET avatar_content_data; // 0x1D8 sz:0x1C
// XCONTENT_DATA_MEDIA media_content_data; // 0x1D8 sz:0x24
//};
be<uint8_t> padding[0x24]; // 0x1D8 sz:0x1C
be<uint8_t> content_flag; // 0x1FC sz:0x01
be<uint8_t> reserved[3]; // 0x1FD sz:0x3
XCONTENT_DATA_INTERNAL() = default;
XCONTENT_DATA_INTERNAL(const XCONTENT_DATA& other) {
device_id = other.device_id;
content_type = other.content_type;
set_display_name(other.display_name());
@ -108,7 +146,7 @@ struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA {
title_id = kCurrentlyRunningTitleId;
}
bool operator==(const XCONTENT_AGGREGATE_DATA& other) const {
bool operator==(const XCONTENT_DATA_INTERNAL& other) const {
// Package is located via device_id/title_id/content_type/file_name, so only
// need to compare those
return device_id == other.device_id && title_id == other.title_id &&
@ -116,18 +154,18 @@ struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA {
file_name() == other.file_name();
}
};
static_assert_size(XCONTENT_AGGREGATE_DATA, 0x148);
static_assert_size(XCONTENT_DATA_INTERNAL, 0x200);
class ContentPackage {
public:
ContentPackage(KernelState* kernel_state, const std::string_view root_name,
const XCONTENT_AGGREGATE_DATA& data,
const XCONTENT_DATA_INTERNAL& data,
const std::filesystem::path& package_path);
~ContentPackage();
void LoadPackageLicenseMask(const std::filesystem::path header_path);
const XCONTENT_AGGREGATE_DATA& GetPackageContentData() const {
const XCONTENT_DATA_INTERNAL& GetPackageContentData() const {
return content_data_;
}
@ -137,7 +175,7 @@ class ContentPackage {
KernelState* kernel_state_;
std::string root_name_;
std::string device_path_;
XCONTENT_AGGREGATE_DATA content_data_;
XCONTENT_DATA_INTERNAL content_data_;
uint32_t license_;
};
@ -147,42 +185,42 @@ class ContentManager {
const std::filesystem::path& root_path);
~ContentManager();
std::vector<XCONTENT_AGGREGATE_DATA> ListContent(
std::vector<XCONTENT_DATA_INTERNAL> ListContent(
const uint32_t device_id, const uint64_t xuid, const uint32_t title_id,
const XContentType content_type) const;
std::vector<XCONTENT_AGGREGATE_DATA> ListContentODD(
std::vector<XCONTENT_DATA_INTERNAL> ListContentODD(
const uint32_t device_id, const uint64_t xuid, const uint32_t title_id,
const XContentType content_type) const;
std::unique_ptr<ContentPackage> ResolvePackage(
const std::string_view root_name, const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data, const uint32_t disc_number = -1);
const XCONTENT_DATA_INTERNAL& data, const uint32_t disc_number = -1);
bool ContentExists(const uint64_t xuid, const XCONTENT_AGGREGATE_DATA& data);
bool ContentExists(const uint64_t xuid, const XCONTENT_DATA_INTERNAL& data);
X_RESULT WriteContentHeaderFile(const uint64_t xuid,
XCONTENT_AGGREGATE_DATA data);
XCONTENT_DATA_INTERNAL data);
X_RESULT ReadContentHeaderFile(const std::string_view file_name,
const uint64_t xuid, const uint32_t title_id,
XContentType content_type,
XCONTENT_AGGREGATE_DATA& data) const;
XCONTENT_DATA_INTERNAL& data) const;
X_RESULT CreateContent(const std::string_view root_name, const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data);
const XCONTENT_DATA_INTERNAL& data);
X_RESULT OpenContent(const std::string_view root_name, const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data,
const XCONTENT_DATA_INTERNAL& data,
uint32_t& content_license,
const uint32_t disc_number = -1);
X_RESULT CloseContent(const std::string_view root_name);
X_RESULT GetContentThumbnail(const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data,
const XCONTENT_DATA_INTERNAL& data,
std::vector<uint8_t>* buffer);
X_RESULT SetContentThumbnail(const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data,
const XCONTENT_DATA_INTERNAL& data,
std::vector<uint8_t> buffer);
X_RESULT DeleteContent(const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data);
const XCONTENT_DATA_INTERNAL& data);
std::filesystem::path ResolveGameUserContentPath(const uint64_t xuid);
bool IsContentOpen(const XCONTENT_AGGREGATE_DATA& data) const;
bool IsContentOpen(const XCONTENT_DATA_INTERNAL& data) const;
void CloseOpenedFilesFromContent(const std::string_view root_name);
uint64_t GetContentTotalSpace() const;
@ -193,7 +231,7 @@ class ContentManager {
const uint64_t xuid, const uint32_t title_id,
const XContentType content_type) const;
std::filesystem::path ResolvePackagePath(const uint64_t xuid,
const XCONTENT_AGGREGATE_DATA& data,
const XCONTENT_DATA_INTERNAL& data,
const uint32_t disc_number = -1);
std::filesystem::path ResolvePackageHeaderPath(
const std::string_view file_name, uint64_t xuid, uint32_t title_id,

View File

@ -74,17 +74,16 @@ dword_result_t XamContentGetLicenseMask_entry(lpdword_t mask_ptr,
}
DECLARE_XAM_EXPORT2(XamContentGetLicenseMask, kContent, kStub, kHighFrequency);
dword_result_t XamContentResolve_entry(dword_t user_index,
lpvoid_t content_data_ptr,
lpvoid_t buffer_ptr, dword_t buffer_size,
dword_t unk1, lpdword_t root_name_ptr,
lpvoid_t overlapped_ptr) {
auto content_data = content_data_ptr.as<XCONTENT_DATA*>();
dword_result_t XamContentResolve_entry(
dword_t user_index, pointer_t<XCONTENT_DATA> content_data_ptr,
lpvoid_t buffer_ptr, dword_t buffer_size, dword_t create_directory,
lpdword_t root_name_ptr, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
// bool create_directory
uint64_t xuid = 0;
const auto profile =
kernel_state()->xam_state()->profile_manager()->GetProfile(
static_cast<uint8_t>(user_index));
if (profile && content_data->content_type == XContentType::kSavedGame) {
if (profile && content_data_ptr->content_type == XContentType::kSavedGame) {
xuid = profile->xuid();
}
@ -96,9 +95,10 @@ dword_result_t XamContentResolve_entry(dword_t user_index,
// Unsupported for now.
return X_ERROR_INVALID_PARAMETER;
} else {
if (content_data->device_id == static_cast<uint32_t>(DummyDeviceId::HDD)) {
if (content_data_ptr->device_id ==
static_cast<uint32_t>(DummyDeviceId::HDD)) {
root_device_path = "\\Device\\Harddisk0\\Partition1\\Content\\";
} else if (content_data->device_id ==
} else if (content_data_ptr->device_id ==
static_cast<uint32_t>(DummyDeviceId::ODD)) {
// Or GAME, but D: usually means DVD drive meanwhile GAME always pinpoints
// to game, even if it is running from HDD
@ -110,8 +110,8 @@ dword_result_t XamContentResolve_entry(dword_t user_index,
const std::string relative_path = fmt::format(
"{:016X}\\{:08X}\\{:08X}\\{}", xuid, kernel_state()->title_id(),
static_cast<uint32_t>(content_data->content_type.get()),
content_data->file_name());
static_cast<uint32_t>(content_data_ptr->content_type.get()),
content_data_ptr->file_name());
char* buffer =
kernel_memory()->TranslateVirtual<char*>(buffer_ptr.guest_address());
@ -126,9 +126,46 @@ dword_result_t XamContentResolve_entry(dword_t user_index,
}
DECLARE_XAM_EXPORT1(XamContentResolve, kContent, kSketchy);
// https://github.com/MrColdbird/gameservice/blob/master/ContentManager.cpp
dword_result_t XamContentCreateEnumerator_entry(
dword_t user_index, dword_t device_id, dword_t content_type,
dword_result_t XamContentResolveInternal_entry(
pointer_t<XCONTENT_DATA_INTERNAL> content_data_ptr, lpvoid_t path_ptr,
dword_t path_size, dword_t create_directory, lpdword_t root_name_ptr,
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
// bool create_directory
std::string root_device_path = "";
if (root_name_ptr) {
// Check if root_name is valid.
// root_device_path = std::string(root_name_ptr);
// Unsupported for now.
return X_ERROR_INVALID_PARAMETER;
} else {
if (content_data_ptr->device_id ==
static_cast<uint32_t>(DummyDeviceId::HDD)) {
root_device_path = "\\Device\\Harddisk0\\Partition1\\Content\\";
} else {
return X_ERROR_INVALID_PARAMETER;
}
}
const std::string relative_path =
fmt::format("{:016X}\\{:08X}\\{:08X}\\{}",
static_cast<uint64_t>(content_data_ptr->xuid),
static_cast<uint32_t>(content_data_ptr->title_id),
static_cast<uint32_t>(content_data_ptr->content_type.get()),
content_data_ptr->file_name());
char* path =
kernel_memory()->TranslateVirtual<char*>(path_ptr.guest_address());
string_util::copy_truncating(path, root_device_path + relative_path,
path_size);
XELOGD(root_device_path + relative_path);
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamContentResolveInternal, kContent, kSketchy);
dword_result_t XamContentCreateEnumeratorInternal_entry(
qword_t xuid, dword_t device_id, dword_t content_type, dword_t title_id,
dword_t content_flags, dword_t items_per_enumerate,
lpdword_t buffer_size_ptr, lpdword_t handle_out) {
assert_not_null(handle_out);
@ -147,17 +184,6 @@ dword_result_t XamContentCreateEnumerator_entry(
*buffer_size_ptr = sizeof(XCONTENT_DATA) * items_per_enumerate;
}
uint64_t xuid = 0;
if (user_index != XUserIndexNone) {
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
if (!user) {
return X_ERROR_NO_SUCH_USER;
}
xuid = user->xuid();
}
auto e = make_object<XStaticEnumerator<XCONTENT_DATA>>(kernel_state(),
items_per_enumerate);
auto result = e->Initialize(XUserIndexAny, 0xFE, 0x20005, 0x20007, 0);
@ -165,7 +191,12 @@ dword_result_t XamContentCreateEnumerator_entry(
return result;
}
std::vector<XCONTENT_AGGREGATE_DATA> enumerated_content = {};
uint32_t title = title_id;
if (!title) {
title = kernel_state()->title_id();
}
std::vector<XCONTENT_DATA_INTERNAL> enumerated_content = {};
if (!device_info || device_info->device_id == DummyDeviceId::HDD) {
std::vector<uint64_t> xuids_to_enumerate = {};
@ -180,8 +211,7 @@ dword_result_t XamContentCreateEnumerator_entry(
for (const auto& xuid : xuids_to_enumerate) {
auto user_enumerated_data =
kernel_state()->content_manager()->ListContent(
static_cast<uint32_t>(DummyDeviceId::HDD), xuid,
kernel_state()->title_id(),
static_cast<uint32_t>(DummyDeviceId::HDD), xuid, title,
static_cast<XContentType>(content_type.value()));
enumerated_content.insert(enumerated_content.end(),
@ -198,8 +228,8 @@ dword_result_t XamContentCreateEnumerator_entry(
if (!device_info || device_info->device_id == DummyDeviceId::ODD) {
auto disc_enumerated_data =
kernel_state()->content_manager()->ListContentODD(
static_cast<uint32_t>(DummyDeviceId::ODD), 0,
kernel_state()->title_id(), XContentType(uint32_t(content_type)));
static_cast<uint32_t>(DummyDeviceId::ODD), 0, title,
XContentType(uint32_t(content_type)));
enumerated_content.insert(enumerated_content.end(),
disc_enumerated_data.cbegin(),
@ -209,27 +239,25 @@ dword_result_t XamContentCreateEnumerator_entry(
for (const auto& content_data : enumerated_content) {
auto item = e->AppendItem();
*item = content_data;
XELOGI("{}: Adding: {} (Filename: {}) to enumerator result", __func__,
xe::to_utf8(content_data.display_name()), content_data.file_name());
XELOGI(
"XamContentCreateEnumeratorInternal: Adding: {} (Filename: {}) to "
"enumerator result",
xe::to_utf8(content_data.display_name()), content_data.file_name());
}
XELOGD("XamContentCreateEnumerator: added {} items to enumerator",
XELOGD("XamContentCreateEnumeratorInternal: added {} items to enumerator",
e->item_count());
*handle_out = e->handle();
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamContentCreateEnumerator, kContent, kImplemented);
DECLARE_XAM_EXPORT1(XamContentCreateEnumeratorInternal, kContent, kImplemented);
enum class kDispositionState : uint32_t { Unknown = 0, Create = 1, Open = 2 };
dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name,
lpvoid_t content_data_ptr,
dword_t content_data_size, dword_t flags,
lpdword_t disposition_ptr,
lpdword_t license_mask_ptr,
dword_t cache_size, qword_t content_size,
lpvoid_t overlapped_ptr) {
// https://github.com/MrColdbird/gameservice/blob/master/ContentManager.cpp
dword_result_t XamContentCreateEnumerator_entry(
dword_t user_index, dword_t device_id, dword_t content_type,
dword_t content_flags, dword_t items_per_enumerate,
lpdword_t buffer_size_ptr, lpdword_t handle_out) {
uint64_t xuid = 0;
if (user_index != XUserIndexNone) {
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
@ -241,11 +269,41 @@ dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name,
xuid = user->xuid();
}
XCONTENT_AGGREGATE_DATA content_data;
return XamContentCreateEnumeratorInternal_entry(
xuid, device_id, content_type, 0, content_flags, items_per_enumerate,
buffer_size_ptr, handle_out);
}
DECLARE_XAM_EXPORT1(XamContentCreateEnumerator, kContent, kImplemented);
enum class kDispositionState : uint32_t { Unknown = 0, Create = 1, Open = 2 };
dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name,
lpvoid_t content_data_ptr,
dword_t content_data_size, dword_t flags,
lpdword_t disposition_ptr,
lpdword_t license_mask_ptr,
dword_t cache_size, qword_t content_size,
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
uint64_t xuid = 0;
if (user_index != XUserIndexNone) {
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
if (!user) {
return X_ERROR_NO_SUCH_USER;
}
xuid = user->xuid();
}
if (!root_name || *root_name == '\0') {
return X_ERROR_INVALID_NAME;
}
XCONTENT_DATA_INTERNAL content_data;
if (content_data_size == sizeof(XCONTENT_DATA)) {
content_data = *content_data_ptr.as<XCONTENT_DATA*>();
} else if (content_data_size == sizeof(XCONTENT_AGGREGATE_DATA)) {
content_data = *content_data_ptr.as<XCONTENT_AGGREGATE_DATA*>();
} else if (content_data_size == sizeof(XCONTENT_DATA_INTERNAL)) {
content_data = *content_data_ptr.as<XCONTENT_DATA_INTERNAL*>();
} else {
assert_always();
return X_ERROR_INVALID_PARAMETER;
@ -352,7 +410,12 @@ dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name,
dword_result_t XamContentCreateEx_entry(
dword_t user_index, lpstring_t root_name, lpvoid_t content_data_ptr,
dword_t flags, lpdword_t disposition_ptr, lpdword_t license_mask_ptr,
dword_t cache_size, qword_t content_size, lpvoid_t overlapped_ptr) {
dword_t cache_size, qword_t content_size,
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
auto content_data = *content_data_ptr.as<XCONTENT_DATA*>();
if (content_data.file_name_raw[0] == '\0') {
return X_ERROR_INVALID_NAME;
}
return xeXamContentCreate(user_index, root_name, content_data_ptr,
sizeof(XCONTENT_DATA), flags, disposition_ptr,
license_mask_ptr, cache_size, content_size,
@ -360,23 +423,26 @@ dword_result_t XamContentCreateEx_entry(
}
DECLARE_XAM_EXPORT1(XamContentCreateEx, kContent, kImplemented);
dword_result_t XamContentCreate_entry(dword_t user_index, lpstring_t root_name,
lpvoid_t content_data_ptr, dword_t flags,
lpdword_t disposition_ptr,
lpdword_t license_mask_ptr,
lpvoid_t overlapped_ptr) {
return xeXamContentCreate(user_index, root_name, content_data_ptr,
sizeof(XCONTENT_DATA), flags, disposition_ptr,
license_mask_ptr, 0, 0, overlapped_ptr);
dword_result_t XamContentCreate_entry(
dword_t user_index, lpstring_t root_name, lpvoid_t content_data_ptr,
dword_t flags, lpdword_t disposition_ptr, lpdword_t license_mask_ptr,
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
return XamContentCreateEx_entry(user_index, root_name, content_data_ptr,
flags, disposition_ptr, license_mask_ptr, 0,
0, overlapped_ptr);
}
DECLARE_XAM_EXPORT1(XamContentCreate, kContent, kImplemented);
dword_result_t XamContentCreateInternal_entry(
lpstring_t root_name, lpvoid_t content_data_ptr, dword_t flags,
lpdword_t disposition_ptr, lpdword_t license_mask_ptr, dword_t cache_size,
qword_t content_size, lpvoid_t overlapped_ptr) {
qword_t content_size, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
auto content_data_internal = *content_data_ptr.as<XCONTENT_DATA_INTERNAL*>();
if (content_data_internal.file_name_raw[0] == '\0') {
return X_ERROR_INVALID_NAME;
}
return xeXamContentCreate(XUserIndexNone, root_name, content_data_ptr,
sizeof(XCONTENT_AGGREGATE_DATA), flags,
sizeof(XCONTENT_DATA_INTERNAL), flags,
disposition_ptr, license_mask_ptr, cache_size,
content_size, overlapped_ptr);
}
@ -435,7 +501,7 @@ dword_result_t XamContentGetCreator_entry(dword_t user_index,
return X_ERROR_NO_SUCH_USER;
}
XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as<XCONTENT_DATA*>();
XCONTENT_DATA_INTERNAL content_data = *content_data_ptr.as<XCONTENT_DATA*>();
auto run = [content_data, xuid = user->xuid(), user_index, is_creator_ptr,
creator_xuid_ptr, overlapped_ptr](uint32_t& extended_error,
@ -501,7 +567,7 @@ dword_result_t XamContentGetThumbnail_entry(dword_t user_index,
assert_not_null(buffer_size_ptr);
uint32_t buffer_size = *buffer_size_ptr;
XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as<XCONTENT_DATA*>();
XCONTENT_DATA_INTERNAL content_data = *content_data_ptr.as<XCONTENT_DATA*>();
// Get thumbnail (if it exists).
std::vector<uint8_t> buffer;
@ -544,7 +610,7 @@ dword_result_t XamContentSetThumbnail_entry(dword_t user_index,
return X_ERROR_NO_SUCH_USER;
}
XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as<XCONTENT_DATA*>();
XCONTENT_DATA_INTERNAL content_data = *content_data_ptr.as<XCONTENT_DATA*>();
// Buffer is PNG data.
auto buffer = std::vector<uint8_t>((uint8_t*)buffer_ptr,
@ -565,9 +631,9 @@ dword_result_t xeXamContentDelete(dword_t user_index, lpvoid_t content_data_ptr,
dword_t content_data_size,
lpvoid_t overlapped_ptr) {
uint64_t xuid = 0;
XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as<XCONTENT_DATA*>();
if (content_data_size == sizeof(XCONTENT_AGGREGATE_DATA)) {
content_data = *content_data_ptr.as<XCONTENT_AGGREGATE_DATA*>();
XCONTENT_DATA_INTERNAL content_data = *content_data_ptr.as<XCONTENT_DATA*>();
if (content_data_size == sizeof(XCONTENT_DATA_INTERNAL)) {
content_data = *content_data_ptr.as<XCONTENT_DATA_INTERNAL*>();
}
if (user_index != XUserIndexNone) {
@ -605,7 +671,7 @@ dword_result_t XamContentDeleteInternal_entry(lpvoid_t content_data_ptr,
// 0xFE as user_index.
// In XAM content size is set to 0x200.
return xeXamContentDelete(XUserIndexNone, content_data_ptr,
sizeof(XCONTENT_AGGREGATE_DATA), overlapped_ptr);
sizeof(XCONTENT_DATA_INTERNAL), overlapped_ptr);
}
DECLARE_XAM_EXPORT1(XamContentDeleteInternal, kContent, kImplemented);
@ -689,12 +755,47 @@ void XamLoaderGetMediaInfo_entry(lpdword_t media_type, lpdword_t unk2) {
}
DECLARE_XAM_EXPORT1(XamLoaderGetMediaInfo, kNone, kStub);
dword_result_t XamContentLaunchImageFromFileInternal_entry(
lpstring_t image_location, lpstring_t xex_name, dword_t unk) {
const std::string image_path = static_cast<std::string>(image_location);
const std::string xex_name_ = static_cast<std::string>(xex_name);
dword_result_t xeXamContentLaunchImage(dword_t user_index,
lpstring_t image_location,
lpvoid_t content_data_ptr,
dword_t content_data_size,
lpstring_t xex_path, dword_t flag) {
XCONTENT_DATA_INTERNAL content_data_internal;
std::string package_path;
std::string xex_name_;
if (!image_location) {
uint32_t title_id;
// meant to use xeXamContentCreate
if (content_data_size == sizeof(XCONTENT_DATA)) {
content_data_internal = *content_data_ptr.as<XCONTENT_DATA*>();
// title_id is written into first 8 characters of filename
title_id = xe::string_util::from_string<uint32_t>(
content_data_internal.file_name().substr(0, 8), true);
} else if (content_data_size == sizeof(XCONTENT_DATA_INTERNAL)) {
content_data_internal = *content_data_ptr.as<XCONTENT_DATA_INTERNAL*>();
title_id = content_data_internal.title_id.get();
} else {
assert_always();
return X_ERROR_INVALID_PARAMETER;
}
vfs::Entry* entry = kernel_state()->file_system()->ResolvePath(image_path);
const uint32_t content =
static_cast<uint32_t>(content_data_internal.content_type.get());
const std::string name = content_data_internal.file_name();
// This should be done via content_manager, however as it isn't capable of
// such action we need to improvise.
xex_name_ = xex_path.value();
package_path =
fmt::format("GAME:/Content/0000000000000000/{:08X}/{:08X}/{}", title_id,
content, name);
} else {
// meant to use xeXamContentOpenFile
package_path = static_cast<std::string>(image_location);
xex_name_ = static_cast<std::string>(xex_path);
}
vfs::Entry* entry = kernel_state()->file_system()->ResolvePath(package_path);
if (!entry) {
return X_STATUS_NO_SUCH_FILE;
@ -702,10 +803,10 @@ dword_result_t XamContentLaunchImageFromFileInternal_entry(
const std::filesystem::path host_path =
kernel_state()->emulator()->content_root() / entry->name();
if (!std::filesystem::exists(host_path)) {
uint64_t progress = 0;
vfs::VirtualFileSystem::ExtractContentFile(
kernel_state()->file_system()->ExtractContentFile(
entry, kernel_state()->emulator()->content_root(), progress, true);
}
@ -733,63 +834,62 @@ dword_result_t XamContentLaunchImageFromFileInternal_entry(
return X_ERROR_SUCCESS;
}
dword_result_t XamContentLaunchImageFromFileInternal_entry(
lpstring_t image_location, lpstring_t xex_name) {
return xeXamContentLaunchImage(XUserIndexNone, image_location, 0, 0, xex_name,
0);
}
DECLARE_XAM_EXPORT1(XamContentLaunchImageFromFileInternal, kContent, kStub);
dword_result_t XamContentLaunchImage_entry(dword_t user_index,
lpvoid_t content_data_ptr,
lpstring_t xex_path) {
return xeXamContentLaunchImage(user_index, 0, content_data_ptr,
sizeof(XCONTENT_DATA), xex_path, 0);
}
DECLARE_XAM_EXPORT1(XamContentLaunchImage, kContent, kStub);
dword_result_t XamContentLaunchImageInternal_entry(lpvoid_t content_data_ptr,
lpstring_t xex_path) {
XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as<XCONTENT_DATA*>();
return xeXamContentLaunchImage(XUserIndexNone, 0, content_data_ptr,
sizeof(XCONTENT_DATA_INTERNAL), xex_path, 0);
}
DECLARE_XAM_EXPORT1(XamContentLaunchImageInternal, kContent, kStub);
// title_id is written into first 8 characters of filename
const uint32_t title_id = xe::string_util::from_string<uint32_t>(
content_data.file_name().substr(0, 8), true);
dword_result_t XamContentLaunchImageInternalEx_entry(lpvoid_t content_data_ptr,
lpstring_t xex_path,
dword_t flag) {
return xeXamContentLaunchImage(XUserIndexNone, 0, content_data_ptr,
sizeof(XCONTENT_DATA_INTERNAL), xex_path,
flag);
}
DECLARE_XAM_EXPORT1(XamContentLaunchImageInternalEx, kContent, kStub);
// This should be done via content_manager, however as it isn't capable of
// such action we need to improvise.
const std::string package_path =
fmt::format("GAME:/Content/0000000000000000/{:08X}/{:08X}/{}", title_id,
static_cast<uint32_t>(content_data.content_type.get()),
content_data.file_name());
auto entry = kernel_state()->file_system()->ResolvePath(package_path);
if (!entry) {
return X_STATUS_NO_SUCH_FILE;
dword_result_t XamContentGetDeviceVolumePath_entry(dword_t device_id,
lpvoid_t path_ptr,
dword_t path_size,
dword_t append_backslash) {
std::string root_device_path;
if (device_id == static_cast<uint32_t>(DummyDeviceId::HDD)) {
root_device_path = "\\Device\\Harddisk0\\Partition1\\Content\\";
} else if (device_id == static_cast<uint32_t>(DummyDeviceId::ODD)) {
// Or GAME, but D: usually means DVD drive meanwhile GAME always pinpoints
// to game, even if it is running from HDD
root_device_path = "D:\\content\\";
} else {
return X_ERROR_FUNCTION_FAILED;
}
const std::filesystem::path host_path =
kernel_state()->emulator()->content_root() / entry->name();
char* path =
kernel_memory()->TranslateVirtual<char*>(path_ptr.guest_address());
if (!std::filesystem::exists(host_path)) {
uint64_t progress = 0;
kernel_state()->file_system()->ExtractContentFile(
entry, kernel_state()->emulator()->content_root(), progress, true);
}
string_util::copy_truncating(path, root_device_path, path_size);
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
auto& loader_data = xam->loader_data();
loader_data.host_path = xe::path_to_utf8(host_path);
loader_data.launch_path = xex_path.value();
xam->SaveLoaderData();
auto display_window = kernel_state()->emulator()->display_window();
auto imgui_drawer = kernel_state()->emulator()->imgui_drawer();
if (display_window && imgui_drawer) {
display_window->app_context().CallInUIThreadSynchronous([imgui_drawer]() {
xe::ui::ImGuiDialog::ShowMessageBox(
imgui_drawer, "Launching new title!",
"Launching new title. \nPlease close Xenia and launch it again. Game "
"should load automatically.");
});
}
kernel_state()->TerminateTitle();
// bool append_backslash
return X_ERROR_SUCCESS;
}
DECLARE_XAM_EXPORT1(XamContentGetDeviceVolumePath, kContent, kStub);
DECLARE_XAM_EXPORT1(XamContentLaunchImageInternal, kContent, kStub);
} // namespace xam
} // namespace kernel
} // namespace xe

View File

@ -23,7 +23,7 @@ namespace xe {
namespace kernel {
namespace xam {
void AddODDContentTest(object_ref<XStaticEnumerator<XCONTENT_AGGREGATE_DATA>> e,
void AddODDContentTest(object_ref<XStaticEnumerator<XCONTENT_DATA_INTERNAL>> e,
XContentType content_type) {
auto root_entry = kernel_state()->file_system()->ResolvePath(
"game:\\Content\\0000000000000000");
@ -89,8 +89,8 @@ dword_result_t XamContentAggregateCreateEnumerator_entry(qword_t xuid,
return X_E_INVALIDARG;
}
auto e = make_object<XStaticEnumerator<XCONTENT_AGGREGATE_DATA>>(
kernel_state(), 1);
auto e =
make_object<XStaticEnumerator<XCONTENT_DATA_INTERNAL>>(kernel_state(), 1);
X_KENUMERATOR_CONTENT_AGGREGATE* extra;
auto result = e->Initialize(XUserIndexAny, 0xFE, 0x2000E, 0x20010, 0, &extra);
if (XFAILED(result)) {

View File

@ -44,16 +44,39 @@ dword_result_t XamProfileFindAccount_entry(
}
DECLARE_XAM_EXPORT1(XamProfileFindAccount, kUserProfiles, kImplemented);
dword_result_t XamProfileOpen_entry(qword_t xuid, lpstring_t mount_path,
dword_t flags, lpvoid_t content_data) {
dword_result_t XamProfileOpen_entry(
qword_t xuid, lpstring_t mount_path, dword_t flags,
pointer_t<XCONTENT_DATA_INTERNAL> content_data_ptr) {
/* Notes:
- If xuid is not local then returns X_ERROR_INVALID_PARAMETER
*/
const bool result =
kernel_state()->xam_state()->profile_manager()->MountProfile(
xuid, mount_path.value());
X_XAMACCOUNTINFO account;
XCONTENT_DATA_INTERNAL content_data;
X_STATUS status;
if (result == true) {
uint32_t device_id = 1;
status = XamProfileFindAccount_entry(xuid, &account, &device_id);
if (status == 0) {
const auto& profile =
kernel_state()->xam_state()->profile_manager()->GetAccount(xuid);
content_data.xuid = account.xuid_online; // "%.16I64X"
content_data.content_type = XContentType::kProfile;
content_data.set_display_name(profile->gamertag);
content_data.set_file_name(xe::string_util::to_hex_string(xuid));
content_data.title_id = 0xFFFE07D1;
// status = XamContentCreateInternal_entry(mount_path, &content_data, 3,
// 0, 0, 0, 0, 0);
if (content_data_ptr) { // status == 0 &&
*content_data_ptr = content_data;
}
}
return status;
}
return result ? X_ERROR_SUCCESS : X_ERROR_INVALID_PARAMETER;
return X_ERROR_INVALID_PARAMETER;
}
DECLARE_XAM_EXPORT1(XamProfileOpen, kNone, kImplemented);

View File

@ -152,11 +152,11 @@ void XContentContainerDevice::Dump(StringBuffer* string_buffer) {
root_entry_->Dump(string_buffer, 0);
}
kernel::xam::XCONTENT_AGGREGATE_DATA XContentContainerDevice::content_header()
kernel::xam::XCONTENT_DATA_INTERNAL XContentContainerDevice::content_header()
const {
kernel::xam::XCONTENT_AGGREGATE_DATA data;
kernel::xam::XCONTENT_DATA_INTERNAL data;
std::memset(&data, 0, sizeof(kernel::xam::XCONTENT_AGGREGATE_DATA));
std::memset(&data, 0, sizeof(kernel::xam::XCONTENT_DATA_INTERNAL));
data.device_id = 1;
data.title_id = header_->content_metadata.execution_info.title_id;

View File

@ -67,7 +67,7 @@ class XContentContainerDevice : public Device {
return static_cast<uint32_t>(header_->content_metadata.content_type.get());
}
kernel::xam::XCONTENT_AGGREGATE_DATA content_header() const;
kernel::xam::XCONTENT_DATA_INTERNAL content_header() const;
uint32_t license_mask() const {
uint32_t final_license = 0;
for (uint8_t i = 0; i < license_count; i++) {

View File

@ -445,12 +445,12 @@ void VirtualFileSystem::ExtractContentHeader(Device* device,
if (std::filesystem::exists(header_path)) {
auto file = xe::filesystem::OpenFile(header_path, "wb");
kernel::xam::XCONTENT_AGGREGATE_DATA data =
kernel::xam::XCONTENT_DATA_INTERNAL data =
xcontent_device->content_header();
uint32_t license_mask = xcontent_device->license_mask();
data.set_file_name(base_path.filename().string());
fwrite(&data, 1, sizeof(kernel::xam::XCONTENT_AGGREGATE_DATA), file);
fwrite(&data, 1, sizeof(kernel::xam::XCONTENT_DATA_INTERNAL), file);
fwrite(&license_mask, 1, sizeof(license_mask), file);
fclose(file);
}