PPTC meets ExeFS Patching. (#1865)

* PPTC meets ExeFS Patching.

* InternalVersion = 1865

* Ready!

* Optimized the PtcProfiler Load/Save methods.
This commit is contained in:
LDj3SNuD 2021-05-13 20:05:15 +02:00 committed by GitHub
parent a8022ca3a1
commit 57ea3f93a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 190 additions and 166 deletions

View file

@ -12,6 +12,8 @@ namespace ARMeilleure.Memory
T ReadTracked<T>(ulong va) where T : unmanaged; T ReadTracked<T>(ulong va) where T : unmanaged;
void Write<T>(ulong va, T value) 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; ref T GetRef<T>(ulong va) where T : unmanaged;
bool IsMapped(ulong va); bool IsMapped(ulong va);

View file

@ -28,7 +28,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\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 ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";
@ -50,8 +50,6 @@ namespace ARMeilleure.Translation.PTC
private static MemoryStream _relocsStream; private static MemoryStream _relocsStream;
private static MemoryStream _unwindInfosStream; private static MemoryStream _unwindInfosStream;
private static BinaryWriter _infosWriter;
private static readonly ulong _outerHeaderMagic; private static readonly ulong _outerHeaderMagic;
private static readonly ulong _innerHeaderMagic; private static readonly ulong _innerHeaderMagic;
@ -153,14 +151,10 @@ namespace ARMeilleure.Translation.PTC
_codesList = new List<byte[]>(); _codesList = new List<byte[]>();
_relocsStream = new MemoryStream(); _relocsStream = new MemoryStream();
_unwindInfosStream = new MemoryStream(); _unwindInfosStream = new MemoryStream();
_infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
} }
private static void DisposeCarriers() private static void DisposeCarriers()
{ {
_infosWriter.Dispose();
_infosStream.Dispose(); _infosStream.Dispose();
_codesList.Clear(); _codesList.Clear();
_relocsStream.Dispose(); _relocsStream.Dispose();
@ -551,46 +545,33 @@ namespace ARMeilleure.Translation.PTC
return; return;
} }
long infosStreamLength = _infosStream.Length;
long relocsStreamLength = _relocsStream.Length;
long unwindInfosStreamLength = _unwindInfosStream.Length;
_infosStream.Seek(0L, SeekOrigin.Begin); _infosStream.Seek(0L, SeekOrigin.Begin);
_relocsStream.Seek(0L, SeekOrigin.Begin); _relocsStream.Seek(0L, SeekOrigin.Begin);
_unwindInfosStream.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 relocsReader = new(_relocsStream, EncodingCache.UTF8NoBOM, true))
using (BinaryReader unwindInfosReader = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader unwindInfosReader = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
{ {
for (int index = 0; index < GetEntriesCount(); index++) for (int index = 0; index < GetEntriesCount(); index++)
{ {
InfoEntry infoEntry = ReadInfo(infosReader); InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
if (infoEntry.Stubbed) if (infoEntry.Stubbed)
{ {
SkipCode(index, infoEntry.CodeLength); SkipCode(index, infoEntry.CodeLength);
SkipReloc(infoEntry.RelocEntriesCount); SkipReloc(infoEntry.RelocEntriesCount);
SkipUnwindInfo(unwindInfosReader); 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) if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
{
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
{ {
infoEntry.Stubbed = true; infoEntry.Stubbed = true;
infoEntry.CodeLength = 0; infoEntry.CodeLength = 0;
@ -599,15 +580,43 @@ namespace ARMeilleure.Translation.PTC
StubCode(index); StubCode(index);
StubReloc(infoEntry.RelocEntriesCount); StubReloc(infoEntry.RelocEntriesCount);
StubUnwindInfo(unwindInfosReader); 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 || if (_infosStream.Length != infosStreamLength || _infosStream.Position != infosStreamLength ||
_relocsStream.Position < _relocsStream.Length || _relocsStream.Length != relocsStreamLength || _relocsStream.Position != relocsStreamLength ||
_unwindInfosStream.Position < _unwindInfosStream.Length) _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); jumpTable.Initialize(PtcJumpTable, funcs);
@ -623,20 +632,6 @@ namespace ARMeilleure.Translation.PTC
return _codesList.Count; 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")] [Conditional("DEBUG")]
private static void SkipCode(int index, int codeLength) private static void SkipCode(int index, int codeLength)
{ {
@ -764,15 +759,9 @@ namespace ARMeilleure.Translation.PTC
private static void UpdateInfo(InfoEntry infoEntry) private static void UpdateInfo(InfoEntry infoEntry)
{ {
_infosStream.Seek(-InfoEntry.Stride, SeekOrigin.Current); _infosStream.Seek(-Unsafe.SizeOf<InfoEntry>(), SeekOrigin.Current);
// WriteInfo. SerializeStructure(_infosStream, infoEntry);
_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);
} }
private static void StubCode(int index) private static void StubCode(int index)
@ -844,7 +833,7 @@ namespace ARMeilleure.Translation.PTC
Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); 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); bool isAddressUnique = funcs.TryAdd(address, func);
@ -919,17 +908,26 @@ namespace ARMeilleure.Translation.PTC
while (!endEvent.WaitOne(refreshRate)); 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) lock (_lock)
{ {
// WriteInfo. InfoEntry infoEntry = new InfoEntry();
_infosWriter.Write((ulong)address); // InfoEntry.Address
_infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize infoEntry.Address = address;
_infosWriter.Write((bool)highCq); // InfoEntry.HighCq infoEntry.GuestSize = guestSize;
_infosWriter.Write((bool)false); // InfoEntry.Stubbed infoEntry.Hash = hash;
_infosWriter.Write((int)ptcInfo.Code.Length); // InfoEntry.CodeLength infoEntry.HighCq = highCq;
_infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount infoEntry.Stubbed = false;
infoEntry.CodeLength = ptcInfo.Code.Length;
infoEntry.RelocEntriesCount = ptcInfo.RelocEntriesCount;
SerializeStructure(_infosStream, infoEntry);
WriteCode(ptcInfo.Code.AsSpan()); WriteCode(ptcInfo.Code.AsSpan());
@ -946,7 +944,7 @@ namespace ARMeilleure.Translation.PTC
_codesList.Add(code.ToArray()); _codesList.Add(code.ToArray());
} }
private static bool GetEndianness() internal static bool GetEndianness()
{ {
return BitConverter.IsLittleEndian; return BitConverter.IsLittleEndian;
} }
@ -1032,12 +1030,12 @@ namespace ARMeilleure.Translation.PTC
} }
} }
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 42*/)]
private struct InfoEntry private struct InfoEntry
{ {
public const int Stride = 26; // Bytes.
public ulong Address; public ulong Address;
public ulong GuestSize; public ulong GuestSize;
public Hash128 Hash;
public bool HighCq; public bool HighCq;
public bool Stubbed; public bool Stubbed;
public int CodeLength; public int CodeLength;

View file

@ -50,7 +50,12 @@ namespace ARMeilleure.Translation.PTC
T structure = default(T); T structure = default(T);
Span<T> spanT = MemoryMarshal.CreateSpan(ref structure, 1); 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; return structure;
} }
@ -130,7 +135,12 @@ namespace ARMeilleure.Translation.PTC
T[] item = new T[itemLength]; 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); list.Add(item);
} }

