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