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 b553402628..6fbeebdb6c 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -1009,7 +1009,8 @@
简体中文
正體中文
Português do Brasil
- српски
+ Polski
+ ไทย
B
@@ -1214,6 +1215,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..7f7ccc073f 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -15,11 +15,13 @@
#include "common/string_util.h"
#include "common/swap.h"
#include "core/file_sys/control_metadata.h"
+
+#include "common/logging/log.h"
#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
-const std::array LANGUAGE_NAMES{{
+const std::array LANGUAGE_NAMES{{
"AmericanEnglish",
"BritishEnglish",
"Japanese",
@@ -36,16 +38,16 @@ 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;
+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;
- }
+bool InflateRawDeflate(std::span compressed, std::vector& out)
+{
+ if (compressed.empty()) return false;
z_stream stream{};
stream.next_in = const_cast(reinterpret_cast(compressed.data()));
@@ -54,54 +56,20 @@ bool InflateRawDeflate(std::span compressed, std::vector& out) {
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);
- 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);
- 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;
+ if (ret != Z_STREAM_END && ret != Z_OK) {
+ return false;
}
- std::memcpy(raw.language_entries.data(), decompressed.data(), LEGACY_LANGUAGE_REGION_SIZE);
+ // Shrink to actual decompressed size
+ out.resize(stream.total_out);
+ return true;
}
} // namespace
@@ -115,7 +83,7 @@ std::string LanguageEntry::GetDeveloperName() const {
developer_name.size());
}
-constexpr std::array language_to_codes = {{
+constexpr std::array language_to_codes = {{
Language::Japanese,
Language::AmericanEnglish,
Language::French,
@@ -134,39 +102,54 @@ constexpr std::array language_to_codes = {{
Language::SimplifiedChinese,
Language::TraditionalChinese,
Language::BrazilianPortuguese,
+ Language::Polish,
+ Language::Thai
}};
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;
}
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index bd109f783f..e0e0bb90a5 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -27,9 +27,29 @@ struct LanguageEntry {
};
static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.");
+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 TitleDataFormat : u8 {
+ Uncompressed = 0,
+ Compressed = 1,
+};
+
// 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;
@@ -40,7 +60,7 @@ struct RawNACP {
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;
@@ -55,11 +75,15 @@ struct RawNACP {
std::array local_communication;
u8 logo_type;
u8 logo_handling;
- bool runtime_add_on_content_install;
- INSERT_PADDING_BYTES(5);
+ u8 runtime_add_on_content_install;
+ u8 runtime_parameter_delivery;
+ u8 appropriate_age_for_china;
+ INSERT_PADDING_BYTES(1);
+ u8 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,9 +93,16 @@ 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 app_error_code_prefix;
INSERT_PADDING_BYTES(1);
+ u8 runtime_upgrade;
+ u32_le supporting_limited_application_licenses;
+ std::array play_log_queryable_application_id;
+ u8 play_log_query_capability;
+ u8 repair_flag;
+ u8 program_index;
+ u8 required_network_service_license_on_launch_flag;
+ u8 app_error_code_prefix;
+ TitleDataFormat titles_data_format;
u8 acd_index;
u8 apparent_platform;
INSERT_PADDING_BYTES(0x22F);
@@ -85,7 +116,8 @@ 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.");
@@ -108,11 +140,13 @@ enum class Language : u8 {
TraditionalChinese = 13,
SimplifiedChinese = 14,
BrazilianPortuguese = 15,
+ Polish = 16,
+ Thai = 17,
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 +165,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 +174,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..b4e82d18c9 100644
--- a/src/core/hle/service/ns/language.cpp
+++ b/src/core/hle/service/ns/language.cpp
@@ -295,6 +295,44 @@ constexpr ApplicationLanguagePriorityList priority_list_brazilian_portuguese = {
ApplicationLanguage::TraditionalChinese,
}};
+constexpr ApplicationLanguagePriorityList priority_list_polish = {{
+ ApplicationLanguage::Polish,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+ }};
+
+constexpr ApplicationLanguagePriorityList priority_list_thai = {{
+ ApplicationLanguage::Thai,
+ ApplicationLanguage::AmericanEnglish,
+ ApplicationLanguage::BritishEnglish,
+ ApplicationLanguage::LatinAmericanSpanish,
+ ApplicationLanguage::CanadianFrench,
+ ApplicationLanguage::French,
+ ApplicationLanguage::German,
+ ApplicationLanguage::Spanish,
+ ApplicationLanguage::Italian,
+ ApplicationLanguage::Dutch,
+ ApplicationLanguage::Portuguese,
+ ApplicationLanguage::Russian,
+ ApplicationLanguage::Japanese,
+ ApplicationLanguage::SimplifiedChinese,
+ ApplicationLanguage::TraditionalChinese,
+ ApplicationLanguage::Korean,
+ }};
+
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(
const ApplicationLanguage lang) {
switch (lang) {
@@ -330,6 +368,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 +414,10 @@ std::optional ConvertToApplicationLanguage(
return ApplicationLanguage::SimplifiedChinese;
case Set::LanguageCode::PT_BR:
return ApplicationLanguage::BrazilianPortuguese;
+ case Set::LanguageCode::PL:
+ return ApplicationLanguage::Polish;
+ case Set::LanguageCode::TH:
+ return ApplicationLanguage::Thai;
default:
return std::nullopt;
}
@@ -411,6 +457,10 @@ std::optional ConvertToLanguageCode(const ApplicationLanguage
return Set::LanguageCode::ZH_HANS;
case ApplicationLanguage::BrazilianPortuguese:
return Set::LanguageCode::PT_BR;
+ case ApplicationLanguage::Polish:
+ return Set::LanguageCode::PL;
+ case ApplicationLanguage::Thai:
+ return Set::LanguageCode::TH;
default:
return std::nullopt;
}
diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h
index dad9934f2e..3502c5c093 100644
--- a/src/core/hle/service/ns/language.h
+++ b/src/core/hle/service/ns/language.h
@@ -26,6 +26,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..831fb26519 100644
--- a/src/core/hle/service/set/settings_types.h
+++ b/src/core/hle/service/set/settings_types.h
@@ -169,6 +169,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 +193,8 @@ enum class LanguageCode : u64 {
ZH_HANS = 0x00736E61482D687A,
ZH_HANT = 0x00746E61482D687A,
PT_BR = 0x00000052422D7470,
+ PL = 0x0000000000006C70,
+ TH = 0x0000000000006874,
};
/// This is nn::settings::system::NotificationVolume
@@ -248,7 +252,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 +271,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 +294,9 @@ 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..e1168d0e9d 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 (Polski)")),
+ PAIR(Language, Thai, tr("Thai (ไทย)")),
}});
translations->insert({Settings::EnumMetadata::Index(),
{