diff --git a/ARMeilleure/Memory/IMemoryManager.cs b/ARMeilleure/Memory/IMemoryManager.cs index 33153903a..cacfc4ac3 100644 --- a/ARMeilleure/Memory/IMemoryManager.cs +++ b/ARMeilleure/Memory/IMemoryManager.cs @@ -12,6 +12,8 @@ namespace ARMeilleure.Memory T ReadTracked(ulong va) where T : unmanaged; void Write(ulong va, T value) where T : unmanaged; + ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false); + ref T GetRef(ulong va) where T : unmanaged; bool IsMapped(ulong va); diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 55a0f4d06..4b5f31738 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -28,7 +28,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 2190; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 1866; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; @@ -50,8 +50,6 @@ namespace ARMeilleure.Translation.PTC private static MemoryStream _relocsStream; private static MemoryStream _unwindInfosStream; - private static BinaryWriter _infosWriter; - private static readonly ulong _outerHeaderMagic; private static readonly ulong _innerHeaderMagic; @@ -153,14 +151,10 @@ namespace ARMeilleure.Translation.PTC _codesList = new List(); _relocsStream = new MemoryStream(); _unwindInfosStream = new MemoryStream(); - - _infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true); } private static void DisposeCarriers() { - _infosWriter.Dispose(); - _infosStream.Dispose(); _codesList.Clear(); _relocsStream.Dispose(); @@ -551,46 +545,33 @@ namespace ARMeilleure.Translation.PTC return; } + long infosStreamLength = _infosStream.Length; + long relocsStreamLength = _relocsStream.Length; + long unwindInfosStreamLength = _unwindInfosStream.Length; + _infosStream.Seek(0L, SeekOrigin.Begin); _relocsStream.Seek(0L, SeekOrigin.Begin); _unwindInfosStream.Seek(0L, SeekOrigin.Begin); - using (BinaryReader infosReader = new(_infosStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader relocsReader = new(_relocsStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader unwindInfosReader = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true)) { for (int index = 0; index < GetEntriesCount(); index++) { - InfoEntry infoEntry = ReadInfo(infosReader); + InfoEntry infoEntry = DeserializeStructure(_infosStream); if (infoEntry.Stubbed) { SkipCode(index, infoEntry.CodeLength); SkipReloc(infoEntry.RelocEntriesCount); SkipUnwindInfo(unwindInfosReader); + + continue; } - else if (infoEntry.HighCq || !PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) || !value.HighCq) - { - byte[] code = ReadCode(index, infoEntry.CodeLength); - Counter callCounter = null; + bool isEntryChanged = infoEntry.Hash != ComputeHash(memory, infoEntry.Address, infoEntry.GuestSize); - if (infoEntry.RelocEntriesCount != 0) - { - RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount); - - PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable, countTable, out callCounter); - } - - UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); - - TranslatedFunction func = FastTranslate(code, callCounter, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq); - - bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func); - - Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique."); - } - else + if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq)) { infoEntry.Stubbed = true; infoEntry.CodeLength = 0; @@ -599,15 +580,43 @@ namespace ARMeilleure.Translation.PTC StubCode(index); StubReloc(infoEntry.RelocEntriesCount); StubUnwindInfo(unwindInfosReader); + + if (isEntryChanged) + { + PtcJumpTable.Clean(infoEntry.Address); + + Logger.Info?.Print(LogClass.Ptc, $"Invalidated translated function (address: 0x{infoEntry.Address:X16})"); + } + + continue; } + + byte[] code = ReadCode(index, infoEntry.CodeLength); + + Counter callCounter = null; + + if (infoEntry.RelocEntriesCount != 0) + { + RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount); + + PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable, countTable, out callCounter); + } + + UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); + + TranslatedFunction func = FastTranslate(code, callCounter, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq); + + bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func); + + Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique."); } } - if (_infosStream.Position < _infosStream.Length || - _relocsStream.Position < _relocsStream.Length || - _unwindInfosStream.Position < _unwindInfosStream.Length) + if (_infosStream.Length != infosStreamLength || _infosStream.Position != infosStreamLength || + _relocsStream.Length != relocsStreamLength || _relocsStream.Position != relocsStreamLength || + _unwindInfosStream.Length != unwindInfosStreamLength || _unwindInfosStream.Position != unwindInfosStreamLength) { - throw new Exception("Could not reach the end of one or more memory streams."); + throw new Exception("The length of a memory stream has changed, or its position has not reached or has exceeded its end."); } jumpTable.Initialize(PtcJumpTable, funcs); @@ -623,20 +632,6 @@ namespace ARMeilleure.Translation.PTC return _codesList.Count; } - private static InfoEntry ReadInfo(BinaryReader infosReader) - { - InfoEntry infoEntry = new InfoEntry(); - - infoEntry.Address = infosReader.ReadUInt64(); - infoEntry.GuestSize = infosReader.ReadUInt64(); - infoEntry.HighCq = infosReader.ReadBoolean(); - infoEntry.Stubbed = infosReader.ReadBoolean(); - infoEntry.CodeLength = infosReader.ReadInt32(); - infoEntry.RelocEntriesCount = infosReader.ReadInt32(); - - return infoEntry; - } - [Conditional("DEBUG")] private static void SkipCode(int index, int codeLength) { @@ -764,15 +759,9 @@ namespace ARMeilleure.Translation.PTC private static void UpdateInfo(InfoEntry infoEntry) { - _infosStream.Seek(-InfoEntry.Stride, SeekOrigin.Current); + _infosStream.Seek(-Unsafe.SizeOf(), SeekOrigin.Current); - // WriteInfo. - _infosWriter.Write((ulong)infoEntry.Address); - _infosWriter.Write((ulong)infoEntry.GuestSize); - _infosWriter.Write((bool)infoEntry.HighCq); - _infosWriter.Write((bool)infoEntry.Stubbed); - _infosWriter.Write((int)infoEntry.CodeLength); - _infosWriter.Write((int)infoEntry.RelocEntriesCount); + SerializeStructure(_infosStream, infoEntry); } private static void StubCode(int index) @@ -844,7 +833,7 @@ namespace ARMeilleure.Translation.PTC Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); - TranslatedFunction func = Translator.Translate(memory, jumpTable, countTable, address, item.mode, item.highCq); + TranslatedFunction func = Translator.Translate(memory, jumpTable, countTable, address, item.funcProfile.Mode, item.funcProfile.HighCq); bool isAddressUnique = funcs.TryAdd(address, func); @@ -919,17 +908,26 @@ namespace ARMeilleure.Translation.PTC while (!endEvent.WaitOne(refreshRate)); } - internal static void WriteInfoCodeRelocUnwindInfo(ulong address, ulong guestSize, bool highCq, PtcInfo ptcInfo) + internal static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize) + { + return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize)))); + } + + internal static void WriteInfoCodeRelocUnwindInfo(ulong address, ulong guestSize, Hash128 hash, bool highCq, PtcInfo ptcInfo) { lock (_lock) { - // WriteInfo. - _infosWriter.Write((ulong)address); // InfoEntry.Address - _infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize - _infosWriter.Write((bool)highCq); // InfoEntry.HighCq - _infosWriter.Write((bool)false); // InfoEntry.Stubbed - _infosWriter.Write((int)ptcInfo.Code.Length); // InfoEntry.CodeLength - _infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount + InfoEntry infoEntry = new InfoEntry(); + + infoEntry.Address = address; + infoEntry.GuestSize = guestSize; + infoEntry.Hash = hash; + infoEntry.HighCq = highCq; + infoEntry.Stubbed = false; + infoEntry.CodeLength = ptcInfo.Code.Length; + infoEntry.RelocEntriesCount = ptcInfo.RelocEntriesCount; + + SerializeStructure(_infosStream, infoEntry); WriteCode(ptcInfo.Code.AsSpan()); @@ -946,7 +944,7 @@ namespace ARMeilleure.Translation.PTC _codesList.Add(code.ToArray()); } - private static bool GetEndianness() + internal static bool GetEndianness() { return BitConverter.IsLittleEndian; } @@ -1032,12 +1030,12 @@ namespace ARMeilleure.Translation.PTC } } + [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 42*/)] private struct InfoEntry { - public const int Stride = 26; // Bytes. - public ulong Address; public ulong GuestSize; + public Hash128 Hash; public bool HighCq; public bool Stubbed; public int CodeLength; diff --git a/ARMeilleure/Translation/PTC/PtcFormatter.cs b/ARMeilleure/Translation/PTC/PtcFormatter.cs index 753c01c80..2f7a9c21f 100644 --- a/ARMeilleure/Translation/PTC/PtcFormatter.cs +++ b/ARMeilleure/Translation/PTC/PtcFormatter.cs @@ -50,7 +50,12 @@ namespace ARMeilleure.Translation.PTC T structure = default(T); Span spanT = MemoryMarshal.CreateSpan(ref structure, 1); - stream.Read(MemoryMarshal.AsBytes(spanT)); + int bytesCount = stream.Read(MemoryMarshal.AsBytes(spanT)); + + if (bytesCount != Unsafe.SizeOf()) + { + throw new EndOfStreamException(); + } return structure; } @@ -130,7 +135,12 @@ namespace ARMeilleure.Translation.PTC T[] item = new T[itemLength]; - stream.Read(MemoryMarshal.AsBytes(item.AsSpan())); + int bytesCount = stream.Read(MemoryMarshal.AsBytes(item.AsSpan())); + + if (bytesCount != itemLength) + { + throw new EndOfStreamException(); + } list.Add(item); } diff --git a/ARMeilleure/Translation/PTC/PtcJumpTable.cs b/ARMeilleure/Translation/PTC/PtcJumpTable.cs index 40a303291..67719623e 100644 --- a/ARMeilleure/Translation/PTC/PtcJumpTable.cs +++ b/ARMeilleure/Translation/PTC/PtcJumpTable.cs @@ -129,7 +129,6 @@ namespace ARMeilleure.Translation.PTC } } - // For future use. public void Clean(ulong guestAddress) { if (Owners.TryGetValue(guestAddress, out List entries)) diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs index d7b2b0f85..e31ff230d 100644 --- a/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -1,13 +1,15 @@ using ARMeilleure.State; +using Ryujinx.Common; using Ryujinx.Common.Logging; using System; +using System.Buffers.Binary; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography; using System.Threading; using static ARMeilleure.Translation.PTC.PtcFormatter; @@ -16,9 +18,9 @@ namespace ARMeilleure.Translation.PTC { public static class PtcProfiler { - private const string HeaderMagic = "Phd"; + private const string OuterHeaderMagicString = "Pohd\0\0\0\0"; - private const uint InternalVersion = 1713; //! Not to be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 1866; //! Not to be incremented manually for each change to the ARMeilleure project. private const int SaveInterval = 30; // Seconds. @@ -26,13 +28,15 @@ namespace ARMeilleure.Translation.PTC private static readonly System.Timers.Timer _timer; + private static readonly ulong _outerHeaderMagic; + private static readonly ManualResetEvent _waitEvent; private static readonly object _lock; private static bool _disposed; - private static byte[] _lastHash; + private static Hash128 _lastHash; internal static Dictionary ProfiledFuncs { get; private set; } @@ -46,6 +50,8 @@ namespace ARMeilleure.Translation.PTC _timer = new System.Timers.Timer((double)SaveInterval * 1000d); _timer.Elapsed += PreSave; + _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); + _waitEvent = new ManualResetEvent(true); _lock = new object(); @@ -90,17 +96,15 @@ namespace ARMeilleure.Translation.PTC return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize; } - internal static ConcurrentQueue<(ulong address, ExecutionMode mode, bool highCq)> GetProfiledFuncsToTranslate(ConcurrentDictionary funcs) + internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(ConcurrentDictionary funcs) { - var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, ExecutionMode mode, bool highCq)>(); + var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>(); foreach (var profiledFunc in ProfiledFuncs) { - ulong address = profiledFunc.Key; - - if (!funcs.ContainsKey(address)) + if (!funcs.ContainsKey(profiledFunc.Key)) { - profiledFuncsToTranslate.Enqueue((address, profiledFunc.Value.Mode, profiledFunc.Value.HighCq)); + profiledFuncsToTranslate.Enqueue((profiledFunc.Key, profiledFunc.Value)); } } @@ -115,7 +119,7 @@ namespace ARMeilleure.Translation.PTC internal static void PreLoad() { - _lastHash = Array.Empty(); + _lastHash = default; string fileNameActual = string.Concat(Ptc.CachePathActual, ".info"); string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info"); @@ -141,70 +145,75 @@ namespace ARMeilleure.Translation.PTC private static bool Load(string fileName, bool isBackup) { - using (FileStream compressedStream = new FileStream(fileName, FileMode.Open)) - using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true)) - using (MemoryStream stream = new MemoryStream()) - using (MD5 md5 = MD5.Create()) + using (FileStream compressedStream = new(fileName, FileMode.Open)) + using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true)) { - try - { - deflateStream.CopyTo(stream); - } - catch + OuterHeader outerHeader = DeserializeStructure(compressedStream); + + if (!outerHeader.IsHeaderValid()) { InvalidateCompressedStream(compressedStream); return false; } - int hashSize = md5.HashSize / 8; - - stream.Seek(0L, SeekOrigin.Begin); - - byte[] currentHash = new byte[hashSize]; - stream.Read(currentHash, 0, hashSize); - - byte[] expectedHash = md5.ComputeHash(stream); - - if (!CompareHash(currentHash, expectedHash)) + if (outerHeader.Magic != _outerHeaderMagic) { InvalidateCompressedStream(compressedStream); return false; } - stream.Seek((long)hashSize, SeekOrigin.Begin); - - Header header = ReadHeader(stream); - - if (header.Magic != HeaderMagic) + if (outerHeader.InfoFileVersion != InternalVersion) { InvalidateCompressedStream(compressedStream); return false; } - if (header.InfoFileVersion != InternalVersion) + if (outerHeader.Endianness != Ptc.GetEndianness()) { InvalidateCompressedStream(compressedStream); return false; } - try + using (MemoryStream stream = new MemoryStream()) { + Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); + + try + { + deflateStream.CopyTo(stream); + } + catch + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + Debug.Assert(stream.Position == stream.Length); + + stream.Seek(0L, SeekOrigin.Begin); + + Hash128 expectedHash = DeserializeStructure(stream); + + Hash128 actualHash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); + + if (actualHash != expectedHash) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + ProfiledFuncs = Deserialize(stream); + + Debug.Assert(stream.Position == stream.Length); + + _lastHash = actualHash; } - catch - { - ProfiledFuncs = new Dictionary(); - - InvalidateCompressedStream(compressedStream); - - return false; - } - - _lastHash = expectedHash; } long fileSize = new FileInfo(fileName).Length; @@ -214,30 +223,16 @@ namespace ARMeilleure.Translation.PTC return true; } - private static bool CompareHash(ReadOnlySpan currentHash, ReadOnlySpan expectedHash) - { - return currentHash.SequenceEqual(expectedHash); - } - - private static Header ReadHeader(Stream stream) - { - using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true)) - { - Header header = new Header(); - - header.Magic = headerReader.ReadString(); - - header.InfoFileVersion = headerReader.ReadUInt32(); - - return header; - } - } - private static Dictionary Deserialize(Stream stream) { return DeserializeDictionary(stream, (stream) => DeserializeStructure(stream)); } + private static ReadOnlySpan GetReadOnlySpan(MemoryStream memoryStream) + { + return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position); + } + private static void InvalidateCompressedStream(FileStream compressedStream) { compressedStream.SetLength(0L); @@ -266,14 +261,20 @@ namespace ARMeilleure.Translation.PTC { int profiledFuncsCount; + OuterHeader outerHeader = new OuterHeader(); + + outerHeader.Magic = _outerHeaderMagic; + + outerHeader.InfoFileVersion = InternalVersion; + outerHeader.Endianness = Ptc.GetEndianness(); + + outerHeader.SetHeaderHash(); + using (MemoryStream stream = new MemoryStream()) - using (MD5 md5 = MD5.Create()) { - int hashSize = md5.HashSize / 8; + Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); - stream.Seek((long)hashSize, SeekOrigin.Begin); - - WriteHeader(stream); + stream.Seek((long)Unsafe.SizeOf(), SeekOrigin.Begin); lock (_lock) { @@ -282,22 +283,26 @@ namespace ARMeilleure.Translation.PTC profiledFuncsCount = ProfiledFuncs.Count; } - stream.Seek((long)hashSize, SeekOrigin.Begin); - byte[] hash = md5.ComputeHash(stream); + Debug.Assert(stream.Position == stream.Length); + + stream.Seek((long)Unsafe.SizeOf(), SeekOrigin.Begin); + Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); stream.Seek(0L, SeekOrigin.Begin); - stream.Write(hash, 0, hashSize); + SerializeStructure(stream, hash); - if (CompareHash(hash, _lastHash)) + if (hash == _lastHash) { return; } - using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate)) - using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true)) + using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate)) + using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true)) { try { + SerializeStructure(compressedStream, outerHeader); + stream.WriteTo(deflateStream); _lastHash = hash; @@ -306,7 +311,7 @@ namespace ARMeilleure.Translation.PTC { compressedStream.Position = 0L; - _lastHash = Array.Empty(); + _lastHash = default; } if (compressedStream.Position < compressedStream.Length) @@ -324,26 +329,35 @@ namespace ARMeilleure.Translation.PTC } } - private static void WriteHeader(Stream stream) - { - using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true)) - { - headerWriter.Write((string)HeaderMagic); // Header.Magic - - headerWriter.Write((uint)InternalVersion); // Header.InfoFileVersion - } - } - private static void Serialize(Stream stream, Dictionary profiledFuncs) { SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure)); } - private struct Header + [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)] + private struct OuterHeader { - public string Magic; + public ulong Magic; public uint InfoFileVersion; + + public bool Endianness; + + public Hash128 HeaderHash; + + public void SetHeaderHash() + { + Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); + + HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf() - Unsafe.SizeOf())); + } + + public bool IsHeaderValid() + { + Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); + + return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf() - Unsafe.SizeOf())) == HeaderHash; + } } [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)] diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 9f88f17a9..81af0681d 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -7,6 +7,7 @@ using ARMeilleure.Memory; using ARMeilleure.State; using ARMeilleure.Translation.Cache; using ARMeilleure.Translation.PTC; +using Ryujinx.Common; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -282,7 +283,9 @@ namespace ARMeilleure.Translation ResetPool(highCq ? 1 : 0); - Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, highCq, ptcInfo); + Hash128 hash = Ptc.ComputeHash(memory, address, funcSize); + + Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, hash, highCq, ptcInfo); } return new TranslatedFunction(func, counter, funcSize, highCq); diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 40e3f6460..05db567a8 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -507,20 +507,18 @@ namespace Ryujinx.HLE.HOS metaData = modLoadResult.Npdm; } - bool hasPatches = _fileSystem.ModLoader.ApplyNsoPatches(TitleId, programs); + _fileSystem.ModLoader.ApplyNsoPatches(TitleId, programs); _contentManager.LoadEntries(_device); bool usePtc = _device.System.EnablePtc; - // don't use PTC if exefs files have been replaced + // Don't use PPTC if ExeFs files have been replaced. usePtc &= !modLoadResult.Modified; - // don't use PTC if exefs files have been patched - usePtc &= !hasPatches; if (_device.System.EnablePtc && !usePtc) { - Logger.Warning?.Print(LogClass.Ptc, $"Detected exefs modifications. PPTC disabled."); + Logger.Warning?.Print(LogClass.Ptc, $"Detected unsupported ExeFs modifications. PPTC disabled."); } Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText;