using Ryujinx.Common; using Ryujinx.Memory; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; namespace Ryujinx.Cpu.Nce { /// /// Native Code Execution CPU code patch. /// public class NceCpuCodePatch { private readonly List _code; private readonly struct PatchTarget { public readonly int TextIndex; public readonly int PatchStartIndex; public readonly int PatchBranchIndex; public PatchTarget(int textIndex, int patchStartIndex, int patchBranchIndex) { TextIndex = textIndex; PatchStartIndex = patchStartIndex; PatchBranchIndex = patchBranchIndex; } } private readonly List _patchTargets; /// public ulong Size => BitUtils.AlignUp((ulong)_code.Count * sizeof(uint), 0x1000UL); public NceCpuCodePatch() { _code = new(); _patchTargets = new(); } internal void AddCode(int textIndex, IEnumerable code) { int patchStartIndex = _code.Count; _code.AddRange(code); _patchTargets.Add(new PatchTarget(textIndex, patchStartIndex, _code.Count - 1)); } /// public void Write(IVirtualMemoryManager memoryManager, ulong patchAddress, ulong textAddress) { uint[] code = _code.ToArray(); foreach (var patchTarget in _patchTargets) { ulong instPatchStartAddress = patchAddress + (ulong)patchTarget.PatchStartIndex * sizeof(uint); ulong instPatchBranchAddress = patchAddress + (ulong)patchTarget.PatchBranchIndex * sizeof(uint); ulong instTextAddress = textAddress + (ulong)patchTarget.TextIndex * sizeof(uint); uint prevInst = memoryManager.Read(instTextAddress); code[patchTarget.PatchBranchIndex] |= EncodeSImm26_2(checked((int)((long)instTextAddress - (long)instPatchBranchAddress + sizeof(uint)))); memoryManager.Write(instTextAddress, 0x14000000u | EncodeSImm26_2(checked((int)((long)instPatchStartAddress - (long)instTextAddress)))); uint newInst = memoryManager.Read(instTextAddress); } if (Size != 0) { memoryManager.Write(patchAddress, MemoryMarshal.Cast(code)); memoryManager.Reprotect(patchAddress, Size, MemoryPermission.ReadAndExecute); } } private static uint EncodeSImm26_2(int value) { uint imm = (uint)(value >> 2) & 0x3ffffff; Debug.Assert(((int)imm << 6) >> 4 == value, $"Failed to encode constant 0x{value:X}."); return imm; } } }