From 732b7eb560fd33b5f260d6600843e596a9301d8a Mon Sep 17 00:00:00 2001 From: xbzk Date: Fri, 20 Feb 2026 23:28:24 +0100 Subject: [PATCH] [file_sys] added packed language entries support (#3585) Normally, language_entries contained plain UTF-8 names. In BotW v1.9.0, that title block is no longer directly readable as old UTF-8, so we were parsing binary data as text and going nuts. ... In patch_manager.cpp (ParseControlNCA), I added a validity check: If update title text is unreadable, we keep update metadata but copy only the base language_entries block (0x0000..0x2FFF) so the game name is valid again. UPDATE: managed to decode the new language entries is a raw headerless zlib deflate. added support for proper detection and inflation. Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3585 Reviewed-by: crueter Reviewed-by: MaranBr Reviewed-by: CamilleLaVey Co-authored-by: xbzk Co-committed-by: xbzk --- src/core/CMakeLists.txt | 1 + src/core/file_sys/control_metadata.cpp | 75 ++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8bcb66ac35..4ac08726f7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1219,6 +1219,7 @@ target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API + ZLIB::ZLIB MbedTLS::mbedcrypto${MBEDTLS_LIB_SUFFIX} MbedTLS::mbedtls${MBEDTLS_LIB_SUFFIX}) diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 6f150ff583..7ca7703afa 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -4,6 +4,13 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include +#include + #include "common/settings.h" #include "common/string_util.h" #include "common/swap.h" @@ -31,6 +38,73 @@ const std::array LANGUAGE_NAMES{{ "BrazilianPortuguese", }}; +namespace { +constexpr std::size_t LEGACY_LANGUAGE_REGION_SIZE = sizeof(std::array); +constexpr std::size_t PACKED_LANGUAGE_REGION_MAX_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; + } + + 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)); + } + } + + 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()); @@ -66,6 +140,7 @@ NACP::NACP() = default; NACP::NACP(VirtualFile file) { file->ReadObject(&raw); + DecodePackedLanguageEntries(raw); } NACP::~NACP() = default;