This commit is contained in:
Jay M. White 2025-01-17 21:26:51 +01:00 committed by GitHub
commit 52401d6808
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 215 additions and 31 deletions

View File

@ -22,6 +22,7 @@ struct WindowsMemoryFunctions
void* m_address_UnmapViewOfFileEx = nullptr; void* m_address_UnmapViewOfFileEx = nullptr;
void* m_address_VirtualAlloc2 = nullptr; void* m_address_VirtualAlloc2 = nullptr;
void* m_address_MapViewOfFile3 = nullptr; void* m_address_MapViewOfFile3 = nullptr;
void* m_address_VirtualProtect = nullptr;
}; };
#endif #endif
@ -111,6 +112,15 @@ public:
/// ///
void UnmapFromMemoryRegion(void* view, size_t size); void UnmapFromMemoryRegion(void* view, size_t size);
///
/// Virtual protect a section from the memory region previously mapped by CreateView.
///
/// @param data Pointer to data to protect.
/// @param size Size of the protection.
/// @param flag What new permission to protect with.
///
bool VirtualProtectMemoryRegion(u8* data, size_t size, u64 flag);
private: private:
#ifdef _WIN32 #ifdef _WIN32
WindowsMemoryRegion* EnsureSplitRegionForMapping(void* address, size_t size); WindowsMemoryRegion* EnsureSplitRegionForMapping(void* address, size_t size);

View File

@ -34,6 +34,9 @@ using PMapViewOfFile3 = PVOID(WINAPI*)(HANDLE FileMapping, HANDLE Process, PVOID
using PUnmapViewOfFileEx = BOOL(WINAPI*)(PVOID BaseAddress, ULONG UnmapFlags); using PUnmapViewOfFileEx = BOOL(WINAPI*)(PVOID BaseAddress, ULONG UnmapFlags);
using PVirtualProtect = BOOL(WINAPI*)(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect,
PDWORD lpflOldProtect);
using PIsApiSetImplemented = BOOL(APIENTRY*)(PCSTR Contract); using PIsApiSetImplemented = BOOL(APIENTRY*)(PCSTR Contract);
namespace Common namespace Common
@ -78,11 +81,14 @@ static bool InitWindowsMemoryFunctions(WindowsMemoryFunctions* functions)
functions->m_api_ms_win_core_memory_l1_1_6_handle.GetSymbolAddress("MapViewOfFile3FromApp"); functions->m_api_ms_win_core_memory_l1_1_6_handle.GetSymbolAddress("MapViewOfFile3FromApp");
void* const address_UnmapViewOfFileEx = void* const address_UnmapViewOfFileEx =
functions->m_kernel32_handle.GetSymbolAddress("UnmapViewOfFileEx"); functions->m_kernel32_handle.GetSymbolAddress("UnmapViewOfFileEx");
void* const address_VirtualProtect =
functions->m_kernel32_handle.GetSymbolAddress("VirtualProtect");
if (address_VirtualAlloc2 && address_MapViewOfFile3 && address_UnmapViewOfFileEx) if (address_VirtualAlloc2 && address_MapViewOfFile3 && address_UnmapViewOfFileEx)
{ {
functions->m_address_VirtualAlloc2 = address_VirtualAlloc2; functions->m_address_VirtualAlloc2 = address_VirtualAlloc2;
functions->m_address_MapViewOfFile3 = address_MapViewOfFile3; functions->m_address_MapViewOfFile3 = address_MapViewOfFile3;
functions->m_address_UnmapViewOfFileEx = address_UnmapViewOfFileEx; functions->m_address_UnmapViewOfFileEx = address_UnmapViewOfFileEx;
functions->m_address_VirtualProtect = address_VirtualProtect;
return true; return true;
} }
@ -209,6 +215,13 @@ void MemArena::ReleaseMemoryRegion()
} }
} }
bool MemArena::VirtualProtectMemoryRegion(u8* data, size_t size, u64 flag)
{
DWORD lpflOldProtect = 0;
return static_cast<PVirtualProtect>(m_memory_functions.m_address_VirtualProtect)(
data, size, flag, &lpflOldProtect);
}
WindowsMemoryRegion* MemArena::EnsureSplitRegionForMapping(void* start_address, size_t size) WindowsMemoryRegion* MemArena::EnsureSplitRegionForMapping(void* start_address, size_t size)
{ {
u8* const address = static_cast<u8*>(start_address); u8* const address = static_cast<u8*>(start_address);

View File

@ -289,4 +289,15 @@ size_t MemPhysical()
#endif #endif
} }
size_t PageSize()
{
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return sysInfo.dwPageSize;
#else
return sysconf(_SC_PAGESIZE);
#endif
}
} // namespace Common } // namespace Common

