[XAM/AVATAR] - expanding on stubs
- Add structs - Sketchy implementation of XamAvatarGetInstalledAssetPackageDescription - Stub XamAvatarSetManifest - Have XAvatarGetAssets return X_E_FAIL in overlapped to allow [58410907](https://github.com/xenia-project/game-compatibility/issues/1848) to start match in netplay - Adrian helped find lots of the unknowns and stub XamAvatarGetManifestLocalUser Co-Authored-By: Adrian <78108584+AdrianCassar@users.noreply.github.com>
This commit is contained in:
committed by
Radosław Gliński
parent
7738ff7225
commit
921a33a798
@@ -2,16 +2,15 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2024 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2025 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/kernel/xam/xam_avatar.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/kernel/xam/xam_private.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
DEFINE_bool(allow_avatar_initialization, false,
|
||||
"Enable Avatar Initialization\n"
|
||||
@@ -25,12 +24,12 @@ namespace xam {
|
||||
|
||||
// Start/End
|
||||
dword_result_t XamAvatarInitialize_entry(
|
||||
dword_t unk1, // 1, 2, 4, etc
|
||||
dword_t unk2, // 0 or 1
|
||||
dword_t processor_number, // for thread creation?
|
||||
lpdword_t function_ptrs, // 20b, 5 pointers
|
||||
lpunknown_t unk5, // ptr in data segment
|
||||
dword_t unk6 // flags - 0x00300000, 0x30, etc
|
||||
dword_t coordinate_system, // 1, 2, 4, etc
|
||||
dword_t unk2, // 0 or 1
|
||||
dword_t processor_number, // for thread creation?
|
||||
lpdword_t function_ptrs, // 20b, 5 pointers
|
||||
lpdword_t unk5, // data segment ptr
|
||||
dword_t unk6 // flags - 0x00300000, 0x30, etc
|
||||
) {
|
||||
if (kernel_state()->title_id() == kAvatarEditorID) {
|
||||
return X_STATUS_SUCCESS;
|
||||
@@ -42,126 +41,273 @@ DECLARE_XAM_EXPORT1(XamAvatarInitialize, kAvatars, kStub);
|
||||
|
||||
void XamAvatarShutdown_entry() {
|
||||
// Calls XMsgStartIORequestEx(0xf3,0x600002,0,0,0,0).
|
||||
// in 12611 its XamUnloadSysApp(0xf2,1)
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarShutdown, kAvatars, kStub);
|
||||
|
||||
// Get & Set
|
||||
dword_result_t XamAvatarGetManifestLocalUser_entry(dword_t user_index,
|
||||
lpdword_t avatar_metadata,
|
||||
qword_t unk3) {
|
||||
// XMsgStartIORequestEx(0xf3, 0x600003, unk3, stack1, 8, 0)
|
||||
dword_result_t XamAvatarGetManifestLocalUser_entry(
|
||||
dword_t user_index, pointer_t<X_AVATAR_METADATA> avatar_metadata_ptr,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
auto run = [=](uint32_t& extended_error, uint32_t& length) {
|
||||
extended_error = X_ERROR_SUCCESS;
|
||||
length = 0;
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
if (user_index >= XUserMaxUserCount) {
|
||||
extended_error = X_E_INVALIDARG;
|
||||
return X_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!avatar_metadata_ptr) {
|
||||
extended_error = X_E_INVALIDARG;
|
||||
return X_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const auto user_profile =
|
||||
kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||
|
||||
if (!user_profile) {
|
||||
extended_error = X_E_NO_SUCH_USER;
|
||||
return X_ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
const uint32_t avatar_info_id =
|
||||
static_cast<uint32_t>(UserSettingId::XPROFILE_GAMERCARD_AVATAR_INFO_1);
|
||||
|
||||
X_USER_PROFILE_SETTING avatar_info_setting_data = {};
|
||||
|
||||
avatar_info_setting_data.user_index = static_cast<uint32_t>(user_index);
|
||||
avatar_info_setting_data.setting_id = avatar_info_id;
|
||||
avatar_info_setting_data.data.type = X_USER_DATA_TYPE::BINARY;
|
||||
avatar_info_setting_data.data.data.binary.size = kMaxUserDataSize;
|
||||
avatar_info_setting_data.data.data.binary.ptr = 0;
|
||||
|
||||
uint32_t avatar_metadata_address = avatar_metadata_ptr.guest_address();
|
||||
|
||||
const bool has_avatar_info_setting =
|
||||
kernel_state()->xam_state()->user_tracker()->GetUserSetting(
|
||||
user_profile->xuid(), kDashboardID, avatar_info_id,
|
||||
&avatar_info_setting_data, avatar_metadata_address);
|
||||
|
||||
// Profile doesn't have avatar info setting
|
||||
if (!avatar_info_setting_data.data.data.binary.ptr) {
|
||||
extended_error = X_E_FAIL;
|
||||
return X_ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
return X_ERROR_SUCCESS;
|
||||
};
|
||||
|
||||
if (!overlapped_ptr) {
|
||||
uint32_t extended_error, length;
|
||||
X_RESULT result = run(extended_error, length);
|
||||
|
||||
return result == X_ERROR_SUCCESS ? result : extended_error;
|
||||
}
|
||||
|
||||
kernel_state()->CompleteOverlappedDeferredEx(run, overlapped_ptr);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetManifestLocalUser, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarGetManifestsByXuid_entry(dword_t unk1, qword_t unk2,
|
||||
qword_t unk3, dword_t unk4,
|
||||
dword_t unk5, qword_t unk6) {
|
||||
// Function_818D1738(0x600004,param_6,&uStack_d0,0x90,0)
|
||||
dword_result_t XamAvatarGetManifestsByXuid_entry(
|
||||
dword_t user_index, dword_t xuid_count, lpqword_t xuid, dword_t unk,
|
||||
dword_t avatar_info_ptr, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
// set unk4 to 0 or 0x80990001 after
|
||||
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
|
||||
X_ERROR_SUCCESS);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetManifestsByXuid, kAvatars, kStub)
|
||||
|
||||
dword_result_t XamAvatarGetAssetsResultSize_entry(qword_t unk1, qword_t unk2,
|
||||
dword_t unk3) {
|
||||
// return XMsgInProcessCall(0xf3,0x600005,local_20,0);
|
||||
dword_result_t XamAvatarGetAssetsResultSize_entry(
|
||||
dword_t avatar_component_mask, lpdword_t result_buffer_size_ptr,
|
||||
lpdword_t gpu_resource_buffer_size_ptr) {
|
||||
*result_buffer_size_ptr = 0;
|
||||
*gpu_resource_buffer_size_ptr = 0;
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetAssetsResultSize, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarGetAssets_entry(qword_t unk1, word_t unk2, dword_t unk3,
|
||||
dword_t unk4, dword_t unk5,
|
||||
qword_t unk6) {
|
||||
dword_result_t XamAvatarGetAssets_entry(
|
||||
pointer_t<X_AVATAR_METADATA> avatar_metadata_ptr,
|
||||
dword_t avatar_component_mask, dword_t flags, lpdword_t result_buffer_ptr,
|
||||
lpdword_t gpu_resource_buffer_ptr,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
// 58410907 doesn't crash if we return failure.
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediateEx(
|
||||
overlapped_ptr, X_ERROR_FUNCTION_FAILED, X_E_FAIL, 0);
|
||||
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetAssets, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarSetCustomAsset_entry(qword_t unk1, qword_t unk2,
|
||||
dword_t unk3, dword_t unk4,
|
||||
dword_t unk5, dword_t unk6) {
|
||||
// XMsgInProcessCall(0xf3,0x60000a,local_30,0);
|
||||
|
||||
dword_result_t XamAvatarSetCustomAsset_entry(
|
||||
dword_t buffer_size, lpdword_t asset_data_ptr, dword_t custom_color_count,
|
||||
lpdword_t custom_colors_ptr,
|
||||
pointer_t<X_AVATAR_METADATA> avatar_metadata_ptr) {
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarSetCustomAsset, kAvatars, kStub)
|
||||
|
||||
dword_result_t XamAvatarSetManifest_entry(qword_t unk1, qword_t unk2,
|
||||
qword_t unk3) {
|
||||
// Function_818D1738(0x600009,param_3,&local_410,0x3ec,0);
|
||||
dword_result_t XamAvatarSetManifest_entry(
|
||||
dword_t user_index, dword_t avatar_info_ptr,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
auto run = [=](uint32_t& extended_error, uint32_t& length) {
|
||||
extended_error = X_ERROR_SUCCESS;
|
||||
length = 0;
|
||||
// Update and save settings.
|
||||
const auto& user_profile =
|
||||
kernel_state()->xam_state()->GetUserProfile(user_index);
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
// Skip writing data about users with id != 0 they're not supported
|
||||
if (!user_profile) {
|
||||
extended_error = X_E_NO_SUCH_USER;
|
||||
return X_ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
const uint32_t avatar_info_id =
|
||||
static_cast<uint32_t>(UserSettingId::XPROFILE_GAMERCARD_AVATAR_INFO_1);
|
||||
|
||||
X_USER_PROFILE_SETTING setting_data;
|
||||
setting_data.user_index = static_cast<uint32_t>(user_index);
|
||||
setting_data.setting_id = avatar_info_id;
|
||||
setting_data.data.type = X_USER_DATA_TYPE::BINARY;
|
||||
setting_data.data.data.binary.size = kMaxUserDataSize;
|
||||
setting_data.data.data.binary.ptr = static_cast<uint32_t>(avatar_info_ptr);
|
||||
|
||||
const UserSetting setting = UserSetting(&setting_data);
|
||||
|
||||
kernel_state()->xam_state()->user_tracker()->UpsertSetting(
|
||||
user_profile->xuid(), kDashboardID, &setting);
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
};
|
||||
|
||||
if (!overlapped_ptr) {
|
||||
uint32_t extended_error, length;
|
||||
X_RESULT result = run(extended_error, length);
|
||||
|
||||
return result == X_ERROR_SUCCESS ? result : extended_error;
|
||||
}
|
||||
|
||||
kernel_state()->CompleteOverlappedDeferredEx(run, overlapped_ptr);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarSetManifest, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarGetMetadataRandom_entry(word_t body_type, word_t unk2,
|
||||
lpdword_t avatar_metadata,
|
||||
qword_t unk4) {
|
||||
// some_function(0x600010,param_4,local_28,0xc,local_30)
|
||||
dword_result_t XamAvatarGetMetadataRandom_entry(
|
||||
dword_t body_type, dword_t avatars_count,
|
||||
pointer_t<X_AVATAR_METADATA> avatar_metadata_ptr,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
|
||||
X_ERROR_SUCCESS);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetMetadataRandom, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarGetMetadataSignedOutProfileCount_entry(dword_t unk1,
|
||||
qword_t unk2) {
|
||||
// Function_818D1738(0x600013,param_2,local_10,4,0);
|
||||
dword_result_t XamAvatarGetMetadataSignedOutProfileCount_entry(
|
||||
lpdword_t profile_count_ptr, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
|
||||
X_ERROR_SUCCESS);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetMetadataSignedOutProfileCount, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarGetMetadataSignedOutProfile_entry(dword_t unk1,
|
||||
dword_t unk2,
|
||||
qword_t unk3) {
|
||||
// Function_818D1738(0x600014,param_3,local_10,8,0);
|
||||
dword_result_t XamAvatarGetMetadataSignedOutProfile_entry(
|
||||
dword_t profile_index, pointer_t<X_AVATAR_METADATA> avatar_metadata_ptr,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
|
||||
X_ERROR_SUCCESS);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetMetadataSignedOutProfile, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarManifestGetBodyType_entry(qword_t body_type) {
|
||||
/* Notes:
|
||||
- Calls XMsgStartIORequestEx(0xf3, 0x60000F, unk4, stack1, 0x18,
|
||||
0x10000000)
|
||||
- return either char of 1 - male, 2 - female, else - unknown
|
||||
*/
|
||||
|
||||
return '1';
|
||||
dword_result_t XamAvatarManifestGetBodyType_entry(
|
||||
pointer_t<X_AVATAR_METADATA> avatar_metadata_ptr) {
|
||||
return static_cast<uint8_t>(X_AVATAR_BODY_TYPE::Male);
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarManifestGetBodyType, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarGetInstrumentation_entry(qword_t unk1, lpdword_t unk2) {
|
||||
/* Notes:
|
||||
- unk1 not used?
|
||||
- unk1 recieves values of 1, 2, and 6
|
||||
- mark implemented once confirmed first param not used and params named
|
||||
*/
|
||||
if (unk2) {
|
||||
*unk2 = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetInstrumentation, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarGetAssetIcon_entry(lpqword_t unk1, dword_t unk2,
|
||||
dword_t unk3, dword_t unk4,
|
||||
qword_t unk5) {
|
||||
// XMsgStartIORequestEx(0xf3, 0x60000B, unk5, stack1, 0x1C, 0x10000000)
|
||||
dword_result_t XamAvatarGetAssetIcon_entry(
|
||||
lpqword_t unk1, dword_t unk2, lpqword_t unk3, lpqword_t unk4,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
|
||||
X_ERROR_SUCCESS);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetAssetIcon, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarGetAssetBinary_entry(lpqword_t unk1, dword_t unk2,
|
||||
dword_t unk3, dword_t unk4,
|
||||
qword_t unk5) {
|
||||
// Function_818D1738(0x600008,param_5,&uStack_60,0x1c,auStack_70);
|
||||
dword_result_t XamAvatarGetAssetBinary_entry(
|
||||
lpvoid_t asset_metadata, dword_t unk2, dword_t unk3, dword_t unk4,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
|
||||
X_ERROR_SUCCESS);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetAssetBinary, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarGetInstalledAssetPackageDescription_entry(
|
||||
qword_t unk1, qword_t unk2) {
|
||||
return X_STATUS_SUCCESS;
|
||||
void XamAvatarGetInstalledAssetPackageDescription_entry(
|
||||
pointer_t<X_ASSET_ID> asset_id_ptr,
|
||||
pointer_t<XCONTENT_AGGREGATE_DATA>
|
||||
content_data_ptr // pointer_t<XCONTENT_DATA_INTERNAL>
|
||||
) {
|
||||
XCONTENT_AGGREGATE_DATA content_data;
|
||||
content_data.content_type = XContentType::kAvatarItem;
|
||||
content_data.xuid = 0;
|
||||
content_data.title_id = asset_id_ptr->title_id;
|
||||
std::string file_name =
|
||||
fmt::format("{:016X}{:08X}{:08X}", asset_id_ptr->data,
|
||||
asset_id_ptr->data2, asset_id_ptr->title_id);
|
||||
content_data.set_file_name(file_name);
|
||||
*content_data_ptr = content_data;
|
||||
XELOGD("Looking for avatar asset: {:X}", file_name);
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGetInstalledAssetPackageDescription, kAvatars,
|
||||
kStub);
|
||||
kSketchy);
|
||||
|
||||
void XamAvatarSetMocks_entry() {
|
||||
// No-op.
|
||||
@@ -169,7 +315,6 @@ void XamAvatarSetMocks_entry() {
|
||||
DECLARE_XAM_EXPORT1(XamAvatarSetMocks, kAvatars, kStub);
|
||||
|
||||
// Animation
|
||||
|
||||
const static std::map<uint64_t, std::string> XAnimationTypeMap = {
|
||||
// Animation Generic Stand
|
||||
{0x0040000000030003, "Animation Generic Stand 0"},
|
||||
@@ -192,74 +337,126 @@ const static std::map<uint64_t, std::string> XAnimationTypeMap = {
|
||||
};
|
||||
|
||||
// https://github.com/xenia-canary/xenia-canary/commit/212c99eee2724de15f471148d10197d89794ff32
|
||||
dword_result_t XamAvatarLoadAnimation_entry(lpqword_t asset_id_ptr,
|
||||
dword_t flags, dword_t output,
|
||||
dword_t unk1) {
|
||||
dword_result_t XamAvatarLoadAnimation_entry(
|
||||
lpqword_t asset_id_ptr, dword_t flags, lpvoid_t output,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
/* Notes:
|
||||
- Calls XMsgStartIORequestEx(0xf3, 0x60000F, unk4, stack1, 0x18,
|
||||
0x10000000)
|
||||
- I cannot find an error code for when asset_id_ptr returns nullptr. It
|
||||
could be due to decompiling issues
|
||||
- unknown[4] & unknown[0] = 0x10000000
|
||||
- Calls XMsgStartIORequestEx(0xf3, 0x60000F, overlapped_ptr, stack1, 0x18,
|
||||
unknown) 0xf2 12611
|
||||
*/
|
||||
assert_true(asset_id_ptr);
|
||||
|
||||
std::string summary = "Request to load avatar animation: ";
|
||||
|
||||
if (XAnimationTypeMap.find(*asset_id_ptr) != XAnimationTypeMap.cend()) {
|
||||
summary += XAnimationTypeMap.at(*asset_id_ptr);
|
||||
} else {
|
||||
summary += fmt::format("Unknown animation {}",
|
||||
summary += fmt::format("Unknown animation 0x{:016x}",
|
||||
static_cast<uint64_t>(*asset_id_ptr));
|
||||
}
|
||||
|
||||
XELOGD("{}", summary);
|
||||
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
|
||||
X_ERROR_SUCCESS);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarLoadAnimation, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarGenerateMipMaps_entry(
|
||||
lpdword_t avatar_assets_ptr, dword_t flags, dword_t buffer_size,
|
||||
lpdword_t mip_map_buffer_ptr, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
|
||||
X_ERROR_SUCCESS);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGenerateMipMaps, kAvatars, kStub);
|
||||
|
||||
// Enum
|
||||
dword_result_t XamAvatarBeginEnumAssets_entry(qword_t unk1, word_t unk2,
|
||||
dword_t unk3, word_t unk4,
|
||||
word_t unk5, qword_t unk6) {
|
||||
// XMsgStartIORequestEx(0xf3, 0x60000c, unkn6, stack1, 0x14, stack2)
|
||||
dword_result_t XamAvatarBeginEnumAssets_entry(
|
||||
dword_t unk1, dword_t unk2, dword_t unk3, word_t unk4, dword_t unk5,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
// unknown[4] & unknown[0] = 0x20080002
|
||||
// buffer_ptr[8]
|
||||
// XMsgStartIORequestEx(0xf3, 0x60000c, overlapped_ptr, buffer_ptr, 0x14,
|
||||
// unknown) 0xf2 12611
|
||||
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
|
||||
X_ERROR_SUCCESS);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarBeginEnumAssets, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarEnumAssets_entry(lpqword_t unk1, dword_t unk2,
|
||||
qword_t unk3) {
|
||||
// XMsgStartIORequestEx(0xf3, 0x60000d, unk3, stack1, 8, stack2)
|
||||
dword_result_t XamAvatarEnumAssets_entry(
|
||||
lpvoid_t unk1, lpqword_t unk2, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
// unk1 pointer to a struct of size 0x8a48
|
||||
// buffer_ptr = concat(unk1, unk2)
|
||||
// unknown = 0x20080002
|
||||
// XMsgStartIORequestEx(0xf3, 0x60000d, overlapped_ptr, &buffer_ptr, 8,
|
||||
// &unknown) 0xf2 12611
|
||||
|
||||
return X_E_NO_MORE_FILES; // Stop it from calling endlessly
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarEnumAssets, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarEndEnumAssets_entry(qword_t unk1) {
|
||||
// some_function(0x60000e,param_1,0,0,local_10)
|
||||
dword_result_t XamAvatarEndEnumAssets_entry(
|
||||
pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
// unknown[4]
|
||||
// unknown[0] = 0x20080002
|
||||
// XMsgStartIORequestEx(0xf2,0x60000e,overlapped_ptr,0,0,unknown); 12611
|
||||
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
|
||||
X_ERROR_SUCCESS);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarEndEnumAssets, kAvatars, kStub);
|
||||
|
||||
// Other
|
||||
dword_result_t XamAvatarGenerateMipMaps_entry(qword_t unk1, qword_t unk2,
|
||||
dword_t unk3, dword_t unk4,
|
||||
qword_t unk5) {
|
||||
// Function_818D1738(0x600007,param_5,local_30,0x10,local_40);
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarGenerateMipMaps, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarWearNow_entry(qword_t unk1, lpdword_t unk2,
|
||||
qword_t unk3) {
|
||||
return X_STATUS_SUCCESS;
|
||||
dword_result_t XamAvatarWearNow_entry(
|
||||
qword_t unk1, lpdword_t unk2, pointer_t<XAM_OVERLAPPED> overlapped_ptr) {
|
||||
X_RESULT result = X_ERROR_SUCCESS;
|
||||
if (kernel_state()->title_id() == kAvatarEditorID) {
|
||||
/*
|
||||
- ops
|
||||
XamSendMessageToLoadedApps(0xffffffff8000000e,0xffffffff80050018,lVar5);
|
||||
XNotifyBroadcast(0xffffffff80050018,lVar5);
|
||||
if (overlapped_ptr) {
|
||||
XMsgCompleteIORequest(overlapped_ptr,0,0,0);
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
// buffer_ptr = concat(unk1, *unk2);
|
||||
// XMsgStartIORequestEx(0xf3,0x600018,overlapped_ptr,&buffer_ptr,0x14,0);
|
||||
if (overlapped_ptr) {
|
||||
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, result);
|
||||
return X_ERROR_IO_PENDING;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarWearNow, kAvatars, kStub);
|
||||
|
||||
dword_result_t XamAvatarReinstallAwardedAsset_entry(qword_t unk1, qword_t unk2,
|
||||
qword_t unk3) {
|
||||
return X_STATUS_SUCCESS;
|
||||
dword_result_t XamAvatarReinstallAwardedAsset_entry(lpstring_t string_out_ptr,
|
||||
dword_t string_size,
|
||||
lpdword_t unk_ptr) {
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamAvatarReinstallAwardedAsset, kAvatars, kStub);
|
||||
|
||||
|
||||
187
src/xenia/kernel/xam/xam_avatar.h
Normal file
187
src/xenia/kernel/xam/xam_avatar.h
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_XAM_XAM_AVATAR_H_
|
||||
#define XENIA_KERNEL_XAM_XAM_AVATAR_H_
|
||||
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xam {
|
||||
|
||||
struct X_ASSET_ID { // X_GUID
|
||||
uint64_t data; // 0x0 sz:0x8
|
||||
uint32_t data2; // 0x8 sz:0x4
|
||||
uint32_t title_id; // 0xC sz:0x4
|
||||
};
|
||||
static_assert_size(X_ASSET_ID, 0x10);
|
||||
|
||||
// More Research Needed
|
||||
struct X_AVATAR_METADATA {
|
||||
// body type exists between 0x120 and 0x130
|
||||
uint8_t data1[0x4];
|
||||
uint32_t weight; // 0x4 sz:0x4
|
||||
uint32_t height; // 0x8 sz:0x4
|
||||
uint8_t data3[0xF0]; // 0xC sz:0xF0
|
||||
uint32_t skin_color; // 0xFC sz:0x4
|
||||
uint8_t data4[0x4]; // 0x100 sz:0x4
|
||||
uint32_t lipstick_color; // 0x104 sz:0x4
|
||||
uint8_t data5[0x8]; // 0x108 sz:0x4
|
||||
uint32_t eyeshadow_color; // 0x110 sz:0x4
|
||||
uint8_t data6[0x2D4];
|
||||
};
|
||||
static_assert_size(X_AVATAR_METADATA, 0x3E8);
|
||||
|
||||
// https://github.com/hetelek/Velocity/blob/master/XboxInternals/AvatarAsset/AvatarAssetDefinintions.h
|
||||
enum X_AVATAR_BODY_TYPE : uint8_t { Unknown, Male, Female, All };
|
||||
|
||||
// v2 assets its int32_t
|
||||
enum X_BINARY_ASSET_TYPE : uint32_t {
|
||||
Component = 1,
|
||||
Texture = 2,
|
||||
ShapeOverride = 3,
|
||||
Animation = 4,
|
||||
ShapeOverridePost = 5,
|
||||
};
|
||||
|
||||
enum X_ASSET_SUBCATEGORY : int32_t {
|
||||
// costume 0x64 - 0x6A
|
||||
CostumeShortDress = 0x64,
|
||||
CostumeLongDress = 0x65,
|
||||
CostumeFormalSuit = 0x67,
|
||||
CostumeCasualSuit = 0x68,
|
||||
CostumeCostume = 0x69,
|
||||
// shirt 0xC8 - 0xD2
|
||||
ShirtSportsTee = 0xC8,
|
||||
ShirtTee = 0xC9,
|
||||
ShirtVest = 0xCA,
|
||||
ShirtPolo = 0xCB,
|
||||
ShirtLongSleeveTee = 0xCC,
|
||||
ShirtShortSleeveShirt = 0xCD,
|
||||
ShirtLongSleeveShirt = 0xCE,
|
||||
ShirtSweater = 0xCF,
|
||||
ShirtHoodie = 0xD0,
|
||||
ShirtJacket = 0xD1,
|
||||
ShirtCoat = 0xD2,
|
||||
// pants 0x12C - 0x135
|
||||
PantsHotpants = 0x12C,
|
||||
PantsShorts = 0x12D,
|
||||
PantsLongShorts = 0x12E,
|
||||
PantsLeggings = 0x12F,
|
||||
PantsPants = 0x130,
|
||||
PantsCargo = 0x131,
|
||||
PantsJeans = 0x132,
|
||||
PantsShortSkirt = 0x133,
|
||||
PantsKilt = 0x134,
|
||||
PantsLongSkirt = 0x135,
|
||||
// shoes 0x190 - 0x197
|
||||
ShoesSandals = 0x190,
|
||||
ShoesHeels = 0x191,
|
||||
ShoesPumps = 0x192,
|
||||
ShoesFormal = 0x193,
|
||||
ShoesTrainers = 0x194,
|
||||
ShoesShortBoots = 0x195,
|
||||
ShoesHighBoots = 0x196,
|
||||
ShoesCostume = 0x197,
|
||||
// hat 0x1F4 - 0x1FD
|
||||
HatBeanie = 0x1F4,
|
||||
HatFlatCap = 0x1f5,
|
||||
HatBaseballCap = 0x1f6,
|
||||
HatPeakCap = 0x1f7,
|
||||
HatBrimmed = 0x1f8,
|
||||
HatFez = 0x1f9,
|
||||
HatHeadwrap = 0x1fa,
|
||||
HatCostume = 0x1fb,
|
||||
HatBearskin = 0x1fc,
|
||||
HatHelmet = 0x1fd,
|
||||
// gloves 0x258 - 0x259
|
||||
GlovesFingerless = 0x258,
|
||||
GlovesFullFingered = 0x259,
|
||||
// glasses 0x2BC - 0x2BE
|
||||
GlassesGlasses = 0x2BC,
|
||||
GlassesSunglasses = 0x2BD,
|
||||
GlassesCostume = 0x2BE,
|
||||
// wrist 0x320 - 0x323
|
||||
WristwearBracelet = 0x320,
|
||||
WristwearWatch = 0x321,
|
||||
WristwearBands = 0x322,
|
||||
WristwearSweatbands = 0x323,
|
||||
// earrings 0x384 - 0x38B
|
||||
EarringsSingleStud = 0x384,
|
||||
EarringsStuds = 0x385,
|
||||
EarringsSingleDangler = 0x386,
|
||||
EarringsDanglers = 0x387,
|
||||
EarringsSingleSmallHoop = 0x388,
|
||||
EarringsSmallHoops = 0x389,
|
||||
EarringsSingleLargeHoop = 0x38a,
|
||||
EarringsLargehoops = 0x38b,
|
||||
// ring 0x3E8 - 0x3EA
|
||||
RingRight = 0x3e8,
|
||||
RingLeft = 0x3e9,
|
||||
// carryable 0x44C - 0x44C
|
||||
Carryable = 0x44c,
|
||||
};
|
||||
|
||||
enum X_SKELETON_VERSION : uint8_t {
|
||||
Nxe = 1,
|
||||
Natal,
|
||||
NxeAndNatal,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct X_ASSET_METADATA {
|
||||
uint8_t metadata_version;
|
||||
X_AVATAR_BODY_TYPE gender;
|
||||
X_BINARY_ASSET_TYPE type;
|
||||
uint32_t asset_type_details;
|
||||
X_ASSET_SUBCATEGORY category;
|
||||
X_SKELETON_VERSION skeleton_version;
|
||||
};
|
||||
static_assert_size(X_ASSET_METADATA, 15);
|
||||
#pragma pack(pop)
|
||||
|
||||
struct X_RGB_COLOR {
|
||||
uint8_t blue;
|
||||
uint8_t green;
|
||||
uint8_t red;
|
||||
uint8_t alpha;
|
||||
};
|
||||
static_assert_size(X_RGB_COLOR, 4);
|
||||
|
||||
struct X_COLOR_GROUP {
|
||||
X_RGB_COLOR color;
|
||||
uint32_t unknown;
|
||||
};
|
||||
static_assert_size(X_COLOR_GROUP, 8);
|
||||
|
||||
struct X_COLOR_TABLE_ENTRY {
|
||||
X_COLOR_GROUP color[3];
|
||||
};
|
||||
static_assert_size(X_COLOR_TABLE_ENTRY, 24);
|
||||
|
||||
struct X_COLOR_TABLE {
|
||||
uint32_t count;
|
||||
uint32_t entries_ptr; // X_COLOR_TABLE_ENTRY*
|
||||
};
|
||||
static_assert_size(X_COLOR_TABLE, 8);
|
||||
|
||||
struct X_AVATAR_ANIMATION {
|
||||
uint32_t frame_count;
|
||||
float duration;
|
||||
float frames_per_second;
|
||||
};
|
||||
static_assert_size(X_AVATAR_ANIMATION, 12);
|
||||
|
||||
} // namespace xam
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_KERNEL_XAM_XAM_AVATAR_H_
|
||||
@@ -187,6 +187,42 @@ class XdbfFile {
|
||||
void LoadFreeEntries(const XdbfFileLoc* free_entries);
|
||||
};
|
||||
|
||||
struct X_STRB_HEADER {
|
||||
uint32_t magic;
|
||||
bool blockAlignmentStored;
|
||||
bool littleEndian;
|
||||
uint8_t guid[0x10];
|
||||
uint8_t blockIDSize;
|
||||
uint8_t blockSpanSize;
|
||||
uint16_t unused;
|
||||
uint8_t blockAlignment;
|
||||
int32_t blockHeaderSize;
|
||||
uint32_t blockStartAddress;
|
||||
};
|
||||
static_assert_size(X_STRB_HEADER, 36);
|
||||
|
||||
enum X_STRRB_BLOCK_ID : uint8_t {
|
||||
STRBEof = 0xFF, // -1
|
||||
STRBInvalid = 0,
|
||||
STRBAnimation = 1,
|
||||
STRBTexture = 2,
|
||||
STRBModel = 3,
|
||||
STRBShapeOverrides = 4,
|
||||
STRBSkeleton = 5,
|
||||
STRBAssetMetadata = 6,
|
||||
STRBCustomColorTable = 7,
|
||||
STRBAssetMetadataVersioned = 8,
|
||||
};
|
||||
|
||||
struct X_STRB_BLOCK {
|
||||
X_STRRB_BLOCK_ID id;
|
||||
int32_t data_length;
|
||||
int32_t field_size;
|
||||
uint32_t data_ptr; // uint8_t*
|
||||
uint32_t data_address;
|
||||
};
|
||||
static_assert_size(X_STRB_BLOCK, 20);
|
||||
|
||||
} // namespace xam
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
Reference in New Issue
Block a user