aboutsummaryrefslogtreecommitdiff
path: root/src/core/file_sys/control_metadata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/file_sys/control_metadata.cpp')
-rw-r--r--src/core/file_sys/control_metadata.cpp157
1 files changed, 58 insertions, 99 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 {