diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 8ca86adf..3ebc86a7 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -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(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(); + 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(); + } + // fs + coreinit::FSRestore(reader); + iosu::fsa::Restore(reader); + + cemuLog_log(LogType::Force, "[SAVESTATE] Loaded state from {}.", path); + + ResumeTitle(/*isThreadRunning*/); + } + /* Virtual mlc storage */ void InitVirtualMlcStorage() diff --git a/src/Cafe/CafeSystem.h b/src/Cafe/CafeSystem.h index 236cf44a..8be2355d 100644 --- a/src/Cafe/CafeSystem.h +++ b/src/Cafe/CafeSystem.h @@ -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(); diff --git a/src/Cafe/Filesystem/fsc.cpp b/src/Cafe/Filesystem/fsc.cpp index b2500667..ec53673d 100644 --- a/src/Cafe/Filesystem/fsc.cpp +++ b/src/Cafe/Filesystem/fsc.cpp @@ -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()) + { + case Child::DIRECTORY_ITERATOR: + { + std::string path = reader.readBE(); + std::vector folders{}; + size_t size = reader.readBE(); + 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(); + FSC_ACCESS_FLAG flags = (FSC_ACCESS_FLAG)reader.readBE(); + sint32 status{}; + file = FSCVirtualFile_Host::OpenFile(path, flags, status); + file->fscSetSeek(reader.readBE()); + break; + } + default: + throw std::exception("Not implemented"); + } + if (reader.readBE()) + { + file->dirIterator = new FSCDirIteratorState; + *file->dirIterator = reader.readBE(); + } + return file; +} + +void FSCVirtualFileDirectoryIterator::Save(MemStreamWriter& writer) +{ + writer.writeBE((uint32)Child::DIRECTORY_ITERATOR); + writer.writeBE(m_path); + writer.writeBE(m_folders.size()); + for (auto& folder : m_folders) + { + folder->Save(writer); + } + FSCVirtualFile::Save(writer); +} \ No newline at end of file diff --git a/src/Cafe/Filesystem/fsc.h b/src/Cafe/Filesystem/fsc.h index 2854a301..b9ef6f84 100644 --- a/src/Cafe/Filesystem/fsc.h +++ b/src/Cafe/Filesystem/fsc.h @@ -89,12 +89,20 @@ public: struct FSCVirtualFile { + enum class Child : uint32 + { + DIRECTORY_ITERATOR, HOST, WUACTX, WUDCTX, NONE + }; + struct FSCDirIteratorState { sint32 index; std::vector dirEntries; }; + virtual void Save(MemStreamWriter& writer); + static FSCVirtualFile* Restore(MemStreamReader& reader); + FSCVirtualFile() { diff --git a/src/Cafe/Filesystem/fscDeviceHostFS.cpp b/src/Cafe/Filesystem/fscDeviceHostFS.cpp index f63d2920..2e1954d9 100644 --- a/src/Cafe/Filesystem/fscDeviceHostFS.cpp +++ b/src/Cafe/Filesystem/fscDeviceHostFS.cpp @@ -157,6 +157,19 @@ bool FSCVirtualFile_Host::fscDirNext(FSCDirEntry* dirEntry) return true; } +void FSCVirtualFile_Host::Save(MemStreamWriter& writer) +{ + writer.writeBE((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; } diff --git a/src/Cafe/Filesystem/fscDeviceHostFS.h b/src/Cafe/Filesystem/fscDeviceHostFS.h index 7121594c..6daab151 100644 --- a/src/Cafe/Filesystem/fscDeviceHostFS.h +++ b/src/Cafe/Filesystem/fscDeviceHostFS.h @@ -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 m_path{}; std::unique_ptr m_dirIterator{}; + // serialization + FSC_ACCESS_FLAG m_accessFlags; }; \ No newline at end of file diff --git a/src/Cafe/Filesystem/fscDeviceWua.cpp b/src/Cafe/Filesystem/fscDeviceWua.cpp index f9014bdb..5bea3715 100644 --- a/src/Cafe/Filesystem/fscDeviceWua.cpp +++ b/src/Cafe/Filesystem/fscDeviceWua.cpp @@ -15,6 +15,11 @@ protected: }; public: + void Save(MemStreamWriter& writer) override + { + throw std::exception("Not implemented"); + } + sint32 fscGetType() override { return m_fscType; diff --git a/src/Cafe/Filesystem/fscDeviceWud.cpp b/src/Cafe/Filesystem/fscDeviceWud.cpp index bf43bf3e..70d9b5c9 100644 --- a/src/Cafe/Filesystem/fscDeviceWud.cpp +++ b/src/Cafe/Filesystem/fscDeviceWud.cpp @@ -22,6 +22,11 @@ protected: } public: + void Save(MemStreamWriter& writer) override + { + throw std::exception("Not implemented"); + } + sint32 fscGetType() override { return m_fscType; diff --git a/src/Cafe/HW/MMU/MMU.cpp b/src/Cafe/HW/MMU/MMU.cpp index 1a776d6b..e6b47838 100644 --- a/src/Cafe/HW/MMU/MMU.cpp +++ b/src/Cafe/HW/MMU/MMU.cpp @@ -80,6 +80,66 @@ MMURange* memory_getMMURangeByAddress(MPTR address) return nullptr; } +bool MMURange::serializeImpl(MemStreamWriter& streamWriter) +{ + streamWriter.writeBE(this->m_isMapped); + if (m_isMapped) + { + streamWriter.writeBE(this->baseAddress); + streamWriter.writeBE((uint8)this->areaId); + streamWriter.writeBE((uint8)this->flags); + streamWriter.writeBE(this->name); + streamWriter.writeBE(this->size); + streamWriter.writeBE(this->initSize); + } + return true; +} + +template<> +void MemStreamWriter::writeBE(const MMURange& v) +{ + writeBE(v.m_isMapped); + writeBE(v.baseAddress); + writeBE((uint8)v.areaId); + writeBE((uint8)v.flags); + writeBE(v.name); + writeBE(v.size); + writeBE(v.initSize); +} + +template <> +MMURange MemStreamReader::readBE() +{ + bool mapped = readBE(); + uint32 base = readBE(); + MMU_MEM_AREA_ID areaid = (MMU_MEM_AREA_ID)readBE(); + MMURange::MFLAG flags = (MMURange::MFLAG)readBE(); + std::string name = readBE(); + uint32 size = readBE(); + uint32 initsize = readBE(); + MMURange range(base, size, areaid, name, flags); + if (mapped) + range.mapMem(); + return range; +} + +bool MMURange::deserializeImpl(MemStreamReader& streamReader) +{ + m_isMapped = streamReader.readBE(); + if (m_isMapped) + { + baseAddress = streamReader.readBE(); + areaId = (MMU_MEM_AREA_ID)streamReader.readBE(); + this->flags = (MFLAG)streamReader.readBE(); + this->name = streamReader.readBE(); + this->size = streamReader.readBE(); + this->initSize = streamReader.readBE(); + 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(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(); + for (size_t i = 0; i < cnt; i++) + { + auto range = s.readBE(); + 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 diff --git a/src/Cafe/HW/MMU/MMU.h b/src/Cafe/HW/MMU/MMU.h index 222e8c0e..d03307a9 100644 --- a/src/Cafe/HW/MMU/MMU.h +++ b/src/Cafe/HW/MMU/MMU.h @@ -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 void memory_readBytes(VAddr address, std::array& buffer) { diff --git a/src/Cafe/IOSU/fsa/iosu_fsa.cpp b/src/Cafe/IOSU/fsa/iosu_fsa.cpp index 1429d083..e2cf8988 100644 --- a/src/Cafe/IOSU/fsa/iosu_fsa.cpp +++ b/src/Cafe/IOSU/fsa/iosu_fsa.cpp @@ -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(const FSCDirEntry& v); +template <> +FSCDirEntry MemStreamReader::readBE(); +template <> +FSCVirtualFile::FSCDirIteratorState MemStreamReader::readBE(); +template <> +void MemStreamWriter::writeBE(const FSCVirtualFile::FSCDirIteratorState& v); + +template <> +void MemStreamWriter::writeBE(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 table{}; + table.m_currentCounter = readBE(); + for (int i = 0; i < 0x3C0; i++) + { + table.m_handleTable[i].handleCheckValue = readBE(); + table.m_handleTable[i].isAllocated = readBE(); + if (readBE()) table.m_handleTable[i].fscFile = FSCVirtualFile::Restore(*this); + } + return table; +} + +template <> +void MemStreamWriter::writeBE(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(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 entry{}; + readData(entry.path, FSC_MAX_DIR_NAME_LENGTH); + entry.isDirectory = readBE(); + entry.isFile = readBE(); + entry.fileSize = readBE(); + return entry; +} + +template <> +FSCVirtualFile::FSCDirIteratorState MemStreamReader::readBE() +{ + FSCVirtualFile::FSCDirIteratorState state{}; + state.index = readBE(); + size_t size = readBE(); + for (size_t i = 0; i < size; i++) + { + state.dirEntries[i] = readBE(); + } + 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(); + for (size_t i = 0; i < 352; i++) + { + _m_sFSAIoMsgQueueMsgBuffer[i] = reader.readBE(); + } + sDirHandleTable = reader.readBE<_FSAHandleTable>(); + sFileHandleTable = reader.readBE<_FSAHandleTable>(); + } +} \ No newline at end of file diff --git a/src/Cafe/IOSU/fsa/iosu_fsa.h b/src/Cafe/IOSU/fsa/iosu_fsa.h index 8181a4a8..d5fb2118 100644 --- a/src/Cafe/IOSU/fsa/iosu_fsa.h +++ b/src/Cafe/IOSU/fsa/iosu_fsa.h @@ -213,5 +213,8 @@ namespace iosu void Initialize(); void Shutdown(); + + void Save(MemStreamWriter& writer); + void Restore(MemStreamReader& reader); } // namespace fsa } // namespace iosu diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index 26636eae..70c047a4 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -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); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.h b/src/Cafe/OS/libs/coreinit/coreinit_FS.h index 0355c9aa..6a0bf9b3 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.h @@ -308,5 +308,8 @@ namespace coreinit FS_VOLSTATE FSGetVolumeState(FSClient_t* fsClient); + void FSSave(MemStreamWriter& s); + void FSRestore(MemStreamReader& s); + void InitializeFS(); }; // namespace coreinit diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index b97f896e..ab82175b 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -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 GetAllThreads() + { + auto ret = std::vector(); + 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) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.h b/src/Cafe/OS/libs/coreinit/coreinit_Thread.h index 7c1e618b..d04b8ee4 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.h @@ -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 GetAllThreads(); void OSContinueThread(OSThread_t* thread); void __OSSuspendThreadInternal(OSThread_t* thread); void __OSSuspendThreadNolock(OSThread_t* thread); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 142d5ef3..33b4b7d6 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -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; diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 735f73af..610fbcac 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -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; diff --git a/src/util/helpers/Serializer.cpp b/src/util/helpers/Serializer.cpp index e9e60b0a..8e162c30 100644 --- a/src/util/helpers/Serializer.cpp +++ b/src/util/helpers/Serializer.cpp @@ -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(const uint32& v) std::memcpy(p, &tmp, sizeof(tmp)); } +template<> +void MemStreamWriter::writeBE(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(const uint16& v) @@ -134,6 +155,19 @@ void MemStreamWriter::writeBE(const uint8& v) m_buffer.emplace_back(v); } +template<> +void MemStreamWriter::writeBE(const bool& v) +{ + writeData(&v, sizeof(bool)); +} + +template<> +bool MemStreamReader::readBE() +{ + bool v{ false }; + readData(&v, sizeof(bool)); + return v; +} template<> void MemStreamWriter::writeBE(const std::string& v)