View file

@ -129,7 +129,6 @@ namespace ARMeilleure.Translation.PTC
} }
} }
// For future use.
public void Clean(ulong guestAddress) public void Clean(ulong guestAddress)
{ {
if (Owners.TryGetValue(guestAddress, out List<int> entries)) if (Owners.TryGetValue(guestAddress, out List<int> entries))

View file

@ -1,13 +1,15 @@
using ARMeilleure.State; using ARMeilleure.State;
using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using System;
using System.Buffers.Binary;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading; using System.Threading;
using static ARMeilleure.Translation.PTC.PtcFormatter; using static ARMeilleure.Translation.PTC.PtcFormatter;
@ -16,9 +18,9 @@ namespace ARMeilleure.Translation.PTC
{ {
public static class PtcProfiler 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. private const int SaveInterval = 30; // Seconds.
@ -26,13 +28,15 @@ namespace ARMeilleure.Translation.PTC
private static readonly System.Timers.Timer _timer; private static readonly System.Timers.Timer _timer;
private static readonly ulong _outerHeaderMagic;
private static readonly ManualResetEvent _waitEvent; private static readonly ManualResetEvent _waitEvent;
private static readonly object _lock; private static readonly object _lock;
private static bool _disposed; private static bool _disposed;
private static byte[] _lastHash; private static Hash128 _lastHash;
internal static Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; } 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 = new System.Timers.Timer((double)SaveInterval * 1000d);
_timer.Elapsed += PreSave; _timer.Elapsed += PreSave;
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
_waitEvent = new ManualResetEvent(true); _waitEvent = new ManualResetEvent(true);
_lock = new object(); _lock = new object();
@ -90,17 +96,15 @@ namespace ARMeilleure.Translation.PTC
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize; 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) foreach (var profiledFunc in ProfiledFuncs)
{ {
ulong address = profiledFunc.Key; if (!funcs.ContainsKey(profiledFunc.Key))
if (!funcs.ContainsKey(address))
{ {
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() internal static void PreLoad()
{ {
_lastHash = Array.Empty<byte>(); _lastHash = default;
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info"); string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info"); string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
@ -141,70 +145,75 @@ namespace ARMeilleure.Translation.PTC
private static bool Load(string fileName, bool isBackup) private static bool Load(string fileName, bool isBackup)
{ {
using (FileStream compressedStream = new FileStream(fileName, FileMode.Open)) using (FileStream compressedStream = new(fileName, FileMode.Open))
using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true)) using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
using (MemoryStream stream = new MemoryStream())
using (MD5 md5 = MD5.Create())
{ {
try OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream);
{
deflateStream.CopyTo(stream); if (!outerHeader.IsHeaderValid())
}
catch
{ {
InvalidateCompressedStream(compressedStream); InvalidateCompressedStream(compressedStream);
return false; return false;
} }
int hashSize = md5.HashSize / 8; if (outerHeader.Magic != _outerHeaderMagic)
stream.Seek(0L, SeekOrigin.Begin);
byte[] currentHash = new byte[hashSize];
stream.Read(currentHash, 0, hashSize);
byte[] expectedHash = md5.ComputeHash(stream);
if (!CompareHash(currentHash, expectedHash))
{ {
InvalidateCompressedStream(compressedStream); InvalidateCompressedStream(compressedStream);
return false; return false;
} }
stream.Seek((long)hashSize, SeekOrigin.Begin); if (outerHeader.InfoFileVersion != InternalVersion)
Header header = ReadHeader(stream);
if (header.Magic != HeaderMagic)
{ {
InvalidateCompressedStream(compressedStream); InvalidateCompressedStream(compressedStream);
return false; return false;
} }
if (header.InfoFileVersion != InternalVersion) if (outerHeader.Endianness != Ptc.GetEndianness())
{ {
InvalidateCompressedStream(compressedStream); InvalidateCompressedStream(compressedStream);
return false; 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); 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; long fileSize = new FileInfo(fileName).Length;
@ -214,30 +223,16 @@ namespace ARMeilleure.Translation.PTC
return true; 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) private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream)
{ {
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(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) private static void InvalidateCompressedStream(FileStream compressedStream)
{ {
compressedStream.SetLength(0L); compressedStream.SetLength(0L);
@ -266,14 +261,20 @@ namespace ARMeilleure.Translation.PTC
{ {
int profiledFuncsCount; int profiledFuncsCount;
OuterHeader outerHeader = new OuterHeader();
outerHeader.Magic = _outerHeaderMagic;
outerHeader.InfoFileVersion = InternalVersion;
outerHeader.Endianness = Ptc.GetEndianness();
outerHeader.SetHeaderHash();
using (MemoryStream stream = new MemoryStream()) 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); stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
WriteHeader(stream);
lock (_lock) lock (_lock)
{ {
@ -282,22 +283,26 @@ namespace ARMeilleure.Translation.PTC
profiledFuncsCount = ProfiledFuncs.Count; profiledFuncsCount = ProfiledFuncs.Count;
} }
stream.Seek((long)hashSize, SeekOrigin.Begin); Debug.Assert(stream.Position == stream.Length);
byte[] hash = md5.ComputeHash(stream);
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
stream.Seek(0L, SeekOrigin.Begin); stream.Seek(0L, SeekOrigin.Begin);
stream.Write(hash, 0, hashSize); SerializeStructure(stream, hash);
if (CompareHash(hash, _lastHash)) if (hash == _lastHash)
{ {
return; return;
} }
using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate)) using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate))
using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true)) using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true))
{ {
try try
{ {
SerializeStructure(compressedStream, outerHeader);
stream.WriteTo(deflateStream); stream.WriteTo(deflateStream);
_lastHash = hash; _lastHash = hash;
@ -306,7 +311,7 @@ namespace ARMeilleure.Translation.PTC
{ {
compressedStream.Position = 0L; compressedStream.Position = 0L;
_lastHash = Array.Empty<byte>(); _lastHash = default;
} }
if (compressedStream.Position < compressedStream.Length) 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) private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
{ {
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure)); 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 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*/)] [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]

View file

@ -7,6 +7,7 @@ using ARMeilleure.Memory;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation.Cache; using ARMeilleure.Translation.Cache;
using ARMeilleure.Translation.PTC; using ARMeilleure.Translation.PTC;
using Ryujinx.Common;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@ -282,7 +283,9 @@ namespace ARMeilleure.Translation
ResetPool(highCq ? 1 : 0); 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); return new TranslatedFunction(func, counter, funcSize, highCq);

View file

@ -507,20 +507,18 @@ namespace Ryujinx.HLE.HOS
metaData = modLoadResult.Npdm; metaData = modLoadResult.Npdm;
} }
bool hasPatches = _fileSystem.ModLoader.ApplyNsoPatches(TitleId, programs); _fileSystem.ModLoader.ApplyNsoPatches(TitleId, programs);
_contentManager.LoadEntries(_device); _contentManager.LoadEntries(_device);
bool usePtc = _device.System.EnablePtc; 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; usePtc &= !modLoadResult.Modified;
// don't use PTC if exefs files have been patched
usePtc &= !hasPatches;
if (_device.System.EnablePtc && !usePtc) 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; Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText;