From b8296a9bc9fea45fd921d9562c3e31dd3a4e7a0f Mon Sep 17 00:00:00 2001 From: Gliniak Date: Sun, 3 Nov 2024 12:09:53 +0100 Subject: [PATCH] [XAM] Partial implementation of: XamContentResolve and Listing of ODD content packages It's not perfect as it doesn't take internal package name or data, but it should be good enough for most titles --- src/xenia/kernel/xam/content_manager.cc | 36 ++++++++++++++ src/xenia/kernel/xam/content_manager.h | 4 ++ src/xenia/kernel/xam/xam_content.cc | 65 +++++++++++++++++++++---- 3 files changed, 95 insertions(+), 10 deletions(-) diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index b1262fed2..dafbb7382 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -243,6 +243,42 @@ std::vector ContentManager::ListContent( return result; } +std::vector ContentManager::ListContentODD( + const uint32_t device_id, const uint64_t xuid, const uint32_t title_id, + const XContentType content_type) const { + std::vector result; + + auto xuid_str = fmt::format("{:016X}", xuid); + auto title_id_str = fmt::format("{:08X}", title_id); + auto content_type_str = + fmt::format("{:08X}", static_cast(content_type)); + + const std::filesystem::path game_content_path = + std::filesystem::path("GAME:") / "content" / xuid_str / title_id_str / + content_type_str; + + auto entry = kernel_state_->file_system()->ResolvePath( + xe::path_to_utf8(game_content_path)); + + if (!entry) { + return {}; + } + + for (const auto& child : entry->children()) { + XCONTENT_AGGREGATE_DATA content_data; + + content_data.device_id = device_id; + content_data.content_type = content_type; + content_data.set_display_name(xe::path_to_utf16(child->name())); + content_data.set_file_name(xe::path_to_utf8(child->name())); + content_data.title_id = title_id; + content_data.xuid = xuid; + result.emplace_back(std::move(content_data)); + } + + return result; +} + std::unique_ptr ContentManager::ResolvePackage( const std::string_view root_name, const uint64_t xuid, const XCONTENT_AGGREGATE_DATA& data, const uint32_t disc_number) { diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index 7fd582687..239561672 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -151,6 +151,10 @@ class ContentManager { const uint32_t device_id, const uint64_t xuid, const uint32_t title_id, const XContentType content_type) const; + std::vector ListContentODD( + const uint32_t device_id, const uint64_t xuid, const uint32_t title_id, + const XContentType content_type) const; + std::unique_ptr ResolvePackage( const std::string_view root_name, const uint64_t xuid, const XCONTENT_AGGREGATE_DATA& data, const uint32_t disc_number = -1); diff --git a/src/xenia/kernel/xam/xam_content.cc b/src/xenia/kernel/xam/xam_content.cc index 693033a6d..2f7117d78 100644 --- a/src/xenia/kernel/xam/xam_content.cc +++ b/src/xenia/kernel/xam/xam_content.cc @@ -76,18 +76,54 @@ 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, dword_t unk2, - dword_t unk3) { + dword_t unk1, lpdword_t root_name_ptr, + lpvoid_t overlapped_ptr) { auto content_data = content_data_ptr.as(); + uint64_t xuid = 0; + const auto profile = + kernel_state()->xam_state()->profile_manager()->GetProfile( + static_cast(user_index)); + if (profile && content_data->content_type == XContentType::kSavedGame) { + xuid = profile->xuid(); + } + 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->device_id == static_cast(DummyDeviceId::HDD)) { + root_device_path = "\\Device\\Harddisk0\\Partition1\\Content\\"; + } else if (content_data->device_id == + static_cast(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_INVALID_PARAMETER; + } + } + + const std::string relative_path = fmt::format( + "{:016X}\\{:08X}\\{:08X}\\{}", xuid, kernel_state()->title_id(), + static_cast(content_data->content_type.get()), + content_data->file_name()); + + char* buffer = + kernel_memory()->TranslateVirtual(buffer_ptr.guest_address()); + + string_util::copy_truncating(buffer, root_device_path + relative_path, + buffer_size); + + // Check if it exists and try to mount that package // Result of buffer_ptr is sent to RtlInitAnsiString. // buffer_size is usually 260 (max path). - // Games expect zero if resolve was successful. - assert_always(); - XELOGW("XamContentResolve unimplemented!"); - return X_ERROR_NOT_FOUND; + return X_ERROR_SUCCESS; } -DECLARE_XAM_EXPORT1(XamContentResolve, kContent, kStub); +DECLARE_XAM_EXPORT1(XamContentResolve, kContent, kSketchy); // https://github.com/MrColdbird/gameservice/blob/master/ContentManager.cpp // https://github.com/LestaD/SourceEngine2007/blob/master/se2007/engine/xboxsystem.cpp#L499 @@ -136,7 +172,8 @@ dword_result_t XamContentCreateEnumerator_entry( auto user_enumerated_data = kernel_state()->content_manager()->ListContent( static_cast(DummyDeviceId::HDD), xuid, - kernel_state()->title_id(), XContentType(uint32_t(content_type))); + kernel_state()->title_id(), + static_cast(content_type.value())); enumerated_content.insert(enumerated_content.end(), user_enumerated_data.cbegin(), @@ -147,7 +184,8 @@ dword_result_t XamContentCreateEnumerator_entry( auto common_enumerated_data = kernel_state()->content_manager()->ListContent( static_cast(DummyDeviceId::HDD), 0, - kernel_state()->title_id(), XContentType(uint32_t(content_type))); + kernel_state()->title_id(), + static_cast(content_type.value())); enumerated_content.insert(enumerated_content.end(), common_enumerated_data.cbegin(), @@ -161,7 +199,14 @@ dword_result_t XamContentCreateEnumerator_entry( } if (!device_info || device_info->device_id == DummyDeviceId::ODD) { - // TODO(gibbed): disc drive content + auto disc_enumerated_data = + kernel_state()->content_manager()->ListContentODD( + static_cast(DummyDeviceId::ODD), 0, + kernel_state()->title_id(), XContentType(uint32_t(content_type))); + + enumerated_content.insert(enumerated_content.end(), + disc_enumerated_data.cbegin(), + disc_enumerated_data.cend()); } for (const auto& content_data : enumerated_content) {