diff --git a/stratosphere/ams_mitm/source/amsmitm_main.cpp b/stratosphere/ams_mitm/source/amsmitm_main.cpp
index 911c5e561..1ce809199 100644
--- a/stratosphere/ams_mitm/source/amsmitm_main.cpp
+++ b/stratosphere/ams_mitm/source/amsmitm_main.cpp
@@ -78,6 +78,16 @@ void __appInit(void) {
if (R_FAILED(rc)) {
std::abort();
}
+
+ rc = pmdmntInitialize();
+ if (R_FAILED(rc)) {
+ std::abort();
+ }
+
+ rc = pminfoInitialize();
+ if (R_FAILED(rc)) {
+ std::abort();
+ }
});
CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION);
diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp
new file mode 100644
index 000000000..535d13ffe
--- /dev/null
+++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2018-2019 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
+#include
+#include
+#include "set_mitm_service.hpp"
+#include "set_shim.h"
+
+void SetMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
+ /* No commands need postprocessing. */
+}
+
+bool SetMitmService::IsValidLanguageCode(u64 lang_code) {
+ static constexpr u64 s_valid_language_codes[] = {
+ LanguageCode_Japanese,
+ LanguageCode_AmericanEnglish,
+ LanguageCode_French,
+ LanguageCode_German,
+ LanguageCode_Italian,
+ LanguageCode_Spanish,
+ LanguageCode_Chinese,
+ LanguageCode_Korean,
+ LanguageCode_Dutch,
+ LanguageCode_Portuguese,
+ LanguageCode_Russian,
+ LanguageCode_Taiwanese,
+ LanguageCode_BritishEnglish,
+ LanguageCode_CanadianFrench,
+ LanguageCode_LatinAmericanSpanish,
+ LanguageCode_SimplifiedChinese,
+ LanguageCode_TraditionalChinese,
+ };
+ size_t num_language_codes = sizeof(s_valid_language_codes) / sizeof(s_valid_language_codes[0]);
+ if (GetRuntimeFirmwareVersion() < FirmwareVersion_400) {
+ /* 4.0.0 added simplified and traditional chinese. */
+ num_language_codes -= 2;
+ }
+
+ for (size_t i = 0; i < num_language_codes; i++) {
+ if (lang_code == s_valid_language_codes[i]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SetMitmService::IsValidRegionCode(u32 region_code) {
+ return region_code < RegionCode_Max;
+}
+
+void SetMitmService::EnsureLocale() {
+ std::scoped_lock lk(this->lock);
+
+ if (!this->got_locale) {
+ std::memset(&this->locale, 0xCC, sizeof(this->locale));
+ if (this->title_id == TitleId_Ns) {
+ u64 app_pid = 0;
+ u64 app_tid = 0;
+ Result rc;
+ if (R_FAILED((rc = pmdmntGetApplicationPid(&app_pid)))) {
+ return;
+ }
+ if (R_FAILED((rc = pminfoGetTitleId(&app_tid, app_pid)))) {
+ return;
+ }
+ this->locale = Utils::GetTitleOverrideLocale(app_tid);
+ } else {
+ this->locale = Utils::GetTitleOverrideLocale(this->title_id);
+ this->got_locale = true;
+ }
+ }
+}
+
+Result SetMitmService::GetLanguageCode(Out out_lang_code) {
+ this->EnsureLocale();
+
+ if (!IsValidLanguageCode(this->locale.language_code)) {
+ return ResultAtmosphereMitmShouldForwardToSession;
+ }
+
+ out_lang_code.SetValue(this->locale.language_code);
+ return ResultSuccess;
+}
+
+Result SetMitmService::GetRegionCode(Out out_region_code) {
+ this->EnsureLocale();
+
+ if (!IsValidRegionCode(this->locale.region_code)) {
+ return ResultAtmosphereMitmShouldForwardToSession;
+ }
+
+ out_region_code.SetValue(this->locale.region_code);
+ return ResultSuccess;
+}
+
+Result SetMitmService::GetAvailableLanguageCodes(OutPointerWithClientSize out_language_codes, Out out_count) {
+ return setGetAvailableLanguageCodesFwd(this->forward_service.get(), out_count.GetPointer(), out_language_codes.pointer, out_language_codes.num_elements);
+}
diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp
new file mode 100644
index 000000000..b7e0341ed
--- /dev/null
+++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018-2019 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 .
+ */
+
+#pragma once
+#include
+#include
+
+#include "../utils.hpp"
+
+enum SetCmd : u32 {
+ SetCmd_GetLanguageCode = 0,
+ SetCmd_GetRegionCode = 4,
+
+ /* Commands for which set:sys *must* act as a passthrough. */
+ /* TODO: Solve the relevant IPC detection problem. */
+ SetCmd_GetAvailableLanguageCodes = 1,
+};
+
+class SetMitmService : public IMitmServiceObject {
+ private:
+ HosMutex lock;
+ OverrideLocale locale;
+ bool got_locale;
+ public:
+ SetMitmService(std::shared_ptr s, u64 pid) : IMitmServiceObject(s, pid) {
+ this->got_locale = false;
+ }
+
+ static bool ShouldMitm(u64 pid, u64 tid) {
+ /* Mitm all applications. */
+ return tid == TitleId_Ns || TitleIdIsApplication(tid);
+ }
+
+ static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
+
+ protected:
+ static bool IsValidLanguageCode(u64 lang_code);
+ static bool IsValidRegionCode(u32 region_code);
+
+ void EnsureLocale();
+ protected:
+ /* Overridden commands. */
+ Result GetLanguageCode(Out out_lang_code);
+ Result GetRegionCode(Out out_region_code);
+
+ /* Forced passthrough commands. */
+ Result GetAvailableLanguageCodes(OutPointerWithClientSize out_language_codes, Out out_count);
+ public:
+ DEFINE_SERVICE_DISPATCH_TABLE {
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+
+ MakeServiceCommandMeta(),
+ };
+};
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..61943496a
--- /dev/null
+++ b/stratosphere/ams_mitm/source/set_mitm/set_shim.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018-2019 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
+#include
+#include "setsys_shim.h"
+
+/* Command forwarders. */
+Result setGetAvailableLanguageCodesFwd(Service* s, s32 *total_entries, u64 *language_codes, size_t max_entries) {
+ IpcCommand c;
+ ipcInitialize(&c);
+ ipcAddRecvStatic(&c, language_codes, max_entries * sizeof(*language_codes), 0);
+
+ struct {
+ u64 magic;
+ u64 cmd_id;
+ } *raw;
+
+ raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
+
+ raw->magic = SFCI_MAGIC;
+ raw->cmd_id = 1;
+
+ Result rc = serviceIpcDispatch(s);
+
+ if (R_SUCCEEDED(rc)) {
+ IpcParsedCommand r;
+
+ struct {
+ u64 magic;
+ u64 result;
+ s32 total_entries;
+ } *resp;
+
+ serviceIpcParse(s, &r, sizeof(*resp));
+ resp = r.Raw;
+
+ rc = resp->result;
+
+ if (R_SUCCEEDED(rc)) {
+ *total_entries = resp->total_entries;
+ }
+ }
+
+ 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..bee7dcf42
--- /dev/null
+++ b/stratosphere/ams_mitm/source/set_mitm/set_shim.h
@@ -0,0 +1,19 @@
+/**
+ * @file set_shim.h
+ * @brief Settings Services (set) IPC wrapper. To be merged into libnx, eventually.
+ * @author SciresM
+ * @copyright libnx Authors
+ */
+#pragma once
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Command forwarders. */
+Result setGetAvailableLanguageCodesFwd(Service* s, s32 *total_entries, u64 *language_codes, size_t max_entries);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/stratosphere/ams_mitm/source/set_mitm/setmitm_main.cpp b/stratosphere/ams_mitm/source/set_mitm/setmitm_main.cpp
index 4960097bb..f3e80a446 100644
--- a/stratosphere/ams_mitm/source/set_mitm/setmitm_main.cpp
+++ b/stratosphere/ams_mitm/source/set_mitm/setmitm_main.cpp
@@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
+
#include
#include
#include
@@ -28,6 +28,8 @@
#include "setsys_settings_items.hpp"
#include "setsys_firmware_version.hpp"
+#include "set_mitm_service.hpp"
+
#include "../utils.hpp"
struct SetSysManagerOptions {
@@ -44,17 +46,20 @@ void SetMitmMain(void *arg) {
/* Initialize version manager. */
VersionManager::Initialize();
-
+
/* Create server manager */
- auto server_manager = new SetMitmManager(3);
-
+ auto server_manager = new SetMitmManager(4);
+
/* Create set:sys mitm. */
AddMitmServerToManager(server_manager, "set:sys", 60);
-
+
+ /* Create set mitm. */
+ AddMitmServerToManager(server_manager, "set", 60);
+
/* Loop forever, servicing our services. */
server_manager->Process();
-
+
delete server_manager;
-
+
}
diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_shim.c b/stratosphere/ams_mitm/source/set_mitm/setsys_shim.c
index 1ef7b887c..d532adb9d 100644
--- a/stratosphere/ams_mitm/source/set_mitm/setsys_shim.c
+++ b/stratosphere/ams_mitm/source/set_mitm/setsys_shim.c
@@ -55,7 +55,7 @@ Result setsysGetEdidFwd(Service* s, SetSysEdid* out) {
Result setsysGetSettingsItemValueFwd(Service *s, const char *name, const char *item_key, void *value_out, size_t value_out_size, u64 *size_out) {
char send_name[SET_MAX_NAME_SIZE];
char send_item_key[SET_MAX_NAME_SIZE];
-
+
memset(send_name, 0, SET_MAX_NAME_SIZE);
memset(send_item_key, 0, SET_MAX_NAME_SIZE);
strncpy(send_name, name, SET_MAX_NAME_SIZE-1);
diff --git a/stratosphere/ams_mitm/source/utils.cpp b/stratosphere/ams_mitm/source/utils.cpp
index b1e5ca315..1304c31a7 100644
--- a/stratosphere/ams_mitm/source/utils.cpp
+++ b/stratosphere/ams_mitm/source/utils.cpp
@@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
+
#include
#include
#include
@@ -85,16 +85,16 @@ void Utils::InitializeThreadFunc(void *args) {
if (R_FAILED(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])))) {
std::abort();
} else {
- svcCloseHandle(tmp_hnd);
+ svcCloseHandle(tmp_hnd);
}
}
});
-
+
/* Mount SD. */
while (R_FAILED(fsMountSdcard(&g_sd_filesystem))) {
svcSleepThread(1000000ULL);
}
-
+
/* Back up CAL0, if it's not backed up already. */
fsFsCreateDirectory(&g_sd_filesystem, "/atmosphere/automatic_backups");
{
@@ -103,18 +103,18 @@ void Utils::InitializeThreadFunc(void *args) {
std::abort();
}
fsStorageClose(&cal0_storage);
-
+
char serial_number[0x40] = {0};
memcpy(serial_number, g_cal0_storage_backup + 0x250, 0x18);
-
-
+
+
char prodinfo_backup_path[FS_MAX_PATH] = {0};
if (strlen(serial_number) > 0) {
snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/%s_PRODINFO.bin", serial_number);
} else {
snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/PRODINFO.bin");
}
-
+
fsFsCreateFile(&g_sd_filesystem, prodinfo_backup_path, ProdinfoSize, 0);
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, prodinfo_backup_path, FS_OPEN_READ | FS_OPEN_WRITE, &g_cal0_file))) {
bool has_auto_backup = false;
@@ -132,19 +132,19 @@ void Utils::InitializeThreadFunc(void *args) {
}
has_auto_backup = is_cal0_valid;
}
-
+
if (!has_auto_backup) {
fsFileSetSize(&g_cal0_file, ProdinfoSize);
fsFileWrite(&g_cal0_file, 0, g_cal0_storage_backup, ProdinfoSize);
fsFileFlush(&g_cal0_file);
}
-
+
/* NOTE: g_cal0_file is intentionally not closed here. This prevents any other process from opening it. */
memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup));
memset(g_cal0_backup, 0, sizeof(g_cal0_backup));
}
}
-
+
/* Check for MitM flags. */
FsDir titles_dir;
if (R_SUCCEEDED(fsFsOpenDirectory(&g_sd_filesystem, "/atmosphere/titles", FS_DIROPEN_DIRECTORY, &titles_dir))) {
@@ -172,7 +172,7 @@ void Utils::InitializeThreadFunc(void *args) {
fsFileClose(&f);
}
}
-
+
memset(title_path, 0, sizeof(title_path));
strcpy(title_path, "/atmosphere/titles/");
strcat(title_path, dir_entry.name);
@@ -195,25 +195,25 @@ void Utils::InitializeThreadFunc(void *args) {
}
fsDirClose(&titles_dir);
}
-
+
Utils::RefreshConfiguration();
-
+
/* Initialize set:sys. */
DoWithSmSession([&]() {
if (R_FAILED(setsysInitialize())) {
std::abort();
}
});
-
+
/* Signal SD is initialized. */
g_has_initialized = true;
-
+
/* Load custom settings configuration. */
SettingsItemManager::LoadConfiguration();
-
+
/* Signal to waiters that we are ready. */
g_sd_signal.Signal();
-
+
/* Initialize HID. */
while (!g_has_hid_session) {
DoWithSmSession([&]() {
@@ -243,7 +243,7 @@ Result Utils::OpenSdFile(const char *fn, int flags, FsFile *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
-
+
return fsFsOpenFile(&g_sd_filesystem, fn, flags, out);
}
@@ -251,7 +251,7 @@ Result Utils::OpenSdFileForAtmosphere(u64 title_id, const char *fn, int flags, F
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
-
+
char path[FS_MAX_PATH];
if (*fn == '/') {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx%s", title_id, fn);
@@ -265,7 +265,7 @@ Result Utils::OpenRomFSSdFile(u64 title_id, const char *fn, int flags, FsFile *o
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
-
+
return OpenRomFSFile(&g_sd_filesystem, title_id, fn, flags, out);
}
@@ -273,7 +273,7 @@ Result Utils::OpenSdDir(const char *path, FsDir *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
-
+
return fsFsOpenDirectory(&g_sd_filesystem, path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out);
}
@@ -281,7 +281,7 @@ Result Utils::OpenSdDirForAtmosphere(u64 title_id, const char *path, FsDir *out)
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
-
+
char safe_path[FS_MAX_PATH];
if (*path == '/') {
snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx%s", title_id, path);
@@ -295,7 +295,7 @@ Result Utils::OpenRomFSSdDir(u64 title_id, const char *path, FsDir *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
-
+
return OpenRomFSDir(&g_sd_filesystem, title_id, path, out);
}
@@ -327,7 +327,7 @@ bool Utils::HasSdRomfsContent(u64 title_id) {
fsFileClose(&data_file);
return true;
}
-
+
/* Check for romfs folder with non-zero content. */
FsDir dir;
if (R_FAILED(Utils::OpenRomFSSdDir(title_id, "", &dir))) {
@@ -336,7 +336,7 @@ bool Utils::HasSdRomfsContent(u64 title_id) {
ON_SCOPE_EXIT {
fsDirClose(&dir);
};
-
+
FsDirectoryEntry dir_entry;
u64 read_entries;
return R_SUCCEEDED(fsDirRead(&dir, 0, &read_entries, 1, &dir_entry)) && read_entries == 1;
@@ -346,40 +346,40 @@ Result Utils::SaveSdFileForAtmosphere(u64 title_id, const char *fn, void *data,
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
-
+
Result rc = ResultSuccess;
-
+
char path[FS_MAX_PATH];
if (*fn == '/') {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx%s", title_id, fn);
} else {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/%s", title_id, fn);
}
-
+
/* Unconditionally create. */
FsFile f;
fsFsCreateFile(&g_sd_filesystem, path, size, 0);
-
+
/* Try to open. */
rc = fsFsOpenFile(&g_sd_filesystem, path, FS_OPEN_READ | FS_OPEN_WRITE, &f);
if (R_FAILED(rc)) {
return rc;
}
-
+
/* Always close, if we opened. */
ON_SCOPE_EXIT {
fsFileClose(&f);
};
-
+
/* Try to make it big enough. */
rc = fsFileSetSize(&f, size);
if (R_FAILED(rc)) {
return rc;
}
-
+
/* Try to write the data. */
rc = fsFileWrite(&f, 0, data, size);
-
+
return rc;
}
@@ -395,14 +395,14 @@ bool Utils::HasTitleFlag(u64 tid, const char *flag) {
if (IsSdInitialized()) {
FsFile f;
char flag_path[FS_MAX_PATH];
-
+
memset(flag_path, 0, sizeof(flag_path));
snprintf(flag_path, sizeof(flag_path) - 1, "flags/%s.flag", flag);
if (R_SUCCEEDED(OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f))) {
fsFileClose(&f);
return true;
}
-
+
/* TODO: Deprecate. */
snprintf(flag_path, sizeof(flag_path) - 1, "%s.flag", flag);
if (R_SUCCEEDED(OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f))) {
@@ -440,7 +440,7 @@ bool Utils::HasSdMitMFlag(u64 tid) {
if (IsHblTid(tid)) {
return true;
}
-
+
if (IsSdInitialized()) {
return std::find(g_mitm_flagged_tids.begin(), g_mitm_flagged_tids.end(), tid) != g_mitm_flagged_tids.end();
}
@@ -458,14 +458,14 @@ Result Utils::GetKeysHeld(u64 *keys) {
if (!Utils::IsHidAvailable()) {
return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID);
}
-
+
hidScanInput();
*keys = 0;
for (int controller = 0; controller < 10; controller++) {
*keys |= hidKeysHeld((HidControllerID) controller);
}
-
+
return ResultSuccess;
}
@@ -481,21 +481,21 @@ bool Utils::HasOverrideButton(u64 tid) {
/* Disable button override disable for non-applications. */
return true;
}
-
+
/* Unconditionally refresh loader.ini contents. */
RefreshConfiguration();
-
+
if (IsHblTid(tid) && HasOverrideKey(&g_hbl_override_config.override_key)) {
return true;
}
-
+
OverrideKey title_cfg = GetTitleOverrideKey(tid);
return HasOverrideKey(&title_cfg);
}
static OverrideKey ParseOverrideKey(const char *value) {
OverrideKey cfg;
-
+
/* Parse on by default. */
if (value[0] == '!') {
cfg.override_by_default = true;
@@ -503,7 +503,7 @@ static OverrideKey ParseOverrideKey(const char *value) {
} else {
cfg.override_by_default = false;
}
-
+
/* Parse key combination. */
if (strcasecmp(value, "A") == 0) {
cfg.key_combination = KEY_A;
@@ -544,7 +544,7 @@ static OverrideKey ParseOverrideKey(const char *value) {
} else {
cfg.key_combination = 0;
}
-
+
return cfg;
}
@@ -586,7 +586,7 @@ static int FsMitmIniHandler(void *user, const char *section, const char *name, c
static int FsMitmTitleSpecificIniHandler(void *user, const char *section, const char *name, const char *value) {
/* We'll output an override key when relevant. */
OverrideKey *user_cfg = reinterpret_cast(user);
-
+
if (strcasecmp(section, "override_config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
*user_cfg = ParseOverrideKey(value);
@@ -600,49 +600,107 @@ static int FsMitmTitleSpecificIniHandler(void *user, const char *section, const
OverrideKey Utils::GetTitleOverrideKey(u64 tid) {
OverrideKey cfg = g_default_override_key;
char path[FS_MAX_PATH+1] = {0};
- snprintf(path, FS_MAX_PATH, "/atmosphere/titles/%016lx/config.ini", tid);
+ snprintf(path, FS_MAX_PATH, "/atmosphere/titles/%016lx/config.ini", tid);
FsFile cfg_file;
-
+
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, path, FS_OPEN_READ, &cfg_file))) {
ON_SCOPE_EXIT { fsFileClose(&cfg_file); };
-
+
size_t config_file_size = 0x20000;
fsFileGetSize(&cfg_file, &config_file_size);
-
+
char *config_buf = reinterpret_cast(calloc(1, config_file_size + 1));
if (config_buf != NULL) {
ON_SCOPE_EXIT { free(config_buf); };
-
+
/* Read title ini contents. */
fsFileRead(&cfg_file, 0, config_buf, config_file_size, &config_file_size);
-
+
/* Parse title ini. */
ini_parse_string(config_buf, FsMitmTitleSpecificIniHandler, &cfg);
}
}
-
+
return cfg;
}
+static int FsMitmTitleSpecificLocaleIniHandler(void *user, const char *section, const char *name, const char *value) {
+ /* We'll output an override locale when relevant. */
+ OverrideLocale *user_locale = reinterpret_cast(user);
+
+ if (strcasecmp(section, "override_config") == 0) {
+ if (strcasecmp(name, "override_language") == 0) {
+ user_locale->language_code = EncodeLanguageCode(value);
+ } else if (strcasecmp(name, "override_region") == 0) {
+ if (strcasecmp(value, "jpn") == 0) {
+ user_locale->region_code = RegionCode_Japan;
+ } else if (strcasecmp(value, "usa") == 0) {
+ user_locale->region_code = RegionCode_America;
+ } else if (strcasecmp(value, "eur") == 0) {
+ user_locale->region_code = RegionCode_Europe;
+ } else if (strcasecmp(value, "aus") == 0) {
+ user_locale->region_code = RegionCode_Australia;
+ } else if (strcasecmp(value, "chn") == 0) {
+ user_locale->region_code = RegionCode_China;
+ } else if (strcasecmp(value, "kor") == 0) {
+ user_locale->region_code = RegionCode_Korea;
+ } else if (strcasecmp(value, "twn") == 0) {
+ user_locale->region_code = RegionCode_Taiwan;
+ }
+ }
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+OverrideLocale Utils::GetTitleOverrideLocale(u64 tid) {
+ OverrideLocale locale;
+ std::memset(&locale, 0xCC, sizeof(locale));
+ char path[FS_MAX_PATH+1] = {0};
+ snprintf(path, FS_MAX_PATH, "/atmosphere/titles/%016lx/config.ini", tid);
+ FsFile cfg_file;
+
+ if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, path, FS_OPEN_READ, &cfg_file))) {
+ ON_SCOPE_EXIT { fsFileClose(&cfg_file); };
+
+ size_t config_file_size = 0x20000;
+ fsFileGetSize(&cfg_file, &config_file_size);
+
+ char *config_buf = reinterpret_cast(calloc(1, config_file_size + 1));
+ if (config_buf != NULL) {
+ ON_SCOPE_EXIT { free(config_buf); };
+
+ /* Read title ini contents. */
+ fsFileRead(&cfg_file, 0, config_buf, config_file_size, &config_file_size);
+
+ /* Parse title ini. */
+ ini_parse_string(config_buf, FsMitmTitleSpecificLocaleIniHandler, &locale);
+ }
+ }
+
+ return locale;
+}
+
void Utils::RefreshConfiguration() {
FsFile config_file;
if (R_FAILED(fsFsOpenFile(&g_sd_filesystem, "/atmosphere/loader.ini", FS_OPEN_READ, &config_file))) {
return;
}
-
+
u64 size;
if (R_FAILED(fsFileGetSize(&config_file, &size))) {
return;
}
-
+
size = std::min(size, (decltype(size))0x7FF);
-
+
/* Read in string. */
std::fill(g_config_ini_data, g_config_ini_data + 0x800, 0);
size_t r_s;
fsFileRead(&config_file, 0, g_config_ini_data, size, &r_s);
fsFileClose(&config_file);
-
+
ini_parse_string(g_config_ini_data, FsMitmIniHandler, NULL);
}
diff --git a/stratosphere/ams_mitm/source/utils.hpp b/stratosphere/ams_mitm/source/utils.hpp
index 04e1faadf..a3e9ce4ab 100644
--- a/stratosphere/ams_mitm/source/utils.hpp
+++ b/stratosphere/ams_mitm/source/utils.hpp
@@ -42,6 +42,56 @@ struct OverrideKey {
bool override_by_default;
};
+struct OverrideLocale {
+ u64 language_code;
+ u32 region_code;
+};
+
+enum RegionCode : u32 {
+ RegionCode_Japan = 0,
+ RegionCode_America = 1,
+ RegionCode_Europe = 2,
+ RegionCode_Australia = 3,
+ RegionCode_China = 4,
+ RegionCode_Korea = 5,
+ RegionCode_Taiwan = 6,
+
+ RegionCode_Max,
+};
+
+static constexpr inline u64 EncodeLanguageCode(const char *code) {
+ u64 lang_code = 0;
+ for (size_t i = 0; i < sizeof(lang_code); i++) {
+ if (code[i] == '\x00') {
+ break;
+ }
+ lang_code |= static_cast(code[i]) << (8ul * i);
+ }
+ return lang_code;
+}
+
+enum LanguageCode : u64 {
+ LanguageCode_Japanese = EncodeLanguageCode("ja"),
+ LanguageCode_AmericanEnglish = EncodeLanguageCode("en-US"),
+ LanguageCode_French = EncodeLanguageCode("fr"),
+ LanguageCode_German = EncodeLanguageCode("de"),
+ LanguageCode_Italian = EncodeLanguageCode("it"),
+ LanguageCode_Spanish = EncodeLanguageCode("es"),
+ LanguageCode_Chinese = EncodeLanguageCode("zh-CN"),
+ LanguageCode_Korean = EncodeLanguageCode("ko"),
+ LanguageCode_Dutch = EncodeLanguageCode("nl"),
+ LanguageCode_Portuguese = EncodeLanguageCode("pt"),
+ LanguageCode_Russian = EncodeLanguageCode("ru"),
+ LanguageCode_Taiwanese = EncodeLanguageCode("zh-TW"),
+ LanguageCode_BritishEnglish = EncodeLanguageCode("en-GB"),
+ LanguageCode_CanadianFrench = EncodeLanguageCode("fr-CA"),
+ LanguageCode_LatinAmericanSpanish = EncodeLanguageCode("es-419"),
+ /* 4.0.0+ */
+ LanguageCode_SimplifiedChinese = EncodeLanguageCode("zh-Hans"),
+ LanguageCode_TraditionalChinese = EncodeLanguageCode("zh-Hant"),
+};
+
+
class Utils {
public:
static bool IsSdInitialized();
@@ -82,12 +132,14 @@ class Utils {
static OverrideKey GetTitleOverrideKey(u64 tid);
static bool HasOverrideButton(u64 tid);
+ static OverrideLocale GetTitleOverrideLocale(u64 tid);
+
/* Settings! */
static Result GetSettingsItemValueSize(const char *name, const char *key, u64 *out_size);
static Result GetSettingsItemValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size);
static Result GetSettingsItemBooleanValue(const char *name, const char *key, bool *out);
-
+
/* Error occurred. */
static void RebootToFatalError(AtmosphereFatalErrorContext *ctx);
private:
diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp
index cab914dd8..484a1347e 100644
--- a/stratosphere/pm/source/pm_main.cpp
+++ b/stratosphere/pm/source/pm_main.cpp
@@ -175,9 +175,9 @@ int main(int argc, char **argv)
/* TODO: Create services. */
s_server_manager.AddWaitable(new ServiceServer("pm:shell", 3));
- s_server_manager.AddWaitable(new ServiceServer("pm:dmnt", 2));
+ s_server_manager.AddWaitable(new ServiceServer("pm:dmnt", 3));
s_server_manager.AddWaitable(new ServiceServer("pm:bm", 6));
- s_server_manager.AddWaitable(new ServiceServer("pm:info", 2));
+ s_server_manager.AddWaitable(new ServiceServer("pm:info", 3));
/* Loop forever, servicing our services. */
s_server_manager.Process();