Parity with ChrisNonyminus:Cemu:savestates; Add SS menu; Move SS func to CafeSystem

This commit is contained in:
Chris Spegal 2023-07-16 12:56:32 -04:00
parent bfbeeae6f6
commit 0f9d27ae12
19 changed files with 573 additions and 5 deletions

View file

@ -68,6 +68,8 @@
// dependency to be removed
#include "gui/guiWrapper.h"
#include "Cafe/OS/libs/coreinit/coreinit_FS.h"
std::string _pathToExecutable;
std::string _pathToBaseExecutable;
@ -792,6 +794,99 @@ namespace CafeSystem
sSystemRunning = false;
}
void PauseTitle()
{
if (!sSystemRunning)
return;
coreinit::SuspendAllThreads();
sSystemRunning = false;
}
void ResumeTitle(bool* runningThreads)
{
if (sSystemRunning)
return;
coreinit::ResumeAllThreads(runningThreads);
sSystemRunning = true;
}
void SaveState(std::string path)
{
cemuLog_log(LogType::Force, "[SAVESTATE] Saving state...");
MemStreamWriter writer(0);
// pause game
PauseTitle();
// memory
memory_Serialize(writer);
// gpu
writer.writeData(LatteGPUState.contextRegister, sizeof(LatteGPUState.contextRegister));
writer.writeData(LatteGPUState.contextRegisterShadowAddr, sizeof(LatteGPUState.contextRegister));
writer.writeData(LatteGPUState.sharedArea, sizeof(gx2GPUSharedArea_t));
// cpu
auto threads = coreinit::GetAllThreads();
for (auto& thr : threads)
{
writer.writeBE<bool>(thr != nullptr);
if (thr != nullptr)
writer.writeData(thr, sizeof(OSThread_t));
}
for (auto& thr : activeThread)
{
writer.writeBE(thr);
}
// fs
coreinit::FSSave(writer);
iosu::fsa::Save(writer);
FileStream* stream = FileStream::createFile(path);
stream->writeData(writer.getResult().data(), writer.getResult().size_bytes());
delete stream;
cemuLog_log(LogType::Force, "[SAVESTATE] Saved state to {}.", path);
ResumeTitle(/*isThreadRunning*/);
}
void LoadState(std::string path)
{
PauseTitle();
cemuLog_log(LogType::Force, "[SAVESTATE] Loading state...", path);
auto data = FileStream::LoadIntoMemory(path);
assert(data.has_value());
MemStreamReader reader(data->data(), data->size());
// memory
memory_Deserialize(reader);
// gpu
reader.readData(LatteGPUState.contextRegister, sizeof(LatteGPUState.contextRegister));
reader.readData(LatteGPUState.contextRegisterShadowAddr, sizeof(LatteGPUState.contextRegister));
reader.readData(LatteGPUState.sharedArea, sizeof(gx2GPUSharedArea_t));
// cpu
auto threads = coreinit::GetAllThreads();
for (auto& thr : threads)
{
bool notnull = reader.readBE<bool>();
if (notnull)
{
if (!thr)
{
thr = new OSThread_t;
}
reader.readData(thr, sizeof(OSThread_t));
}
}
for (size_t i = 0; i < 256; i++)
{
activeThread[i] = reader.readBE<uint32>();
}
// fs
coreinit::FSRestore(reader);
iosu::fsa::Restore(reader);
cemuLog_log(LogType::Force, "[SAVESTATE] Loaded state from {}.", path);
ResumeTitle(/*isThreadRunning*/);
}
/* Virtual mlc storage */
void InitVirtualMlcStorage()

View file

@ -30,6 +30,12 @@ namespace CafeSystem
void ShutdownTitle();
void PauseTitle();
void ResumeTitle(bool* runningThreads = nullptr);
void SaveState(std::string path);
void LoadState(std::string path);
std::string GetMlcStoragePath(TitleId titleId);
void MlcStorageMountAllTitles();

View file

