Save the game title in savestates, if available.

So that the UI can show them.  Old savestates still load fine, just don't
have the title.
This commit is contained in:
Unknown W. Brackets 2016-01-23 12:53:03 -08:00
parent 8d41664bb1
commit 6a5096e546
4 changed files with 97 additions and 54 deletions

View file

@ -161,53 +161,81 @@ PointerWrapSection::~PointerWrapSection() {
}
}
CChunkFileReader::Error CChunkFileReader::LoadFile(const std::string& _rFilename, int _Revision, const char *_VersionString, u8 *&_buffer, size_t &sz, std::string *_failureReason) {
if (!File::Exists(_rFilename)) {
*_failureReason = "LoadStateDoesntExist";
ERROR_LOG(COMMON, "ChunkReader: File doesn't exist");
return ERROR_BAD_FILE;
}
// Check file size
const u64 fileSize = File::GetFileSize(_rFilename);
static const u64 headerSize = sizeof(SChunkHeader);
if (fileSize < headerSize)
{
ERROR_LOG(COMMON, "ChunkReader: File too small");
return ERROR_BAD_FILE;
}
File::IOFile pFile(_rFilename, "rb");
if (!pFile)
{
CChunkFileReader::Error CChunkFileReader::LoadFileHeader(File::IOFile &pFile, SChunkHeader &header, std::string *title) {
if (!pFile) {
ERROR_LOG(COMMON, "ChunkReader: Can't open file for reading");
return ERROR_BAD_FILE;
}
// read the header
SChunkHeader header;
if (!pFile.ReadArray(&header, 1))
{
const u64 fileSize = pFile.GetSize();
u64 headerSize = sizeof(SChunkHeader);
if (fileSize < headerSize) {
ERROR_LOG(COMMON, "ChunkReader: File too small");
return ERROR_BAD_FILE;
}
if (!pFile.ReadArray(&header, 1)) {
ERROR_LOG(COMMON, "ChunkReader: Bad header size");
return ERROR_BAD_FILE;
}
// Check revision
if (header.Revision != _Revision)
{
ERROR_LOG(COMMON, "ChunkReader: Wrong file revision, got %d expected %d", header.Revision, _Revision);
if (header.Revision < REVISION_MIN) {
ERROR_LOG(COMMON, "ChunkReader: Wrong file revision, got %d expected >= %d", header.Revision, REVISION_MIN);
return ERROR_BAD_FILE;
}
// get size
sz = (int)(fileSize - headerSize);
if (header.ExpectedSize != sz)
{
ERROR_LOG(COMMON, "ChunkReader: Bad file size, got %u expected %u", (u32)sz, header.ExpectedSize);
if (header.Revision >= REVISION_TITLE) {
char titleFixed[128];
if (!pFile.ReadArray(titleFixed, sizeof(titleFixed))) {
ERROR_LOG(COMMON, "ChunkReader: Unable to read title");
return ERROR_BAD_FILE;
}
if (title) {
*title = titleFixed;
}
headerSize += 128;
} else if (title) {
title->clear();
}
u32 sz = (u32)(fileSize - headerSize);
if (header.ExpectedSize != sz) {
ERROR_LOG(COMMON, "ChunkReader: Bad file size, got %u expected %u", sz, header.ExpectedSize);
return ERROR_BAD_FILE;
}
return ERROR_NONE;
}
CChunkFileReader::Error CChunkFileReader::GetFileTitle(const std::string &filename, std::string *title) {
if (!File::Exists(filename)) {
ERROR_LOG(COMMON, "ChunkReader: File doesn't exist");
return ERROR_BAD_FILE;
}
File::IOFile pFile(filename, "rb");
SChunkHeader header;
return LoadFileHeader(pFile, header, title);
}
CChunkFileReader::Error CChunkFileReader::LoadFile(const std::string &filename, const char *gitVersion, u8 *&_buffer, size_t &sz, std::string *failureReason) {
if (!File::Exists(filename)) {
*failureReason = "LoadStateDoesntExist";
ERROR_LOG(COMMON, "ChunkReader: File doesn't exist");
return ERROR_BAD_FILE;
}
File::IOFile pFile(filename, "rb");
SChunkHeader header;
Error err = LoadFileHeader(pFile, header, nullptr);
if (err != ERROR_NONE) {
return err;
}
// read the state
sz = header.ExpectedSize;
u8 *buffer = new u8[sz];
if (!pFile.ReadBytes(buffer, sz))
{
@ -235,10 +263,10 @@ CChunkFileReader::Error CChunkFileReader::LoadFile(const std::string& _rFilename
}
// Takes ownership of buffer.
CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string& _rFilename, int _Revision, const char *_VersionString, u8 *buffer, size_t sz) {
INFO_LOG(COMMON, "ChunkReader: Writing %s" , _rFilename.c_str());
CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string &filename, const std::string &title, const char *gitVersion, u8 *buffer, size_t sz) {
INFO_LOG(COMMON, "ChunkReader: Writing %s", filename.c_str());
File::IOFile pFile(_rFilename, "wb");
File::IOFile pFile(filename, "wb");
if (!pFile)
{
ERROR_LOG(COMMON, "ChunkReader: Error opening file for write");
@ -251,12 +279,17 @@ CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string& _rFilename
// Create header
SChunkHeader header;
header.Compress = compress ? 1 : 0;
header.Revision = _Revision;
header.Revision = REVISION_CURRENT;
header.ExpectedSize = (u32)sz;
header.UncompressedSize = (u32)sz;
strncpy(header.GitVersion, _VersionString, 32);
strncpy(header.GitVersion, gitVersion, 32);
header.GitVersion[31] = '\0';
// Setup the fixed-length title.
char titleFixed[128];
strncpy(titleFixed, title.c_str(), sizeof(titleFixed));
titleFixed[sizeof(titleFixed) - 1] = '\0';
// Write to file
if (compress) {
size_t comp_len = snappy_max_compressed_length(sz);
@ -264,11 +297,14 @@ CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string& _rFilename
snappy_compress((const char *)buffer, sz, (char *)compressed_buffer, &comp_len);
delete [] buffer;
header.ExpectedSize = (u32)comp_len;
if (!pFile.WriteArray(&header, 1))
{
if (!pFile.WriteArray(&header, 1)) {
ERROR_LOG(COMMON, "ChunkReader: Failed writing header");
return ERROR_BAD_FILE;
}
if (!pFile.WriteArray(titleFixed, sizeof(titleFixed))) {
ERROR_LOG(COMMON, "ChunkReader: Failed writing title");
return ERROR_BAD_FILE;
}
if (!pFile.WriteBytes(&compressed_buffer[0], comp_len)) {
ERROR_LOG(COMMON, "ChunkReader: Failed writing compressed data");
return ERROR_BAD_FILE;
@ -292,6 +328,6 @@ CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string& _rFilename
delete [] buffer;
}
INFO_LOG(COMMON, "ChunkReader: Done writing %s", _rFilename.c_str());
INFO_LOG(COMMON, "ChunkReader: Done writing %s", filename.c_str());
return ERROR_NONE;
}

View file

@ -632,28 +632,28 @@ public:
// Load file template
template<class T>
static Error Load(const std::string& _rFilename, int _Revision, const char *_VersionString, T& _class, std::string* _failureReason)
static Error Load(const std::string &filename, const char *gitVersion, T& _class, std::string *failureReason)
{
*_failureReason = "LoadStateWrongVersion";
*failureReason = "LoadStateWrongVersion";
u8 *ptr = nullptr;
size_t sz;
Error error = LoadFile(_rFilename, _Revision, _VersionString, ptr, sz, _failureReason);
Error error = LoadFile(filename, gitVersion, ptr, sz, failureReason);
if (error == ERROR_NONE) {
error = LoadPtr(ptr, _class);
delete [] ptr;
}
INFO_LOG(COMMON, "ChunkReader: Done loading %s" , _rFilename.c_str());
INFO_LOG(COMMON, "ChunkReader: Done loading %s", filename.c_str());
if (error == ERROR_NONE) {
_failureReason->clear();
failureReason->clear();
}
return error;
}
// Save file template
template<class T>
static Error Save(const std::string& _rFilename, int _Revision, const char *_VersionString, T& _class)
static Error Save(const std::string &filename, const std::string &title, const char *gitVersion, T& _class)
{
// Get data
size_t const sz = MeasurePtr(_class);
@ -662,7 +662,7 @@ public:
// SaveFile takes ownership of buffer
if (error == ERROR_NONE)
error = SaveFile(_rFilename, _Revision, _VersionString, buffer, sz);
error = SaveFile(filename, title, gitVersion, buffer, sz);
return error;
}
@ -690,10 +690,9 @@ public:
return ERROR_NONE;
}
private:
static CChunkFileReader::Error LoadFile(const std::string& _rFilename, int _Revision, const char *_VersionString, u8 *&buffer, size_t &sz, std::string *_failureReason);
static CChunkFileReader::Error SaveFile(const std::string& _rFilename, int _Revision, const char *_VersionString, u8 *buffer, size_t sz);
static Error GetFileTitle(const std::string &filename, std::string *title);
private:
struct SChunkHeader
{
int Revision;
@ -702,4 +701,14 @@ private:
u32 UncompressedSize;
char GitVersion[32];
};
enum {
REVISION_MIN = 4,
REVISION_TITLE = 5,
REVISION_CURRENT = REVISION_TITLE,
};
static Error LoadFile(const std::string &filename, const char *gitVersion, u8 *&buffer, size_t &sz, std::string *failureReason);
static Error SaveFile(const std::string &filename, const std::string &title, const char *gitVersion, u8 *buffer, size_t sz);
static Error LoadFileHeader(File::IOFile &pFile, SChunkHeader &header, std::string *title);
};

View file

@ -541,7 +541,7 @@ namespace SaveState
{
case SAVESTATE_LOAD:
INFO_LOG(COMMON, "Loading state from %s", op.filename.c_str());
result = CChunkFileReader::Load(op.filename, REVISION, PPSSPP_GIT_VERSION, state, &reason);
result = CChunkFileReader::Load(op.filename, PPSSPP_GIT_VERSION, state, &reason);
if (result == CChunkFileReader::ERROR_NONE) {
osm.Show(sc->T("Loaded State"), 2.0);
callbackResult = true;
@ -559,7 +559,7 @@ namespace SaveState
case SAVESTATE_SAVE:
INFO_LOG(COMMON, "Saving state to %s", op.filename.c_str());
result = CChunkFileReader::Save(op.filename, REVISION, PPSSPP_GIT_VERSION, state);
result = CChunkFileReader::Save(op.filename, g_paramSFO.GetValueString("TITLE"), PPSSPP_GIT_VERSION, state);
if (result == CChunkFileReader::ERROR_NONE) {
osm.Show(sc->T("Saved State"), 2.0);

View file

@ -25,8 +25,6 @@ namespace SaveState
{
typedef std::function<void(bool status, void *cbUserData)> Callback;
// TODO: Better place for this?
const int REVISION = 4;
const int SAVESTATESLOTS = 5;
void Init();