aboutsummaryrefslogtreecommitdiff
path: root/src/core/file_sys
diff options
context:
space:
mode:
authorlizzie <lizzie@eden-emu.dev>2026-02-23 13:39:16 +0100
committercrueter <crueter@eden-emu.dev>2026-02-23 13:39:16 +0100
commitc610b611261ae2bdb5df7bac6104f1c1e98b6354 (patch)
tree62fb29b8fe8f9ef2775cf571f63e00d413f025ed /src/core/file_sys
parent963f9981ce9846a4ed97f6b12c49d9b61db6fab9 (diff)
[common, hle] add new updates Polish+Thai languages (#3587)
Signed-off-by: lizzie <lizzie@eden-emu.dev> Co-authored-by: maufeat <sahyno1996@gmail.com> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3587 Reviewed-by: Maufeat <sahyno1996@gmail.com> Reviewed-by: DraVee <dravee@eden-emu.dev> Co-authored-by: lizzie <lizzie@eden-emu.dev> Co-committed-by: lizzie <lizzie@eden-emu.dev>
Diffstat (limited to 'src/core/file_sys')
-rw-r--r--src/core/file_sys/control_metadata.cpp157
-rw-r--r--src/core/file_sys/control_metadata.h225
2 files changed, 247 insertions, 135 deletions
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 <zlib.h>
#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<const char*, 16> LANGUAGE_NAMES{{
+const std::array<const char*, size_t(Language::Count)> LANGUAGE_NAMES{{
"AmericanEnglish",
"BritishEnglish",
"Japanese",
@@ -36,137 +37,95 @@ const std::array<const char*, 16> LANGUAGE_NAMES{{
"TraditionalChinese",
"SimplifiedChinese",
"BrazilianPortuguese",
+ "Polish",
+ "Thai",
}};
-namespace {
-constexpr std::size_t LEGACY_LANGUAGE_REGION_SIZE = sizeof(std::array<LanguageEntry, 16>);
-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<const u8> compressed, std::vector<u8>& out) {
- if (compressed.empty() || compressed.size() > std::numeric_limits<uInt>::max()) {
- return false;
- }
- z_stream stream{};
- stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(compressed.data()));
- stream.avail_in = static_cast<uInt>(compressed.size());
- if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) {
- return false;
- }
+ bool InflateRawDeflate(std::span<const u8> compressed, std::vector<u8>& out)
+ {
+ if (compressed.empty()) return false;
- std::array<u8, 0x1000> chunk{};
- int ret = Z_OK;
- while (ret == Z_OK) {
- stream.next_out = reinterpret_cast<Bytef*>(chunk.data());
- stream.avail_out = static_cast<uInt>(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<Bytef*>(reinterpret_cast<const Bytef*>(compressed.data()));
+ stream.avail_in = static_cast<uInt>(compressed.size());
+ if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) {
return false;
}
- const auto produced = chunk.size() - static_cast<std::size_t>(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<std::ptrdiff_t>(produced));
- }
- }
-
- inflateEnd(&stream);
- return ret == Z_STREAM_END;
-}
+ out.resize(MAX_EXPANDED_LANG_SIZE);
+ stream.next_out = reinterpret_cast<Bytef*>(out.data());
+ stream.avail_out = static_cast<uInt>(out.size());
-void DecodePackedLanguageEntries(RawNACP& raw) {
- auto* packed_region = reinterpret_cast<u8*>(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<std::size_t>(compressed_size_le);
+ int ret = inflate(&stream, Z_FINISH);
+ inflateEnd(&stream);
- if (compressed_size == 0 || compressed_size > LEGACY_LANGUAGE_REGION_SIZE - sizeof(u16_le)) {
- return;
- }
-
- std::vector<u8> decompressed;
- if (!InflateRawDeflate(
- std::span<const u8>(packed_region + sizeof(u16_le), compressed_size), decompressed)) {
- return;
- }
+ if (ret != Z_STREAM_END && ret != Z_OK) {
+ return false;
+ }
- if (decompressed.size() < LEGACY_LANGUAGE_REGION_SIZE ||
- decompressed.size() % sizeof(LanguageEntry) != 0) {
- return;
+ // Shrink to actual decompressed size
+ out.resize(stream.total_out);
+ return true;
}
-
- 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());
-}
-
-constexpr std::array<Language, 18> 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,
-}};
+ return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), developer_name.size());
+}
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<const u8> compressed_payload{raw.language_entries.compressed_data.buffer,
+ compressed_size};
+
+ std::vector<u8> 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<s32>(Settings::values.language_index.GetValue())];
+ u32 index = static_cast<u32>(Settings::values.language_index.GetValue());
- {
- const auto& language_entry = raw.language_entries.at(static_cast<u8>(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<u8>(Language::AmericanEnglish));
+ return language_entries.at(static_cast<u8>(Language::AmericanEnglish));
}
-std::array<std::string, 16> NACP::GetApplicationNames() const {
- std::array<std::string, 16> names{};
- for (size_t i = 0; i < raw.language_entries.size(); ++i) {
- names[i] = raw.language_entries[i].GetApplicationName();
+std::vector<std::string> NACP::GetApplicationNames() const {
+ std::vector<std::string> 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<LanguageEntry, 16> 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<u8, 0x10> key;
+};
+static_assert(sizeof(ApplicationNeighborDetectionGroupConfiguration) == 0x18);
+
+// NeighborDetectionClientConfiguration
+struct NeighborDetectionClientConfiguration {
+ ApplicationNeighborDetectionGroupConfiguration send_group_configuration; ///< SendGroupConfiguration
+ std::array<ApplicationNeighborDetectionGroupConfiguration, 0x10> 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<u64, 8> 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<ApplicationControlDataConditionData, 0x8> 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<LanguageEntry, 16> language_entries;
+ LanguageEntryData language_entries;
std::array<u8, 0x25> 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<u8, 0x20> rating_age;
std::array<char, 0x10> 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<u8, 0x41> 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<u8, 0x8*16> 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<u8, 0x89> 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<u8, 0x400> 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<const char*, 16> LANGUAGE_NAMES;
+extern const std::array<const char*, size_t(Language::Count)> 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<std::string, 16> GetApplicationNames() const;
+ std::vector<std::string> GetApplicationNames() const;
std::vector<u8> GetRawBytes() const;
bool GetUserAccountSwitchLock() const;
u64 GetDeviceSaveDataSize() const;
@@ -140,6 +292,7 @@ public:
private:
RawNACP raw{};
+ std::vector<LanguageEntry> language_entries;
};
} // namespace FileSys