@ -269,6 +269,8 @@ FSCMountPathNode* fsc_lookupPathVirtualNode(const char* path, sint32 priority)
class FSCVirtualFileDirectoryIterator : public FSCVirtualFile
{
public:
void Save(MemStreamWriter& writer) override;
sint32 fscGetType() override
{
return FSC_TYPE_DIRECTORY;
@ -717,3 +719,59 @@ void fsc_init()
{
fsc_reset();
}
void FSCVirtualFile::Save(MemStreamWriter& writer)
{
writer.writeBE(dirIterator != nullptr);
if (dirIterator) writer.writeBE(*dirIterator);
}
#include "Cafe/Filesystem/fscDeviceHostFS.h"
FSCVirtualFile* FSCVirtualFile::Restore(MemStreamReader& reader)
{
FSCVirtualFile* file;
switch ((Child)reader.readBE<uint32>())
{
case Child::DIRECTORY_ITERATOR:
{
std::string path = reader.readBE<std::string>();
std::vector<FSCVirtualFile*> folders{};
size_t size = reader.readBE<size_t>();
for (size_t i = 0; i < size; i++)
{
folders.push_back(Restore(reader));
}
file = new FSCVirtualFileDirectoryIterator(path, folders);
break;
}
case Child::HOST:
{
std::string path = reader.readBE<std::string>();
FSC_ACCESS_FLAG flags = (FSC_ACCESS_FLAG)reader.readBE<uint32>();
sint32 status{};
file = FSCVirtualFile_Host::OpenFile(path, flags, status);
file->fscSetSeek(reader.readBE<uint64>());
break;
}
default:
throw std::exception("Not implemented");
}
if (reader.readBE<bool>())
{
file->dirIterator = new FSCDirIteratorState;
*file->dirIterator = reader.readBE<FSCDirIteratorState>();
}
return file;
}
void FSCVirtualFileDirectoryIterator::Save(MemStreamWriter& writer)
{
writer.writeBE<uint32>((uint32)Child::DIRECTORY_ITERATOR);
writer.writeBE(m_path);
writer.writeBE(m_folders.size());
for (auto& folder : m_folders)
{
folder->Save(writer);
}
FSCVirtualFile::Save(writer);
}

View file

@ -89,12 +89,20 @@ public:
struct FSCVirtualFile
{
enum class Child : uint32
{
DIRECTORY_ITERATOR, HOST, WUACTX, WUDCTX, NONE
};
struct FSCDirIteratorState
{
sint32 index;
std::vector<FSCDirEntry> dirEntries;
};
virtual void Save(MemStreamWriter& writer);
static FSCVirtualFile* Restore(MemStreamReader& reader);
FSCVirtualFile()
{

View file

@ -157,6 +157,19 @@ bool FSCVirtualFile_Host::fscDirNext(FSCDirEntry* dirEntry)
return true;
}
void FSCVirtualFile_Host::Save(MemStreamWriter& writer)
{
writer.writeBE<uint32>((uint32)Child::HOST);
writer.writeBE(m_path->string());
writer.writeBE((uint32)m_accessFlags);
writer.writeBE(m_seek);
FSCVirtualFile::Save(writer);
}
FSCVirtualFile* FSCVirtualFile_Host::OpenFile(const fs::path& path, FSC_ACCESS_FLAG accessFlags, sint32& fscStatus)
{
if (!HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE) && !HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
@ -191,9 +204,11 @@ FSCVirtualFile* FSCVirtualFile_Host::OpenFile(const fs::path& path, FSC_ACCESS_F
if (fs)
{
FSCVirtualFile_Host* vf = new FSCVirtualFile_Host(FSC_TYPE_FILE);
vf->m_path.reset(new std::filesystem::path(path));
vf->m_fs = fs;
vf->m_isWritable = writeAccessRequested;
vf->m_fileSize = fs->GetSize();
vf->m_accessFlags = accessFlags;
fscStatus = FSC_STATUS_OK;
return vf;
}
@ -208,6 +223,7 @@ FSCVirtualFile* FSCVirtualFile_Host::OpenFile(const fs::path& path, FSC_ACCESS_F
{
FSCVirtualFile_Host* vf = new FSCVirtualFile_Host(FSC_TYPE_DIRECTORY);
vf->m_path.reset(new std::filesystem::path(path));
vf->m_accessFlags = accessFlags;
fscStatus = FSC_STATUS_OK;
return vf;
}

View file

@ -3,6 +3,7 @@
class FSCVirtualFile_Host : public FSCVirtualFile
{
public:
void Save(MemStreamWriter& writer) override;
static FSCVirtualFile* OpenFile(const fs::path& path, FSC_ACCESS_FLAG accessFlags, sint32& fscStatus);
~FSCVirtualFile_Host() override;
@ -31,4 +32,6 @@ private:
// directory
std::unique_ptr<std::filesystem::path> m_path{};
std::unique_ptr<std::filesystem::directory_iterator> m_dirIterator{};
// serialization
FSC_ACCESS_FLAG m_accessFlags;
};

View file

@ -15,6 +15,11 @@ protected:
};
public:
void Save(MemStreamWriter& writer) override
{
throw std::exception("Not implemented");
}
sint32 fscGetType() override
{
return m_fscType;

View file

@ -22,6 +22,11 @@ protected:
}
public:
void Save(MemStreamWriter& writer) override
{
throw std::exception("Not implemented");
}
sint32 fscGetType() override
{
return m_fscType;

View file

@ -80,6 +80,66 @@ MMURange* memory_getMMURangeByAddress(MPTR address)
return nullptr;
}
bool MMURange::serializeImpl(MemStreamWriter& streamWriter)
{
streamWriter.writeBE<bool>(this->m_isMapped);
if (m_isMapped)
{
streamWriter.writeBE(this->baseAddress);
streamWriter.writeBE<uint8>((uint8)this->areaId);
streamWriter.writeBE<uint8>((uint8)this->flags);
streamWriter.writeBE(this->name);
streamWriter.writeBE(this->size);
streamWriter.writeBE(this->initSize);
}
return true;
}
template<>
void MemStreamWriter::writeBE<MMURange>(const MMURange& v)
{
writeBE(v.m_isMapped);
writeBE(v.baseAddress);
writeBE<uint8>((uint8)v.areaId);
writeBE<uint8>((uint8)v.flags);
writeBE(v.name);
writeBE(v.size);
writeBE(v.initSize);
}
template <>
MMURange MemStreamReader::readBE<MMURange>()
{
bool mapped = readBE<bool>();
uint32 base = readBE<uint32>();
MMU_MEM_AREA_ID areaid = (MMU_MEM_AREA_ID)readBE<uint8>();
MMURange::MFLAG flags = (MMURange::MFLAG)readBE<uint8>();
std::string name = readBE<std::string>();
uint32 size = readBE<uint32>();
uint32 initsize = readBE<uint32>();
MMURange range(base, size, areaid, name, flags);
if (mapped)
range.mapMem();
return range;
}
bool MMURange::deserializeImpl(MemStreamReader& streamReader)
{
m_isMapped = streamReader.readBE<bool>();
if (m_isMapped)
{
baseAddress = streamReader.readBE<uint32>();
areaId = (MMU_MEM_AREA_ID)streamReader.readBE<uint8>();
this->flags = (MFLAG)streamReader.readBE<uint8>();
this->name = streamReader.readBE<std::string>();
this->size = streamReader.readBE<uint32>();
this->initSize = streamReader.readBE<uint32>();
m_isMapped = false;
mapMem();
}
return true;
}
MMURange::MMURange(const uint32 baseAddress, const uint32 size, MMU_MEM_AREA_ID areaId, const std::string_view name, MFLAG flags) : baseAddress(baseAddress), size(size), initSize(size), areaId(areaId), name(name), flags(flags)
{
g_mmuRanges.emplace_back(this);
@ -403,6 +463,35 @@ void memory_createDump()
}
}
void memory_Serialize(MemStreamWriter& s)
{
s.writeBE<uint64>(g_mmuRanges.size());
for (auto& itr : g_mmuRanges)
{
s.writeBE(*itr);
if (itr->isMapped())
{
s.writeData(memory_base + itr->getBase(), itr->getSize());
}
}
}
void memory_Deserialize(MemStreamReader& s)
{
g_mmuRanges.clear();
size_t cnt = s.readBE<uint64>();
for (size_t i = 0; i < cnt; i++)
{
auto range = s.readBE<MMURange>();
bool mapped = range.isMapped();
if (mapped)
{
s.readData(memory_base + range.getBase(), range.getSize());
}
g_mmuRanges.push_back(std::move(&range));
}
}
namespace MMU
{
// MMIO access handler

View file

@ -1,5 +1,7 @@
#pragma once
#include "util/helpers/Serializer.h"
void memory_init();
void memory_mapForCurrentTitle();
void memory_logModifiedMemoryRanges();
@ -48,6 +50,9 @@ struct MMURange
FLAG_MAP_EARLY = (1 << 1), // map at Cemu launch, normally memory is mapped when a game is loaded
};
bool serializeImpl(MemStreamWriter& streamWriter);
bool deserializeImpl(MemStreamReader& streamReader);
MMURange(const uint32 baseAddress, const uint32 size, MMU_MEM_AREA_ID areaId, const std::string_view name, MFLAG flags = (MFLAG)0);
void mapMem();
@ -107,14 +112,16 @@ struct MMURange
bool isOptional() const { return (flags & MFLAG::FLAG_OPTIONAL) != 0; };
bool isMappedEarly() const { return (flags & MFLAG::FLAG_MAP_EARLY) != 0; };
const uint32 baseAddress;
const uint32 initSize; // initial size
const std::string name;
const MFLAG flags;
const MMU_MEM_AREA_ID areaId;
uint32 baseAddress;
uint32 initSize; // initial size
std::string name;
MFLAG flags;
MMU_MEM_AREA_ID areaId;
// runtime parameters
uint32 size;
bool m_isMapped{};
friend class MemStreamWriter;
friend class MemStreamReader;
};
@ -202,6 +209,9 @@ uint8 memory_readU8(uint32 address);
void memory_createDump();
void memory_Serialize(MemStreamWriter& s);
void memory_Deserialize(MemStreamReader& s);
template<size_t count>
void memory_readBytes(VAddr address, std::array<uint8, count>& buffer)
{

View file

@ -169,6 +169,8 @@ namespace iosu
}
class _FSAHandleTable {
friend class MemStreamWriter;
friend class MemStreamReader;
struct _FSAHandleResource
{
bool isAllocated{false};
@ -904,3 +906,109 @@ namespace iosu
}
} // namespace fsa
} // namespace iosu
template <>
void MemStreamWriter::writeBE<FSCDirEntry>(const FSCDirEntry& v);
template <>
FSCDirEntry MemStreamReader::readBE<FSCDirEntry>();
template <>
FSCVirtualFile::FSCDirIteratorState MemStreamReader::readBE<FSCVirtualFile::FSCDirIteratorState>();
template <>
void MemStreamWriter::writeBE<FSCVirtualFile::FSCDirIteratorState>(const FSCVirtualFile::FSCDirIteratorState& v);
template <>
void MemStreamWriter::writeBE<iosu::fsa::_FSAHandleTable>(const iosu::fsa::_FSAHandleTable& v)
{
writeBE(v.m_currentCounter);
for (int i = 0; i < 0x3C0; i++)
{
writeBE(v.m_handleTable[i].handleCheckValue);
writeBE(v.m_handleTable[i].isAllocated);
writeBE(v.m_handleTable[i].fscFile != nullptr);
if (v.m_handleTable[i].fscFile != nullptr) v.m_handleTable[i].fscFile->Save(*this);
}
}
template <>
iosu::fsa::_FSAHandleTable MemStreamReader::readBE<iosu::fsa::_FSAHandleTable>()
{
iosu::fsa::_FSAHandleTable table{};
table.m_currentCounter = readBE<uint32>();
for (int i = 0; i < 0x3C0; i++)
{
table.m_handleTable[i].handleCheckValue = readBE<uint16>();
table.m_handleTable[i].isAllocated = readBE<bool>();
if (readBE<bool>()) table.m_handleTable[i].fscFile = FSCVirtualFile::Restore(*this);
}
return table;
}
template <>
void MemStreamWriter::writeBE<FSCVirtualFile::FSCDirIteratorState>(const FSCVirtualFile::FSCDirIteratorState& v)
{
writeBE(v.index);
writeBE(v.dirEntries.size());
for (int i = 0; i < v.dirEntries.size(); i++)
{
writeBE(v.dirEntries[i]);
}
}
template <>
void MemStreamWriter::writeBE<FSCDirEntry>(const FSCDirEntry& v)
{
writeData(v.path, FSC_MAX_DIR_NAME_LENGTH);
writeBE(v.isDirectory);
writeBE(v.isFile);
writeBE(v.fileSize);
}
template <>
FSCDirEntry MemStreamReader::readBE<FSCDirEntry>()
{
FSCDirEntry entry{};
readData(entry.path, FSC_MAX_DIR_NAME_LENGTH);
entry.isDirectory = readBE<bool>();
entry.isFile = readBE<bool>();
entry.fileSize = readBE<uint32>();
return entry;
}
template <>
FSCVirtualFile::FSCDirIteratorState MemStreamReader::readBE<FSCVirtualFile::FSCDirIteratorState>()
{
FSCVirtualFile::FSCDirIteratorState state{};
state.index = readBE<sint32>();
size_t size = readBE<size_t>();
for (size_t i = 0; i < size; i++)
{
state.dirEntries[i] = readBE<FSCDirEntry>();
}
return state;
}
namespace iosu::fsa
{
void Save(MemStreamWriter& writer)
{
writer.writeBE(sFSAIoMsgQueue);
for (size_t i = 0; i < 352; i++)
{
writer.writeBE(_m_sFSAIoMsgQueueMsgBuffer[i]);
}
writer.writeBE(sDirHandleTable);
writer.writeBE(sFileHandleTable);
}
void Restore(MemStreamReader& reader)
{
sFSAIoMsgQueue = reader.readBE<IOSMsgQueueId>();
for (size_t i = 0; i < 352; i++)
{
_m_sFSAIoMsgQueueMsgBuffer[i] = reader.readBE<iosu::kernel::IOSMessage>();
}
sDirHandleTable = reader.readBE<_FSAHandleTable>();
sFileHandleTable = reader.readBE<_FSAHandleTable>();
}
}

View file

@ -213,5 +213,8 @@ namespace iosu
void Initialize();
void Shutdown();
void Save(MemStreamWriter& writer);
void Restore(MemStreamReader& reader);
} // namespace fsa
} // namespace iosu

