mirror of
https://github.com/jessicanataliagta/PSPRecomp
synced 2026-09-26 16:49:34 -04:00
more save game load game fixes
more save game load game fixes
This commit is contained in:
@@ -2,15 +2,19 @@
|
||||
setlocal EnableExtensions
|
||||
for %%I in ("%~dp0..\..") do set "REPO=%%~fI"
|
||||
set "BUILD=%REPO%\out\vcs-release-ninja"
|
||||
set "MARKER=%BUILD%\.v9_5_host_texture_objects_done"
|
||||
set "MARKER95=%BUILD%\.v9_5_host_texture_objects_done"
|
||||
set "MARKER96=%BUILD%\.v9_6_savedata_cancel_objects_done"
|
||||
|
||||
call "%~dp0FORCE_V9_5_HOST_TEXTURE_OBJECTS.bat"
|
||||
if errorlevel 1 exit /b %errorlevel%
|
||||
call "%~dp0FORCE_V9_6_SAVEDATA_CANCEL_OBJECTS.bat"
|
||||
if errorlevel 1 exit /b %errorlevel%
|
||||
|
||||
call "%~dp0scripts\build_release_ninja.bat"
|
||||
set "RC=%errorlevel%"
|
||||
if "%RC%"=="0" (
|
||||
if not exist "%BUILD%" mkdir "%BUILD%" >nul 2>nul
|
||||
>"%MARKER%" echo V9.5 host-texture isolation successfully rebuilt critical objects.
|
||||
>"%MARKER95%" echo V9.5 host-texture isolation successfully rebuilt critical objects.
|
||||
>"%MARKER96%" echo V9.6 mode-aware savedata cancellation successfully rebuilt vcs_profile.cpp.
|
||||
)
|
||||
exit /b %RC%
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
@echo off
|
||||
setlocal EnableExtensions
|
||||
for %%I in ("%~dp0..\..") do set "REPO=%%~fI"
|
||||
set "BUILD=%REPO%\out\vcs-release-ninja"
|
||||
set "MARKER=%BUILD%\.v9_6_savedata_cancel_objects_done"
|
||||
|
||||
if exist "%MARKER%" exit /b 0
|
||||
|
||||
echo [V9.6] SAVEDATA CANCEL: invalidating stale profile objects...
|
||||
if not exist "%BUILD%" (
|
||||
echo [V9.6] Build tree does not exist yet; normal build will create it.
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command ^
|
||||
"$build=[IO.Path]::GetFullPath('%BUILD%');" ^
|
||||
"$names=@('vcs_profile.cpp.obj');" ^
|
||||
"$found=Get-ChildItem -LiteralPath $build -Recurse -File -Filter '*.obj' -ErrorAction SilentlyContinue ^| Where-Object { $names -contains $_.Name };" ^
|
||||
"Write-Host ('[V9.6] stale objects found: ' + $found.Count);" ^
|
||||
"foreach($f in $found){Write-Host (' deleting: ' + $f.FullName); Remove-Item -LiteralPath $f.FullName -Force -ErrorAction Stop};" ^
|
||||
"$src='%REPO%\profiles\vcs\host\vcs_profile.cpp';" ^
|
||||
"if(Test-Path -LiteralPath $src){(Get-Item -LiteralPath $src).LastWriteTime=Get-Date}"
|
||||
if errorlevel 1 (
|
||||
echo ERROR: failed to invalidate V9.6 savedata cancel objects.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo [V9.6] Savedata cancel objects invalidated. Ninja must compile vcs_profile.cpp again.
|
||||
exit /b 0
|
||||
@@ -2207,6 +2207,14 @@ constexpr std::uint32_t kPspUtilityCircle = 0x002000u;
|
||||
constexpr std::uint32_t kPspUtilityCross = 0x004000u;
|
||||
|
||||
constexpr std::uint32_t kUtilityCommonResultOffset = 0x1Cu;
|
||||
// PSP utility dialog common.result values. Keep these separate from savedata
|
||||
// I/O error codes: user cancellation is CANCEL (1), while ABORT (2) is a
|
||||
// different firmware result. VCS needs a legacy ABORT workaround only for
|
||||
// the promoted LISTLOAD path; normal LISTSAVE/LISTDELETE cancellation must use
|
||||
// the real PSP CANCEL result or the retail frontend enters its loading path.
|
||||
constexpr std::uint32_t kPspUtilityDialogResultSuccess = 0u;
|
||||
constexpr std::uint32_t kPspUtilityDialogResultCancel = 1u;
|
||||
constexpr std::uint32_t kPspUtilityDialogResultAbort = 2u;
|
||||
constexpr std::uint32_t kSavedataModeOffset = 0x30u;
|
||||
constexpr std::uint32_t kSavedataGameNameOffset = 0x3Cu;
|
||||
constexpr std::uint32_t kSavedataSaveNameOffset = 0x4Cu;
|
||||
@@ -2549,7 +2557,7 @@ void initialize_savedata_list_ui(psprecomp::Runtime &runtime) {
|
||||
savedata_utility.ui_initialized = true;
|
||||
savedata_utility_ui_begin(savedata_utility.mode, savedata_utility.slots,
|
||||
savedata_utility.selected);
|
||||
std::cout << "[savedata] V9.4 list UI initialized mode=" << savedata_utility.mode
|
||||
std::cout << "[savedata] V9.6 list UI initialized mode=" << savedata_utility.mode
|
||||
<< " slots=" << savedata_utility.slots.size()
|
||||
<< " selected=" << savedata_utility.selected << "\n";
|
||||
}
|
||||
@@ -2802,19 +2810,34 @@ const char *savedata_failure_message(std::uint32_t mode) noexcept {
|
||||
}
|
||||
|
||||
void cancel_savedata_list_utility(psprecomp::Runtime &runtime) {
|
||||
// PSP utility cancellation is not success. The retail VCS completion
|
||||
// handler treats common.result == 0 as a successful load even when
|
||||
// abortStatus is non-zero, which is why backing out of our promoted
|
||||
// LOAD->LISTLOAD picker dropped into New Game. VCS' own AOT path has an
|
||||
// explicit cancel branch for result==2 + abortStatus!=0.
|
||||
runtime.memory().store32(savedata_utility.parameter_address + kSavedataAbortStatusOffset, 1u);
|
||||
runtime.memory().store32(savedata_utility.parameter_address + kUtilityCommonResultOffset, 2u);
|
||||
// Real PSP savedata list dialogs report a user Back/Cancel as
|
||||
// common.result = CANCEL (1) and do not set abortStatus. V9.4 deliberately
|
||||
// used ABORT (2) + abortStatus=1 to get VCS' promoted LOAD->LISTLOAD path
|
||||
// back into gameplay; applying that same workaround to LISTSAVE was wrong:
|
||||
// VCS interprets it as a load/restore transition and shows a black LOADING
|
||||
// screen. Keep the proven LOAD compatibility workaround, but use exact PSP
|
||||
// cancellation semantics for SAVE/DELETE.
|
||||
const bool load_cancel_workaround = savedata_utility.mode == 4u ||
|
||||
savedata_utility.startup_picker || savedata_utility.direct_load_picker;
|
||||
const std::uint32_t common_result = load_cancel_workaround
|
||||
? kPspUtilityDialogResultAbort
|
||||
: kPspUtilityDialogResultCancel;
|
||||
const std::uint32_t abort_status = load_cancel_workaround ? 1u : 0u;
|
||||
|
||||
runtime.memory().store32(savedata_utility.parameter_address +
|
||||
kSavedataAbortStatusOffset, abort_status);
|
||||
runtime.memory().store32(savedata_utility.parameter_address +
|
||||
kUtilityCommonResultOffset, common_result);
|
||||
savedata_utility.operation_complete = true;
|
||||
savedata_utility.status = UtilityStatus::Quit;
|
||||
savedata_utility_ui_end();
|
||||
display_window_set_system_utility_mode(false);
|
||||
std::cout << "[savedata] V9.4 picker cancelled mode=" << savedata_utility.mode
|
||||
<< " commonResult=2 abortStatus=1\n";
|
||||
std::cout << "[savedata] V9.6 picker cancelled mode=" << savedata_utility.mode
|
||||
<< " commonResult=" << common_result
|
||||
<< " abortStatus=" << abort_status
|
||||
<< (load_cancel_workaround ? " policy=load-compat-abort"
|
||||
: " policy=psp-user-cancel")
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
void execute_selected_savedata_slot(psprecomp::Runtime &runtime) {
|
||||
@@ -2837,7 +2860,7 @@ void execute_selected_savedata_slot(psprecomp::Runtime &runtime) {
|
||||
savedata_utility.status = UtilityStatus::Quit;
|
||||
savedata_utility_ui_end();
|
||||
display_window_set_system_utility_mode(false);
|
||||
std::cout << "[savedata] V9.4 LOAD selected slot=" << slot.save_name
|
||||
std::cout << "[savedata] V9.6 LOAD selected slot=" << slot.save_name
|
||||
<< " result=0\n";
|
||||
} else {
|
||||
savedata_utility.prompt = SavedataUtilityUiPrompt::Result;
|
||||
@@ -7722,7 +7745,7 @@ void install_profile(psprecomp::Runtime &runtime, std::uint32_t user_arena_start
|
||||
initialize_savedata_list_ui(rt);
|
||||
display_window_set_system_utility_mode(true);
|
||||
savedata_utility.previous_buttons = effective_controller_buttons();
|
||||
std::cout << "[savedata] V9.4 LOAD picker active; guest mode="
|
||||
std::cout << "[savedata] V9.6 LOAD picker active; guest mode="
|
||||
<< guest_mode << " slots=" << savedata_utility.slots.size() << "\n";
|
||||
} else if (savedata_mode_has_list_ui(savedata_utility.mode)) {
|
||||
initialize_savedata_list_ui(rt);
|
||||
|
||||
@@ -20,8 +20,10 @@ if 'startup_load_picker_consumed' not in profile or 'savedata_utility.startup_pi
|
||||
if 'savedata_utility.mode = 4u' not in profile: fail('startup picker is not host-side LISTLOAD')
|
||||
if 'guest_mode == 0u' not in profile or 'guest_mode == 2u' not in profile: fail('AUTOLOAD/LOAD modes not covered')
|
||||
if 'if (!savedata_utility.startup_picker)' not in profile: fail('startup picker cancellation guard missing')
|
||||
if 'kUtilityCommonResultOffset, 2u' not in profile or 'kSavedataAbortStatusOffset, 1u' not in profile:
|
||||
fail('picker cancel is still reported as successful LOAD')
|
||||
if 'load_cancel_workaround' not in profile or 'kPspUtilityDialogResultAbort' not in profile:
|
||||
fail('promoted LOAD picker cancel compatibility path missing')
|
||||
if 'kPspUtilityDialogResultCancel = 1u' not in profile:
|
||||
fail('PSP user-cancel result is not defined for non-load dialogs')
|
||||
if 'MouseMenu=false' not in ini: fail('mouse menu is not disabled by default')
|
||||
if 'if (mouse_menu_enabled()) enqueue_synthetic_pulse(state, kPspCross, 2);' not in display:
|
||||
fail('savedata mouse click is not gated by MouseMenu')
|
||||
@@ -29,5 +31,5 @@ print('LOAD-ONLY STARTUP AUDIT: PASS')
|
||||
print(' - native GAME frontend boot hook is unlinked')
|
||||
print(' - V8 generated autoload interception is removed')
|
||||
print(' - first AUTOLOAD and every explicit LOAD become the in-frame Load Game picker')
|
||||
print(' - cancel returns result=2 + abortStatus=1 instead of successful LOAD')
|
||||
print(' - LOAD cancel keeps the proven abort compatibility path; SAVE uses PSP CANCEL')
|
||||
print(' - menu mouse remains disabled by default and gates savedata clicks')
|
||||
|
||||
@@ -43,8 +43,9 @@ require(ui_cpp, '58.0f, 183.0f, 82.0f, 46.0f', 'next slot stays above footer')
|
||||
require(ui_cpp, 'submit_smooth_text', 'antialiased text atlas path')
|
||||
require(ui_cpp, 'blend_source_factor = 2u', 'text source-alpha blending')
|
||||
require(ui_cpp, 'blend_dest_factor = 3u', 'text inverse-alpha blending')
|
||||
require(profile_cpp, 'kUtilityCommonResultOffset, 2u', 'cancel result is not reported as success')
|
||||
require(profile_cpp, 'kSavedataAbortStatusOffset, 1u', 'cancel abortStatus is asserted')
|
||||
require(profile_cpp, 'load_cancel_workaround', 'mode-aware cancel policy')
|
||||
require(profile_cpp, 'kPspUtilityDialogResultCancel = 1u', 'PSP cancel result')
|
||||
require(profile_cpp, 'kPspUtilityDialogResultAbort = 2u', 'LOAD compatibility abort result')
|
||||
require(ui_cpp, 'quad(v, 0, 0, 480, 23', 'PSP utility banner geometry')
|
||||
require(ui_cpp, 'quad(v, 180, 136, 480, 137', 'PSP save-info separator geometry')
|
||||
require(profile_cpp, 'slot.icon0_path = icon0.string();', 'savedata ICON0 path preserved')
|
||||
|
||||
@@ -10,13 +10,15 @@ force = (root/'FORCE_V9_4_SAVEDATA_UI_OBJECTS.bat').read_text(encoding='utf-8')
|
||||
errors=[]
|
||||
def req(text, needle, msg):
|
||||
if needle not in text: errors.append(msg)
|
||||
req(profile, 'kUtilityCommonResultOffset, 2u', 'cancel common.result must be 2')
|
||||
req(profile, 'kSavedataAbortStatusOffset, 1u', 'cancel abortStatus must be 1')
|
||||
req(profile, 'load_cancel_workaround', 'mode-aware cancellation missing')
|
||||
req(profile, 'kPspUtilityDialogResultCancel = 1u', 'PSP CANCEL constant missing')
|
||||
req(profile, 'kPspUtilityDialogResultAbort = 2u', 'LOAD ABORT compatibility constant missing')
|
||||
req(ui, 'submit_smooth_text', 'smooth atlas text path missing')
|
||||
req(ui, 'draw_save_thumbnail(v, icons, 58.0f, 37.0f, 82.0f, 46.0f', 'previous slot safe row missing')
|
||||
req(ui, 'draw_save_thumbnail(v, icons, 58.0f, 183.0f, 82.0f, 46.0f', 'next slot safe row missing')
|
||||
req(ui, 'Draw text last so slot thumbnails can never cover labels or metadata.', 'text draw ordering guard missing')
|
||||
req(build, 'FORCE_V9_4_SAVEDATA_UI_OBJECTS.bat', 'V9.4 force rebuild not wired')
|
||||
if 'FORCE_V9_4_SAVEDATA_UI_OBJECTS.bat' not in build and 'FORCE_V9_5_HOST_TEXTURE_OBJECTS.bat' not in build:
|
||||
errors.append('savedata UI object invalidation not wired')
|
||||
req(force, 'savedata_utility_ui.cpp.obj', 'UI object invalidation missing')
|
||||
req(force, 'vcs_profile.cpp.obj', 'profile object invalidation missing')
|
||||
if not atlas.is_file() or atlas.stat().st_size < 10000:
|
||||
@@ -26,7 +28,7 @@ if errors:
|
||||
for e in errors: print(' - '+e)
|
||||
sys.exit(1)
|
||||
print('V9.4 SAVEDATA UI AUDIT: PASS')
|
||||
print(' - cancel is not reported to VCS as successful load')
|
||||
print(' - cancel is mode-aware: LOAD compatibility abort, SAVE/DELETE PSP cancel')
|
||||
print(' - slot scroller stays between banner and footer')
|
||||
print(' - antialiased text atlas is drawn after slot/icon geometry')
|
||||
print(' - stale V9.3 UI/profile objects are force-invalidated once')
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
import sys
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
profile = (root / 'host/vcs_profile.cpp').read_text(encoding='utf-8', errors='replace')
|
||||
build = (root / 'BUILD_VCS_NINJA.bat').read_text(encoding='utf-8', errors='replace')
|
||||
force_path = root / 'FORCE_V9_6_SAVEDATA_CANCEL_OBJECTS.bat'
|
||||
force = force_path.read_text(encoding='utf-8', errors='replace') if force_path.is_file() else ''
|
||||
errors = []
|
||||
def req(text, needle, msg):
|
||||
if needle not in text:
|
||||
errors.append(msg)
|
||||
req(profile, 'kPspUtilityDialogResultCancel = 1u', 'PSP CANCEL must be 1')
|
||||
req(profile, 'kPspUtilityDialogResultAbort = 2u', 'PSP ABORT must be 2')
|
||||
req(profile, 'const bool load_cancel_workaround = savedata_utility.mode == 4u', 'LISTLOAD compatibility policy missing')
|
||||
req(profile, '? kPspUtilityDialogResultAbort', 'LOAD cancel must use ABORT compatibility result')
|
||||
req(profile, ': kPspUtilityDialogResultCancel', 'SAVE/DELETE cancel must use PSP CANCEL')
|
||||
req(profile, 'const std::uint32_t abort_status = load_cancel_workaround ? 1u : 0u;', 'SAVE/DELETE abortStatus must remain zero')
|
||||
req(profile, 'policy=psp-user-cancel', 'physical SAVE cancel diagnostic missing')
|
||||
req(build, 'FORCE_V9_6_SAVEDATA_CANCEL_OBJECTS.bat', 'V9.6 object invalidation not wired')
|
||||
req(force, 'vcs_profile.cpp.obj', 'vcs_profile object invalidation missing')
|
||||
if errors:
|
||||
print('V9.6 SAVEDATA CANCEL AUDIT: FAIL')
|
||||
for e in errors:
|
||||
print(' - ' + e)
|
||||
sys.exit(1)
|
||||
print('V9.6 SAVEDATA CANCEL AUDIT: PASS')
|
||||
print(' - LISTLOAD retains the physically-proven VCS abort compatibility path')
|
||||
print(' - LISTSAVE/LISTDELETE use real PSP common.result=CANCEL (1), abortStatus=0')
|
||||
print(' - vcs_profile.cpp is force-invalidated once for existing Ninja trees')
|
||||
Reference in New Issue
Block a user