diff --git a/src/core/hle/service/ns/application_manager_interface.cpp b/src/core/hle/service/ns/application_manager_interface.cpp index 81f42f0937..03d139fe41 100644 --- a/src/core/hle/service/ns/application_manager_interface.cpp +++ b/src/core/hle/service/ns/application_manager_interface.cpp @@ -13,6 +13,7 @@ #include "core/hle/service/ns/content_management_interface.h" #include "core/hle/service/ns/read_only_application_control_data_interface.h" #include "core/file_sys/patch_manager.h" +#include "frontend_common/firmware_manager.h" namespace Service::NS { @@ -429,13 +430,13 @@ Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled( } Result IApplicationManagerInterface::GetApplicationViewDeprecated( - OutArray out_application_views, + OutArray out_application_views, InArray application_ids) { const auto size = (std::min)(out_application_views.size(), application_ids.size()); LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size()); for (size_t i = 0; i < size; i++) { - ApplicationView view{}; + ApplicationViewV19 view{}; view.application_id = application_ids[i]; view.version = 0x70000; view.flags = 0x401f17; @@ -447,34 +448,53 @@ Result IApplicationManagerInterface::GetApplicationViewDeprecated( } Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo( - OutArray out_application_views, + OutBuffer out_buffer, + Out out_count, InArray application_ids) { - const auto size = (std::min)(out_application_views.size(), application_ids.size()); - LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size()); - - for (size_t i = 0; i < size; i++) { - ApplicationViewWithPromotionInfo view{}; - view.view.application_id = application_ids[i]; - view.view.version = 0x70000; - view.view.flags = 0x401f17; - view.promotion = {}; - - out_application_views[i] = view; + const auto requested = application_ids.size(); + LOG_WARNING(Service_NS, "(STUBBED) called, size={}", requested); + + const auto fw_pair = FirmwareManager::GetFirmwareVersion(system); + const bool is_fw20 = fw_pair.first.major >= 20; + + const size_t per_entry_size = is_fw20 ? (sizeof(ApplicationViewV20) + sizeof(PromotionInfo)) + : (sizeof(ApplicationViewV19) + sizeof(PromotionInfo)); + const size_t capacity_entries = out_buffer.size() / per_entry_size; + const size_t to_write_entries = (std::min)(requested, capacity_entries); + + u8* dst = out_buffer.data(); + for (size_t i = 0; i < to_write_entries; ++i) { + ApplicationViewWithPromotionData data{}; + data.view.application_id = application_ids[i]; + data.view.version = 0x70000; + data.view.unk = 0; + data.view.flags = 0x401f17; + data.view.download_state = {}; + data.view.download_progress = {}; + data.promotion = {}; + + const size_t written = WriteApplicationViewWithPromotion(dst, out_buffer.size() - (dst - out_buffer.data()), data, is_fw20); + if (written == 0) { + break; + } + dst += written; } + *out_count = static_cast(dst - out_buffer.data()) / static_cast(per_entry_size); R_SUCCEED(); } Result IApplicationManagerInterface::GetApplicationView( - OutArray out_application_views, + OutArray out_application_views, InArray application_ids) { const auto size = (std::min)(out_application_views.size(), application_ids.size()); LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size()); for (size_t i = 0; i < size; i++) { - ApplicationView view{}; + ApplicationViewV20 view{}; view.application_id = application_ids[i]; view.version = 0x70000; + view.unk = 0; view.flags = 0x401f17; out_application_views[i] = view; diff --git a/src/core/hle/service/ns/application_manager_interface.h b/src/core/hle/service/ns/application_manager_interface.h index 9c36939257..915581172e 100644 --- a/src/core/hle/service/ns/application_manager_interface.h +++ b/src/core/hle/service/ns/application_manager_interface.h @@ -36,13 +36,14 @@ public: Result IsGameCardApplicationRunning(Out out_is_running); Result IsAnyApplicationEntityInstalled(Out out_is_any_application_entity_installed); Result GetApplicationViewDeprecated( - OutArray out_application_views, + OutArray out_application_views, InArray application_ids); Result GetApplicationViewWithPromotionInfo( - OutArray out_application_views, + OutBuffer out_buffer, + Out out_count, InArray application_ids); Result GetApplicationView( - OutArray out_application_views, + OutArray out_application_views, InArray application_ids); Result GetApplicationRightsOnClient( OutArray out_rights, Out out_count, diff --git a/src/core/hle/service/ns/ns_types.h b/src/core/hle/service/ns/ns_types.h index f7c712f0f6..31981a6821 100644 --- a/src/core/hle/service/ns/ns_types.h +++ b/src/core/hle/service/ns/ns_types.h @@ -55,16 +55,6 @@ struct ApplicationDownloadState { static_assert(sizeof(ApplicationDownloadState) == 0x20, "ApplicationDownloadState has incorrect size."); -/// ApplicationView -struct ApplicationView { - u64 application_id; ///< ApplicationId. - u32 version; ///< Application Version(?) - u32 flags; ///< Flags. - ApplicationDownloadState download_state; ///< \ref ApplicationDownloadState - ApplicationDownloadState download_progress; ///< \ref ApplicationDownloadState -}; -static_assert(sizeof(ApplicationView) == 0x50, "ApplicationView has incorrect size."); - struct ApplicationRightsOnClient { u64 application_id; Common::UUID uid; @@ -88,14 +78,74 @@ struct PromotionInfo { }; static_assert(sizeof(PromotionInfo) == 0x20, "PromotionInfo has incorrect size."); -// TODO(Maufeat): NsApplicationViewWithPromotionInfo is on SDK20+ 0x78 bytes -/// NsApplicationViewWithPromotionInfo -struct ApplicationViewWithPromotionInfo { - ApplicationView view; ///< \ref NsApplicationView - PromotionInfo promotion; ///< \ref NsPromotionInfo +struct ApplicationViewV19 { + u64 application_id; + u32 version; + u32 flags; + ApplicationDownloadState download_state; + ApplicationDownloadState download_progress; +}; +static_assert(sizeof(ApplicationViewV19) == 0x50, "ApplicationViewV19 has incorrect size."); + +struct ApplicationViewV20 { + u64 application_id; + u32 version; + u32 unk; + u32 flags; + ApplicationDownloadState download_state; + ApplicationDownloadState download_progress; +}; +static_assert(sizeof(ApplicationViewV20) == 0x58, "ApplicationViewV20 has incorrect size."); + +struct ApplicationViewData { + u64 application_id{}; + u32 version{}; + u32 unk{}; + u32 flags{}; + ApplicationDownloadState download_state{}; + ApplicationDownloadState download_progress{}; }; -static_assert(sizeof(ApplicationViewWithPromotionInfo) == 0x70, - "ApplicationViewWithPromotionInfo has incorrect size."); + +inline size_t WriteApplicationView(void* dst, size_t dst_size, const ApplicationViewData& data, + bool is_fw20) { + if (is_fw20) { + if (dst_size < sizeof(ApplicationViewV20)) return 0; + auto* out = reinterpret_cast(dst); + out->application_id = data.application_id; + out->version = data.version; + out->unk = data.unk; + out->flags = data.flags; + out->download_state = data.download_state; + out->download_progress = data.download_progress; + return sizeof(ApplicationViewV20); + } else { + if (dst_size < sizeof(ApplicationViewV19)) return 0; + auto* out = reinterpret_cast(dst); + out->application_id = data.application_id; + out->version = data.version; + out->flags = data.flags; + out->download_state = data.download_state; + out->download_progress = data.download_progress; + return sizeof(ApplicationViewV19); + } +} + +struct ApplicationViewWithPromotionData { + ApplicationViewData view; + PromotionInfo promotion; +}; + +inline size_t WriteApplicationViewWithPromotion(void* dst, size_t dst_size, + const ApplicationViewWithPromotionData& data, + bool sdk20_plus) { + const size_t view_written = WriteApplicationView(dst, dst_size, data.view, sdk20_plus); + if (view_written == 0) return 0; + const size_t remaining = dst_size - view_written; + if (remaining < sizeof(PromotionInfo)) return 0; + auto* promo_dst = reinterpret_cast(dst) + view_written; + std::memcpy(promo_dst, &data.promotion, sizeof(PromotionInfo)); + return view_written + sizeof(PromotionInfo); +} struct ApplicationOccupiedSizeEntity { FileSys::StorageId storage_id; diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp index b79369faad..7b7d381450 100644 --- a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp @@ -27,8 +27,8 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa {2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"}, {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, {4, nullptr, "SelectApplicationDesiredLanguage"}, - {5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithoutIcon"}, - {19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithoutIcon"}, + {5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlDataWithoutIcon"}, + {19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlDataWithoutIcon3"}, }; // clang-format on @@ -125,11 +125,68 @@ Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLan R_SUCCEED(); } -Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon( +Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2( OutBuffer out_buffer, Out out_total_size, - ApplicationControlSource application_control_source, u64 application_id) { - LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}", - application_control_source, application_id); + ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) { + LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}", + application_control_source, flag1, flag2, application_id); + + const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + const auto size = out_buffer.size(); + + const auto nacp_size = sizeof(FileSys::RawNACP); + + if (size < nacp_size) { + LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})", + size, nacp_size); + R_THROW(ResultUnknown); + } + + if (control.first != nullptr) { + const auto bytes = control.first->GetRawBytes(); + const auto copy_len = (std::min)(static_cast(bytes.size()), static_cast(nacp_size)); + std::memcpy(out_buffer.data(), bytes.data(), copy_len); + if (copy_len < nacp_size) { + std::memset(out_buffer.data() + copy_len, 0, nacp_size - copy_len); + } + } else { + LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}", application_id); + std::memset(out_buffer.data(), 0, nacp_size); + } + + const auto icon_area_size = size - nacp_size; + size_t available_icon_bytes = 0; + if (control.second != nullptr) { + available_icon_bytes = control.second->GetSize(); + } + if (icon_area_size > 0) { + if (control.second != nullptr) { + const size_t to_copy = std::min(available_icon_bytes, icon_area_size); + if (to_copy > 0) { + std::vector tmp(to_copy); + control.second->Read(tmp.data(), to_copy); + std::memcpy(out_buffer.data() + nacp_size, tmp.data(), to_copy); + } + if (to_copy < icon_area_size) { + std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy); + } + } else { + std::memset(out_buffer.data() + nacp_size, 0, icon_area_size); + } + } + + const u32 total_available = static_cast(nacp_size + available_icon_bytes); + *out_total_size = (static_cast(total_available) << 32); + R_SUCCEED(); +} + +Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3( + OutBuffer out_buffer, Out out_total_size, + ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) { + LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}", + application_control_source, flag1, flag2, application_id); const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), system.GetContentProvider()}; @@ -158,10 +215,13 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithou } const auto icon_area_size = size - nacp_size; + size_t available_icon_bytes = 0; + if (control.second != nullptr) { + available_icon_bytes = control.second->GetSize(); + } if (icon_area_size > 0) { if (control.second != nullptr) { - const auto icon_size = control.second->GetSize(); - const auto to_copy = static_cast((std::min)(icon_size, icon_area_size)); + const auto to_copy = static_cast((std::min)(available_icon_bytes, icon_area_size)); control.second->Read(out_buffer.data() + nacp_size, to_copy); if (to_copy < icon_area_size) { std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy); @@ -173,7 +233,8 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithou } } - *out_total_size = static_cast(size); + const u32 actual_total_size = static_cast(nacp_size + available_icon_bytes); + *out_total_size = static_cast(actual_total_size) << 32; R_SUCCEED(); } diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.h b/src/core/hle/service/ns/read_only_application_control_data_interface.h index 99366c5792..72b399e596 100644 --- a/src/core/hle/service/ns/read_only_application_control_data_interface.h +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.h @@ -27,10 +27,19 @@ public: u32 supported_languages); Result ConvertApplicationLanguageToLanguageCode(Out out_language_code, ApplicationLanguage application_language); - Result GetApplicationControlDataWithoutIcon( + Result GetApplicationControlData2( OutBuffer out_buffer, Out out_total_size, ApplicationControlSource application_control_source, + u8 flag1, + u8 flag2, + u64 application_id); + Result GetApplicationControlData3( + OutBuffer out_buffer, + Out out_total_size, + ApplicationControlSource application_control_source, + u8 flag1, + u8 flag2, u64 application_id); };