diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index b0487302b3..565decb390 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -64,7 +64,8 @@
- @string/language_spanish
- @string/language_taiwanese
- @string/language_traditional_chinese
- - @string/language_serbian
+ - @string/language_polish
+ - @string/language_thai
@@ -87,6 +88,7 @@
- 11
- 16
- 18
+ - 19
@@ -407,6 +409,7 @@
- @string/app_language_persian
- @string/app_language_hebrew
- @string/app_language_serbian
+ - @string/app_language_thai
- 0
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 8a1193303e..a34d75d7cb 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -1011,7 +1011,8 @@
简体中文
正體中文
Português do Brasil
- српски
+ Polska
+ แบบไทย
B
@@ -1216,6 +1217,7 @@
فارسی
עברית
Српски
+ แบบไทย
Theme Color
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index d4935d9b6d..638be4127f 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -120,7 +120,7 @@ ENUM(AudioMode, Mono, Stereo, Surround);
ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch,
Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin,
- ChineseSimplified, ChineseTraditional, PortugueseBrazilian, Serbian);
+ ChineseSimplified, ChineseTraditional, PortugueseBrazilian, Polish, Thai);
ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan);
ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt,
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 7ca7703afa..3676c1bbc8 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -12,6 +12,7 @@
#include
#include "common/settings.h"
+#include "common/settings_enums.h"
#include "common/string_util.h"
#include "common/swap.h"
#include "core/file_sys/control_metadata.h"
@@ -19,7 +20,7 @@
namespace FileSys {
-const std::array LANGUAGE_NAMES{{
+const std::array LANGUAGE_NAMES{{
"AmericanEnglish",
"BritishEnglish",
"Japanese",
@@ -36,137 +37,95 @@ const std::array LANGUAGE_NAMES{{
"TraditionalChinese",
"SimplifiedChinese",
"BrazilianPortuguese",
+ "Polish",
+ "Thai",
}};
-namespace {
-constexpr std::size_t LEGACY_LANGUAGE_REGION_SIZE = sizeof(std::array);
-constexpr std::size_t PACKED_LANGUAGE_REGION_MAX_SIZE = sizeof(LanguageEntry) * 32;
+namespace
+{
+ constexpr std::size_t MAX_EXPANDED_LANG_SIZE = sizeof(LanguageEntry) * 32;
-bool InflateRawDeflate(std::span compressed, std::vector& out) {
- if (compressed.empty() || compressed.size() > std::numeric_limits::max()) {
- return false;
- }
- z_stream stream{};
- stream.next_in = const_cast(reinterpret_cast(compressed.data()));
- stream.avail_in = static_cast(compressed.size());
- if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) {
- return false;
- }
+ bool InflateRawDeflate(std::span compressed, std::vector& out)
+ {
+ if (compressed.empty()) return false;
- std::array chunk{};
- int ret = Z_OK;
- while (ret == Z_OK) {
- stream.next_out = reinterpret_cast(chunk.data());
- stream.avail_out = static_cast(chunk.size());
- ret = inflate(&stream, Z_NO_FLUSH);
- if (ret != Z_OK && ret != Z_STREAM_END) {
- inflateEnd(&stream);
+ z_stream stream{};
+ stream.next_in = const_cast(reinterpret_cast(compressed.data()));
+ stream.avail_in = static_cast(compressed.size());
+ if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) {
return false;
}
- const auto produced = chunk.size() - static_cast(stream.avail_out);
- if (produced != 0) {
- if (out.size() + produced > PACKED_LANGUAGE_REGION_MAX_SIZE) {
- inflateEnd(&stream);
- return false;
- }
- out.insert(out.end(), chunk.begin(),
- chunk.begin() + static_cast(produced));
+ out.resize(MAX_EXPANDED_LANG_SIZE);
+ stream.next_out = reinterpret_cast(out.data());
+ stream.avail_out = static_cast(out.size());
+
+ int ret = inflate(&stream, Z_FINISH);
+ inflateEnd(&stream);
+
+ if (ret != Z_STREAM_END && ret != Z_OK) {
+ return false;
}
+
+ // Shrink to actual decompressed size
+ out.resize(stream.total_out);
+ return true;
}
-
- inflateEnd(&stream);
- return ret == Z_STREAM_END;
-}
-
-void DecodePackedLanguageEntries(RawNACP& raw) {
- auto* packed_region = reinterpret_cast(raw.language_entries.data());
- u16_le compressed_size_le{};
- std::memcpy(&compressed_size_le, packed_region, sizeof(compressed_size_le));
- const auto compressed_size = static_cast(compressed_size_le);
-
- if (compressed_size == 0 || compressed_size > LEGACY_LANGUAGE_REGION_SIZE - sizeof(u16_le)) {
- return;
- }
-
- std::vector decompressed;
- if (!InflateRawDeflate(
- std::span(packed_region + sizeof(u16_le), compressed_size), decompressed)) {
- return;
- }
-
- if (decompressed.size() < LEGACY_LANGUAGE_REGION_SIZE ||
- decompressed.size() % sizeof(LanguageEntry) != 0) {
- return;
- }
-
- std::memcpy(raw.language_entries.data(), decompressed.data(), LEGACY_LANGUAGE_REGION_SIZE);
-}
} // namespace
std::string LanguageEntry::GetApplicationName() const {
- return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
- application_name.size());
+ return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), application_name.size());
}
std::string LanguageEntry::GetDeveloperName() const {
- return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(),
- developer_name.size());
+ return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), developer_name.size());
}
-constexpr std::array language_to_codes = {{
- Language::Japanese,
- Language::AmericanEnglish,
- Language::French,
- Language::German,
- Language::Italian,
- Language::Spanish,
- Language::SimplifiedChinese,
- Language::Korean,
- Language::Dutch,
- Language::Portuguese,
- Language::Russian,
- Language::TraditionalChinese,
- Language::BritishEnglish,
- Language::CanadianFrench,
- Language::LatinAmericanSpanish,
- Language::SimplifiedChinese,
- Language::TraditionalChinese,
- Language::BrazilianPortuguese,
-}};
-
NACP::NACP() = default;
-NACP::NACP(VirtualFile file) {
+NACP::NACP(VirtualFile file)
+{
file->ReadObject(&raw);
- DecodePackedLanguageEntries(raw);
+ if (raw.titles_data_format == TitleDataFormat::Compressed) {
+ const u16 compressed_size = raw.language_entries.compressed_data.buffer_size;
+ std::span compressed_payload{raw.language_entries.compressed_data.buffer,
+ compressed_size};
+
+ std::vector decompressed;
+ if (InflateRawDeflate(compressed_payload, decompressed)) {
+ const size_t entry_count = decompressed.size() / sizeof(LanguageEntry);
+ language_entries.resize(entry_count);
+ std::memcpy(language_entries.data(), decompressed.data(), decompressed.size());
+ }
+ } else {
+ language_entries.resize(16);
+ std::memcpy(language_entries.data(), raw.language_entries.language_entries.data(),
+ sizeof(raw.language_entries.language_entries));
+ }
}
NACP::~NACP() = default;
const LanguageEntry& NACP::GetLanguageEntry() const {
- Language language =
- language_to_codes[static_cast(Settings::values.language_index.GetValue())];
+ u32 index = static_cast(Settings::values.language_index.GetValue());
- {
- const auto& language_entry = raw.language_entries.at(static_cast(language));
- if (!language_entry.GetApplicationName().empty())
- return language_entry;
+ if (index < language_entries.size()) {
+ return language_entries[index];
}
- for (const auto& language_entry : raw.language_entries) {
- if (!language_entry.GetApplicationName().empty())
- return language_entry;
+ for (const auto& entry : language_entries) {
+ return entry;
}
- return raw.language_entries.at(static_cast(Language::AmericanEnglish));
+ return language_entries.at(static_cast(Language::AmericanEnglish));
}
-std::array NACP::GetApplicationNames() const {
- std::array names{};
- for (size_t i = 0; i < raw.language_entries.size(); ++i) {
- names[i] = raw.language_entries[i].GetApplicationName();
+std::vector NACP::GetApplicationNames() const {
+ std::vector names;
+ names.reserve(language_entries.size());
+ for (const auto& entry : language_entries) {
+ names.push_back(entry.GetApplicationName());
}
return names;
}
@@ -205,7 +164,7 @@ bool NACP::GetUserAccountSwitchLock() const {
}
u32 NACP::GetSupportedLanguages() const {
- return raw.supported_languages;
+ return u32(raw.supported_languages);
}
u64 NACP::GetDeviceSaveDataSize() const {
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index bd109f783f..7b90ba235d 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -25,22 +25,176 @@ struct LanguageEntry {
std::string GetApplicationName() const;
std::string GetDeveloperName() const;
};
-static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.");
+static_assert(sizeof(LanguageEntry) == 0x300);
+
+struct LanguageEntryData {
+ union {
+ // TitleDataFormat::Uncompressed (16 entries)
+ std::array language_entries;
+
+ // TitleDataFormat::Compressed (18+ entries)
+ struct {
+ u16 buffer_size;
+ u8 buffer[0x2FFE];
+ } compressed_data;
+ };
+};
+
+enum class TitleDataFormat : u8 {
+ Uncompressed = 0,
+ Compressed = 1,
+};
+
+struct ApplicationNeighborDetectionGroupConfiguration {
+ u64 group_id; ///< GroupId
+ std::array key;
+};
+static_assert(sizeof(ApplicationNeighborDetectionGroupConfiguration) == 0x18);
+
+// NeighborDetectionClientConfiguration
+struct NeighborDetectionClientConfiguration {
+ ApplicationNeighborDetectionGroupConfiguration send_group_configuration; ///< SendGroupConfiguration
+ std::array receivable_group_configurations; ///< ReceivableGroupConfigurations
+};
+static_assert(sizeof(NeighborDetectionClientConfiguration) == 0x198);
+
+enum class ApparentPlatform : u8 {
+ NX = 0,
+ Ounce = 1,
+ Count = 2
+};
+
+enum class JitConfigurationFlag : u64 {
+ None = 0,
+ IsEnabled = 1,
+};
+
+struct JitConfiguration {
+ JitConfigurationFlag jit_configuration_flag;
+ u64 memory_size;
+};
+static_assert(sizeof(JitConfiguration) == 0x10);
+
+enum RequiredAddOnContentsSetDescriptorFlag : u16 {
+ None = 0,
+ Continue = 1
+};
+struct RequiredAddOnContentsSetDescriptor {
+ u16 index : 15;
+ RequiredAddOnContentsSetDescriptorFlag flag : 1;
+};
+static_assert(sizeof(RequiredAddOnContentsSetDescriptor) == 0x2);
+
+struct RequiredAddOnContentsSetBinaryDescriptor {
+ RequiredAddOnContentsSetDescriptor descriptors[0x20];
+};
+static_assert(sizeof(RequiredAddOnContentsSetBinaryDescriptor) == 0x40);
+
+enum class PlayReportPermission : u8 {
+ None = 0,
+ TargetMarketing = 1,
+};
+
+enum class CrashScreenshotForProd : u8 {
+ Deny = 0,
+ Allow = 1,
+};
+
+enum class CrashScreenshotForDev : u8 {
+ Deny = 0,
+ Allow = 1,
+};
+
+enum class ContentsAvailabilityTransitionPolicy : u8 {
+ NoPolicy = 0,
+ Stable = 1,
+ Changeable = 2,
+};
+
+struct AccessibleLaunchRequiredVersion {
+ std::array application_id;
+};
+static_assert(sizeof(AccessibleLaunchRequiredVersion) == 0x40);
+
+enum class CrashReport : u8 {
+ Deny = 0,
+ Allow = 1,
+};
+
+enum class PlayLogQueryCapability : u8 {
+ None = 0,
+ WhiteList = 1,
+ All = 2,
+};
+
+struct ApplicationControlDataConditionData {
+ u8 priority;
+ INSERT_PADDING_BYTES(7);
+ u16 aoc_index;
+ INSERT_PADDING_BYTES(6);
+};
+static_assert(sizeof(ApplicationControlDataConditionData) == 0x10);
+
+#pragma pack(push, 1)
+struct ApplicationControlDataCondition {
+ u64 type;
+ std::array data;
+ u8 count;
+};
+#pragma pack(pop)
+static_assert(sizeof(ApplicationControlDataCondition) == 0x89);
+
+// A language on the NX. These are for names and icons.
+#define NACP_LANGUAGE_LIST \
+ NACP_LANGUAGE_ELEM(AmericanEnglish, 0) \
+ NACP_LANGUAGE_ELEM(BritishEnglish, 1) \
+ NACP_LANGUAGE_ELEM(Japanese, 2) \
+ NACP_LANGUAGE_ELEM(French, 3) \
+ NACP_LANGUAGE_ELEM(German, 4) \
+ NACP_LANGUAGE_ELEM(LatinAmericanSpanish, 5) \
+ NACP_LANGUAGE_ELEM(Spanish, 6) \
+ NACP_LANGUAGE_ELEM(Italian, 7) \
+ NACP_LANGUAGE_ELEM(Dutch, 8) \
+ NACP_LANGUAGE_ELEM(CanadianFrench, 9) \
+ NACP_LANGUAGE_ELEM(Portuguese, 10) \
+ NACP_LANGUAGE_ELEM(Russian, 11) \
+ NACP_LANGUAGE_ELEM(Korean, 12) \
+ NACP_LANGUAGE_ELEM(TraditionalChinese, 13) \
+ NACP_LANGUAGE_ELEM(SimplifiedChinese, 14) \
+ NACP_LANGUAGE_ELEM(BrazilianPortuguese, 15) \
+ NACP_LANGUAGE_ELEM(Polish, 16) \
+ NACP_LANGUAGE_ELEM(Thai, 17) \
+
+enum class Language : u8 {
+#define NACP_LANGUAGE_ELEM(X, N) X = N,
+ NACP_LANGUAGE_LIST
+#undef NACP_LANGUAGE_ELEM
+ Count = 18,
+ Default = 255,
+};
+
+// Yes it's duplicated, why? Bits i guess
+enum class SupportedLanguage : u32 {
+#define NACP_LANGUAGE_ELEM(X, N) X = 1ULL << N,
+ NACP_LANGUAGE_LIST
+#undef NACP_LANGUAGE_ELEM
+};
+#undef NACP_LANGUAGE_LIST
// The raw file format of a NACP file.
struct RawNACP {
- std::array language_entries;
+ LanguageEntryData language_entries;
std::array isbn;
u8 startup_user_account;
u8 user_account_switch_lock;
u8 addon_content_registration_type;
u32_le application_attribute;
- u32_le supported_languages;
+ SupportedLanguage supported_languages;
u32_le parental_control;
bool screenshot_enabled;
u8 video_capture_mode;
bool data_loss_confirmation;
- INSERT_PADDING_BYTES(1);
+ u8 play_log_policy;
u64_le presence_group_id;
std::array rating_age;
std::array version_string;
@@ -56,10 +210,14 @@ struct RawNACP {
u8 logo_type;
u8 logo_handling;
bool runtime_add_on_content_install;
- INSERT_PADDING_BYTES(5);
+ u8 runtime_parameter_delivery;
+ u8 appropriate_age_for_china;
+ INSERT_PADDING_BYTES(1);
+ CrashReport crash_report;
u64_le seed_for_pseudo_device_id;
std::array bcat_passphrase;
- INSERT_PADDING_BYTES(7);
+ u8 startup_user_account_option;
+ INSERT_PADDING_BYTES(6); // ReservedForUserAccountSaveDataOperation
u64_le user_account_save_data_max_size;
u64_le user_account_save_data_max_journal_size;
u64_le device_save_data_max_size;
@@ -69,13 +227,28 @@ struct RawNACP {
u64_le cache_storage_journal_size;
u64_le cache_storage_data_and_journal_max_size;
u16_le cache_storage_max_index;
- INSERT_PADDING_BYTES(0x8B);
+ u8 runtime_upgrade;
+ u32_le supporting_limited_application_licenses;
+ std::array play_log_queryable_application_id;
+ PlayLogQueryCapability play_log_query_capability;
+ u8 repair_flag;
+ u8 program_index;
+ u8 required_network_service_license_on_launch_flag;
u8 app_error_code_prefix;
- INSERT_PADDING_BYTES(1);
+ TitleDataFormat titles_data_format;
u8 acd_index;
- u8 apparent_platform;
- INSERT_PADDING_BYTES(0x22F);
- std::array app_control_data_condition;
+ ApparentPlatform apparent_platform;
+ // NeighborDetectionClientConfiguration neighbor_detection_client_configuration;
+ // JitConfiguration jit_configuration;
+ // RequiredAddOnContentsSetBinaryDescriptor required_add_on_contents_set_binary_descriptor;
+ // PlayReportPermission play_report_permission;
+ // CrashScreenshotForProd crash_screenshot_for_prod;
+ // CrashScreenshotForDev crash_screenshot_for_dev;
+ // ContentsAvailabilityTransitionPolicy contents_availability_transition_policy;
+ // SupportedLanguage supported_language_copy; ///< TODO: add to XML generation.
+ INSERT_PADDING_BYTES(0x1EF);
+ AccessibleLaunchRequiredVersion accessible_launch_required_version;
+ ApplicationControlDataCondition application_control_data_condition; ///< Used for Switch 2 upgrade packs, which are distributed as AddOnContent titles
u8 initial_program_index;
INSERT_PADDING_BYTES(2);
u32_le accessible_program_index_flags;
@@ -85,34 +258,13 @@ struct RawNACP {
u8 has_ingame_voice_chat;
INSERT_PADDING_BYTES(3);
u32_le supported_extra_addon_content_flag;
- INSERT_PADDING_BYTES(0x698);
+ u8 has_karaoke_feature;
+ INSERT_PADDING_BYTES(0x697);
std::array platform_specific_region;
};
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
-// A language on the NX. These are for names and icons.
-enum class Language : u8 {
- AmericanEnglish = 0,
- BritishEnglish = 1,
- Japanese = 2,
- French = 3,
- German = 4,
- LatinAmericanSpanish = 5,
- Spanish = 6,
- Italian = 7,
- Dutch = 8,
- CanadianFrench = 9,
- Portuguese = 10,
- Russian = 11,
- Korean = 12,
- TraditionalChinese = 13,
- SimplifiedChinese = 14,
- BrazilianPortuguese = 15,
-
- Default = 255,
-};
-
-extern const std::array LANGUAGE_NAMES;
+extern const std::array LANGUAGE_NAMES;
// A class representing the format used by NX metadata files, typically named Control.nacp.
// These store application name, dev name, title id, and other miscellaneous data.
@@ -131,7 +283,7 @@ public:
u64 GetDefaultNormalSaveSize() const;
u64 GetDefaultJournalSaveSize() const;
u32 GetSupportedLanguages() const;
- std::array GetApplicationNames() const;
+ std::vector GetApplicationNames() const;
std::vector GetRawBytes() const;
bool GetUserAccountSwitchLock() const;
u64 GetDeviceSaveDataSize() const;
@@ -140,6 +292,7 @@ public:
private:
RawNACP raw{};
+ std::vector language_entries;
};
} // namespace FileSys
diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp
index d187be935a..ff5ccdd6d3 100644
--- a/src/core/hle/service/ns/language.cpp
+++ b/src/core/hle/service/ns/language.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -22,6 +25,8 @@ constexpr ApplicationLanguagePriorityList priority_list_american_english = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_british_english = {{
@@ -40,6 +45,8 @@ constexpr ApplicationLanguagePriorityList priority_list_british_english = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_japanese = {{
@@ -58,6 +65,8 @@ constexpr ApplicationLanguagePriorityList priority_list_japanese = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_french = {{
@@ -76,6 +85,8 @@ constexpr ApplicationLanguagePriorityList priority_list_french = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_german = {{
@@ -94,6 +105,8 @@ constexpr ApplicationLanguagePriorityList priority_list_german = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_latin_american_spanish = {{
@@ -112,6 +125,8 @@ constexpr ApplicationLanguagePriorityList priority_list_latin_american_spanish =
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_spanish = {{
@@ -130,6 +145,8 @@ constexpr ApplicationLanguagePriorityList priority_list_spanish = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_italian = {{
@@ -148,6 +165,8 @@ constexpr ApplicationLanguagePriorityList priority_list_italian = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_dutch = {{
@@ -166,6 +185,8 @@ constexpr ApplicationLanguagePriorityList priority_list_dutch = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_canadian_french = {{
@@ -184,6 +205,8 @@ constexpr ApplicationLanguagePriorityList priority_list_canadian_french = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_portuguese = {{
@@ -202,6 +225,8 @@ constexpr ApplicationLanguagePriorityList priority_list_portuguese = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_russian = {{
@@ -220,6 +245,8 @@ constexpr ApplicationLanguagePriorityList priority_list_russian = {{
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_korean = {{
@@ -238,6 +265,8 @@ constexpr ApplicationLanguagePriorityList priority_list_korean = {{
ApplicationLanguage::Japanese,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_traditional_chinese = {{
@@ -256,6 +285,8 @@ constexpr ApplicationLanguagePriorityList priority_list_traditional_chinese = {{
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_simplified_chinese = {{
@@ -274,6 +305,8 @@ constexpr ApplicationLanguagePriorityList priority_list_simplified_chinese = {{
ApplicationLanguage::Portuguese,
ApplicationLanguage::Russian,
ApplicationLanguage::Korean,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
}};
constexpr ApplicationLanguagePriorityList priority_list_brazilian_portuguese = {{
@@ -293,6 +326,43 @@ constexpr ApplicationLanguagePriorityList priority_list_brazilian_portuguese = {
ApplicationLanguage::Korean,
ApplicationLanguage::SimplifiedChinese,
ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::Thai,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_thai = {{
+ ApplicationLanguage::Thai,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Korean,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Polish,
+}};
+
+constexpr ApplicationLanguagePriorityList priority_list_polish = {{
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Korean,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
}};
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(
@@ -330,6 +400,10 @@ const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(
return &priority_list_simplified_chinese;
case ApplicationLanguage::BrazilianPortuguese:
return &priority_list_brazilian_portuguese;
+ case ApplicationLanguage::Polish:
+ return &priority_list_polish;
+ case ApplicationLanguage::Thai:
+ return &priority_list_thai;
default:
return nullptr;
}
@@ -372,6 +446,10 @@ std::optional ConvertToApplicationLanguage(
return ApplicationLanguage::SimplifiedChinese;
case Set::LanguageCode::PT_BR:
return ApplicationLanguage::BrazilianPortuguese;
+ case Set::LanguageCode::TH:
+ return ApplicationLanguage::Thai;
+ case Set::LanguageCode::PL:
+ return ApplicationLanguage::Polish;
default:
return std::nullopt;
}
@@ -411,6 +489,10 @@ std::optional ConvertToLanguageCode(const ApplicationLanguage
return Set::LanguageCode::ZH_HANS;
case ApplicationLanguage::BrazilianPortuguese:
return Set::LanguageCode::PT_BR;
+ case ApplicationLanguage::Thai:
+ return Set::LanguageCode::TH;
+ case ApplicationLanguage::Polish:
+ return Set::LanguageCode::PL;
default:
return std::nullopt;
}
diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h
index dad9934f2e..d86f59df49 100644
--- a/src/core/hle/service/ns/language.h
+++ b/src/core/hle/service/ns/language.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -26,6 +29,8 @@ enum class ApplicationLanguage : u8 {
TraditionalChinese,
SimplifiedChinese,
BrazilianPortuguese,
+ Polish,
+ Thai,
Count
};
using ApplicationLanguagePriorityList =
diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h
index 7f152cb523..469b8d745c 100644
--- a/src/core/hle/service/set/settings_types.h
+++ b/src/core/hle/service/set/settings_types.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -169,6 +172,8 @@ enum class Language : u32 {
SimplifiedCHhinese,
TraditionalChinese,
BrazilianPortuguese,
+ Polish,
+ Thai
};
/// This is "nn::settings::LanguageCode", which is a NUL-terminated string stored in a u64.
@@ -191,6 +196,8 @@ enum class LanguageCode : u64 {
ZH_HANS = 0x00736E61482D687A,
ZH_HANT = 0x00746E61482D687A,
PT_BR = 0x00000052422D7470,
+ PL = 0x000000000000706C,
+ TH = 0x0000000000006874,
};
/// This is nn::settings::system::NotificationVolume
@@ -248,7 +255,7 @@ enum class PlatformRegion : s32 {
Terra = 2,
};
-constexpr std::array available_language_codes = {{
+constexpr std::array available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
LanguageCode::FR,
@@ -267,9 +274,11 @@ constexpr std::array available_language_codes = {{
LanguageCode::ZH_HANS,
LanguageCode::ZH_HANT,
LanguageCode::PT_BR,
+ LanguageCode::PL,
+ LanguageCode::TH
}};
-static constexpr std::array, 18> language_to_layout{{
+static constexpr std::array, 20> language_to_layout{{
{LanguageCode::JA, KeyboardLayout::Japanese},
{LanguageCode::EN_US, KeyboardLayout::EnglishUs},
{LanguageCode::FR, KeyboardLayout::French},
@@ -288,6 +297,8 @@ static constexpr std::array, 18> languag
{LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
{LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
{LanguageCode::PT_BR, KeyboardLayout::Portuguese},
+ {LanguageCode::PL, KeyboardLayout::EnglishUsInternational},
+ {LanguageCode::TH, KeyboardLayout::EnglishUsInternational}
}};
/// This is nn::settings::system::AccountNotificationFlag
diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp
index f4355197b0..9666092802 100644
--- a/src/qt_common/config/shared_translation.cpp
+++ b/src/qt_common/config/shared_translation.cpp
@@ -651,7 +651,8 @@ std::unique_ptr ComboboxEnumeration(QObject* parent)
PAIR(Language, ChineseSimplified, tr("Simplified Chinese")),
PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")),
PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")),
- PAIR(Language, Serbian, tr("Serbian (српски)")),
+ PAIR(Language, Polish, tr("Polish (polska)")),
+ PAIR(Language, Thai, tr("Thai (แบบไทย)")),
}});
translations->insert({Settings::EnumMetadata::Index(),
{