fixed save games

fixed save games
This commit is contained in:
Jessica_Natalia
2026-08-18 03:52:21 -03:00
parent 568c95072d
commit 82fbb0fa58
13 changed files with 1775 additions and 36 deletions
+42
View File
@@ -0,0 +1,42 @@
@echo off
setlocal EnableExtensions
for %%I in ("%~dp0..\..") do set "REPO=%%~fI"
set "BIN=%REPO%\out\vcs-release-ninja\bin\Release"
set "CHECKPOINT=%BIN%\VCS_SAVE_REPRO_CHECKPOINT.bin"
set "TRACE=%BIN%\VCS_SAVE_REPRO_TRACE.txt"
set "LOG=%BIN%\VCSNative.log"
set "MISSING=%BIN%\VCS_SAVE_REPRO_TRACE_MISSING.txt"
set "BUNDLE=%BIN%\VCS_SAVE_REPRO_BUNDLE.zip"
if not exist "%CHECKPOINT%" (
echo ERROR: missing %CHECKPOINT%
echo The expensive post-mission checkpoint is not present.
pause
exit /b 2
)
if exist "%MISSING%" del /q "%MISSING%" >nul 2>&1
set "FILES='%CHECKPOINT%'"
if exist "%TRACE%" (
set "FILES=%FILES%,'%TRACE%'"
) else (
>"%MISSING%" echo VCS SAVE REPRO TRACE WAS NOT WRITTEN.
>>"%MISSING%" echo The checkpoint is valid and is included in this bundle.
>>"%MISSING%" echo V8.2.6B adds a post-F8/GetAsyncKeyState F10 fallback; restore the checkpoint and retry F10 if a full trace is still needed.
set "FILES=%FILES%,'%MISSING%'"
echo WARNING: %TRACE% is missing.
echo The bundle will still be created with the checkpoint and runtime log.
)
if exist "%LOG%" set "FILES=%FILES%,'%LOG%'"
powershell -NoProfile -ExecutionPolicy Bypass -Command "Compress-Archive -LiteralPath @(%FILES%) -DestinationPath '%BUNDLE%' -Force"
if errorlevel 1 (
echo ERROR: could not create bundle.
pause
exit /b 4
)
echo.
echo SAVE REPRO bundle created:
echo %BUNDLE%
echo.
if not exist "%TRACE%" echo NOTE: trace missing, but checkpoint was preserved in the ZIP.
echo Send this ZIP back in the chat.
pause
exit /b 0
+30
View File
@@ -0,0 +1,30 @@
@echo off
setlocal EnableExtensions
for %%I in ("%~dp0..\..") do set "REPO=%%~fI"
set "BIN=%REPO%\out\vcs-release-ninja\bin\Release"
set "GAME=%BIN%\PSP_DATA"
set "CHECKPOINT=%BIN%\VCS_SAVE_REPRO_CHECKPOINT.bin"
set "TRACE=%BIN%\VCS_SAVE_REPRO_TRACE.txt"
if not exist "%CHECKPOINT%" (
echo ERROR: checkpoint not found:
echo %CHECKPOINT%
echo.
echo Finish the problematic mission once with V8.2.6 and press F8 after gameplay returns.
pause
exit /b 2
)
if not exist "%GAME%\PSP_GAME\SYSDIR\EBOOT_DECRYPTED.ELF" (
echo ERROR: PSP_DATA not found beside the Release executable:
echo %GAME%
pause
exit /b 3
)
if exist "%TRACE%" del /q "%TRACE%" >nul 2>&1
set "PSPRECOMP_SAVE_REPRO_AUTO_RESTORE=1"
echo ================================================================
echo VCS SAVE REPRO - AUTO RESTORE
echo Checkpoint:
echo %CHECKPOINT%
echo ================================================================
call "%~dp0scripts\play.bat" "%GAME%"
exit /b %ERRORLEVEL%
+18 -4
View File
@@ -133,6 +133,7 @@ struct WindowState {
std::atomic<bool> ready{false};
std::atomic<bool> focused{false};
std::atomic<bool> close_requested{false};
std::atomic<std::uint32_t> save_repro_commands{0u};
// Raw mouse motion accumulated by the window thread and drained by the
// guest's controller poll. Raw input rather than cursor position: the
// cursor stops at the screen edge, and a camera that stops turning when
@@ -448,10 +449,17 @@ LRESULT CALLBACK window_procedure(HWND window, UINT message, WPARAM wparam, LPAR
state.focused.store(false, std::memory_order_relaxed);
return 0;
case WM_KEYDOWN:
// Keyboard state is sampled with GetAsyncKeyState. Do not infer pause
// menu ownership from Escape here: the guest may consume the press for
// an intro/transition. Cursor mode is driven only by VCS' real native
// frontend-active byte through display_window_set_guest_frontend_active.
// Gameplay keys keep using the existing sampled-input path. Diagnostic
// F8/F10 edges are queued here on the UI thread, with auto-repeat
// ignored, so no GetAsyncKeyState call is added to guest timing.
if ((static_cast<std::uint32_t>(lparam) & (1u << 30u)) == 0u) {
if (wparam == VK_F8)
state.save_repro_commands.fetch_or(0x1u, std::memory_order_release);
else if (wparam == VK_F10)
state.save_repro_commands.fetch_or(0x2u, std::memory_order_release);
}
// Do not infer pause menu ownership from Escape here: the guest may
// consume the press for an intro/transition.
return 0;
case WM_INPUT: {
// Raw mouse deltas. Sized from the message rather than assumed: the
@@ -1310,6 +1318,11 @@ bool display_window_close_requested() {
return window_state().close_requested.load(std::memory_order_relaxed);
}
std::uint32_t display_window_take_save_repro_commands() noexcept {
if (!display_window_enabled()) return 0u;
return window_state().save_repro_commands.exchange(0u, std::memory_order_acq_rel);
}
// Keeps the last rendered frame on screen after the guest stops so the run can
// be inspected. PSPRECOMP_WINDOW_HOLD=0 closes immediately instead.
void display_window_shutdown() {
@@ -1357,6 +1370,7 @@ void display_window_set_guest_frontend_active(bool) noexcept {}
bool display_window_native_boot_user_committed() noexcept { return false; }
void display_window_set_system_utility_mode(bool) noexcept {}
bool display_window_close_requested() { return false; }
std::uint32_t display_window_take_save_repro_commands() noexcept { return 0u; }
void display_window_shutdown() {}
} // namespace vcs
+4
View File
@@ -99,6 +99,10 @@ void display_window_set_system_utility_mode(bool active) noexcept;
// True once the user closed the window or pressed Escape.
[[nodiscard]] bool display_window_close_requested();
// Diagnostic-only F8/F10 edges captured by the UI thread. The guest runtime
// consumes and clears these bits at vblank.
[[nodiscard]] std::uint32_t display_window_take_save_repro_commands() noexcept;
void display_window_shutdown();
// Native window handle (HWND on Windows, nullptr elsewhere or before the window
+7 -1
View File
@@ -308,13 +308,19 @@ int main(int argc, char **argv) {
runtime.cpu().set_gpr(31, 0u);
runtime.cpu().set_gpr(4, 0u);
runtime.cpu().set_gpr(5, 0u);
std::string save_repro_restore_error;
const bool save_repro_restored =
vcs::restore_save_repro_checkpoint_if_requested(runtime, save_repro_restore_error);
if (!save_repro_restore_error.empty())
throw psprecomp::Error("SAVE REPRO restore failed: " + save_repro_restore_error);
const std::uint32_t runtime_entry = save_repro_restored ? runtime.cpu().pc : elf.runtime_entry();
const std::uint64_t max_dispatches = configured_max_dispatches();
std::cout << "Dispatch cap: " << max_dispatches << "\n";
vcs::display_window_start();
vcs::install_display_heartbeat();
vcs::install_starvation_preemption();
runtime.run(elf.runtime_entry(), max_dispatches);
runtime.run(runtime_entry, max_dispatches);
const bool shutdown_diag = std::getenv("PSPRECOMP_SHUTDOWN_DIAG") != nullptr;
if (shutdown_diag) std::cerr << "[shutdown] runtime-run-returned\n";
std::cout << "Runtime stopped: " << runtime.stop_reason() << "\n";
File diff suppressed because it is too large Load Diff
+4
View File
@@ -6,6 +6,10 @@
namespace vcs {
void install_profile(psprecomp::Runtime &runtime, std::uint32_t user_arena_start);
// Restores the persistent V8.2.6 post-mission save-repro checkpoint when
// PSPRECOMP_SAVE_REPRO_AUTO_RESTORE=1. Returns true only when a checkpoint
// was requested and restored successfully; `error` is populated on failure.
bool restore_save_repro_checkpoint_if_requested(psprecomp::Runtime &runtime, std::string &error);
// Feeds the display window a dispatch/vblank heartbeat so long synchronous
// guest phases still show progress instead of looking frozen.
+3 -3
View File
@@ -65,7 +65,7 @@ void runtime_log_initialize(const VcsConfiguration &configuration) {
return;
}
s.file << "VCSNative runtime log\n";
s.file << "stage=correctness-v8.2.5-news-atrac-stream-fix-2026-08-18\n";
s.file << "stage=correctness-v8.2.7-save-thread-lifecycle-fix-2026-08-18\n";
s.file << "config=" << configuration.source_path.string() << '\n';
s.file << "started=" << timestamp_now() << '\n';
s.file << "perf_telemetry=" << (configuration.diagnostics.perf_telemetry ? 1 : 0)
@@ -85,9 +85,9 @@ void runtime_log_initialize(const VcsConfiguration &configuration) {
<< " unwind_fix=1 reentry_guard=1 dataflow=1 vfpu_block32=133 mem_runs=35 mem_words=287"
<< " append32=51 advance32=89 simd_mat4=4 simd_matvec=19"
<< " gpr_shadow_clusters=4 gpr_shadow_regs=24 gpr_shadow_occurrences=3461 geometry_shadow=0"
<< " perf_layer=8 cpu_lean_revision=2 correctness_revision=825 recovery_from_821=1"
<< " perf_layer=8 cpu_lean_revision=2 correctness_revision=827 recovery_from_821=1"
<< " save_transaction=1 save_lifecycle_v82=1 sas_endflag_latched=1 sas_loop_history_restore=1"
<< " atrac_virtual_source=1 atrac_stall_diag=1 atrac_stream_resident_status=1 atrac_nonloop_resident=-2 atrac_loop_resident=-3 output2_success_zero=1 output2_master_watermark=1 output2_late_catchup=0 runtime_chain_telemetry_default=0 arch_fastmem=1 aot_direct_fastmem_default=1"
<< " atrac_virtual_source=1 atrac_stall_diag=1 atrac_stream_resident_status=1 atrac_nonloop_resident=-2 atrac_loop_resident=-3 output2_success_zero=1 output2_master_watermark=1 output2_late_catchup=0 save_repro_checkpoint=1 save_repro_trace=1 save_repro_hotkey_f8=1 save_repro_trace_hotkey_f10=1 save_repro_auto_restore=1 save_repro_dispatch_sample_stride=64 save_repro_passive_until_f8=1 save_repro_hle_hotpath=0 save_repro_ui_hotkeys=1 save_repro_f10_async_fallback=1 save_repro_collector_partial_bundle=1 save_exitdelete_semantics=1 save_repro_legacy_exitdelete_repair=1 save_partition_reuse=1 news_atrac_v825_guard=1 runtime_chain_telemetry_default=0 arch_fastmem=1 aot_direct_fastmem_default=1"
<< " tier2_direct_fastmem=1 tier2_direct_mem_sites=1969 tier2_deep_telemetry_default=0 geometry_fusion_rollback=1"
<< " entity_leaf_inline=1 entity_leaf_scheduler_accounting=1 entity_leaf_resume_pc_fix=1"
<< " ge_async_default=0 parallel_vertex_decode_default=0"
+55 -5
View File
@@ -109,6 +109,10 @@ set "CORRECTNESS_V822_STAMP=%BUILD%\.vcs_correctness_v822_recovery_20260817"
set "CORRECTNESS_V823_NEWS_STAMP=%BUILD%\.vcs_correctness_v823_news_audio_20260817"
set "CORRECTNESS_V824_NEWS_PACING_STAMP=%BUILD%\.vcs_correctness_v824_news_pacing_20260818"
set "CORRECTNESS_V825_NEWS_ATRAC_STAMP=%BUILD%\.vcs_correctness_v825_news_atrac_stream_20260818"
set "CORRECTNESS_V826_SAVE_REPRO_STAMP=%BUILD%\.vcs_correctness_v826_save_repro_20260818"
set "CORRECTNESS_V826A_SAVE_REPRO_STAMP=%BUILD%\.vcs_correctness_v826a_save_repro_passive_20260818"
set "CORRECTNESS_V826B_SAVE_REPRO_STAMP=%BUILD%\.vcs_correctness_v826b_save_repro_trace_20260818"
set "CORRECTNESS_V827_SAVE_THREAD_STAMP=%BUILD%\.vcs_correctness_v827_save_thread_lifecycle_20260818"
if not defined PSPRECOMP_TIER2_DEEP_TELEMETRY set "PSPRECOMP_TIER2_DEEP_TELEMETRY=OFF"
if not defined PSPRECOMP_RUNTIME_CHAIN_TELEMETRY set "PSPRECOMP_RUNTIME_CHAIN_TELEMETRY=OFF"
if /I "%PSPRECOMP_TIER2_DEEP_TELEMETRY%"=="1" set "PSPRECOMP_TIER2_DEEP_TELEMETRY=ON"
@@ -127,7 +131,7 @@ echo Ninja: %NINJA_EXE%
echo Ninja workers: %JOBS%
echo cl.exe /MP: OFF ^(Ninja owns compile parallelism^)
echo Generated AOT: O3, cold /Ob0, measured hot /Ob3; V8.2 runtime-chain lean + V8.1 Geometry/direct-fastmem baseline
echo Correctness: V8.2.5 NEWS ATRAC stream state - resident sentinels + V8.2.3 Output2 base
echo Correctness: V8.2.6B SAVE REPRO TRACE HOTFIX over protected V8.2.5 NEWS audio
echo Host/core LTCG: ON
echo AVX2/fast paths: ON
echo Tier2 deep diag: %PSPRECOMP_TIER2_DEEP_TELEMETRY%
@@ -150,10 +154,10 @@ if not defined PYTHON3_CMD goto :NO_PYTHON3
echo Python 3: %PYTHON3_CMD%
%PYTHON3_CMD% "%PROFILE%\tools\build_tier2_superblocks.py" "%PROFILE%"
if errorlevel 1 goto :FAIL
rem V8.2.5 checker includes the protected V8.2 CPU/Geometry invariants and the
rem V8.2.3 Output2 ABI/watermark invariants. Older revision checkers pin their
rem exact stage strings and therefore must not gate a newer correctness stage.
%PYTHON3_CMD% "%PROFILE%\tests\check_v825_news_atrac_stream.py"
rem V8.2.7 checker includes the protected V8.2 CPU/Geometry, V8.2.5 NEWS,
rem V8.2.6 passive checkpoint contract and the corrected PSP ExitDelete lifecycle.
rem Older revision checkers pin exact stage strings and must not gate this stage.
%PYTHON3_CMD% "%PROFILE%\tests\check_v827_save_thread_lifecycle.py"
if errorlevel 1 goto :FAIL
if exist "%BUILD%" if not exist "%SUPERBLOCK_STAMP%" (
@@ -337,6 +341,48 @@ if exist "%BUILD%" if not exist "%CORRECTNESS_V825_NEWS_ATRAC_STAMP%" (
del /s /q "%BUILD%\*vcs_runtime_log*.obj" >nul 2>&1
)
if exist "%BUILD%" if not exist "%CORRECTNESS_V826_SAVE_REPRO_STAMP%" (
echo.
echo [0c-v826save/7] V8.2.6 SAVE REPRO CAPTURE - rebuilding only checkpoint/trace host objects...
rem No runtime.hpp, generated AOT, Tier2, Geometry or DX12 changes. runtime.cpp
rem only exposes current HLE identity to the diagnostic trace.
del /s /q "%BUILD%\*vcs_profile*.obj" >nul 2>&1
del /s /q "%BUILD%\*vcs_runtime_log*.obj" >nul 2>&1
del /s /q "%BUILD%\*runtime.cpp.obj" >nul 2>&1
del /s /q "%BUILD%\*main.cpp.obj" >nul 2>&1
)
if exist "%BUILD%" if not exist "%CORRECTNESS_V826A_SAVE_REPRO_STAMP%" (
echo.
echo [0c-v826a/7] V8.2.6A SAVE REPRO PASSIVE RECOVERY - removing passive HLE instrumentation...
rem Revert core runtime.cpp to the exact V8.2.5 hot path. Diagnostic F8/F10
rem edges are queued by the Win32 window thread and consumed once per vblank.
rem Rebuild only files touched by this recovery; keep generated AOT/Tier2/DX12.
del /s /q "%BUILD%\*vcs_profile*.obj" >nul 2>&1
del /s /q "%BUILD%\*vcs_runtime_log*.obj" >nul 2>&1
del /s /q "%BUILD%\*runtime.cpp.obj" >nul 2>&1
del /s /q "%BUILD%\*display_window*.obj" >nul 2>&1
)
if exist "%BUILD%" if not exist "%CORRECTNESS_V826B_SAVE_REPRO_STAMP%" (
echo.
echo [0c-v826b/7] V8.2.6B SAVE REPRO TRACE HOTFIX - rebuilding trace host objects only...
rem Before F8/restore the runtime path remains V8.2.5/V8.2.6A. The F10
rem async-key fallback is active only after trace capture has been armed.
del /s /q "%BUILD%\*vcs_profile*.obj" >nul 2>&1
del /s /q "%BUILD%\*vcs_runtime_log*.obj" >nul 2>&1
)
if exist "%BUILD%" if not exist "%CORRECTNESS_V827_SAVE_THREAD_STAMP%" (
echo.
echo [0c-v827save/7] V8.2.7 SAVE THREAD LIFECYCLE - rebuilding profile/runtime-log objects only...
rem Fix sceKernelExitDeleteThread stack/object reclamation and migrate the
rem already captured V8.2.6 checkpoint. Generated AOT/Tier2/DX12 are unchanged.
del /s /q "%BUILD%\*vcs_profile*.obj" >nul 2>&1
del /s /q "%BUILD%\*vcs_runtime_log*.obj" >nul 2>&1
)
if exist "%BUILD%" if not exist "%BOOTFIX_STAMP%" (
echo.
echo [0c/7] BOOTFIX revision changed - invalidating stale .obj/.pch once...
@@ -390,6 +436,10 @@ if errorlevel 1 goto :FAIL
>"%CORRECTNESS_V823_NEWS_STAMP%" echo VCS V8.2.3 NEWS AUDIO FIX 2026-08-17
>"%CORRECTNESS_V824_NEWS_PACING_STAMP%" echo VCS V8.2.4 NEWS PACING FIX 2026-08-18
>"%CORRECTNESS_V825_NEWS_ATRAC_STAMP%" echo VCS V8.2.5 NEWS ATRAC STREAM FIX 2026-08-18
>"%CORRECTNESS_V826_SAVE_REPRO_STAMP%" echo VCS V8.2.6 SAVE REPRO CAPTURE 2026-08-18
>"%CORRECTNESS_V826A_SAVE_REPRO_STAMP%" echo VCS V8.2.6A SAVE REPRO PASSIVE RECOVERY 2026-08-18
>"%CORRECTNESS_V826B_SAVE_REPRO_STAMP%" echo VCS V8.2.6B SAVE REPRO TRACE HOTFIX 2026-08-18
>"%CORRECTNESS_V827_SAVE_THREAD_STAMP%" echo VCS V8.2.7 SAVE THREAD LIFECYCLE FIX 2026-08-18
echo.
echo [2b/7] Building tests and DX12 probes...
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
from pathlib import Path
root = Path(__file__).resolve().parents[3]
profile = root / 'profiles' / 'vcs'
host = profile / 'host'
source = (host / 'vcs_profile.cpp').read_text(encoding='utf-8')
runtime = (root / 'src' / 'runtime.cpp').read_text(encoding='utf-8')
main = (host / 'main.cpp').read_text(encoding='utf-8')
header = (host / 'vcs_profile.hpp').read_text(encoding='utf-8')
log = (host / 'vcs_runtime_log.cpp').read_text(encoding='utf-8')
guest = (profile / 'generated' / 'generated_unit_0170.cpp').read_text(encoding='utf-8')
def need(cond, msg):
if not cond:
print('FAIL:', msg)
raise SystemExit(1)
print('PASS:', msg)
# Runtime identity and protected V8.2/V8.2.5 guards.
need('stage=correctness-v8.2.6-save-repro-capture-2026-08-18' in log, 'V8.2.6 runtime stage')
need('correctness_revision=826' in log, 'V8.2.6 correctness revision')
need('hot_blocks=1060' in log and 'static_fused_calls=36' in log and 'geometry_fusion_rollback=1' in log,
'V8.2 CPU/Tier2 shape preserved')
need('ge_async_quarantined=1' in log and 'parallel_vertex_decode_quarantined=1' in log,
'unstable GE paths remain quarantined')
need('atrac_stream_resident_status=1' in log and 'atrac_nonloop_resident=-2' in log and
'atrac_loop_resident=-3' in log and 'news_atrac_v825_guard=1' in log,
'working V8.2.5 NEWS semantics are a regression guard')
need('output2_late_catchup=0' in log, 'rejected Output2 catch-up remains disabled')
need('kAtracRemainNonLoopOnMemory = 0xFFFFFFFEu' in source and
'kAtracRemainLoopOnMemory = 0xFFFFFFFDu' in source and
source.count('atrac_remain_frame_status(*state)') >= 3,
'V8.2.5 ATRAC resident implementation preserved')
need('static_cast<std::uint32_t>(-1)' in guest and 'static_cast<std::uint32_t>(-2)' in guest,
'VCS guest resident-state branches remain covered')
# Checkpoint/trace surface.
for token, label in [
('kSaveReproVersion = 826u', 'checkpoint format version'),
('VCS_SAVE_REPRO_CHECKPOINT.bin', 'persistent checkpoint filename'),
('VCS_SAVE_REPRO_TRACE.txt', 'circular trace filename'),
('VK_F8', 'F8 checkpoint hotkey'),
('VK_F10', 'F10 trace hotkey'),
('PSPRECOMP_SAVE_REPRO_AUTO_RESTORE', 'auto-restore switch'),
('kSaveReproTraceCapacity = 131072u', 'deep trace ring'),
('kSaveReproDispatchSampleStride = 64u', 'sampled dispatch trace cadence'),
('save_repro_fnv1a', 'checkpoint integrity checksum'),
('file_open_flags', 'live file descriptor reopen flags'),
('recent_atrac_reads', 'ATRAC producer association state'),
('save_repro_write_kernel_state', 'kernel/thread state serialization'),
('save_repro_write_media_audio_state', 'audio/media state serialization'),
('save_repro_write_ge_state', 'GE/callback state serialization'),
]:
need(token in source, label)
need('save_repro_checkpoint=1' in log and 'save_repro_trace=1' in log and
'save_repro_auto_restore=1' in log and 'save_repro_dispatch_sample_stride=64' in log,
'checkpoint runtime capabilities advertised')
need('save-repro full checkpoint did not restore RAM/time/controller/CPU exactly' in source,
'full RAM/EDRAM persistent checkpoint roundtrip regression test present')
need('save_repro_dump_trace(rt, "window-close")' in source,
'trace is preserved automatically when the repro window closes')
# Restore must enter at the captured guest continuation, not the ELF entry.
need('restore_save_repro_checkpoint_if_requested' in header and
'restore_save_repro_checkpoint_if_requested(runtime, save_repro_restore_error)' in main,
'main invokes persistent checkpoint restore')
need('save_repro_restored ? runtime.cpu().pc : elf.runtime_entry()' in main,
'restored CPU PC is used as Runtime::run entry')
# HLE trace gets exact import identity without changing generated-call ABI.
need('runtime_current_import_library() noexcept' in runtime and 'runtime_current_import_nid() noexcept' in runtime,
'runtime exposes current import identity to diagnostic trace')
need('g_runtime_current_import_library = previous_import_library' in runtime and
'g_runtime_current_import_nid = previous_import_nid' in runtime,
'nested import metadata is restored')
need('runtime_current_import_library() noexcept;' in source and 'runtime_current_import_nid() noexcept;' in source,
'profile uses local diagnostic declarations without runtime.hpp churn')
# Diagnostic only: do not alter protected time/scheduler configuration.
capture_block = source[source.index('// V8.2.6 SAVE REPRO CAPTURE'):source.index('constexpr std::uint32_t kPspUtilityStart')]
need('PSPRECOMP_TIME_TICK_DISPATCHES' not in capture_block and 'g_runtime_starvation_interval_fast' not in capture_block,
'checkpoint block does not override scheduler cadence')
need('save_repro_capture_requested = true' in source and
'if (save_repro_capture_requested)' in source and
'save_repro_write_checkpoint(rt, ctx, error)' in source,
'F8 arms at vblank and checkpoint captures exact post-dispatch CPU context')
print('V8.2.6 SAVE REPRO CAPTURE audit PASS')
@@ -0,0 +1,79 @@
#!/usr/bin/env python3
from pathlib import Path
root = Path(__file__).resolve().parents[3]
profile = root / 'profiles' / 'vcs'
host = profile / 'host'
source = (host / 'vcs_profile.cpp').read_text(encoding='utf-8')
runtime = (root / 'src' / 'runtime.cpp').read_text(encoding='utf-8')
display_cpp = (host / 'display_window.cpp').read_text(encoding='utf-8')
display_hpp = (host / 'display_window.hpp').read_text(encoding='utf-8')
main = (host / 'main.cpp').read_text(encoding='utf-8')
header = (host / 'vcs_profile.hpp').read_text(encoding='utf-8')
log = (host / 'vcs_runtime_log.cpp').read_text(encoding='utf-8')
guest = (profile / 'generated' / 'generated_unit_0170.cpp').read_text(encoding='utf-8')
def need(cond, msg):
if not cond:
print('FAIL:', msg)
raise SystemExit(1)
print('PASS:', msg)
need('stage=correctness-v8.2.6a-save-repro-passive-recovery-2026-08-18' in log, 'V8.2.6A runtime stage')
need('correctness_revision=8261' in log, 'V8.2.6A correctness revision')
need('hot_blocks=1060' in log and 'static_fused_calls=36' in log and 'geometry_fusion_rollback=1' in log,
'V8.2 CPU/Tier2 shape preserved')
need('atrac_stream_resident_status=1' in log and 'atrac_nonloop_resident=-2' in log and
'atrac_loop_resident=-3' in log and 'news_atrac_v825_guard=1' in log,
'working V8.2.5 NEWS semantics preserved')
need('output2_late_catchup=0' in log, 'rejected Output2 catch-up remains disabled')
need('save_repro_passive_until_f8=1' in log and 'save_repro_hle_hotpath=0' in log and
'save_repro_ui_hotkeys=1' in log, 'passive-capture recovery advertised')
need('kAtracRemainNonLoopOnMemory = 0xFFFFFFFEu' in source and
'kAtracRemainLoopOnMemory = 0xFFFFFFFDu' in source, 'V8.2.5 ATRAC resident code preserved')
need('static_cast<std::uint32_t>(-1)' in guest and 'static_cast<std::uint32_t>(-2)' in guest,
'VCS guest ATRAC resident branches preserved')
# The regression source: V8.2.6 put library/NID bookkeeping around every import.
# V8.2.6A must have none of that in core runtime or profile hot paths.
for forbidden in ['g_runtime_current_import_library', 'g_runtime_current_import_nid',
'runtime_current_import_library()', 'runtime_current_import_nid()']:
need(forbidden not in runtime, f'core runtime has no passive import identity: {forbidden}')
need('save_repro_trace_hle' not in source, 'profile has no per-HLE SAVE_REPRO tracing')
need('GetAsyncKeyState(' not in source, 'SAVE_REPRO does not poll Win32 keyboard from guest/vblank code')
need('display_window_take_save_repro_commands()' in source, 'guest consumes queued UI-thread F8/F10 commands')
need('save_repro_commands.fetch_or(0x1u' in display_cpp and 'save_repro_commands.fetch_or(0x2u' in display_cpp,
'WndProc queues F8/F10 edges')
need('save_repro_commands.exchange(0u' in display_cpp and
'display_window_take_save_repro_commands() noexcept' in display_hpp,
'queued diagnostic commands are atomically consumed')
need('(1u << 30u)' in display_cpp, 'Win32 key autorepeat is ignored')
# Checkpoint remains useful once explicitly armed.
for token, label in [
('kSaveReproVersion = 826u', 'checkpoint format compatibility'),
('VCS_SAVE_REPRO_CHECKPOINT.bin', 'checkpoint filename'),
('VCS_SAVE_REPRO_TRACE.txt', 'trace filename'),
('PSPRECOMP_SAVE_REPRO_AUTO_RESTORE', 'auto-restore switch'),
('kSaveReproTraceCapacity = 131072u', 'trace ring capacity'),
('kSaveReproDispatchSampleStride = 64u', 'dispatch trace sampling'),
('save_repro_fnv1a', 'checkpoint checksum'),
('save_repro_write_kernel_state', 'kernel state serialization'),
('save_repro_write_media_audio_state', 'media/audio state serialization'),
('save_repro_write_ge_state', 'GE state serialization'),
]: need(token in source, label)
need('save_repro_capture_requested = true' in source and
'if (save_repro_capture_requested)' in source and
'save_repro_write_checkpoint(rt, ctx, error)' in source,
'F8 still captures at exact post-dispatch boundary')
need('restore_save_repro_checkpoint_if_requested' in header and
'restore_save_repro_checkpoint_if_requested(runtime, save_repro_restore_error)' in main and
'save_repro_restored ? runtime.cpu().pc : elf.runtime_entry()' in main,
'persistent checkpoint restore path preserved')
need('save_repro_dump_trace(rt, "window-close")' in source,
'trace auto-dump on close preserved after capture')
capture = source[source.index('// V8.2.6 SAVE REPRO CAPTURE'):source.index('constexpr std::uint32_t kPspUtilityStart')]
need('PSPRECOMP_TIME_TICK_DISPATCHES' not in capture and 'g_runtime_starvation_interval_fast' not in capture,
'capture block does not change scheduler cadence')
print('V8.2.6A SAVE REPRO PASSIVE RECOVERY audit PASS')
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
from pathlib import Path
root = Path(__file__).resolve().parents[3]
profile = root / 'profiles' / 'vcs'
host = profile / 'host'
source = (host / 'vcs_profile.cpp').read_text(encoding='utf-8')
runtime = (root / 'src' / 'runtime.cpp').read_text(encoding='utf-8')
display_cpp = (host / 'display_window.cpp').read_text(encoding='utf-8')
log = (host / 'vcs_runtime_log.cpp').read_text(encoding='utf-8')
collector = (profile / 'COLLECT_SAVE_REPRO.bat').read_text(encoding='utf-8')
restore = (profile / 'RESTORE_SAVE_REPRO.bat').read_text(encoding='utf-8')
guest = (profile / 'generated' / 'generated_unit_0170.cpp').read_text(encoding='utf-8')
def need(cond, msg):
if not cond:
print('FAIL:', msg)
raise SystemExit(1)
print('PASS:', msg)
need('stage=correctness-v8.2.6b-save-repro-trace-hotfix-2026-08-18' in log, 'V8.2.6B runtime stage')
need('correctness_revision=8262' in log, 'V8.2.6B correctness revision')
need('hot_blocks=1060' in log and 'static_fused_calls=36' in log and 'geometry_fusion_rollback=1' in log,
'V8.2 CPU/Tier2 shape preserved')
need('atrac_stream_resident_status=1' in log and 'atrac_nonloop_resident=-2' in log and
'atrac_loop_resident=-3' in log and 'news_atrac_v825_guard=1' in log,
'working V8.2.5 NEWS semantics preserved')
need('output2_late_catchup=0' in log, 'rejected Output2 catch-up remains disabled')
need('save_repro_passive_until_f8=1' in log and 'save_repro_hle_hotpath=0' in log and
'save_repro_f10_async_fallback=1' in log and 'save_repro_collector_partial_bundle=1' in log,
'V8.2.6B trace recovery advertised')
need('kAtracRemainNonLoopOnMemory = 0xFFFFFFFEu' in source and
'kAtracRemainLoopOnMemory = 0xFFFFFFFDu' in source, 'V8.2.5 ATRAC resident code preserved')
need('static_cast<std::uint32_t>(-1)' in guest and 'static_cast<std::uint32_t>(-2)' in guest,
'VCS guest ATRAC resident branches preserved')
for forbidden in ['g_runtime_current_import_library', 'g_runtime_current_import_nid',
'runtime_current_import_library()', 'runtime_current_import_nid()']:
need(forbidden not in runtime, f'core runtime has no passive import identity: {forbidden}')
need('save_repro_trace_hle' not in source, 'profile still has no per-HLE SAVE_REPRO tracing')
need('save_repro_commands.fetch_or(0x1u' in display_cpp and 'save_repro_commands.fetch_or(0x2u' in display_cpp,
'WndProc F8/F10 edge queue preserved')
need('if (save_repro_trace_enabled)' in source and 'GetAsyncKeyState(VK_F10)' in source and
'f10_async_was_down' in source, 'F10 async fallback is gated behind armed trace')
# Ensure GetAsyncKeyState is not used anywhere before the save-repro function block.
prefix = source[:source.index('void save_repro_vblank_hotkeys')]
need('GetAsyncKeyState(VK_F10)' not in prefix, 'no F10 polling exists on pre-F8 gameplay/cutscene path')
need('VCS_SAVE_REPRO_TRACE_MISSING.txt' in collector and
'The bundle will still be created with the checkpoint and runtime log.' in collector and
'exit /b 3' not in collector, 'collector preserves checkpoint even when trace is absent')
need('if exist "%TRACE%" del /q "%TRACE%"' in restore, 'restore deletes stale trace before a new repro')
for token, label in [
('kSaveReproVersion = 826u', 'checkpoint format compatibility'),
('VCS_SAVE_REPRO_CHECKPOINT.bin', 'checkpoint filename'),
('VCS_SAVE_REPRO_TRACE.txt', 'trace filename'),
('PSPRECOMP_SAVE_REPRO_AUTO_RESTORE', 'auto-restore switch'),
('kSaveReproTraceCapacity = 131072u', 'trace ring capacity'),
('kSaveReproDispatchSampleStride = 64u', 'dispatch trace sampling'),
('save_repro_dump_trace(runtime, "manual-F10")', 'manual F10 dump path'),
('save_repro_dump_trace(rt, "window-close")', 'window-close dump fallback'),
]: need(token in source, label)
need('PSPRECOMP_TIME_TICK_DISPATCHES' not in source[source.index('// V8.2.6 SAVE REPRO CAPTURE'):source.index('constexpr std::uint32_t kPspUtilityStart')],
'capture block does not change scheduler cadence')
print('V8.2.6B SAVE REPRO TRACE HOTFIX audit PASS')
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
from pathlib import Path
root = Path(__file__).resolve().parents[3]
profile = root / 'profiles' / 'vcs'
host = profile / 'host'
source = (host / 'vcs_profile.cpp').read_text(encoding='utf-8')
log = (host / 'vcs_runtime_log.cpp').read_text(encoding='utf-8')
runtime = (root / 'src' / 'runtime.cpp').read_text(encoding='utf-8')
guest_registry = (profile / 'generated' / 'generated_registry.cpp').read_text(encoding='utf-8')
sfx = (profile / 'generated' / 'generated_unit_0096.cpp').read_text(encoding='utf-8')
memstick = (profile / 'generated' / 'generated_unit_0172.cpp').read_text(encoding='utf-8')
stupid = (profile / 'generated' / 'generated_unit_0076.cpp').read_text(encoding='utf-8')
def need(cond, msg):
if not cond:
print('FAIL:', msg)
raise SystemExit(1)
print('PASS:', msg)
need('stage=correctness-v8.2.7-save-thread-lifecycle-fix-2026-08-18' in log, 'V8.2.7 runtime stage')
need('correctness_revision=827' in log, 'V8.2.7 correctness revision')
need('save_exitdelete_semantics=1' in log and 'save_repro_legacy_exitdelete_repair=1' in log and 'save_partition_reuse=1' in log,
'ExitDelete/partition fix and migration metadata')
need('atrac_stream_resident_status=1' in log and 'atrac_nonloop_resident=-2' in log and
'atrac_loop_resident=-3' in log and 'news_atrac_v825_guard=1' in log,
'working V8.2.5 NEWS semantics preserved')
need('output2_late_catchup=0' in log, 'rejected Output2 pacing experiment remains disabled')
need('hot_blocks=1060' in log and 'static_fused_calls=36' in log and 'geometry_fusion_rollback=1' in log,
'V8.2 CPU/Tier2 shape preserved')
# Core correctness: ExitThread remains dormant/Completed, ExitDelete actually
# destroys the object and releases its stack.
need('void exit_delete_current_thread' in source, 'dedicated ExitDelete lifecycle helper')
need('release_thread_stack(deleted);' in source and 'thread_table.threads.erase(deleted_uid);' in source,
'ExitDelete releases stack and erases thread object')
need('wake_thread_end_waiters(deleted_uid, 0u);' in source,
'ExitDelete still wakes thread-end waiters')
need('activate_next_thread(ctx, "thread-exit-delete")' in source,
'ExitDelete schedules the next ready PSP thread')
exitdelete_reg = source[source.index('runtime.register_hle("ThreadManForUser", 0x809CE29Bu'):]
exitdelete_reg = exitdelete_reg[:exitdelete_reg.index('runtime.register_hle("ThreadManForUser", 0x383F7BCCu')]
need('exit_delete_current_thread(rt, ctx);' in exitdelete_reg and 'complete_current_thread(rt, ctx);' not in exitdelete_reg,
'sceKernelExitDeleteThread no longer aliases sceKernelExitThread')
exit_reg = source[source.index('runtime.register_hle("ThreadManForUser", 0xAA73C935u'):]
exit_reg = exit_reg[:exit_reg.index('runtime.register_hle("ThreadManForUser", 0x278C0DF5u')]
need('complete_current_thread(rt, ctx);' in exit_reg,
'sceKernelExitThread keeps dormant Completed semantics')
# Evidence baked into the generated guest: all three historically leaked VCS
# worker entries reach the ExitDelete import.
need('runtime.register_function(0x08B734F4u, &import_74, "ThreadManForUser::0x809CE29B")' in guest_registry,
'guest import 0x08B734F4 maps to sceKernelExitDeleteThread')
need('L_08986B50:' in sfx and 'ctx.pc = 0x08B734F4u;' in sfx,
'sfx bank worker reaches ExitDelete import')
need('L_08AB5AA0:' in memstick and 'ctx.pc = 0x08B734F4u;' in memstick,
'memstick worker reaches ExitDelete import')
need('L_08934734:' in stupid and 'ctx.pc = 0x08B734F4u;' in stupid,
'stupidthread worker reaches ExitDelete import')
# Existing V8.2.6 checkpoint migration is intentionally scoped to proven VCS
# worker entries and must leave normal ExitThread records alone.
need('bool is_legacy_vcs_exit_delete_worker' in source, 'legacy checkpoint worker classifier')
for token, label in [
('thread.entry == 0x08934734u && thread.name == "stupidthread"', 'stupidthread migration guard'),
('thread.entry == 0x08AB5AA0u && thread.name == "memstick"', 'memstick migration guard'),
('thread.entry == 0x08986B50u && thread.name == "sfx bank load thread"', 'sfx worker migration guard'),
('SAVE_REPRO legacy_memory_repair exitdelete_threads=', 'restore migration diagnostic'),
]:
need(token in source, label)
need('const LegacyExitDeleteRepairStats exitdelete_repair = repair_legacy_vcs_exit_delete_threads();' in source,
'checkpoint restore invokes legacy ExitDelete repair')
need('std::uint32_t partition_arena_base{};' in source and 'allocate_user_arena_range' in source and
'recompute_partition_frontier' in source, 'partition allocator tracks reusable arena holes')
need(source.count('recompute_partition_frontier();') >= 5,
'partition/FPL free and restore paths recompute the active frontier')
need('PARTITION_ALLOC failed name=' in source and 'FPL_CREATE failed name=' in source,
'partition exhaustion diagnostics are present only on failure paths')
need('sceKernelExitDeleteThread left a Completed thread record behind' in source,
'live ExitDelete regression self-test')
need('legacy ExitDelete migration did not recover the top-down stack frontier' in source,
'legacy checkpoint migration regression self-test')
need('thread_table.threads.contains(1) && thread_table.threads.contains(13)' in source,
'migration self-test protects ordinary/current threads')
# V8.2.6A passive rule remains: no reintroduction of import tracking into core.
for forbidden in ['g_runtime_current_import_library', 'g_runtime_current_import_nid',
'runtime_current_import_library()', 'runtime_current_import_nid()']:
need(forbidden not in runtime, f'core runtime remains free of passive save tracing: {forbidden}')
need('kSaveReproVersion = 826u' in source, 'existing user checkpoint remains format-compatible')
need('PSPRECOMP_TIME_TICK_DISPATCHES' not in source[source.index('// V8.2.6 SAVE REPRO CAPTURE'):source.index('constexpr std::uint32_t kPspUtilityStart')],
'save repair does not change scheduler cadence')
print('V8.2.7 SAVE THREAD LIFECYCLE audit PASS')