mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Merge pull request #2240 from wwylele/auto-region
Config: auto-select region and language
This commit is contained in:
		
						commit
						f556d6ee90
					
				
					 11 changed files with 108 additions and 7 deletions
				
			
		|  | @ -89,7 +89,8 @@ void Config::ReadValues() { | ||||||
| 
 | 
 | ||||||
|     // System
 |     // System
 | ||||||
|     Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false); |     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
 |     // Miscellaneous
 | ||||||
|     Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info"); |     Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info"); | ||||||
|  |  | ||||||
|  | @ -101,7 +101,7 @@ use_virtual_sd = | ||||||
| is_new_3ds = | is_new_3ds = | ||||||
| 
 | 
 | ||||||
| # The system region that Citra will use during emulation | # 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 = | region_value = | ||||||
| 
 | 
 | ||||||
| [Miscellaneous] | [Miscellaneous] | ||||||
|  |  | ||||||
|  | @ -72,7 +72,8 @@ void Config::ReadValues() { | ||||||
| 
 | 
 | ||||||
|     qt_config->beginGroup("System"); |     qt_config->beginGroup("System"); | ||||||
|     Settings::values.is_new_3ds = qt_config->value("is_new_3ds", false).toBool(); |     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->endGroup(); | ||||||
| 
 | 
 | ||||||
|     qt_config->beginGroup("Miscellaneous"); |     qt_config->beginGroup("Miscellaneous"); | ||||||
|  |  | ||||||
|  | @ -23,13 +23,15 @@ void ConfigureGeneral::setConfiguration() { | ||||||
|     ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); |     ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); | ||||||
|     ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |     ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | ||||||
|     ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit); |     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() { | void ConfigureGeneral::applyConfiguration() { | ||||||
|     UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); |     UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); | ||||||
|     UISettings::values.confirm_before_closing = ui->toggle_check_exit->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::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); | ||||||
|     Settings::Apply(); |     Settings::Apply(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -82,6 +82,11 @@ | ||||||
|             </item> |             </item> | ||||||
|             <item> |             <item> | ||||||
|              <widget class="QComboBox" name="region_combobox"> |              <widget class="QComboBox" name="region_combobox"> | ||||||
|  |               <item> | ||||||
|  |                <property name="text"> | ||||||
|  |                 <string>Auto-select</string> | ||||||
|  |                </property> | ||||||
|  |               </item> | ||||||
|               <item> |               <item> | ||||||
|                <property name="text"> |                <property name="text"> | ||||||
|                 <string notr="true">JPN</string> |                 <string notr="true">JPN</string> | ||||||
|  |  | ||||||
|  | @ -129,6 +129,9 @@ | ||||||
|         </item> |         </item> | ||||||
|         <item row="2" column="1"> |         <item row="2" column="1"> | ||||||
|          <widget class="QComboBox" name="combo_language"> |          <widget class="QComboBox" name="combo_language"> | ||||||
|  |           <property name="toolTip"> | ||||||
|  |            <string>Note: this can be overridden when region setting is auto-select</string> | ||||||
|  |           </property> | ||||||
|           <item> |           <item> | ||||||
|            <property name="text"> |            <property name="text"> | ||||||
|             <string>Japanese (日本語)</string> |             <string>Japanese (日本語)</string> | ||||||
|  |  | ||||||
|  | @ -115,6 +115,8 @@ static const std::vector<u8> cfg_system_savedata_id = { | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00, |     0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static u32 preferred_region_code = 0; | ||||||
|  | 
 | ||||||
| void GetCountryCodeString(Service::Interface* self) { | void GetCountryCodeString(Service::Interface* self) { | ||||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|     u32 country_code_id = cmd_buff[1]; |     u32 country_code_id = cmd_buff[1]; | ||||||
|  | @ -160,11 +162,18 @@ void GetCountryCodeID(Service::Interface* self) { | ||||||
|     cmd_buff[2] = country_code_id; |     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) { | void SecureInfoGetRegion(Service::Interface* self) { | ||||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
| 
 | 
 | ||||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; |     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||||
|     cmd_buff[2] = Settings::values.region_value; |     cmd_buff[2] = GetRegionValue(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GenHashConsoleUnique(Service::Interface* self) { | void GenHashConsoleUnique(Service::Interface* self) { | ||||||
|  | @ -184,7 +193,7 @@ void GetRegionCanadaUSA(Service::Interface* self) { | ||||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; |     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||||
| 
 | 
 | ||||||
|     u8 canada_or_usa = 1; |     u8 canada_or_usa = 1; | ||||||
|     if (canada_or_usa == Settings::values.region_value) { |     if (canada_or_usa == GetRegionValue()) { | ||||||
|         cmd_buff[2] = 1; |         cmd_buff[2] = 1; | ||||||
|     } else { |     } else { | ||||||
|         cmd_buff[2] = 0; |         cmd_buff[2] = 0; | ||||||
|  | @ -314,10 +323,47 @@ static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 fl | ||||||
|     return MakeResult<void*>(pointer); |     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) { | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) { | ||||||
|     void* pointer; |     void* pointer; | ||||||
|     CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag)); |     CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag)); | ||||||
|     memcpy(output, pointer, size); |     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; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -535,10 +581,17 @@ void Init() { | ||||||
|     AddService(new CFG_U); |     AddService(new CFG_U); | ||||||
| 
 | 
 | ||||||
|     LoadConfigNANDSaveFile(); |     LoadConfigNANDSaveFile(); | ||||||
|  | 
 | ||||||
|  |     preferred_region_code = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Shutdown() {} | 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) { | void SetUsername(const std::u16string& name) { | ||||||
|     ASSERT(name.size() <= 10); |     ASSERT(name.size() <= 10); | ||||||
|     UsernameBlock block{}; |     UsernameBlock block{}; | ||||||
|  |  | ||||||
|  | @ -282,6 +282,13 @@ void Init(); | ||||||
| /// Shutdown the config service
 | /// Shutdown the config service
 | ||||||
| void Shutdown(); | 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.
 | // Utilities for frontend to set config data.
 | ||||||
| // Note: before calling these functions, LoadConfigNANDSaveFile should be called,
 | // Note: before calling these functions, LoadConfigNANDSaveFile should be called,
 | ||||||
| // and UpdateConfigNANDSavegame should be called after making changes to config data.
 | // and UpdateConfigNANDSavegame should be called after making changes to config data.
 | ||||||
|  |  | ||||||
|  | @ -11,8 +11,10 @@ | ||||||
| #include "core/file_sys/archive_romfs.h" | #include "core/file_sys/archive_romfs.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/resource_limit.h" | #include "core/hle/kernel/resource_limit.h" | ||||||
|  | #include "core/hle/service/cfg/cfg.h" | ||||||
| #include "core/hle/service/fs/archive.h" | #include "core/hle/service/fs/archive.h" | ||||||
| #include "core/loader/ncch.h" | #include "core/loader/ncch.h" | ||||||
|  | #include "core/loader/smdh.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | @ -309,6 +311,23 @@ ResultStatus AppLoader_NCCH::LoadExeFS() { | ||||||
|     return ResultStatus::Success; |     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() { | ResultStatus AppLoader_NCCH::Load() { | ||||||
|     if (is_loaded) |     if (is_loaded) | ||||||
|         return ResultStatus::ErrorAlreadyLoaded; |         return ResultStatus::ErrorAlreadyLoaded; | ||||||
|  | @ -325,6 +344,9 @@ ResultStatus AppLoader_NCCH::Load() { | ||||||
| 
 | 
 | ||||||
|     Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this), |     Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this), | ||||||
|                                      Service::FS::ArchiveIdCode::RomFS); |                                      Service::FS::ArchiveIdCode::RomFS); | ||||||
|  | 
 | ||||||
|  |     ParseRegionLockoutInfo(); | ||||||
|  | 
 | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -229,6 +229,9 @@ private: | ||||||
|      */ |      */ | ||||||
|     ResultStatus LoadExeFS(); |     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_exefs_loaded = false; | ||||||
|     bool is_compressed = false; |     bool is_compressed = false; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -110,5 +110,9 @@ struct Values { | ||||||
|     u16 gdbstub_port; |     u16 gdbstub_port; | ||||||
| } extern values; | } 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(); | void Apply(); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue