Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add save states #953

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
83 changes: 83 additions & 0 deletions src/Cafe/CafeSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
// dependency to be removed
#include "gui/guiWrapper.h"

#include "Cafe/OS/libs/coreinit/coreinit_FS.h"

#include <time.h>

#if BOOST_OS_LINUX
Expand Down Expand Up @@ -975,6 +977,87 @@ namespace CafeSystem
sSystemRunning = false;
}

void PauseTitle()
{
if (!sSystemRunning)
return;
coreinit::SuspendActiveThreads();
iosu::pdm::Stop();
sSystemRunning = false;
}

void ResumeTitle()
{
if (sSystemRunning)
return;
coreinit::ResumeActiveThreads();
iosu::pdm::StartTrackingTime(GetForegroundTitleId());
sSystemRunning = true;
}

void SaveState(std::string path)
{
cemuLog_log(LogType::SaveStates, "Saving state...");
MemStreamWriter writer(0);
// pause game
PauseTitle();
// memory
memory_Serialize(writer);


nn::temp::save(writer);
nn::aoc::save(writer);
osLib_save(writer);
iosu::kernel::save(writer);
iosu::fsa::save(writer);
iosu::odm::save(writer);

// gpu
writer.writeData(LatteGPUState.contextRegister, sizeof(LatteGPUState.contextRegister));
writer.writeData(LatteGPUState.contextRegisterShadowAddr, sizeof(LatteGPUState.contextRegister));
writer.writeData(LatteGPUState.sharedArea, sizeof(gx2GPUSharedArea_t));

FileStream* stream = FileStream::createFile(path);
stream->writeData(writer.getResult().data(), writer.getResult().size_bytes());
delete stream;
cemuLog_log(LogType::SaveStates, "Saved state to {}.", path);

ResumeTitle(/*isThreadRunning*/);
}

void LoadState(std::string path)
{
PauseTitle();
//coreinit::__OSDeleteAllActivePPCThreads();
DestroyMemorySpace();

cemuLog_log(LogType::SaveStates, "Loading state...", path);

auto data = FileStream::LoadIntoMemory(path);
assert(data.has_value());
MemStreamReader reader(data->data(), data->size());

// memory

memory_Deserialize(reader);

nn::temp::restore(reader);
nn::aoc::restore(reader);
osLib_restore(reader);
iosu::kernel::restore(reader);
iosu::fsa::restore(reader);
iosu::odm::restore(reader);

// gpu
reader.readData(LatteGPUState.contextRegister, sizeof(LatteGPUState.contextRegister));
reader.readData(LatteGPUState.contextRegisterShadowAddr, sizeof(LatteGPUState.contextRegister));
reader.readData(LatteGPUState.sharedArea, sizeof(gx2GPUSharedArea_t));

cemuLog_log(LogType::SaveStates, "Loaded state from {}.", path);

ResumeTitle(/*isThreadRunning*/);
}

/* Virtual mlc storage */

void InitVirtualMlcStorage()
Expand Down
6 changes: 6 additions & 0 deletions src/Cafe/CafeSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ namespace CafeSystem

void ShutdownTitle();

void PauseTitle();
void ResumeTitle();

void SaveState(std::string path);
void LoadState(std::string path);

std::string GetMlcStoragePath(TitleId titleId);
void MlcStorageMountAllTitles();

Expand Down
75 changes: 75 additions & 0 deletions src/Cafe/Filesystem/fsc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ class FSCVirtualFileDirectoryIterator : public FSCVirtualFile
dirIterator->dirEntries.emplace_back(dirEntry);
}

void Save(MemStreamWriter& writer) override;

private:
void PopulateIterationList()
{
Expand Down Expand Up @@ -733,3 +735,76 @@ void fsc_init()
{
fsc_reset();
}

template <>
void MemStreamWriter::write<FSCVirtualFile::FSCDirIteratorState>(const FSCVirtualFile::FSCDirIteratorState& v)
{
write(v.index);
writePODVector(v.dirEntries);
}

template <>
void MemStreamReader::read(FSCVirtualFile::FSCDirIteratorState& v)
{
read(v.index);
readPODVector(v.dirEntries);
}

void FSCVirtualFile::Save(MemStreamWriter& writer)
{
writer.writeBool(dirIterator != nullptr);
if (dirIterator) writer.write(*dirIterator);
writer.writeBool(m_isAppend);
}

void FSCVirtualFileDirectoryIterator::Save(MemStreamWriter& writer)
{
writer.write<uint32>((uint32)Child::DIRECTORY_ITERATOR);
writer.write(m_path);
writer.write<uint32>(m_folders.size());
for (auto& folder : m_folders)
{
folder->Save(writer);
}
FSCVirtualFile::Save(writer);
}

#include "Cafe/Filesystem/fscDeviceHostFS.h"

FSCVirtualFile* FSCVirtualFile::Restore(MemStreamReader& reader)
{
FSCVirtualFile* file;
switch ((Child)reader.read<uint32>())
{
case Child::DIRECTORY_ITERATOR:
{
std::string path = reader.read<std::string>();
std::vector<FSCVirtualFile*> folders{};
size_t size = reader.read<uint32>();
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.read<std::string>();
FSC_ACCESS_FLAG flags = (FSC_ACCESS_FLAG)reader.read<uint32>();
sint32 status{};
file = FSCVirtualFile_Host::OpenFile(path, flags, status);
file->fscSetSeek(reader.read<uint64>());
break;
}
default:
throw std::exception("Not implemented");
}
if (reader.readBool())
{
file->dirIterator = new FSCDirIteratorState;
reader.read(*file->dirIterator);
}
reader.readBool(file->m_isAppend);
return file;
}
8 changes: 8 additions & 0 deletions src/Cafe/Filesystem/fsc.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,20 @@ class fscDeviceC

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()
{

Expand Down
14 changes: 13 additions & 1 deletion src/Cafe/Filesystem/fscDeviceHostFS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,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;
}
Expand All @@ -208,6 +210,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;
}
Expand Down Expand Up @@ -292,4 +295,13 @@ class fscDeviceHostFSC : public fscDeviceC
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority)
{
return fsc_mount(mountPath, hostTargetPath, &fscDeviceHostFSC::instance(), nullptr, priority) == FSC_STATUS_OK;
}
}