View File

@ -34,5 +34,6 @@ bool ReadProtectMemory(void* ptr, size_t size);
bool WriteProtectMemory(void* ptr, size_t size, bool executable = false); bool WriteProtectMemory(void* ptr, size_t size, bool executable = false);
bool UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); bool UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
size_t MemPhysical(); size_t MemPhysical();
size_t PageSize();
} // namespace Common } // namespace Common

View File

@ -75,6 +75,7 @@
#include "Core/State.h" #include "Core/State.h"
#include "Core/System.h" #include "Core/System.h"
#include "Core/WiiRoot.h" #include "Core/WiiRoot.h"
#include "Core/HW/Memmap.h"
#ifdef USE_MEMORYWATCHER #ifdef USE_MEMORYWATCHER
#include "Core/MemoryWatcher.h" #include "Core/MemoryWatcher.h"
@ -385,8 +386,9 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
// The JIT need to be able to intercept faults, both for fastmem and for the BLR optimization. // The JIT need to be able to intercept faults, both for fastmem and for the BLR optimization.
const bool exception_handler = EMM::IsExceptionHandlerSupported(); const bool exception_handler = EMM::IsExceptionHandlerSupported();
if (exception_handler) if (exception_handler)
{
EMM::InstallExceptionHandler(); EMM::InstallExceptionHandler();
}
#ifdef USE_MEMORYWATCHER #ifdef USE_MEMORYWATCHER
s_memory_watcher = std::make_unique<MemoryWatcher>(); s_memory_watcher = std::make_unique<MemoryWatcher>();
#endif #endif
@ -689,6 +691,9 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
cpuThreadFunc(system, savestate_path, delete_savestate); cpuThreadFunc(system, savestate_path, delete_savestate);
} }
if (cpuThreadFunc == CpuThread)
system.GetMemory().InitDirtyPages();
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ...")); INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stopping GDB ..."));
GDBStub::Deinit(); GDBStub::Deinit();
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped.")); INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "GDB stopped."));

View File

