summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/citra/config.cpp3
-rw-r--r--src/citra/default_ini.h2
-rw-r--r--src/citra_qt/config.cpp3
-rw-r--r--src/citra_qt/configure_general.cpp6
-rw-r--r--src/citra_qt/configure_general.ui5
-rw-r--r--src/citra_qt/configure_system.ui3
-rw-r--r--src/core/hle/service/cfg/cfg.cpp57
-rw-r--r--src/core/hle/service/cfg/cfg.h7
-rw-r--r--src/core/loader/ncch.cpp22
-rw-r--r--src/core/loader/ncch.h3
-rw-r--r--src/core/settings.h4
11 files changed, 108 insertions, 7 deletions
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 29462c982..98f093258 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -89,7 +89,8 @@ void Config::ReadValues() {
// System
Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false);
- Settings::values.region_value = sdl2_config->GetInteger("System", "region_value", 1);
+ Settings::values.region_value =
+ sdl2_config->GetInteger("System", "region_value", Settings::REGION_VALUE_AUTO_SELECT);
// Miscellaneous
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info");
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 87cdec69b..bb4720d25 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -101,7 +101,7 @@ use_virtual_sd =
is_new_3ds =
# The system region that Citra will use during emulation
-# 0: Japan, 1: USA (default), 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
+# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
region_value =
[Miscellaneous]
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 06a4e9d25..c904c4b00 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -72,7 +72,8 @@ void Config::ReadValues() {
qt_config->beginGroup("System");
Settings::values.is_new_3ds = qt_config->value("is_new_3ds", false).toBool();
- Settings::values.region_value = qt_config->value("region_value", 1).toInt();
+ Settings::values.region_value =
+ qt_config->value("region_value", Settings::REGION_VALUE_AUTO_SELECT).toInt();
qt_config->endGroup();
qt_config->beginGroup("Miscellaneous");
diff --git a/src/citra_qt/configure_general.cpp b/src/citra_qt/configure_general.cpp
index 03cd8835b..ac90a6df4 100644
--- a/src/citra_qt/configure_general.cpp
+++ b/src/citra_qt/configure_general.cpp
@@ -23,13 +23,15 @@ void ConfigureGeneral::setConfiguration() {
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit);
- ui->region_combobox->setCurrentIndex(Settings::values.region_value);
+
+ // The first item is "auto-select" with actual value -1, so plus one here will do the trick
+ ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1);
}
void ConfigureGeneral::applyConfiguration() {
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
- Settings::values.region_value = ui->region_combobox->currentIndex();
+ Settings::values.region_value = ui->region_combobox->currentIndex() - 1;
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
Settings::Apply();
}
diff --git a/src/citra_qt/configure_general.ui b/src/citra_qt/configure_general.ui
index 81688113f..342954e41 100644
--- a/src/citra_qt/configure_general.ui
+++ b/src/citra_qt/configure_general.ui
@@ -84,6 +84,11 @@
<widget class="QComboBox" name="region_combobox">
<item>
<property name="text">
+ <string>Auto-select</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
<string notr="true">JPN</string>
</property>
</item>
diff --git a/src/citra_qt/configure_system.ui b/src/citra_qt/configure_system.ui
index 6a906b61b..cc54fa37f 100644
--- a/src/citra_qt/configure_system.ui
+++ b/src/citra_qt/configure_system.ui
@@ -129,6 +129,9 @@
</item>
<item row="2" column="1">
<widget class="QComboBox" name="combo_language">
+ <property name="toolTip">
+ <string>Note: this can be overridden when region setting is auto-select</string>
+ </property>
<item>
<property name="text">
<string>Japanese (日本語)</string>
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 65655f45d..0bf59eb76 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -115,6 +115,8 @@ static const std::vector<u8> cfg_system_savedata_id = {
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00,
};
+static u32 preferred_region_code = 0;
+
void GetCountryCodeString(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 country_code_id = cmd_buff[1];
@@ -160,11 +162,18 @@ void GetCountryCodeID(Service::Interface* self) {
cmd_buff[2] = country_code_id;
}
+static u32 GetRegionValue() {
+ if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT)
+ return preferred_region_code;
+
+ return Settings::values.region_value;
+}
+
void SecureInfoGetRegion(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw;
- cmd_buff[2] = Settings::values.region_value;
+ cmd_buff[2] = GetRegionValue();
}
void GenHashConsoleUnique(Service::Interface* self) {
@@ -184,7 +193,7 @@ void GetRegionCanadaUSA(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw;
u8 canada_or_usa = 1;
- if (canada_or_usa == Settings::values.region_value) {
+ if (canada_or_usa == GetRegionValue()) {
cmd_buff[2] = 1;
} else {
cmd_buff[2] = 0;
@@ -314,10 +323,47 @@ static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 fl
return MakeResult<void*>(pointer);
}
+/// Checks if the language is available in the chosen region, and returns a proper one
+static u8 AdjustLanguageInfoBlock(u32 region, u8 language) {
+ static const std::array<std::vector<u8>, 7> region_languages{{
+ // JPN
+ {LANGUAGE_JP},
+ // USA
+ {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_ES, LANGUAGE_PT},
+ // EUR
+ {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT,
+ LANGUAGE_RU},
+ // AUS
+ {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT,
+ LANGUAGE_RU},
+ // CHN
+ {LANGUAGE_ZH},
+ // KOR
+ {LANGUAGE_KO},
+ // TWN
+ {LANGUAGE_TW},
+ }};
+ const auto& available = region_languages[region];
+ if (std::find(available.begin(), available.end(), language) == available.end()) {
+ return available[0];
+ }
+ return language;
+}
+
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) {
void* pointer;
CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
memcpy(output, pointer, size);
+
+ // override the language setting if the region setting is auto
+ if (block_id == LanguageBlockID &&
+ Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) {
+ u8 language;
+ memcpy(&language, output, sizeof(u8));
+ language = AdjustLanguageInfoBlock(preferred_region_code, language);
+ memcpy(output, &language, sizeof(u8));
+ }
+
return RESULT_SUCCESS;
}
@@ -535,10 +581,17 @@ void Init() {
AddService(new CFG_U);
LoadConfigNANDSaveFile();
+
+ preferred_region_code = 0;
}
void Shutdown() {}
+void SetPreferredRegionCode(u32 region_code) {
+ preferred_region_code = region_code;
+ LOG_INFO(Service_CFG, "Preferred region code set to %u", preferred_region_code);
+}
+
void SetUsername(const std::u16string& name) {
ASSERT(name.size() <= 10);
UsernameBlock block{};
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index fb47c2aa5..618c9647e 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -282,6 +282,13 @@ void Init();
/// Shutdown the config service
void Shutdown();
+/**
+ * Set the region code preferred by the game so that CFG will adjust to it when the region setting
+ * is auto.
+ * @param region_code the preferred region code to set
+ */
+void SetPreferredRegionCode(u32 region_code);
+
// Utilities for frontend to set config data.
// Note: before calling these functions, LoadConfigNANDSaveFile should be called,
// and UpdateConfigNANDSavegame should be called after making changes to config data.
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 6f2164428..a204dc336 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -11,8 +11,10 @@
#include "core/file_sys/archive_romfs.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/fs/archive.h"
#include "core/loader/ncch.h"
+#include "core/loader/smdh.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -309,6 +311,23 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
return ResultStatus::Success;
}
+void AppLoader_NCCH::ParseRegionLockoutInfo() {
+ std::vector<u8> smdh_buffer;
+ if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) {
+ SMDH smdh;
+ memcpy(&smdh, smdh_buffer.data(), sizeof(SMDH));
+ u32 region_lockout = smdh.region_lockout;
+ constexpr u32 REGION_COUNT = 7;
+ for (u32 region = 0; region < REGION_COUNT; ++region) {
+ if (region_lockout & 1) {
+ Service::CFG::SetPreferredRegionCode(region);
+ break;
+ }
+ region_lockout >>= 1;
+ }
+ }
+}
+
ResultStatus AppLoader_NCCH::Load() {
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
@@ -325,6 +344,9 @@ ResultStatus AppLoader_NCCH::Load() {
Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this),
Service::FS::ArchiveIdCode::RomFS);
+
+ ParseRegionLockoutInfo();
+
return ResultStatus::Success;
}
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index 6afc171a5..fe08f5b45 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -229,6 +229,9 @@ private:
*/
ResultStatus LoadExeFS();
+ /// Reads the region lockout info in the SMDH and send it to CFG service
+ void ParseRegionLockoutInfo();
+
bool is_exefs_loaded = false;
bool is_compressed = false;
diff --git a/src/core/settings.h b/src/core/settings.h
index db4c8fada..4e7a4b1be 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -110,5 +110,9 @@ struct Values {
u16 gdbstub_port;
} extern values;
+// a special value for Values::region_value indicating that citra will automatically select a region
+// value to fit the region lockout info of the game
+static constexpr int REGION_VALUE_AUTO_SELECT = -1;
+
void Apply();
}