View file

@ -2638,6 +2638,16 @@ namespace coreinit
return FSA_RESULT::OK;
}
void FSSave(MemStreamWriter& s)
{
s.writeData(g_fsRegisteredClientBodies, sizeof(FSClientBody_t));
}
void FSRestore(MemStreamReader& s)
{
s.readData(g_fsRegisteredClientBodies, sizeof(FSClientBody_t));
}
void InitializeFS()
{
cafeExportRegister("coreinit", FSInit, LogType::CoreinitFile);

View file

@ -308,5 +308,8 @@ namespace coreinit
FS_VOLSTATE FSGetVolumeState(FSClient_t* fsClient);
void FSSave(MemStreamWriter& s);
void FSRestore(MemStreamReader& s);
void InitializeFS();
}; // namespace coreinit

View file

@ -198,6 +198,65 @@ namespace coreinit
return __currentCoreThread[currentInstance->spr.UPIR];
}
void SuspendAllThreads()
{
for (auto& thr : activeThread)
{
auto* ptr = (OSThread_t*)memory_getPointerFromVirtualOffset(thr);
if (thr != 0)
{
OSSuspendThread(ptr);
}
}
if (__currentCoreThread[0])
OSSuspendThread(__currentCoreThread[0]);
if (__currentCoreThread[1])
OSSuspendThread(__currentCoreThread[1]);
if (__currentCoreThread[2])
OSSuspendThread(__currentCoreThread[2]);
}
void ResumeAllThreads(bool* runningThreads)
{
for (auto& thr : activeThread)
{
int i = 0;
auto* ptr = (OSThread_t*)memory_getPointerFromVirtualOffset(thr);
if (thr != 0)
{
if (runningThreads && runningThreads[i])
{
if (s_threadToFiber.find(ptr) == s_threadToFiber.end())
__OSCreateHostThread(ptr);
OSResumeThread(ptr);
}
else if (!runningThreads)
{
if (s_threadToFiber.find(ptr) == s_threadToFiber.end())
__OSCreateHostThread(ptr);
OSResumeThread(ptr);
}
}
i++;
}
if (__currentCoreThread[0])
OSResumeThread(__currentCoreThread[0]);
if (__currentCoreThread[1])
OSResumeThread(__currentCoreThread[1]);
if (__currentCoreThread[2])
OSResumeThread(__currentCoreThread[2]);
}
std::vector<OSThread_t*> GetAllThreads()
{
auto ret = std::vector<OSThread_t*>();
ret.push_back(__currentCoreThread[0]);
ret.push_back(__currentCoreThread[1]);
ret.push_back(__currentCoreThread[2]);
return ret;
}
MPTR funcPtr_threadEntry = 0;
void threadEntry(PPCInterpreter_t* hCPU)

