Merge 5c3faef23b
into d9092fb232
This commit is contained in:
commit
03a081c4c9
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue