From 921a33a7988b08a78fe3654c2611f80c19fd8d58 Mon Sep 17 00:00:00 2001 From: The-Little-Wolf <116989599+The-Little-Wolf@users.noreply.github.com> Date: Sun, 12 Oct 2025 12:32:30 -0700 Subject: [PATCH] [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> --- src/xenia/kernel/xam/xam_avatar.cc | 391 +++++++++++++++++++++------- src/xenia/kernel/xam/xam_avatar.h | 187 +++++++++++++ src/xenia/kernel/xam/xdbf/xdbf_io.h | 36 +++ 3 files changed, 517 insertions(+), 97 deletions(-) create mode 100644 src/xenia/kernel/xam/xam_avatar.h diff --git a/src/xenia/kernel/xam/xam_avatar.cc b/src/xenia/kernel/xam/xam_avatar.cc index 5bf8a8024..f1dee5e20 100644 --- a/src/xenia/kernel/xam/xam_avatar.cc +++ b/src/xenia/kernel/xam/xam_avatar.cc @@ -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 avatar_metadata_ptr, + pointer_t 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(UserSettingId::XPROFILE_GAMERCARD_AVATAR_INFO_1); + + X_USER_PROFILE_SETTING avatar_info_setting_data = {}; + + avatar_info_setting_data.user_index = static_cast(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 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 avatar_metadata_ptr, + dword_t avatar_component_mask, dword_t flags, lpdword_t result_buffer_ptr, + lpdword_t gpu_resource_buffer_ptr, + pointer_t 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 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 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(UserSettingId::XPROFILE_GAMERCARD_AVATAR_INFO_1); + + X_USER_PROFILE_SETTING setting_data; + setting_data.user_index = static_cast(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(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 avatar_metadata_ptr, + pointer_t 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 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 avatar_metadata_ptr, + pointer_t 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 avatar_metadata_ptr) { + return static_cast(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 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 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 asset_id_ptr, + pointer_t + content_data_ptr // pointer_t +) { + 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 XAnimationTypeMap = { // Animation Generic Stand {0x0040000000030003, "Animation Generic Stand 0"}, @@ -192,74 +337,126 @@ const static std::map 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 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(*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 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 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 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 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 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); diff --git a/src/xenia/kernel/xam/xam_avatar.h b/src/xenia/kernel/xam/xam_avatar.h new file mode 100644 index 000000000..c87573d84 --- /dev/null +++ b/src/xenia/kernel/xam/xam_avatar.h @@ -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_ diff --git a/src/xenia/kernel/xam/xdbf/xdbf_io.h b/src/xenia/kernel/xam/xdbf/xdbf_io.h index ba7f532b1..612d23ab1 100644 --- a/src/xenia/kernel/xam/xdbf/xdbf_io.h +++ b/src/xenia/kernel/xam/xdbf/xdbf_io.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