diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp index 4c12ff57c..d27b7cf0c 100644 --- a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp @@ -15,37 +15,94 @@ */ #include #include "set_mitm_service.hpp" +#include "set_shim.h" namespace ams::mitm::settings { using namespace ams::settings; + namespace { + + constinit os::ProcessId g_application_process_id = os::InvalidProcessId; + constinit cfg::OverrideLocale g_application_locale; + constinit bool g_valid_language; + constinit bool g_valid_region; + } + + SetMitmService::SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c) : sf::MitmServiceImplBase(std::forward>(s), c) { + if (this->client_info.program_id == ncm::SystemProgramId::Ns) { + os::ProcessId application_process_id; + if (R_SUCCEEDED(pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) { + this->locale = g_application_locale; + this->is_valid_language = g_valid_language; + this->is_valid_region = g_valid_region; + this->got_locale = true; + } else { + this->InvalidateLocale(); + } + } else { + this->InvalidateLocale(); + } + } + Result SetMitmService::EnsureLocale() { + /* Optimization: if locale has already been gotten, we can stop. */ + if (AMS_LIKELY(this->got_locale)) { + return ResultSuccess(); + } + std::scoped_lock lk(this->lock); const bool is_ns = this->client_info.program_id == ncm::SystemProgramId::Ns; if (!this->got_locale) { - std::memset(&this->locale, 0xCC, sizeof(this->locale)); ncm::ProgramId program_id = this->client_info.program_id; + os::ProcessId application_process_id = os::InvalidProcessId; + if (is_ns) { /* When NS asks for a locale, refresh to get the current application locale. */ - os::ProcessId application_process_id; - R_TRY(pm::dmnt::GetApplicationProcessId(&application_process_id)); - R_TRY(pm::info::GetProgramId(&program_id, application_process_id)); + R_TRY(pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))); + R_TRY(pm::info::GetProgramId(std::addressof(program_id), application_process_id)); + } + this->locale = cfg::GetOverrideLocale(program_id); + this->is_valid_language = settings::IsValidLanguageCode(this->locale.language_code); + this->is_valid_region = settings::IsValidRegionCode(this->locale.region_code); + this->got_locale = true; + + if (is_ns) { + g_application_locale = this->locale; + g_valid_language = this->is_valid_language; + g_valid_region = this->is_valid_region; + g_application_process_id = application_process_id; } - this->locale = cfg::GetOverrideLocale(program_id); - this->got_locale = !is_ns; } return ResultSuccess(); } + void SetMitmService::InvalidateLocale() { + std::scoped_lock lk(this->lock); + + std::memset(std::addressof(this->locale), 0xCC, sizeof(this->locale)); + this->is_valid_language = false; + this->is_valid_region = false; + this->got_locale = false; + } + Result SetMitmService::GetLanguageCode(sf::Out out) { this->EnsureLocale(); /* If there's no override locale, just use the actual one. */ - R_UNLESS(settings::IsValidLanguageCode(this->locale.language_code), sm::mitm::ResultShouldForwardToSession()); + if (AMS_UNLIKELY(!this->is_valid_language)) { + static_assert(sizeof(u64) == sizeof(settings::LanguageCode)); + R_TRY(setGetLanguageCodeFwd(this->forward_service.get(), reinterpret_cast(std::addressof(this->locale.language_code)))); + + this->is_valid_language = true; + if (this->client_info.program_id == ncm::SystemProgramId::Ns) { + g_application_locale.language_code = this->locale.language_code; + g_valid_language = true; + } + } out.SetValue(this->locale.language_code); return ResultSuccess(); @@ -55,7 +112,16 @@ namespace ams::mitm::settings { this->EnsureLocale(); /* If there's no override locale, just use the actual one. */ - R_UNLESS(settings::IsValidRegionCode(this->locale.region_code), sm::mitm::ResultShouldForwardToSession()); + if (AMS_UNLIKELY(!this->is_valid_region)) { + static_assert(sizeof(::SetRegion) == sizeof(settings::RegionCode)); + R_TRY(setGetRegionCodeFwd(this->forward_service.get(), reinterpret_cast<::SetRegion *>(std::addressof(this->locale.region_code)))); + + this->is_valid_region = true; + if (this->client_info.program_id == ncm::SystemProgramId::Ns) { + g_application_locale.region_code = this->locale.region_code; + g_valid_region = true; + } + } out.SetValue(this->locale.region_code); return ResultSuccess(); diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp index fe57f425f..a69bcaefd 100644 --- a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp @@ -33,8 +33,10 @@ namespace ams::mitm::settings { os::Mutex lock{false}; cfg::OverrideLocale locale; bool got_locale = false; + bool is_valid_language = false; + bool is_valid_region = false; public: - using MitmServiceImplBase::MitmServiceImplBase; + SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c); public: static bool ShouldMitm(const sm::MitmProcessInfo &client_info) { /* We will mitm: @@ -44,6 +46,7 @@ namespace ams::mitm::settings { return client_info.program_id == ncm::SystemProgramId::Ns || is_game; } private: + void InvalidateLocale(); Result EnsureLocale(); public: Result GetLanguageCode(sf::Out out); diff --git a/stratosphere/ams_mitm/source/set_mitm/set_shim.c b/stratosphere/ams_mitm/source/set_mitm/set_shim.c new file mode 100644 index 000000000..255ad7603 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/set_shim.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "set_shim.h" + +static Result _setCmdNoInOut64(Service* srv, u64 *out, u32 cmd_id) { + return serviceDispatchOut(srv, cmd_id, *out); +} + +static Result _setCmdNoInOutU32(Service* srv, u32 *out, u32 cmd_id) { + return serviceDispatchOut(srv, cmd_id, *out); +} + +/* Forwarding shims. */ +Result setGetLanguageCodeFwd(Service *s, u64* out) { + return _setCmdNoInOut64(s, out, 0); +} + +Result setGetRegionCodeFwd(Service *s, SetRegion *out) { + s32 code=0; + Result rc = _setCmdNoInOutU32(s, (u32*)&code, 4); + if (R_SUCCEEDED(rc) && out) *out = code; + return rc; +} diff --git a/stratosphere/ams_mitm/source/set_mitm/set_shim.h b/stratosphere/ams_mitm/source/set_mitm/set_shim.h new file mode 100644 index 000000000..f2f3cad1a --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/set_shim.h @@ -0,0 +1,20 @@ +/** + * @file set_shim.h + * @brief Settings Services (fs) IPC wrapper for set.mitm. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forwarding shims. */ +Result setGetLanguageCodeFwd(Service *s, u64* out); +Result setGetRegionCodeFwd(Service *s, SetRegion *out); + +#ifdef __cplusplus +} +#endif \ No newline at end of file