Headless: actually apply --state, and report the result

SaveState::Load only queues the operation - SaveState::Process() applies it, and
headless never called that anywhere. So --state silently did nothing: the state
was queued before boot and sat in the queue for the rest of the run. Call it at
the top of the run loop, the same place in the cycle EmuScreen::render does.

Also pass a callback so the outcome is visible, and make a failed load set a
non-zero exit code. Without that, headless reported success for a state it never
loaded - which is why it didn't catch the savestate regression this branch fixes.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01DCPmm7FoQUoqrbMdhfqhQ2
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-08-30 23:54:29 +02:00
1 parent 729653bf42
commit 8768c30b1d
1 file changed
+19 -1
+19 -1
View File
@@ -82,6 +82,8 @@ static bool g_screenshotFailed = false;
static std::string g_debugOutputBuffer;
static bool g_writeFailureScreenshot = true;
static bool g_writeDebugOutput = true;
// Set from the savestate callback on the emu thread, read after it has been joined.
static bool g_stateLoadFailed = false;
#if PPSSPP_PLATFORM(ANDROID)
JNIEnv *getEnv() {
@@ -339,6 +341,10 @@ static bool RunAutoTest(GraphicsContext *graphicsContext, CoreParameter &corePar
double deadline = time_now_d() + opt.timeout;
coreState = coreParameter.startBreak ? CORE_STEPPING_CPU : CORE_RUNNING_CPU;
while (coreState == CORE_RUNNING_CPU || coreState == CORE_STEPPING_CPU) {
// Savestate loads/saves are queued and applied here, same as EmuScreen::render does in the
// app. Without this, --state silently did nothing at all.
SaveState::Process();
int blockTicks = (int)usToCycles(1000000 / 10);
PSP_RunLoopFor(blockTicks);
@@ -873,7 +879,14 @@ int main(int argc, const char* argv[]) {
}
if (stateToLoad) {
SaveState::Load(Path(stateToLoad), -1);
// 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) {
// The message already reads as a full sentence, e.g. "Failed to load state: <reason>".
fprintf(stderr, "%.*s\n", (int)message.size(), message.data());
if (status == SaveState::Status::FAILURE) {
g_stateLoadFailed = true;
}
});
}
std::string errorMessage;
@@ -893,6 +906,11 @@ int main(int argc, const char* argv[]) {
retval = 1;
}
if (g_stateLoadFailed && retval == 0) {
// Whatever the run itself reported, the state we were told to load never got applied.
retval = 1;
}
graphicsContext->ShutdownAPI();
delete graphicsContext;