|
|
|
@ -5,8 +5,13 @@
|
|
|
|
|
#include "common/settings.h"
|
|
|
|
|
#include "core/arm/debug.h"
|
|
|
|
|
#include "core/core.h"
|
|
|
|
|
#include "core/file_sys/content_archive.h"
|
|
|
|
|
#include "core/file_sys/control_metadata.h"
|
|
|
|
|
#include "core/file_sys/fs_filesystem.h"
|
|
|
|
|
#include "core/file_sys/nca_metadata.h"
|
|
|
|
|
#include "core/file_sys/patch_manager.h"
|
|
|
|
|
#include "core/file_sys/registered_cache.h"
|
|
|
|
|
#include "core/file_sys/submission_package.h"
|
|
|
|
|
#include "core/file_sys/vfs/vfs.h"
|
|
|
|
|
#include "core/hle/service/filesystem/filesystem.h"
|
|
|
|
|
#include "core/hle/service/glue/glue_manager.h"
|
|
|
|
@ -198,7 +203,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
|
|
|
|
{1003, nullptr, "RequestVerifyApplication"},
|
|
|
|
|
{1004, nullptr, "CorruptContentForDebug"},
|
|
|
|
|
{1200, nullptr, "NeedsUpdateVulnerability"},
|
|
|
|
|
{1300, nullptr, "IsAnyApplicationEntityInstalled"},
|
|
|
|
|
{1300, &IApplicationManagerInterface::IsAnyApplicationEntityInstalled, "IsAnyApplicationEntityInstalled"},
|
|
|
|
|
{1301, nullptr, "DeleteApplicationContentEntities"},
|
|
|
|
|
{1302, nullptr, "CleanupUnrecordedApplicationEntity"},
|
|
|
|
|
{1303, nullptr, "CleanupAddOnContentsWithNoRights"},
|
|
|
|
@ -340,7 +345,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
|
|
|
|
service_context.CreateEvent("IApplicationManagerInterface::GamecardMountFailureEvent");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IApplicationManagerInterface::~IApplicationManagerInterface(){
|
|
|
|
|
IApplicationManagerInterface::~IApplicationManagerInterface() {
|
|
|
|
|
service_context.CloseEvent(record_update_system_event);
|
|
|
|
|
service_context.CloseEvent(gamecard_update_detection_event);
|
|
|
|
|
service_context.CloseEvent(gamecard_mount_status_event);
|
|
|
|
@ -353,54 +358,39 @@ void IApplicationManagerInterface::ListApplicationRecord(HLERequestContext& ctx)
|
|
|
|
|
const auto limit = ctx.GetWriteBufferNumElements<ApplicationRecord>();
|
|
|
|
|
|
|
|
|
|
LOG_ERROR(Service_NS, "(STUBBED) called");
|
|
|
|
|
const auto& cache = system.GetContentProviderUnion();
|
|
|
|
|
const auto installed_games = cache.ListEntriesFilterOrigin(
|
|
|
|
|
std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
|
|
|
|
|
|
|
|
|
|
std::vector<ApplicationRecord> application_records;
|
|
|
|
|
std::vector<ApplicationRecord> application_records2;
|
|
|
|
|
|
|
|
|
|
application_records.push_back(ApplicationRecord{
|
|
|
|
|
.application_id = 0x010074F013262000,
|
|
|
|
|
.type = 3,
|
|
|
|
|
.unknown = 2,
|
|
|
|
|
.unknown2 = 0,
|
|
|
|
|
});
|
|
|
|
|
application_records.push_back(ApplicationRecord{
|
|
|
|
|
.application_id = 0x01000F0002BB6000,
|
|
|
|
|
.type = 2,
|
|
|
|
|
.unknown = 2,
|
|
|
|
|
.unknown2 = 1,
|
|
|
|
|
});
|
|
|
|
|
application_records.push_back(ApplicationRecord{
|
|
|
|
|
.application_id = 0x0100A7F002830000,
|
|
|
|
|
.type = 1,
|
|
|
|
|
.unknown = 2,
|
|
|
|
|
.unknown2 = 2,
|
|
|
|
|
});
|
|
|
|
|
application_records.push_back(ApplicationRecord{
|
|
|
|
|
.application_id = 0x01008DF012A7A000,
|
|
|
|
|
.type = 3,
|
|
|
|
|
.unknown = 2,
|
|
|
|
|
.unknown2 = 3,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (const auto& data : application_records) {
|
|
|
|
|
if (application_records2.size() >= limit) {
|
|
|
|
|
u8 ii{24};
|
|
|
|
|
for (const auto& [slot, game] : installed_games) {
|
|
|
|
|
if (application_records.size() >= limit) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (game.title_id == 0 || game.title_id < 0x0100000000001FFFull) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (offset > 0) {
|
|
|
|
|
offset--;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
application_records2.push_back(data);
|
|
|
|
|
application_records.push_back(ApplicationRecord{
|
|
|
|
|
.application_id = game.title_id,
|
|
|
|
|
.type = ApplicationRecordType::Installed,
|
|
|
|
|
.unknown = 0, // 2 = needs update
|
|
|
|
|
.unknown2 = ii++,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!application_records2.empty()) {
|
|
|
|
|
ctx.WriteBuffer(application_records2);
|
|
|
|
|
if (!application_records.empty()) {
|
|
|
|
|
ctx.WriteBuffer(application_records);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 3};
|
|
|
|
|
rb.Push(ResultSuccess);
|
|
|
|
|
rb.Push(application_records2.size());
|
|
|
|
|
rb.Push(application_records.size());
|
|
|
|
|
}
|
|
|
|
|
void IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent(HLERequestContext& ctx) {
|
|
|
|
|
LOG_ERROR(Service_NS, "(STUBBED) called");
|
|
|
|
@ -478,6 +468,14 @@ void IApplicationManagerInterface::GetGameCardMountFailureEvent(HLERequestContex
|
|
|
|
|
rb.PushCopyObjects(gamecard_mount_failure_event->GetReadableEvent());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IApplicationManagerInterface::IsAnyApplicationEntityInstalled(HLERequestContext& ctx) {
|
|
|
|
|
LOG_ERROR(Service_NS, "(STUBBED) called");
|
|
|
|
|
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 3};
|
|
|
|
|
rb.Push(ResultSuccess);
|
|
|
|
|
rb.Push<u8>(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IApplicationManagerInterface::GetApplicationView(HLERequestContext& ctx) {
|
|
|
|
|
std::vector<u64> application_id(ctx.GetReadBufferNumElements<u64>());
|
|
|
|
|
const auto app_buffer = ctx.ReadBuffer();
|
|
|
|
@ -485,10 +483,12 @@ void IApplicationManagerInterface::GetApplicationView(HLERequestContext& ctx) {
|
|
|
|
|
|
|
|
|
|
std::vector<ApplicationView> app_view;
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG(Service_NS, "(STUBBED) called, size={}", application_id.size());
|
|
|
|
|
LOG_ERROR(Service_NS, "(STUBBED) called, size={}", application_id.size());
|
|
|
|
|
for (const u64& data : application_id) {
|
|
|
|
|
app_view.push_back(ApplicationView{
|
|
|
|
|
.application_id = data,
|
|
|
|
|
.unk = 0x70000,
|
|
|
|
|
.flags = 0x401f17,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -505,12 +505,14 @@ void IApplicationManagerInterface::GetApplicationViewWithPromotionInfo(HLEReques
|
|
|
|
|
|
|
|
|
|
std::vector<ApplicationViewWithPromotionInfo> app_view;
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG(Service_NS, "(STUBBED) called, size={}", application_id.size());
|
|
|
|
|
LOG_ERROR(Service_NS, "(STUBBED) called, size={}", application_id.size());
|
|
|
|
|
|
|
|
|
|
for (const u64& data : application_id) {
|
|
|
|
|
app_view.push_back(ApplicationViewWithPromotionInfo{
|
|
|
|
|
.view{
|
|
|
|
|
.application_id = data,
|
|
|
|
|
.unk = 0x70000,
|
|
|
|
|
.flags = 0x401f17,
|
|
|
|
|
},
|
|
|
|
|
.promotion = {},
|
|
|
|
|
});
|
|
|
|
@ -672,7 +674,7 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_)
|
|
|
|
|
: ServiceFramework{system_, "IContentManagementInterface"} {
|
|
|
|
|
// clang-format off
|
|
|
|
|
static const FunctionInfo functions[] = {
|
|
|
|
|
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
|
|
|
|
{11, &IContentManagementInterface::CalculateApplicationOccupiedSize, "CalculateApplicationOccupiedSize"},
|
|
|
|
|
{43, &IContentManagementInterface::CheckSdCardMountStatus, "CheckSdCardMountStatus"},
|
|
|
|
|
{47, &IContentManagementInterface::GetTotalSpaceSize, "GetTotalSpaceSize"},
|
|
|
|
|
{48, &IContentManagementInterface::GetFreeSpaceSize, "GetFreeSpaceSize"},
|
|
|
|
@ -688,6 +690,37 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_)
|
|
|
|
|
|
|
|
|
|
IContentManagementInterface::~IContentManagementInterface() = default;
|
|
|
|
|
|
|
|
|
|
void IContentManagementInterface::CalculateApplicationOccupiedSize(HLERequestContext& ctx) {
|
|
|
|
|
struct ApplicationOccupiedSizeEntity {
|
|
|
|
|
u8 storage;
|
|
|
|
|
INSERT_PADDING_BYTES(0x7);
|
|
|
|
|
u64 app_size;
|
|
|
|
|
u64 patch_size;
|
|
|
|
|
u64 aoc_size;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ApplicationOccupiedSize {
|
|
|
|
|
ApplicationOccupiedSizeEntity a;
|
|
|
|
|
ApplicationOccupiedSizeEntity b;
|
|
|
|
|
ApplicationOccupiedSizeEntity c;
|
|
|
|
|
ApplicationOccupiedSizeEntity d;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ApplicationOccupiedSizeEntity dat{
|
|
|
|
|
.storage = 5,
|
|
|
|
|
.app_size = 8ULL * 1024ULL * 1024ULL * 1024ULL,
|
|
|
|
|
.patch_size = 2ULL * 1024ULL * 1024ULL * 1024ULL,
|
|
|
|
|
.aoc_size = 12ULL * 1024ULL * 1024ULL,
|
|
|
|
|
};
|
|
|
|
|
ApplicationOccupiedSize oout{dat, dat, dat, dat};
|
|
|
|
|
|
|
|
|
|
LOG_ERROR(Service_NS, "(STUBBED) called");
|
|
|
|
|
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 34};
|
|
|
|
|
rb.Push(ResultSuccess);
|
|
|
|
|
rb.PushRaw(oout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IContentManagementInterface::CheckSdCardMountStatus(HLERequestContext& ctx) {
|
|
|
|
|
LOG_ERROR(Service_NS, "(STUBBED) called");
|
|
|
|
|
|
|
|
|
@ -696,27 +729,63 @@ void IContentManagementInterface::CheckSdCardMountStatus(HLERequestContext& ctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IContentManagementInterface::GetTotalSpaceSize(HLERequestContext& ctx) {
|
|
|
|
|
IPC::RequestParser rp{ ctx };
|
|
|
|
|
const auto storage{ rp.PopEnum<FileSys::StorageId>() };
|
|
|
|
|
IPC::RequestParser rp{ctx};
|
|
|
|
|
const auto storage{rp.PopEnum<FileSys::StorageId>()};
|
|
|
|
|
|
|
|
|
|
LOG_INFO(Service_Capture, "called, storage={}", storage);
|
|
|
|
|
|
|
|
|
|
IPC::ResponseBuilder rb{ ctx, 4 };
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 4};
|
|
|
|
|
rb.Push(ResultSuccess);
|
|
|
|
|
rb.Push<u64>(system.GetFileSystemController().GetTotalSpaceSize(storage));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IContentManagementInterface::GetFreeSpaceSize(HLERequestContext& ctx) {
|
|
|
|
|
IPC::RequestParser rp{ ctx };
|
|
|
|
|
const auto storage{ rp.PopEnum<FileSys::StorageId>() };
|
|
|
|
|
IPC::RequestParser rp{ctx};
|
|
|
|
|
const auto storage{rp.PopEnum<FileSys::StorageId>()};
|
|
|
|
|
|
|
|
|
|
LOG_INFO(Service_Capture, "called, storage={}", storage);
|
|
|
|
|
|
|
|
|
|
IPC::ResponseBuilder rb{ ctx, 4 };
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 4};
|
|
|
|
|
rb.Push(ResultSuccess);
|
|
|
|
|
rb.Push<u64>(system.GetFileSystemController().GetFreeSpaceSize(storage));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DynamicRightsInterface::DynamicRightsInterface(Core::System& system_)
|
|
|
|
|
: ServiceFramework{system_, "DynamicRightsInterface"} {
|
|
|
|
|
// clang-format off
|
|
|
|
|
static const FunctionInfo functions[] = {
|
|
|
|
|
{0, nullptr, "RequestApplicationRightsOnServer"},
|
|
|
|
|
{1, nullptr, "RequestAssignRights"},
|
|
|
|
|
{4, nullptr, "DeprecatedRequestAssignRightsToResume"},
|
|
|
|
|
{5, nullptr, "VerifyActivatedRightsOwners"},
|
|
|
|
|
{6, nullptr, "DeprecatedGetApplicationRightsStatus"},
|
|
|
|
|
{7, nullptr, "RequestPrefetchForDynamicRights"},
|
|
|
|
|
{8, nullptr, "GetDynamicRightsState"},
|
|
|
|
|
{9, nullptr, "RequestApplicationRightsOnServerToResume"},
|
|
|
|
|
{10, nullptr, "RequestAssignRightsToResume"},
|
|
|
|
|
{11, nullptr, "GetActivatedRightsUsers"},
|
|
|
|
|
{12, nullptr, "GetApplicationRightsStatus"},
|
|
|
|
|
{13, nullptr, "GetRunningApplicationStatus"},
|
|
|
|
|
{14, nullptr, "SelectApplicationLicense"},
|
|
|
|
|
{15, nullptr, "RequestContentsAuthorizationToken"},
|
|
|
|
|
{16, nullptr, "QualifyUser"},
|
|
|
|
|
{17, nullptr, "QualifyUserWithProcessId"},
|
|
|
|
|
{18, nullptr, "NotifyApplicationRightsCheckStart"},
|
|
|
|
|
{19, nullptr, "UpdateUserList"},
|
|
|
|
|
{20, nullptr, "IsRightsLostUser"},
|
|
|
|
|
{21, nullptr, "SetRequiredAddOnContentsOnContentsAvailabilityTransition"},
|
|
|
|
|
{22, nullptr, "GetLimitedApplicationLicense"},
|
|
|
|
|
{23, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
|
|
|
|
|
{24, nullptr, "NotifyLimitedApplicationLicenseUpgradableEventForDebug"},
|
|
|
|
|
{25, nullptr, "RequestProceedDynamicRightsState"},
|
|
|
|
|
};
|
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
|
|
RegisterHandlers(functions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DynamicRightsInterface::~DynamicRightsInterface() = default;
|
|
|
|
|
|
|
|
|
|
IDocumentInterface::IDocumentInterface(Core::System& system_)
|
|
|
|
|
: ServiceFramework{system_, "IDocumentInterface"} {
|
|
|
|
|
// clang-format off
|
|
|
|
@ -769,7 +838,7 @@ IDownloadTaskInterface::IDownloadTaskInterface(Core::System& system_)
|
|
|
|
|
{705, nullptr, "RequestDownloadTaskListData"},
|
|
|
|
|
{706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
|
|
|
|
|
{707, &IDownloadTaskInterface::EnableAutoCommit, "EnableAutoCommit"},
|
|
|
|
|
{708, nullptr, "DisableAutoCommit"},
|
|
|
|
|
{708, &IDownloadTaskInterface::DisableAutoCommit, "DisableAutoCommit"},
|
|
|
|
|
{709, nullptr, "TriggerDynamicCommitEvent"},
|
|
|
|
|
};
|
|
|
|
|
// clang-format on
|
|
|
|
@ -784,6 +853,11 @@ void IDownloadTaskInterface::EnableAutoCommit(HLERequestContext& ctx) {
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
|
|
|
rb.Push(ResultSuccess);
|
|
|
|
|
}
|
|
|
|
|
void IDownloadTaskInterface::DisableAutoCommit(HLERequestContext& ctx) {
|
|
|
|
|
LOG_ERROR(Service_SET, "called");
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
|
|
|
rb.Push(ResultSuccess);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IECommerceInterface::IECommerceInterface(Core::System& system_)
|
|
|
|
|
: ServiceFramework{system_, "IECommerceInterface"} {
|
|
|
|
@ -863,7 +937,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
|
|
|
|
// clang-format off
|
|
|
|
|
static const FunctionInfo functions[] = {
|
|
|
|
|
{0, &IReadOnlyApplicationControlDataInterface::GetApplicationControlData, "GetApplicationControlData"},
|
|
|
|
|
{1, nullptr, "GetApplicationDesiredLanguage"},
|
|
|
|
|
{1, &IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage, "GetApplicationDesiredLanguage"},
|
|
|
|
|
{2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
|
|
|
|
|
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
|
|
|
|
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
|
|
|
@ -876,36 +950,121 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
|
|
|
|
IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
|
|
|
|
|
|
|
|
|
|
void IReadOnlyApplicationControlDataInterface::GetApplicationControlData(HLERequestContext& ctx) {
|
|
|
|
|
enum class ApplicationControlSource : u8 {
|
|
|
|
|
CacheOnly,
|
|
|
|
|
Storage,
|
|
|
|
|
StorageOnly,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct RequestParameters {
|
|
|
|
|
ApplicationControlSource source;
|
|
|
|
|
u64 application_id;
|
|
|
|
|
};
|
|
|
|
|
static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size.");
|
|
|
|
|
|
|
|
|
|
IPC::RequestParser rp{ctx};
|
|
|
|
|
std::vector<u8> nacp_data{};
|
|
|
|
|
const auto parameters{rp.PopRaw<RequestParameters>()};
|
|
|
|
|
const auto result =
|
|
|
|
|
system.GetARPManager().GetControlProperty(&nacp_data, parameters.application_id);
|
|
|
|
|
const auto flag = rp.PopRaw<u64>();
|
|
|
|
|
const auto title_id = rp.PopRaw<u64>();
|
|
|
|
|
const auto size = ctx.GetWriteBufferSize();
|
|
|
|
|
|
|
|
|
|
if (result == ResultSuccess) {
|
|
|
|
|
ctx.WriteBuffer(nacp_data.data(), nacp_data.size());
|
|
|
|
|
LOG_ERROR(Service_NS, "called with flag={:016X}, title_id={:016X}", flag, title_id);
|
|
|
|
|
|
|
|
|
|
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
|
|
|
|
system.GetContentProvider()};
|
|
|
|
|
const auto control = pm.GetControlMetadata();
|
|
|
|
|
|
|
|
|
|
std::vector<u8> out;
|
|
|
|
|
|
|
|
|
|
if (control.first != nullptr) {
|
|
|
|
|
if (size < 0x4000) {
|
|
|
|
|
LOG_ERROR(Service_NS,
|
|
|
|
|
"output buffer is too small! (actual={:016X}, expected_min=0x4000)", size);
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
|
|
|
// TODO(DarkLordZach): Find a better error code for this.
|
|
|
|
|
rb.Push(ResultUnknown);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out.resize(0x4000);
|
|
|
|
|
const auto bytes = control.first->GetRawBytes();
|
|
|
|
|
std::memcpy(out.data(), bytes.data(), bytes.size());
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
|
|
|
|
|
title_id);
|
|
|
|
|
out.resize(std::min<u64>(0x4000, size));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
|
|
|
rb.Push(result);
|
|
|
|
|
if (control.second != nullptr) {
|
|
|
|
|
if (size < 0x4000 + control.second->GetSize()) {
|
|
|
|
|
LOG_ERROR(Service_NS,
|
|
|
|
|
"output buffer is too small! (actual={:016X}, expected_min={:016X})", size,
|
|
|
|
|
0x4000 + control.second->GetSize());
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
|
|
|
// TODO(DarkLordZach): Find a better error code for this.
|
|
|
|
|
rb.Push(ResultUnknown);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out.resize(0x4000 + control.second->GetSize());
|
|
|
|
|
control.second->Read(out.data() + 0x4000, control.second->GetSize());
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
|
|
|
|
|
title_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.WriteBuffer(out);
|
|
|
|
|
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 3};
|
|
|
|
|
rb.Push(ResultSuccess);
|
|
|
|
|
rb.Push<u32>(static_cast<u32>(out.size()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage(
|
|
|
|
|
HLERequestContext& ctx) {
|
|
|
|
|
IPC::RequestParser rp{ctx};
|
|
|
|
|
const auto supported_languages = rp.Pop<u32>();
|
|
|
|
|
|
|
|
|
|
u8 desired_language{};
|
|
|
|
|
const auto res = GetApplicationDesiredLanguage(&desired_language, supported_languages);
|
|
|
|
|
if (res == ResultSuccess) {
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 3};
|
|
|
|
|
rb.Push(ResultSuccess);
|
|
|
|
|
rb.Push<u32>(desired_language);
|
|
|
|
|
} else {
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
|
|
|
rb.Push(res);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage(
|
|
|
|
|
u8* out_desired_language, const u32 supported_languages) {
|
|
|
|
|
LOG_ERROR(Service_NS, "called with supported_languages={:08X}", supported_languages);
|
|
|
|
|
|
|
|
|
|
// Get language code from settings
|
|
|
|
|
const auto language_code =
|
|
|
|
|
Set::GetLanguageCodeFromIndex(static_cast<s32>(Settings::values.language_index.GetValue()));
|
|
|
|
|
|
|
|
|
|
// Convert to application language, get priority list
|
|
|
|
|
const auto application_language = ConvertToApplicationLanguage(language_code);
|
|
|
|
|
if (application_language == std::nullopt) {
|
|
|
|
|
LOG_ERROR(Service_NS, "Could not convert application language! language_code={}",
|
|
|
|
|
language_code);
|
|
|
|
|
return Service::NS::ResultApplicationLanguageNotFound;
|
|
|
|
|
}
|
|
|
|
|
const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
|
|
|
|
|
if (!priority_list) {
|
|
|
|
|
LOG_ERROR(Service_NS,
|
|
|
|
|
"Could not find application language priorities! application_language={}",
|
|
|
|
|
*application_language);
|
|
|
|
|
return Service::NS::ResultApplicationLanguageNotFound;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try to find a valid language.
|
|
|
|
|
for (const auto lang : *priority_list) {
|
|
|
|
|
const auto supported_flag = GetSupportedLanguageFlag(lang);
|
|
|
|
|
if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) {
|
|
|
|
|
*out_desired_language = static_cast<u8>(lang);
|
|
|
|
|
return ResultSuccess;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_ERROR(Service_NS, "Could not find a valid language! supported_languages={:08X}",
|
|
|
|
|
supported_languages);
|
|
|
|
|
return Service::NS::ResultApplicationLanguageNotFound;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
|
|
|
|
|
// clang-format off
|
|
|
|
|
static const FunctionInfo functions[] = {
|
|
|
|
|
{7988, nullptr, "GetDynamicRightsInterface"},
|
|
|
|
|
{7988, &NS::PushInterface<DynamicRightsInterface>, "GetDynamicRightsInterface"},
|
|
|
|
|
{7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
|
|
|
|
|
{7991, &NS::PushInterface<IReadOnlyApplicationRecordInterface>, "GetReadOnlyApplicationRecordInterface"},
|
|
|
|
|
{7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
|
|
|
|
|