From 14ef840dd3057dc2df925c5aba6d8341bedb39fd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 28 Dec 2012 17:23:05 -0800 Subject: [PATCH 1/5] Add a command line option to load a state. For debugging. --- Core/SaveState.cpp | 8 +++++++- Windows/main.cpp | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index aa74d0cebe..11f5945c10 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -87,7 +87,7 @@ namespace SaveState // Don't actually run it until next CoreTiming::Advance(). // It's possible there might be a duplicate but it won't hurt us. - if (Core_IsStepping()) + if (Core_IsStepping() && __KernelIsRunning()) { // Warning: this may run on a different thread. Process(0, 0); @@ -122,6 +122,12 @@ namespace SaveState void Process(u64 userdata, int cyclesLate) { + if (!__KernelIsRunning()) + { + ERROR_LOG(COMMON, "Savestate failure: Unable to load without kernel, this should never happen."); + return; + } + std::vector operations = Flush(); SaveStart state; diff --git a/Windows/main.cpp b/Windows/main.cpp index d2f9b186ac..75d1ec9e92 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -21,6 +21,7 @@ #include "file/zip_read.h" #include "../Core/Config.h" +#include "../Core/SaveState.h" #include "EmuThread.h" #include "LogManager.h" @@ -53,6 +54,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin const char *fileToStart = NULL; const char *fileToLog = NULL; + const char *stateToLoad = NULL; bool hideLog = true; bool autoRun = true; @@ -93,6 +95,10 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin fileToLog = __argv[++i]; if (!strncmp(__argv[i], "--log=", strlen("--log=")) && strlen(__argv[i]) > strlen("--log=")) fileToLog = __argv[i] + strlen("--log="); + if (!strcmp(__argv[i], "--state") && i < __argc - 1) + stateToLoad = __argv[++i]; + if (!strncmp(__argv[i], "--state=", strlen("--state=")) && strlen(__argv[i]) > strlen("--state=")) + stateToLoad = __argv[i] + strlen("--state="); break; } } @@ -158,6 +164,8 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin if (autoRun) MainWindow::SetNextState(CORE_RUNNING); + if (fileToStart != NULL && stateToLoad != NULL) + SaveState::Load(stateToLoad); //so.. we're at the message pump of the GUI thread MSG msg; From da551d71c3f7e911b612ab8809417d3b0a1fd955 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 28 Dec 2012 17:24:14 -0800 Subject: [PATCH 2/5] Disable load/save while no iso loaded. --- Windows/WndMainWindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index 5237026b8b..aa8347af09 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -656,6 +656,8 @@ namespace MainWindow enable = g_State.bEmuThreadStarted ? MF_GRAYED : MF_ENABLED; EnableMenuItem(menu,ID_FILE_LOAD,enable); + EnableMenuItem(menu,ID_FILE_SAVESTATE,!enable); + EnableMenuItem(menu,ID_FILE_LOADSTATE,!enable); EnableMenuItem(menu,ID_CPU_DYNAREC,enable); EnableMenuItem(menu,ID_CPU_INTERPRETER,enable); EnableMenuItem(menu,ID_CPU_FASTINTERPRETER,enable); From df627f77388f5d18cc299ce465fe5164e15aecbb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 28 Dec 2012 18:13:32 -0800 Subject: [PATCH 3/5] Save states using gzip, load either way. Could implement an option to save states uncompressed. There's a small performance hit. Test files were 8% - 14% the size, but these are just from menus/etc. --- Common/ChunkFile.h | 14 ++++----- Common/FileUtil.cpp | 77 ++++++++++++++++++++++++++++++++++++--------- Common/FileUtil.h | 41 +++++++++++++++++++----- 3 files changed, 104 insertions(+), 28 deletions(-) diff --git a/Common/ChunkFile.h b/Common/ChunkFile.h index 1671cecc8a..b97a33b90e 100644 --- a/Common/ChunkFile.h +++ b/Common/ChunkFile.h @@ -369,7 +369,7 @@ public: if (!File::Exists(_rFilename)) return false; - // Check file size + // Check file size - most probably it can't compress THAT well. const u64 fileSize = File::GetSize(_rFilename); static const u64 headerSize = sizeof(SChunkHeader); if (fileSize < headerSize) @@ -378,7 +378,7 @@ public: return false; } - File::IOFile pFile(_rFilename, "rb"); + File::IOFile pFile(_rFilename, "rb", File::METHOD_GZIP); if (!pFile) { ERROR_LOG(COMMON,"ChunkReader: Can't open file for reading"); @@ -402,11 +402,11 @@ public: } // get size - const int sz = (int)(fileSize - headerSize); - if (header.ExpectedSize != sz) + const int sz = header.ExpectedSize; + if (sz > 128 * 1024 * 1024) { - ERROR_LOG(COMMON,"ChunkReader: Bad file size, got %d expected %d", - sz, header.ExpectedSize); + ERROR_LOG(COMMON,"ChunkReader: File too large, got %d bytes", + header.ExpectedSize); return false; } @@ -432,7 +432,7 @@ public: static bool Save(const std::string& _rFilename, int _Revision, T& _class) { INFO_LOG(COMMON, "ChunkReader: Writing %s" , _rFilename.c_str()); - File::IOFile pFile(_rFilename, "wb"); + File::IOFile pFile(_rFilename, "wb9", File::METHOD_GZIP); if (!pFile) { ERROR_LOG(COMMON,"ChunkReader: Error opening file for write"); diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index 8ba82fa901..47207e00a7 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -767,17 +767,17 @@ bool ReadFileToString(bool text_file, const char *filename, std::string &str) } IOFile::IOFile() - : m_file(NULL), m_good(true) + : m_file(NULL), m_zFile(NULL), m_good(true) {} IOFile::IOFile(std::FILE* file) - : m_file(file), m_good(true) + : m_file(file), m_zFile(NULL), m_good(true) {} -IOFile::IOFile(const std::string& filename, const char openmode[]) - : m_file(NULL), m_good(true) +IOFile::IOFile(const std::string& filename, const char openmode[], IOFileMethod method) + : m_file(NULL), m_zFile(NULL), m_good(true), m_method(method) { - Open(filename, openmode); + Open(filename, openmode, method); } IOFile::~IOFile() @@ -785,14 +785,21 @@ IOFile::~IOFile() Close(); } -bool IOFile::Open(const std::string& filename, const char openmode[]) +bool IOFile::Open(const std::string& filename, const char openmode[], IOFileMethod method) { Close(); + + m_method = method; + if (0 != (m_method & METHOD_GZIP)) + m_zFile = gzopen(filename.c_str(), openmode); + else + { #ifdef _WIN32 - fopen_s(&m_file, filename.c_str(), openmode); + fopen_s(&m_file, filename.c_str(), openmode); #else - m_file = fopen(filename.c_str(), openmode); + m_file = fopen(filename.c_str(), openmode); #endif + } m_good = IsOpen(); return m_good; @@ -800,10 +807,19 @@ bool IOFile::Open(const std::string& filename, const char openmode[]) bool IOFile::Close() { - if (!IsOpen() || 0 != std::fclose(m_file)) - m_good = false; + if (0 != (m_method & METHOD_GZIP)) + { + if (Z_OK != gzclose(m_zFile)) + m_good = false; + m_zFile = NULL; + } + else + { + if (!IsOpen() || 0 != std::fclose(m_file)) + m_good = false; + m_file = NULL; + } - m_file = NULL; return m_good; } @@ -819,19 +835,32 @@ void IOFile::SetHandle(std::FILE* file) Close(); Clear(); m_file = file; + m_method = METHOD_STANDARD; } u64 IOFile::GetSize() { if (IsOpen()) + { + // Not supported. + if (0 != (m_method & METHOD_GZIP)) + return 0; return File::GetSize(m_file); + } else return 0; } bool IOFile::Seek(s64 off, int origin) { - if (!IsOpen() || 0 != fseeko(m_file, off, origin)) + if (!IsOpen()) + m_good = false; + else if (0 != (m_method & METHOD_GZIP)) + { + if (-1 == gzseek(m_zFile, (z_off_t) off, origin)) + m_good = false; + } + else if (0 != fseeko(m_file, off, origin)) m_good = false; return m_good; @@ -840,14 +869,26 @@ bool IOFile::Seek(s64 off, int origin) u64 IOFile::Tell() { if (IsOpen()) + { + if (0 != (m_method & METHOD_GZIP)) + return gztell(m_zFile); + return ftello(m_file); + } else return -1; } bool IOFile::Flush() { - if (!IsOpen() || 0 != std::fflush(m_file)) + if (!IsOpen()) + m_good = false; + else if (0 != (m_method & METHOD_GZIP)) + { + if (-1 == gzflush(m_zFile, Z_PARTIAL_FLUSH)) + m_good = false; + } + else if (0 != std::fflush(m_file)) m_good = false; return m_good; @@ -855,7 +896,15 @@ bool IOFile::Flush() bool IOFile::Resize(u64 size) { - if (!IsOpen() || 0 != + if (!IsOpen()) + m_good = false; + else if (0 != (m_method & METHOD_GZIP)) + { + // gzseek() adds nulls to the seeked location. + if (-1 == gzseek(m_zFile, (z_off_t) size, SEEK_SET)) + m_good = false; + } + else if (0 != #ifdef _WIN32 // ector: _chsize sucks, not 64-bit safe // F|RES: changed to _chsize_s. i think it is 64-bit safe diff --git a/Common/FileUtil.h b/Common/FileUtil.h index 4c9a86f5cf..12a79ea730 100644 --- a/Common/FileUtil.h +++ b/Common/FileUtil.h @@ -25,6 +25,7 @@ #include #include "Common.h" +#include "../ext/zlib/zlib.h" // User directory indices for GetUserPath enum { @@ -124,6 +125,13 @@ std::string &GetExeDirectory(); bool WriteStringToFile(bool text_file, const std::string &str, const char *filename); bool ReadFileToString(bool text_file, const char *filename, std::string &str); +enum IOFileMethod +{ + METHOD_STANDARD = 0x00, + // Warning: may not be 64-bit safe. + METHOD_GZIP = 0x01, +}; + // simple wrapper for cstdlib file functions to // hopefully will make error checking easier // and make forgetting an fclose() harder @@ -132,17 +140,24 @@ class IOFile : NonCopyable public: IOFile(); IOFile(std::FILE* file); - IOFile(const std::string& filename, const char openmode[]); + IOFile(const std::string& filename, const char openmode[], IOFileMethod method = METHOD_STANDARD); ~IOFile(); - bool Open(const std::string& filename, const char openmode[]); + bool Open(const std::string& filename, const char openmode[], IOFileMethod method = METHOD_STANDARD); bool Close(); template bool ReadArray(T* data, size_t length) { - if (!IsOpen() || length != std::fread(data, sizeof(T), length, m_file)) + if (!IsOpen()) + m_good = false; + else if (0 != (m_method & METHOD_GZIP)) + { + if (sizeof(T) * length != gzread(m_zFile, data, sizeof(T) * length)) + m_good = false; + } + else if (length != std::fread(data, sizeof(T), length, m_file)) m_good = false; return m_good; @@ -151,7 +166,14 @@ public: template bool WriteArray(const T* data, size_t length) { - if (!IsOpen() || length != std::fwrite(data, sizeof(T), length, m_file)) + if (!IsOpen()) + m_good = false; + else if (0 != (m_method & METHOD_GZIP)) + { + if (sizeof(T) * length != gzwrite(m_zFile, data, sizeof(T) * length)) + m_good = false; + } + else if (length != std::fwrite(data, sizeof(T), length, m_file)) m_good = false; return m_good; @@ -167,11 +189,11 @@ public: return WriteArray(reinterpret_cast(data), length); } - bool IsOpen() { return NULL != m_file; } + bool IsOpen() { return NULL != m_file || NULL != m_zFile; } // m_good is set to false when a read, write or other function fails bool IsGood() { return m_good; } - operator void*() { return m_good ? m_file : NULL; } + operator void*() { return m_good ? (NULL != m_file ? m_file : m_zFile) : NULL; } std::FILE* ReleaseHandle(); @@ -189,13 +211,18 @@ public: void Clear() { m_good = true; #undef clearerr - std::clearerr(m_file); + if (NULL != m_file) + std::clearerr(m_file); + if (NULL != m_zFile) + gzclearerr(m_zFile); } private: IOFile& operator=(const IOFile&) /*= delete*/; std::FILE* m_file; + gzFile m_zFile; + IOFileMethod m_method; bool m_good; }; From e03044b2416983889b9bedb7d069c694d830bc66 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 28 Dec 2012 23:30:43 -0800 Subject: [PATCH 4/5] Fix a crash with FPL objects in save states. --- Core/HLE/sceKernelMemory.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 97ce91136b..9ac86fc5ae 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -86,6 +86,8 @@ struct FPL : public KernelObject virtual void DoState(PointerWrap &p) { p.Do(nf); + if (p.mode == p.MODE_READ) + blocks = new bool[nf.numBlocks]; p.DoArray(blocks, nf.numBlocks); p.Do(address); p.DoMarker("FPL"); From 1d8b90259147e0c21ab0f97ff90e151a6e855251 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 28 Dec 2012 23:57:11 -0800 Subject: [PATCH 5/5] Revert "Save states using gzip, load either way." This reverts commit df627f77388f5d18cc299ce465fe5164e15aecbb. --- Common/ChunkFile.h | 14 ++++----- Common/FileUtil.cpp | 77 +++++++++------------------------------------ Common/FileUtil.h | 41 +++++------------------- 3 files changed, 28 insertions(+), 104 deletions(-) diff --git a/Common/ChunkFile.h b/Common/ChunkFile.h index b97a33b90e..1671cecc8a 100644 --- a/Common/ChunkFile.h +++ b/Common/ChunkFile.h @@ -369,7 +369,7 @@ public: if (!File::Exists(_rFilename)) return false; - // Check file size - most probably it can't compress THAT well. + // Check file size const u64 fileSize = File::GetSize(_rFilename); static const u64 headerSize = sizeof(SChunkHeader); if (fileSize < headerSize) @@ -378,7 +378,7 @@ public: return false; } - File::IOFile pFile(_rFilename, "rb", File::METHOD_GZIP); + File::IOFile pFile(_rFilename, "rb"); if (!pFile) { ERROR_LOG(COMMON,"ChunkReader: Can't open file for reading"); @@ -402,11 +402,11 @@ public: } // get size - const int sz = header.ExpectedSize; - if (sz > 128 * 1024 * 1024) + const int sz = (int)(fileSize - headerSize); + if (header.ExpectedSize != sz) { - ERROR_LOG(COMMON,"ChunkReader: File too large, got %d bytes", - header.ExpectedSize); + ERROR_LOG(COMMON,"ChunkReader: Bad file size, got %d expected %d", + sz, header.ExpectedSize); return false; } @@ -432,7 +432,7 @@ public: static bool Save(const std::string& _rFilename, int _Revision, T& _class) { INFO_LOG(COMMON, "ChunkReader: Writing %s" , _rFilename.c_str()); - File::IOFile pFile(_rFilename, "wb9", File::METHOD_GZIP); + File::IOFile pFile(_rFilename, "wb"); if (!pFile) { ERROR_LOG(COMMON,"ChunkReader: Error opening file for write"); diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index 47207e00a7..8ba82fa901 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -767,17 +767,17 @@ bool ReadFileToString(bool text_file, const char *filename, std::string &str) } IOFile::IOFile() - : m_file(NULL), m_zFile(NULL), m_good(true) + : m_file(NULL), m_good(true) {} IOFile::IOFile(std::FILE* file) - : m_file(file), m_zFile(NULL), m_good(true) + : m_file(file), m_good(true) {} -IOFile::IOFile(const std::string& filename, const char openmode[], IOFileMethod method) - : m_file(NULL), m_zFile(NULL), m_good(true), m_method(method) +IOFile::IOFile(const std::string& filename, const char openmode[]) + : m_file(NULL), m_good(true) { - Open(filename, openmode, method); + Open(filename, openmode); } IOFile::~IOFile() @@ -785,21 +785,14 @@ IOFile::~IOFile() Close(); } -bool IOFile::Open(const std::string& filename, const char openmode[], IOFileMethod method) +bool IOFile::Open(const std::string& filename, const char openmode[]) { Close(); - - m_method = method; - if (0 != (m_method & METHOD_GZIP)) - m_zFile = gzopen(filename.c_str(), openmode); - else - { #ifdef _WIN32 - fopen_s(&m_file, filename.c_str(), openmode); + fopen_s(&m_file, filename.c_str(), openmode); #else - m_file = fopen(filename.c_str(), openmode); + m_file = fopen(filename.c_str(), openmode); #endif - } m_good = IsOpen(); return m_good; @@ -807,19 +800,10 @@ bool IOFile::Open(const std::string& filename, const char openmode[], IOFileMeth bool IOFile::Close() { - if (0 != (m_method & METHOD_GZIP)) - { - if (Z_OK != gzclose(m_zFile)) - m_good = false; - m_zFile = NULL; - } - else - { - if (!IsOpen() || 0 != std::fclose(m_file)) - m_good = false; - m_file = NULL; - } + if (!IsOpen() || 0 != std::fclose(m_file)) + m_good = false; + m_file = NULL; return m_good; } @@ -835,32 +819,19 @@ void IOFile::SetHandle(std::FILE* file) Close(); Clear(); m_file = file; - m_method = METHOD_STANDARD; } u64 IOFile::GetSize() { if (IsOpen()) - { - // Not supported. - if (0 != (m_method & METHOD_GZIP)) - return 0; return File::GetSize(m_file); - } else return 0; } bool IOFile::Seek(s64 off, int origin) { - if (!IsOpen()) - m_good = false; - else if (0 != (m_method & METHOD_GZIP)) - { - if (-1 == gzseek(m_zFile, (z_off_t) off, origin)) - m_good = false; - } - else if (0 != fseeko(m_file, off, origin)) + if (!IsOpen() || 0 != fseeko(m_file, off, origin)) m_good = false; return m_good; @@ -869,26 +840,14 @@ bool IOFile::Seek(s64 off, int origin) u64 IOFile::Tell() { if (IsOpen()) - { - if (0 != (m_method & METHOD_GZIP)) - return gztell(m_zFile); - return ftello(m_file); - } else return -1; } bool IOFile::Flush() { - if (!IsOpen()) - m_good = false; - else if (0 != (m_method & METHOD_GZIP)) - { - if (-1 == gzflush(m_zFile, Z_PARTIAL_FLUSH)) - m_good = false; - } - else if (0 != std::fflush(m_file)) + if (!IsOpen() || 0 != std::fflush(m_file)) m_good = false; return m_good; @@ -896,15 +855,7 @@ bool IOFile::Flush() bool IOFile::Resize(u64 size) { - if (!IsOpen()) - m_good = false; - else if (0 != (m_method & METHOD_GZIP)) - { - // gzseek() adds nulls to the seeked location. - if (-1 == gzseek(m_zFile, (z_off_t) size, SEEK_SET)) - m_good = false; - } - else if (0 != + if (!IsOpen() || 0 != #ifdef _WIN32 // ector: _chsize sucks, not 64-bit safe // F|RES: changed to _chsize_s. i think it is 64-bit safe diff --git a/Common/FileUtil.h b/Common/FileUtil.h index 12a79ea730..4c9a86f5cf 100644 --- a/Common/FileUtil.h +++ b/Common/FileUtil.h @@ -25,7 +25,6 @@ #include #include "Common.h" -#include "../ext/zlib/zlib.h" // User directory indices for GetUserPath enum { @@ -125,13 +124,6 @@ std::string &GetExeDirectory(); bool WriteStringToFile(bool text_file, const std::string &str, const char *filename); bool ReadFileToString(bool text_file, const char *filename, std::string &str); -enum IOFileMethod -{ - METHOD_STANDARD = 0x00, - // Warning: may not be 64-bit safe. - METHOD_GZIP = 0x01, -}; - // simple wrapper for cstdlib file functions to // hopefully will make error checking easier // and make forgetting an fclose() harder @@ -140,24 +132,17 @@ class IOFile : NonCopyable public: IOFile(); IOFile(std::FILE* file); - IOFile(const std::string& filename, const char openmode[], IOFileMethod method = METHOD_STANDARD); + IOFile(const std::string& filename, const char openmode[]); ~IOFile(); - bool Open(const std::string& filename, const char openmode[], IOFileMethod method = METHOD_STANDARD); + bool Open(const std::string& filename, const char openmode[]); bool Close(); template bool ReadArray(T* data, size_t length) { - if (!IsOpen()) - m_good = false; - else if (0 != (m_method & METHOD_GZIP)) - { - if (sizeof(T) * length != gzread(m_zFile, data, sizeof(T) * length)) - m_good = false; - } - else if (length != std::fread(data, sizeof(T), length, m_file)) + if (!IsOpen() || length != std::fread(data, sizeof(T), length, m_file)) m_good = false; return m_good; @@ -166,14 +151,7 @@ public: template bool WriteArray(const T* data, size_t length) { - if (!IsOpen()) - m_good = false; - else if (0 != (m_method & METHOD_GZIP)) - { - if (sizeof(T) * length != gzwrite(m_zFile, data, sizeof(T) * length)) - m_good = false; - } - else if (length != std::fwrite(data, sizeof(T), length, m_file)) + if (!IsOpen() || length != std::fwrite(data, sizeof(T), length, m_file)) m_good = false; return m_good; @@ -189,11 +167,11 @@ public: return WriteArray(reinterpret_cast(data), length); } - bool IsOpen() { return NULL != m_file || NULL != m_zFile; } + bool IsOpen() { return NULL != m_file; } // m_good is set to false when a read, write or other function fails bool IsGood() { return m_good; } - operator void*() { return m_good ? (NULL != m_file ? m_file : m_zFile) : NULL; } + operator void*() { return m_good ? m_file : NULL; } std::FILE* ReleaseHandle(); @@ -211,18 +189,13 @@ public: void Clear() { m_good = true; #undef clearerr - if (NULL != m_file) - std::clearerr(m_file); - if (NULL != m_zFile) - gzclearerr(m_zFile); + std::clearerr(m_file); } private: IOFile& operator=(const IOFile&) /*= delete*/; std::FILE* m_file; - gzFile m_zFile; - IOFileMethod m_method; bool m_good; };