void FSCVirtualFile_Host::Save(MemStreamWriter& writer)
{
writer.write<uint32>((uint32)Child::HOST);
writer.write(m_path->string());
writer.write((uint32)m_accessFlags);
writer.write(m_seek);
FSCVirtualFile::Save(writer);
}
4 changes: 4 additions & 0 deletions src/Cafe/Filesystem/fscDeviceHostFS.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class FSCVirtualFile_Host : public FSCVirtualFile
void fscSetFileLength(uint64 endOffset) override;
bool fscDirNext(FSCDirEntry* dirEntry) override;

void Save(MemStreamWriter& writer) override;

private:
FSCVirtualFile_Host(uint32 type) : m_type(type) {};

Expand All @@ -31,4 +33,6 @@ class FSCVirtualFile_Host : public FSCVirtualFile
// directory
std::unique_ptr<std::filesystem::path> m_path{};
std::unique_ptr<std::filesystem::directory_iterator> m_dirIterator{};
// serialization
FSC_ACCESS_FLAG m_accessFlags;
};
5 changes: 5 additions & 0 deletions src/Cafe/Filesystem/fscDeviceWua.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ class FSCDeviceWuaFileCtx : public FSCVirtualFile
return true;
}

void Save(MemStreamWriter& writer) override
{
throw std::exception("Not implemented");
}

private:
ZArchiveReader* m_archive{nullptr};
sint32 m_fscType;
Expand Down
5 changes: 5 additions & 0 deletions src/Cafe/Filesystem/fscDeviceWud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ class FSCDeviceWudFileCtx : public FSCVirtualFile
return true;
}

void Save(MemStreamWriter& writer) override
{
throw std::exception("Not implemented");
}

private:
FSTVolume* m_volume{nullptr};
sint32 m_fscType;
Expand Down
51 changes: 51 additions & 0 deletions src/Cafe/HW/MMU/MMU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,33 @@ MMURange* memory_getMMURangeByAddress(MPTR address)
return nullptr;
}

template<>
void MemStreamWriter::write(const MMURange& v)
{
writeBool(v.m_isMapped);
write(v.baseAddress);
write((uint8)v.areaId);
write((uint8)v.flags);
write(v.name);
write(v.size);
write(v.initSize);
}

template <>
void MemStreamReader::read(MMURange& mmuRange)
{
bool needsMapped = readBool();
mmuRange.m_isMapped = false;
mmuRange.baseAddress = read<uint32>();
mmuRange.areaId = (MMU_MEM_AREA_ID)read<uint8>();
mmuRange.flags = (MMURange::MFLAG)read<uint8>();
mmuRange.name = read<std::string>();
mmuRange.size = read<uint32>();
mmuRange.initSize = read<uint32>();
if (needsMapped)
mmuRange.mapMem();
}

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);
Expand Down Expand Up @@ -412,6 +439,30 @@ void memory_createDump()
}
}

void memory_Serialize(MemStreamWriter& s)
{
for (auto& itr : g_mmuRanges)
{
s.write(*itr);
if (itr->isMapped())
{
s.writeData(memory_base + itr->getBase(), itr->getSize());
}
}
}

void memory_Deserialize(MemStreamReader& s)
{
for (auto& itr : g_mmuRanges)
{
s.read<MMURange>(*itr);
if (itr->isMapped())
{
s.readData(memory_base + itr->getBase(), itr->getSize());
}
}
}

namespace MMU
{
// MMIO access handler
Expand Down
17 changes: 12 additions & 5 deletions src/Cafe/HW/MMU/MMU.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "util/helpers/Serializer.h"

void memory_init();
void memory_mapForCurrentTitle();
void memory_unmapForCurrentTitle();
Expand Down Expand Up @@ -108,14 +110,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;
};


Expand Down Expand Up @@ -203,6 +207,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)
{
Expand Down
Loading