mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-21 02:09:05 +02:00
Merge pull request #22666 from hrydgard/savestate-fixes
Savestate fixes: rewind ring, slot operations, load failure messages
This commit is contained in:
54 files changed
+319
-131
No files matched your search
@@ -383,7 +383,12 @@ CChunkFileReader::Error CChunkFileReader::LoadFileHeader(File::IOFile &pFile, SC
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
CChunkFileReader::Error CChunkFileReader::GetFileTitle(const Path &filename, std::string *title) {
|
||||
// Not necessarily null-terminated.
|
||||
static std::string HeaderGitVersion(const char (&gitVersion)[32]) {
|
||||
return std::string(gitVersion, strnlen(gitVersion, sizeof(gitVersion)));
|
||||
}
|
||||
|
||||
CChunkFileReader::Error CChunkFileReader::GetFileTitle(const Path &filename, std::string *title, std::string *gitVersion) {
|
||||
if (!File::Exists(filename)) {
|
||||
ERROR_LOG(Log::SaveState, "ChunkReader: File doesn't exist");
|
||||
return ERROR_BAD_FILE;
|
||||
@@ -391,7 +396,11 @@ CChunkFileReader::Error CChunkFileReader::GetFileTitle(const Path &filename, std
|
||||
|
||||
File::IOFile pFile(filename, "rb");
|
||||
SChunkHeader header;
|
||||
return LoadFileHeader(pFile, header, title);
|
||||
Error err = LoadFileHeader(pFile, header, title);
|
||||
if (err == ERROR_NONE && gitVersion) {
|
||||
*gitVersion = HeaderGitVersion(header.GitVersion);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
CChunkFileReader::Error CChunkFileReader::LoadFile(const Path &filename, std::string *gitVersion, u8 *&_buffer, size_t &sz, std::string *failureReason) {
|
||||
@@ -460,11 +469,7 @@ CChunkFileReader::Error CChunkFileReader::LoadFile(const Path &filename, std::st
|
||||
_buffer = buffer;
|
||||
}
|
||||
|
||||
if (header.GitVersion[31]) {
|
||||
*gitVersion = std::string(header.GitVersion, 32);
|
||||
} else {
|
||||
*gitVersion = header.GitVersion;
|
||||
}
|
||||
*gitVersion = HeaderGitVersion(header.GitVersion);
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
@@ -200,7 +200,8 @@ public:
|
||||
ERROR_BAD_ALLOC,
|
||||
};
|
||||
|
||||
// May fail badly if ptr doesn't point to valid data.
|
||||
// May fail badly if ptr doesn't point to valid data. On failure, errorString is the title of the
|
||||
// section that failed.
|
||||
template<class T>
|
||||
static Error LoadPtr(u8 *ptr, size_t size, T &_class, std::string *errorString)
|
||||
{
|
||||
@@ -211,8 +212,7 @@ public:
|
||||
if (p.error != p.ERROR_FAILURE) {
|
||||
return ERROR_NONE;
|
||||
} else {
|
||||
std::string badSectionTitle = p.GetBadSectionTitle() ? p.GetBadSectionTitle() : "(unknown bad section)";
|
||||
*errorString = std::string("Failure at ") + badSectionTitle;
|
||||
*errorString = p.GetBadSectionTitle() ? p.GetBadSectionTitle() : "unknown section";
|
||||
return ERROR_BROKEN_STATE;
|
||||
}
|
||||
}
|
||||
@@ -292,7 +292,9 @@ public:
|
||||
template<class T>
|
||||
static Error Load(const Path &filename, std::string *gitVersion, T& _class, std::string *failureReason)
|
||||
{
|
||||
*failureReason = "LoadStateWrongVersion";
|
||||
// LoadFile names the failures it can tell apart. A truncated or corrupt file isn't one of them,
|
||||
// and isn't necessarily from another version either.
|
||||
failureReason->clear();
|
||||
|
||||
u8 *ptr = nullptr;
|
||||
size_t sz;
|
||||
@@ -334,13 +336,16 @@ public:
|
||||
std::vector<u8> buffer(sz);
|
||||
|
||||
// Step 2: Dump the state.
|
||||
ptr = &buffer[0];
|
||||
p.SetMode(PointerWrap::MODE_WRITE);
|
||||
p.RewindForWrite(buffer.data());
|
||||
_class.DoState(p);
|
||||
if (!p.CheckAfterWrite()) {
|
||||
return ERROR_BROKEN_STATE;
|
||||
}
|
||||
|
||||
// Step 3: Verify the state.
|
||||
ptr = &buffer[0];
|
||||
ptr = buffer.data();
|
||||
p.SetMode(PointerWrap::MODE_VERIFY);
|
||||
p.SetReadEnd(buffer.data() + sz);
|
||||
_class.DoState(p);
|
||||
|
||||
if (p.error == PointerWrap::ERROR_FAILURE) {
|
||||
@@ -349,7 +354,7 @@ public:
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static Error GetFileTitle(const Path &filename, std::string *title);
|
||||
static Error GetFileTitle(const Path &filename, std::string *title, std::string *gitVersion = nullptr);
|
||||
|
||||
private:
|
||||
struct SChunkHeader
|
||||
|
||||
@@ -57,10 +57,18 @@ struct SaveStateResult {
|
||||
bool done = false;
|
||||
SaveState::Status status = SaveState::Status::FAILURE;
|
||||
std::string message;
|
||||
std::string metadata;
|
||||
};
|
||||
|
||||
enum class StateOp {
|
||||
LOAD,
|
||||
SAVE,
|
||||
REWIND,
|
||||
VERIFY,
|
||||
};
|
||||
|
||||
struct WebSocketGameState : public DebuggerSubscriber {
|
||||
void StateLoadSave(DebuggerRequest &req, bool save);
|
||||
void StateOperation(DebuggerRequest &req, StateOp op);
|
||||
void Broadcast(net::WebSocketServer *ws) override;
|
||||
|
||||
private:
|
||||
@@ -74,23 +82,33 @@ private:
|
||||
|
||||
} // namespace
|
||||
|
||||
// Load a savestate file (game.state.load), or save one (game.state.save)
|
||||
// Load a savestate file (game.state.load), save one (game.state.save), rewind to the newest
|
||||
// rewind snapshot (game.state.rewind), or check that saving and reading back the current state
|
||||
// agree (game.state.verify)
|
||||
//
|
||||
// The operation is queued, and happens at the end of the next frame the CPU runs, so follow this
|
||||
// with something that runs it, like cpu.runUntilTime.
|
||||
//
|
||||
// Rewind snapshots are only taken with the RewindSnapshotInterval setting on. A rewind restores the
|
||||
// newest snapshot and keeps it, so a later rewind comes back to it, but one within 0.8 seconds of
|
||||
// the last steps further back.
|
||||
//
|
||||
// Parameters:
|
||||
// - path: string, path to the .ppst file.
|
||||
// - path: string, path to the .ppst file (only for load and save).
|
||||
// - wait: optional boolean, respond when the operation has finished rather than when queued.
|
||||
//
|
||||
// Response (same event name) once queued, or an error if the network or achievements hardcore
|
||||
// mode refuses it. With wait, it's sent once done instead, with a message string, or an error
|
||||
// if the load or save failed.
|
||||
void WebSocketGameState::StateLoadSave(DebuggerRequest &req, bool save) {
|
||||
const char *event = save ? "game.state.save" : "game.state.load";
|
||||
// mode refuses it, or there's nothing to rewind to. With wait, it's sent once done instead, with
|
||||
// message and metadata strings (for a rewind, how long ago the snapshot was taken), or an error
|
||||
// if the operation failed.
|
||||
void WebSocketGameState::StateOperation(DebuggerRequest &req, StateOp op) {
|
||||
static const char *const events[] = { "game.state.load", "game.state.save", "game.state.rewind", "game.state.verify" };
|
||||
const char *event = events[(int)op];
|
||||
std::string path;
|
||||
bool wait = false;
|
||||
if (!req.ParamString("path", &path) || !req.ParamBool("wait", &wait, DebuggerParamType::OPTIONAL))
|
||||
if ((op == StateOp::LOAD || op == StateOp::SAVE) && !req.ParamString("path", &path))
|
||||
return;
|
||||
if (!req.ParamBool("wait", &wait, DebuggerParamType::OPTIONAL))
|
||||
return;
|
||||
|
||||
// The queued operation holds the only other reference, so Broadcast() can also tell when it's
|
||||
@@ -98,7 +116,7 @@ void WebSocketGameState::StateLoadSave(DebuggerRequest &req, bool save) {
|
||||
std::shared_ptr<SaveStateResult> result;
|
||||
if (wait)
|
||||
result = std::make_shared<SaveStateResult>();
|
||||
auto callback = [event, result](SaveState::Status status, std::string_view message, std::string_view) {
|
||||
auto callback = [event, result](SaveState::Status status, std::string_view message, std::string_view metadata) {
|
||||
if (status == SaveState::Status::FAILURE) {
|
||||
ERROR_LOG(Log::System, "%s: %.*s", event, STR_VIEW(message));
|
||||
}
|
||||
@@ -106,22 +124,43 @@ void WebSocketGameState::StateLoadSave(DebuggerRequest &req, bool save) {
|
||||
std::lock_guard<std::mutex> guard(result->lock);
|
||||
result->status = status;
|
||||
result->message = message;
|
||||
result->metadata = metadata;
|
||||
result->done = true;
|
||||
}
|
||||
};
|
||||
|
||||
bool running = false;
|
||||
bool canRewind = true;
|
||||
bool queued = false;
|
||||
Core_RunOnCPUThread([&] {
|
||||
running = PSP_GetBootState() == BootState::Complete;
|
||||
if (running) {
|
||||
queued = save ? SaveState::Save(Path(path), -1, callback) : SaveState::Load(Path(path), -1, callback);
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
switch (op) {
|
||||
case StateOp::LOAD:
|
||||
queued = SaveState::Load(Path(path), -1, callback);
|
||||
break;
|
||||
case StateOp::SAVE:
|
||||
queued = SaveState::Save(Path(path), -1, callback);
|
||||
break;
|
||||
case StateOp::REWIND:
|
||||
canRewind = SaveState::CanRewind();
|
||||
if (canRewind) {
|
||||
queued = SaveState::Rewind(callback);
|
||||
}
|
||||
break;
|
||||
case StateOp::VERIFY:
|
||||
queued = SaveState::Verify(callback);
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (!running)
|
||||
return req.Fail("Game not running");
|
||||
if (!canRewind)
|
||||
return req.Fail("No rewind snapshots");
|
||||
if (!queued) {
|
||||
return req.Fail(save ? "Could not queue savestate save" : "Could not queue savestate load");
|
||||
return req.Fail(std::string("Could not queue ") + event);
|
||||
}
|
||||
|
||||
if (!wait) {
|
||||
@@ -136,17 +175,22 @@ void WebSocketGameState::Broadcast(net::WebSocketServer *ws) {
|
||||
for (auto it = waiting_.begin(); it != waiting_.end(); ) {
|
||||
// Check this first: if the callback ran, it was done before letting go.
|
||||
bool dropped = it->result.use_count() == 1;
|
||||
std::lock_guard<std::mutex> guard(it->result->lock);
|
||||
if (!it->result->done && !dropped) {
|
||||
// Our own reference, since erasing the entry below must not free the mutex we're holding.
|
||||
std::shared_ptr<SaveStateResult> result = it->result;
|
||||
std::lock_guard<std::mutex> guard(result->lock);
|
||||
if (!result->done && !dropped) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool failed = !it->result->done || it->result->status == SaveState::Status::FAILURE;
|
||||
bool failed = !result->done || result->status == SaveState::Status::FAILURE;
|
||||
JsonWriter j;
|
||||
j.begin();
|
||||
j.writeString("event", failed ? "error" : it->event);
|
||||
j.writeString("message", it->result->done ? it->result->message : "Savestate operation was dropped");
|
||||
j.writeString("message", result->done ? result->message : "Savestate operation was dropped");
|
||||
if (result->done) {
|
||||
j.writeString("metadata", result->metadata);
|
||||
}
|
||||
if (failed) {
|
||||
j.writeInt("level", (int)LogLevel::LERROR);
|
||||
}
|
||||
@@ -173,8 +217,10 @@ DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map) {
|
||||
auto p = new WebSocketGameState();
|
||||
map["game.reset"] = &WebSocketGameReset;
|
||||
map["game.status"] = &WebSocketGameStatus;
|
||||
map["game.state.load"] = [p](DebuggerRequest &req) { p->StateLoadSave(req, false); };
|
||||
map["game.state.save"] = [p](DebuggerRequest &req) { p->StateLoadSave(req, true); };
|
||||
map["game.state.load"] = [p](DebuggerRequest &req) { p->StateOperation(req, StateOp::LOAD); };
|
||||
map["game.state.save"] = [p](DebuggerRequest &req) { p->StateOperation(req, StateOp::SAVE); };
|
||||
map["game.state.rewind"] = [p](DebuggerRequest &req) { p->StateOperation(req, StateOp::REWIND); };
|
||||
map["game.state.verify"] = [p](DebuggerRequest &req) { p->StateOperation(req, StateOp::VERIFY); };
|
||||
map["game.speed.get"] = &WebSocketGameSpeedGet;
|
||||
map["game.speed.set"] = &WebSocketGameSpeedSet;
|
||||
map["version"] = &WebSocketVersion;
|
||||
|
||||
+77
-16
@@ -16,6 +16,7 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
@@ -98,9 +99,13 @@ static StateRingbuffer rewindStates;
|
||||
// on some Android devices when we allow many save states.
|
||||
static double g_lastRescan = 0.0;
|
||||
static std::map<std::string, uint64_t, std::less<>> g_files; // the value is the mod-time.
|
||||
// Set when a slot save finishes. That's on the emu thread, so the rescan is left to the next reader.
|
||||
static std::atomic<bool> g_filesStale;
|
||||
|
||||
struct SaveStart {
|
||||
void DoState(PointerWrap &p);
|
||||
// Rewind snapshots and verification don't count towards IsStale().
|
||||
bool countsAsSave = true;
|
||||
};
|
||||
|
||||
enum class OperationType {
|
||||
@@ -127,6 +132,7 @@ int g_screenshotFailures;
|
||||
|
||||
CChunkFileReader::Error SaveToRam(std::vector<u8> &data) {
|
||||
SaveStart state;
|
||||
state.countsAsSave = false;
|
||||
return CChunkFileReader::MeasureAndSavePtr(state, &data);
|
||||
}
|
||||
|
||||
@@ -154,7 +160,7 @@ int g_screenshotFailures;
|
||||
|
||||
if (s >= 2) {
|
||||
// This only increments on save, of course (once, not also in the measuring pass.)
|
||||
if (p.mode == p.MODE_WRITE) {
|
||||
if (p.mode == p.MODE_WRITE && countsAsSave) {
|
||||
++saveStateGeneration;
|
||||
}
|
||||
Do(p, saveStateGeneration);
|
||||
@@ -201,16 +207,26 @@ int g_screenshotFailures;
|
||||
pspFileSystem.DoState(p);
|
||||
}
|
||||
|
||||
// For an operation that was refused after a warning was already shown, so there's no message.
|
||||
// The callback still has to hear about it (auto-load tracks failure that way).
|
||||
static void CallbackRefused(const Callback &callback) {
|
||||
if (callback) {
|
||||
callback(Status::FAILURE, "", "");
|
||||
}
|
||||
}
|
||||
|
||||
bool Enqueue(const SaveState::Operation &op) {
|
||||
// These two both refuse the operation, and both say so - dropping it silently just
|
||||
// looks like the hotkey didn't work. Callers that ask the same questions themselves
|
||||
// return before getting here, so nothing shows a message twice.
|
||||
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
|
||||
CallbackRefused(op.callback);
|
||||
return false;
|
||||
}
|
||||
// Hardcore mode bans loading savestates outright, and saving too unless the user opted
|
||||
// back into that.
|
||||
if (Achievements::WarnUserIfHardcoreModeActive(op.type == OperationType::Save)) {
|
||||
CallbackRefused(op.callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -245,14 +261,17 @@ int g_screenshotFailures;
|
||||
return Enqueue(Operation(OperationType::Save, filename, slot, callback));
|
||||
}
|
||||
|
||||
void Verify(Callback callback) {
|
||||
Enqueue(Operation(OperationType::Verify, Path(), -1, callback));
|
||||
bool Verify(Callback callback) {
|
||||
return Enqueue(Operation(OperationType::Verify, Path(), -1, callback));
|
||||
}
|
||||
|
||||
void Rewind(Callback callback) {
|
||||
bool Rewind(Callback callback) {
|
||||
// Or a snapshot can be taken just before the rewind runs (after a long pause, say), and the
|
||||
// rewind then restores that.
|
||||
rewindStates.NotifyState();
|
||||
if (coreState == CoreState::CORE_RUNTIME_ERROR)
|
||||
Core_Break(BreakReason::SavestateRewind, 0);
|
||||
Enqueue(Operation(OperationType::Rewind, Path(), -1, callback));
|
||||
return Enqueue(Operation(OperationType::Rewind, Path(), -1, callback));
|
||||
}
|
||||
|
||||
bool CanRewind() {
|
||||
@@ -407,11 +426,20 @@ int g_screenshotFailures;
|
||||
}
|
||||
|
||||
void LoadSlot(std::string_view gamePrefix, int slot, Callback callback) {
|
||||
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
|
||||
// Enqueue() would refuse too, but only after the load undo backup below has replaced the old one.
|
||||
if (NetworkWarnUserIfOnlineAndCantSavestate() || Achievements::WarnUserIfHardcoreModeActive(false)) {
|
||||
CallbackRefused(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
Path fn = GenerateSaveSlotPath(gamePrefix, slot, STATE_EXTENSION);
|
||||
if (!fn.empty() && !File::Exists(fn)) {
|
||||
if (callback) {
|
||||
auto sc = GetI18NCategory(I18NCat::SCREEN);
|
||||
callback(Status::FAILURE, sc->T("LoadStateDoesntExist", sc->T("Failed to load state")), "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!fn.empty()) {
|
||||
// This add only 1 extra state, should we just always enable it?
|
||||
if (g_Config.bEnableStateUndo) {
|
||||
@@ -474,7 +502,9 @@ int g_screenshotFailures;
|
||||
}
|
||||
|
||||
void SaveSlot(std::string_view gamePrefix, int slot, Callback callback) {
|
||||
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
|
||||
// Enqueue() would refuse too, but only after the screenshots below have been rotated.
|
||||
if (NetworkWarnUserIfOnlineAndCantSavestate() || Achievements::WarnUserIfHardcoreModeActive(true)) {
|
||||
CallbackRefused(callback);
|
||||
return;
|
||||
}
|
||||
Path fn = GenerateSaveSlotPath(gamePrefix, slot, STATE_EXTENSION);
|
||||
@@ -494,6 +524,7 @@ int g_screenshotFailures;
|
||||
DeleteIfExists(fn);
|
||||
}
|
||||
File::Rename(fn.WithExtraExtension(".tmp"), fn);
|
||||
g_filesStale = true;
|
||||
}
|
||||
if (callback) {
|
||||
callback(status, message, "");
|
||||
@@ -531,6 +562,7 @@ int g_screenshotFailures;
|
||||
// Swap them so they can undo again to redo. Mistakes happen.
|
||||
SwapIfExists(shotUndo, shot);
|
||||
SwapIfExists(fnUndo, fn);
|
||||
Rescan(gamePrefix);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -585,7 +617,14 @@ int g_screenshotFailures;
|
||||
g_lastRescan = time_now_d();
|
||||
}
|
||||
|
||||
static void RescanIfStale(std::string_view gamePrefix) {
|
||||
if (g_filesStale.exchange(false)) {
|
||||
Rescan(gamePrefix);
|
||||
}
|
||||
}
|
||||
|
||||
static bool SaveStateFileExists(std::string_view gamePrefix, int slot, const char *extension) {
|
||||
RescanIfStale(gamePrefix);
|
||||
return g_files.find(GenerateSaveSlotFilename(gamePrefix, slot, extension)) != g_files.end();
|
||||
}
|
||||
|
||||
@@ -651,6 +690,7 @@ int g_screenshotFailures;
|
||||
}
|
||||
|
||||
int GetNewestSlot(std::string_view gamePrefix) {
|
||||
RescanIfStale(gamePrefix);
|
||||
int newestSlot = -1;
|
||||
int64_t newestTime = 0;
|
||||
for (int i = 0; i < g_Config.iSaveStateSlotCount; i++) {
|
||||
@@ -667,6 +707,7 @@ int g_screenshotFailures;
|
||||
}
|
||||
|
||||
int GetOldestSlot(std::string_view gamePrefix) {
|
||||
RescanIfStale(gamePrefix);
|
||||
int oldestSlot = -1;
|
||||
int64_t oldestTime = INT64_MAX;
|
||||
for (int i = 0; i < g_Config.iSaveStateSlotCount; i++) {
|
||||
@@ -763,6 +804,17 @@ int g_screenshotFailures;
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Which build last saved the file, from its header. Shown when a load fails, since a state from
|
||||
// another version is the usual suspect.
|
||||
static std::string SavedByVersion(const Path &path) {
|
||||
std::string title;
|
||||
std::string gitVersion;
|
||||
if (CChunkFileReader::GetFileTitle(path, &title, &gitVersion) != CChunkFileReader::ERROR_NONE || gitVersion.empty()) {
|
||||
return "";
|
||||
}
|
||||
return "PPSSPP " + gitVersion;
|
||||
}
|
||||
|
||||
static bool HandleLoadFailure(bool wasRewinding, std::string *metadata) {
|
||||
if (wasRewinding) {
|
||||
WARN_LOG(Log::SaveState, "HandleLoadFailure - trying a rewind state.");
|
||||
@@ -857,6 +909,10 @@ int g_screenshotFailures;
|
||||
|
||||
if (!__KernelIsRunning()) {
|
||||
ERROR_LOG(Log::SaveState, "Savestate failure: Unable to load without kernel, this should never happen.");
|
||||
// needsProcess is already clear, so anything queued would only run after the next Enqueue.
|
||||
for (const auto &op : Flush()) {
|
||||
CallbackRefused(op.callback);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -871,6 +927,7 @@ int g_screenshotFailures;
|
||||
// identification has finished and hardcore mode has come up.
|
||||
if (Achievements::WarnUserIfHardcoreModeActive(op.type == OperationType::Save)) {
|
||||
WARN_LOG(Log::SaveState, "Dropping queued savestate operation - hardcore mode is active");
|
||||
CallbackRefused(op.callback);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -917,11 +974,16 @@ int g_screenshotFailures;
|
||||
g_lastSaveTime = time_now_d();
|
||||
} else if (result == CChunkFileReader::ERROR_BROKEN_STATE) {
|
||||
HandleLoadFailure(false, &callbackMetadata);
|
||||
callbackMessage = std::string(i18nLoadFailure) + ": " + errorString;
|
||||
ERROR_LOG(Log::SaveState, "Load state failure: %s", errorString.c_str());
|
||||
// The file was fine, its contents weren't: almost always a state from another version.
|
||||
// errorString is the section that failed.
|
||||
callbackMessage = sc->T("LoadStateWrongVersion", i18nLoadFailure);
|
||||
const std::string version = SavedByVersion(op.path);
|
||||
callbackMetadata = version.empty() ? "(" + errorString + ")" : version + " (" + errorString + ")";
|
||||
ERROR_LOG(Log::SaveState, "Load state failure at %s (saved by %s)", errorString.c_str(), version.c_str());
|
||||
callbackResult = Status::FAILURE;
|
||||
} else {
|
||||
callbackMessage = sc->T(errorString, i18nLoadFailure);
|
||||
callbackMessage = errorString.empty() ? std::string(i18nLoadFailure) : std::string(sc->T(errorString, i18nLoadFailure));
|
||||
callbackMetadata = SavedByVersion(op.path);
|
||||
callbackResult = Status::FAILURE;
|
||||
}
|
||||
break;
|
||||
@@ -964,10 +1026,9 @@ int g_screenshotFailures;
|
||||
|
||||
case OperationType::Verify:
|
||||
{
|
||||
// Its write pass counts as a save, which it isn't.
|
||||
const int generation = saveStateGeneration;
|
||||
int tempResult = CChunkFileReader::Verify(state) == CChunkFileReader::ERROR_NONE;
|
||||
saveStateGeneration = generation;
|
||||
SaveStart verifyState;
|
||||
verifyState.countsAsSave = false;
|
||||
int tempResult = CChunkFileReader::Verify(verifyState) == CChunkFileReader::ERROR_NONE;
|
||||
callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE;
|
||||
if (tempResult) {
|
||||
INFO_LOG(Log::SaveState, "Verified save state system");
|
||||
@@ -994,11 +1055,11 @@ int g_screenshotFailures;
|
||||
hasLoadedState = true;
|
||||
Core_ResetException();
|
||||
} else {
|
||||
callbackMessage = std::string(i18nLoadFailure) + ": " + errorString;
|
||||
callbackMessage = std::string(i18nLoadFailure) + " (" + errorString + ")";
|
||||
callbackResult = Status::FAILURE;
|
||||
}
|
||||
} else {
|
||||
callbackMessage = std::string(i18nLoadFailure) + ": " + errorString;
|
||||
callbackMessage = i18nLoadFailure;
|
||||
callbackResult = Status::FAILURE;
|
||||
}
|
||||
break;
|
||||
|
||||
+2
-2
@@ -100,11 +100,11 @@ namespace SaveState {
|
||||
|
||||
// For testing / automated tests. Runs a save state verification pass (async.)
|
||||
// Warning: callback will be called on a different thread.
|
||||
void Verify(Callback callback = Callback());
|
||||
bool Verify(Callback callback = Callback());
|
||||
|
||||
// To go back to a previous snapshot (only if enabled.)
|
||||
// Warning: callback will be called on a different thread.
|
||||
void Rewind(Callback callback = Callback());
|
||||
bool Rewind(Callback callback = Callback());
|
||||
|
||||
// Returns true if there are rewind snapshots available.
|
||||
bool CanRewind();
|
||||
|
||||
+71
-21
@@ -4,6 +4,7 @@
|
||||
#include "Core/SaveState.h"
|
||||
#include "Core/SaveStateRewind.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Config.h"
|
||||
|
||||
namespace SaveState {
|
||||
@@ -18,9 +19,7 @@ CChunkFileReader::Error StateRingbuffer::Save() {
|
||||
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
|
||||
int n = next_++ % size_;
|
||||
if ((next_ % size_) == first_)
|
||||
++first_;
|
||||
const int n = next_;
|
||||
|
||||
std::vector<u8> *compressBuffer = &buffer_;
|
||||
CChunkFileReader::Error err;
|
||||
@@ -38,15 +37,35 @@ CChunkFileReader::Error StateRingbuffer::Save() {
|
||||
|
||||
if (err == CChunkFileReader::ERROR_NONE) {
|
||||
ScheduleCompress(&states_[n].stateBuffer, compressBuffer, &bases_[base_]);
|
||||
states_[n].savedTime = time_now_d();
|
||||
states_[n].emuTime = CoreTiming::GetGlobalTimeUs() / 1000000.0;
|
||||
baseMapping_[n] = baseGeneration_[base_];
|
||||
next_ = (next_ + 1) % size_;
|
||||
if (count_ < size_) {
|
||||
count_++;
|
||||
}
|
||||
} else {
|
||||
states_[n].clear();
|
||||
}
|
||||
// The state a rewind restored isn't the newest any more, so the next rewind comes back to this one.
|
||||
newestRestored_ = false;
|
||||
|
||||
baseMapping_[n] = baseGeneration_[base_];
|
||||
// States compressed against a base that has since been replaced can't be decoded any more.
|
||||
// They're the oldest ones, so drop them from the end.
|
||||
while (count_ > 0) {
|
||||
const int oldest = (next_ - count_ + size_) % size_;
|
||||
if (BaseAlive(baseMapping_[oldest])) {
|
||||
break;
|
||||
}
|
||||
states_[oldest].clear();
|
||||
count_--;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
bool StateRingbuffer::BaseAlive(int generation) const {
|
||||
return generation >= 0 && baseGeneration_[generation % ARRAY_SIZE(bases_)] == generation;
|
||||
}
|
||||
|
||||
CChunkFileReader::Error StateRingbuffer::Restore(std::string *errorString, std::string *metadata) {
|
||||
// The newest state may still be compressing.
|
||||
if (compressThread_.joinable())
|
||||
@@ -58,37 +77,66 @@ CChunkFileReader::Error StateRingbuffer::Restore(std::string *errorString, std::
|
||||
if (Empty())
|
||||
return CChunkFileReader::ERROR_BAD_FILE;
|
||||
|
||||
int n = (--next_ + size_) % size_;
|
||||
if (states_[n].empty())
|
||||
return CChunkFileReader::ERROR_BAD_FILE;
|
||||
|
||||
auto pa = GetI18NCategory(I18NCat::PAUSE);
|
||||
// Restoring a state doesn't use it up: rewind, play on a bit, rewind again, and you're back at the
|
||||
// same state. Rewinding again quickly steps further back instead (except from the oldest state).
|
||||
const double now = time_now_d();
|
||||
const double emuNow = CoreTiming::GetGlobalTimeUs() / 1000000.0;
|
||||
const bool quick = newestRestored_ && now - lastRestoreTime_ < STEP_BACK_SECONDS;
|
||||
if (quick) {
|
||||
if (count_ > 1) {
|
||||
DropNewest();
|
||||
}
|
||||
} else if (!retryingAfterFailure_) {
|
||||
rewindFromEmuTime_ = emuNow;
|
||||
}
|
||||
retryingAfterFailure_ = false;
|
||||
|
||||
const int n = (next_ - 1 + size_) % size_;
|
||||
const int generation = baseMapping_[n];
|
||||
const int baseSlot = generation < 0 ? -1 : generation % (int)ARRAY_SIZE(bases_);
|
||||
if (baseSlot < 0 || baseGeneration_[baseSlot] != generation) {
|
||||
// The base this state was compressed against has since been overwritten, so it can't be
|
||||
// decoded any more. Only two bases are kept, but the state ring is longer.
|
||||
WARN_LOG(Log::SaveState, "Rewind: state %d was compressed against a base that's gone", n);
|
||||
if (states_[n].empty() || !BaseAlive(generation)) {
|
||||
// Save() drops states whose base is gone, so this shouldn't happen.
|
||||
WARN_LOG(Log::SaveState, "Rewind: state %d can't be decoded", n);
|
||||
DropNewest();
|
||||
return CChunkFileReader::ERROR_BAD_FILE;
|
||||
}
|
||||
const int baseSlot = generation % (int)ARRAY_SIZE(bases_);
|
||||
|
||||
static std::vector<u8> buffer;
|
||||
LockedDecompress(buffer, states_[n].stateBuffer, bases_[baseSlot]);
|
||||
CChunkFileReader::Error error = LoadFromRam(buffer, errorString);
|
||||
*metadata = pa->T("Rewind");
|
||||
if (error != CChunkFileReader::ERROR_NONE) {
|
||||
// Don't come back to it. HandleLoadFailure() tries the next one, and after a partial load the
|
||||
// clock is meaningless, so that keeps rewindFromEmuTime_.
|
||||
DropNewest();
|
||||
retryingAfterFailure_ = true;
|
||||
return error;
|
||||
}
|
||||
newestRestored_ = true;
|
||||
lastRestoreTime_ = now;
|
||||
|
||||
if (states_[n].savedTime) {
|
||||
auto pa = GetI18NCategory(I18NCat::PAUSE);
|
||||
*metadata = pa->T("Rewind");
|
||||
// In emulated time, from where the rewinding started: wall-clock time would count pauses,
|
||||
// fast-forward, and the time spent in states rewound to earlier.
|
||||
const double secondsBack = rewindFromEmuTime_ - states_[n].emuTime;
|
||||
if (secondsBack >= 0.0) {
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
metadata->append(" (");
|
||||
metadata->append(ApplySafeSubstitutions(di->T("%1 seconds ago"), static_cast<int>(time_now_d() - states_[n].savedTime)));
|
||||
metadata->append(ApplySafeSubstitutions(di->T("%1 seconds ago"), (int)(secondsBack + 0.5)));
|
||||
metadata->append(")");
|
||||
}
|
||||
|
||||
rewindLastTime_ = time_now_d();
|
||||
rewindLastTime_ = now;
|
||||
return error;
|
||||
}
|
||||
|
||||
void StateRingbuffer::DropNewest() {
|
||||
next_ = (next_ - 1 + size_) % size_;
|
||||
states_[next_].clear();
|
||||
count_--;
|
||||
newestRestored_ = false;
|
||||
}
|
||||
|
||||
void StateRingbuffer::ScheduleCompress(std::vector<u8> *result, const std::vector<u8> *state, const std::vector<u8> *base) {
|
||||
if (compressThread_.joinable())
|
||||
compressThread_.join();
|
||||
@@ -103,7 +151,7 @@ void StateRingbuffer::ScheduleCompress(std::vector<u8> *result, const std::vecto
|
||||
void StateRingbuffer::Compress(std::vector<u8> &result, const std::vector<u8> &state, const std::vector<u8> &base) {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
// Bail if we were cleared before locking.
|
||||
if (first_ == 0 && next_ == 0)
|
||||
if (count_ == 0)
|
||||
return;
|
||||
|
||||
double start_time = time_now_d();
|
||||
@@ -159,8 +207,8 @@ void StateRingbuffer::Clear() {
|
||||
|
||||
// This lock is mainly for shutdown.
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
first_ = 0;
|
||||
next_ = 0;
|
||||
count_ = 0;
|
||||
for (auto &b : bases_) {
|
||||
b.clear();
|
||||
}
|
||||
@@ -176,6 +224,8 @@ void StateRingbuffer::Clear() {
|
||||
buffer_.clear();
|
||||
base_ = -1;
|
||||
baseUsage_ = 0;
|
||||
newestRestored_ = false;
|
||||
retryingAfterFailure_ = false;
|
||||
rewindLastTime_ = time_now_d();
|
||||
}
|
||||
|
||||
|
||||
+18
-4
@@ -35,7 +35,7 @@ public:
|
||||
void Clear();
|
||||
|
||||
bool Empty() const {
|
||||
return next_ == first_;
|
||||
return count_ == 0;
|
||||
}
|
||||
|
||||
void Process();
|
||||
@@ -44,23 +44,30 @@ public:
|
||||
double NextStateTimestamp() const;
|
||||
|
||||
private:
|
||||
bool BaseAlive(int generation) const;
|
||||
void DropNewest();
|
||||
|
||||
const int BLOCK_SIZE = 8192;
|
||||
const int REWIND_NUM_STATES = 20;
|
||||
// TODO: Instead, based on size of compressed state?
|
||||
const int BASE_USAGE_INTERVAL = 15;
|
||||
// A rewind this soon after the last one steps back past the state that one restored.
|
||||
const double STEP_BACK_SECONDS = 0.8;
|
||||
|
||||
typedef std::vector<u8> StateBuffer;
|
||||
|
||||
struct RewindState {
|
||||
StateBuffer stateBuffer;
|
||||
double savedTime;
|
||||
// CoreTiming's clock when the state was taken, in seconds.
|
||||
double emuTime = 0.0;
|
||||
|
||||
bool empty() const { return stateBuffer.empty(); }
|
||||
void clear() { stateBuffer.clear(); savedTime = 0.0; }
|
||||
void clear() { stateBuffer.clear(); emuTime = 0.0; }
|
||||
};
|
||||
|
||||
int first_ = 0;
|
||||
// The slot the next save goes in, and how many slots before it hold states we can restore.
|
||||
int next_ = 0;
|
||||
int count_ = 0;
|
||||
int size_;
|
||||
|
||||
std::vector<RewindState> states_;
|
||||
@@ -78,6 +85,13 @@ private:
|
||||
int baseUsage_ = 0;
|
||||
|
||||
double rewindLastTime_ = 0.0f;
|
||||
|
||||
// Whether the newest state is the one the last rewind restored, and when that was.
|
||||
bool newestRestored_ = false;
|
||||
double lastRestoreTime_ = 0.0;
|
||||
// CoreTiming's clock when the current run of rewinds started, for "seconds ago".
|
||||
double rewindFromEmuTime_ = 0.0;
|
||||
bool retryingAfterFailure_ = false;
|
||||
};
|
||||
|
||||
} // namespace SaveState
|
||||
+11
-8
@@ -53,6 +53,13 @@ struct PendingScreenshot {
|
||||
|
||||
PendingScreenshot g_pendingScreenshot;
|
||||
|
||||
// The callback may schedule a retry, so the pending screenshot has to be out of the way before it runs.
|
||||
static void FailPendingScreenshot() {
|
||||
PendingScreenshot pending = std::move(g_pendingScreenshot);
|
||||
g_pendingScreenshot = {};
|
||||
pending.callback(ScreenshotResult::ScreenshotNotPossible);
|
||||
}
|
||||
|
||||
class JPEGFileStream : public jpge::output_stream {
|
||||
public:
|
||||
JPEGFileStream(const Path &filename) {
|
||||
@@ -329,16 +336,14 @@ bool ScreenshotNotifyEndOfFrame(Draw::DrawContext *draw) {
|
||||
if (g_display.rotation != DisplayRotation::ROTATE_0) {
|
||||
GPUDebugBuffer temp;
|
||||
if (!::GetOutputFramebuffer(draw, temp)) {
|
||||
g_pendingScreenshot.callback(ScreenshotResult::ScreenshotNotPossible);
|
||||
g_pendingScreenshot = {};
|
||||
FailPendingScreenshot();
|
||||
return false;
|
||||
}
|
||||
_dbg_assert_msg_(temp.IsBackBuffer(), "temp isn't backbuffer??");
|
||||
buf = ApplyRotation(temp, g_display.rotation);
|
||||
} else {
|
||||
if (!::GetOutputFramebuffer(draw, buf)) {
|
||||
g_pendingScreenshot.callback(ScreenshotResult::ScreenshotNotPossible);
|
||||
g_pendingScreenshot = {};
|
||||
FailPendingScreenshot();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -359,16 +364,14 @@ bool ScreenshotNotifyPostGameRender(Draw::DrawContext *draw) {
|
||||
GPUDebugBuffer buf;
|
||||
if (!gpu) {
|
||||
ERROR_LOG(Log::System, "Can't take screenshots when GPU not running");
|
||||
g_pendingScreenshot.callback(ScreenshotResult::ScreenshotNotPossible);
|
||||
g_pendingScreenshot = {};
|
||||
FailPendingScreenshot();
|
||||
return false;
|
||||
}
|
||||
|
||||
const int maxRes = g_pendingScreenshot.maxRes;
|
||||
|
||||
if (!gpu->GetCurrentFramebuffer(buf, g_pendingScreenshot.type == ScreenshotType::Render ? GPU_DBG_FRAMEBUF_RENDER : GPU_DBG_FRAMEBUF_DISPLAY, maxRes)) {
|
||||
g_pendingScreenshot.callback(ScreenshotResult::ScreenshotNotPossible);
|
||||
g_pendingScreenshot = {};
|
||||
FailPendingScreenshot();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1768,7 +1768,7 @@ ScreenRenderFlags EmuScreen::RunEmulation(bool skipBufferEffects) {
|
||||
} else if (PSP_CoreParameter().frozen) {
|
||||
std::string errorString;
|
||||
if (CChunkFileReader::ERROR_NONE != SaveState::LoadFromRam(freezeState_, &errorString)) {
|
||||
ERROR_LOG(Log::SaveState, "Failed to load freeze state (%s). Unfreezing.", errorString.c_str());
|
||||
ERROR_LOG(Log::SaveState, "Failed to load freeze state (failure at %s). Unfreezing.", errorString.c_str());
|
||||
PSP_CoreParameter().frozen = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1295,7 +1295,7 @@ Loaded. Game may refuse to save over different savedata. = تم التحميل.
|
||||
Loaded. Game may refuse to save over newer savedata. = تم التحميل. قد ترفض اللعبة الحفظ فوق بيانات حفظ أحدث. # AI translated
|
||||
Loaded. Save in game, restart, and load for less bugs. = تم التحميل. احفظ داخل اللعبة، ثم أعد التشغيل وحمّل الحفظ لتقليل المشاكل. # AI translated
|
||||
LoadStateDoesntExist = Failed to load state: Savestate doesn't exist!
|
||||
LoadStateWrongVersion = Failed to load state: Savestate is for an older version of PPSSPP!
|
||||
LoadStateWrongVersion = Failed to load state: Savestate is from a newer version of PPSSPP!
|
||||
norewind = No rewind save states available.
|
||||
Playing = جاري اللعب
|
||||
PressESC = إضغط خروج للدخول علي قائمة التوقف المؤقت.
|
||||
|
||||
@@ -1283,7 +1283,7 @@ Loaded. Game may refuse to save over different savedata. = Yükləndi. Oyun, ba
|
||||
Loaded. Game may refuse to save over newer savedata. = Yükləndi. Oyun, yeni qorunuş veriləni üzərinə qorumağı rədd edə bilər.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Yükləndi. Daha az yanlış üçün, oyunda qorunun, yenidən başladın və yükləyin.
|
||||
LoadStateDoesntExist = Durum yüklənişi uğursuz oldu: Qorunmuş durum yoxdur!
|
||||
LoadStateWrongVersion = Durum yüklənişi uğursuz oldu: Qorunmuş durum köhnə PPSSPP sürümü üçündür!
|
||||
LoadStateWrongVersion = Durum yüklənişi uğursuz oldu: Qorunmuş durum daha yeni PPSSPP sürümündəndir!
|
||||
norewind = Geri çəkiş durum qorunuşu əlçatan deyil.
|
||||
Playing = Oynayır
|
||||
PressESC = Ara seçməsini açmaq üçün ESC'ni basın.
|
||||
|
||||
@@ -1282,7 +1282,7 @@ Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, r
|
||||
Loaded. Game may refuse to save over different savedata. = Loaded. Game may refuse to save over different savedata.
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
LoadStateDoesntExist = Failed to load state: Savestate doesn't exist!
|
||||
LoadStateWrongVersion = Failed to load state: Savestate is for an older version of PPSSPP!
|
||||
LoadStateWrongVersion = Failed to load state: Savestate is from a newer version of PPSSPP!
|
||||
norewind = No rewind save states available.
|
||||
Playing = Playing
|
||||
PressESC = Press ESC to open the pause menu.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = Неуспех при зареждане на състояние: Записа не съществува!
|
||||
LoadStateWrongVersion = Неуспех при зареждане на състояние: Записа е за по-стара версия на PPSSPP!
|
||||
LoadStateWrongVersion = Неуспех при зареждане на състояние: Записа е от по-нова версия на PPSSPP!
|
||||
norewind = No rewind save states available.
|
||||
Playing = Playing
|
||||
PressESC = Натиснете ESC за да отворите pause menu-то.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Carregat. El joc pot
|
||||
Loaded. Game may refuse to save over newer savedata. = Carregat. El joc pot no guardar sobre dades més actuals.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Carregat. Guarda la partida, reinicia, i recarrega per a menys errors.
|
||||
LoadStateDoesntExist = Error en carregar l'estat: el fitxer no existeix.
|
||||
LoadStateWrongVersion = Error en carregar l'estat: el fitxer és d'una versió anterior del PPSSPP.
|
||||
LoadStateWrongVersion = Error en carregar l'estat: el fitxer és d'una versió més nova del PPSSPP.
|
||||
norewind = No hi ha estats desats de rebobinatge disponibles.
|
||||
Playing = Reproduint...
|
||||
PressESC = Prem Esc per obrir el menú de pausa.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = Nepodařilo se načíst stav: Uložený stav neexistuje!
|
||||
LoadStateWrongVersion = Nepodařilo se načíst stav: Stav je pro starší verzi PPSSPP!
|
||||
LoadStateWrongVersion = Nepodařilo se načíst stav: Stav je z novější verze PPSSPP!
|
||||
norewind = Nejsou dostupné žádné uložené stavy pro přetočení.
|
||||
Playing = Playing
|
||||
PressESC = Stiskněte ESC k otevření nabídky při pozastavení.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = Kunne ikke hente status: Spil-status eksisterer ikke!
|
||||
LoadStateWrongVersion = Kunne ikke hente stauts: Spil-status er for en ældre version af PPSSPP!
|
||||
LoadStateWrongVersion = Kunne ikke hente stauts: Spil-status er fra en nyere version af PPSSPP!
|
||||
norewind = Ingen status for fortrydelse er tilgængelig.
|
||||
Playing = Playing
|
||||
PressESC = Tryk ESC for at åbne pause menu.
|
||||
|
||||
@@ -1286,7 +1286,7 @@ Loaded. Game may refuse to save over different savedata. = Geladen. Das Spiel ka
|
||||
Loaded. Game may refuse to save over newer savedata. = Geladen. Das Spiel weigert sich möglicherweise, über neuere gespeicherte Daten zu speichern.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Geladen. Im Spiel speichern, neu starten und laden, um Fehler zu vermeiden.
|
||||
LoadStateDoesntExist = Fehler beim Laden des Spielstands: Spielstand existiert nicht!
|
||||
LoadStateWrongVersion = Fehler beim Laden des Spielstands: Spielstand ist für eine alte Version von PPSSPP!
|
||||
LoadStateWrongVersion = Fehler beim Laden des Spielstands: Spielstand stammt von einer neueren Version von PPSSPP!
|
||||
norewind = Keine Rückspulfunktion verfügbar
|
||||
Playing = Spiele
|
||||
PressESC = Drücke ESC, um das Pausemenü zu öffnen
|
||||
|
||||
@@ -1313,7 +1313,7 @@ Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, r
|
||||
Loaded. Game may refuse to save over different savedata. = Loaded. Game may refuse to save over different savedata.
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
LoadStateDoesntExist = Failed to load state: Savestate doesn't exist!
|
||||
LoadStateWrongVersion = Failed to load state: Savestate is for an older version of PPSSPP!
|
||||
LoadStateWrongVersion = Failed to load state: Savestate is from a newer version of PPSSPP!
|
||||
norewind = No rewind save states available.
|
||||
Playing = Playing
|
||||
PressESC = Press ESC to open the pause menu.
|
||||
|
||||
@@ -1288,7 +1288,7 @@ Loaded. Game may refuse to save over different savedata. = Cargado. El juego pue
|
||||
Loaded. Game may refuse to save over newer savedata. = Cargado. El juego puede negarse a guardar datos guardados más recientes.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Cargado. Guarda el juego, reinicia y carga para evitar errores.
|
||||
LoadStateDoesntExist = Error al cargar el estado: ¡El estado guardado no existe!
|
||||
LoadStateWrongVersion = Error al cargar el estado: ¡El estado guardado es para una versión anterior de PPSSPP!
|
||||
LoadStateWrongVersion = Error al cargar el estado: ¡El estado guardado es de una versión más reciente de PPSSPP!
|
||||
norewind = No hay estados de rebobinado guardados disponibles.
|
||||
Playing = Reproduciendo...
|
||||
PressESC = Presiona Esc para abrir el menú de pausa.
|
||||
|
||||
@@ -1284,7 +1284,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = !خطا در بارگیری: هیچ ذخیره ی سریعی پیدا نشد
|
||||
LoadStateWrongVersion = !است PPSSPP خطا در بارگیری: ذخیره موجود برای ورژنهای پایین تر
|
||||
LoadStateWrongVersion = !است PPSSPP خطا در بارگیری: ذخیره موجود برای ورژنهای بالاتر
|
||||
norewind = No rewind save states available.
|
||||
Playing = Playing
|
||||
PressESC = .را برای منوی سریع فشار دهید ESC دکمه
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Ladattu. Peli saattaa
|
||||
Loaded. Game may refuse to save over newer savedata. = Ladattu. Peli saattaa kieltäytyä tallentamasta uudelle tallennustiedolle.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Ladattu. Tallenna pelissä, käynnistä uudelleen ja lataa peli, vähentääksesi virheitä.
|
||||
LoadStateDoesntExist = Tilatallennuksen lataaminen epäonnistui: Tallennusta ei ole olemassa!
|
||||
LoadStateWrongVersion = Tilatallennuksen lataaminen epäonnistui: Tallennus on vanhemmalle PPSSPP-versiolle!
|
||||
LoadStateWrongVersion = Tilatallennuksen lataaminen epäonnistui: Tallennus on uudemmasta PPSSPP-versiosta!
|
||||
norewind = Takaisinkelauksen tilatallennuksia ei ole saatavilla.
|
||||
Playing = Playing
|
||||
PressESC = Press ESC to open the pause menu.
|
||||
|
||||
@@ -1314,7 +1314,7 @@ Loaded. Game may refuse to save over different savedata. = Chargé. Le jeu peut
|
||||
Loaded. Game may refuse to save over newer savedata. = Chargé. Le jeu peut refuser d'écraser les données de sauvegarde plus récentes.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Chargé.\nSauvegarder dans le jeu, redémarrer,\net charger la sauvegarde pour moins de bugs.
|
||||
LoadStateDoesntExist = Échec du chargement de l'état :\nl'état n'existe pas !
|
||||
LoadStateWrongVersion = Échec du chargement de l'état :\nl'état appartient à une ancienne version de PPSSPP !
|
||||
LoadStateWrongVersion = Échec du chargement de l'état :\nl'état provient d'une version plus récente de PPSSPP !
|
||||
norewind = Pas d'instantané de rembobinage disponible.
|
||||
Playing = En jeu
|
||||
PressESC = Appuyer sur "Échap" pour ouvrir le menu Pause.
|
||||
|
||||
@@ -1286,7 +1286,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = Erro ó cargar o estado: non existe o arquivo.
|
||||
LoadStateWrongVersion = Erro ó cargar o estado: o arquivo é dunha versión anterior de PPSSPP.
|
||||
LoadStateWrongVersion = Erro ó cargar o estado: o arquivo é dunha versión máis nova de PPSSPP.
|
||||
norewind = Non hai estados gardados de rebobinado dispoñibles.
|
||||
Playing = Playing
|
||||
PressESC = Presiona Esc para abrir o menú de pausa.
|
||||
|
||||
@@ -1286,7 +1286,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Φορτώθηκε. Αποθηκεύστε στο παιχνίδι, κάντε επανεκκίνηση και φορτώστε για λιγότερα σφάλματα.
|
||||
LoadStateDoesntExist = Σφάλμα φόρτωσης σημείου: Το σημείο αποθήκευσης δεν υπάρχει!
|
||||
LoadStateWrongVersion = Σφάλμα φόρτωσης σημείου: Το σημείο αποθήκευσης είναι για μία παλαιότερη έκδοση του PPSSPP!
|
||||
LoadStateWrongVersion = Σφάλμα φόρτωσης σημείου: Το σημείο αποθήκευσης είναι από μία νεότερη έκδοση του PPSSPP!
|
||||
norewind = Κανένα σημείο αποθήκευσης rewind διαθέσιμο.
|
||||
Playing = Παίζει
|
||||
PressESC = Πατήστε το ESC για να ανοίξετε το μενού παύσης.
|
||||
|
||||
@@ -1284,7 +1284,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = לא הצליח לטעון את המצב: המצב שמור אינו קיים!
|
||||
LoadStateWrongVersion = לא הצליח לטעון את המצב: המצב הוא שמירה שמיועדת לגרסה ישנה יותר של PPSSPP!
|
||||
LoadStateWrongVersion = לא הצליח לטעון את המצב: המצב הוא שמירה מגרסה חדשה יותר של PPSSPP!
|
||||
norewind = No rewind save states available.
|
||||
Playing = Playing
|
||||
PressESC = לחץ על ESC כדי לפתוח את התפריט.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Učitano. Spremi u igri, ponovo pokreni, i učitaj za manje bugova.
|
||||
LoadStateDoesntExist = Nije moguće učitati state: Savestate ne postoji!
|
||||
LoadStateWrongVersion = Nije moguće učitati state: Savestate je za stariju verziju PPSSPP-a!
|
||||
LoadStateWrongVersion = Nije moguće učitati state: Savestate je iz novije verzije PPSSPP-a!
|
||||
norewind = Rewind save state-ovi nisu dostupni.
|
||||
Playing = Playing
|
||||
PressESC = Pritisni ESC za otvaranje izbornika.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Betöltve. Lehet, hog
|
||||
Loaded. Game may refuse to save over newer savedata. = Betöltve. Lehet, hogy a játék nem fog felülírni újabb mentéseket.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Betöltve. Ments a játékban, indítsd újra, és töltsd be a mentést kevesebb hibáért!
|
||||
LoadStateDoesntExist = Hiba az állapotmentés betöltése közben: Az állapotmentés nem létezik!
|
||||
LoadStateWrongVersion = Hiba az állapotmentés betöltése közben: Az állapotmentés egy régebbi PPSSPP verzióhoz való!
|
||||
LoadStateWrongVersion = Hiba az állapotmentés betöltése közben: Az állapotmentés egy újabb PPSSPP verzióból származik!
|
||||
norewind = Nem érhető el visszatekerési állapotmentés.
|
||||
Playing = Lejátszás
|
||||
PressESC = Nyomd meg az ESC-et a menühöz.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Termuat. Game mungkin
|
||||
Loaded. Game may refuse to save over newer savedata. = Termuat. Game mungkin menolak untuk menyimpan lebih dari simpanan data yang lebih baru.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Termuat. Simpan dalam game, mulai ulang, dan muat untuk mengurangi kesalahan.
|
||||
LoadStateDoesntExist = Gagal memuat save state game: Savestate tidak ada!
|
||||
LoadStateWrongVersion = Gagal memuat save state game: Savestate ini untuk versi lama PPSSPP!
|
||||
LoadStateWrongVersion = Gagal memuat save state game: Savestate ini dari versi PPSSPP yang lebih baru!
|
||||
norewind = Tidak ada save state lama yang tersedia.
|
||||
Playing = Bermain
|
||||
PressESC = Tekan ESC untuk membuka menu jeda.
|
||||
|
||||
@@ -1290,7 +1290,7 @@ GLToolsWarning = AVVISO: rilevato GLTools, potrebbe causare problemi
|
||||
In menu = Nei menu
|
||||
Loaded State = Stato Caricato
|
||||
LoadStateDoesntExist = Caricamento stato fallito: lo stato non esiste!
|
||||
LoadStateWrongVersion = Caricamento stato fallito: lo stato è di una versione precedente di PPSSPP!
|
||||
LoadStateWrongVersion = Caricamento stato fallito: lo stato è di una versione più recente di PPSSPP!
|
||||
norewind = Nessun riavvolgimento degli stati salvati disponibile.
|
||||
Playing = Sta giocando
|
||||
PressESC = Premere ESC per aprire il Menu Pausa
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = ロードしました
|
||||
Loaded. Game may refuse to save over newer savedata. = ロードしました。最新のセーブデータがある場合セーブが拒否される場合があります。
|
||||
Loaded. Save in game, restart, and load for less bugs. = ロードしました。バグの発生を減らすためにゲーム中でセーブを行い、再起動してロードしてください。
|
||||
LoadStateDoesntExist = ステートのロードに失敗しました: セーブステートがありません。
|
||||
LoadStateWrongVersion = ステートのロードに失敗しました: 古いバージョンのセーブステートです。
|
||||
LoadStateWrongVersion = ステートのロードに失敗しました: 新しいバージョンのセーブステートです。
|
||||
norewind = 巻き戻すセーブステートがありません。
|
||||
Playing = プレイ中
|
||||
PressESC = ESCを押すと一時停止メニューを開きます。
|
||||
|
||||
@@ -1282,7 +1282,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = Gagal memuat: File simpen-cepet ora kasedhiya!
|
||||
LoadStateWrongVersion = Gagal memuat: File simpen-cepet iki kanggi versi lawas PPSSPP!
|
||||
LoadStateWrongVersion = Gagal memuat: File simpen-cepet iki saka versi PPSSPP sing luwih anyar!
|
||||
norewind = No rewind save states available.
|
||||
Playing = Playing
|
||||
PressESC = Tekan ESC kanggo ing Menu.
|
||||
|
||||
@@ -1280,7 +1280,7 @@ Loaded. Save in game, restart, and load for less bugs. =ផ្ទុក។ រ
|
||||
Loaded. Game may refuse to save over different savedata. =ផ្ទុក។ ហ្គេមអាចបដិសេធមិនរក្សាទុកទិន្នន័យរក្សាទុកផ្សេងៗគ្នា។
|
||||
Loaded. Game may refuse to save over newer savedata. =ផ្ទុក។ ហ្គេមអាចបដិសេធមិនរក្សាទុកទិន្នន័យរក្សាទុកថ្មីជាងនេះ។
|
||||
LoadStateDoesntExist =បរាជ័យក្នុងការផ្ទុកស្ថានភាព៖ Savestate មិនមានទេ!
|
||||
LoadStateWrongVersion =បរាជ័យក្នុងការផ្ទុកស្ថានភាព៖ Savestate គឺសម្រាប់កំណែចាស់របស់ PPSSPP!
|
||||
LoadStateWrongVersion =បរាជ័យក្នុងការផ្ទុកស្ថានភាព៖ Savestate គឺមកពីកំណែថ្មីជាងរបស់ PPSSPP!
|
||||
norewind =មិនមានស្ថានភាពរក្សាទុកថយក្រោយទេ។
|
||||
Playing =កំពុងលេង
|
||||
PressESC =ចុច ESC ដើម្បីបើកម៉ឺនុយផ្អាក។
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Save in game, restart, and load for less bugs. = 불러왔습니다. 버
|
||||
Loaded. Game may refuse to save over different savedata. = 불러왔습니다. 게임이 다른 저장 데이터에 대한 저장을 거부할 수 있습니다.
|
||||
Loaded. Game may refuse to save over newer savedata. = 불러왔습니다. 게임이 최신 저장 데이터에 대한 저장을 거부할 수 있습니다.
|
||||
LoadStateDoesntExist = 상태 불러오기 실패: 저장상태가 존재하지 않습니다!
|
||||
LoadStateWrongVersion = 상태 불러오기 실패: 저장상태는 이전 버전의 PPSSPP용입니다!
|
||||
LoadStateWrongVersion = 상태 불러오기 실패: 저장상태는 더 새로운 버전의 PPSSPP에서 만들어졌습니다!
|
||||
norewind = 되감기 저장 상태를 사용할 수 없습니다.
|
||||
Playing = 게임 중
|
||||
PressESC = 일시 중지 메뉴를 열려면 ESC를 누르세요.
|
||||
|
||||
@@ -1296,7 +1296,7 @@ Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, r
|
||||
Loaded. Game may refuse to save over different savedata. = Loaded. Game may refuse to save over different savedata.
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
LoadStateDoesntExist = Failed to load state: Savestate doesn't exist!
|
||||
LoadStateWrongVersion = Failed to load state: Savestate is for an older version of PPSSPP!
|
||||
LoadStateWrongVersion = Failed to load state: Savestate is from a newer version of PPSSPP!
|
||||
norewind = No rewind save states available.
|
||||
Playing = Playing
|
||||
PressESC = Press ESC to open the pause menu.
|
||||
|
||||
@@ -1282,7 +1282,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = ລົ້ມເຫຼວໃນການໂຫຼດໄຟລ໌ບັນທຶກ: ໄຟລ໌ບັນທຶກບໍ່ຖືກຕ້ອງ!
|
||||
LoadStateWrongVersion = ລົ້ມເຫຼວໃນການໂຫຼດໄຟລ໌ບັນທຶກ: ໄຟລ໌ບັນທຶກແມ່ນເວີຊັ່ນເກົ່າຂອງ PPSSPP!
|
||||
LoadStateWrongVersion = ລົ້ມເຫຼວໃນການໂຫຼດໄຟລ໌ບັນທຶກ: ໄຟລ໌ບັນທຶກແມ່ນມາຈາກເວີຊັ່ນໃໝ່ກວ່າຂອງ PPSSPP!
|
||||
norewind = ບໍ່ມີຂໍ້ມູນໄຟລ໌ບັນທຶກແບບຍ້ອນຫຼັງ.
|
||||
Playing = Playing
|
||||
PressESC = ກົດ ESC ເພື່ອເປີດເມນູຢຸດຊົ່ວຄາວ
|
||||
|
||||
@@ -1284,7 +1284,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = Nepavyko pakrauti statuso: išsaugojimo statusas neegzistuoja!
|
||||
LoadStateWrongVersion = Nepavyko pakrauti statuso: išsaugojimo statusas skirtas senesniai PPSSPP versijai!
|
||||
LoadStateWrongVersion = Nepavyko pakrauti statuso: išsaugojimo statusas yra iš naujesnės PPSSPP versijos!
|
||||
norewind = No rewind save states available.
|
||||
Playing = Playing
|
||||
PressESC = Paspauskite ESC, kad atidarytumėte pauzės meniu.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Dimuatkan. Permainan
|
||||
Loaded. Game may refuse to save over newer savedata. = Dimuatkan. Permainan mungkin enggan menyimpan di atas data simpanan baharu.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Dimuatkan. Simpan dalam permainan, mula semula, dan muat untuk mengurangkan bug.
|
||||
LoadStateDoesntExist = Gagal memuatkan state: Savestate tidak ditemui!
|
||||
LoadStateWrongVersion = Gagal memuatkan state: Savestate adalah untuk versi PPSSPP yang lebih lama!
|
||||
LoadStateWrongVersion = Gagal memuatkan state: Savestate adalah daripada versi PPSSPP yang lebih baharu!
|
||||
norewind = Tiada keadaan simpanan putar semula tersedia.
|
||||
Playing = Dimainkan
|
||||
PressESC = Tekan ESC untuk membuka menu jeda.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = Laden mislukt: Savestate bestaat niet!
|
||||
LoadStateWrongVersion = Laden mislukt: Savestate is voor een oudere versie van PPSSPP!
|
||||
LoadStateWrongVersion = Laden mislukt: Savestate is van een nieuwere versie van PPSSPP!
|
||||
norewind = Geen savestates beschikbaar voor terugspoelen.
|
||||
Playing = Playing
|
||||
PressESC = Druk op ESC om het pauzemenu te openen
|
||||
|
||||
@@ -1312,7 +1312,7 @@ Loaded. Save in game, restart, and load for less bugs. = Lasta inn. Lagre i spel
|
||||
Loaded. Game may refuse to save over different savedata. = Lasta inn. Spelet kan nekte å lagre over andre lagringsdata.
|
||||
Loaded. Game may refuse to save over newer savedata. = Lasta inn. Spelet kan nekte å lagre over nyare lagringsdata.
|
||||
LoadStateDoesntExist = Klarte ikkje å laste inn tilstand: Lagringstilstanden finst ikkje!
|
||||
LoadStateWrongVersion = Klarte ikkje å laste inn tilstand: Lagringstilstanden er for ein eldre versjon av PPSSPP!
|
||||
LoadStateWrongVersion = Klarte ikkje å laste inn tilstand: Lagringstilstanden er frå ein nyare versjon av PPSSPP!
|
||||
norewind = Ingen tilbakespolingstilstandar tilgjengelege.
|
||||
Playing = Spelar
|
||||
PressESC = Trykk ESC for å opne pausemenyen.
|
||||
|
||||
@@ -1312,7 +1312,7 @@ Loaded. Save in game, restart, and load for less bugs. = Lastet inn. Lagre i spi
|
||||
Loaded. Game may refuse to save over different savedata. = Lastet inn. Spillet kan nekte å lagre over andre lagringsdata.
|
||||
Loaded. Game may refuse to save over newer savedata. = Lastet inn. Spillet kan nekte å lagre over nyere lagringsdata.
|
||||
LoadStateDoesntExist = Mislyktes å laste: Lagret fil eksisterer ikke!
|
||||
LoadStateWrongVersion = Mislyktes å laste: Lagret fil er for en eldre versjon av PPSSPP!
|
||||
LoadStateWrongVersion = Mislyktes å laste: Lagret fil er fra en nyere versjon av PPSSPP!
|
||||
norewind = Ingen tilbakespolingstilstander tilgjengelig.
|
||||
Playing = Spiller
|
||||
PressESC = Trykk ESC for å åpne pausemenyen.
|
||||
|
||||
@@ -1295,7 +1295,7 @@ Loaded. Game may refuse to save over different savedata. = Wczytano. Gra może o
|
||||
Loaded. Game may refuse to save over newer savedata. = Wczytano. Gra może odmówić nadpisywania nowszych zapisów.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Wczytano. By zmniejszyć ryzyko potencjalnych problemów, zapisz stan w grze, zrestartuj ją i wczytaj.
|
||||
LoadStateDoesntExist = Nie można wczytać stanu: Zapis stanu nie istnieje!
|
||||
LoadStateWrongVersion = Nie można wczytać stanu: Zapis stanu przeznaczony jest dla starszej wersji PPSSPP!
|
||||
LoadStateWrongVersion = Nie można wczytać stanu: Zapis stanu pochodzi z nowszej wersji PPSSPP!
|
||||
norewind = Brak zapisów stanów potrzebnych do przewinięcia.
|
||||
Playing = W grze
|
||||
PressESC = Naciśnij klawisz ESC, aby otworzyć menu pauzy.
|
||||
|
||||
@@ -1310,7 +1310,7 @@ Loaded. Save in game, restart, and load for less bugs. = Carregado. Salve no jog
|
||||
Loaded. Game may refuse to save over different savedata. = Carregado. O jogo pode recusar a salvar sobre os dados diferentes dos salvamentos.
|
||||
Loaded. Game may refuse to save over newer savedata. = Carregado. O jogo pode recusar a salvar sobre os dados mais novos dos salvamentos.
|
||||
LoadStateDoesntExist = Falhou em carregar o state: O state salvo não existe!
|
||||
LoadStateWrongVersion = Falhou em carregar o state: O state salvo é de uma versão mais velha do PPSSPP!
|
||||
LoadStateWrongVersion = Falhou em carregar o state: O state salvo é de uma versão mais nova do PPSSPP!
|
||||
norewind = Não há states salvos disponíveis pra retroceder.
|
||||
Playing = Jogando
|
||||
PressESC = Pressione ESC pra abrir o menu da pausa.
|
||||
|
||||
@@ -1312,7 +1312,7 @@ Loaded. Save in game, restart, and load for less bugs. = Carregado. Salva no jog
|
||||
Loaded. Game may refuse to save over different savedata. = Carregado. O jogo poderá recusar-se a salvar sobre dados salvos diferentes.
|
||||
Loaded. Game may refuse to save over newer savedata. = Carregado. O jogo poderá recusar-se a salvar sobre dados salvos mais novos.
|
||||
LoadStateDoesntExist = Erro ao carregar o estado: O Estado salvo não existe!
|
||||
LoadStateWrongVersion = Erro ao carregar o estado: O Estado salvo é de uma versão mais antiga da PPSSPP!
|
||||
LoadStateWrongVersion = Erro ao carregar o estado: O Estado salvo é de uma versão mais recente da PPSSPP!
|
||||
norewind = Não há estados salvos disponíveis para retroceder.
|
||||
Playing = A jogar
|
||||
PressESC = Pressione a tecla Esc para abrir o menu de pausa.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Loaded. Save in game, restart, and load for less bugs.
|
||||
LoadStateDoesntExist = Eroare la încărcare de stare: Starea nu există!
|
||||
LoadStateWrongVersion = Eroare la încărcare de stare: Starea e pt. o versiune PPSSPP mai veche!
|
||||
LoadStateWrongVersion = Eroare la încărcare de stare: Starea e de la o versiune PPSSPP mai nouă!
|
||||
norewind = Fără stări salvare disponibile.
|
||||
Playing = Playing
|
||||
PressESC = Apasă ESC pt. a deschide meniul pauză.
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Game may refuse to save over different savedata. = Загружено.
|
||||
Loaded. Game may refuse to save over newer savedata. = Загружено. Игра может отказаться сохранять поверх более новых сохранений.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Загружено. Сохранитесь в игре, перезапустите игру и загрузите для исправления ошибок.
|
||||
LoadStateDoesntExist = Не удалось загрузить состояние: файл не существует!
|
||||
LoadStateWrongVersion = Не удалось загрузить состояние: файл состояния от старой версии PPSSPP!
|
||||
LoadStateWrongVersion = Не удалось загрузить состояние: файл состояния от более новой версии PPSSPP!
|
||||
norewind = Нет доступных состояний для возврата.
|
||||
Playing = В игре
|
||||
PressESC = Нажмите ESC, чтобы открыть меню паузы.
|
||||
|
||||
@@ -1289,7 +1289,7 @@ Loaded. Game may refuse to save over different savedata. = Inläst. Spelet kan v
|
||||
Loaded. Game may refuse to save over newer savedata. = Inläst. Spelet kan vägra att skriva över nyare sparade data.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Inläst. Spara i spelet, starta om och läs in igen för att minska risken för fel.
|
||||
LoadStateDoesntExist = Tillståndet kunde inte läsas in: det sparade tillståndet finns inte!
|
||||
LoadStateWrongVersion = Tillståndet kunde inte läsas in: det är från en annan PPSSPP-version!
|
||||
LoadStateWrongVersion = Tillståndet kunde inte läsas in: det är från en nyare PPSSPP-version!
|
||||
norewind = Det finns inga sparade tillstånd att spola tillbaka till.
|
||||
Playing = Spelar
|
||||
PressESC = Tryck på Esc för att öppna pausmenyn.
|
||||
|
||||
@@ -1285,7 +1285,7 @@ Loaded. Game may refuse to save over different savedata. = Nakarga. Maaaring tum
|
||||
Loaded. Game may refuse to save over newer savedata. = Nakarga. Maaaring tumangging mag-save ang laro sa mas bagong savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Nakarga. Mag-save sa laro, mag-restart, at mag-load para maiwasan ang mga bugs.
|
||||
LoadStateDoesntExist = May Problema sa load state: Walang Savestate!
|
||||
LoadStateWrongVersion = May Problema sa load state: Ang savestate na eto ay para sa lumang bersiyon ng PPSSPP!
|
||||
LoadStateWrongVersion = May Problema sa load state: Ang savestate na eto ay mula sa mas bagong bersiyon ng PPSSPP!
|
||||
norewind = Walang available na rewind save states.
|
||||
Playing = Naglalaro
|
||||
PressESC = Pindutin ang ESC para mabuksan ang pause menu.
|
||||
|
||||
@@ -1288,7 +1288,7 @@ Loaded. Game may refuse to save over different savedata. = Yüklendi. Oyun belki
|
||||
Loaded. Game may refuse to save over newer savedata. = Yüklendi. Oyun belki yeni verilerin üzerine kaydetmeyi reddedebilir.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Yüklendi. Daha az bug için oyunda kaydedin, yeniden başlatın ve yükleyin.
|
||||
LoadStateDoesntExist = Durum yüklenemedi: Durum kaydı yerinde yok!
|
||||
LoadStateWrongVersion = Durum yüklenemedi: Durum kaydı eski bir PPSSPP versiyonu için uygun!
|
||||
LoadStateWrongVersion = Durum yüklenemedi: Durum kaydı daha yeni bir PPSSPP versiyonundan!
|
||||
norewind = Geriye sarma durumu bulunamadı.
|
||||
Playing = Oynatılıyor...
|
||||
PressESC = PPSSPP menüsünü açmak için ESC'ye basın.
|
||||
|
||||
@@ -1286,7 +1286,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = Завантажено. Збережіться в грі, перезапустіть гру і завантажте для виправлення помилок.
|
||||
LoadStateDoesntExist = Не вдається завантажити стан: файла не існує!
|
||||
LoadStateWrongVersion = Не вдається завантажити стан: файл стану від старої версії PPSSPP!
|
||||
LoadStateWrongVersion = Не вдається завантажити стан: файл стану від новішої версії PPSSPP!
|
||||
norewind = Немає доступних збережень для повернення.
|
||||
Playing = У грі
|
||||
PressESC = Натисніть ESC для переходу в меню
|
||||
|
||||
@@ -1286,7 +1286,7 @@ Loaded. Game may refuse to save over different savedata. = Loaded. Game may refu
|
||||
Loaded. Game may refuse to save over newer savedata. = Loaded. Game may refuse to save over newer savedata.
|
||||
Loaded. Save in game, restart, and load for less bugs. = loaded. lưu trong game, khởi động lại và tải để ít lỗi hơn.
|
||||
LoadStateDoesntExist = Không load được: không có file save!
|
||||
LoadStateWrongVersion = Không load được: file save là của một phiên bản cũ của PPSSPP!
|
||||
LoadStateWrongVersion = Không load được: file save là của một phiên bản mới hơn của PPSSPP!
|
||||
norewind = Không tua lại được, trạng thái không có sẵn..
|
||||
Playing = Playing
|
||||
PressESC = Nhấn ESC để mở menu.
|
||||
|
||||
@@ -1288,7 +1288,7 @@ Loaded. Game may refuse to save over different savedata. = 已读取,游戏可
|
||||
Loaded. Game may refuse to save over newer savedata. = 已读取,游戏可能无法保存在进度提前的存档。
|
||||
Loaded. Save in game, restart, and load for less bugs. = 已读取,请在游戏中存档并重启, 再次读档以减少bug。
|
||||
LoadStateDoesntExist = 即时存档读取失败:存档不存在
|
||||
LoadStateWrongVersion = 即时存档读取失败:存档是旧版本PPSSPP的
|
||||
LoadStateWrongVersion = 即时存档读取失败:存档来自较新版本的PPSSPP
|
||||
norewind = 没有可用的倒带自动即时存档。
|
||||
Playing = 游戏中
|
||||
PressESC = 按下ESC键打开暂停菜单。
|
||||
|
||||
@@ -1287,7 +1287,7 @@ Loaded. Save in game, restart, and load for less bugs. = 已載入,在遊戲
|
||||
Loaded. Game may refuse to save over different savedata. = 已載入,遊戲可能會拒絕覆寫不同的存檔
|
||||
Loaded. Game may refuse to save over newer savedata. = 已載入,遊戲可能會拒絕覆寫較新的存檔
|
||||
LoadStateDoesntExist = 無法載入存檔:存檔不存在!
|
||||
LoadStateWrongVersion = 無法載入存檔:存檔僅適用於舊版 PPSSPP!
|
||||
LoadStateWrongVersion = 無法載入存檔:存檔來自較新版本的 PPSSPP!
|
||||
norewind = 沒有可用的倒轉存檔
|
||||
Playing = 正在遊玩
|
||||
PressESC = 按下 ESC 以開啟暫停選單
|
||||
|
||||
@@ -271,7 +271,7 @@ file - this is just an index.
|
||||
|
||||
| Category | Events | File |
|
||||
|---|---|---|
|
||||
| Game/version | `game.reset`, `game.status`, `game.state.load` (queue loading a `.ppst` file, applied at the end of the next frame the CPU runs; with `wait: true` the response comes once it's done, or as an error if it failed), `game.state.save` (the same for saving one), `game.speed.get/set` (emulation speed - unlimited fast-forward, or a percentage of 60 FPS; see below), `version` | `GameSubscriber.cpp` |
|
||||
| Game/version | `game.reset`, `game.status`, `game.state.load` (queue loading a `.ppst` file, applied at the end of the next frame the CPU runs; with `wait: true` the response comes once it's done, or as an error if it failed), `game.state.save` (the same for saving one), `game.state.rewind` (the same for the newest rewind snapshot, which it keeps, so only a rewind within 0.8 seconds of the last steps further back; needs `RewindSnapshotInterval` set, and the `wait` response's `metadata` says how far back that is in emulated time), `game.state.verify` (the same for checking that saving the current state and reading it back agree), `game.speed.get/set` (emulation speed - unlimited fast-forward, or a percentage of 60 FPS; see below), `version` | `GameSubscriber.cpp` |
|
||||
| CPU core | `cpu.stepping`, `cpu.resume`, `cpu.status` (reports `ticks` plus `us`, emulated microseconds, and `clockHz` - use `us` to line up with wall-clock timings, since games change the clock frequency and the ticks-per-second ratio isn't fixed), `cpu.getAllRegs`, `cpu.getReg`, `cpu.setReg`, `cpu.evaluate` | `CPUCoreSubscriber.cpp` |
|
||||
| Stepping | `cpu.stepInto`, `cpu.stepOver`, `cpu.stepOut`, `cpu.runUntil`, `cpu.runUntilTime` (run until a point in emulated time - `us` absolute or `relativeUs` from now - and break there; this is how to get a scripted repro reproducibly "N seconds into the game" instead of polling `cpu.status` in a loop), `cpu.nextHLE` | `SteppingSubscriber.cpp` |
|
||||
| Breakpoints | `cpu.breakpoint.add/update/remove/list`, `memory.breakpoint.add/update/remove/list`, `cpu.regBreakpoint.add/update/remove/list` (break when a register is written to, by any instruction anywhere - currently GPRs only; interpreter-only, no effect under a JIT backend) | `BreakpointSubscriber.cpp` |
|
||||
|
||||
@@ -1390,10 +1390,14 @@ int main(int argc, const char* argv[]) {
|
||||
if (stateToLoad) {
|
||||
g_stateLoadPending = true;
|
||||
// Queued now, actually applied by SaveState::Process() once the game is up and running.
|
||||
SaveState::Load(Path(stateToLoad), -1, [](SaveState::Status status, std::string_view message, std::string_view) {
|
||||
SaveState::Load(Path(stateToLoad), -1, [](SaveState::Status status, std::string_view message, std::string_view metadata) {
|
||||
// The message already reads as a full sentence, e.g. "Failed to load state: <reason>".
|
||||
fprintf(stderr, "%.*s\n", (int)message.size(), message.data());
|
||||
fprintf(stderr, "%.*s\n", STR_VIEW(message));
|
||||
if (status == SaveState::Status::FAILURE) {
|
||||
// Which version saved the state.
|
||||
if (!metadata.empty()) {
|
||||
fprintf(stderr, "%.*s\n", STR_VIEW(metadata));
|
||||
}
|
||||
g_stateLoadFailed = true;
|
||||
}
|
||||
g_stateLoadPending = false;
|
||||
|
||||
Reference in new issue
Block a user