1 // Copyright (c) 2012- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #include <algorithm>
19 #include <vector>
20 #include <thread>
21 #include <mutex>
22 
23 #include "Common/Data/Text/I18n.h"
24 #include "Common/Thread/ThreadUtil.h"
25 #include "Common/Data/Text/Parsers.h"
26 
27 #include "Common/File/FileUtil.h"
28 #include "Common/Serialize/Serializer.h"
29 #include "Common/Serialize/SerializeFuncs.h"
30 #include "Common/StringUtils.h"
31 #include "Common/TimeUtil.h"
32 
33 #include "Core/SaveState.h"
34 #include "Core/Config.h"
35 #include "Core/Core.h"
36 #include "Core/CoreTiming.h"
37 #include "Core/Host.h"
38 #include "Core/Screenshot.h"
39 #include "Core/System.h"
40 #include "Core/FileSystems/MetaFileSystem.h"
41 #include "Core/ELF/ParamSFO.h"
42 #include "Core/HLE/HLE.h"
43 #include "Core/HLE/sceDisplay.h"
44 #include "Core/HLE/ReplaceTables.h"
45 #include "Core/HLE/sceKernel.h"
46 #include "Core/HLE/sceUtility.h"
47 #include "Core/MemMap.h"
48 #include "Core/MIPS/MIPS.h"
49 #include "Core/MIPS/JitCommon/JitBlockCache.h"
50 #include "HW/MemoryStick.h"
51 #include "GPU/GPUState.h"
52 
53 #ifndef MOBILE_DEVICE
54 #include "Core/AVIDump.h"
55 #include "Core/HLE/__sceAudio.h"
56 #endif
57 
58 // Slot number is visual only, -2 will display special message
59 constexpr int LOAD_UNDO_SLOT = -2;
60 
61 namespace SaveState
62 {
63 	struct SaveStart
64 	{
65 		void DoState(PointerWrap &p);
66 	};
67 
68 	enum OperationType
69 	{
70 		SAVESTATE_SAVE,
71 		SAVESTATE_LOAD,
72 		SAVESTATE_VERIFY,
73 		SAVESTATE_REWIND,
74 		SAVESTATE_SAVE_SCREENSHOT,
75 	};
76 
77 	struct Operation
78 	{
79 		// The slot number is for visual purposes only. Set to -1 for operations where we don't display a message for example.
OperationSaveState::Operation80 		Operation(OperationType t, const Path &f, int slot_, Callback cb, void *cbUserData_)
81 			: type(t), filename(f), callback(cb), slot(slot_), cbUserData(cbUserData_)
82 		{
83 		}
84 
85 		OperationType type;
86 		Path filename;
87 		Callback callback;
88 		int slot;
89 		void *cbUserData;
90 	};
91 
SaveToRam(std::vector<u8> & data)92 	CChunkFileReader::Error SaveToRam(std::vector<u8> &data) {
93 		SaveStart state;
94 		size_t sz = CChunkFileReader::MeasurePtr(state);
95 		if (data.size() < sz)
96 			data.resize(sz);
97 		return CChunkFileReader::SavePtr(&data[0], state, sz);
98 	}
99 
LoadFromRam(std::vector<u8> & data,std::string * errorString)100 	CChunkFileReader::Error LoadFromRam(std::vector<u8> &data, std::string *errorString) {
101 		SaveStart state;
102 		return CChunkFileReader::LoadPtr(&data[0], state, errorString);
103 	}
104 
105 	struct StateRingbuffer
106 	{
StateRingbufferSaveState::StateRingbuffer107 		StateRingbuffer(int size) : first_(0), next_(0), size_(size), base_(-1)
108 		{
109 			states_.resize(size);
110 			baseMapping_.resize(size);
111 		}
112 
SaveSaveState::StateRingbuffer113 		CChunkFileReader::Error Save()
114 		{
115 			std::lock_guard<std::mutex> guard(lock_);
116 
117 			int n = next_++ % size_;
118 			if ((next_ % size_) == first_)
119 				++first_;
120 
121 			static std::vector<u8> buffer;
122 			std::vector<u8> *compressBuffer = &buffer;
123 			CChunkFileReader::Error err;
124 
125 			if (base_ == -1 || ++baseUsage_ > BASE_USAGE_INTERVAL)
126 			{
127 				base_ = (base_ + 1) % ARRAY_SIZE(bases_);
128 				baseUsage_ = 0;
129 				err = SaveToRam(bases_[base_]);
130 				// Let's not bother savestating twice.
131 				compressBuffer = &bases_[base_];
132 			}
133 			else
134 				err = SaveToRam(buffer);
135 
136 			if (err == CChunkFileReader::ERROR_NONE)
137 				ScheduleCompress(&states_[n], compressBuffer, &bases_[base_]);
138 			else
139 				states_[n].clear();
140 			baseMapping_[n] = base_;
141 			return err;
142 		}
143 
RestoreSaveState::StateRingbuffer144 		CChunkFileReader::Error Restore(std::string *errorString)
145 		{
146 			std::lock_guard<std::mutex> guard(lock_);
147 
148 			// No valid states left.
149 			if (Empty())
150 				return CChunkFileReader::ERROR_BAD_FILE;
151 
152 			int n = (--next_ + size_) % size_;
153 			if (states_[n].empty())
154 				return CChunkFileReader::ERROR_BAD_FILE;
155 
156 			static std::vector<u8> buffer;
157 			LockedDecompress(buffer, states_[n], bases_[baseMapping_[n]]);
158 			return LoadFromRam(buffer, errorString);
159 		}
160 
ScheduleCompressSaveState::StateRingbuffer161 		void ScheduleCompress(std::vector<u8> *result, const std::vector<u8> *state, const std::vector<u8> *base)
162 		{
163 			if (compressThread_.joinable())
164 				compressThread_.join();
165 			compressThread_ = std::thread([=]{
166 				SetCurrentThreadName("SaveStateCompress");
167 				Compress(*result, *state, *base);
168 			});
169 		}
170 
CompressSaveState::StateRingbuffer171 		void Compress(std::vector<u8> &result, const std::vector<u8> &state, const std::vector<u8> &base)
172 		{
173 			std::lock_guard<std::mutex> guard(lock_);
174 			// Bail if we were cleared before locking.
175 			if (first_ == 0 && next_ == 0)
176 				return;
177 
178 			result.clear();
179 			for (size_t i = 0; i < state.size(); i += BLOCK_SIZE)
180 			{
181 				int blockSize = std::min(BLOCK_SIZE, (int)(state.size() - i));
182 				if (i + blockSize > base.size() || memcmp(&state[i], &base[i], blockSize) != 0)
183 				{
184 					result.push_back(1);
185 					result.insert(result.end(), state.begin() + i, state.begin() +i + blockSize);
186 				}
187 				else
188 					result.push_back(0);
189 			}
190 		}
191 
LockedDecompressSaveState::StateRingbuffer192 		void LockedDecompress(std::vector<u8> &result, const std::vector<u8> &compressed, const std::vector<u8> &base)
193 		{
194 			result.clear();
195 			result.reserve(base.size());
196 			auto basePos = base.begin();
197 			for (size_t i = 0; i < compressed.size(); )
198 			{
199 				if (compressed[i] == 0)
200 				{
201 					++i;
202 					int blockSize = std::min(BLOCK_SIZE, (int)(base.size() - result.size()));
203 					result.insert(result.end(), basePos, basePos + blockSize);
204 					basePos += blockSize;
205 				}
206 				else
207 				{
208 					++i;
209 					int blockSize = std::min(BLOCK_SIZE, (int)(compressed.size() - i));
210 					result.insert(result.end(), compressed.begin() + i, compressed.begin() + i + blockSize);
211 					i += blockSize;
212 					basePos += blockSize;
213 				}
214 			}
215 		}
216 
ClearSaveState::StateRingbuffer217 		void Clear()
218 		{
219 			if (compressThread_.joinable())
220 				compressThread_.join();
221 
222 			// This lock is mainly for shutdown.
223 			std::lock_guard<std::mutex> guard(lock_);
224 			first_ = 0;
225 			next_ = 0;
226 		}
227 
EmptySaveState::StateRingbuffer228 		bool Empty() const
229 		{
230 			return next_ == first_;
231 		}
232 
233 		static const int BLOCK_SIZE;
234 		// TODO: Instead, based on size of compressed state?
235 		static const int BASE_USAGE_INTERVAL;
236 
237 		typedef std::vector<u8> StateBuffer;
238 
239 		int first_;
240 		int next_;
241 		int size_;
242 
243 		std::vector<StateBuffer> states_;
244 		StateBuffer bases_[2];
245 		std::vector<int> baseMapping_;
246 		std::mutex lock_;
247 		std::thread compressThread_;
248 
249 		int base_;
250 		int baseUsage_;
251 	};
252 
253 	static bool needsProcess = false;
254 	static bool needsRestart = false;
255 	static std::vector<Operation> pending;
256 	static std::mutex mutex;
257 	static int screenshotFailures = 0;
258 	static bool hasLoadedState = false;
259 	static const int STALE_STATE_USES = 2;
260 	// 4 hours of total gameplay since the virtual PSP started the game.
261 	static const u64 STALE_STATE_TIME = 4 * 3600 * 1000000ULL;
262 	static int saveStateGeneration = 0;
263 	static int saveDataGeneration = 0;
264 	static int lastSaveDataGeneration = 0;
265 	static std::string saveStateInitialGitVersion = "";
266 
267 	// TODO: Should this be configurable?
268 	static const int REWIND_NUM_STATES = 20;
269 	static const int SCREENSHOT_FAILURE_RETRIES = 15;
270 	static StateRingbuffer rewindStates(REWIND_NUM_STATES);
271 	// TODO: Any reason for this to be configurable?
272 	const static float rewindMaxWallFrequency = 1.0f;
273 	static double rewindLastTime = 0.0f;
274 	const int StateRingbuffer::BLOCK_SIZE = 8192;
275 	const int StateRingbuffer::BASE_USAGE_INTERVAL = 15;
276 
DoState(PointerWrap & p)277 	void SaveStart::DoState(PointerWrap &p)
278 	{
279 		auto s = p.Section("SaveStart", 1, 2);
280 		if (!s)
281 			return;
282 
283 		if (s >= 2) {
284 			// This only increments on save, of course.
285 			++saveStateGeneration;
286 			Do(p, saveStateGeneration);
287 			// This saves the first git version to create this save state (or generation of save states.)
288 			if (saveStateInitialGitVersion.empty())
289 				saveStateInitialGitVersion = PPSSPP_GIT_VERSION;
290 			Do(p, saveStateInitialGitVersion);
291 		} else {
292 			saveStateGeneration = 1;
293 		}
294 		if (s >= 3) {
295 			// Keep track of savedata (not save states) too.
296 			Do(p, saveDataGeneration);
297 		} else {
298 			saveDataGeneration = 0;
299 		}
300 
301 		// Gotta do CoreTiming first since we'll restore into it.
302 		CoreTiming::DoState(p);
303 
304 		// Memory is a bit tricky when jit is enabled, since there's emuhacks in it.
305 		auto savedReplacements = SaveAndClearReplacements();
306 		if (MIPSComp::jit && p.mode == p.MODE_WRITE)
307 		{
308 			std::vector<u32> savedBlocks;
309 			savedBlocks = MIPSComp::jit->SaveAndClearEmuHackOps();
310 			Memory::DoState(p);
311 			MIPSComp::jit->RestoreSavedEmuHackOps(savedBlocks);
312 		}
313 		else
314 			Memory::DoState(p);
315 		RestoreSavedReplacements(savedReplacements);
316 
317 		MemoryStick_DoState(p);
318 		currentMIPS->DoState(p);
319 		HLEDoState(p);
320 		__KernelDoState(p);
321 		// Kernel object destructors might close open files, so do the filesystem last.
322 		pspFileSystem.DoState(p);
323 	}
324 
Enqueue(SaveState::Operation op)325 	void Enqueue(SaveState::Operation op)
326 	{
327 		std::lock_guard<std::mutex> guard(mutex);
328 		pending.push_back(op);
329 
330 		// Don't actually run it until next frame.
331 		// It's possible there might be a duplicate but it won't hurt us.
332 		needsProcess = true;
333 		Core_UpdateSingleStep();
334 	}
335 
Load(const Path & filename,int slot,Callback callback,void * cbUserData)336 	void Load(const Path &filename, int slot, Callback callback, void *cbUserData)
337 	{
338 		if (coreState == CoreState::CORE_RUNTIME_ERROR)
339 			Core_EnableStepping(true);
340 		Enqueue(Operation(SAVESTATE_LOAD, filename, slot, callback, cbUserData));
341 	}
342 
Save(const Path & filename,int slot,Callback callback,void * cbUserData)343 	void Save(const Path &filename, int slot, Callback callback, void *cbUserData)
344 	{
345 		if (coreState == CoreState::CORE_RUNTIME_ERROR)
346 			Core_EnableStepping(true);
347 		Enqueue(Operation(SAVESTATE_SAVE, filename, slot, callback, cbUserData));
348 	}
349 
Verify(Callback callback,void * cbUserData)350 	void Verify(Callback callback, void *cbUserData)
351 	{
352 		Enqueue(Operation(SAVESTATE_VERIFY, Path(), -1, callback, cbUserData));
353 	}
354 
Rewind(Callback callback,void * cbUserData)355 	void Rewind(Callback callback, void *cbUserData)
356 	{
357 		if (coreState == CoreState::CORE_RUNTIME_ERROR)
358 			Core_EnableStepping(true);
359 		Enqueue(Operation(SAVESTATE_REWIND, Path(), -1, callback, cbUserData));
360 	}
361 
SaveScreenshot(const Path & filename,Callback callback,void * cbUserData)362 	void SaveScreenshot(const Path &filename, Callback callback, void *cbUserData)
363 	{
364 		Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, -1, callback, cbUserData));
365 	}
366 
CanRewind()367 	bool CanRewind()
368 	{
369 		return !rewindStates.Empty();
370 	}
371 
372 	// Slot utilities
373 
AppendSlotTitle(const std::string & filename,const std::string & title)374 	std::string AppendSlotTitle(const std::string &filename, const std::string &title) {
375 		char slotChar = 0;
376 		auto detectSlot = [&](const std::string &ext) {
377 			if (!endsWith(filename, std::string(".") + ext)) {
378 				return false;
379 			}
380 
381 			// Usually these are slots, let's check the slot # after the last '_'.
382 			size_t slotNumPos = filename.find_last_of('_');
383 			if (slotNumPos == filename.npos) {
384 				return false;
385 			}
386 
387 			const size_t extLength = ext.length() + 1;
388 			// If we take out the extension, '_', etc. we should be left with only a single digit.
389 			if (slotNumPos + 1 + extLength != filename.length() - 1) {
390 				return false;
391 			}
392 
393 			slotChar = filename[slotNumPos + 1];
394 			if (slotChar < '0' || slotChar > '8') {
395 				return false;
396 			}
397 
398 			// Change from zero indexed to human friendly.
399 			slotChar++;
400 			return true;
401 		};
402 
403 		if (detectSlot(STATE_EXTENSION)) {
404 			return StringFromFormat("%s (%c)", title.c_str(), slotChar);
405 		}
406 		if (detectSlot(UNDO_STATE_EXTENSION)) {
407 			auto sy = GetI18NCategory("System");
408 			// Allow the number to be positioned where it makes sense.
409 			std::string undo = sy->T("undo %c");
410 			return title + " (" + StringFromFormat(undo.c_str(), slotChar) + ")";
411 		}
412 
413 		// Couldn't detect, use the filename.
414 		return title + " (" + filename + ")";
415 	}
416 
GetTitle(const Path & filename)417 	std::string GetTitle(const Path &filename) {
418 		std::string title;
419 		if (CChunkFileReader::GetFileTitle(filename, &title) == CChunkFileReader::ERROR_NONE) {
420 			if (title.empty()) {
421 				return filename.GetFilename();
422 			}
423 
424 			return AppendSlotTitle(filename.GetFilename(), title);
425 		}
426 
427 		// The file can't be loaded - let's note that.
428 		auto sy = GetI18NCategory("System");
429 		return filename.GetFilename() + " " + sy->T("(broken)");
430 	}
431 
GenerateFullDiscId(const Path & gameFilename)432 	std::string GenerateFullDiscId(const Path &gameFilename) {
433 		std::string discId = g_paramSFO.GetValueString("DISC_ID");
434 		std::string discVer = g_paramSFO.GetValueString("DISC_VERSION");
435 		if (discId.empty()) {
436 			discId = g_paramSFO.GenerateFakeID();
437 			discVer = "1.00";
438 		}
439 		return StringFromFormat("%s_%s", discId.c_str(), discVer.c_str());
440 	}
441 
GenerateSaveSlotFilename(const Path & gameFilename,int slot,const char * extension)442 	Path GenerateSaveSlotFilename(const Path &gameFilename, int slot, const char *extension)
443 	{
444 		std::string filename = StringFromFormat("%s_%d.%s", GenerateFullDiscId(gameFilename).c_str(), slot, extension);
445 		return GetSysDirectory(DIRECTORY_SAVESTATE) / filename;
446 	}
447 
GetCurrentSlot()448 	int GetCurrentSlot()
449 	{
450 		return g_Config.iCurrentStateSlot;
451 	}
452 
NextSlot()453 	void NextSlot()
454 	{
455 		g_Config.iCurrentStateSlot = (g_Config.iCurrentStateSlot + 1) % NUM_SLOTS;
456 	}
457 
DeleteIfExists(const Path & fn)458 	static void DeleteIfExists(const Path &fn) {
459 		// Just avoiding error messages.
460 		if (File::Exists(fn)) {
461 			File::Delete(fn);
462 		}
463 	}
464 
RenameIfExists(const Path & from,const Path & to)465 	static void RenameIfExists(const Path &from, const Path &to) {
466 		if (File::Exists(from)) {
467 			File::Rename(from, to);
468 		}
469 	}
470 
SwapIfExists(const Path & from,const Path & to)471 	static void SwapIfExists(const Path &from, const Path &to) {
472 		Path temp = from.WithExtraExtension(".tmp");
473 		if (File::Exists(from)) {
474 			File::Rename(from, temp);
475 			File::Rename(to, from);
476 			File::Rename(temp, to);
477 		}
478 	}
479 
LoadSlot(const Path & gameFilename,int slot,Callback callback,void * cbUserData)480 	void LoadSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData)
481 	{
482 		Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION);
483 		if (!fn.empty()) {
484 			// This add only 1 extra state, should we just always enable it?
485 			if (g_Config.bEnableStateUndo) {
486 				Path backup = GetSysDirectory(DIRECTORY_SAVESTATE) / LOAD_UNDO_NAME;
487 
488 				auto saveCallback = [=](Status status, const std::string &message, void *data) {
489 					if (status != Status::FAILURE) {
490 						DeleteIfExists(backup);
491 						File::Rename(backup.WithExtraExtension(".tmp"), backup);
492 						g_Config.sStateLoadUndoGame = GenerateFullDiscId(gameFilename);
493 						g_Config.Save("Saving config for savestate last load undo");
494 					} else {
495 						ERROR_LOG(SAVESTATE, "Saving load undo state failed: %s", message.c_str());
496 					}
497 					Load(fn, slot, callback, cbUserData);
498 				};
499 
500 				if (!backup.empty()) {
501 					Save(backup.WithExtraExtension(".tmp"), LOAD_UNDO_SLOT, saveCallback, cbUserData);
502 				} else {
503 					ERROR_LOG(SAVESTATE, "Saving load undo state failed. Error in the file system.");
504 					Load(fn, slot, callback, cbUserData);
505 				}
506 			} else {
507 				Load(fn, slot, callback, cbUserData);
508 			}
509 		} else {
510 			auto sy = GetI18NCategory("System");
511 			if (callback)
512 				callback(Status::FAILURE, sy->T("Failed to load state. Error in the file system."), cbUserData);
513 		}
514 	}
515 
UndoLoad(const Path & gameFilename,Callback callback,void * cbUserData)516 	bool UndoLoad(const Path &gameFilename, Callback callback, void *cbUserData)
517 	{
518 		if (g_Config.sStateLoadUndoGame != GenerateFullDiscId(gameFilename)) {
519 			auto sy = GetI18NCategory("System");
520 			if (callback)
521 				callback(Status::FAILURE, sy->T("Error: load undo state is from a different game"), cbUserData);
522 			return false;
523 		}
524 
525 		Path fn = GetSysDirectory(DIRECTORY_SAVESTATE) / LOAD_UNDO_NAME;
526 		if (!fn.empty()) {
527 			Load(fn, LOAD_UNDO_SLOT, callback, cbUserData);
528 			return true;
529 		} else {
530 			auto sy = GetI18NCategory("System");
531 			if (callback)
532 				callback(Status::FAILURE, sy->T("Failed to load state for load undo. Error in the file system."), cbUserData);
533 			return false;
534 		}
535 	}
536 
SaveSlot(const Path & gameFilename,int slot,Callback callback,void * cbUserData)537 	void SaveSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData)
538 	{
539 		Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION);
540 		Path shot = GenerateSaveSlotFilename(gameFilename, slot, SCREENSHOT_EXTENSION);
541 		Path fnUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_STATE_EXTENSION);
542 		Path shotUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_SCREENSHOT_EXTENSION);
543 		if (!fn.empty()) {
544 			auto renameCallback = [=](Status status, const std::string &message, void *data) {
545 				if (status != Status::FAILURE) {
546 					if (g_Config.bEnableStateUndo) {
547 						DeleteIfExists(fnUndo);
548 						RenameIfExists(fn, fnUndo);
549 						g_Config.sStateUndoLastSaveGame = GenerateFullDiscId(gameFilename);
550 						g_Config.iStateUndoLastSaveSlot = slot;
551 						g_Config.Save("Saving config for savestate last save undo");
552 					} else {
553 						DeleteIfExists(fn);
554 					}
555 					File::Rename(fn.WithExtraExtension(".tmp"), fn);
556 				}
557 				if (callback) {
558 					callback(status, message, data);
559 				}
560 			};
561 			// Let's also create a screenshot.
562 			if (g_Config.bEnableStateUndo) {
563 				DeleteIfExists(shotUndo);
564 				RenameIfExists(shot, shotUndo);
565 			}
566 			SaveScreenshot(shot, Callback(), 0);
567 			Save(fn.WithExtraExtension(".tmp"), slot, renameCallback, cbUserData);
568 		} else {
569 			auto sy = GetI18NCategory("System");
570 			if (callback)
571 				callback(Status::FAILURE, sy->T("Failed to save state. Error in the file system."), cbUserData);
572 		}
573 	}
574 
UndoSaveSlot(const Path & gameFilename,int slot)575 	bool UndoSaveSlot(const Path &gameFilename, int slot) {
576 		Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION);
577 		Path shot = GenerateSaveSlotFilename(gameFilename, slot, SCREENSHOT_EXTENSION);
578 		Path fnUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_STATE_EXTENSION);
579 		Path shotUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_SCREENSHOT_EXTENSION);
580 
581 		// Do nothing if there's no undo.
582 		if (File::Exists(fnUndo)) {
583 			// Swap them so they can undo again to redo.  Mistakes happen.
584 			SwapIfExists(shotUndo, shot);
585 			SwapIfExists(fnUndo, fn);
586 			return true;
587 		}
588 
589 		return false;
590 	}
591 
592 
UndoLastSave(const Path & gameFilename)593 	bool UndoLastSave(const Path &gameFilename) {
594 		if (g_Config.sStateUndoLastSaveGame != GenerateFullDiscId(gameFilename))
595 			return false;
596 
597 		return UndoSaveSlot(gameFilename, g_Config.iStateUndoLastSaveSlot);
598 	}
599 
HasSaveInSlot(const Path & gameFilename,int slot)600 	bool HasSaveInSlot(const Path &gameFilename, int slot)
601 	{
602 		Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION);
603 		return File::Exists(fn);
604 	}
605 
HasUndoSaveInSlot(const Path & gameFilename,int slot)606 	bool HasUndoSaveInSlot(const Path &gameFilename, int slot)
607 	{
608 		Path fn = GenerateSaveSlotFilename(gameFilename, slot, UNDO_STATE_EXTENSION);
609 		return File::Exists(fn);
610 	}
611 
HasUndoLastSave(const Path & gameFilename)612 	bool HasUndoLastSave(const Path &gameFilename)
613 	{
614 		if (g_Config.sStateUndoLastSaveGame != GenerateFullDiscId(gameFilename))
615 			return false;
616 
617 		return HasUndoSaveInSlot(gameFilename, g_Config.iStateUndoLastSaveSlot);
618 	}
619 
HasScreenshotInSlot(const Path & gameFilename,int slot)620 	bool HasScreenshotInSlot(const Path &gameFilename, int slot)
621 	{
622 		Path fn = GenerateSaveSlotFilename(gameFilename, slot, SCREENSHOT_EXTENSION);
623 		return File::Exists(fn);
624 	}
625 
HasUndoLoad(const Path & gameFilename)626 	bool HasUndoLoad(const Path &gameFilename)
627 	{
628 		Path fn = GetSysDirectory(DIRECTORY_SAVESTATE) / LOAD_UNDO_NAME;
629 		return File::Exists(fn) && g_Config.sStateLoadUndoGame == GenerateFullDiscId(gameFilename);
630 	}
631 
operator <(const tm & t1,const tm & t2)632 	bool operator < (const tm &t1, const tm &t2) {
633 		if (t1.tm_year < t2.tm_year) return true;
634 		if (t1.tm_year > t2.tm_year) return false;
635 		if (t1.tm_mon < t2.tm_mon) return true;
636 		if (t1.tm_mon > t2.tm_mon) return false;
637 		if (t1.tm_mday < t2.tm_mday) return true;
638 		if (t1.tm_mday > t2.tm_mday) return false;
639 		if (t1.tm_hour < t2.tm_hour) return true;
640 		if (t1.tm_hour > t2.tm_hour) return false;
641 		if (t1.tm_min < t2.tm_min) return true;
642 		if (t1.tm_min > t2.tm_min) return false;
643 		if (t1.tm_sec < t2.tm_sec) return true;
644 		if (t1.tm_sec > t2.tm_sec) return false;
645 		return false;
646 	}
647 
operator >(const tm & t1,const tm & t2)648 	bool operator > (const tm &t1, const tm &t2) {
649 		if (t1.tm_year > t2.tm_year) return true;
650 		if (t1.tm_year < t2.tm_year) return false;
651 		if (t1.tm_mon > t2.tm_mon) return true;
652 		if (t1.tm_mon < t2.tm_mon) return false;
653 		if (t1.tm_mday > t2.tm_mday) return true;
654 		if (t1.tm_mday < t2.tm_mday) return false;
655 		if (t1.tm_hour > t2.tm_hour) return true;
656 		if (t1.tm_hour < t2.tm_hour) return false;
657 		if (t1.tm_min > t2.tm_min) return true;
658 		if (t1.tm_min < t2.tm_min) return false;
659 		if (t1.tm_sec > t2.tm_sec) return true;
660 		if (t1.tm_sec < t2.tm_sec) return false;
661 		return false;
662 	}
663 
operator !(const tm & t1)664 	bool operator ! (const tm &t1) {
665 		if (t1.tm_year || t1.tm_mon || t1.tm_mday || t1.tm_hour || t1.tm_min || t1.tm_sec) return false;
666 		return true;
667 	}
668 
GetNewestSlot(const Path & gameFilename)669 	int GetNewestSlot(const Path &gameFilename) {
670 		int newestSlot = -1;
671 		tm newestDate = {0};
672 		for (int i = 0; i < NUM_SLOTS; i++) {
673 			Path fn = GenerateSaveSlotFilename(gameFilename, i, STATE_EXTENSION);
674 			if (File::Exists(fn)) {
675 				tm time;
676 				bool success = File::GetModifTime(fn, time);
677 				if (success && newestDate < time) {
678 					newestDate = time;
679 					newestSlot = i;
680 				}
681 			}
682 		}
683 		return newestSlot;
684 	}
685 
GetOldestSlot(const Path & gameFilename)686 	int GetOldestSlot(const Path &gameFilename) {
687 		int oldestSlot = -1;
688 		tm oldestDate = {0};
689 		for (int i = 0; i < NUM_SLOTS; i++) {
690 			Path fn = GenerateSaveSlotFilename(gameFilename, i, STATE_EXTENSION);
691 			if (File::Exists(fn)) {
692 				tm time;
693 				bool success = File::GetModifTime(fn, time);
694 				if (success && (!oldestDate || oldestDate > time)) {
695 					oldestDate = time;
696 					oldestSlot = i;
697 				}
698 			}
699 		}
700 		return oldestSlot;
701 	}
702 
GetSlotDateAsString(const Path & gameFilename,int slot)703 	std::string GetSlotDateAsString(const Path &gameFilename, int slot) {
704 		Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION);
705 		if (File::Exists(fn)) {
706 			tm time;
707 			if (File::GetModifTime(fn, time)) {
708 				char buf[256];
709 				// TODO: Use local time format? Americans and some others might not like ISO standard :)
710 				switch (g_Config.iDateFormat) {
711 				case PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD:
712 					strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &time);
713 					break;
714 				case PSP_SYSTEMPARAM_DATE_FORMAT_MMDDYYYY:
715 					strftime(buf, sizeof(buf), "%m-%d-%Y %H:%M:%S", &time);
716 					break;
717 				case PSP_SYSTEMPARAM_DATE_FORMAT_DDMMYYYY:
718 					strftime(buf, sizeof(buf), "%d-%m-%Y %H:%M:%S", &time);
719 					break;
720 				default: // Should never happen
721 					return "";
722 				}
723 				return std::string(buf);
724 			}
725 		}
726 		return "";
727 	}
728 
Flush()729 	std::vector<Operation> Flush()
730 	{
731 		std::lock_guard<std::mutex> guard(mutex);
732 		std::vector<Operation> copy = pending;
733 		pending.clear();
734 
735 		return copy;
736 	}
737 
HandleLoadFailure()738 	bool HandleLoadFailure()
739 	{
740 		// Okay, first, let's give the rewind state a shot - maybe we can at least not reset entirely.
741 		// Even if this was a rewind, maybe we can still load a previous one.
742 		CChunkFileReader::Error result;
743 		do {
744 			std::string errorString;
745 			result = rewindStates.Restore(&errorString);
746 		} while (result == CChunkFileReader::ERROR_BROKEN_STATE);
747 
748 		if (result == CChunkFileReader::ERROR_NONE) {
749 			return true;
750 		}
751 
752 		// We tried, our only remaining option is to reset the game.
753 		needsRestart = true;
754 		// Make sure we don't proceed to run anything yet.
755 		coreState = CORE_NEXTFRAME;
756 		return false;
757 	}
758 
CheckRewindState()759 	static inline void CheckRewindState()
760 	{
761 		if (gpuStats.numFlips % g_Config.iRewindFlipFrequency != 0)
762 			return;
763 
764 		// For fast-forwarding, otherwise they may be useless and too close.
765 		double now = time_now_d();
766 		float diff = now - rewindLastTime;
767 		if (diff < rewindMaxWallFrequency)
768 			return;
769 
770 		rewindLastTime = now;
771 		DEBUG_LOG(BOOT, "Saving rewind state");
772 		rewindStates.Save();
773 	}
774 
HasLoadedState()775 	bool HasLoadedState() {
776 		return hasLoadedState;
777 	}
778 
IsStale()779 	bool IsStale() {
780 		if (saveStateGeneration >= STALE_STATE_USES) {
781 			return CoreTiming::GetGlobalTimeUs() > STALE_STATE_TIME;
782 		}
783 		return false;
784 	}
785 
IsOldVersion()786 	bool IsOldVersion() {
787 		if (saveStateInitialGitVersion.empty())
788 			return false;
789 
790 		Version state(saveStateInitialGitVersion);
791 		Version gitVer(PPSSPP_GIT_VERSION);
792 		if (!state.IsValid() || !gitVer.IsValid())
793 			return false;
794 
795 		return state < gitVer;
796 	}
797 
TriggerLoadWarnings(std::string & callbackMessage)798 	static Status TriggerLoadWarnings(std::string &callbackMessage) {
799 		auto sc = GetI18NCategory("Screen");
800 
801 		if (g_Config.bHideStateWarnings)
802 			return Status::SUCCESS;
803 
804 		if (IsStale()) {
805 			// For anyone wondering why (too long to put on the screen in an osm):
806 			// Using save states instead of saves simulates many hour play sessions.
807 			// Sometimes this exposes game bugs that were rarely seen on real devices,
808 			// because few people played on a real PSP for 10 hours straight.
809 			callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs.");
810 			return Status::WARNING;
811 		}
812 		if (IsOldVersion()) {
813 			// Save states also preserve bugs from old PPSSPP versions, so warn.
814 			callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs.");
815 			return Status::WARNING;
816 		}
817 		// If the loaded state (saveDataGeneration) is older, the game may prevent saving again.
818 		// This can happen with newer too, but ignore to/from 0 as a common likely safe case.
819 		if (saveDataGeneration != lastSaveDataGeneration && saveDataGeneration != 0 && lastSaveDataGeneration != 0) {
820 			if (saveDataGeneration < lastSaveDataGeneration)
821 				callbackMessage = sc->T("Loaded. Game may refuse to save over newer savedata.");
822 			else
823 				callbackMessage = sc->T("Loaded. Game may refuse to save over different savedata.");
824 			return Status::WARNING;
825 		}
826 		return Status::SUCCESS;
827 	}
828 
Process()829 	void Process()
830 	{
831 		if (g_Config.iRewindFlipFrequency != 0 && gpuStats.numFlips != 0)
832 			CheckRewindState();
833 
834 		if (!needsProcess)
835 			return;
836 		needsProcess = false;
837 
838 		if (!__KernelIsRunning())
839 		{
840 			ERROR_LOG(SAVESTATE, "Savestate failure: Unable to load without kernel, this should never happen.");
841 			return;
842 		}
843 
844 		std::vector<Operation> operations = Flush();
845 		SaveStart state;
846 
847 		for (size_t i = 0, n = operations.size(); i < n; ++i)
848 		{
849 			Operation &op = operations[i];
850 			CChunkFileReader::Error result;
851 			Status callbackResult;
852 			bool tempResult;
853 			std::string callbackMessage;
854 			std::string title;
855 
856 			auto sc = GetI18NCategory("Screen");
857 			const char *i18nLoadFailure = sc->T("Load savestate failed", "");
858 			const char *i18nSaveFailure = sc->T("Save State Failed", "");
859 			if (strlen(i18nLoadFailure) == 0)
860 				i18nLoadFailure = sc->T("Failed to load state");
861 			if (strlen(i18nSaveFailure) == 0)
862 				i18nSaveFailure = sc->T("Failed to save state");
863 
864 			std::string slot_prefix = op.slot >= 0 ? StringFromFormat("(%d) ", op.slot + 1) : "";
865 			std::string errorString;
866 
867 			switch (op.type)
868 			{
869 			case SAVESTATE_LOAD:
870 				INFO_LOG(SAVESTATE, "Loading state from '%s'", op.filename.c_str());
871 				// Use the state's latest version as a guess for saveStateInitialGitVersion.
872 				result = CChunkFileReader::Load(op.filename, &saveStateInitialGitVersion, state, &errorString);
873 				if (result == CChunkFileReader::ERROR_NONE) {
874 					callbackMessage = op.slot != LOAD_UNDO_SLOT ? sc->T("Loaded State") : sc->T("State load undone");
875 					callbackResult = TriggerLoadWarnings(callbackMessage);
876 					hasLoadedState = true;
877 					Core_ResetException();
878 
879 					if (!slot_prefix.empty())
880 						callbackMessage = slot_prefix + callbackMessage;
881 
882 #ifndef MOBILE_DEVICE
883 					if (g_Config.bSaveLoadResetsAVdumping) {
884 						if (g_Config.bDumpFrames) {
885 							AVIDump::Stop();
886 							AVIDump::Start(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight);
887 						}
888 						if (g_Config.bDumpAudio) {
889 							WAVDump::Reset();
890 						}
891 					}
892 #endif
893 				} else if (result == CChunkFileReader::ERROR_BROKEN_STATE) {
894 					HandleLoadFailure();
895 					callbackMessage = std::string(i18nLoadFailure) + ": " + errorString;
896 					ERROR_LOG(SAVESTATE, "Load state failure: %s", errorString.c_str());
897 					callbackResult = Status::FAILURE;
898 				} else {
899 					callbackMessage = sc->T(errorString.c_str(), i18nLoadFailure);
900 					callbackResult = Status::FAILURE;
901 				}
902 				break;
903 
904 			case SAVESTATE_SAVE:
905 				INFO_LOG(SAVESTATE, "Saving state to %s", op.filename.c_str());
906 				title = g_paramSFO.GetValueString("TITLE");
907 				if (title.empty()) {
908 					// Homebrew title
909 					title = PSP_CoreParameter().fileToStart.ToVisualString();
910 					std::size_t lslash = title.find_last_of("/");
911 					title = title.substr(lslash + 1);
912 				}
913 				result = CChunkFileReader::Save(op.filename, title, PPSSPP_GIT_VERSION, state);
914 				if (result == CChunkFileReader::ERROR_NONE) {
915 					callbackMessage = slot_prefix + sc->T("Saved State");
916 					callbackResult = Status::SUCCESS;
917 #ifndef MOBILE_DEVICE
918 					if (g_Config.bSaveLoadResetsAVdumping) {
919 						if (g_Config.bDumpFrames) {
920 							AVIDump::Stop();
921 							AVIDump::Start(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight);
922 						}
923 						if (g_Config.bDumpAudio) {
924 							WAVDump::Reset();
925 						}
926 					}
927 #endif
928 				} else if (result == CChunkFileReader::ERROR_BROKEN_STATE) {
929 					// TODO: What else might we want to do here? This should be very unusual.
930 					callbackMessage = i18nSaveFailure;
931 					ERROR_LOG(SAVESTATE, "Save state failure");
932 					callbackResult = Status::FAILURE;
933 				} else {
934 					callbackMessage = i18nSaveFailure;
935 					callbackResult = Status::FAILURE;
936 				}
937 				break;
938 
939 			case SAVESTATE_VERIFY:
940 				tempResult = CChunkFileReader::Verify(state) == CChunkFileReader::ERROR_NONE;
941 				callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE;
942 				if (tempResult) {
943 					INFO_LOG(SAVESTATE, "Verified save state system");
944 				} else {
945 					ERROR_LOG(SAVESTATE, "Save state system verification failed");
946 				}
947 				break;
948 
949 			case SAVESTATE_REWIND:
950 				INFO_LOG(SAVESTATE, "Rewinding to recent savestate snapshot");
951 				result = rewindStates.Restore(&errorString);
952 				if (result == CChunkFileReader::ERROR_NONE) {
953 					callbackMessage = sc->T("Loaded State");
954 					callbackResult = Status::SUCCESS;
955 					hasLoadedState = true;
956 					Core_ResetException();
957 				} else if (result == CChunkFileReader::ERROR_BROKEN_STATE) {
958 					// Cripes.  Good news is, we might have more.  Let's try those too, better than a reset.
959 					if (HandleLoadFailure()) {
960 						// Well, we did rewind, even if too much...
961 						callbackMessage = sc->T("Loaded State");
962 						callbackResult = Status::SUCCESS;
963 						hasLoadedState = true;
964 						Core_ResetException();
965 					} else {
966 						callbackMessage = std::string(i18nLoadFailure) + ": " + errorString;
967 						callbackResult = Status::FAILURE;
968 					}
969 				} else {
970 					callbackMessage = std::string(i18nLoadFailure) + ": " + errorString;
971 					callbackResult = Status::FAILURE;
972 				}
973 				break;
974 
975 			case SAVESTATE_SAVE_SCREENSHOT:
976 			{
977 				int maxRes = g_Config.iInternalResolution > 2 ? 2 : -1;
978 				tempResult = TakeGameScreenshot(op.filename, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, maxRes);
979 				callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE;
980 				if (!tempResult) {
981 					ERROR_LOG(SAVESTATE, "Failed to take a screenshot for the savestate! %s", op.filename.c_str());
982 					if (screenshotFailures++ < SCREENSHOT_FAILURE_RETRIES) {
983 						// Requeue for next frame.
984 						SaveScreenshot(op.filename, op.callback, op.cbUserData);
985 					}
986 				} else {
987 					screenshotFailures = 0;
988 				}
989 				break;
990 			}
991 			default:
992 				ERROR_LOG(SAVESTATE, "Savestate failure: unknown operation type %d", op.type);
993 				callbackResult = Status::FAILURE;
994 				break;
995 			}
996 
997 			if (op.callback)
998 				op.callback(callbackResult, callbackMessage, op.cbUserData);
999 		}
1000 		if (operations.size()) {
1001 			// Avoid triggering frame skipping due to slowdown
1002 			__DisplaySetWasPaused();
1003 		}
1004 	}
1005 
NotifySaveData()1006 	void NotifySaveData() {
1007 		saveDataGeneration++;
1008 		lastSaveDataGeneration = saveDataGeneration;
1009 	}
1010 
Cleanup()1011 	void Cleanup() {
1012 		if (needsRestart) {
1013 			PSP_Shutdown();
1014 			std::string resetError;
1015 			if (!PSP_Init(PSP_CoreParameter(), &resetError)) {
1016 				ERROR_LOG(BOOT, "Error resetting: %s", resetError.c_str());
1017 				// TODO: This probably doesn't clean up well enough.
1018 				Core_Stop();
1019 				return;
1020 			}
1021 			host->BootDone();
1022 			host->UpdateDisassembly();
1023 			needsRestart = false;
1024 		}
1025 	}
1026 
Init()1027 	void Init()
1028 	{
1029 		// Make sure there's a directory for save slots
1030 		File::CreateFullPath(GetSysDirectory(DIRECTORY_SAVESTATE));
1031 
1032 		std::lock_guard<std::mutex> guard(mutex);
1033 		rewindStates.Clear();
1034 
1035 		hasLoadedState = false;
1036 		saveStateGeneration = 0;
1037 		saveDataGeneration = 0;
1038 		lastSaveDataGeneration = 0;
1039 		saveStateInitialGitVersion.clear();
1040 	}
1041 
Shutdown()1042 	void Shutdown()
1043 	{
1044 		std::lock_guard<std::mutex> guard(mutex);
1045 		rewindStates.Clear();
1046 	}
1047 }
1048