@ -82,9 +82,9 @@ void Shutdown(Core::System& system)
system.GetCoreTiming().Shutdown(); system.GetCoreTiming().Shutdown();
} }
void DoState(Core::System& system, PointerWrap& p) void DoState(Core::System& system, PointerWrap& p, bool delta)
{ {
system.GetMemory().DoState(p); system.GetMemory().DoState(p, delta);
p.DoMarker("Memory"); p.DoMarker("Memory");
system.GetMemoryInterface().DoState(p); system.GetMemoryInterface().DoState(p);
p.DoMarker("MemoryInterface"); p.DoMarker("MemoryInterface");

View File

@ -14,5 +14,5 @@ namespace HW
{ {
void Init(Core::System& system, const Sram* override_sram); void Init(Core::System& system, const Sram* override_sram);
void Shutdown(Core::System& system); void Shutdown(Core::System& system);
void DoState(Core::System& system, PointerWrap& p); void DoState(Core::System& system, PointerWrap& p, bool delta);
} // namespace HW } // namespace HW

View File

@ -47,6 +47,57 @@ MemoryManager::MemoryManager(Core::System& system) : m_system(system)
MemoryManager::~MemoryManager() = default; MemoryManager::~MemoryManager() = default;
u64 MemoryManager::GetDirtyPageIndexFromAddress(u64 address)
{
const size_t page_size = Common::PageSize();
const size_t page_mask = page_size - 1;
return address & ~page_mask;
}
bool MemoryManager::HandleFault(uintptr_t fault_address)
{
u8* fault_address_bytes = reinterpret_cast<u8*>(fault_address);
if (!IsAddressInEmulatedMemory(fault_address_bytes) || IsPageDirty(fault_address))
{
return false;
}
SetPageDirtyBit(fault_address, 0x1, true);
bool change_protection =
m_arena.VirtualProtectMemoryRegion(fault_address_bytes, 0x1, PAGE_READWRITE);
if (!change_protection)
{
return false;
}
return true;
}
void MemoryManager::WriteProtectPhysicalMemoryRegions()
{
for (const PhysicalMemoryRegion& region : m_physical_regions)
{
if (!region.active || !region.track)
continue;
bool change_protection =
m_arena.VirtualProtectMemoryRegion((*region.out_pointer), region.size, PAGE_READONLY);
if (!change_protection)
{
PanicAlertFmt("Memory::WriteProtectPhysicalMemoryRegions(): Failed to write protect for "
"this block of memory at 0x{:08X}.",
reinterpret_cast<u64>(*region.out_pointer));
}
const size_t page_size = Common::PageSize();
const intptr_t out_pointer = reinterpret_cast<intptr_t>(*region.out_pointer);
for (size_t i = out_pointer; i < region.size; i += page_size)
{
m_dirty_pages[i] = false;
}
}
}
void MemoryManager::InitMMIO(bool is_wii) void MemoryManager::InitMMIO(bool is_wii)
{ {
m_mmio_mapping = std::make_unique<MMIO::Mapping>(); m_mmio_mapping = std::make_unique<MMIO::Mapping>();
@ -71,6 +122,11 @@ void MemoryManager::InitMMIO(bool is_wii)
} }
} }
void MemoryManager::InitDirtyPages()
{
WriteProtectPhysicalMemoryRegions();
}
void MemoryManager::Init() void MemoryManager::Init()
{ {
const auto get_mem1_size = [] { const auto get_mem1_size = [] {
@ -95,13 +151,14 @@ void MemoryManager::Init()
m_exram_mask = GetExRamSize() - 1; m_exram_mask = GetExRamSize() - 1;
m_physical_regions[0] = PhysicalMemoryRegion{ m_physical_regions[0] = PhysicalMemoryRegion{
&m_ram, 0x00000000, GetRamSize(), PhysicalMemoryRegion::ALWAYS, 0, false}; &m_ram, 0x00000000, GetRamSize(), PhysicalMemoryRegion::ALWAYS, 0, false, true};
m_physical_regions[1] = PhysicalMemoryRegion{ m_physical_regions[1] = PhysicalMemoryRegion{
&m_l1_cache, 0xE0000000, GetL1CacheSize(), PhysicalMemoryRegion::ALWAYS, 0, false}; &m_l1_cache, 0xE0000000, GetL1CacheSize(), PhysicalMemoryRegion::ALWAYS, 0, false, false};
m_physical_regions[2] = PhysicalMemoryRegion{ m_physical_regions[2] = PhysicalMemoryRegion{
&m_fake_vmem, 0x7E000000, GetFakeVMemSize(), PhysicalMemoryRegion::FAKE_VMEM, 0, false}; &m_fake_vmem, 0x7E000000, GetFakeVMemSize(), PhysicalMemoryRegion::FAKE_VMEM, 0,
false, false};
m_physical_regions[3] = PhysicalMemoryRegion{ m_physical_regions[3] = PhysicalMemoryRegion{
&m_exram, 0x10000000, GetExRamSize(), PhysicalMemoryRegion::WII_ONLY, 0, false}; &m_exram, 0x10000000, GetExRamSize(), PhysicalMemoryRegion::WII_ONLY, 0, false, true};
const bool wii = m_system.IsWii(); const bool wii = m_system.IsWii();
const bool mmu = m_system.IsMMUMode(); const bool mmu = m_system.IsMMUMode();
@ -164,6 +221,21 @@ bool MemoryManager::IsAddressInFastmemArea(const u8* address) const
return address >= m_fastmem_arena && address < m_fastmem_arena + m_fastmem_arena_size; return address >= m_fastmem_arena && address < m_fastmem_arena + m_fastmem_arena_size;
} }
bool MemoryManager::IsAddressInEmulatedMemory(const u8* address) const
{
for (const PhysicalMemoryRegion& region : m_physical_regions)
{
if (!region.active || !region.track)
continue;
if (address >= *region.out_pointer && address < *region.out_pointer + region.size)
{
return true;
}
}
return false;
}
bool MemoryManager::InitFastmemArena() bool MemoryManager::InitFastmemArena()
{ {
// Here we set up memory mappings for fastmem. The basic idea of fastmem is that we reserve 4 GiB // Here we set up memory mappings for fastmem. The basic idea of fastmem is that we reserve 4 GiB
@ -290,7 +362,7 @@ void MemoryManager::UpdateLogicalMemory(const PowerPC::BatTable& dbat_table)
} }
} }
void MemoryManager::DoState(PointerWrap& p) void MemoryManager::DoState(PointerWrap& p, bool delta)
{ {
const u32 current_ram_size = GetRamSize(); const u32 current_ram_size = GetRamSize();
const u32 current_l1_cache_size = GetL1CacheSize(); const u32 current_l1_cache_size = GetL1CacheSize();
@ -327,28 +399,60 @@ void MemoryManager::DoState(PointerWrap& p)
p.SetVerifyMode(); p.SetVerifyMode();
return; return;
} }
if (delta)
p.DoArray(m_ram, current_ram_size); {
p.DoArray(m_l1_cache, current_l1_cache_size); const u32 page_size = static_cast<u32>(Common::PageSize());
p.DoMarker("Memory RAM"); p.Do(m_dirty_pages);
if (current_have_fake_vmem) for (size_t i = 0; i < current_ram_size; i += page_size)
p.DoArray(m_fake_vmem, current_fake_vmem_size); {
p.DoMarker("Memory FakeVMEM"); if (IsPageDirty(reinterpret_cast<uintptr_t>(&m_ram[i])))
if (current_have_exram) {
p.DoArray(m_exram, current_exram_size); p.DoArray(m_ram + i, page_size);
p.DoMarker("Memory EXRAM"); }
}
p.DoArray(m_l1_cache, current_l1_cache_size);
p.DoMarker("Memory RAM");
if (current_have_fake_vmem)
{
p.DoArray(m_fake_vmem, current_fake_vmem_size);
}
p.DoMarker("Memory FakeVMEM");
if (current_have_exram)
{
for (size_t i = 0; i < current_exram_size; i += page_size)
{
if (IsPageDirty(reinterpret_cast<uintptr_t>(&m_exram[i])))
{
p.DoArray(m_exram + i, page_size);
}
}
}
p.DoMarker("Memory EXRAM");
}
else
{
p.DoArray(m_ram, current_ram_size);
p.DoArray(m_l1_cache, current_l1_cache_size);
p.DoMarker("Memory RAM");
if (current_have_fake_vmem)
p.DoArray(m_fake_vmem, current_fake_vmem_size);
p.DoMarker("Memory FakeVMEM");
if (current_have_exram)
p.DoArray(m_exram, current_exram_size);
p.DoMarker("Memory EXRAM");
}
} }
void MemoryManager::Shutdown() void MemoryManager::Shutdown()
{ {
ShutdownFastmemArena(); ShutdownFastmemArena();
m_dirty_pages.clear();
m_is_initialized = false; m_is_initialized = false;
for (const PhysicalMemoryRegion& region : m_physical_regions) for (const PhysicalMemoryRegion& region : m_physical_regions)
{ {
if (!region.active) if (!region.active)
continue; continue;
m_arena.ReleaseView(*region.out_pointer, region.size); m_arena.ReleaseView(*region.out_pointer, region.size);
*region.out_pointer = nullptr; *region.out_pointer = nullptr;
} }
@ -573,4 +677,22 @@ void MemoryManager::Write_U64_Swap(u64 value, u32 address)
CopyToEmu(address, &value, sizeof(value)); CopyToEmu(address, &value, sizeof(value));
} }
bool MemoryManager::IsPageDirty(uintptr_t address)
{
return m_dirty_pages[GetDirtyPageIndexFromAddress(address)];
}
void MemoryManager::SetPageDirtyBit(uintptr_t address, size_t size, bool dirty)
{
for (size_t i = 0; i < size; i++)
{
m_dirty_pages[GetDirtyPageIndexFromAddress(address + i)] = dirty;
}
}
void MemoryManager::ResetDirtyPages()
{
WriteProtectPhysicalMemoryRegions();
}
} // namespace Memory } // namespace Memory

