From 697bc1a9c72ab4d0dbd6ab62d0cf6ab10ce3bdf5 Mon Sep 17 00:00:00 2001
From: Weiyi Wang <wwylele@gmail.com>
Date: Mon, 17 Sep 2018 11:41:33 -0400
Subject: [PATCH] loader, cfg: choose region based on language if multiple
 regions available

---
 src/core/hle/service/cfg/cfg.cpp | 29 +++++++++++++++++++----------
 src/core/hle/service/cfg/cfg.h   | 10 +++++-----
 src/core/loader/ncch.cpp         |  6 ++++--
 3 files changed, 28 insertions(+), 17 deletions(-)

diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 49d17c190..321628664 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -3,6 +3,7 @@
 // Refer to the license.txt file included.
 
 #include <algorithm>
+#include <tuple>
 #include <cryptopp/osrng.h>
 #include <cryptopp/sha.h>
 #include "common/file_util.h"
@@ -571,7 +572,8 @@ Module::Module() {
 Module::~Module() = default;
 
 /// Checks if the language is available in the chosen region, and returns a proper one
-static SystemLanguage AdjustLanguageInfoBlock(u32 region, SystemLanguage language) {
+static std::tuple<u32 /*region*/, SystemLanguage> AdjustLanguageInfoBlock(
+    const std::vector<u32>& region_code, SystemLanguage language) {
     static const std::array<std::vector<SystemLanguage>, 7> region_languages{{
         // JPN
         {LANGUAGE_JP},
@@ -590,21 +592,28 @@ static SystemLanguage AdjustLanguageInfoBlock(u32 region, SystemLanguage languag
         // TWN
         {LANGUAGE_TW},
     }};
-    const auto& available = region_languages[region];
-    if (std::find(available.begin(), available.end(), language) == available.end()) {
-        return available[0];
+    // Check if any available region supports the languages
+    for (u32 region : region_code) {
+        const auto& available = region_languages[region];
+        if (std::find(available.begin(), available.end(), language) != available.end()) {
+            // found a proper region, so return this region - language pair
+            return {region, language};
+        }
     }
-    return language;
+    // The language is not available in any available region, so default to the first region and
+    // language
+    u32 default_region = region_code[0];
+    return {default_region, region_languages[default_region][0]};
 }
 
-void Module::SetPreferredRegionCode(u32 region_code) {
-    preferred_region_code = region_code;
+void Module::SetPreferredRegionCodes(const std::vector<u32>& region_codes) {
+    const SystemLanguage current_language = GetSystemLanguage();
+    auto [region, adjusted_language] = AdjustLanguageInfoBlock(region_codes, current_language);
+
+    preferred_region_code = region;
     LOG_INFO(Service_CFG, "Preferred region code set to {}", preferred_region_code);
 
     if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) {
-        const SystemLanguage current_language = GetSystemLanguage();
-        const SystemLanguage adjusted_language =
-            AdjustLanguageInfoBlock(region_code, current_language);
         if (current_language != adjusted_language) {
             LOG_WARNING(Service_CFG, "System language {} does not fit the region. Adjusted to {}",
                         static_cast<int>(current_language), static_cast<int>(adjusted_language));
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index 5f5c8e1f1..190430a7e 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -7,6 +7,7 @@
 #include <array>
 #include <memory>
 #include <string>
+#include <vector>
 #include "common/common_types.h"
 #include "core/hle/service/fs/archive.h"
 
@@ -298,12 +299,11 @@ public:
     u32 GetRegionValue();
 
     /**
-     * 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
+     * Set the region codes preferred by the game so that CFG will adjust to it when the region
+     * setting is auto.
+     * @param region_codes the preferred region codes to set
      */
-    void SetPreferredRegionCode(u32 region_code);
+    void SetPreferredRegionCodes(const std::vector<u32>& region_codes);
 
     // Utilities for frontend to set config data.
     // Note: 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 2e4d956cf..2b29a0f98 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -8,6 +8,7 @@
 #include <cstring>
 #include <locale>
 #include <memory>
+#include <vector>
 #include <fmt/format.h>
 #include "common/logging/log.h"
 #include "common/string_util.h"
@@ -136,13 +137,14 @@ void AppLoader_NCCH::ParseRegionLockoutInfo() {
         memcpy(&smdh, smdh_buffer.data(), sizeof(SMDH));
         u32 region_lockout = smdh.region_lockout;
         constexpr u32 REGION_COUNT = 7;
+        std::vector<u32> regions;
         for (u32 region = 0; region < REGION_COUNT; ++region) {
             if (region_lockout & 1) {
-                Service::CFG::GetCurrentModule()->SetPreferredRegionCode(region);
-                break;
+                regions.push_back(region);
             }
             region_lockout >>= 1;
         }
+        Service::CFG::GetCurrentModule()->SetPreferredRegionCodes(regions);
     }
 }