From 883f61e3ddafc0de17cb78f355f860e8ce4406a6 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 2 Jan 2013 21:00:10 +0100 Subject: [PATCH] Savestates: Add quicksave/quickload. Add Android UI for savestates. All single slot for now. --- Common/StringUtil.cpp | 8 ++ Common/StringUtil.h | 2 + Core/Config.cpp | 3 +- Core/Dialog/SavedataParam.cpp | 14 ++-- Core/ELF/ParamSFO.cpp | 14 ++-- Core/ELF/ParamSFO.h | 3 +- Core/FileSystems/BlockDevices.h | 22 +++--- Core/FileSystems/DirectoryFileSystem.cpp | 5 ++ Core/FileSystems/DirectoryFileSystem.h | 1 + Core/FileSystems/FileSystem.h | 2 + Core/FileSystems/ISOFileSystem.h | 38 ++++----- Core/FileSystems/MetaFileSystem.cpp | 16 ++-- Core/FileSystems/MetaFileSystem.h | 3 + Core/HLE/sceKernel.cpp | 2 +- Core/PSPLoaders.cpp | 5 +- Core/SaveState.cpp | 98 +++++++++++++++++++++--- Core/SaveState.h | 16 +++- Core/System.cpp | 4 +- Core/System.h | 2 + Windows/WndMainWindow.cpp | 38 +++++++-- Windows/WndMainWindow.h | 2 +- Windows/ppsspp.rc | 12 +-- Windows/resource.h | 8 +- android/jni/MenuScreens.cpp | 36 +++++++-- android/jni/NativeApp.cpp | 4 + 25 files changed, 267 insertions(+), 91 deletions(-) diff --git a/Common/StringUtil.cpp b/Common/StringUtil.cpp index 8d96eae2b8..ae0a09b0c4 100644 --- a/Common/StringUtil.cpp +++ b/Common/StringUtil.cpp @@ -376,3 +376,11 @@ std::string UriEncode(const std::string & sSrc) delete [] pStart; return sResult; } + +bool StringEndsWith(std::string const &fullString, std::string const &ending) { + if (fullString.length() >= ending.length()) { + return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); + } else { + return false; + } +} diff --git a/Common/StringUtil.h b/Common/StringUtil.h index a5477a3996..b4282c3533 100644 --- a/Common/StringUtil.h +++ b/Common/StringUtil.h @@ -129,4 +129,6 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st std::string UriDecode(const std::string & sSrc); std::string UriEncode(const std::string & sSrc); +bool StringEndsWith(std::string const &fullString, std::string const &ending); + #endif // _STRINGUTIL_H_ diff --git a/Core/Config.cpp b/Core/Config.cpp index e0ea8cff4e..113860c86a 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -59,7 +59,7 @@ void CConfig::Load(const char *iniFileName) graphics->Get("DisplayFramebuffer", &bDisplayFramebuffer, false); graphics->Get("WindowZoom", &iWindowZoom, 1); graphics->Get("BufferedRendering", &bBufferedRendering, true); - graphics->Get("HardwareTransform", &bHardwareTransform, false); + graphics->Get("HardwareTransform", &bHardwareTransform, true); graphics->Get("LinearFiltering", &bLinearFiltering, false); IniFile::Section *sound = iniFile.GetOrCreateSection("Sound"); @@ -69,7 +69,6 @@ void CConfig::Load(const char *iniFileName) control->Get("ShowStick", &bShowAnalogStick, false); control->Get("ShowTouchControls", &bShowTouchControls, true); - // Ephemeral settings bDrawWireframe = false; } diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 0c34ce9e63..24b187a025 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -226,8 +226,8 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, int saveId) PSPFileInfo sfoInfo = pspFileSystem.GetFileInfo(sfopath); if(sfoInfo.exists) // Read old sfo if exist { - u8 *sfoData = new u8[sfoInfo.size]; - size_t sfoSize = sfoInfo.size; + u8 *sfoData = new u8[(size_t)sfoInfo.size]; + size_t sfoSize = (size_t)sfoInfo.size; if(ReadPSPFile(sfopath,sfoData,sfoSize)) { sfoFile.ReadSFO(sfoData,sfoSize); @@ -475,7 +475,7 @@ bool SavedataParam::GetList(SceUtilitySavedataParam *param) if (Memory::IsValidAddress(outputBuffer)) { std::string searchString = GetGameName(param)+GetSaveName(param); - for(int i = 0; i < allDir.size() && i < maxFile; i++) + for (size_t i = 0; i < allDir.size() && i < maxFile; i++) { std::string dirName = allDir[i].name; if(PSPMatch(dirName, searchString)) @@ -484,7 +484,7 @@ bool SavedataParam::GetList(SceUtilitySavedataParam *param) } } - for(int i = 0; i < validDir.size(); i++) + for (size_t i = 0; i < validDir.size(); i++) { u32 baseAddr = outputBuffer + (i*72); Memory::Write_U32(0x11FF,baseAddr + 0); // mode @@ -514,7 +514,7 @@ bool SavedataParam::GetFilesList(SceUtilitySavedataParam *param) } u32 dataAddr = param->fileListAddr; - if(!Memory::IsValidAddress(dataAddr)) + if (!Memory::IsValidAddress(dataAddr)) return false; // TODO : Need to be checked against more game @@ -524,7 +524,7 @@ bool SavedataParam::GetFilesList(SceUtilitySavedataParam *param) //for Valkyria2, dataAddr+0 and dataAddr+12 has "5" for 5 files int numFiles = Memory::Read_U32(dataAddr+12); int foundFiles = 0; - for(int i = 0; i < numFiles; i++) + for (int i = 0; i < numFiles; i++) { // for each file (80 bytes): // u32 mode, u32 ??, u64 size, u64 ctime, u64 ??, u64 atime, u64 ???, u64 mtime, u64 ??? @@ -535,7 +535,7 @@ bool SavedataParam::GetFilesList(SceUtilitySavedataParam *param) strncpy(fileName, Memory::GetCharPointer(curFileInfoAddr + 64),16); std::string filePath = savePath + GetGameName(param) + GetSaveName(param) + "/" + fileName; PSPFileInfo info = pspFileSystem.GetFileInfo(filePath); - if(info.exists) + if (info.exists) { Memory::Write_U32(0x21FF, curFileInfoAddr+0); Memory::Write_U64(info.size, curFileInfoAddr+8); diff --git a/Core/ELF/ParamSFO.cpp b/Core/ELF/ParamSFO.cpp index 7965c1455b..bfba51a6cd 100644 --- a/Core/ELF/ParamSFO.cpp +++ b/Core/ELF/ParamSFO.cpp @@ -147,7 +147,7 @@ bool ParamSFOData::WriteSFO(u8 **paramsfo, size_t *size) total_size += sizeof(Header); // Get size info - for(std::map::iterator it = values.begin(); it != values.end(); it++) + for (std::map::iterator it = values.begin(); it != values.end(); it++) { key_size += it->first.size()+1; data_size += it->second.max_size; @@ -168,29 +168,29 @@ bool ParamSFOData::WriteSFO(u8 **paramsfo, size_t *size) u8* data = new u8[total_size]; *paramsfo = data; - memset(data,0,total_size); - memcpy(data,&header,sizeof(Header)); + memset(data, 0, total_size); + memcpy(data, &header, sizeof(Header)); // Now fill IndexTable *index_ptr = (IndexTable*)(data + sizeof(Header)); u8* key_ptr = data + header.key_table_start; u8* data_ptr = data + header.data_table_start; - for(std::map::iterator it = values.begin(); it != values.end(); it++) + for (std::map::iterator it = values.begin(); it != values.end(); it++) { u16 offset = (u16)(key_ptr - (data+header.key_table_start)); index_ptr->key_table_offset = offset; offset = (u16)(data_ptr - (data+header.data_table_start)); index_ptr->data_table_offset = offset; index_ptr->param_max_len = it->second.max_size; - if(it->second.type == VT_INT) + if (it->second.type == VT_INT) { index_ptr->param_fmt = 0x0404; index_ptr->param_len = 4; *(int*)data_ptr = it->second.i_value; } - else if(it->second.type == VT_UTF8_SPE) + else if (it->second.type == VT_UTF8_SPE) { index_ptr->param_fmt = 0x0004; index_ptr->param_len = it->second.u_size; @@ -198,7 +198,7 @@ bool ParamSFOData::WriteSFO(u8 **paramsfo, size_t *size) memset(data_ptr,0,index_ptr->param_max_len); memcpy(data_ptr,it->second.u_value,index_ptr->param_len); } - else if(it->second.type == VT_UTF8) + else if (it->second.type == VT_UTF8) { index_ptr->param_fmt = 0x0204; index_ptr->param_len = it->second.s_value.size()+1; diff --git a/Core/ELF/ParamSFO.h b/Core/ELF/ParamSFO.h index 5f842dfecf..1620563dd0 100644 --- a/Core/ELF/ParamSFO.h +++ b/Core/ELF/ParamSFO.h @@ -33,14 +33,15 @@ public: bool ReadSFO(const u8 *paramsfo, size_t size); bool WriteSFO(u8 **paramsfo, size_t *size); -private: +private: enum ValueType { VT_INT, VT_UTF8, VT_UTF8_SPE // raw data in u8 }; + class ValueData { public: diff --git a/Core/FileSystems/BlockDevices.h b/Core/FileSystems/BlockDevices.h index 626c11e6ed..48faa59cad 100644 --- a/Core/FileSystems/BlockDevices.h +++ b/Core/FileSystems/BlockDevices.h @@ -38,28 +38,32 @@ public: class CISOFileBlockDevice : public BlockDevice { +public: + CISOFileBlockDevice(std::string _filename); + ~CISOFileBlockDevice(); + bool ReadBlock(int blockNumber, u8 *outPtr); + int GetNumBlocks() { return numBlocks;} + +private: std::string filename; FILE *f; u32 *index; int indexShift; u32 blockSize; int numBlocks; -public: - CISOFileBlockDevice(std::string _filename); - ~CISOFileBlockDevice(); - bool ReadBlock(int blockNumber, u8 *outPtr); - int GetNumBlocks() { return numBlocks;} }; class FileBlockDevice : public BlockDevice { - std::string filename; - FILE *f; - size_t filesize; public: FileBlockDevice(std::string _filename); ~FileBlockDevice(); bool ReadBlock(int blockNumber, u8 *outPtr); - int GetNumBlocks() {return (int)(filesize/GetBlockSize());} + int GetNumBlocks() {return (int)(filesize / GetBlockSize());} + +private: + std::string filename; + FILE *f; + size_t filesize; }; diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index cbf7bb9e80..523661e9fd 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -510,6 +510,11 @@ PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) { return x; } +bool DirectoryFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) { + outpath = GetLocalPath(inpath); + return true; +} + std::vector DirectoryFileSystem::GetDirListing(std::string path) { std::vector myVector; #ifdef _WIN32 diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index 6cb334fc53..739a8427ad 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -67,6 +67,7 @@ public: bool RmDir(const std::string &dirname); bool RenameFile(const std::string &from, const std::string &to); bool DeleteFile(const std::string &filename); + bool GetHostPath(const std::string &inpath, std::string &outpath); private: struct OpenFileEntry { diff --git a/Core/FileSystems/FileSystem.h b/Core/FileSystems/FileSystem.h index 4d8670effd..87fc1b3f83 100644 --- a/Core/FileSystems/FileSystem.h +++ b/Core/FileSystems/FileSystem.h @@ -103,6 +103,7 @@ public: virtual bool RmDir(const std::string &dirname) = 0; virtual bool RenameFile(const std::string &from, const std::string &to) = 0; virtual bool DeleteFile(const std::string &filename) = 0; + virtual bool GetHostPath(const std::string &inpath, std::string &outpath) = 0; }; @@ -122,6 +123,7 @@ public: virtual bool RmDir(const std::string &dirname) {return false;} virtual bool RenameFile(const std::string &from, const std::string &to) {return false;} virtual bool DeleteFile(const std::string &filename) {return false;} + virtual bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;} }; diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index f07b25850d..ab90080829 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -27,6 +27,26 @@ class ISOFileSystem : public IFileSystem { +public: + ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice); + ~ISOFileSystem(); + void DoState(PointerWrap &p); + std::vector GetDirListing(std::string path); + u32 OpenFile(std::string filename, FileAccess access); + void CloseFile(u32 handle); + size_t ReadFile(u32 handle, u8 *pointer, s64 size); + size_t SeekFile(u32 handle, s32 position, FileMove type); + PSPFileInfo GetFileInfo(std::string filename); + bool OwnsHandle(u32 handle); + + size_t WriteFile(u32 handle, const u8 *pointer, s64 size); + bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;} + virtual bool MkDir(const std::string &dirname) {return false;} + virtual bool RmDir(const std::string &dirname) {return false;} + virtual bool RenameFile(const std::string &from, const std::string &to) {return false;} + virtual bool DeleteFile(const std::string &filename) {return false;} + +private: struct TreeEntry { TreeEntry(){} @@ -69,22 +89,4 @@ class ISOFileSystem : public IFileSystem void ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root); TreeEntry *GetFromPath(std::string path, bool catchError=true); std::string EntryFullPath(TreeEntry *e); - -public: - ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice); - ~ISOFileSystem(); - void DoState(PointerWrap &p); - std::vector GetDirListing(std::string path); - u32 OpenFile(std::string filename, FileAccess access); - void CloseFile(u32 handle); - size_t ReadFile(u32 handle, u8 *pointer, s64 size); - size_t WriteFile(u32 handle, const u8 *pointer, s64 size); - size_t SeekFile(u32 handle, s32 position, FileMove type); - PSPFileInfo GetFileInfo(std::string filename); - bool OwnsHandle(u32 handle); - - virtual bool MkDir(const std::string &dirname) {return false;} - virtual bool RmDir(const std::string &dirname) {return false;} - virtual bool RenameFile(const std::string &from, const std::string &to) {return false;} - virtual bool DeleteFile(const std::string &filename) {return false;} }; diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index 58ae33fae1..ad144396d8 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include "Common/StringUtil.h" #include "MetaFileSystem.h" static bool ApplyPathStringToComponentsVector(std::vector &vector, const std::string &pathString) @@ -251,14 +252,15 @@ PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename) } } -//TODO: Not sure where this should live. Seems a bit wrong putting it in common -bool stringEndsWith (std::string const &fullString, std::string const &ending) +bool MetaFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) { - if (fullString.length() >= ending.length()) { - return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); - } else { - return false; - } + std::string of; + IFileSystem *system; + if (MapFilePath(inpath, of, &system)) { + return system->GetHostPath(of, outpath); + } else { + return false; + } } std::vector MetaFileSystem::GetDirListing(std::string path) diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index 7fd0a96db2..88fc50d23c 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -41,6 +41,9 @@ public: IFileSystem *GetHandleOwner(u32 handle); bool MapFilePath(const std::string &inpath, std::string &outpath, IFileSystem **system); + // Only possible if a file system is a DirectoryFileSystem or similar. + bool GetHostPath(const std::string &inpath, std::string &outpath); + std::vector GetDirListing(std::string path); u32 OpenFile(std::string filename, FileAccess access); void CloseFile(u32 handle); diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 94eebea14a..e21876fd41 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -78,7 +78,6 @@ void __KernelInit() return; } - SaveState::Init(); __KernelTimeInit(); __InterruptsInit(); __KernelMemoryInit(); @@ -101,6 +100,7 @@ void __KernelInit() __ImposeInit(); __UsbInit(); __FontInit(); + SaveState::Init(); // Must be after IO, as it may create a directory // "Internal" PSP libraries __PPGeInit(); diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index afc8cad456..7915b80d66 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -77,11 +77,10 @@ bool Load_PSP_ISO(const char *filename, std::string *error_string) u32 fd = pspFileSystem.OpenFile(sfoPath, FILEACCESS_READ); pspFileSystem.ReadFile(fd, paramsfo, fileInfo.size); pspFileSystem.CloseFile(fd); - ParamSFOData data; - if (data.ReadSFO(paramsfo, (size_t)fileInfo.size)) + if (g_paramSFO.ReadSFO(paramsfo, (size_t)fileInfo.size)) { char title[1024]; - sprintf(title, "%s : %s", data.GetValueString("DISC_ID").c_str(), data.GetValueString("TITLE").c_str()); + sprintf(title, "%s : %s", g_paramSFO.GetValueString("DISC_ID").c_str(), g_paramSFO.GetValueString("TITLE").c_str()); INFO_LOG(LOADER, "%s", title); host->SetWindowTitle(title); } diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 11f5945c10..215dda6451 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "../Common/StdMutex.h" +#include "../Common/FileUtil.h" #include #include "SaveState.h" @@ -45,14 +46,15 @@ namespace SaveState struct Operation { - Operation(OperationType t, const std::string &f, Callback cb) - : type(t), filename(f), callback(cb) + Operation(OperationType t, const std::string &f, Callback cb, void *cbUserData_) + : type(t), filename(f), callback(cb), cbUserData(cbUserData_) { } OperationType type; std::string filename; Callback callback; + void *cbUserData; }; static int timer; @@ -61,7 +63,6 @@ namespace SaveState void Process(u64 userdata, int cyclesLate); - // This is where the magic happens. void SaveStart::DoState(PointerWrap &p) { // Gotta do CoreTiming first since we'll restore into it. @@ -96,19 +97,94 @@ namespace SaveState CoreTiming::ScheduleEvent_Threadsafe(0, timer); } - void Load(const std::string &filename, Callback callback) + void Load(const std::string &filename, Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_LOAD, filename, callback)); + Enqueue(Operation(SAVESTATE_LOAD, filename, callback, cbUserData)); } - void Save(const std::string &filename, Callback callback) + void Save(const std::string &filename, Callback callback, void *cbUserData) { - Enqueue(Operation(SAVESTATE_SAVE, filename, callback)); + Enqueue(Operation(SAVESTATE_SAVE, filename, callback, cbUserData)); } - void Verify(Callback callback) + + // Slot utilities + + std::string GenerateSaveSlotFilename(int slot) { - Enqueue(Operation(SAVESTATE_VERIFY, std::string(""), callback)); + char discID[256]; + char temp[256]; + sprintf(discID, "%s_%s", + g_paramSFO.GetValueString("DISC_ID").c_str(), + g_paramSFO.GetValueString("DISC_VERSION").c_str()); + sprintf(temp, "ms0:/PSP/PPSSPP_STATE/%s_%i.ppst", discID, slot); + std::string hostPath; + if (pspFileSystem.GetHostPath(std::string(temp), hostPath)) { + return hostPath; + } else { + return ""; + } + } + + void LoadSlot(int slot, Callback callback, void *cbUserData) + { + std::string fn = GenerateSaveSlotFilename(slot); + if (!fn.empty()) + Load(fn, callback, cbUserData); + else + (*callback)(false, cbUserData); + } + + void SaveSlot(int slot, Callback callback, void *cbUserData) + { + std::string fn = GenerateSaveSlotFilename(slot); + if (!fn.empty()) + Save(fn, callback, cbUserData); + else + (*callback)(false, cbUserData); + } + + void HasSaveInSlot(int slot) + { + std::string fn = GenerateSaveSlotFilename(slot); + } + + bool operator < (const tm &t1, const tm &t2) { + if (t1.tm_year < t2.tm_year) return true; + if (t1.tm_year > t2.tm_year) return false; + if (t1.tm_mon < t2.tm_mon) return true; + if (t1.tm_mon > t2.tm_mon) return false; + if (t1.tm_mday < t2.tm_mday) return true; + if (t1.tm_mday > t2.tm_mday) return false; + if (t1.tm_hour < t2.tm_hour) return true; + if (t1.tm_hour > t2.tm_hour) return false; + if (t1.tm_min < t2.tm_min) return true; + if (t1.tm_min > t2.tm_min) return false; + if (t1.tm_sec < t2.tm_sec) return true; + if (t1.tm_sec > t2.tm_sec) return false; + return false; + } + + int GetMostRecentSaveSlot() { + int newestSlot = -1; + tm newestDate = {0}; + for (int i = 0; i < SAVESTATESLOTS; i++) { + std::string fn = GenerateSaveSlotFilename(i); + if (File::Exists(fn)) { + tm time = File::GetModifTime(fn); + if (newestDate < time) { + newestDate = time; + newestSlot = i; + } + } + } + return newestSlot; + } + + + void Verify(Callback callback, void *cbUserData) + { + Enqueue(Operation(SAVESTATE_VERIFY, std::string(""), callback, cbUserData)); } std::vector Flush() @@ -164,12 +240,14 @@ namespace SaveState } if (op.callback != NULL) - op.callback(result); + op.callback(result, op.cbUserData); } } void Init() { timer = CoreTiming::RegisterEvent("SaveState", Process); + // Make sure there's a directory for save slots + pspFileSystem.MkDir("ms0:/PSP/PPSSPP_STATE"); } } diff --git a/Core/SaveState.h b/Core/SaveState.h index 82a7021040..2edecfe930 100644 --- a/Core/SaveState.h +++ b/Core/SaveState.h @@ -20,20 +20,28 @@ namespace SaveState { - typedef void (*Callback)(bool status); + typedef void (*Callback)(bool status, void *cbUserData); // TODO: Better place for this? const int REVISION = 1; + const int SAVESTATESLOTS = 4; void Init(); + void SaveSlot(int slot, Callback callback, void *cbUserData = 0); + void LoadSlot(int slot, Callback callback, void *cbUserData = 0); + void HasSaveInSlot(int slot); + int GetNewestSlot(); + // Load the specified file into the current state (async.) // Warning: callback will be called on a different thread. - void Load(const std::string &filename, Callback callback = NULL); + void Load(const std::string &filename, Callback callback = 0, void *cbUserData = 0); + // Save the current state to the specified file (async.) // Warning: callback will be called on a different thread. - void Save(const std::string &filename, Callback callback = NULL); + void Save(const std::string &filename, Callback callback = 0, void *cbUserData = 0); + // For testing / automated tests. Runs a save state verification pass (async.) // Warning: callback will be called on a different thread. - void Verify(Callback callback = NULL); + void Verify(Callback callback = 0, void *cbUserData = 0); }; diff --git a/Core/System.cpp b/Core/System.cpp index 9110dcc245..67c3b74cd6 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -37,10 +37,10 @@ #include "CoreParameter.h" #include "FileSystems/MetaFileSystem.h" #include "Loaders.h" - +#include "ELF/ParamSFO.h" MetaFileSystem pspFileSystem; - +ParamSFOData g_paramSFO; static CoreParameter coreParameter; bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) diff --git a/Core/System.h b/Core/System.h index a3340b2a21..a47d1622fe 100644 --- a/Core/System.h +++ b/Core/System.h @@ -21,8 +21,10 @@ #include "MemMap.h" #include "FileSystems/MetaFileSystem.h" #include "CoreParameter.h" +#include "ELF/ParamSFO.h" extern MetaFileSystem pspFileSystem; +extern ParamSFOData g_paramSFO; bool PSP_Init(const CoreParameter &coreParam, std::string *error_string); bool PSP_IsInited(); diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index f6442f8315..beec2a68c8 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -312,7 +312,7 @@ namespace MainWindow UpdateMenus(); break; - case ID_FILE_LOADSTATE: + case ID_FILE_LOADSTATEFILE: if (g_State.bEmuThreadStarted) { nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; @@ -327,7 +327,7 @@ namespace MainWindow } break; - case ID_FILE_SAVESTATE: + case ID_FILE_SAVESTATEFILE: if (g_State.bEmuThreadStarted) { nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; @@ -342,6 +342,32 @@ namespace MainWindow } break; + // TODO: Add UI for multiple slots + + case ID_FILE_QUICKLOADSTATE: + if (g_State.bEmuThreadStarted) + { + nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; + for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); + } + SetCursor(LoadCursor(0,IDC_WAIT)); + SaveState::LoadSlot(0, SaveStateActionFinished); + break; + + case ID_FILE_QUICKSAVESTATE: + if (g_State.bEmuThreadStarted) + { + nextState = Core_IsStepping() ? CORE_STEPPING : CORE_RUNNING; + for (int i=0; iGetDlgHandle(), WM_COMMAND, IDC_STOP, 0); + } + SetCursor(LoadCursor(0,IDC_WAIT)); + SaveState::SaveSlot(0, SaveStateActionFinished); + break; + case ID_OPTIONS_SCREEN1X: SetZoom(1); UpdateMenus(); @@ -675,8 +701,10 @@ 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_FILE_SAVESTATEFILE,!enable); + EnableMenuItem(menu,ID_FILE_LOADSTATEFILE,!enable); + EnableMenuItem(menu,ID_FILE_QUICKSAVESTATE,!enable); + EnableMenuItem(menu,ID_FILE_QUICKLOADSTATE,!enable); EnableMenuItem(menu,ID_CPU_DYNAREC,enable); EnableMenuItem(menu,ID_CPU_INTERPRETER,enable); EnableMenuItem(menu,ID_CPU_FASTINTERPRETER,enable); @@ -788,7 +816,7 @@ namespace MainWindow } } - void SaveStateActionFinished(bool result) + void SaveStateActionFinished(bool result, void *userdata) { // TODO: Improve messaging? if (!result) diff --git a/Windows/WndMainWindow.h b/Windows/WndMainWindow.h index b58823ca25..11e7d4c2cb 100644 --- a/Windows/WndMainWindow.h +++ b/Windows/WndMainWindow.h @@ -17,7 +17,7 @@ namespace MainWindow void SetPlaying(const char*text); void BrowseAndBoot(); void SetNextState(CoreState state); - void SaveStateActionFinished(bool result); + void SaveStateActionFinished(bool result, void *userdata); void _ViewFullScreen(HWND hWnd); void _ViewNormal(HWND hWnd); } diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 0eafb5b937..11fcf4fd43 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -187,9 +187,11 @@ BEGIN MENUITEM "&Open...", ID_FILE_LOAD MENUITEM "&Close", ID_EMULATION_STOP MENUITEM SEPARATOR -MENUITEM "&Load State\tF4" ID_FILE_LOADSTATE -MENUITEM "&Save State\tF2" ID_FILE_SAVESTATE -MENUITEM SEPARATOR + MENUITEM "Quickload state\tF4" ID_FILE_QUICKLOADSTATE + MENUITEM "Quicksave state\tF2" ID_FILE_QUICKSAVESTATE + MENUITEM "&Load State File..." ID_FILE_LOADSTATEFILE + MENUITEM "&Save State File..." ID_FILE_SAVESTATEFILE + MENUITEM SEPARATOR MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Emulation" @@ -321,8 +323,8 @@ BEGIN "R", ID_DEBUG_REGISTERS, VIRTKEY, CONTROL, NOINVERT VK_F7, ID_EMULATION_RUN, VIRTKEY, NOINVERT VK_F8, ID_EMULATION_PAUSE, VIRTKEY, NOINVERT - VK_F2, ID_FILE_SAVESTATE, VIRTKEY, NOINVERT - VK_F4, ID_FILE_LOADSTATE, VIRTKEY, NOINVERT + VK_F2, ID_FILE_QUICKSAVESTATE, VIRTKEY, NOINVERT + VK_F4, ID_FILE_QUICKLOADSTATE, VIRTKEY, NOINVERT "1", ID_OPTIONS_SCREEN1X, VIRTKEY, CONTROL, NOINVERT "2", ID_OPTIONS_SCREEN2X, VIRTKEY, CONTROL, NOINVERT "3", ID_OPTIONS_SCREEN3X, VIRTKEY, CONTROL, NOINVERT diff --git a/Windows/resource.h b/Windows/resource.h index af56ef8446..79b18ad927 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -53,8 +53,8 @@ #define ID_DEBUG_BREAKPOINTS 122 #define ID_FILE_LOAD_BIN 123 #define ID_FILE_LOAD_ISO 125 -#define ID_FILE_LOADSTATE 126 -#define ID_FILE_SAVESTATE 127 +#define ID_FILE_LOADSTATEFILE 126 +#define ID_FILE_SAVESTATEFILE 127 #define ID_EMULATION_RESET 130 #define IDD_ABOUTBOX 133 #define ID_DEBUG_LOADMAPFILE 134 @@ -248,13 +248,15 @@ #define ID_OPTIONS_FASTMEMORY 40125 #define IDC_STEPHLE 40126 #define ID_OPTIONS_LINEARFILTERING 40127 +#define ID_FILE_QUICKSAVESTATE 40128 +#define ID_FILE_QUICKLOADSTATE 40129 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 232 -#define _APS_NEXT_COMMAND_VALUE 40127 +#define _APS_NEXT_COMMAND_VALUE 40130 #define _APS_NEXT_CONTROL_VALUE 1162 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/android/jni/MenuScreens.cpp b/android/jni/MenuScreens.cpp index b35ae132a7..1d22f0ac52 100644 --- a/android/jni/MenuScreens.cpp +++ b/android/jni/MenuScreens.cpp @@ -29,6 +29,7 @@ #include "ui/ui.h" #include "ui_atlas.h" #include "util/random/rng.h" +#include "util/text/utf8.h" #include "UIShader.h" #include "../../GPU/ge_constants.h" @@ -36,10 +37,16 @@ #include "../../GPU/GPUInterface.h" #include "../../Core/Config.h" #include "../../Core/CoreParameter.h" +#include "../../Core/SaveState.h" #include "MenuScreens.h" #include "EmuScreen.h" + +// Ugly communication with NativeApp +extern std::string game_title; + + static const int symbols[4] = { I_CROSS, I_CIRCLE, @@ -207,22 +214,39 @@ void InGameMenuScreen::render() { UIBegin(); DrawBackground(1.0f); - ui_draw2d.DrawText(UBUNTU48, "Emulation Paused", dp_xres / 2, 30, 0xFFFFFFFF, ALIGN_HCENTER); + const char *title; + if (UTF8StringHasNonASCII(game_title.c_str())) { + title = "(can't display japanese title)"; + } else { + title = game_title.c_str(); + } + + ui_draw2d.DrawText(UBUNTU48, title, dp_xres / 2, 30, 0xFFFFFFFF, ALIGN_HCENTER); int x = 30; int y = 50; - UICheckBox(GEN_ID, x, y += 50, "Show Debug Statistics (experimental)", ALIGN_TOPLEFT, &g_Config.bShowDebugStats); - UICheckBox(GEN_ID, x, y += 50, "Hardware Transform (experimental)", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); + UICheckBox(GEN_ID, x, y += 50, "Show Debug Statistics", ALIGN_TOPLEFT, &g_Config.bShowDebugStats); + UICheckBox(GEN_ID, x, y += 50, "Hardware Transform", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); + + // TODO: Add UI for more than one slot. + VLinear vlinear1(x, y + 80, 20); + UIText(UBUNTU24, vlinear1, "Save states are experimental (and large)", 0xFFFFFFFF); + if (UIButton(GEN_ID, vlinear1, LARGE_BUTTON_WIDTH, "Save State", ALIGN_LEFT)) { + SaveState::SaveSlot(0, 0, 0); + screenManager()->finishDialog(this, DR_CANCEL); + } + if (UIButton(GEN_ID, vlinear1, LARGE_BUTTON_WIDTH, "Load State", ALIGN_LEFT)) { + SaveState::LoadSlot(0, 0, 0); + screenManager()->finishDialog(this, DR_CANCEL); + } VLinear vlinear(dp_xres - 10, 160, 20); if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH, "Continue", ALIGN_RIGHT)) { screenManager()->finishDialog(this, DR_CANCEL); } - if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH, "Return to Menu", ALIGN_RIGHT)) { screenManager()->finishDialog(this, DR_OK); } - if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH, "Dump Next Frame", ALIGN_RIGHT)) { gpu->DumpNextFrame(); } @@ -253,7 +277,7 @@ void SettingsScreen::render() { int y = 50; UICheckBox(GEN_ID, x, y += 50, "Sound Emulation", ALIGN_TOPLEFT, &g_Config.bEnableSound); UICheckBox(GEN_ID, x, y += 50, "Buffered Rendering (may fix flicker)", ALIGN_TOPLEFT, &g_Config.bBufferedRendering); - UICheckBox(GEN_ID, x, y += 50, "Hardware Transform (experimental)", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); + UICheckBox(GEN_ID, x, y += 50, "Hardware Transform", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); bool useFastInt = g_Config.iCpuCore == CPU_FASTINTERPRETER; UICheckBox(GEN_ID, x, y += 50, "Slightly faster interpreter (may crash)", ALIGN_TOPLEFT, &useFastInt); diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index cbec8230ac..9faf23f009 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -53,6 +53,7 @@ Texture *uiTexture; ScreenManager *screenManager; std::string config_filename; +std::string game_title; class AndroidLogger : public LogListener { @@ -112,6 +113,9 @@ public: virtual bool AttemptLoadSymbolMap() {return false;} virtual void ResetSymbolMap() {} virtual void AddSymbol(std::string name, u32 addr, u32 size, int type=0) {} + virtual void SetWindowTitle(const char *message) { + game_title = message; + } }; // globals