View File

@ -8,6 +8,7 @@
#include <span> #include <span>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/MathUtil.h" #include "Common/MathUtil.h"
@ -48,6 +49,7 @@ struct PhysicalMemoryRegion
} flags; } flags;
u32 shm_position; u32 shm_position;
bool active; bool active;
bool track;
}; };
struct LogicalMemoryView struct LogicalMemoryView
@ -78,6 +80,7 @@ public:
u32 GetExRamMask() const { return m_exram_mask; } u32 GetExRamMask() const { return m_exram_mask; }
bool IsAddressInFastmemArea(const u8* address) const; bool IsAddressInFastmemArea(const u8* address) const;
bool IsAddressInEmulatedMemory(const u8* address) const;
u8* GetPhysicalBase() const { return m_physical_base; } u8* GetPhysicalBase() const { return m_physical_base; }
u8* GetLogicalBase() const { return m_logical_base; } u8* GetLogicalBase() const { return m_logical_base; }
u8* GetPhysicalPageMappingsBase() const { return m_physical_page_mappings_base; } u8* GetPhysicalPageMappingsBase() const { return m_physical_page_mappings_base; }
@ -94,10 +97,11 @@ public:
// Init and Shutdown // Init and Shutdown
bool IsInitialized() const { return m_is_initialized; } bool IsInitialized() const { return m_is_initialized; }
void Init(); void Init();
void InitDirtyPages();
void Shutdown(); void Shutdown();
bool InitFastmemArena(); bool InitFastmemArena();
void ShutdownFastmemArena(); void ShutdownFastmemArena();
void DoState(PointerWrap& p); void DoState(PointerWrap& p, bool delta);
void UpdateLogicalMemory(const PowerPC::BatTable& dbat_table); void UpdateLogicalMemory(const PowerPC::BatTable& dbat_table);
@ -130,6 +134,13 @@ public:
void Write_U32_Swap(u32 var, u32 address); void Write_U32_Swap(u32 var, u32 address);
void Write_U64_Swap(u64 var, u32 address); void Write_U64_Swap(u64 var, u32 address);
bool IsPageDirty(uintptr_t address);
void SetPageDirtyBit(uintptr_t address, size_t size, bool dirty);
void ResetDirtyPages();
bool HandleFault(uintptr_t fault_address);
std::map<u64, u8>& GetDirtyPages() { return m_dirty_pages; }
// Templated functions for byteswapped copies. // Templated functions for byteswapped copies.
template <typename T> template <typename T>
void CopyFromEmuSwapped(T* data, u32 address, size_t size) const void CopyFromEmuSwapped(T* data, u32 address, size_t size) const
@ -254,6 +265,11 @@ private:
Core::System& m_system; Core::System& m_system;
std::map<u64, u8> m_dirty_pages;
u64 GetDirtyPageIndexFromAddress(u64 address);
void WriteProtectPhysicalMemoryRegions();
void InitMMIO(bool is_wii); void InitMMIO(bool is_wii);
}; };
} // namespace Memory } // namespace Memory

