diff --git a/ARMeilleure/Memory/IMemoryManager.cs b/ARMeilleure/Memory/IMemoryManager.cs
index 33153903a5..cacfc4ac3c 100644
--- a/ARMeilleure/Memory/IMemoryManager.cs
+++ b/ARMeilleure/Memory/IMemoryManager.cs
@@ -12,6 +12,8 @@ namespace ARMeilleure.Memory
         T ReadTracked<T>(ulong va) where T : unmanaged;
         void Write<T>(ulong va, T value) where T : unmanaged;
 
+        ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false);
+
         ref T GetRef<T>(ulong va) where T : unmanaged;
 
         bool IsMapped(ulong va);
diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs
index 55a0f4d069..4b5f317387 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<byte[]>();
             _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<InfoEntry>(_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<uint> 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<uint> 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<InfoEntry>(), 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 753c01c80c..2f7a9c21f9 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<T> spanT = MemoryMarshal.CreateSpan(ref structure, 1);
-            stream.Read(MemoryMarshal.AsBytes(spanT));
+            int bytesCount = stream.Read(MemoryMarshal.AsBytes(spanT));
+
+            if (bytesCount != Unsafe.SizeOf<T>())
+            {
+                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 40a3032917..67719623e4 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<int> entries))
diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs
index d7b2b0f85d..e31ff230d5 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<ulong, FuncProfile> 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<ulong, TranslatedFunction> funcs)
+        internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(ConcurrentDictionary<ulong, TranslatedFunction> 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<byte>();
+            _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<OuterHeader>(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<Hash128>(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<ulong, FuncProfile>();
-
-                    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<byte> currentHash, ReadOnlySpan<byte> 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<ulong, FuncProfile> Deserialize(Stream stream)
         {
             return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
         }
 
+        private static ReadOnlySpan<byte> 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<Hash128>(), 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<Hash128>(), 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<byte>();
+                        _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<ulong, FuncProfile> 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<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
+
+                HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>()));
+            }
+
+            public bool IsHeaderValid()
+            {
+                Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1);
+
+                return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())) == HeaderHash;
+            }
         }
 
         [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs
index 9f88f17a9b..81af0681d0 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 40e3f64601..05db567a8f 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;