diff options
| author | xbzk <xbzk@eden-emu.dev> | 2026-02-20 23:28:24 +0100 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2026-02-20 23:28:24 +0100 |
| commit | 732b7eb560fd33b5f260d6600843e596a9301d8a (patch) | |
| tree | 72fbccd6b510ff7fc55ae0ff038492c28c2c532d /src/core/file_sys/control_metadata.cpp | |
| parent | 93eecca894396271ae48b33dc3d789f5da82b548 (diff) | |
[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 <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3585
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
Diffstat (limited to 'src/core/file_sys/control_metadata.cpp')
| -rw-r--r-- | src/core/file_sys/control_metadata.cpp | 75 |
1 files changed, 75 insertions, 0 deletions
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 <array> +#include <cstddef> +#include <cstring> +#include <limits> +#include <span> +#include <zlib.h> + #include "common/settings.h" #include "common/string_util.h" #include "common/swap.h" @@ -31,6 +38,73 @@ const std::array<const char*, 16> LANGUAGE_NAMES{{ "BrazilianPortuguese", }}; +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; + +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; + } + + 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); + 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; +} + +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); + + 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 (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; |