View file

@ -524,6 +524,9 @@ namespace coreinit
sint32 __OSResumeThreadInternal(OSThread_t* thread, sint32 resumeCount);
sint32 OSResumeThread(OSThread_t* thread);
void SuspendAllThreads();
void ResumeAllThreads(bool* runningThreads);
std::vector<OSThread_t*> GetAllThreads();
void OSContinueThread(OSThread_t* thread);
void __OSSuspendThreadInternal(OSThread_t* thread);
void __OSSuspendThreadNolock(OSThread_t* thread);

View file

@ -83,6 +83,8 @@ enum
MAINFRAME_MENU_ID_FILE_LOAD = 20100,
MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE,
MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER,
MAINFRAME_MENU_ID_FILE_SAVESTATE,
MAINFRAME_MENU_ID_FILE_LOADSTATE,
MAINFRAME_MENU_ID_FILE_EXIT,
MAINFRAME_MENU_ID_FILE_END_EMULATION,
MAINFRAME_MENU_ID_FILE_RECENT_0,
@ -131,6 +133,13 @@ enum
MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE = 21000,
MAINFRAME_MENU_ID_NFC_RECENT_0,
MAINFRAME_MENU_ID_NFC_RECENT_LAST = MAINFRAME_MENU_ID_NFC_RECENT_0 + 15,
// savestates
MAINFRAME_MENU_ID_SAVESTATES_PAUSE_TITLE,
MAINFRAME_MENU_ID_SAVESTATES_RESUME_TITLE,
MAINFRAME_MENU_ID_SAVESTATES_SAVE_STATE,
MAINFRAME_MENU_ID_SAVESTATES_LOAD_STATE,
// debug
MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN = 21100,
MAINFRAME_MENU_ID_DEBUG_VIEW_LOGGING_WINDOW,
@ -174,6 +183,7 @@ EVT_MOVE(MainWindow::OnMove)
EVT_MENU(MAINFRAME_MENU_ID_FILE_LOAD, MainWindow::OnFileMenu)
EVT_MENU(MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE, MainWindow::OnInstallUpdate)
EVT_MENU(MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, MainWindow::OnOpenCemuFolder)
EVT_MENU(MAINFRAME_MENU_ID_FILE_EXIT, MainWindow::OnFileExit)
EVT_MENU(MAINFRAME_MENU_ID_FILE_END_EMULATION, MainWindow::OnFileMenu)
EVT_MENU_RANGE(MAINFRAME_MENU_ID_FILE_RECENT_0 + 0, MAINFRAME_MENU_ID_FILE_RECENT_LAST, MainWindow::OnFileMenu)
@ -204,6 +214,11 @@ EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_0125X, MainWindow::OnDebugSetting)
// nfc menu
EVT_MENU(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, MainWindow::OnNFCMenu)
EVT_MENU_RANGE(MAINFRAME_MENU_ID_NFC_RECENT_0 + 0, MAINFRAME_MENU_ID_NFC_RECENT_LAST, MainWindow::OnNFCMenu)
// savestates menu
EVT_MENU(MAINFRAME_MENU_ID_SAVESTATES_PAUSE_TITLE, MainWindow::OnSavestatesMenu)
EVT_MENU(MAINFRAME_MENU_ID_SAVESTATES_RESUME_TITLE, MainWindow::OnSavestatesMenu)
EVT_MENU(MAINFRAME_MENU_ID_SAVESTATES_SAVE_STATE, MainWindow::OnSavestatesMenu)
EVT_MENU(MAINFRAME_MENU_ID_SAVESTATES_LOAD_STATE, MainWindow::OnSavestatesMenu)
// debug -> logging menu
EVT_MENU_RANGE(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + 0, MAINFRAME_MENU_ID_DEBUG_LOGGING0 + 98, MainWindow::OnDebugLoggingToggleFlagGeneric)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO, MainWindow::OnPPCInfoToggle)
@ -778,6 +793,27 @@ void MainWindow::OnNFCMenu(wxCommandEvent& event)
}
}
void MainWindow::OnSavestatesMenu(wxCommandEvent& event)
{
const auto menuId = event.GetId();
if (menuId == MAINFRAME_MENU_ID_SAVESTATES_PAUSE_TITLE)
{
CafeSystem::PauseTitle();
}
if (menuId == MAINFRAME_MENU_ID_SAVESTATES_RESUME_TITLE)
{
CafeSystem::ResumeTitle();
}
else if (menuId == MAINFRAME_MENU_ID_SAVESTATES_SAVE_STATE)
{
CafeSystem::SaveState("state.bin");
}
else if (menuId == MAINFRAME_MENU_ID_SAVESTATES_LOAD_STATE)
{
CafeSystem::LoadState("state.bin");
}
}
void MainWindow::OnFileExit(wxCommandEvent& event)
{
// todo: Safely clean up everything
@ -2184,6 +2220,16 @@ void MainWindow::RecreateMenu()
nfcMenu->Append(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, _("&Scan NFC tag from file"))->Enable(false);
m_menuBar->Append(nfcMenu, _("&NFC"));
m_nfcMenuSeparator0 = nullptr;
// savestates menu
wxMenu* savestatesMenu = new wxMenu;
savestatesMenu->Append(MAINFRAME_MENU_ID_SAVESTATES_PAUSE_TITLE, _("&Pause"), wxEmptyString);
savestatesMenu->Append(MAINFRAME_MENU_ID_SAVESTATES_RESUME_TITLE, _("&Resume"), wxEmptyString);
savestatesMenu->AppendSeparator();
savestatesMenu->Append(MAINFRAME_MENU_ID_SAVESTATES_SAVE_STATE, _("&Save state"), wxEmptyString);
savestatesMenu->Append(MAINFRAME_MENU_ID_SAVESTATES_LOAD_STATE, _("&Load state"), wxEmptyString);
m_menuBar->Append(savestatesMenu, _("&Savestates"));
// debug->logging submenu
wxMenu* debugLoggingMenu = new wxMenu;

View file

@ -93,6 +93,7 @@ public:
void OnInstallUpdate(wxCommandEvent& event);
void OnFileExit(wxCommandEvent& event);
void OnNFCMenu(wxCommandEvent& event);
void OnSavestatesMenu(wxCommandEvent& event);
void OnOptionsInput(wxCommandEvent& event);
void OnAccountSelect(wxCommandEvent& event);
void OnConsoleLanguage(wxCommandEvent& event);
@ -227,6 +228,12 @@ private:
wxMenu* m_nfcMenu;
wxMenuItem* m_nfcMenuSeparator0;
// savestates
//wxMenuItem* m_pause;
//wxMenuItem* m_resume;
//wxMenuItem* m_saveState;
//wxMenuItem* m_loadState;
// debug
wxMenu* m_debugMenu;
wxMenu* m_loggingSubmenu;

View file

@ -36,6 +36,19 @@ uint32 MemStreamReader::readBE()
return v;
}
template<>
sint32 MemStreamReader::readBE()
{
if (!reserveReadLength(sizeof(sint32)))
return 0;
const uint8* p = m_data + m_cursorPos;
sint32 v;
std::memcpy(&v, p, sizeof(v));
v = _BE(v);
m_cursorPos += sizeof(sint32);
return v;
}
template<>
uint64 MemStreamReader::readBE()
{
@ -117,6 +130,14 @@ void MemStreamWriter::writeBE<uint32>(const uint32& v)
std::memcpy(p, &tmp, sizeof(tmp));
}
template<>
void MemStreamWriter::writeBE<sint32>(const sint32& v)
{
m_buffer.resize(m_buffer.size() + 4);
uint8* p = m_buffer.data() + m_buffer.size() - 4;
uint32 tmp = _BE(v);
std::memcpy(p, &tmp, sizeof(tmp));
}
template<>
void MemStreamWriter::writeBE<uint16>(const uint16& v)
@ -134,6 +155,19 @@ void MemStreamWriter::writeBE<uint8>(const uint8& v)
m_buffer.emplace_back(v);
}
template<>
void MemStreamWriter::writeBE<bool>(const bool& v)
{
writeData(&v, sizeof(bool));
}
template<>
bool MemStreamReader::readBE<bool>()
{
bool v{ false };
readData(&v, sizeof(bool));
return v;
}
template<>
void MemStreamWriter::writeBE<std::string>(const std::string& v)