View File

@ -14,6 +14,7 @@
#include "Common/MsgHandler.h" #include "Common/MsgHandler.h"
#include "Common/Thread.h" #include "Common/Thread.h"
#include "Core/HW/Memmap.h"
#include "Core/MachineContext.h" #include "Core/MachineContext.h"
#include "Core/PowerPC/JitInterface.h" #include "Core/PowerPC/JitInterface.h"
#include "Core/System.h" #include "Core/System.h"
@ -24,6 +25,7 @@
#ifndef _WIN32 #ifndef _WIN32
#include <unistd.h> // Needed for _POSIX_VERSION #include <unistd.h> // Needed for _POSIX_VERSION
#endif #endif
#include <Common/MemoryUtil.h>
#if defined(__APPLE__) #if defined(__APPLE__)
#ifdef _M_X86_64 #ifdef _M_X86_64
@ -60,8 +62,12 @@ static LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs)
// virtual address of the inaccessible data // virtual address of the inaccessible data
uintptr_t fault_address = (uintptr_t)pPtrs->ExceptionRecord->ExceptionInformation[1]; uintptr_t fault_address = (uintptr_t)pPtrs->ExceptionRecord->ExceptionInformation[1];
SContext* ctx = pPtrs->ContextRecord; SContext* ctx = pPtrs->ContextRecord;
Core::System& system = Core::System::GetInstance();
if (Core::System::GetInstance().GetJitInterface().HandleFault(fault_address, ctx)) if (system.GetMemory().HandleFault(fault_address))
{
return EXCEPTION_CONTINUE_EXECUTION;
}
else if (system.GetJitInterface().HandleFault(fault_address, ctx))
{ {
return EXCEPTION_CONTINUE_EXECUTION; return EXCEPTION_CONTINUE_EXECUTION;
} }

