// Copyright (C) 2026 SharpEmu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later using SharpEmu.HLE; namespace SharpEmu.Core.Cpu; public sealed class TrackedCpuMemory : ICpuMemory, ITrackedCpuMemory, IGuestMemoryAllocator, ICpuMemoryWrapper { private readonly ICpuMemory _inner; public TrackedCpuMemory(ICpuMemory inner) { _inner = inner ?? throw new ArgumentNullException(nameof(inner)); } public CpuMemoryAccessFailure? LastFailure { get; private set; } public ICpuMemory Inner => _inner; public string DescribeReadRange(ulong address, ulong size) => _inner.DescribeReadRange(address, size); public bool TryRead(ulong virtualAddress, Span destination) { var result = _inner.TryRead(virtualAddress, destination); if (!result) { LastFailure = new CpuMemoryAccessFailure(virtualAddress, destination.Length, isWrite: false); } return result; } public bool TryWrite(ulong virtualAddress, ReadOnlySpan source) { var result = _inner.TryWrite(virtualAddress, source); if (!result) { LastFailure = new CpuMemoryAccessFailure(virtualAddress, source.Length, isWrite: true); } return result; } public bool TryCompare( ulong virtualAddress, ReadOnlySpan expected, out bool equal) => _inner.TryCompare(virtualAddress, expected, out equal); public bool TryCopy(ulong destinationAddress, ulong sourceAddress, ulong length) => _inner.TryCopy(destinationAddress, sourceAddress, length); public bool CanRead(ulong address, ulong size) => _inner.CanRead(address, size); public bool TryAllocateGuestMemory(ulong size, ulong alignment, out ulong address) { if (_inner is IGuestMemoryAllocator allocator) { return allocator.TryAllocateGuestMemory(size, alignment, out address); } address = 0; return false; } public bool TryFreeGuestMemory(ulong address) { return _inner is IGuestMemoryAllocator allocator && allocator.TryFreeGuestMemory(address); } }