diff --git a/Core/Dialog/PSPGamedataInstallDialog.cpp b/Core/Dialog/PSPGamedataInstallDialog.cpp index ba3d60d258..00d5741257 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.cpp +++ b/Core/Dialog/PSPGamedataInstallDialog.cpp @@ -328,6 +328,11 @@ void PSPGamedataInstallDialog::DoState(PointerWrap &p) { Do(p, allFilesSize); Do(p, allReadSize); Do(p, progressValue); + if (p.mode == p.MODE_READ && (numFiles < 0 || numFiles > (int)inFileNames.size() || readFiles < 0 || readFiles > numFiles)) { + ERROR_LOG(Log::SaveState, "Gamedata install: bad file counts in savestate (%d of %d, %d names)", readFiles, numFiles, (int)inFileNames.size()); + p.SetError(p.ERROR_FAILURE); + return; + } } else { param.ptr = 0; } diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index ed2474d0a7..ce34a68bb3 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -1087,6 +1087,19 @@ void PSPOskDialog::DoState(PointerWrap &p) Do(p, currentKeyboardLanguage); Do(p, i_level); DoArray(p, i_value, ARRAY_SIZE(i_value)); + if (p.mode == p.MODE_READ) { + // All index tables. A broken state gets the default keyboard and no composition. + if (currentKeyboard < 0 || currentKeyboard >= OSK_KEYBOARD_COUNT || currentKeyboardLanguage < 0 || currentKeyboardLanguage >= OSK_LANGUAGE_COUNT) { + currentKeyboardLanguage = OSK_LANGUAGE_ENGLISH; + currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE; + } + selectedChar = std::clamp(selectedChar, 0, numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard] - 1); + // Consonants, vowels and final consonants, -1 for none. + if (i_level < 0 || i_level > 3 || i_value[0] < -1 || i_value[0] >= 19 || i_value[1] < -1 || i_value[1] >= 21 || i_value[2] < -1 || i_value[2] >= 27) { + i_level = 0; + i_value[0] = i_value[1] = i_value[2] = 0; + } + } } else if (p.mode == p.MODE_READ) { // Pick a keyboard the field allows, as Init does, and keep the selection on it. currentKeyboardLanguage = OSK_LANGUAGE_ENGLISH; diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index dbed39974d..9f9a4b39ad 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -166,7 +166,7 @@ namespace void SaveFileInfo::DoState(PointerWrap &p) { - auto s = p.Section("SaveFileInfo", 1, 2); + auto s = p.Section("SaveFileInfo", 1, 3); if (!s) return; @@ -179,6 +179,11 @@ void SaveFileInfo::DoState(PointerWrap &p) DoCString(p, saveDetail); Do(p, modif_time); + if (s >= 3) { + // The delete dialogs delete by it, and for LISTALLDELETE it isn't the game name and save name. + Do(p, saveDir); + Do(p, broken); + } if (s <= 1) { u32 textureData; @@ -1953,6 +1958,30 @@ bool SavedataParam::WouldHaveMultiSaveName(const SceUtilitySavedataParam *param) } } +// Slots without an icon of their own shared noSaveIcon's image before the save, but come back as +// separate images of the same texture. Share it again, or Clear() frees it once per slot. +void SavedataParam::ShareLoadedIcons() { + for (int i = 0; i < saveDataListCount; i++) { + PPGeImage *texture = saveDataList[i].texture; + if (!texture || texture->TextureAddress() == 0) { + continue; + } + for (int j = 0; j < i; j++) { + PPGeImage *other = saveDataList[j].texture; + if (other && other->TextureAddress() == texture->TextureAddress()) { + if (!noSaveIcon) { + noSaveIcon = new SaveFileInfo(); + noSaveIcon->texture = other; + } + texture->Forget(); + delete texture; + saveDataList[i].texture = other; + break; + } + } + } +} + void SavedataParam::DoState(PointerWrap &p) { auto s = p.Section("SavedataParam", 1, 2); if (!s) @@ -2002,6 +2031,7 @@ void SavedataParam::DoState(PointerWrap &p) { if (saveDataListCount != 0) { saveDataList = new SaveFileInfo[saveDataListCount]; DoArray(p, saveDataList, saveDataListCount); + ShareLoadedIcons(); } else { saveDataList = nullptr; } diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index 5689b51954..138d9d360b 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -363,6 +363,7 @@ public: private: void Clear(); + void ShareLoadedIcons(); void SetFileInfo(int idx, PSPFileInfo &info, const std::string &saveName, const std::string &saveDir = ""); void SetFileInfo(SaveFileInfo &saveInfo, PSPFileInfo &info, const std::string &saveName, const std::string &saveDir = ""); void ClearFileInfo(SaveFileInfo &saveInfo, const std::string &saveName); diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 0ec3e80cde..638d77080e 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -1136,6 +1136,9 @@ void DirectoryFileSystem::DoState(PointerWrap &p) { } if (s >= 2) { Do(p, entry.hFile.needsTrunc_); + } else { + // Open() set it up for a file opened with TRUNCATE, which was truncated long before the save. + entry.hFile.needsTrunc_ = -1; } // Let's hope that things don't go that badly with the file mysteriously auto-closed. // Better than not loading the save state at all, hopefully. diff --git a/Core/HLE/sceFont.cpp b/Core/HLE/sceFont.cpp index 0f3b0354e3..f8c171ac98 100644 --- a/Core/HLE/sceFont.cpp +++ b/Core/HLE/sceFont.cpp @@ -359,6 +359,12 @@ public: } if (s >= 3) { Do(p, mode_); + // Only the destructor looks at this: it deletes the font in the user modes, so an internal + // font mustn't be in one, nor a font loaded above out of them. + const bool userMode = mode_ == FONT_OPEN_USERBUFFER || mode_ == FONT_OPEN_USERFILE_FULL || mode_ == FONT_OPEN_USERFILE_HANDLERS; + if (p.mode == p.MODE_READ && userMode != (internalFont == -1)) { + mode_ = internalFont == -1 ? FONT_OPEN_USERBUFFER : FONT_OPEN_INTERNAL_FULL; + } } else { // Only the destructor looks at this: a font loaded above is ours to delete. mode_ = internalFont == -1 ? FONT_OPEN_USERBUFFER : FONT_OPEN_INTERNAL_FULL; @@ -733,6 +739,12 @@ public: openAllocatedAddresses_.resize(params_.numFonts); charInfoBitmapAddress_ = 0; } + // They're all indexed by font number. + const size_t numFonts = (size_t)params_.numFonts; + if (p.mode == p.MODE_READ && (fonts_.size() != numFonts || fontRefCount_.size() != numFonts || openAllocatedAddresses_.size() != numFonts)) { + ERROR_LOG(Log::SaveState, "FontLib: font counts in savestate don't match (%d)", (int)params_.numFonts); + p.SetError(p.ERROR_FAILURE); + } } void SetFileFontHandle(u32 handle) { diff --git a/Core/HLE/sceReg.cpp b/Core/HLE/sceReg.cpp index 3c08c48cc5..a8156576dd 100644 --- a/Core/HLE/sceReg.cpp +++ b/Core/HLE/sceReg.cpp @@ -70,6 +70,16 @@ struct OverlayValue { std::string str; // STR/BIN payload int intValue = 0; bool isDir = false; + + void DoState(PointerWrap &p) { + auto s = p.Section("RegOverlayValue", 1); + if (!s) + return; + Do(p, type); + Do(p, str); + Do(p, intValue); + Do(p, isDir); + } }; static std::map> g_overlay; @@ -1118,10 +1128,13 @@ static const KeyValue *LookupCategory(std::string_view path, int *count) { } void __RegDoState(PointerWrap &p) { - auto s = p.Section("sceReg", 0, 3); + auto s = p.Section("sceReg", 0, 4); if (!s) { if (p.mode == PointerWrap::MODE_READ) { + // Like below, older states keep the values written this session. + std::map> overlay = std::move(g_overlay); __RegInit(); + g_overlay = std::move(overlay); } return; } @@ -1142,6 +1155,14 @@ void __RegDoState(PointerWrap &p) { g_handleGen = std::max(g_handleGen, g_openCategories.rbegin()->first + 1); } } + // Values the game (or a plugin, like CLANNAD's chfont.prx) wrote. Older states keep the current + // session's, most likely written by the same plugin at boot. + if (s >= 4) { + Do(p, g_overlay); + } + if (p.mode == PointerWrap::MODE_READ) { + InvalidateMergedCache(); + } } // Registry level (it seems only /system can exist, so kinda pointless) diff --git a/Core/HW/AsyncIOManager.cpp b/Core/HW/AsyncIOManager.cpp index 75de06ebbf..05dac3245a 100644 --- a/Core/HW/AsyncIOManager.cpp +++ b/Core/HW/AsyncIOManager.cpp @@ -164,6 +164,7 @@ void AsyncIOManager::DoState(PointerWrap &p) { } else { std::map oldResults; Do(p, oldResults); + results_.clear(); for (auto it = oldResults.begin(), end = oldResults.end(); it != end; ++it) { results_[it->first] = AsyncIOResult(it->second); } diff --git a/Core/Util/PPGeDraw.h b/Core/Util/PPGeDraw.h index 7b1297d5e0..ddae3abf90 100644 --- a/Core/Util/PPGeDraw.h +++ b/Core/Util/PPGeDraw.h @@ -134,6 +134,11 @@ public: return width_; } + // Where the loaded texture is in kernel memory, or 0. + u32 TextureAddress() const { + return texture_; + } + int Height() const { return height_; }