View File

@ -139,7 +139,7 @@ void EnableCompression(bool compression)
s_use_compression = compression; s_use_compression = compression;
} }
static void DoState(Core::System& system, PointerWrap& p) static void DoState(Core::System& system, PointerWrap& p, bool delta)
{ {
bool is_wii = system.IsWii() || system.IsMIOS(); bool is_wii = system.IsWii() || system.IsMIOS();
const bool is_wii_currently = is_wii; const bool is_wii_currently = is_wii;
@ -188,7 +188,7 @@ static void DoState(Core::System& system, PointerWrap& p)
p.DoMarker("CoreTiming"); p.DoMarker("CoreTiming");
// HW needs to be restored before PowerPC because the data cache might need to be flushed. // HW needs to be restored before PowerPC because the data cache might need to be flushed.
HW::DoState(system, p); HW::DoState(system, p, delta);
p.DoMarker("HW"); p.DoMarker("HW");
system.GetPowerPC().DoState(p); system.GetPowerPC().DoState(p);
@ -224,7 +224,7 @@ void LoadFromBuffer(Core::System& system, std::vector<u8>& buffer)
[&] { [&] {
u8* ptr = buffer.data(); u8* ptr = buffer.data();
PointerWrap p(&ptr, buffer.size(), PointerWrap::Mode::Read); PointerWrap p(&ptr, buffer.size(), PointerWrap::Mode::Read);
DoState(system, p); DoState(system, p, false);
}, },
true); true);
} }
@ -237,13 +237,13 @@ void SaveToBuffer(Core::System& system, std::vector<u8>& buffer)
u8* ptr = nullptr; u8* ptr = nullptr;
PointerWrap p_measure(&ptr, 0, PointerWrap::Mode::Measure); PointerWrap p_measure(&ptr, 0, PointerWrap::Mode::Measure);
DoState(system, p_measure); DoState(system, p_measure, false);
const size_t buffer_size = reinterpret_cast<size_t>(ptr); const size_t buffer_size = reinterpret_cast<size_t>(ptr);
buffer.resize(buffer_size); buffer.resize(buffer_size);
ptr = buffer.data(); ptr = buffer.data();
PointerWrap p(&ptr, buffer_size, PointerWrap::Mode::Write); PointerWrap p(&ptr, buffer_size, PointerWrap::Mode::Write);
DoState(system, p); DoState(system, p, false);
}, },
true); true);
} }
@ -486,7 +486,7 @@ void SaveAs(Core::System& system, const std::string& filename, bool wait)
// Measure the size of the buffer. // Measure the size of the buffer.
u8* ptr = nullptr; u8* ptr = nullptr;
PointerWrap p_measure(&ptr, 0, PointerWrap::Mode::Measure); PointerWrap p_measure(&ptr, 0, PointerWrap::Mode::Measure);
DoState(system, p_measure); DoState(system, p_measure, false);
const size_t buffer_size = reinterpret_cast<size_t>(ptr); const size_t buffer_size = reinterpret_cast<size_t>(ptr);
// Then actually do the write. // Then actually do the write.
@ -494,7 +494,7 @@ void SaveAs(Core::System& system, const std::string& filename, bool wait)
current_buffer.resize(buffer_size); current_buffer.resize(buffer_size);
ptr = current_buffer.data(); ptr = current_buffer.data();
PointerWrap p(&ptr, buffer_size, PointerWrap::Mode::Write); PointerWrap p(&ptr, buffer_size, PointerWrap::Mode::Write);
DoState(system, p); DoState(system, p, true);
if (p.IsWriteMode()) if (p.IsWriteMode())
{ {
@ -900,7 +900,7 @@ void LoadAs(Core::System& system, const std::string& filename)
{ {
u8* ptr = buffer.data(); u8* ptr = buffer.data();
PointerWrap p(&ptr, buffer.size(), PointerWrap::Mode::Read); PointerWrap p(&ptr, buffer.size(), PointerWrap::Mode::Read);
DoState(system, p); DoState(system, p, true);
loaded = true; loaded = true;
loadedSuccessfully = p.IsReadMode(); loadedSuccessfully = p.IsReadMode();
} }