[XAM/CONTENT] - Add Missing returns and functions
- Add missing return values to XamContentCreateEx & XamContentCreateInternal - Add Struct of XCONTENT_DATA_INTERNAL - Properly implement XamProfileOpen - Stub XamContentGetDeviceVolumePath
This commit is contained in:
committed by
Radosław Gliński
parent
d430342d93
commit
3dc8130ed6
@@ -68,6 +68,8 @@ X_HRESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||
uint8_t* content_data_ptr =
|
||||
memory_->TranslateVirtual<uint8_t*>(data_ptr->buffer_ptr);
|
||||
|
||||
assert_true(data_ptr->buffer_size == sizeof(XCONTENT_AGGREGATE_DATA));
|
||||
|
||||
std::memset(content_data_ptr, 0, data_ptr->buffer_size);
|
||||
|
||||
uint32_t item_count = 0;
|
||||
|
||||
@@ -39,22 +39,22 @@ namespace xam {
|
||||
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
|
||||
@@ -119,6 +119,65 @@ struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA {
|
||||
};
|
||||
static_assert_size(XCONTENT_AGGREGATE_DATA, 0x148);
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
|
||||
// added in V4532 0x148
|
||||
be<uint64_t> content_size; // 0x148 sz:0x8
|
||||
|
||||
// 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());
|
||||
set_file_name(other.file_name());
|
||||
padding[0] = padding[1] = 0;
|
||||
xuid = 0;
|
||||
title_id = kCurrentlyRunningTitleId;
|
||||
}
|
||||
|
||||
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 &&
|
||||
content_type == other.content_type &&
|
||||
file_name() == other.file_name();
|
||||
}
|
||||
};
|
||||
static_assert_size(XCONTENT_DATA_INTERNAL, 0x200);
|
||||
|
||||
class ContentPackage {
|
||||
public:
|
||||
ContentPackage(KernelState* kernel_state, const std::string_view root_name,
|
||||
|
||||
@@ -74,17 +74,15 @@ 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) {
|
||||
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 +94,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 +109,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());
|
||||
@@ -229,7 +228,7 @@ dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name,
|
||||
lpdword_t disposition_ptr,
|
||||
lpdword_t license_mask_ptr,
|
||||
dword_t cache_size, qword_t content_size,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
uint64_t xuid = 0;
|
||||
if (user_index != XUserIndexNone) {
|
||||
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||
@@ -241,6 +240,10 @@ dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name,
|
||||
xuid = user->xuid();
|
||||
}
|
||||
|
||||
if (!root_name || *root_name == '\0') {
|
||||
return X_ERROR_INVALID_NAME;
|
||||
}
|
||||
|
||||
XCONTENT_AGGREGATE_DATA content_data;
|
||||
if (content_data_size == sizeof(XCONTENT_DATA)) {
|
||||
content_data = *content_data_ptr.as<XCONTENT_DATA*>();
|
||||
@@ -352,7 +355,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,21 +368,24 @@ 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_AGGREGATE_DATA*>();
|
||||
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,
|
||||
disposition_ptr, license_mask_ptr, cache_size,
|
||||
@@ -382,19 +393,17 @@ dword_result_t XamContentCreateInternal_entry(
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentCreateInternal, kContent, kImplemented);
|
||||
|
||||
dword_result_t XamContentOpenFile_entry(dword_t user_index,
|
||||
lpstring_t root_name, lpstring_t path,
|
||||
dword_t flags,
|
||||
lpdword_t disposition_ptr,
|
||||
lpdword_t license_mask_ptr,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
dword_result_t XamContentOpenFile_entry(
|
||||
dword_t user_index, lpstring_t root_name, lpstring_t path, dword_t flags,
|
||||
lpdword_t disposition_ptr, lpdword_t license_mask_ptr,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
// TODO(gibbed): arguments assumed based on XamContentCreate.
|
||||
return X_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentOpenFile, kContent, kStub);
|
||||
|
||||
dword_result_t XamContentFlush_entry(lpstring_t root_name,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
X_RESULT result = X_ERROR_SUCCESS;
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, result);
|
||||
@@ -406,7 +415,7 @@ dword_result_t XamContentFlush_entry(lpstring_t root_name,
|
||||
DECLARE_XAM_EXPORT1(XamContentFlush, kContent, kStub);
|
||||
|
||||
dword_result_t XamContentClose_entry(lpstring_t root_name,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
// Closes a previously opened root from XamContentCreate*.
|
||||
auto result =
|
||||
kernel_state()->content_manager()->CloseContent(root_name.value());
|
||||
@@ -420,11 +429,9 @@ dword_result_t XamContentClose_entry(lpstring_t root_name,
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentClose, kContent, kImplemented);
|
||||
|
||||
dword_result_t XamContentGetCreator_entry(dword_t user_index,
|
||||
lpvoid_t content_data_ptr,
|
||||
lpdword_t is_creator_ptr,
|
||||
lpqword_t creator_xuid_ptr,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
dword_result_t XamContentGetCreator_entry(
|
||||
dword_t user_index, lpvoid_t content_data_ptr, lpdword_t is_creator_ptr,
|
||||
lpqword_t creator_xuid_ptr, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
if (!is_creator_ptr) {
|
||||
return X_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
@@ -488,11 +495,9 @@ dword_result_t XamContentGetCreator_entry(dword_t user_index,
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentGetCreator, kContent, kImplemented);
|
||||
|
||||
dword_result_t XamContentGetThumbnail_entry(dword_t user_index,
|
||||
lpvoid_t content_data_ptr,
|
||||
lpvoid_t buffer_ptr,
|
||||
lpdword_t buffer_size_ptr,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
dword_result_t XamContentGetThumbnail_entry(
|
||||
dword_t user_index, lpvoid_t content_data_ptr, lpvoid_t buffer_ptr,
|
||||
lpdword_t buffer_size_ptr, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||
|
||||
if (!user) {
|
||||
@@ -533,11 +538,9 @@ dword_result_t XamContentGetThumbnail_entry(dword_t user_index,
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentGetThumbnail, kContent, kImplemented);
|
||||
|
||||
dword_result_t XamContentSetThumbnail_entry(dword_t user_index,
|
||||
lpvoid_t content_data_ptr,
|
||||
lpvoid_t buffer_ptr,
|
||||
dword_t buffer_size,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
dword_result_t XamContentSetThumbnail_entry(
|
||||
dword_t user_index, lpvoid_t content_data_ptr, lpvoid_t buffer_ptr,
|
||||
dword_t buffer_size, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
const auto& user = kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||
|
||||
if (!user) {
|
||||
@@ -563,7 +566,7 @@ DECLARE_XAM_EXPORT1(XamContentSetThumbnail, kContent, kImplemented);
|
||||
|
||||
dword_result_t xeXamContentDelete(dword_t user_index, lpvoid_t content_data_ptr,
|
||||
dword_t content_data_size,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
pointer_t<XAM_OVERLAPPED> 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)) {
|
||||
@@ -591,16 +594,16 @@ dword_result_t xeXamContentDelete(dword_t user_index, lpvoid_t content_data_ptr,
|
||||
}
|
||||
}
|
||||
|
||||
dword_result_t XamContentDelete_entry(dword_t user_index,
|
||||
lpvoid_t content_data_ptr,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
dword_result_t XamContentDelete_entry(
|
||||
dword_t user_index, lpvoid_t content_data_ptr,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
return xeXamContentDelete(user_index, content_data_ptr, sizeof(XCONTENT_DATA),
|
||||
overlapped_ptr);
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentDelete, kContent, kImplemented);
|
||||
|
||||
dword_result_t XamContentDeleteInternal_entry(lpvoid_t content_data_ptr,
|
||||
lpvoid_t overlapped_ptr) {
|
||||
dword_result_t XamContentDeleteInternal_entry(
|
||||
lpvoid_t content_data_ptr, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
// INFO: Analysis of xam.xex shows that "internal" functions are wrappers with
|
||||
// 0xFE as user_index.
|
||||
// In XAM content size is set to 0x200.
|
||||
@@ -790,6 +793,24 @@ dword_result_t XamContentLaunchImageInternal_entry(lpvoid_t content_data_ptr,
|
||||
}
|
||||
|
||||
DECLARE_XAM_EXPORT1(XamContentLaunchImageInternal, kContent, kStub);
|
||||
|
||||
dword_result_t XamContentGetDeviceVolumePath_entry(dword_t device_id,
|
||||
lpvoid_t path_ptr,
|
||||
dword_t path_size,
|
||||
dword_t append_backslash) {
|
||||
std::string volume_path = "hdd0\\";
|
||||
if (device_id != static_cast<uint32_t>(DummyDeviceId::HDD)) {
|
||||
return X_ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
char* path =
|
||||
kernel_memory()->TranslateVirtual<char*>(path_ptr.guest_address());
|
||||
|
||||
string_util::copy_truncating(path, volume_path, path_size);
|
||||
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentGetDeviceVolumePath, kContent, kStub);
|
||||
} // namespace xam
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
@@ -44,16 +44,23 @@ 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_AGGREGATE_DATA> 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());
|
||||
|
||||
return result ? X_ERROR_SUCCESS : X_ERROR_INVALID_PARAMETER;
|
||||
if (!kernel_state()->xam_state()->profile_manager()->MountProfile(
|
||||
xuid, mount_path.value())) {
|
||||
return X_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
if (content_data_ptr) {
|
||||
content_data_ptr->content_type = XContentType::kProfile;
|
||||
content_data_ptr->device_id = 1;
|
||||
content_data_ptr->set_file_name(xe::string_util::to_hex_string(xuid));
|
||||
content_data_ptr->title_id = kDashboardID;
|
||||
}
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamProfileOpen, kNone, kImplemented);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user