From cc6d2dc162ca00a3db9af09f176798a8297926ab Mon Sep 17 00:00:00 2001 From: LotP <22-lotp@users.noreply.git.ryujinx.app> Date: Wed, 25 Feb 2026 13:58:31 -0600 Subject: [PATCH] fix nacp language buffer (ryubing/ryujinx!281) See merge request ryubing/ryujinx!281 --- Directory.Packages.props | 2 +- .../HOS/SystemState/SystemLanguage.cs | 2 + .../HOS/SystemState/SystemStateMgr.cs | 4 +- .../HOS/SystemState/TitleLanguage.cs | 2 + .../Sdk/Ns/ApplicationControlProperty.cs | 63 ++++++++++++++++++- .../Systems/AppLibrary/ApplicationLibrary.cs | 4 +- .../Systems/Configuration/System/Language.cs | 2 + 7 files changed, 73 insertions(+), 6 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7f437a93a..fba3792db 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -41,7 +41,7 @@ - + diff --git a/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs b/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs index f5b7fc0f1..8e7a44005 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs @@ -20,5 +20,7 @@ namespace Ryujinx.HLE.HOS.SystemState SimplifiedChinese, TraditionalChinese, BrazilianPortuguese, + Polish, + Thai, } } diff --git a/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs index 91277232c..74378b153 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs @@ -23,7 +23,9 @@ namespace Ryujinx.HLE.HOS.SystemState "es-419", "zh-Hans", "zh-Hant", - "pt-BR" + "pt-BR", + "pl", + "th" ]; internal long DesiredKeyboardLayout { get; private set; } diff --git a/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs b/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs index e3bfb9165..e651410ce 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs @@ -18,5 +18,7 @@ namespace Ryujinx.HLE.HOS.SystemState TraditionalChinese, SimplifiedChinese, BrazilianPortuguese, + Polish, + Thai, } } diff --git a/src/Ryujinx.Horizon/Sdk/Ns/ApplicationControlProperty.cs b/src/Ryujinx.Horizon/Sdk/Ns/ApplicationControlProperty.cs index 7640967c6..e6a65feef 100644 --- a/src/Ryujinx.Horizon/Sdk/Ns/ApplicationControlProperty.cs +++ b/src/Ryujinx.Horizon/Sdk/Ns/ApplicationControlProperty.cs @@ -1,12 +1,19 @@ using Ryujinx.Common.Memory; using System; +using System.Buffers.Binary; +using System.IO; +using System.IO.Compression; +using System.Runtime.InteropServices; using System.Text; namespace Ryujinx.Horizon.Sdk.Ns { public struct ApplicationControlProperty { - public Array16 Title; + /// + /// Use to access titles instead of accessing them directly. + /// + public Array16 TitleBlock; public Array37 Isbn; public StartupUserAccountValue StartupUserAccount; public UserAccountSwitchLockValue UserAccountSwitchLock; @@ -58,7 +65,10 @@ namespace Ryujinx.Horizon.Sdk.Ns public RepairFlagValue RepairFlag; public byte ProgramIndex; public RequiredNetworkServiceLicenseOnLaunchValue RequiredNetworkServiceLicenseOnLaunchFlag; - public Array4 Reserved3214; + public byte ApplicationErrorCodePrefix; + public TitleCompressionValue TitleCompression; + public byte AcdIndex; + public byte ApparentPlatform; public ApplicationNeighborDetectionClientConfiguration NeighborDetectionClientConfiguration; public ApplicationJitConfiguration JitConfiguration; public RequiredAddOnContentsSetBinaryDescriptor RequiredAddOnContentsSetBinaryDescriptors; @@ -74,6 +84,47 @@ namespace Ryujinx.Horizon.Sdk.Ns public readonly string DisplayVersionString => Encoding.UTF8.GetString(DisplayVersion.AsSpan()).TrimEnd('\0'); public readonly string ApplicationErrorCodeCategoryString => Encoding.UTF8.GetString(ApplicationErrorCodeCategory.AsSpan()).TrimEnd('\0'); public readonly string BcatPassphraseString => Encoding.UTF8.GetString(BcatPassphrase.AsSpan()).TrimEnd('\0'); + + private const int TitleCount = 32; + private const int TitleEntrySize = 0x300; + + + /// + /// Returns the resolved title entries. When is + /// , the raw bytes are + /// decompressed (raw deflate) from 0x3000 into 0x6000 bytes yielding up to 32 entries. + /// Otherwise the 16 uncompressed entries from are returned directly. + /// + public readonly ApplicationTitle[] Title + { + get + { + var titles = new ApplicationTitle[TitleCount]; + + if (TitleCompression != TitleCompressionValue.Enable) + { + TitleBlock.AsSpan().CopyTo(titles); + + return titles; + } + + ReadOnlySpan titleBytes = MemoryMarshal.AsBytes(TitleBlock.AsSpan()); + ushort compressedBlobSize = BinaryPrimitives.ReadUInt16LittleEndian(titleBytes); + ReadOnlySpan compressedBlob = titleBytes.Slice(2, compressedBlobSize); + + byte[] decompressed = new byte[TitleCount * TitleEntrySize]; + + using (var compressedStream = new MemoryStream(compressedBlob.ToArray())) + using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) + { + deflateStream.ReadExactly(decompressed, 0, decompressed.Length); + } + + MemoryMarshal.Cast(decompressed).CopyTo(titles); + + return titles; + } + } public struct ApplicationTitle { @@ -130,6 +181,8 @@ namespace Ryujinx.Horizon.Sdk.Ns TraditionalChinese = 13, SimplifiedChinese = 14, BrazilianPortuguese = 15, + Polish = 16, + Thai = 17, } public enum Organization @@ -302,5 +355,11 @@ namespace Ryujinx.Horizon.Sdk.Ns Deny = 0, Allow = 1, } + + public enum TitleCompressionValue : byte + { + Disable = 0, + Enable = 1, + } } } diff --git a/src/Ryujinx/Systems/AppLibrary/ApplicationLibrary.cs b/src/Ryujinx/Systems/AppLibrary/ApplicationLibrary.cs index 2831802fe..a36ba7f30 100644 --- a/src/Ryujinx/Systems/AppLibrary/ApplicationLibrary.cs +++ b/src/Ryujinx/Systems/AppLibrary/ApplicationLibrary.cs @@ -1404,7 +1404,7 @@ namespace Ryujinx.Ava.Systems.AppLibrary if (string.IsNullOrWhiteSpace(data.Name)) { - foreach (ref readonly ApplicationControlProperty.ApplicationTitle controlTitle in controlData.Title) + foreach (ApplicationControlProperty.ApplicationTitle controlTitle in controlData.Title) { if (!controlTitle.NameString.IsEmpty()) { @@ -1417,7 +1417,7 @@ namespace Ryujinx.Ava.Systems.AppLibrary if (string.IsNullOrWhiteSpace(data.Developer)) { - foreach (ref readonly ApplicationControlProperty.ApplicationTitle controlTitle in controlData.Title) + foreach (ApplicationControlProperty.ApplicationTitle controlTitle in controlData.Title) { if (!controlTitle.PublisherString.IsEmpty()) { diff --git a/src/Ryujinx/Systems/Configuration/System/Language.cs b/src/Ryujinx/Systems/Configuration/System/Language.cs index dd44dff37..4cf91feb6 100644 --- a/src/Ryujinx/Systems/Configuration/System/Language.cs +++ b/src/Ryujinx/Systems/Configuration/System/Language.cs @@ -24,6 +24,8 @@ namespace Ryujinx.Ava.Systems.Configuration.System SimplifiedChinese, TraditionalChinese, BrazilianPortuguese, + Polish, + Thai, } public static class LanguageEnumHelper