[hle] add GetApplicationControlData2 and GetApplicationControlData3 to make on FW20.0.0 - FW21.1.0 icons work on qlaunch (#3153)
This change makes firmware 20+ show icons in qlaunch and adds a way for ApplicationViewWithPromotion to handle below 20 and upwards struct Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3153 Reviewed-by: MaranBr <maranbr@eden-emu.dev> Reviewed-by: crueter <crueter@eden-emu.dev> Co-authored-by: Maufeat <sahyno1996@gmail.com> Co-committed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
parent
06b83a58a6
commit
30d75d4e52
|
|
@ -13,6 +13,7 @@
|
||||||
#include "core/hle/service/ns/content_management_interface.h"
|
#include "core/hle/service/ns/content_management_interface.h"
|
||||||
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
|
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
|
#include "frontend_common/firmware_manager.h"
|
||||||
|
|
||||||
namespace Service::NS {
|
namespace Service::NS {
|
||||||
|
|
||||||
|
|
@ -429,13 +430,13 @@ Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IApplicationManagerInterface::GetApplicationViewDeprecated(
|
Result IApplicationManagerInterface::GetApplicationViewDeprecated(
|
||||||
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
|
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
|
||||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
||||||
const auto size = (std::min)(out_application_views.size(), application_ids.size());
|
const auto size = (std::min)(out_application_views.size(), application_ids.size());
|
||||||
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
|
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
ApplicationView view{};
|
ApplicationViewV19 view{};
|
||||||
view.application_id = application_ids[i];
|
view.application_id = application_ids[i];
|
||||||
view.version = 0x70000;
|
view.version = 0x70000;
|
||||||
view.flags = 0x401f17;
|
view.flags = 0x401f17;
|
||||||
|
|
@ -447,34 +448,53 @@ Result IApplicationManagerInterface::GetApplicationViewDeprecated(
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo(
|
Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo(
|
||||||
OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
|
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||||
|
Out<u32> out_count,
|
||||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
||||||
const auto size = (std::min)(out_application_views.size(), application_ids.size());
|
const auto requested = application_ids.size();
|
||||||
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
|
LOG_WARNING(Service_NS, "called, size={}", requested);
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
const auto fw_pair = FirmwareManager::GetFirmwareVersion(system);
|
||||||
ApplicationViewWithPromotionInfo view{};
|
const bool is_fw20 = fw_pair.first.major >= 20;
|
||||||
view.view.application_id = application_ids[i];
|
|
||||||
view.view.version = 0x70000;
|
|
||||||
view.view.flags = 0x401f17;
|
|
||||||
view.promotion = {};
|
|
||||||
|
|
||||||
out_application_views[i] = view;
|
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<u32>(dst - out_buffer.data()) / static_cast<u32>(per_entry_size);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IApplicationManagerInterface::GetApplicationView(
|
Result IApplicationManagerInterface::GetApplicationView(
|
||||||
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
|
OutArray<ApplicationViewV20, BufferAttr_HipcMapAlias> out_application_views,
|
||||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
||||||
const auto size = (std::min)(out_application_views.size(), application_ids.size());
|
const auto size = (std::min)(out_application_views.size(), application_ids.size());
|
||||||
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
|
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
ApplicationView view{};
|
ApplicationViewV20 view{};
|
||||||
view.application_id = application_ids[i];
|
view.application_id = application_ids[i];
|
||||||
view.version = 0x70000;
|
view.version = 0x70000;
|
||||||
|
view.unk = 0;
|
||||||
view.flags = 0x401f17;
|
view.flags = 0x401f17;
|
||||||
|
|
||||||
out_application_views[i] = view;
|
out_application_views[i] = view;
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,14 @@ public:
|
||||||
Result IsGameCardApplicationRunning(Out<bool> out_is_running);
|
Result IsGameCardApplicationRunning(Out<bool> out_is_running);
|
||||||
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
|
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
|
||||||
Result GetApplicationViewDeprecated(
|
Result GetApplicationViewDeprecated(
|
||||||
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
|
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
|
||||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
||||||
Result GetApplicationViewWithPromotionInfo(
|
Result GetApplicationViewWithPromotionInfo(
|
||||||
OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
|
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||||
|
Out<u32> out_count,
|
||||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
||||||
Result GetApplicationView(
|
Result GetApplicationView(
|
||||||
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
|
OutArray<ApplicationViewV20, BufferAttr_HipcMapAlias> out_application_views,
|
||||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
||||||
Result GetApplicationRightsOnClient(
|
Result GetApplicationRightsOnClient(
|
||||||
OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
|
OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
|
||||||
|
|
|
||||||
|
|
@ -55,16 +55,6 @@ struct ApplicationDownloadState {
|
||||||
static_assert(sizeof(ApplicationDownloadState) == 0x20,
|
static_assert(sizeof(ApplicationDownloadState) == 0x20,
|
||||||
"ApplicationDownloadState has incorrect size.");
|
"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 {
|
struct ApplicationRightsOnClient {
|
||||||
u64 application_id;
|
u64 application_id;
|
||||||
Common::UUID uid;
|
Common::UUID uid;
|
||||||
|
|
@ -88,14 +78,74 @@ struct PromotionInfo {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(PromotionInfo) == 0x20, "PromotionInfo has incorrect size.");
|
static_assert(sizeof(PromotionInfo) == 0x20, "PromotionInfo has incorrect size.");
|
||||||
|
|
||||||
// TODO(Maufeat): NsApplicationViewWithPromotionInfo is on SDK20+ 0x78 bytes
|
struct ApplicationViewV19 {
|
||||||
/// NsApplicationViewWithPromotionInfo
|
u64 application_id;
|
||||||
struct ApplicationViewWithPromotionInfo {
|
u32 version;
|
||||||
ApplicationView view; ///< \ref NsApplicationView
|
u32 flags;
|
||||||
PromotionInfo promotion; ///< \ref NsPromotionInfo
|
ApplicationDownloadState download_state;
|
||||||
|
ApplicationDownloadState download_progress;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(ApplicationViewWithPromotionInfo) == 0x70,
|
static_assert(sizeof(ApplicationViewV19) == 0x50, "ApplicationViewV19 has incorrect size.");
|
||||||
"ApplicationViewWithPromotionInfo 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{};
|
||||||
|
};
|
||||||
|
|
||||||
|
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<ApplicationViewV20*>(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<ApplicationViewV19*>(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<u8*>(dst) + view_written;
|
||||||
|
std::memcpy(promo_dst, &data.promotion, sizeof(PromotionInfo));
|
||||||
|
return view_written + sizeof(PromotionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
struct ApplicationOccupiedSizeEntity {
|
struct ApplicationOccupiedSizeEntity {
|
||||||
FileSys::StorageId storage_id;
|
FileSys::StorageId storage_id;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,12 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include <stb_image.h>
|
||||||
|
#include <stb_image_resize.h>
|
||||||
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/file_sys/control_metadata.h"
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
|
|
@ -17,6 +23,49 @@
|
||||||
|
|
||||||
namespace Service::NS {
|
namespace Service::NS {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void JPGToMemory(void* context, void* data, int size) {
|
||||||
|
auto* buffer = static_cast<std::vector<u8>*>(context);
|
||||||
|
const auto* char_data = static_cast<const u8*>(data);
|
||||||
|
buffer->insert(buffer->end(), char_data, char_data + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SanitizeJPEGImageSize(std::vector<u8>& image) {
|
||||||
|
constexpr std::size_t max_jpeg_image_size = 0x20000;
|
||||||
|
constexpr int profile_dimensions = 174; // for grid view thingy
|
||||||
|
int original_width, original_height, color_channels;
|
||||||
|
|
||||||
|
auto* plain_image =
|
||||||
|
stbi_load_from_memory(image.data(), static_cast<int>(image.size()), &original_width,
|
||||||
|
&original_height, &color_channels, STBI_rgb);
|
||||||
|
|
||||||
|
if (plain_image == nullptr) {
|
||||||
|
LOG_ERROR(Service_NS, "Failed to load JPEG for sanitization.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original_width != profile_dimensions || original_height != profile_dimensions) {
|
||||||
|
std::vector<u8> out_image(profile_dimensions * profile_dimensions * STBI_rgb);
|
||||||
|
stbir_resize_uint8_srgb(plain_image, original_width, original_height, 0, out_image.data(),
|
||||||
|
profile_dimensions, profile_dimensions, 0, STBI_rgb, 0,
|
||||||
|
STBIR_FILTER_BOX);
|
||||||
|
image.clear();
|
||||||
|
if (!stbi_write_jpg_to_func(JPGToMemory, &image, profile_dimensions, profile_dimensions,
|
||||||
|
STBI_rgb, out_image.data(), 90)) {
|
||||||
|
LOG_ERROR(Service_NS, "Failed to resize the user provided image.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stbi_image_free(plain_image);
|
||||||
|
|
||||||
|
if (image.size() > max_jpeg_image_size) {
|
||||||
|
image.resize(max_jpeg_image_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
|
IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
|
||||||
Core::System& system_)
|
Core::System& system_)
|
||||||
: ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
|
: ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
|
||||||
|
|
@ -27,8 +76,8 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||||
{2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
|
{2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
|
||||||
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||||
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
||||||
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithoutIcon"},
|
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlDataWithoutIcon"},
|
||||||
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithoutIcon"},
|
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlDataWithoutIcon3"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|
@ -125,11 +174,77 @@ Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLan
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon(
|
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
|
||||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u64> out_total_size,
|
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u64> out_total_size,
|
||||||
ApplicationControlSource application_control_source, u64 application_id) {
|
ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {
|
||||||
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
|
LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
|
||||||
application_control_source, application_id);
|
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<size_t>(bytes.size()), static_cast<size_t>(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;
|
||||||
|
std::vector<u8> final_icon_data;
|
||||||
|
|
||||||
|
if (control.second != nullptr) {
|
||||||
|
size_t full_size = control.second->GetSize();
|
||||||
|
if (full_size > 0) {
|
||||||
|
final_icon_data.resize(full_size);
|
||||||
|
control.second->Read(final_icon_data.data(), full_size);
|
||||||
|
|
||||||
|
if (flag1 == 1) {
|
||||||
|
SanitizeJPEGImageSize(final_icon_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t available_icon_bytes = final_icon_data.size();
|
||||||
|
|
||||||
|
if (icon_area_size > 0) {
|
||||||
|
const size_t to_copy = (std::min)(available_icon_bytes, icon_area_size);
|
||||||
|
|
||||||
|
if (to_copy > 0) {
|
||||||
|
std::memcpy(out_buffer.data() + nacp_size, final_icon_data.data(), to_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to_copy < icon_area_size) {
|
||||||
|
std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 total_available = static_cast<u32>(nacp_size + available_icon_bytes);
|
||||||
|
|
||||||
|
*out_total_size = (static_cast<u64>(total_available) << 32) | static_cast<u64>(flag1);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
|
||||||
|
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b,
|
||||||
|
Out<u32> out_actual_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(),
|
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
|
||||||
system.GetContentProvider()};
|
system.GetContentProvider()};
|
||||||
|
|
@ -158,22 +273,43 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithou
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto icon_area_size = size - nacp_size;
|
const auto icon_area_size = size - nacp_size;
|
||||||
if (icon_area_size > 0) {
|
std::vector<u8> final_icon_data;
|
||||||
if (control.second != nullptr) {
|
|
||||||
const auto icon_size = control.second->GetSize();
|
if (control.second != nullptr) {
|
||||||
const auto to_copy = static_cast<size_t>((std::min)(icon_size, icon_area_size));
|
size_t full_size = control.second->GetSize();
|
||||||
control.second->Read(out_buffer.data() + nacp_size, to_copy);
|
if (full_size > 0) {
|
||||||
if (to_copy < icon_area_size) {
|
final_icon_data.resize(full_size);
|
||||||
std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy);
|
control.second->Read(final_icon_data.data(), full_size);
|
||||||
|
|
||||||
|
if (flag1 == 1) {
|
||||||
|
SanitizeJPEGImageSize(final_icon_data);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
std::memset(out_buffer.data() + nacp_size, 0, icon_area_size);
|
|
||||||
LOG_WARNING(Service_NS, "missing icon data for application_id={:016X}, zero-filling icon area",
|
|
||||||
application_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_total_size = static_cast<u64>(size);
|
size_t available_icon_bytes = final_icon_data.size();
|
||||||
|
|
||||||
|
if (icon_area_size > 0) {
|
||||||
|
const size_t to_copy = (std::min)(available_icon_bytes, icon_area_size);
|
||||||
|
if (to_copy > 0) {
|
||||||
|
std::memcpy(out_buffer.data() + nacp_size, final_icon_data.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 actual_total_size = static_cast<u32>(nacp_size + available_icon_bytes);
|
||||||
|
|
||||||
|
// Out 1: always 0x10001 (likely presents flags: Bit0=Icon, Bit16=NACP)
|
||||||
|
// Out 2: reflects flag1 application (0 if flag1=0, 0x10001 if flag1=1)
|
||||||
|
// Out 3: The actual size of data
|
||||||
|
*out_flags_a = 0x10001;
|
||||||
|
*out_flags_b = (flag1 == 1) ? 0x10001 : 0;
|
||||||
|
*out_actual_size = actual_total_size;
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,21 @@ public:
|
||||||
u32 supported_languages);
|
u32 supported_languages);
|
||||||
Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
|
Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
|
||||||
ApplicationLanguage application_language);
|
ApplicationLanguage application_language);
|
||||||
Result GetApplicationControlDataWithoutIcon(
|
Result GetApplicationControlData2(
|
||||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||||
Out<u64> out_total_size,
|
Out<u64> out_total_size,
|
||||||
ApplicationControlSource application_control_source,
|
ApplicationControlSource application_control_source,
|
||||||
|
u8 flag1,
|
||||||
|
u8 flag2,
|
||||||
|
u64 application_id);
|
||||||
|
Result GetApplicationControlData3(
|
||||||
|
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||||
|
Out<u32> out_flags_a,
|
||||||
|
Out<u32> out_flags_b,
|
||||||
|
Out<u32> out_actual_size,
|
||||||
|
ApplicationControlSource application_control_source,
|
||||||
|
u8 flag1,
|
||||||
|
u8 flag2,
|
||||||
u64 application_id);
|
u64 application_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue