mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-04-20 20:12:05 +00:00
Introduced with TLoZ BotW 1.9.0, a compression flag determines whether the first 0x3000 bytes of the NACP title block contain a zlib-compressed blob that decompresses to 0x6000 bytes with up to 32 language entries. Added Polish and Thai language support (indexes 16/17), NacpHelper decompression utility, and updated all title-reading call sites to use resolved entries.
61 lines
2.5 KiB
C#
61 lines
2.5 KiB
C#
using System;
|
|
using System.Buffers.Binary;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Runtime.InteropServices;
|
|
using LibHac.Ns;
|
|
|
|
namespace Ryujinx.HLE.Utilities
|
|
{
|
|
public static class NacpHelper
|
|
{
|
|
private const int CompressedTitleCount = 32;
|
|
private const int UncompressedTitleCount = 16;
|
|
private const int TitleEntrySize = 0x300;
|
|
private const int TitleCompressionByteIndex = 1; // offset within Reserved3214
|
|
|
|
public static bool IsCompressed(ref readonly ApplicationControlProperty nacp)
|
|
=> nacp.Reserved3214[TitleCompressionByteIndex] == 1;
|
|
|
|
public static int GetTitleCount(ref readonly ApplicationControlProperty nacp)
|
|
=> IsCompressed(in nacp) ? CompressedTitleCount : UncompressedTitleCount;
|
|
|
|
/// <summary>
|
|
/// Decompresses a zlib-compressed NACP title block into 32 title entries.
|
|
/// Only call this when <see cref="IsCompressed"/> returns <c>true</c>.
|
|
/// </summary>
|
|
public static ApplicationControlProperty.ApplicationTitle[] DecompressTitleEntries(
|
|
ref readonly ApplicationControlProperty nacp)
|
|
{
|
|
ReadOnlySpan<byte> titleBytes = MemoryMarshal.AsBytes(
|
|
(ReadOnlySpan<ApplicationControlProperty.ApplicationTitle>)nacp.Title);
|
|
|
|
ushort compressedBlobSize = BinaryPrimitives.ReadUInt16LittleEndian(titleBytes);
|
|
ReadOnlySpan<byte> compressedBlob = titleBytes.Slice(2, compressedBlobSize);
|
|
|
|
byte[] decompressed = new byte[CompressedTitleCount * TitleEntrySize];
|
|
|
|
using (var compressedStream = new MemoryStream(compressedBlob.ToArray()))
|
|
using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress))
|
|
{
|
|
int totalRead = 0;
|
|
while (totalRead < decompressed.Length)
|
|
{
|
|
int read = deflateStream.Read(decompressed, totalRead, decompressed.Length - totalRead);
|
|
if (read == 0)
|
|
break;
|
|
totalRead += read;
|
|
}
|
|
}
|
|
|
|
var result = new ApplicationControlProperty.ApplicationTitle[CompressedTitleCount];
|
|
for (int i = 0; i < CompressedTitleCount; i++)
|
|
{
|
|
result[i] = MemoryMarshal.Read<ApplicationControlProperty.ApplicationTitle>(
|
|
decompressed.AsSpan(i * TitleEntrySize, TitleEntrySize));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|