summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/kernel/thread.cpp4
-rw-r--r--src/core/hle/service/acc/acc.cpp18
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp28
-rw-r--r--src/core/hle/service/acc/profile_manager.h66
-rw-r--r--src/core/hle/service/am/am.cpp40
-rw-r--r--src/core/hle/service/am/applets/applets.cpp15
-rw-r--r--src/core/hle/service/am/applets/applets.h27
-rw-r--r--src/core/hle/service/am/applets/profile_select.cpp8
-rw-r--r--src/core/hle/service/am/applets/profile_select.h5
-rw-r--r--src/core/hle/service/mii/mii.cpp341
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp416
-rw-r--r--src/core/hle/service/mii/mii_manager.h273
-rw-r--r--src/core/hle/service/ncm/ncm.cpp80
-rw-r--r--src/core/hle/service/ns/errors.h12
-rw-r--r--src/core/hle/service/ns/language.cpp392
-rw-r--r--src/core/hle/service/ns/language.h45
-rw-r--r--src/core/hle/service/ns/ns.cpp862
-rw-r--r--src/core/hle/service/ns/ns.h82
-rw-r--r--src/core/hle/service/ns/ns_language.h42
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp3
-rw-r--r--src/core/hle/service/time/time.cpp9
21 files changed, 2247 insertions, 521 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 2abf9efca..c73a40977 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -75,9 +75,9 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
// This function might be called from any thread so we have to be cautious and use the
// thread-safe version of ScheduleEvent.
+ const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe(
- Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(),
- callback_handle);
+ cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
void Thread::CancelWakeupTimer() {
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index ba7d7acbd..86bf53d08 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -34,7 +34,7 @@ constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
}};
-static std::string GetImagePath(UUID uuid) {
+static std::string GetImagePath(Common::UUID uuid) {
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
}
@@ -46,7 +46,7 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) {
class IProfile final : public ServiceFramework<IProfile> {
public:
- explicit IProfile(UUID user_id, ProfileManager& profile_manager)
+ explicit IProfile(Common::UUID user_id, ProfileManager& profile_manager)
: ServiceFramework("IProfile"), profile_manager(profile_manager), user_id(user_id) {
static const FunctionInfo functions[] = {
{0, &IProfile::Get, "Get"},
@@ -131,7 +131,7 @@ private:
}
const ProfileManager& profile_manager;
- UUID user_id; ///< The user id this profile refers to.
+ Common::UUID user_id; ///< The user id this profile refers to.
};
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
@@ -179,7 +179,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- UUID user_id = rp.PopRaw<UUID>();
+ Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 3};
@@ -205,12 +205,12 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<UUID>(profile_manager->GetLastOpenedUser());
+ rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser());
}
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- UUID user_id = rp.PopRaw<UUID>();
+ Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -245,15 +245,15 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
IPC::ResponseBuilder rb{ctx, 6};
if (profile_manager->GetUserCount() != 1) {
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u128>(INVALID_UUID);
+ rb.PushRaw<u128>(Common::INVALID_UUID);
return;
}
const auto user_list = profile_manager->GetAllUsers();
if (std::all_of(user_list.begin(), user_list.end(),
- [](const auto& user) { return user.uuid == INVALID_UUID; })) {
+ [](const auto& user) { return user.uuid == Common::INVALID_UUID; })) {
rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
- rb.PushRaw<u128>(INVALID_UUID);
+ rb.PushRaw<u128>(Common::INVALID_UUID);
return;
}
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 1316d0b07..49aa5908b 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -13,6 +13,8 @@
namespace Service::Account {
+using Common::UUID;
+
struct UserRaw {
UUID uuid;
UUID uuid2;
@@ -35,26 +37,6 @@ constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
-UUID UUID::Generate() {
- std::random_device device;
- std::mt19937 gen(device());
- std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
- return UUID{distribution(gen), distribution(gen)};
-}
-
-std::string UUID::Format() const {
- return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
-}
-
-std::string UUID::FormatSwitch() const {
- std::array<u8, 16> s{};
- std::memcpy(s.data(), uuid.data(), sizeof(u128));
- return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
- ":02x}{:02x}{:02x}{:02x}{:02x}",
- s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
- s[12], s[13], s[14], s[15]);
-}
-
ProfileManager::ProfileManager() {
ParseUserSaveFile();
@@ -217,7 +199,7 @@ bool ProfileManager::UserExists(UUID uuid) const {
bool ProfileManager::UserExistsIndex(std::size_t index) const {
if (index >= MAX_USERS)
return false;
- return profiles[index].user_uuid.uuid != INVALID_UUID;
+ return profiles[index].user_uuid.uuid != Common::INVALID_UUID;
}
/// Opens a specific user
@@ -311,7 +293,7 @@ bool ProfileManager::RemoveUser(UUID uuid) {
bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
const auto index = GetUserIndex(uuid);
- if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
+ if (!index || profile_new.user_uuid == UUID(Common::INVALID_UUID)) {
return false;
}
@@ -342,7 +324,7 @@ void ProfileManager::ParseUserSaveFile() {
}
for (const auto& user : data.users) {
- if (user.uuid == UUID(INVALID_UUID)) {
+ if (user.uuid == UUID(Common::INVALID_UUID)) {
continue;
}
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index c4ce2e0b3..fd7abb541 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -9,47 +9,15 @@
#include "common/common_types.h"
#include "common/swap.h"
+#include "common/uuid.h"
#include "core/hle/result.h"
namespace Service::Account {
constexpr std::size_t MAX_USERS = 8;
-constexpr u128 INVALID_UUID{{0, 0}};
-
-struct UUID {
- // UUIDs which are 0 are considered invalid!
- u128 uuid = INVALID_UUID;
- UUID() = default;
- explicit UUID(const u128& id) : uuid{id} {}
- explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
-
- explicit operator bool() const {
- return uuid != INVALID_UUID;
- }
-
- bool operator==(const UUID& rhs) const {
- return uuid == rhs.uuid;
- }
-
- bool operator!=(const UUID& rhs) const {
- return !operator==(rhs);
- }
-
- // TODO(ogniK): Properly generate uuids based on RFC-4122
- static UUID Generate();
-
- // Set the UUID to {0,0} to be considered an invalid user
- void Invalidate() {
- uuid = INVALID_UUID;
- }
-
- std::string Format() const;
- std::string FormatSwitch() const;
-};
-static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
constexpr std::size_t profile_username_size = 32;
using ProfileUsername = std::array<u8, profile_username_size>;
-using UserIDArray = std::array<UUID, MAX_USERS>;
+using UserIDArray = std::array<Common::UUID, MAX_USERS>;
/// Contains extra data related to a user.
/// TODO: RE this structure
@@ -66,7 +34,7 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
struct ProfileInfo {
- UUID user_uuid;
+ Common::UUID user_uuid;
ProfileUsername username;
u64 creation_time;
ProfileData data; // TODO(ognik): Work out what this is
@@ -74,7 +42,7 @@ struct ProfileInfo {
};
struct ProfileBase {
- UUID user_uuid;
+ Common::UUID user_uuid;
u64_le timestamp;
ProfileUsername username;
@@ -96,33 +64,33 @@ public:
~ProfileManager();
ResultCode AddUser(const ProfileInfo& user);
- ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
- ResultCode CreateNewUser(UUID uuid, const std::string& username);
- std::optional<UUID> GetUser(std::size_t index) const;
- std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
+ ResultCode CreateNewUser(Common::UUID uuid, const ProfileUsername& username);
+ ResultCode CreateNewUser(Common::UUID uuid, const std::string& username);
+ std::optional<Common::UUID> GetUser(std::size_t index) const;
+ std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const;
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
- bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
+ bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
ProfileData& data) const;
- bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
+ bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const;
bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
ProfileData& data) const;
std::size_t GetUserCount() const;
std::size_t GetOpenUserCount() const;
- bool UserExists(UUID uuid) const;
+ bool UserExists(Common::UUID uuid) const;
bool UserExistsIndex(std::size_t index) const;
- void OpenUser(UUID uuid);
- void CloseUser(UUID uuid);
+ void OpenUser(Common::UUID uuid);
+ void CloseUser(Common::UUID uuid);
UserIDArray GetOpenUsers() const;
UserIDArray GetAllUsers() const;
- UUID GetLastOpenedUser() const;
+ Common::UUID GetLastOpenedUser() const;
bool CanSystemRegisterUser() const;
- bool RemoveUser(UUID uuid);
- bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
+ bool RemoveUser(Common::UUID uuid);
+ bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new);
private:
void ParseUserSaveFile();
@@ -132,7 +100,7 @@ private:
std::array<ProfileInfo, MAX_USERS> profiles{};
std::size_t user_count = 0;
- UUID last_opened_user{INVALID_UUID};
+ Common::UUID last_opened_user{Common::INVALID_UUID};
};
}; // namespace Service::Account
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 1a32a109f..3f201c821 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -8,6 +8,8 @@
#include <cstring>
#include "audio_core/audio_renderer.h"
#include "core/core.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
@@ -29,9 +31,11 @@
#include "core/hle/service/am/tcap.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/set/set.h"
+#include "core/hle/service/sm/sm.h"
#include "core/hle/service/vi/vi.h"
#include "core/settings.h"
@@ -1100,10 +1104,42 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
// TODO(bunnei): This should be configurable
LOG_DEBUG(Service_AM, "called");
+ // Get supported languages from NACP, if possible
+ // Default to 0 (all languages supported)
+ u32 supported_languages = 0;
+ FileSys::PatchManager pm{Core::System::GetInstance().CurrentProcess()->GetTitleID()};
+
+ const auto res = pm.GetControlMetadata();
+ if (res.first != nullptr) {
+ supported_languages = res.first->GetSupportedLanguages();
+ }
+
+ // Call IApplicationManagerInterface implementation.
+ auto& service_manager = Core::System::GetInstance().ServiceManager();
+ auto ns_am2 = service_manager.GetService<Service::NS::NS>("ns:am2");
+ auto app_man = ns_am2->GetApplicationManagerInterface();
+
+ // Get desired application language
+ const auto res_lang = app_man->GetApplicationDesiredLanguage(supported_languages);
+ if (res_lang.Failed()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res_lang.Code());
+ return;
+ }
+
+ // Convert to settings language code.
+ const auto res_code = app_man->ConvertApplicationLanguageToLanguageCode(*res_lang);
+ if (res_code.Failed()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res_code.Code());
+ return;
+ }
+
+ LOG_DEBUG(Service_AM, "got desired_language={:016X}", *res_code);
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push(
- static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index)));
+ rb.Push(*res_code);
}
void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index e812c66e9..14fa92318 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -121,6 +121,21 @@ void Applet::Initialize() {
initialized = true;
}
+AppletFrontendSet::AppletFrontendSet() = default;
+
+AppletFrontendSet::AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer,
+ ProfileSelect profile_select,
+ SoftwareKeyboard software_keyboard, WebBrowser web_browser)
+ : error{std::move(error)}, photo_viewer{std::move(photo_viewer)}, profile_select{std::move(
+ profile_select)},
+ software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)} {}
+
+AppletFrontendSet::~AppletFrontendSet() = default;
+
+AppletFrontendSet::AppletFrontendSet(AppletFrontendSet&&) noexcept = default;
+
+AppletFrontendSet& AppletFrontendSet::operator=(AppletFrontendSet&&) noexcept = default;
+
AppletManager::AppletManager() = default;
AppletManager::~AppletManager() = default;
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 7f932672c..b46e10a4a 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -137,11 +137,28 @@ protected:
};
struct AppletFrontendSet {
- std::unique_ptr<Core::Frontend::ErrorApplet> error;
- std::unique_ptr<Core::Frontend::PhotoViewerApplet> photo_viewer;
- std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_select;
- std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
- std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
+ using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
+ using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
+ using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
+ using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
+ using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
+
+ AppletFrontendSet();
+ AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, ProfileSelect profile_select,
+ SoftwareKeyboard software_keyboard, WebBrowser web_browser);
+ ~AppletFrontendSet();
+
+ AppletFrontendSet(const AppletFrontendSet&) = delete;
+ AppletFrontendSet& operator=(const AppletFrontendSet&) = delete;
+
+ AppletFrontendSet(AppletFrontendSet&&) noexcept;
+ AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
+
+ ErrorApplet error;
+ PhotoViewer photo_viewer;
+ ProfileSelect profile_select;
+ SoftwareKeyboard software_keyboard;
+ WebBrowser web_browser;
};
class AppletManager {
diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp
index d113bd2eb..57b5419e8 100644
--- a/src/core/hle/service/am/applets/profile_select.cpp
+++ b/src/core/hle/service/am/applets/profile_select.cpp
@@ -53,19 +53,19 @@ void ProfileSelect::Execute() {
return;
}
- frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); });
+ frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); });
}
-void ProfileSelect::SelectionComplete(std::optional<Account::UUID> uuid) {
+void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
UserSelectionOutput output{};
- if (uuid.has_value() && uuid->uuid != Account::INVALID_UUID) {
+ if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) {
output.result = 0;
output.uuid_selected = uuid->uuid;
} else {
status = ERR_USER_CANCELLED_SELECTION;
output.result = ERR_USER_CANCELLED_SELECTION.raw;
- output.uuid_selected = Account::INVALID_UUID;
+ output.uuid_selected = Common::INVALID_UUID;
}
final_data = std::vector<u8>(sizeof(UserSelectionOutput));
diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h
index a2ac6cf50..563cd744a 100644
--- a/src/core/hle/service/am/applets/profile_select.h
+++ b/src/core/hle/service/am/applets/profile_select.h
@@ -7,7 +7,8 @@
#include <vector>
#include "common/common_funcs.h"
-#include "core/hle/service/acc/profile_manager.h"
+#include "common/uuid.h"
+#include "core/hle/result.h"
#include "core/hle/service/am/applets/applets.h"
namespace Service::AM::Applets {
@@ -38,7 +39,7 @@ public:
void ExecuteInteractive() override;
void Execute() override;
- void SelectionComplete(std::optional<Account::UUID> uuid);
+ void SelectionComplete(std::optional<Common::UUID> uuid);
private:
const Core::Frontend::ProfileSelectApplet& frontend;
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index a6197124a..ce84e25ed 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -4,42 +4,50 @@
#include <memory>
+#include <fmt/ostream.h>
+
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/mii/mii.h"
+#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Mii {
+constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
+constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
+constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99};
+
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "IsUpdated"},
- {1, nullptr, "IsFullDatabase"},
- {2, nullptr, "GetCount"},
- {3, nullptr, "Get"},
- {4, nullptr, "Get1"},
+ {0, &IDatabaseService::IsUpdated, "IsUpdated"},
+ {1, &IDatabaseService::IsFullDatabase, "IsFullDatabase"},
+ {2, &IDatabaseService::GetCount, "GetCount"},
+ {3, &IDatabaseService::Get, "Get"},
+ {4, &IDatabaseService::Get1, "Get1"},
{5, nullptr, "UpdateLatest"},
- {6, nullptr, "BuildRandom"},
- {7, nullptr, "BuildDefault"},
- {8, nullptr, "Get2"},
- {9, nullptr, "Get3"},
+ {6, &IDatabaseService::BuildRandom, "BuildRandom"},
+ {7, &IDatabaseService::BuildDefault, "BuildDefault"},
+ {8, &IDatabaseService::Get2, "Get2"},
+ {9, &IDatabaseService::Get3, "Get3"},
{10, nullptr, "UpdateLatest1"},
- {11, nullptr, "FindIndex"},
- {12, nullptr, "Move"},
- {13, nullptr, "AddOrReplace"},
- {14, nullptr, "Delete"},
- {15, nullptr, "DestroyFile"},
- {16, nullptr, "DeleteFile"},
- {17, nullptr, "Format"},
+ {11, &IDatabaseService::FindIndex, "FindIndex"},
+ {12, &IDatabaseService::Move, "Move"},
+ {13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
+ {14, &IDatabaseService::Delete, "Delete"},
+ {15, &IDatabaseService::DestroyFile, "DestroyFile"},
+ {16, &IDatabaseService::DeleteFile, "DeleteFile"},
+ {17, &IDatabaseService::Format, "Format"},
{18, nullptr, "Import"},
{19, nullptr, "Export"},
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
- {21, nullptr, "GetIndex"},
+ {21, &IDatabaseService::GetIndex, "GetIndex"},
{22, nullptr, "SetInterfaceVersion"},
{23, nullptr, "Convert"},
};
@@ -47,6 +55,305 @@ public:
RegisterHandlers(functions);
}
+
+private:
+ template <typename OutType>
+ std::vector<u8> SerializeArray(OutType (MiiManager::*getter)(u32) const, u32 offset,
+ u32 requested_size, u32& read_size) {
+ read_size = std::min(requested_size, db.Size() - offset);
+
+ std::vector<u8> out(read_size * sizeof(OutType));
+
+ for (u32 i = 0; i < read_size; ++i) {
+ const auto obj = (db.*getter)(offset + i);
+ std::memcpy(out.data() + i * sizeof(OutType), &obj, sizeof(OutType));
+ }
+
+ return out;
+ }
+
+ void IsUpdated(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with source={}", source);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(db.CheckUpdatedFlag());
+ db.ResetUpdatedFlag();
+ }
+
+ void IsFullDatabase(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Mii, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(db.Full());
+ }
+
+ void GetCount(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with source={}", source);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(db.Size());
+ }
+
+ // Gets Miis from database at offset and index in format MiiInfoElement
+ void Get(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto size{rp.PopRaw<u32>()};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
+ offsets[0], source);
+
+ u32 read_size{};
+ ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfoElement, offsets[0], size, read_size));
+ offsets[0] += read_size;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(read_size);
+ }
+
+ // Gets Miis from database at offset and index in format MiiInfo
+ void Get1(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto size{rp.PopRaw<u32>()};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
+ offsets[1], source);
+
+ u32 read_size{};
+ ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfo, offsets[1], size, read_size));
+ offsets[1] += read_size;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(read_size);
+ }
+
+ void BuildRandom(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>();
+
+ if (unknown1 > 3) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1);
+ return;
+ }
+
+ if (unknown2 > 2) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2);
+ return;
+ }
+
+ if (unknown3 > 3) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3);
+ return;
+ }
+
+ LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
+ unknown1, unknown2, unknown3);
+
+ const auto info = db.CreateRandom({unknown1, unknown2, unknown3});
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<MiiInfo>(info);
+ }
+
+ void BuildDefault(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto index{rp.PopRaw<u32>()};
+
+ if (index > 5) {
+ LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
+ index);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ return;
+ }
+
+ LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
+
+ const auto info = db.CreateDefault(index);
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<MiiInfo>(info);
+ }
+
+ // Gets Miis from database at offset and index in format MiiStoreDataElement
+ void Get2(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto size{rp.PopRaw<u32>()};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
+ offsets[2], source);
+
+ u32 read_size{};
+ ctx.WriteBuffer(
+ SerializeArray(&MiiManager::GetStoreDataElement, offsets[2], size, read_size));
+ offsets[2] += read_size;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(read_size);
+ }
+
+ // Gets Miis from database at offset and index in format MiiStoreData
+ void Get3(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto size{rp.PopRaw<u32>()};
+ const auto source{rp.PopRaw<Source>()};
+
+ LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
+ offsets[3], source);
+
+ u32 read_size{};
+ ctx.WriteBuffer(SerializeArray(&MiiManager::GetStoreData, offsets[3], size, read_size));
+ offsets[3] += read_size;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(read_size);
+ }
+
+ void FindIndex(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto uuid{rp.PopRaw<Common::UUID>()};
+ const auto unknown{rp.PopRaw<bool>()};
+
+ LOG_DEBUG(Service_Mii, "called with uuid={}, unknown={}", uuid.FormatSwitch(), unknown);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ const auto index = db.IndexOf(uuid);
+ if (index > MAX_MIIS) {
+ // TODO(DarkLordZach): Find a better error code
+ rb.Push(ResultCode(-1));
+ rb.Push(index);
+ } else {
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(index);
+ }
+ }
+
+ void Move(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto uuid{rp.PopRaw<Common::UUID>()};
+ const auto index{rp.PopRaw<s32>()};
+
+ if (index < 0) {
+ LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ARGUMENT);
+ return;
+ }
+
+ LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
+
+ const auto success = db.Move(uuid, index);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ // TODO(DarkLordZach): Find a better error code
+ rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
+ }
+
+ void AddOrReplace(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto data{rp.PopRaw<MiiStoreData>()};
+
+ LOG_DEBUG(Service_Mii, "called with Mii data uuid={}, name={}", data.uuid.FormatSwitch(),
+ Common::UTF16ToUTF8(data.Name()));
+
+ const auto success = db.AddOrReplace(data);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ // TODO(DarkLordZach): Find a better error code
+ rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
+ }
+
+ void Delete(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto uuid{rp.PopRaw<Common::UUID>()};
+
+ LOG_DEBUG(Service_Mii, "called with uuid={}", uuid.FormatSwitch());
+
+ const auto success = db.Remove(uuid);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY);
+ }
+
+ void DestroyFile(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Mii, "called");
+
+ if (!db.IsTestModeEnabled()) {
+ LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file.");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_IN_TEST_MODE);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(db.DestroyFile());
+ }
+
+ void DeleteFile(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Mii, "called");
+
+ if (!db.IsTestModeEnabled()) {
+ LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file.");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_IN_TEST_MODE);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(db.DeleteFile());
+ }
+
+ void Format(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Mii, "called");
+
+ db.Clear();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetIndex(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto info{rp.PopRaw<MiiInfo>()};
+
+ LOG_DEBUG(Service_Mii, "called with Mii info uuid={}, name={}", info.uuid.FormatSwitch(),
+ Common::UTF16ToUTF8(info.Name()));
+
+ const auto index = db.IndexOf(info);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(index);
+ }
+
+ MiiManager db;
+
+ // Last read offsets of Get functions
+ std::array<u32, 4> offsets{};
};
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
new file mode 100644
index 000000000..131b01d62
--- /dev/null
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -0,0 +1,416 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstring>
+#include "common/assert.h"
+#include "common/file_util.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/hle/service/mii/mii_manager.h"
+
+namespace Service::Mii {
+
+namespace {
+
+constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
+constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'};
+
+// This value was retrieved from HW test
+constexpr MiiStoreData DEFAULT_MII = {
+ {
+ 0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
+ 0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
+ },
+ {'y', 'u', 'z', 'u', '\0'},
+ Common::UUID{1, 0},
+ 0,
+ 0,
+};
+
+// Default values taken from multiple real databases
+const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
+
+constexpr std::array<const char*, 4> SOURCE_NAMES{
+ "Database",
+ "Default",
+ "Account",
+ "Friend",
+};
+
+template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
+std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
+ std::array<T, DestArraySize> out{};
+ std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
+ return out;
+}
+
+MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
+ MiiStoreBitFields bf{};
+ std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
+ return {
+ data.uuid,
+ ResizeArray<char16_t, 10, 11>(data.name),
+ static_cast<u8>(bf.font_region.Value()),
+ static_cast<u8>(bf.favorite_color.Value()),
+ static_cast<u8>(bf.gender.Value()),
+ static_cast<u8>(bf.height.Value()),
+ static_cast<u8>(bf.weight.Value()),
+ static_cast<u8>(bf.mii_type.Value()),
+ static_cast<u8>(bf.mii_region.Value()),
+ static_cast<u8>(bf.face_type.Value()),
+ static_cast<u8>(bf.face_color.Value()),
+ static_cast<u8>(bf.face_wrinkle.Value()),
+ static_cast<u8>(bf.face_makeup.Value()),
+ static_cast<u8>(bf.hair_type.Value()),
+ static_cast<u8>(bf.hair_color.Value()),
+ static_cast<bool>(bf.hair_flip.Value()),
+ static_cast<u8>(bf.eye_type.Value()),
+ static_cast<u8>(bf.eye_color.Value()),
+ static_cast<u8>(bf.eye_scale.Value()),
+ static_cast<u8>(bf.eye_aspect.Value()),
+ static_cast<u8>(bf.eye_rotate.Value()),
+ static_cast<u8>(bf.eye_x.Value()),
+ static_cast<u8>(bf.eye_y.Value()),
+ static_cast<u8>(bf.eyebrow_type.Value()),
+ static_cast<u8>(bf.eyebrow_color.Value()),
+ static_cast<u8>(bf.eyebrow_scale.Value()),
+ static_cast<u8>(bf.eyebrow_aspect.Value()),
+ static_cast<u8>(bf.eyebrow_rotate.Value()),
+ static_cast<u8>(bf.eyebrow_x.Value()),
+ static_cast<u8>(bf.eyebrow_y.Value()),
+ static_cast<u8>(bf.nose_type.Value()),
+ static_cast<u8>(bf.nose_scale.Value()),
+ static_cast<u8>(bf.nose_y.Value()),
+ static_cast<u8>(bf.mouth_type.Value()),
+ static_cast<u8>(bf.mouth_color.Value()),
+ static_cast<u8>(bf.mouth_scale.Value()),
+ static_cast<u8>(bf.mouth_aspect.Value()),
+ static_cast<u8>(bf.mouth_y.Value()),
+ static_cast<u8>(bf.facial_hair_color.Value()),
+ static_cast<u8>(bf.beard_type.Value()),
+ static_cast<u8>(bf.mustache_type.Value()),
+ static_cast<u8>(bf.mustache_scale.Value()),
+ static_cast<u8>(bf.mustache_y.Value()),
+ static_cast<u8>(bf.glasses_type.Value()),
+ static_cast<u8>(bf.glasses_color.Value()),
+ static_cast<u8>(bf.glasses_scale.Value()),
+ static_cast<u8>(bf.glasses_y.Value()),
+ static_cast<u8>(bf.mole_type.Value()),
+ static_cast<u8>(bf.mole_scale.Value()),
+ static_cast<u8>(bf.mole_x.Value()),
+ static_cast<u8>(bf.mole_y.Value()),
+ 0x00,
+ };
+}
+MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
+ MiiStoreData out{};
+ out.name = ResizeArray<char16_t, 11, 10>(info.name);
+ out.uuid = info.uuid;
+
+ MiiStoreBitFields bf{};
+
+ bf.hair_type.Assign(info.hair_type);
+ bf.mole_type.Assign(info.mole_type);
+ bf.height.Assign(info.height);
+ bf.hair_flip.Assign(info.hair_flip);
+ bf.weight.Assign(info.weight);
+ bf.hair_color.Assign(info.hair_color);
+
+ bf.gender.Assign(info.gender);
+ bf.eye_color.Assign(info.eye_color);
+ bf.eyebrow_color.Assign(info.eyebrow_color);
+ bf.mouth_color.Assign(info.mouth_color);
+ bf.facial_hair_color.Assign(info.facial_hair_color);
+
+ bf.mii_type.Assign(info.mii_type);
+ bf.glasses_color.Assign(info.glasses_color);
+ bf.font_region.Assign(info.font_region);
+ bf.eye_type.Assign(info.eye_type);
+ bf.mii_region.Assign(info.mii_region);
+ bf.mouth_type.Assign(info.mouth_type);
+ bf.glasses_scale.Assign(info.glasses_scale);
+ bf.eye_y.Assign(info.eye_y);
+
+ bf.mustache_type.Assign(info.mustache_type);
+ bf.eyebrow_type.Assign(info.eyebrow_type);
+ bf.beard_type.Assign(info.beard_type);
+ bf.nose_type.Assign(info.nose_type);
+ bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
+ bf.nose_y.Assign(info.nose_y);
+ bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
+ bf.mouth_y.Assign(info.mouth_y);
+
+ bf.eye_rotate.Assign(info.eye_rotate);
+ bf.mustache_y.Assign(info.mustache_y);
+ bf.eye_aspect.Assign(info.eye_aspect_ratio);
+ bf.glasses_y.Assign(info.glasses_y);
+ bf.eye_scale.Assign(info.eye_scale);
+ bf.mole_x.Assign(info.mole_x);
+ bf.mole_y.Assign(info.mole_y);
+
+ bf.glasses_type.Assign(info.glasses_type);
+ bf.face_type.Assign(info.face_type);
+ bf.favorite_color.Assign(info.favorite_color);
+ bf.face_wrinkle.Assign(info.face_wrinkle);
+ bf.face_color.Assign(info.face_color);
+ bf.eye_x.Assign(info.eye_x);
+ bf.face_makeup.Assign(info.face_makeup);
+
+ bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
+ bf.eyebrow_scale.Assign(info.eyebrow_scale);
+ bf.eyebrow_y.Assign(info.eyebrow_y);
+ bf.eyebrow_x.Assign(info.eyebrow_x);
+ bf.mouth_scale.Assign(info.mouth_scale);
+ bf.nose_scale.Assign(info.nose_scale);
+ bf.mole_scale.Assign(info.mole_scale);
+ bf.mustache_scale.Assign(info.mustache_scale);
+
+ std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
+
+ return out;
+}
+
+} // namespace
+
+std::ostream& operator<<(std::ostream& os, Source source) {
+ os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
+ return os;
+}
+
+std::u16string MiiInfo::Name() const {
+ return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
+}
+
+bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
+ return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0;
+}
+
+bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
+ return !operator==(lhs, rhs);
+}
+
+std::u16string MiiStoreData::Name() const {
+ return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
+}
+
+MiiManager::MiiManager() = default;
+
+MiiManager::~MiiManager() = default;
+
+MiiInfo MiiManager::CreateRandom(RandomParameters params) {
+ LOG_WARNING(Service_Mii,
+ "(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
+ params.unknown_1, params.unknown_2, params.unknown_3);
+
+ return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID());
+}
+
+MiiInfo MiiManager::CreateDefault(u32 index) {
+ const auto new_mii = CreateMiiWithUniqueUUID();
+
+ database.miis.at(index) = new_mii;
+
+ EnsureDatabasePartition();
+ return ConvertStoreDataToInfo(new_mii);
+}
+
+bool MiiManager::CheckUpdatedFlag() const {
+ return updated_flag;
+}
+
+void MiiManager::ResetUpdatedFlag() {
+ updated_flag = false;
+}
+
+bool MiiManager::IsTestModeEnabled() const {
+ return is_test_mode_enabled;
+}
+
+bool MiiManager::Empty() const {
+ return Size() == 0;
+}
+
+bool MiiManager::Full() const {
+ return Size() == MAX_MIIS;
+}
+
+void MiiManager::Clear() {
+ updated_flag = true;
+ std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
+}
+
+u32 MiiManager::Size() const {
+ return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
+ [](const MiiStoreData& elem) { return elem.uuid; }));
+}
+
+MiiInfo MiiManager::GetInfo(u32 index) const {
+ return ConvertStoreDataToInfo(GetStoreData(index));
+}
+
+MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
+ return {GetInfo(index), Source::Database};
+}
+
+MiiStoreData MiiManager::GetStoreData(u32 index) const {
+ return database.miis.at(index);
+}
+
+MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
+ return {GetStoreData(index), Source::Database};
+}
+
+bool MiiManager::Remove(Common::UUID uuid) {
+ const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
+ [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
+
+ if (iter == database.miis.end())
+ return false;
+
+ updated_flag = true;
+ *iter = MiiStoreData{};
+ EnsureDatabasePartition();
+ return true;
+}
+
+u32 MiiManager::IndexOf(Common::UUID uuid) const {
+ const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
+ [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
+
+ if (iter == database.miis.end())
+ return INVALID_INDEX;
+
+ return static_cast<u32>(std::distance(database.miis.begin(), iter));
+}
+
+u32 MiiManager::IndexOf(const MiiInfo& info) const {
+ const auto iter =
+ std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) {
+ return ConvertStoreDataToInfo(elem) == info;
+ });
+
+ if (iter == database.miis.end())
+ return INVALID_INDEX;
+
+ return static_cast<u32>(std::distance(database.miis.begin(), iter));
+}
+
+bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
+ const auto index = IndexOf(uuid);
+
+ if (index == INVALID_INDEX || new_index >= MAX_MIIS)
+ return false;
+
+ updated_flag = true;
+ const auto moving = database.miis[index];
+ const auto replacing = database.miis[new_index];
+ if (replacing.uuid) {
+ database.miis[index] = replacing;
+ database.miis[new_index] = moving;
+ } else {
+ database.miis[index] = MiiStoreData{};
+ database.miis[new_index] = moving;
+ }
+
+ EnsureDatabasePartition();
+ return true;
+}
+
+bool MiiManager::AddOrReplace(const MiiStoreData& data) {
+ const auto index = IndexOf(data.uuid);
+
+ updated_flag = true;
+ if (index == INVALID_INDEX) {
+ const auto size = Size();
+ if (size == MAX_MIIS)
+ return false;
+ database.miis[size] = data;
+ } else {
+ database.miis[index] = data;
+ }
+
+ return true;
+}
+
+bool MiiManager::DestroyFile() {
+ database = DEFAULT_MII_DATABASE;
+ updated_flag = false;
+ return DeleteFile();
+}
+
+bool MiiManager::DeleteFile() {
+ const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
+ return FileUtil::Exists(path) && FileUtil::Delete(path);
+}
+
+void MiiManager::WriteToFile() {
+ const auto raw_path =
+ FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
+ if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
+ FileUtil::Delete(raw_path);
+
+ const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
+
+ if (!FileUtil::CreateFullPath(path)) {
+ LOG_WARNING(Service_Mii,
+ "Failed to create full path of MiiDatabase.dat. Create the directory "
+ "nand/system/save/8000000000000030 to mitigate this "
+ "issue.");
+ return;
+ }
+
+ FileUtil::IOFile save(path, "wb");
+
+ if (!save.IsOpen()) {
+ LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
+ "made in current session will be saved.");
+ return;
+ }
+
+ save.Resize(sizeof(MiiDatabase));
+ if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
+ LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed "
+ "and/or regenerated on next run.");
+ save.Resize(0);
+ }
+}
+
+void MiiManager::ReadFromFile() {
+ FileUtil::IOFile save(
+ FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
+
+ if (!save.IsOpen()) {
+ LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
+ "blank Mii database with no Miis.");
+ std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
+ return;
+ }
+
+ if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
+ LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
+ "Mii database with no Miis.");
+ std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
+ return;
+ }
+
+ EnsureDatabasePartition();
+}
+
+MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const {
+ auto new_mii = DEFAULT_MII;
+
+ do {
+ new_mii.uuid = Common::UUID::Generate();
+ } while (IndexOf(new_mii.uuid) != INVALID_INDEX);
+
+ return new_mii;
+}
+
+void MiiManager::EnsureDatabasePartition() {
+ std::stable_partition(database.miis.begin(), database.miis.end(),
+ [](const MiiStoreData& elem) { return elem.uuid; });
+}
+
+} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
new file mode 100644
index 000000000..38ad78a0d
--- /dev/null
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -0,0 +1,273 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/uuid.h"
+
+namespace Service::Mii {
+
+constexpr std::size_t MAX_MIIS = 100;
+constexpr u32 INVALID_INDEX = 0xFFFFFFFF;
+
+struct RandomParameters {
+ u32 unknown_1;
+ u32 unknown_2;
+ u32 unknown_3;
+};
+static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
+
+enum class Source : u32 {
+ Database = 0,
+ Default = 1,
+ Account = 2,
+ Friend = 3,
+};
+
+std::ostream& operator<<(std::ostream& os, Source source);
+
+struct MiiInfo {
+ Common::UUID uuid;
+ std::array<char16_t, 11> name;
+ u8 font_region;
+ u8 favorite_color;
+ u8 gender;
+ u8 height;
+ u8 weight;
+ u8 mii_type;
+ u8 mii_region;
+ u8 face_type;
+ u8 face_color;
+ u8 face_wrinkle;
+ u8 face_makeup;
+ u8 hair_type;
+ u8 hair_color;
+ bool hair_flip;
+ u8 eye_type;
+ u8 eye_color;
+ u8 eye_scale;
+ u8 eye_aspect_ratio;
+ u8 eye_rotate;
+ u8 eye_x;
+ u8 eye_y;
+ u8 eyebrow_type;
+ u8 eyebrow_color;
+ u8 eyebrow_scale;
+ u8 eyebrow_aspect_ratio;
+ u8 eyebrow_rotate;
+ u8 eyebrow_x;
+ u8 eyebrow_y;
+ u8 nose_type;
+ u8 nose_scale;
+ u8 nose_y;
+ u8 mouth_type;
+ u8 mouth_color;
+ u8 mouth_scale;
+ u8 mouth_aspect_ratio;
+ u8 mouth_y;
+ u8 facial_hair_color;
+ u8 beard_type;
+ u8 mustache_type;
+ u8 mustache_scale;
+ u8 mustache_y;
+ u8 glasses_type;
+ u8 glasses_color;
+ u8 glasses_scale;
+ u8 glasses_y;
+ u8 mole_type;
+ u8 mole_scale;
+ u8 mole_x;
+ u8 mole_y;
+ INSERT_PADDING_BYTES(1);
+
+ std::u16string Name() const;
+};
+static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
+static_assert(std::has_unique_object_representations_v<MiiInfo>,
+ "All bits of MiiInfo must contribute to its value.");
+
+bool operator==(const MiiInfo& lhs, const MiiInfo& rhs);
+bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
+
+#pragma pack(push, 4)
+struct MiiInfoElement {
+ MiiInfo info;
+ Source source;
+};
+static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
+
+struct MiiStoreBitFields {
+ union {
+ u32 word_0;
+
+ BitField<24, 8, u32> hair_type;
+ BitField<23, 1, u32> mole_type;
+ BitField<16, 7, u32> height;
+ BitField<15, 1, u32> hair_flip;
+ BitField<8, 7, u32> weight;
+ BitField<0, 7, u32> hair_color;
+ };
+
+ union {
+ u32 word_1;
+
+ BitField<31, 1, u32> gender;
+ BitField<24, 7, u32> eye_color;
+ BitField<16, 7, u32> eyebrow_color;
+ BitField<8, 7, u32> mouth_color;
+ BitField<0, 7, u32> facial_hair_color;
+ };
+
+ union {
+ u32 word_2;
+
+ BitField<31, 1, u32> mii_type;
+ BitField<24, 7, u32> glasses_color;
+ BitField<22, 2, u32> font_region;
+ BitField<16, 6, u32> eye_type;
+ BitField<14, 2, u32> mii_region;
+ BitField<8, 6, u32> mouth_type;
+ BitField<5, 3, u32> glasses_scale;
+ BitField<0, 5, u32> eye_y;
+ };
+
+ union {
+ u32 word_3;
+
+ BitField<29, 3, u32> mustache_type;
+ BitField<24, 5, u32> eyebrow_type;
+ BitField<21, 3, u32> beard_type;
+ BitField<16, 5, u32> nose_type;
+ BitField<13, 3, u32> mouth_aspect;
+ BitField<8, 5, u32> nose_y;
+ BitField<5, 3, u32> eyebrow_aspect;
+ BitField<0, 5, u32> mouth_y;
+ };
+
+ union {
+ u32 word_4;
+
+ BitField<29, 3, u32> eye_rotate;
+ BitField<24, 5, u32> mustache_y;
+ BitField<21, 3, u32> eye_aspect;
+ BitField<16, 5, u32> glasses_y;
+ BitField<13, 3, u32> eye_scale;
+ BitField<8, 5, u32> mole_x;
+ BitField<0, 5, u32> mole_y;
+ };
+
+ union {
+ u32 word_5;
+
+ BitField<24, 5, u32> glasses_type;
+ BitField<20, 4, u32> face_type;
+ BitField<16, 4, u32> favorite_color;
+ BitField<12, 4, u32> face_wrinkle;
+ BitField<8, 4, u32> face_color;
+ BitField<4, 4, u32> eye_x;
+ BitField<0, 4, u32> face_makeup;
+ };
+
+ union {
+ u32 word_6;
+
+ BitField<28, 4, u32> eyebrow_rotate;
+ BitField<24, 4, u32> eyebrow_scale;
+ BitField<20, 4, u32> eyebrow_y;
+ BitField<16, 4, u32> eyebrow_x;
+ BitField<12, 4, u32> mouth_scale;
+ BitField<8, 4, u32> nose_scale;
+ BitField<4, 4, u32> mole_scale;
+ BitField<0, 4, u32> mustache_scale;
+ };
+};
+static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
+static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
+ "MiiStoreBitFields is not trivially copyable.");
+
+struct MiiStoreData {
+ // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
+ // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
+ // not suitable for our uses.
+ std::array<u8, 0x1C> data;
+ static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
+
+ std::array<char16_t, 10> name;
+ Common::UUID uuid;
+ u16 crc_1;
+ u16 crc_2;
+
+ std::u16string Name() const;
+};
+static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
+
+struct MiiStoreDataElement {
+ MiiStoreData data;
+ Source source;
+};
+static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
+
+struct MiiDatabase {
+ u32 magic; // 'NFDB'
+ std::array<MiiStoreData, MAX_MIIS> miis;
+ INSERT_PADDING_BYTES(1);
+ u8 count;
+ u16 crc;
+};
+static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
+#pragma pack(pop)
+
+// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
+// with providing an easy interface for HLE emulation of the mii service.
+class MiiManager {
+public:
+ MiiManager();
+ ~MiiManager();
+
+ MiiInfo CreateRandom(RandomParameters params);
+ MiiInfo CreateDefault(u32 index);
+
+ bool CheckUpdatedFlag() const;
+ void ResetUpdatedFlag();
+
+ bool IsTestModeEnabled() const;
+
+ bool Empty() const;
+ bool Full() const;
+
+ void Clear();
+
+ u32 Size() const;
+
+ MiiInfo GetInfo(u32 index) const;
+ MiiInfoElement GetInfoElement(u32 index) const;
+ MiiStoreData GetStoreData(u32 index) const;
+ MiiStoreDataElement GetStoreDataElement(u32 index) const;
+
+ bool Remove(Common::UUID uuid);
+ u32 IndexOf(Common::UUID uuid) const;
+ u32 IndexOf(const MiiInfo& info) const;
+
+ bool Move(Common::UUID uuid, u32 new_index);
+ bool AddOrReplace(const MiiStoreData& data);
+
+ bool DestroyFile();
+ bool DeleteFile();
+
+private:
+ void WriteToFile();
+ void ReadFromFile();
+
+ MiiStoreData CreateMiiWithUniqueUUID() const;
+
+ void EnsureDatabasePartition();
+
+ MiiDatabase database;
+ bool updated_flag = false;
+ bool is_test_mode_enabled = false;
+};
+
+}; // namespace Service::Mii
diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp
index 5d31f638f..b405a4b66 100644
--- a/src/core/hle/service/ncm/ncm.cpp
+++ b/src/core/hle/service/ncm/ncm.cpp
@@ -4,15 +4,89 @@
#include <memory>
+#include "core/file_sys/romfs_factory.h"
+#include "core/hle/ipc_helpers.h"
#include "core/hle/service/ncm/ncm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::NCM {
-class LocationResolver final : public ServiceFramework<LocationResolver> {
+class ILocationResolver final : public ServiceFramework<ILocationResolver> {
public:
- explicit LocationResolver() : ServiceFramework{"lr"} {
+ explicit ILocationResolver(FileSys::StorageId id)
+ : ServiceFramework{"ILocationResolver"}, storage(id) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ResolveProgramPath"},
+ {1, nullptr, "RedirectProgramPath"},
+ {2, nullptr, "ResolveApplicationControlPath"},
+ {3, nullptr, "ResolveApplicationHtmlDocumentPath"},
+ {4, nullptr, "ResolveDataPath"},
+ {5, nullptr, "RedirectApplicationControlPath"},
+ {6, nullptr, "RedirectApplicationHtmlDocumentPath"},
+ {7, nullptr, "ResolveApplicationLegalInformationPath"},
+ {8, nullptr, "RedirectApplicationLegalInformationPath"},
+ {9, nullptr, "Refresh"},
+ {10, nullptr, "RedirectProgramPath2"},
+ {11, nullptr, "Refresh2"},
+ {12, nullptr, "DeleteProgramPath"},
+ {13, nullptr, "DeleteApplicationControlPath"},
+ {14, nullptr, "DeleteApplicationHtmlDocumentPath"},
+ {15, nullptr, "DeleteApplicationLegalInformationPath"},
+ {16, nullptr, ""},
+ {17, nullptr, ""},
+ {18, nullptr, ""},
+ {19, nullptr, ""},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ FileSys::StorageId storage;
+};
+
+class IRegisteredLocationResolver final : public ServiceFramework<IRegisteredLocationResolver> {
+public:
+ explicit IRegisteredLocationResolver() : ServiceFramework{"IRegisteredLocationResolver"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ResolveProgramPath"},
+ {1, nullptr, "RegisterProgramPath"},
+ {2, nullptr, "UnregisterProgramPath"},
+ {3, nullptr, "RedirectProgramPath"},
+ {4, nullptr, "ResolveHtmlDocumentPath"},
+ {5, nullptr, "RegisterHtmlDocumentPath"},
+ {6, nullptr, "UnregisterHtmlDocumentPath"},
+ {7, nullptr, "RedirectHtmlDocumentPath"},
+ {8, nullptr, ""},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class IAddOnContentLocationResolver final : public ServiceFramework<IAddOnContentLocationResolver> {
+public:
+ explicit IAddOnContentLocationResolver() : ServiceFramework{"IAddOnContentLocationResolver"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ResolveAddOnContentPath"},
+ {1, nullptr, "RegisterAddOnContentStorage"},
+ {2, nullptr, "UnregisterAllAddOnContentPath"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
+class LR final : public ServiceFramework<LR> {
+public:
+ explicit LR() : ServiceFramework{"lr"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenLocationResolver"},
@@ -52,7 +126,7 @@ public:
};
void InstallInterfaces(SM::ServiceManager& sm) {
- std::make_shared<LocationResolver>()->InstallAsService(sm);
+ std::make_shared<LR>()->InstallAsService(sm);
std::make_shared<NCM>()->InstallAsService(sm);
}
diff --git a/src/core/hle/service/ns/errors.h b/src/core/hle/service/ns/errors.h
new file mode 100644
index 000000000..f4aea8a65
--- /dev/null
+++ b/src/core/hle/service/ns/errors.h
@@ -0,0 +1,12 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::NS {
+
+constexpr ResultCode ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300};
+} \ No newline at end of file
diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp
new file mode 100644
index 000000000..29c4a820c
--- /dev/null
+++ b/src/core/hle/service/ns/language.cpp
@@ -0,0 +1,392 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/ns/language.h"
+#include "core/hle/service/set/set.h"
+
+namespace Service::NS {
+
+constexpr ApplicationLanguagePriorityList priority_list_american_english = {{
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_british_english = {{
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_japanese = {{
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_french = {{
+ ApplicationLanguage::French,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_german = {{
+ ApplicationLanguage::German,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_latin_american_spanish = {{
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_spanish = {{
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_italian = {{
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_dutch = {{
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_canadian_french = {{
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_portuguese = {{
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_russian = {{
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_korean = {{
+ ApplicationLanguage::Korean,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_traditional_chinese = {{
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Korean,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_simplified_chinese = {{
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Korean,
+}};
+
+const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(
+ const ApplicationLanguage lang) {
+ switch (lang) {
+ case ApplicationLanguage::AmericanEnglish:
+ return &priority_list_american_english;
+ case ApplicationLanguage::BritishEnglish:
+ return &priority_list_british_english;
+ case ApplicationLanguage::Japanese:
+ return &priority_list_japanese;
+ case ApplicationLanguage::French:
+ return &priority_list_french;
+ case ApplicationLanguage::German:
+ return &priority_list_german;
+ case ApplicationLanguage::LatinAmericanSpanish:
+ return &priority_list_latin_american_spanish;
+ case ApplicationLanguage::Spanish:
+ return &priority_list_spanish;
+ case ApplicationLanguage::Italian:
+ return &priority_list_italian;
+ case ApplicationLanguage::Dutch:
+ return &priority_list_dutch;
+ case ApplicationLanguage::CanadianFrench:
+ return &priority_list_canadian_french;
+ case ApplicationLanguage::Portuguese:
+ return &priority_list_portuguese;
+ case ApplicationLanguage::Russian:
+ return &priority_list_russian;
+ case ApplicationLanguage::Korean:
+ return &priority_list_korean;
+ case ApplicationLanguage::TraditionalChinese:
+ return &priority_list_traditional_chinese;
+ case ApplicationLanguage::SimplifiedChinese:
+ return &priority_list_simplified_chinese;
+ default:
+ return nullptr;
+ }
+}
+
+std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
+ const Set::LanguageCode language_code) {
+ switch (language_code) {
+ case Set::LanguageCode::EN_US:
+ return ApplicationLanguage::AmericanEnglish;
+ case Set::LanguageCode::EN_GB:
+ return ApplicationLanguage::BritishEnglish;
+ case Set::LanguageCode::JA:
+ return ApplicationLanguage::Japanese;
+ case Set::LanguageCode::FR:
+ return ApplicationLanguage::French;
+ case Set::LanguageCode::DE:
+ return ApplicationLanguage::German;
+ case Set::LanguageCode::ES_419:
+ return ApplicationLanguage::LatinAmericanSpanish;
+ case Set::LanguageCode::ES:
+ return ApplicationLanguage::Spanish;
+ case Set::LanguageCode::IT:
+ return ApplicationLanguage::Italian;
+ case Set::LanguageCode::NL:
+ return ApplicationLanguage::Dutch;
+ case Set::LanguageCode::FR_CA:
+ return ApplicationLanguage::CanadianFrench;
+ case Set::LanguageCode::PT:
+ return ApplicationLanguage::Portuguese;
+ case Set::LanguageCode::RU:
+ return ApplicationLanguage::Russian;
+ case Set::LanguageCode::KO:
+ return ApplicationLanguage::Korean;
+ case Set::LanguageCode::ZH_HANT:
+ return ApplicationLanguage::TraditionalChinese;
+ case Set::LanguageCode::ZH_HANS:
+ return ApplicationLanguage::SimplifiedChinese;
+ default:
+ return std::nullopt;
+ }
+}
+
+std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage lang) {
+ switch (lang) {
+ case ApplicationLanguage::AmericanEnglish:
+ return Set::LanguageCode::EN_US;
+ case ApplicationLanguage::BritishEnglish:
+ return Set::LanguageCode::EN_GB;
+ case ApplicationLanguage::Japanese:
+ return Set::LanguageCode::JA;
+ case ApplicationLanguage::French:
+ return Set::LanguageCode::FR;
+ case ApplicationLanguage::German:
+ return Set::LanguageCode::DE;
+ case ApplicationLanguage::LatinAmericanSpanish:
+ return Set::LanguageCode::ES_419;
+ case ApplicationLanguage::Spanish:
+ return Set::LanguageCode::ES;
+ case ApplicationLanguage::Italian:
+ return Set::LanguageCode::IT;
+ case ApplicationLanguage::Dutch:
+ return Set::LanguageCode::NL;
+ case ApplicationLanguage::CanadianFrench:
+ return Set::LanguageCode::FR_CA;
+ case ApplicationLanguage::Portuguese:
+ return Set::LanguageCode::PT;
+ case ApplicationLanguage::Russian:
+ return Set::LanguageCode::RU;
+ case ApplicationLanguage::Korean:
+ return Set::LanguageCode::KO;
+ case ApplicationLanguage::TraditionalChinese:
+ return Set::LanguageCode::ZH_HANT;
+ case ApplicationLanguage::SimplifiedChinese:
+ return Set::LanguageCode::ZH_HANS;
+ default:
+ return std::nullopt;
+ }
+}
+} // namespace Service::NS \ No newline at end of file
diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h
new file mode 100644
index 000000000..e9829f9d2
--- /dev/null
+++ b/src/core/hle/service/ns/language.h
@@ -0,0 +1,45 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <optional>
+#include <string>
+#include "common/common_types.h"
+
+namespace Service::Set {
+enum class LanguageCode : u64;
+}
+
+namespace Service::NS {
+/// This is nn::ns::detail::ApplicationLanguage
+enum class ApplicationLanguage : u8 {
+ AmericanEnglish = 0,
+ BritishEnglish,
+ Japanese,
+ French,
+ German,
+ LatinAmericanSpanish,
+ Spanish,
+ Italian,
+ Dutch,
+ CanadianFrench,
+ Portuguese,
+ Russian,
+ Korean,
+ TraditionalChinese,
+ SimplifiedChinese,
+ Count
+};
+using ApplicationLanguagePriorityList =
+ const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
+
+constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
+ return 1U << static_cast<u32>(lang);
+}
+
+const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
+std::optional<ApplicationLanguage> ConvertToApplicationLanguage(Set::LanguageCode language_code);
+std::optional<Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
+} // namespace Service::NS \ No newline at end of file
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 0eb04037a..ce88a2941 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -7,445 +7,507 @@
#include "core/file_sys/patch_manager.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/service/ns/errors.h"
+#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/ns/pl_u.h"
+#include "core/hle/service/set/set.h"
+#include "core/settings.h"
namespace Service::NS {
-class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
-public:
- explicit IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "CreateUserAccount"},
- };
- // clang-format on
+IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "CreateUserAccount"},
+ };
+ // clang-format on
- RegisterHandlers(functions);
- }
-};
+ RegisterHandlers(functions);
+}
-class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
-public:
- explicit IApplicationManagerInterface() : ServiceFramework{"IApplicationManagerInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "ListApplicationRecord"},
- {1, nullptr, "GenerateApplicationRecordCount"},
- {2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
- {3, nullptr, "GetApplicationViewDeprecated"},
- {4, nullptr, "DeleteApplicationEntity"},
- {5, nullptr, "DeleteApplicationCompletely"},
- {6, nullptr, "IsAnyApplicationEntityRedundant"},
- {7, nullptr, "DeleteRedundantApplicationEntity"},
- {8, nullptr, "IsApplicationEntityMovable"},
- {9, nullptr, "MoveApplicationEntity"},
- {11, nullptr, "CalculateApplicationOccupiedSize"},
- {16, nullptr, "PushApplicationRecord"},
- {17, nullptr, "ListApplicationRecordContentMeta"},
- {19, nullptr, "LaunchApplicationOld"},
- {21, nullptr, "GetApplicationContentPath"},
- {22, nullptr, "TerminateApplication"},
- {23, nullptr, "ResolveApplicationContentPath"},
- {26, nullptr, "BeginInstallApplication"},
- {27, nullptr, "DeleteApplicationRecord"},
- {30, nullptr, "RequestApplicationUpdateInfo"},
- {32, nullptr, "CancelApplicationDownload"},
- {33, nullptr, "ResumeApplicationDownload"},
- {35, nullptr, "UpdateVersionList"},
- {36, nullptr, "PushLaunchVersion"},
- {37, nullptr, "ListRequiredVersion"},
- {38, nullptr, "CheckApplicationLaunchVersion"},
- {39, nullptr, "CheckApplicationLaunchRights"},
- {40, nullptr, "GetApplicationLogoData"},
- {41, nullptr, "CalculateApplicationDownloadRequiredSize"},
- {42, nullptr, "CleanupSdCard"},
- {43, nullptr, "CheckSdCardMountStatus"},
- {44, nullptr, "GetSdCardMountStatusChangedEvent"},
- {45, nullptr, "GetGameCardAttachmentEvent"},
- {46, nullptr, "GetGameCardAttachmentInfo"},
- {47, nullptr, "GetTotalSpaceSize"},
- {48, nullptr, "GetFreeSpaceSize"},
- {49, nullptr, "GetSdCardRemovedEvent"},
- {52, nullptr, "GetGameCardUpdateDetectionEvent"},
- {53, nullptr, "DisableApplicationAutoDelete"},
- {54, nullptr, "EnableApplicationAutoDelete"},
- {55, nullptr, "GetApplicationDesiredLanguage"},
- {56, nullptr, "SetApplicationTerminateResult"},
- {57, nullptr, "ClearApplicationTerminateResult"},
- {58, nullptr, "GetLastSdCardMountUnexpectedResult"},
- {59, nullptr, "ConvertApplicationLanguageToLanguageCode"},
- {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
- {61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
- {62, nullptr, "GetGameCardStopper"},
- {63, nullptr, "IsSystemProgramInstalled"},
- {64, nullptr, "StartApplyDeltaTask"},
- {65, nullptr, "GetRequestServerStopper"},
- {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
- {67, nullptr, "CancelApplicationApplyDelta"},
- {68, nullptr, "ResumeApplicationApplyDelta"},
- {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
- {70, nullptr, "ResumeAll"},
- {71, nullptr, "GetStorageSize"},
- {80, nullptr, "RequestDownloadApplication"},
- {81, nullptr, "RequestDownloadAddOnContent"},
- {82, nullptr, "DownloadApplication"},
- {83, nullptr, "CheckApplicationResumeRights"},
- {84, nullptr, "GetDynamicCommitEvent"},
- {85, nullptr, "RequestUpdateApplication2"},
- {86, nullptr, "EnableApplicationCrashReport"},
- {87, nullptr, "IsApplicationCrashReportEnabled"},
- {90, nullptr, "BoostSystemMemoryResourceLimit"},
- {91, nullptr, "DeprecatedLaunchApplication"},
- {92, nullptr, "GetRunningApplicationProgramId"},
- {93, nullptr, "GetMainApplicationProgramIndex"},
- {94, nullptr, "LaunchApplication"},
- {95, nullptr, "GetApplicationLaunchInfo"},
- {96, nullptr, "AcquireApplicationLaunchInfo"},
- {97, nullptr, "GetMainApplicationProgramIndex2"},
- {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
- {100, nullptr, "ResetToFactorySettings"},
- {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
- {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
- {200, nullptr, "CalculateUserSaveDataStatistics"},
- {201, nullptr, "DeleteUserSaveDataAll"},
- {210, nullptr, "DeleteUserSystemSaveData"},
- {211, nullptr, "DeleteSaveData"},
- {220, nullptr, "UnregisterNetworkServiceAccount"},
- {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
- {300, nullptr, "GetApplicationShellEvent"},
- {301, nullptr, "PopApplicationShellEventInfo"},
- {302, nullptr, "LaunchLibraryApplet"},
- {303, nullptr, "TerminateLibraryApplet"},
- {304, nullptr, "LaunchSystemApplet"},
- {305, nullptr, "TerminateSystemApplet"},
- {306, nullptr, "LaunchOverlayApplet"},
- {307, nullptr, "TerminateOverlayApplet"},
- {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
- {401, nullptr, "InvalidateAllApplicationControlCache"},
- {402, nullptr, "RequestDownloadApplicationControlData"},
- {403, nullptr, "GetMaxApplicationControlCacheCount"},
- {404, nullptr, "InvalidateApplicationControlCache"},
- {405, nullptr, "ListApplicationControlCacheEntryInfo"},
- {406, nullptr, "GetApplicationControlProperty"},
- {502, nullptr, "RequestCheckGameCardRegistration"},
- {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
- {504, nullptr, "RequestRegisterGameCard"},
- {505, nullptr, "GetGameCardMountFailureEvent"},
- {506, nullptr, "IsGameCardInserted"},
- {507, nullptr, "EnsureGameCardAccess"},
- {508, nullptr, "GetLastGameCardMountFailureResult"},
- {509, nullptr, "ListApplicationIdOnGameCard"},
- {600, nullptr, "CountApplicationContentMeta"},
- {601, nullptr, "ListApplicationContentMetaStatus"},
- {602, nullptr, "ListAvailableAddOnContent"},
- {603, nullptr, "GetOwnedApplicationContentMetaStatus"},
- {604, nullptr, "RegisterContentsExternalKey"},
- {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
- {606, nullptr, "GetContentMetaStorage"},
- {607, nullptr, "ListAvailableAddOnContent"},
- {700, nullptr, "PushDownloadTaskList"},
- {701, nullptr, "ClearTaskStatusList"},
- {702, nullptr, "RequestDownloadTaskList"},
- {703, nullptr, "RequestEnsureDownloadTask"},
- {704, nullptr, "ListDownloadTaskStatus"},
- {705, nullptr, "RequestDownloadTaskListData"},
- {800, nullptr, "RequestVersionList"},
- {801, nullptr, "ListVersionList"},
- {802, nullptr, "RequestVersionListData"},
- {900, nullptr, "GetApplicationRecord"},
- {901, nullptr, "GetApplicationRecordProperty"},
- {902, nullptr, "EnableApplicationAutoUpdate"},
- {903, nullptr, "DisableApplicationAutoUpdate"},
- {904, nullptr, "TouchApplication"},
- {905, nullptr, "RequestApplicationUpdate"},
- {906, nullptr, "IsApplicationUpdateRequested"},
- {907, nullptr, "WithdrawApplicationUpdateRequest"},
- {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
- {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
- {910, nullptr, "HasApplicationRecord"},
- {911, nullptr, "SetPreInstalledApplication"},
- {912, nullptr, "ClearPreInstalledApplicationFlag"},
- {1000, nullptr, "RequestVerifyApplicationDeprecated"},
- {1001, nullptr, "CorruptApplicationForDebug"},
- {1002, nullptr, "RequestVerifyAddOnContentsRights"},
- {1003, nullptr, "RequestVerifyApplication"},
- {1004, nullptr, "CorruptContentForDebug"},
- {1200, nullptr, "NeedsUpdateVulnerability"},
- {1300, nullptr, "IsAnyApplicationEntityInstalled"},
- {1301, nullptr, "DeleteApplicationContentEntities"},
- {1302, nullptr, "CleanupUnrecordedApplicationEntity"},
- {1303, nullptr, "CleanupAddOnContentsWithNoRights"},
- {1304, nullptr, "DeleteApplicationContentEntity"},
- {1305, nullptr, "TryDeleteRunningApplicationEntity"},
- {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
- {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
- {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
- {1309, nullptr, "CleanupUnavailableAddOnContents"},
- {1400, nullptr, "PrepareShutdown"},
- {1500, nullptr, "FormatSdCard"},
- {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
- {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
- {1504, nullptr, "InsertSdCard"},
- {1505, nullptr, "RemoveSdCard"},
- {1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
- {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
- {1700, nullptr, "ListApplicationDownloadingContentMeta"},
- {1701, nullptr, "GetApplicationView"},
- {1702, nullptr, "GetApplicationDownloadTaskStatus"},
- {1703, nullptr, "GetApplicationViewDownloadErrorContext"},
- {1800, nullptr, "IsNotificationSetupCompleted"},
- {1801, nullptr, "GetLastNotificationInfoCount"},
- {1802, nullptr, "ListLastNotificationInfo"},
- {1803, nullptr, "ListNotificationTask"},
- {1900, nullptr, "IsActiveAccount"},
- {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
- {1902, nullptr, "GetApplicationTicketInfo"},
- {2000, nullptr, "GetSystemDeliveryInfo"},
- {2001, nullptr, "SelectLatestSystemDeliveryInfo"},
- {2002, nullptr, "VerifyDeliveryProtocolVersion"},
- {2003, nullptr, "GetApplicationDeliveryInfo"},
- {2004, nullptr, "HasAllContentsToDeliver"},
- {2005, nullptr, "CompareApplicationDeliveryInfo"},
- {2006, nullptr, "CanDeliverApplication"},
- {2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
- {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
- {2009, nullptr, "EstimateRequiredSize"},
- {2010, nullptr, "RequestReceiveApplication"},
- {2011, nullptr, "CommitReceiveApplication"},
- {2012, nullptr, "GetReceiveApplicationProgress"},
- {2013, nullptr, "RequestSendApplication"},
- {2014, nullptr, "GetSendApplicationProgress"},
- {2015, nullptr, "CompareSystemDeliveryInfo"},
- {2016, nullptr, "ListNotCommittedContentMeta"},
- {2017, nullptr, "CreateDownloadTask"},
- {2018, nullptr, "GetApplicationDeliveryInfoHash"},
- {2050, nullptr, "GetApplicationRightsOnClient"},
- {2100, nullptr, "GetApplicationTerminateResult"},
- {2101, nullptr, "GetRawApplicationTerminateResult"},
- {2150, nullptr, "CreateRightsEnvironment"},
- {2151, nullptr, "DestroyRightsEnvironment"},
- {2152, nullptr, "ActivateRightsEnvironment"},
- {2153, nullptr, "DeactivateRightsEnvironment"},
- {2154, nullptr, "ForceActivateRightsContextForExit"},
- {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
- {2161, nullptr, "SetUsersToRightsEnvironment"},
- {2170, nullptr, "GetRightsEnvironmentStatus"},
- {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
- {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
- {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
- {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
- {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
- {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
- {2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
- {2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
- {2250, nullptr, "RequestReportActiveELicence"},
- {2300, nullptr, "ListEventLog"},
- };
- // clang-format on
+IAccountProxyInterface::~IAccountProxyInterface() = default;
+
+IApplicationManagerInterface::IApplicationManagerInterface()
+ : ServiceFramework{"IApplicationManagerInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ListApplicationRecord"},
+ {1, nullptr, "GenerateApplicationRecordCount"},
+ {2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
+ {3, nullptr, "GetApplicationViewDeprecated"},
+ {4, nullptr, "DeleteApplicationEntity"},
+ {5, nullptr, "DeleteApplicationCompletely"},
+ {6, nullptr, "IsAnyApplicationEntityRedundant"},
+ {7, nullptr, "DeleteRedundantApplicationEntity"},
+ {8, nullptr, "IsApplicationEntityMovable"},
+ {9, nullptr, "MoveApplicationEntity"},
+ {11, nullptr, "CalculateApplicationOccupiedSize"},
+ {16, nullptr, "PushApplicationRecord"},
+ {17, nullptr, "ListApplicationRecordContentMeta"},
+ {19, nullptr, "LaunchApplicationOld"},
+ {21, nullptr, "GetApplicationContentPath"},
+ {22, nullptr, "TerminateApplication"},
+ {23, nullptr, "ResolveApplicationContentPath"},
+ {26, nullptr, "BeginInstallApplication"},
+ {27, nullptr, "DeleteApplicationRecord"},
+ {30, nullptr, "RequestApplicationUpdateInfo"},
+ {32, nullptr, "CancelApplicationDownload"},
+ {33, nullptr, "ResumeApplicationDownload"},
+ {35, nullptr, "UpdateVersionList"},
+ {36, nullptr, "PushLaunchVersion"},
+ {37, nullptr, "ListRequiredVersion"},
+ {38, nullptr, "CheckApplicationLaunchVersion"},
+ {39, nullptr, "CheckApplicationLaunchRights"},
+ {40, nullptr, "GetApplicationLogoData"},
+ {41, nullptr, "CalculateApplicationDownloadRequiredSize"},
+ {42, nullptr, "CleanupSdCard"},
+ {43, nullptr, "CheckSdCardMountStatus"},
+ {44, nullptr, "GetSdCardMountStatusChangedEvent"},
+ {45, nullptr, "GetGameCardAttachmentEvent"},
+ {46, nullptr, "GetGameCardAttachmentInfo"},
+ {47, nullptr, "GetTotalSpaceSize"},
+ {48, nullptr, "GetFreeSpaceSize"},
+ {49, nullptr, "GetSdCardRemovedEvent"},
+ {52, nullptr, "GetGameCardUpdateDetectionEvent"},
+ {53, nullptr, "DisableApplicationAutoDelete"},
+ {54, nullptr, "EnableApplicationAutoDelete"},
+ {55, &IApplicationManagerInterface::GetApplicationDesiredLanguage, "GetApplicationDesiredLanguage"},
+ {56, nullptr, "SetApplicationTerminateResult"},
+ {57, nullptr, "ClearApplicationTerminateResult"},
+ {58, nullptr, "GetLastSdCardMountUnexpectedResult"},
+ {59, &IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode, "ConvertApplicationLanguageToLanguageCode"},
+ {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
+ {61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
+ {62, nullptr, "GetGameCardStopper"},
+ {63, nullptr, "IsSystemProgramInstalled"},
+ {64, nullptr, "StartApplyDeltaTask"},
+ {65, nullptr, "GetRequestServerStopper"},
+ {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
+ {67, nullptr, "CancelApplicationApplyDelta"},
+ {68, nullptr, "ResumeApplicationApplyDelta"},
+ {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
+ {70, nullptr, "ResumeAll"},
+ {71, nullptr, "GetStorageSize"},
+ {80, nullptr, "RequestDownloadApplication"},
+ {81, nullptr, "RequestDownloadAddOnContent"},
+ {82, nullptr, "DownloadApplication"},
+ {83, nullptr, "CheckApplicationResumeRights"},
+ {84, nullptr, "GetDynamicCommitEvent"},
+ {85, nullptr, "RequestUpdateApplication2"},
+ {86, nullptr, "EnableApplicationCrashReport"},
+ {87, nullptr, "IsApplicationCrashReportEnabled"},
+ {90, nullptr, "BoostSystemMemoryResourceLimit"},
+ {91, nullptr, "DeprecatedLaunchApplication"},
+ {92, nullptr, "GetRunningApplicationProgramId"},
+ {93, nullptr, "GetMainApplicationProgramIndex"},
+ {94, nullptr, "LaunchApplication"},
+ {95, nullptr, "GetApplicationLaunchInfo"},
+ {96, nullptr, "AcquireApplicationLaunchInfo"},
+ {97, nullptr, "GetMainApplicationProgramIndex2"},
+ {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
+ {100, nullptr, "ResetToFactorySettings"},
+ {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
+ {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
+ {200, nullptr, "CalculateUserSaveDataStatistics"},
+ {201, nullptr, "DeleteUserSaveDataAll"},
+ {210, nullptr, "DeleteUserSystemSaveData"},
+ {211, nullptr, "DeleteSaveData"},
+ {220, nullptr, "UnregisterNetworkServiceAccount"},
+ {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
+ {300, nullptr, "GetApplicationShellEvent"},
+ {301, nullptr, "PopApplicationShellEventInfo"},
+ {302, nullptr, "LaunchLibraryApplet"},
+ {303, nullptr, "TerminateLibraryApplet"},
+ {304, nullptr, "LaunchSystemApplet"},
+ {305, nullptr, "TerminateSystemApplet"},
+ {306, nullptr, "LaunchOverlayApplet"},
+ {307, nullptr, "TerminateOverlayApplet"},
+ {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
+ {401, nullptr, "InvalidateAllApplicationControlCache"},
+ {402, nullptr, "RequestDownloadApplicationControlData"},
+ {403, nullptr, "GetMaxApplicationControlCacheCount"},
+ {404, nullptr, "InvalidateApplicationControlCache"},
+ {405, nullptr, "ListApplicationControlCacheEntryInfo"},
+ {406, nullptr, "GetApplicationControlProperty"},
+ {502, nullptr, "RequestCheckGameCardRegistration"},
+ {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
+ {504, nullptr, "RequestRegisterGameCard"},
+ {505, nullptr, "GetGameCardMountFailureEvent"},
+ {506, nullptr, "IsGameCardInserted"},
+ {507, nullptr, "EnsureGameCardAccess"},
+ {508, nullptr, "GetLastGameCardMountFailureResult"},
+ {509, nullptr, "ListApplicationIdOnGameCard"},
+ {600, nullptr, "CountApplicationContentMeta"},
+ {601, nullptr, "ListApplicationContentMetaStatus"},
+ {602, nullptr, "ListAvailableAddOnContent"},
+ {603, nullptr, "GetOwnedApplicationContentMetaStatus"},
+ {604, nullptr, "RegisterContentsExternalKey"},
+ {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
+ {606, nullptr, "GetContentMetaStorage"},
+ {607, nullptr, "ListAvailableAddOnContent"},
+ {700, nullptr, "PushDownloadTaskList"},
+ {701, nullptr, "ClearTaskStatusList"},
+ {702, nullptr, "RequestDownloadTaskList"},
+ {703, nullptr, "RequestEnsureDownloadTask"},
+ {704, nullptr, "ListDownloadTaskStatus"},
+ {705, nullptr, "RequestDownloadTaskListData"},
+ {800, nullptr, "RequestVersionList"},
+ {801, nullptr, "ListVersionList"},
+ {802, nullptr, "RequestVersionListData"},
+ {900, nullptr, "GetApplicationRecord"},
+ {901, nullptr, "GetApplicationRecordProperty"},
+ {902, nullptr, "EnableApplicationAutoUpdate"},
+ {903, nullptr, "DisableApplicationAutoUpdate"},
+ {904, nullptr, "TouchApplication"},
+ {905, nullptr, "RequestApplicationUpdate"},
+ {906, nullptr, "IsApplicationUpdateRequested"},
+ {907, nullptr, "WithdrawApplicationUpdateRequest"},
+ {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
+ {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
+ {910, nullptr, "HasApplicationRecord"},
+ {911, nullptr, "SetPreInstalledApplication"},
+ {912, nullptr, "ClearPreInstalledApplicationFlag"},
+ {1000, nullptr, "RequestVerifyApplicationDeprecated"},
+ {1001, nullptr, "CorruptApplicationForDebug"},
+ {1002, nullptr, "RequestVerifyAddOnContentsRights"},
+ {1003, nullptr, "RequestVerifyApplication"},
+ {1004, nullptr, "CorruptContentForDebug"},
+ {1200, nullptr, "NeedsUpdateVulnerability"},
+ {1300, nullptr, "IsAnyApplicationEntityInstalled"},
+ {1301, nullptr, "DeleteApplicationContentEntities"},
+ {1302, nullptr, "CleanupUnrecordedApplicationEntity"},
+ {1303, nullptr, "CleanupAddOnContentsWithNoRights"},
+ {1304, nullptr, "DeleteApplicationContentEntity"},
+ {1305, nullptr, "TryDeleteRunningApplicationEntity"},
+ {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
+ {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
+ {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
+ {1309, nullptr, "CleanupUnavailableAddOnContents"},
+ {1400, nullptr, "PrepareShutdown"},
+ {1500, nullptr, "FormatSdCard"},
+ {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
+ {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
+ {1504, nullptr, "InsertSdCard"},
+ {1505, nullptr, "RemoveSdCard"},
+ {1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
+ {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
+ {1700, nullptr, "ListApplicationDownloadingContentMeta"},
+ {1701, nullptr, "GetApplicationView"},
+ {1702, nullptr, "GetApplicationDownloadTaskStatus"},
+ {1703, nullptr, "GetApplicationViewDownloadErrorContext"},
+ {1800, nullptr, "IsNotificationSetupCompleted"},
+ {1801, nullptr, "GetLastNotificationInfoCount"},
+ {1802, nullptr, "ListLastNotificationInfo"},
+ {1803, nullptr, "ListNotificationTask"},
+ {1900, nullptr, "IsActiveAccount"},
+ {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
+ {1902, nullptr, "GetApplicationTicketInfo"},
+ {2000, nullptr, "GetSystemDeliveryInfo"},
+ {2001, nullptr, "SelectLatestSystemDeliveryInfo"},
+ {2002, nullptr, "VerifyDeliveryProtocolVersion"},
+ {2003, nullptr, "GetApplicationDeliveryInfo"},
+ {2004, nullptr, "HasAllContentsToDeliver"},
+ {2005, nullptr, "CompareApplicationDeliveryInfo"},
+ {2006, nullptr, "CanDeliverApplication"},
+ {2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
+ {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
+ {2009, nullptr, "EstimateRequiredSize"},
+ {2010, nullptr, "RequestReceiveApplication"},
+ {2011, nullptr, "CommitReceiveApplication"},
+ {2012, nullptr, "GetReceiveApplicationProgress"},
+ {2013, nullptr, "RequestSendApplication"},
+ {2014, nullptr, "GetSendApplicationProgress"},
+ {2015, nullptr, "CompareSystemDeliveryInfo"},
+ {2016, nullptr, "ListNotCommittedContentMeta"},
+ {2017, nullptr, "CreateDownloadTask"},
+ {2018, nullptr, "GetApplicationDeliveryInfoHash"},
+ {2050, nullptr, "GetApplicationRightsOnClient"},
+ {2100, nullptr, "GetApplicationTerminateResult"},
+ {2101, nullptr, "GetRawApplicationTerminateResult"},
+ {2150, nullptr, "CreateRightsEnvironment"},
+ {2151, nullptr, "DestroyRightsEnvironment"},
+ {2152, nullptr, "ActivateRightsEnvironment"},
+ {2153, nullptr, "DeactivateRightsEnvironment"},
+ {2154, nullptr, "ForceActivateRightsContextForExit"},
+ {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
+ {2161, nullptr, "SetUsersToRightsEnvironment"},
+ {2170, nullptr, "GetRightsEnvironmentStatus"},
+ {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
+ {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
+ {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
+ {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
+ {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
+ {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
+ {2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
+ {2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
+ {2250, nullptr, "RequestReportActiveELicence"},
+ {2300, nullptr, "ListEventLog"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
- RegisterHandlers(functions);
- }
+IApplicationManagerInterface::~IApplicationManagerInterface() = default;
+
+void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto flag = rp.PopRaw<u64>();
+ LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
- void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto flag = rp.PopRaw<u64>();
- LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
-
- const auto title_id = rp.PopRaw<u64>();
-
- const auto size = ctx.GetWriteBufferSize();
-
- const FileSys::PatchManager pm{title_id};
- 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(ResultCode(-1));
- 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));
+ const auto title_id = rp.PopRaw<u64>();
+
+ const auto size = ctx.GetWriteBufferSize();
+
+ const FileSys::PatchManager pm{title_id};
+ 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(ResultCode(-1));
+ return;
}
- 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(ResultCode(-1));
- 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);
+ 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));
+ }
+
+ 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(ResultCode(-1));
+ return;
}
- ctx.WriteBuffer(out);
+ 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(RESULT_SUCCESS);
+ rb.Push<u32>(static_cast<u32>(out.size()));
+}
+
+void IApplicationManagerInterface::GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto supported_languages = rp.Pop<u32>();
+ const auto res = GetApplicationDesiredLanguage(supported_languages);
+ if (res.Succeeded()) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(static_cast<u32>(out.size()));
+ rb.Push<u32>(*res);
+ } else {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res.Code());
}
-};
+}
-class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
-public:
- explicit IApplicationVersionInterface() : ServiceFramework{"IApplicationVersionInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetLaunchRequiredVersion"},
- {1, nullptr, "UpgradeLaunchRequiredVersion"},
- {35, nullptr, "UpdateVersionList"},
- {36, nullptr, "PushLaunchVersion"},
- {37, nullptr, "ListRequiredVersion"},
- {800, nullptr, "RequestVersionList"},
- {801, nullptr, "ListVersionList"},
- {802, nullptr, "RequestVersionListData"},
- {1000, nullptr, "PerformAutoUpdate"},
- };
- // clang-format on
+ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
+ const u32 supported_languages) {
+ LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
- RegisterHandlers(functions);
- }
-};
+ // Get language code from settings
+ const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index);
-class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
-public:
- explicit IContentManagerInterface() : ServiceFramework{"IContentManagerInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {11, nullptr, "CalculateApplicationOccupiedSize"},
- {43, nullptr, "CheckSdCardMountStatus"},
- {47, nullptr, "GetTotalSpaceSize"},
- {48, nullptr, "GetFreeSpaceSize"},
- {600, nullptr, "CountApplicationContentMeta"},
- {601, nullptr, "ListApplicationContentMetaStatus"},
- {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
- {607, nullptr, "IsAnyApplicationRunning"},
- };
- // clang-format on
+ // Convert to application language, get priority list
+ const auto application_language = ConvertToApplicationLanguage(language_code);
+ if (application_language == std::nullopt) {
+ return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
+ }
+ const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
+ if (!priority_list) {
+ return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
+ }
- RegisterHandlers(functions);
+ // 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) {
+ return MakeResult(static_cast<u8>(lang));
+ }
}
-};
-class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
-public:
- explicit IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {21, nullptr, "GetApplicationContentPath"},
- {23, nullptr, "ResolveApplicationContentPath"},
- {93, nullptr, "GetRunningApplicationProgramId"},
- };
- // clang-format on
+ return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
+}
- RegisterHandlers(functions);
- }
-};
+void IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
+ Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto application_language = rp.Pop<u8>();
-class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
-public:
- explicit IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {701, nullptr, "ClearTaskStatusList"},
- {702, nullptr, "RequestDownloadTaskList"},
- {703, nullptr, "RequestEnsureDownloadTask"},
- {704, nullptr, "ListDownloadTaskStatus"},
- {705, nullptr, "RequestDownloadTaskListData"},
- {706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
- {707, nullptr, "EnableAutoCommit"},
- {708, nullptr, "DisableAutoCommit"},
- {709, nullptr, "TriggerDynamicCommitEvent"},
- };
- // clang-format on
+ const auto res = ConvertApplicationLanguageToLanguageCode(application_language);
+ if (res.Succeeded()) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(*res);
+ } else {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res.Code());
+ }
+}
- RegisterHandlers(functions);
+ResultVal<u64> IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
+ u8 application_language) {
+ const auto language_code =
+ ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language));
+ if (language_code == std::nullopt) {
+ return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
}
-};
-class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
-public:
- explicit IECommerceInterface() : ServiceFramework{"IECommerceInterface"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "RequestLinkDevice"},
- {1, nullptr, "RequestCleanupAllPreInstalledApplications"},
- {2, nullptr, "RequestCleanupPreInstalledApplication"},
- {3, nullptr, "RequestSyncRights"},
- {4, nullptr, "RequestUnlinkDevice"},
- {5, nullptr, "RequestRevokeAllELicense"},
- };
- // clang-format on
+ return MakeResult(static_cast<u64>(*language_code));
+}
- RegisterHandlers(functions);
- }
-};
+IApplicationVersionInterface::IApplicationVersionInterface()
+ : ServiceFramework{"IApplicationVersionInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetLaunchRequiredVersion"},
+ {1, nullptr, "UpgradeLaunchRequiredVersion"},
+ {35, nullptr, "UpdateVersionList"},
+ {36, nullptr, "PushLaunchVersion"},
+ {37, nullptr, "ListRequiredVersion"},
+ {800, nullptr, "RequestVersionList"},
+ {801, nullptr, "ListVersionList"},
+ {802, nullptr, "RequestVersionListData"},
+ {1000, nullptr, "PerformAutoUpdate"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
-class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
-public:
- explicit IFactoryResetInterface() : ServiceFramework{"IFactoryResetInterface"} {
- // clang-format off
+IApplicationVersionInterface::~IApplicationVersionInterface() = default;
+
+IContentManagerInterface::IContentManagerInterface()
+ : ServiceFramework{"IContentManagerInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {11, nullptr, "CalculateApplicationOccupiedSize"},
+ {43, nullptr, "CheckSdCardMountStatus"},
+ {47, nullptr, "GetTotalSpaceSize"},
+ {48, nullptr, "GetFreeSpaceSize"},
+ {600, nullptr, "CountApplicationContentMeta"},
+ {601, nullptr, "ListApplicationContentMetaStatus"},
+ {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
+ {607, nullptr, "IsAnyApplicationRunning"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IContentManagerInterface::~IContentManagerInterface() = default;
+
+IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {21, nullptr, "GetApplicationContentPath"},
+ {23, nullptr, "ResolveApplicationContentPath"},
+ {93, nullptr, "GetRunningApplicationProgramId"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDocumentInterface::~IDocumentInterface() = default;
+
+IDownloadTaskInterface::IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {701, nullptr, "ClearTaskStatusList"},
+ {702, nullptr, "RequestDownloadTaskList"},
+ {703, nullptr, "RequestEnsureDownloadTask"},
+ {704, nullptr, "ListDownloadTaskStatus"},
+ {705, nullptr, "RequestDownloadTaskListData"},
+ {706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
+ {707, nullptr, "EnableAutoCommit"},
+ {708, nullptr, "DisableAutoCommit"},
+ {709, nullptr, "TriggerDynamicCommitEvent"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IDownloadTaskInterface::~IDownloadTaskInterface() = default;
+
+IECommerceInterface::IECommerceInterface() : ServiceFramework{"IECommerceInterface"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "RequestLinkDevice"},
+ {1, nullptr, "RequestCleanupAllPreInstalledApplications"},
+ {2, nullptr, "RequestCleanupPreInstalledApplication"},
+ {3, nullptr, "RequestSyncRights"},
+ {4, nullptr, "RequestUnlinkDevice"},
+ {5, nullptr, "RequestRevokeAllELicense"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IECommerceInterface::~IECommerceInterface() = default;
+
+IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
+ : ServiceFramework{"IFactoryResetInterface"} {
+ // clang-format off
static const FunctionInfo functions[] = {
{100, nullptr, "ResetToFactorySettings"},
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
};
- // clang-format on
+ // clang-format on
- RegisterHandlers(functions);
- }
-};
-
-class NS final : public ServiceFramework<NS> {
-public:
- explicit NS(const char* name) : ServiceFramework{name} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
- {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
- {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
- {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
- {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
- {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
- {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
- {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
- };
- // clang-format on
+ RegisterHandlers(functions);
+}
- RegisterHandlers(functions);
- }
+IFactoryResetInterface::~IFactoryResetInterface() = default;
+
+NS::NS(const char* name) : ServiceFramework{name} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
+ {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
+ {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
+ {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
+ {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
+ {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
+ {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
+ {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
-private:
- template <typename T>
- void PushInterface(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NS, "called");
+NS::~NS() = default;
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<T>();
- }
-};
+std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
+ return GetInterface<IApplicationManagerInterface>();
+}
class NS_DEV final : public ServiceFramework<NS_DEV> {
public:
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index b81ca8f1e..0f4bab4cb 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -8,6 +8,88 @@
namespace Service::NS {
+class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
+public:
+ explicit IAccountProxyInterface();
+ ~IAccountProxyInterface();
+};
+
+class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
+public:
+ explicit IApplicationManagerInterface();
+ ~IApplicationManagerInterface();
+
+ ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
+ ResultVal<u64> ConvertApplicationLanguageToLanguageCode(u8 application_language);
+
+private:
+ void GetApplicationControlData(Kernel::HLERequestContext& ctx);
+ void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx);
+ void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx);
+};
+
+class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
+public:
+ explicit IApplicationVersionInterface();
+ ~IApplicationVersionInterface();
+};
+
+class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
+public:
+ explicit IContentManagerInterface();
+ ~IContentManagerInterface();
+};
+
+class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
+public:
+ explicit IDocumentInterface();
+ ~IDocumentInterface();
+};
+
+class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
+public:
+ explicit IDownloadTaskInterface();
+ ~IDownloadTaskInterface();
+};
+
+class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
+public:
+ explicit IECommerceInterface();
+ ~IECommerceInterface();
+};
+
+class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
+public:
+ explicit IFactoryResetInterface();
+ ~IFactoryResetInterface();
+};
+
+class NS final : public ServiceFramework<NS> {
+public:
+ explicit NS(const char* name);
+ ~NS();
+
+ std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
+
+private:
+ template <typename T>
+ void PushInterface(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<T>();
+ }
+
+ template <typename T>
+ std::shared_ptr<T> GetInterface() const {
+ static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
+ "Not a base of ServiceFrameworkBase");
+
+ return std::make_shared<T>();
+ }
+};
+
/// Registers all NS services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
diff --git a/src/core/hle/service/ns/ns_language.h b/src/core/hle/service/ns/ns_language.h
new file mode 100644
index 000000000..59ac85a19
--- /dev/null
+++ b/src/core/hle/service/ns/ns_language.h
@@ -0,0 +1,42 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+#include <optional>
+#include <string>
+#include "common/common_types.h"
+#include "core/hle/service/set/set.h"
+
+namespace Service::NS {
+/// This is nn::ns::detail::ApplicationLanguage
+enum class ApplicationLanguage : u8 {
+ AmericanEnglish = 0,
+ BritishEnglish,
+ Japanese,
+ French,
+ German,
+ LatinAmericanSpanish,
+ Spanish,
+ Italian,
+ Dutch,
+ CanadianFrench,
+ Portuguese,
+ Russian,
+ Korean,
+ TraditionalChinese,
+ SimplifiedChinese,
+ Count
+};
+using ApplicationLanguagePriorityList =
+ const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
+
+constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
+ return 1U << static_cast<u32>(lang);
+}
+
+const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
+std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
+ Service::Set::LanguageCode language_code);
+std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
+} // namespace Service::NS \ No newline at end of file
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 45812d238..0e28755bd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -185,7 +185,8 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
IoctlGetGpuTime params{};
std::memcpy(&params, input.data(), input.size());
- params.gpu_time = Core::Timing::cyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
+ const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
+ params.gpu_time = static_cast<u64_le>(ns.count());
std::memcpy(output.data(), &params, output.size());
return 0;
}
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index aa115935d..346bad80d 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -108,8 +108,9 @@ private:
LOG_DEBUG(Service_Time, "called");
const auto& core_timing = Core::System::GetInstance().CoreTiming();
- const SteadyClockTimePoint steady_clock_time_point{
- Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000};
+ const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
+ const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000),
+ {}};
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(steady_clock_time_point);
@@ -284,8 +285,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
}
const auto& core_timing = Core::System::GetInstance().CoreTiming();
- const SteadyClockTimePoint steady_clock_time_point{
- Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000, {}};
+ const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
+ const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
CalendarTime calendar_time{};
calendar_time.year = tm->tm_year + 1900;