2009-09-08 12:08:10 +00:00
/* PCSX2 - PS2 Emulator for PCs
2010-05-03 14:08:02 +00:00
* Copyright (C) 2002-2010 PCSX2 Dev Team
2009-11-12 12:51:00 +00:00
*
2009-09-08 12:08:10 +00:00
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
2009-02-09 21:15:56 +00:00
*
2009-09-08 12:08:10 +00:00
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
2009-02-09 21:15:56 +00:00
*/
2009-09-08 12:08:10 +00:00
2009-02-09 21:15:56 +00:00
#include "PrecompiledHeader.h"
#include "SaveState.h"
2022-04-11 22:15:44 +10:00
#include "common/FileSystem.h"
2022-05-20 00:46:33 +10:00
#include "common/Path.h"
2022-04-11 22:15:44 +10:00
#include "common/SafeArray.inl"
#include "common/ScopedGuard.h"
#include "common/StringUtil.h"
#include "common/ZipHelpers.h"
2009-08-16 06:26:40 +00:00
#include "ps2/BiosTools.h"
2009-02-09 21:15:56 +00:00
#include "COP0.h"
2010-11-22 16:24:54 +00:00
#include "VUmicro.h"
2011-08-12 02:31:49 +00:00
#include "MTVU.h"
2009-02-09 21:15:56 +00:00
#include "Cache.h"
2021-09-16 20:14:42 +10:00
#include "Config.h"
2022-04-10 11:45:04 -07:00
#include "CDVD/CDVD.h"
2022-04-11 22:15:44 +10:00
#include "R3000A.h"
2009-11-14 12:02:56 +00:00
#include "Elfheader.h"
2010-06-28 03:09:51 +00:00
#include "Counters.h"
2021-09-18 09:39:52 +10:00
#include "Patch.h"
2021-11-07 13:21:17 -05:00
#include "DebugTools/Breakpoints.h"
2021-12-07 19:21:46 +10:00
#include "Host.h"
#include "GS.h"
#include "GS/GS.h"
2020-09-24 17:31:26 +02:00
#include "SPU2/spu2.h"
2021-11-20 18:57:58 -08:00
#include "PAD/Gamepad.h"
2022-12-05 01:00:06 +10:00
#include "USB/USB.h"
2021-12-07 19:21:46 +10:00
#include "VMManager.h"
2021-09-18 13:09:31 +10:00
2022-04-18 23:35:14 +10:00
#ifdef ENABLE_ACHIEVEMENTS
#include "Frontend/Achievements.h"
#endif
2022-05-18 23:27:23 +10:00
#include "fmt/core.h"
2021-12-07 19:21:46 +10:00
#include <csetjmp>
#include <png.h>
2009-02-09 21:15:56 +00:00
using namespace R5900 ;
2022-10-29 21:00:38 +10:00
static tlbs s_tlb_backup [ std :: size ( tlb )];
2009-02-09 21:15:56 +00:00
static void PreLoadPrep ()
{
2022-04-11 22:15:44 +10:00
// ensure everything is in sync before we start overwriting stuff.
if ( THREAD_VU1 )
vu1Thread . WaitVU ();
GetMTGS (). WaitGS ( false );
2022-10-29 21:00:38 +10:00
// backup current TLBs, since we're going to overwrite them all
std :: memcpy ( s_tlb_backup , tlb , sizeof ( s_tlb_backup ));
// clear protected pages, since we don't want to fault loading EE memory
mmap_ResetBlockTracking ();
2009-03-04 11:33:45 +00:00
SysClearExecutionCache ();
2009-02-09 21:15:56 +00:00
}
static void PostLoadPrep ()
{
2020-10-17 04:31:26 -05:00
resetCache ();
2009-02-09 21:15:56 +00:00
// WriteCP0Status(cpuRegs.CP0.n.Status.val);
2022-10-29 21:00:38 +10:00
for ( int i = 0 ; i < 48 ; i ++ )
{
if ( std :: memcmp ( & s_tlb_backup [ i ], & tlb [ i ], sizeof ( tlbs )) != 0 )
{
UnmapTLB ( s_tlb_backup [ i ], i );
MapTLB ( tlb [ i ], i );
}
}
2014-06-12 00:35:28 +02:00
if ( EmuConfig . Gamefixes . GoemonTlbHack ) GoemonPreloadTlb ();
2021-11-07 13:21:17 -05:00
CBreakPoints :: SetSkipFirst ( BREAKPOINT_EE , 0 );
CBreakPoints :: SetSkipFirst ( BREAKPOINT_IOP , 0 );
2010-06-28 03:09:51 +00:00
UpdateVSyncRate ();
2009-02-09 21:15:56 +00:00
}
2010-11-18 13:01:38 +00:00
// --------------------------------------------------------------------------------------
// SaveStateBase (implementations)
// --------------------------------------------------------------------------------------
2009-12-22 03:15:30 +00:00
SaveStateBase :: SaveStateBase ( SafeArray < u8 >& memblock )
2009-02-09 21:15:56 +00:00
{
2010-04-27 13:12:03 +00:00
Init ( & memblock );
}
SaveStateBase :: SaveStateBase ( SafeArray < u8 >* memblock )
{
Init ( memblock );
}
void SaveStateBase :: Init ( SafeArray < u8 >* memblock )
{
m_memory = memblock ;
2009-12-22 03:15:30 +00:00
m_version = g_SaveVersion ;
m_idx = 0 ;
2009-02-09 21:15:56 +00:00
}
2009-09-16 17:23:02 +00:00
void SaveStateBase :: PrepBlock ( int size )
2009-02-09 21:15:56 +00:00
{
2011-07-24 13:02:50 +00:00
pxAssertDev ( m_memory , "Savestate memory/buffer pointer is null!" );
2010-04-27 13:12:03 +00:00
2009-09-16 17:23:02 +00:00
const int end = m_idx + size ;
if ( IsSaving () )
2010-04-27 13:12:03 +00:00
m_memory -> MakeRoomFor ( end );
2009-09-16 17:23:02 +00:00
else
2009-03-19 04:16:24 +00:00
{
2010-04-27 13:12:03 +00:00
if ( m_memory -> GetSizeInBytes () < end )
2009-12-22 03:15:30 +00:00
throw Exception :: SaveStateLoadError ();
2009-09-16 17:23:02 +00:00
}
}
void SaveStateBase :: FreezeTag ( const char * src )
{
2009-11-18 16:53:44 +00:00
const uint allowedlen = sizeof ( m_tagspace ) - 1 ;
2022-05-18 23:27:23 +10:00
pxAssertDev ( strlen ( src ) < allowedlen , "Tag name exceeds the allowed length" );
2009-09-16 17:23:02 +00:00
2009-09-23 12:53:32 +00:00
memzero ( m_tagspace );
2009-09-16 17:23:02 +00:00
strcpy ( m_tagspace , src );
Freeze ( m_tagspace );
if ( strcmp ( m_tagspace , src ) != 0 )
{
2022-05-18 23:27:23 +10:00
std :: string msg ( fmt :: format ( "Savestate data corruption detected while reading tag: {}" , src ));
pxFail ( msg . c_str () );
throw Exception :: SaveStateLoadError (). SetDiagMsg ( std :: move ( msg ));
2009-03-19 04:16:24 +00:00
}
}
2010-12-14 07:28:05 +00:00
SaveStateBase & SaveStateBase :: FreezeBios ()
2009-02-09 21:15:56 +00:00
{
2010-12-14 07:28:05 +00:00
FreezeTag ( "BIOS" );
2009-03-19 04:16:24 +00:00
// Check the BIOS, and issue a warning if the bios for this state
// doesn't match the bios currently being used (chances are it'll still
// work fine, but some games are very picky).
2022-09-13 21:20:25 -05:00
2010-12-14 07:28:05 +00:00
u32 bioscheck = BiosChecksum ;
char biosdesc [ 256 ];
memzero ( biosdesc );
2021-09-25 14:35:21 +10:00
memcpy ( biosdesc , BiosDescription . c_str (), std :: min ( sizeof ( biosdesc ), BiosDescription . length () ) );
2022-09-13 21:20:25 -05:00
2010-12-14 07:28:05 +00:00
Freeze ( bioscheck );
Freeze ( biosdesc );
2009-03-19 04:16:24 +00:00
2010-12-14 07:28:05 +00:00
if ( bioscheck != BiosChecksum )
2009-09-16 17:23:02 +00:00
{
2010-12-14 07:28:05 +00:00
Console . Newline ();
Console . Indent ( 1 ). Error ( "Warning: BIOS Version Mismatch, savestate may be unstable!" );
Console . Indent ( 2 ). Error (
2021-09-25 14:35:21 +10:00
"Current BIOS: %s (crc=0x%08x) \n "
2010-12-14 07:28:05 +00:00
"Savestate BIOS: %s (crc=0x%08x) \n " ,
2021-09-25 14:35:21 +10:00
BiosDescription . c_str (), BiosChecksum ,
2010-12-14 07:28:05 +00:00
biosdesc , bioscheck
);
2009-09-16 17:23:02 +00:00
}
2022-09-13 21:20:25 -05:00
2010-12-14 07:28:05 +00:00
return * this ;
2009-09-16 17:23:02 +00:00
}
2010-12-14 07:28:05 +00:00
SaveStateBase & SaveStateBase :: FreezeInternals ()
2009-09-16 17:23:02 +00:00
{
2022-09-18 14:46:16 +10:00
const u32 previousCRC = ElfCRC ;
2011-12-29 17:08:17 +00:00
// Print this until the MTVU problem in gifPathFreeze is taken care of (rama)
if ( THREAD_VU1 ) Console . Warning ( "MTVU speedhack is enabled, saved states may not be stable" );
2022-09-13 21:20:25 -05:00
2009-03-19 04:16:24 +00:00
// Second Block - Various CPU Registers and States
// -----------------------------------------------
FreezeTag ( "cpuRegs" );
2009-09-16 17:23:02 +00:00
Freeze ( cpuRegs ); // cpu regs + COP0
Freeze ( psxRegs ); // iop regs
2009-03-19 04:16:24 +00:00
Freeze ( fpuRegs );
2009-09-16 17:23:02 +00:00
Freeze ( tlb ); // tlbs
2021-10-14 22:27:02 +01:00
Freeze ( AllowParams1 ); //OSDConfig written (Fast Boot)
Freeze ( AllowParams2 );
Freeze ( g_GameStarted );
Freeze ( g_GameLoading );
2021-10-20 10:41:50 +01:00
Freeze ( ElfCRC );
char localDiscSerial [ 256 ];
2022-04-12 21:26:39 +10:00
StringUtil :: Strlcpy ( localDiscSerial , DiscSerial . c_str (), sizeof ( localDiscSerial ));
2021-10-20 10:41:50 +01:00
Freeze ( localDiscSerial );
if ( IsLoading ())
2022-09-18 14:46:16 +10:00
{
2022-04-12 21:26:39 +10:00
DiscSerial = localDiscSerial ;
2009-02-09 21:15:56 +00:00
2022-09-18 14:46:16 +10:00
if ( ElfCRC != previousCRC )
{
// HACK: LastELF isn't in the save state... Load it before we go too far into restoring state.
// When we next bump save states, we should include it. We need this for achievements, because
// we want to load and activate achievements before restoring any of their tracked state.
if ( const std :: string & elf_override = VMManager :: Internal :: GetElfOverride (); ! elf_override . empty ())
cdvdReloadElfInfo ( fmt :: format ( "host:{}" , elf_override ));
else
cdvdReloadElfInfo ();
}
}
2009-03-19 04:16:24 +00:00
// Third Block - Cycle Timers and Events
// -------------------------------------
FreezeTag ( "Cycles" );
2009-02-09 21:15:56 +00:00
Freeze ( EEsCycle );
Freeze ( EEoCycle );
2021-10-20 10:41:50 +01:00
Freeze ( nextCounter );
Freeze ( nextsCounter );
Freeze ( psxNextsCounter );
Freeze ( psxNextCounter );
2009-02-09 21:15:56 +00:00
2009-03-19 04:16:24 +00:00
// Fourth Block - EE-related systems
// ---------------------------------
2009-09-16 17:23:02 +00:00
FreezeTag ( "EE-Subsystems" );
2009-02-09 21:15:56 +00:00
rcntFreeze ();
gsFreeze ();
vuMicroFreeze ();
2021-10-20 10:41:50 +01:00
vuJITFreeze ();
2009-02-09 21:15:56 +00:00
vif0Freeze ();
vif1Freeze ();
sifFreeze ();
ipuFreeze ();
2010-09-05 00:36:03 +00:00
ipuDmaFreeze ();
2009-02-20 00:39:58 +00:00
gifFreeze ();
2011-07-24 13:02:50 +00:00
gifDmaFreeze ();
2009-03-04 13:05:06 +00:00
sprFreeze ();
2020-12-29 23:15:14 +00:00
mtvuFreeze ();
2009-02-09 21:15:56 +00:00
2009-03-19 04:16:24 +00:00
// Fifth Block - iop-related systems
// ---------------------------------
2009-09-16 17:23:02 +00:00
FreezeTag ( "IOP-Subsystems" );
2010-11-17 14:06:51 +00:00
FreezeMem ( iopMem -> Sif , sizeof ( iopMem -> Sif )); // iop's sif memory (not really needed, but oh well)
2009-03-19 04:16:24 +00:00
psxRcntFreeze ();
2009-02-09 21:15:56 +00:00
sioFreeze ();
2009-03-19 04:16:24 +00:00
sio2Freeze ();
2009-02-09 21:15:56 +00:00
cdrFreeze ();
cdvdFreeze ();
2020-09-25 14:14:02 +02:00
2010-04-27 16:23:08 +00:00
// technically this is HLE BIOS territory, but we don't have enough such stuff
// to merit an HLE Bios sub-section... yet.
deci2Freeze ();
2009-02-09 21:15:56 +00:00
2019-01-27 17:58:36 -05:00
InputRecordingFreeze ();
2018-07-02 20:40:44 -04:00
2010-12-14 07:28:05 +00:00
return * this ;
2009-11-04 09:30:22 +00:00
}
2010-11-18 13:01:38 +00:00
// --------------------------------------------------------------------------------------
// memSavingState (implementations)
// --------------------------------------------------------------------------------------
2009-02-09 21:15:56 +00:00
// uncompressed to/from memory state saves implementation
2010-03-05 14:24:44 +00:00
memSavingState :: memSavingState ( SafeArray < u8 >& save_to )
: SaveStateBase ( save_to )
2009-02-09 21:15:56 +00:00
{
}
2010-04-27 13:12:03 +00:00
memSavingState :: memSavingState ( SafeArray < u8 >* save_to )
: SaveStateBase ( save_to )
{
}
2009-09-16 17:23:02 +00:00
// Saving of state data
2009-02-09 21:15:56 +00:00
void memSavingState :: FreezeMem ( void * data , int size )
{
2010-12-14 07:28:05 +00:00
if ( ! size ) return ;
m_memory -> MakeRoomFor ( m_idx + size );
2014-08-27 13:45:23 +10:00
memcpy ( m_memory -> GetPtr ( m_idx ), data , size );
2009-10-09 15:17:53 +00:00
m_idx += size ;
2009-02-09 21:15:56 +00:00
}
2010-11-22 16:24:54 +00:00
void memSavingState :: MakeRoomForData ()
2009-10-09 17:28:17 +00:00
{
2011-07-24 13:02:50 +00:00
pxAssertDev ( m_memory , "Savestate memory/buffer pointer is null!" );
2010-04-27 13:12:03 +00:00
m_memory -> ChunkSize = ReallocThreshold ;
2010-11-18 13:01:38 +00:00
m_memory -> MakeRoomFor ( m_idx + MemoryBaseAllocSize );
2009-10-09 17:28:17 +00:00
}
2010-11-22 16:24:54 +00:00
// --------------------------------------------------------------------------------------
// memLoadingState (implementations)
// --------------------------------------------------------------------------------------
2010-03-05 14:24:44 +00:00
memLoadingState :: memLoadingState ( const SafeArray < u8 >& load_from )
: SaveStateBase ( const_cast < SafeArray < u8 >&> ( load_from ) )
2009-02-09 21:15:56 +00:00
{
}
2010-04-27 13:12:03 +00:00
memLoadingState :: memLoadingState ( const SafeArray < u8 >* load_from )
: SaveStateBase ( const_cast < SafeArray < u8 >*> ( load_from ) )
{
}
2010-11-22 16:24:54 +00:00
// Loading of state data from a memory buffer...
2009-02-09 21:15:56 +00:00
void memLoadingState :: FreezeMem ( void * data , int size )
{
2010-04-27 13:12:03 +00:00
const u8 * const src = m_memory -> GetPtr ( m_idx );
2009-10-09 15:17:53 +00:00
m_idx += size ;
2014-08-27 13:45:23 +10:00
memcpy ( data , src , size );
2009-02-09 21:15:56 +00:00
}
2021-05-11 18:29:01 +02:00
2022-05-18 23:27:23 +10:00
std :: string Exception :: SaveStateLoadError :: FormatDiagnosticMessage () const
2021-05-11 18:29:01 +02:00
{
2022-05-18 23:27:23 +10:00
std :: string retval = "Savestate is corrupt or incomplete! \n " ;
2021-12-07 19:21:46 +10:00
Host :: AddOSDMessage ( "Error: Savestate is corrupt or incomplete!" , 15.0f );
2021-05-11 18:29:01 +02:00
_formatDiagMsg ( retval );
return retval ;
}
2022-05-18 23:27:23 +10:00
std :: string Exception :: SaveStateLoadError :: FormatDisplayMessage () const
2021-05-11 18:29:01 +02:00
{
2022-05-18 23:27:23 +10:00
std :: string retval = "The savestate cannot be loaded, as it appears to be corrupt or incomplete. \n " ;
2021-12-07 19:21:46 +10:00
Host :: AddOSDMessage ( "Error: The savestate cannot be loaded, as it appears to be corrupt or incomplete." , 15.0f );
2021-05-11 18:29:01 +02:00
_formatUserMsg ( retval );
return retval ;
}
2021-09-18 09:39:52 +10:00
// Used to hold the current state backup (fullcopy of PS2 memory and subcomponents states).
//static VmStateBuffer state_buffer( L"Public Savestate Buffer" );
2022-04-11 22:15:44 +10:00
static const char * EntryFilename_StateVersion = "PCSX2 Savestate Version.id" ;
static const char * EntryFilename_Screenshot = "Screenshot.png" ;
static const char * EntryFilename_InternalStructures = "PCSX2 Internal Structures.dat" ;
2021-09-18 09:39:52 +10:00
struct SysState_Component
{
const char * name ;
int ( * freeze )( FreezeAction , freezeData * );
};
2021-12-07 19:21:46 +10:00
static int SysState_MTGSFreeze ( FreezeAction mode , freezeData * fP )
2021-09-18 09:39:52 +10:00
{
MTGS_FreezeData sstate = { fP , 0 };
GetMTGS (). Freeze ( mode , sstate );
return sstate . retval ;
}
static constexpr SysState_Component SPU2 { "SPU2" , SPU2freeze };
2021-12-07 19:21:46 +10:00
static constexpr SysState_Component PAD_ { "PAD" , PADfreeze };
2022-12-05 01:00:06 +10:00
static constexpr SysState_Component USB_ { "USB" , USBfreeze };
2021-09-18 09:39:52 +10:00
static constexpr SysState_Component GS { "GS" , SysState_MTGSFreeze };
2021-12-07 19:21:46 +10:00
static void SysState_ComponentFreezeOutRoot ( void * dest , SysState_Component comp )
2021-09-18 09:39:52 +10:00
{
freezeData fP = { 0 , ( u8 * ) dest };
if ( comp . freeze ( FreezeAction :: Size , & fP ) != 0 )
return ;
if ( ! fP . size )
return ;
Console . Indent (). WriteLn ( "Saving %s" , comp . name );
if ( comp . freeze ( FreezeAction :: Save , & fP ) != 0 )
throw std :: runtime_error ( std :: string ( " * " ) + comp . name + std :: string ( ": Error saving state! \n " ));
}
2022-04-11 22:15:44 +10:00
static void SysState_ComponentFreezeIn ( zip_file_t * zf , SysState_Component comp )
2021-09-18 09:39:52 +10:00
{
2022-04-18 23:35:14 +10:00
if ( ! zf )
return ;
2021-09-18 09:39:52 +10:00
freezeData fP = { 0 , nullptr };
if ( comp . freeze ( FreezeAction :: Size , & fP ) != 0 )
fP . size = 0 ;
Console . Indent (). WriteLn ( "Loading %s" , comp . name );
2021-11-13 19:21:17 -06:00
auto data = std :: make_unique < u8 [] > ( fP . size );
fP . data = data . get ();
2021-09-18 09:39:52 +10:00
2022-04-11 22:15:44 +10:00
if ( zip_fread ( zf , data . get (), fP . size ) != static_cast < zip_int64_t > ( fP . size ) || comp . freeze ( FreezeAction :: Load , & fP ) != 0 )
2021-09-18 09:39:52 +10:00
throw std :: runtime_error ( std :: string ( " * " ) + comp . name + std :: string ( ": Error loading state! \n " ));
}
2021-12-07 19:21:46 +10:00
static void SysState_ComponentFreezeOut ( SaveStateBase & writer , SysState_Component comp )
2021-09-18 09:39:52 +10:00
{
freezeData fP = { 0 , NULL };
if ( comp . freeze ( FreezeAction :: Size , & fP ) == 0 )
{
const int size = fP . size ;
writer . PrepBlock ( size );
SysState_ComponentFreezeOutRoot ( writer . GetBlockPtr (), comp );
writer . CommitBlock ( size );
}
return ;
}
// --------------------------------------------------------------------------------------
// BaseSavestateEntry
// --------------------------------------------------------------------------------------
class BaseSavestateEntry
{
protected :
BaseSavestateEntry () = default ;
public :
virtual ~ BaseSavestateEntry () = default ;
2022-04-11 22:15:44 +10:00
virtual const char * GetFilename () const = 0 ;
virtual void FreezeIn ( zip_file_t * zf ) const = 0 ;
2021-09-18 09:39:52 +10:00
virtual void FreezeOut ( SaveStateBase & writer ) const = 0 ;
virtual bool IsRequired () const = 0 ;
};
class MemorySavestateEntry : public BaseSavestateEntry
{
protected :
MemorySavestateEntry () {}
virtual ~ MemorySavestateEntry () = default ;
public :
2022-04-11 22:15:44 +10:00
virtual void FreezeIn ( zip_file_t * zf ) const ;
2021-09-18 09:39:52 +10:00
virtual void FreezeOut ( SaveStateBase & writer ) const ;
virtual bool IsRequired () const { return true ; }
protected :
virtual u8 * GetDataPtr () const = 0 ;
2022-04-11 22:15:44 +10:00
virtual u32 GetDataSize () const = 0 ;
2021-09-18 09:39:52 +10:00
};
2022-04-11 22:15:44 +10:00
void MemorySavestateEntry :: FreezeIn ( zip_file_t * zf ) const
2021-09-18 09:39:52 +10:00
{
2022-04-11 22:15:44 +10:00
const u32 expectedSize = GetDataSize ();
const s64 bytesRead = zip_fread ( zf , GetDataPtr (), expectedSize );
if ( bytesRead != static_cast < s64 > ( expectedSize ))
2021-09-18 09:39:52 +10:00
{
Console . WriteLn ( Color_Yellow , " '%s' is incomplete (expected 0x%x bytes, loading only 0x%x bytes)" ,
2022-04-11 22:15:44 +10:00
GetFilename (), expectedSize , static_cast < u32 > ( bytesRead ));
2021-09-18 09:39:52 +10:00
}
}
void MemorySavestateEntry :: FreezeOut ( SaveStateBase & writer ) const
{
writer . FreezeMem ( GetDataPtr (), GetDataSize ());
}
// --------------------------------------------------------------------------------------
// SavestateEntry_* (EmotionMemory, IopMemory, etc)
// --------------------------------------------------------------------------------------
// Implementation Rationale:
// The address locations of PS2 virtual memory components is fully dynamic, so we need to
// resolve the pointers at the time they are requested (eeMem, iopMem, etc). Thusly, we
// cannot use static struct member initializers -- we need virtual functions that compute
// and resolve the addresses on-demand instead... --air
class SavestateEntry_EmotionMemory : public MemorySavestateEntry
{
public :
virtual ~ SavestateEntry_EmotionMemory () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "eeMemory.bin" ; }
2021-09-18 09:39:52 +10:00
u8 * GetDataPtr () const { return eeMem -> Main ; }
uint GetDataSize () const { return sizeof ( eeMem -> Main ); }
2022-04-11 22:15:44 +10:00
virtual void FreezeIn ( zip_file_t * zf ) const
2021-09-18 09:39:52 +10:00
{
SysClearExecutionCache ();
2022-04-11 22:15:44 +10:00
MemorySavestateEntry :: FreezeIn ( zf );
2021-09-18 09:39:52 +10:00
}
};
class SavestateEntry_IopMemory : public MemorySavestateEntry
{
public :
virtual ~ SavestateEntry_IopMemory () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "iopMemory.bin" ; }
2021-09-18 09:39:52 +10:00
u8 * GetDataPtr () const { return iopMem -> Main ; }
uint GetDataSize () const { return sizeof ( iopMem -> Main ); }
};
class SavestateEntry_HwRegs : public MemorySavestateEntry
{
public :
virtual ~ SavestateEntry_HwRegs () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "eeHwRegs.bin" ; }
2021-09-18 09:39:52 +10:00
u8 * GetDataPtr () const { return eeHw ; }
uint GetDataSize () const { return sizeof ( eeHw ); }
};
class SavestateEntry_IopHwRegs : public MemorySavestateEntry
{
public :
virtual ~ SavestateEntry_IopHwRegs () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "iopHwRegs.bin" ; }
2021-09-18 09:39:52 +10:00
u8 * GetDataPtr () const { return iopHw ; }
uint GetDataSize () const { return sizeof ( iopHw ); }
};
class SavestateEntry_Scratchpad : public MemorySavestateEntry
{
public :
virtual ~ SavestateEntry_Scratchpad () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "Scratchpad.bin" ; }
2021-09-18 09:39:52 +10:00
u8 * GetDataPtr () const { return eeMem -> Scratch ; }
uint GetDataSize () const { return sizeof ( eeMem -> Scratch ); }
};
class SavestateEntry_VU0mem : public MemorySavestateEntry
{
public :
virtual ~ SavestateEntry_VU0mem () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "vu0Memory.bin" ; }
2021-09-18 09:39:52 +10:00
u8 * GetDataPtr () const { return vuRegs [ 0 ]. Mem ; }
uint GetDataSize () const { return VU0_MEMSIZE ; }
};
class SavestateEntry_VU1mem : public MemorySavestateEntry
{
public :
virtual ~ SavestateEntry_VU1mem () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "vu1Memory.bin" ; }
2021-09-18 09:39:52 +10:00
u8 * GetDataPtr () const { return vuRegs [ 1 ]. Mem ; }
uint GetDataSize () const { return VU1_MEMSIZE ; }
};
class SavestateEntry_VU0prog : public MemorySavestateEntry
{
public :
virtual ~ SavestateEntry_VU0prog () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "vu0MicroMem.bin" ; }
2021-09-18 09:39:52 +10:00
u8 * GetDataPtr () const { return vuRegs [ 0 ]. Micro ; }
uint GetDataSize () const { return VU0_PROGSIZE ; }
};
class SavestateEntry_VU1prog : public MemorySavestateEntry
{
public :
virtual ~ SavestateEntry_VU1prog () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "vu1MicroMem.bin" ; }
2021-09-18 09:39:52 +10:00
u8 * GetDataPtr () const { return vuRegs [ 1 ]. Micro ; }
uint GetDataSize () const { return VU1_PROGSIZE ; }
};
class SavestateEntry_SPU2 : public BaseSavestateEntry
{
public :
virtual ~ SavestateEntry_SPU2 () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "SPU2.bin" ; }
void FreezeIn ( zip_file_t * zf ) const { return SysState_ComponentFreezeIn ( zf , SPU2 ); }
2021-09-18 09:39:52 +10:00
void FreezeOut ( SaveStateBase & writer ) const { return SysState_ComponentFreezeOut ( writer , SPU2 ); }
bool IsRequired () const { return true ; }
};
class SavestateEntry_USB : public BaseSavestateEntry
{
public :
virtual ~ SavestateEntry_USB () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "USB.bin" ; }
2022-12-05 01:00:06 +10:00
void FreezeIn ( zip_file_t * zf ) const { return SysState_ComponentFreezeIn ( zf , USB_ ); }
void FreezeOut ( SaveStateBase & writer ) const { return SysState_ComponentFreezeOut ( writer , USB_ ); }
2021-11-04 21:21:57 -05:00
bool IsRequired () const { return false ; }
2021-09-18 09:39:52 +10:00
};
class SavestateEntry_PAD : public BaseSavestateEntry
{
public :
virtual ~ SavestateEntry_PAD () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "PAD.bin" ; }
void FreezeIn ( zip_file_t * zf ) const { return SysState_ComponentFreezeIn ( zf , PAD_ ); }
2021-12-07 19:21:46 +10:00
void FreezeOut ( SaveStateBase & writer ) const { return SysState_ComponentFreezeOut ( writer , PAD_ ); }
2021-09-18 09:39:52 +10:00
bool IsRequired () const { return true ; }
};
class SavestateEntry_GS : public BaseSavestateEntry
{
public :
virtual ~ SavestateEntry_GS () = default ;
2022-04-11 22:15:44 +10:00
const char * GetFilename () const { return "GS.bin" ; }
void FreezeIn ( zip_file_t * zf ) const { return SysState_ComponentFreezeIn ( zf , GS ); }
2021-09-18 09:39:52 +10:00
void FreezeOut ( SaveStateBase & writer ) const { return SysState_ComponentFreezeOut ( writer , GS ); }
bool IsRequired () const { return true ; }
};
2022-04-18 23:35:14 +10:00
#ifdef ENABLE_ACHIEVEMENTS
class SaveStateEntry_Achievements : public BaseSavestateEntry
{
virtual ~ SaveStateEntry_Achievements () override = default ;
2021-09-18 09:39:52 +10:00
2022-04-18 23:35:14 +10:00
const char * GetFilename () const override { return "Achievements.bin" ; }
void FreezeIn ( zip_file_t * zf ) const override
{
if ( ! Achievements :: IsActive ())
return ;
std :: optional < std :: vector < u8 >> data ;
if ( zf )
data = ReadBinaryFileInZip ( zf );
if ( data . has_value () && ! data -> empty ())
Achievements :: LoadState ( data -> data (), data -> size ());
else
Achievements :: LoadState ( nullptr , 0 );
}
void FreezeOut ( SaveStateBase & writer ) const override
{
if ( ! Achievements :: IsActive ())
return ;
std :: vector < u8 > data ( Achievements :: SaveState ());
if ( ! data . empty ())
{
writer . PrepBlock ( static_cast < int > ( data . size ()));
std :: memcpy ( writer . GetBlockPtr (), data . data (), data . size ());
writer . CommitBlock ( static_cast < int > ( data . size ()));
}
}
bool IsRequired () const override { return false ; }
};
#endif
2021-09-18 09:39:52 +10:00
// (cpuRegs, iopRegs, VPU/GIF/DMAC structures should all remain as part of a larger unified
// block, since they're all PCSX2-dependent and having separate files in the archie for them
// would not be useful).
//
static const std :: unique_ptr < BaseSavestateEntry > SavestateEntries [] = {
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_EmotionMemory ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_IopMemory ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_HwRegs ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_IopHwRegs ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_Scratchpad ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_VU0mem ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_VU1mem ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_VU0prog ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_VU1prog ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_SPU2 ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_USB ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_PAD ),
std :: unique_ptr < BaseSavestateEntry > ( new SavestateEntry_GS ),
2022-04-18 23:35:14 +10:00
#ifdef ENABLE_ACHIEVEMENTS
std :: unique_ptr < BaseSavestateEntry > ( new SaveStateEntry_Achievements ),
#endif
2021-09-18 09:39:52 +10:00
};
2022-04-11 22:15:44 +10:00
std :: unique_ptr < ArchiveEntryList > SaveState_DownloadState ()
2021-09-18 09:39:52 +10:00
{
2022-05-18 23:27:23 +10:00
std :: unique_ptr < ArchiveEntryList > destlist = std :: make_unique < ArchiveEntryList > ( new VmStateBuffer ( "Zippable Savestate" ));
2022-04-11 22:15:44 +10:00
2021-09-18 09:39:52 +10:00
memSavingState saveme ( destlist -> GetBuffer ());
ArchiveEntry internals ( EntryFilename_InternalStructures );
internals . SetDataIndex ( saveme . GetCurrentPos ());
saveme . FreezeBios ();
saveme . FreezeInternals ();
internals . SetDataSize ( saveme . GetCurrentPos () - internals . GetDataIndex ());
destlist -> Add ( internals );
2021-08-30 19:43:40 -05:00
for ( const std :: unique_ptr < BaseSavestateEntry >& entry : SavestateEntries )
2021-09-18 09:39:52 +10:00
{
uint startpos = saveme . GetCurrentPos ();
2021-08-30 19:43:40 -05:00
entry -> FreezeOut ( saveme );
destlist -> Add (
ArchiveEntry ( entry -> GetFilename ())
. SetDataIndex ( startpos )
. SetDataSize ( saveme . GetCurrentPos () - startpos ));
2021-09-18 09:39:52 +10:00
}
2022-04-11 22:15:44 +10:00
return destlist ;
2021-09-18 09:39:52 +10:00
}
2021-12-07 19:21:46 +10:00
std :: unique_ptr < SaveStateScreenshotData > SaveState_SaveScreenshot ()
{
static constexpr u32 SCREENSHOT_WIDTH = 640 ;
static constexpr u32 SCREENSHOT_HEIGHT = 480 ;
2022-12-10 19:11:08 +10:00
u32 width , height ;
std :: vector < u32 > pixels ;
if ( ! GetMTGS (). SaveMemorySnapshot ( SCREENSHOT_WIDTH , SCREENSHOT_HEIGHT , true , false , & width , & height , & pixels ))
2021-12-07 19:21:46 +10:00
{
// saving failed for some reason, device lost?
return nullptr ;
}
std :: unique_ptr < SaveStateScreenshotData > data = std :: make_unique < SaveStateScreenshotData > ();
2022-12-10 19:11:08 +10:00
data -> width = width ;
data -> height = height ;
2021-12-07 19:21:46 +10:00
data -> pixels = std :: move ( pixels );
return data ;
}
2022-04-11 22:15:44 +10:00
static bool SaveState_CompressScreenshot ( SaveStateScreenshotData * data , zip_t * zf )
2021-12-07 19:21:46 +10:00
{
2022-04-11 22:15:44 +10:00
zip_error_t ze = {};
zip_source_t * const zs = zip_source_buffer_create ( nullptr , 0 , 0 , & ze );
if ( ! zs )
return false ;
if ( zip_source_begin_write ( zs ) != 0 )
{
zip_source_free ( zs );
return false ;
}
ScopedGuard zs_free ([ zs ]() { zip_source_free ( zs ); });
2021-12-07 19:21:46 +10:00
png_structp png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING , nullptr , nullptr , nullptr );
png_infop info_ptr = nullptr ;
if ( ! png_ptr )
return false ;
2022-04-11 22:15:44 +10:00
ScopedGuard cleanup ([ & png_ptr , & info_ptr ]() {
2021-12-07 19:21:46 +10:00
if ( png_ptr )
png_destroy_write_struct ( & png_ptr , info_ptr ? & info_ptr : nullptr );
});
info_ptr = png_create_info_struct ( png_ptr );
if ( ! info_ptr )
return false ;
if ( setjmp ( png_jmpbuf ( png_ptr )))
return false ;
2022-04-11 22:15:44 +10:00
png_set_write_fn ( png_ptr , zs , []( png_structp png_ptr , png_bytep data_ptr , png_size_t size ) {
zip_source_write ( static_cast < zip_source_t *> ( png_get_io_ptr ( png_ptr )), data_ptr , size );
2021-12-07 19:21:46 +10:00
}, []( png_structp png_ptr ) {});
png_set_compression_level ( png_ptr , 5 );
png_set_IHDR ( png_ptr , info_ptr , data -> width , data -> height , 8 , PNG_COLOR_TYPE_RGBA ,
PNG_INTERLACE_NONE , PNG_COMPRESSION_TYPE_DEFAULT , PNG_FILTER_TYPE_DEFAULT );
png_write_info ( png_ptr , info_ptr );
for ( u32 y = 0 ; y < data -> height ; ++ y )
{
// ensure the alpha channel is set to opaque
u32 * row = & data -> pixels [ y * data -> width ];
for ( u32 x = 0 ; x < data -> width ; x ++ )
row [ x ] |= 0xFF000000u ;
png_write_row ( png_ptr , reinterpret_cast < png_bytep > ( row ));
}
png_write_end ( png_ptr , nullptr );
2022-04-11 22:15:44 +10:00
if ( zip_source_commit_write ( zs ) != 0 )
return false ;
const s64 file_index = zip_file_add ( zf , EntryFilename_Screenshot , zs , 0 );
if ( file_index < 0 )
return false ;
// png is already compressed, no point doing it twice
zip_set_file_compression ( zf , file_index , ZIP_CM_STORE , 0 );
// source is now owned by the zip file for later compression
zs_free . Cancel ();
2021-12-07 19:21:46 +10:00
return true ;
}
2022-05-08 16:45:38 +10:00
static bool SaveState_ReadScreenshot ( zip_t * zf , u32 * out_width , u32 * out_height , std :: vector < u32 >* out_pixels )
{
auto zff = zip_fopen_managed ( zf , EntryFilename_Screenshot , 0 );
if ( ! zff )
return false ;
png_structp png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING , nullptr , nullptr , nullptr );
if ( ! png_ptr )
return false ;
png_infop info_ptr = png_create_info_struct ( png_ptr );
if ( ! info_ptr )
{
png_destroy_read_struct ( & png_ptr , nullptr , nullptr );
return false ;
}
ScopedGuard cleanup ([ & png_ptr , & info_ptr ]() {
png_destroy_read_struct ( & png_ptr , & info_ptr , nullptr );
});
if ( setjmp ( png_jmpbuf ( png_ptr )))
return false ;
png_set_read_fn ( png_ptr , zff . get (), []( png_structp png_ptr , png_bytep data_ptr , png_size_t size ) {
zip_fread ( static_cast < zip_file_t *> ( png_get_io_ptr ( png_ptr )), data_ptr , size );
});
png_read_info ( png_ptr , info_ptr );
png_uint_32 width = 0 ;
png_uint_32 height = 0 ;
int bitDepth = 0 ;
int colorType = - 1 ;
if ( png_get_IHDR ( png_ptr , info_ptr , & width , & height , & bitDepth , & colorType , nullptr , nullptr , nullptr ) != 1 ||
width == 0 || height == 0 )
{
return false ;
}
const png_uint_32 bytesPerRow = png_get_rowbytes ( png_ptr , info_ptr );
std :: vector < u8 > rowData ( bytesPerRow );
* out_width = width ;
* out_height = height ;
out_pixels -> resize ( width * height );
for ( u32 y = 0 ; y < height ; y ++ )
{
png_read_row ( png_ptr , static_cast < png_bytep > ( rowData . data ()), nullptr );
const u8 * row_ptr = rowData . data ();
u32 * out_ptr = & out_pixels -> at ( y * width );
if ( colorType == PNG_COLOR_TYPE_RGB )
{
for ( u32 x = 0 ; x < width ; x ++ )
{
u32 pixel = static_cast < u32 > ( * ( row_ptr ) ++ );
pixel |= static_cast < u32 > ( * ( row_ptr ) ++ ) << 8 ;
pixel |= static_cast < u32 > ( * ( row_ptr ) ++ ) << 16 ;
pixel |= static_cast < u32 > ( * ( row_ptr ) ++ ) << 24 ;
* ( out_ptr ++ ) = pixel | 0xFF000000u ; // make opaque
}
}
else if ( colorType == PNG_COLOR_TYPE_RGBA )
{
for ( u32 x = 0 ; x < width ; x ++ )
{
u32 pixel ;
std :: memcpy ( & pixel , row_ptr , sizeof ( u32 ));
row_ptr += sizeof ( u32 );
* ( out_ptr ++ ) = pixel | 0xFF000000u ; // make opaque
}
}
}
return true ;
}
2021-09-18 09:39:52 +10:00
// --------------------------------------------------------------------------------------
// CompressThread_VmState
// --------------------------------------------------------------------------------------
2022-05-08 16:45:38 +10:00
static bool SaveState_AddToZip ( zip_t * zf , ArchiveEntryList * srclist , SaveStateScreenshotData * screenshot )
2021-09-18 09:39:52 +10:00
{
2022-04-12 18:41:22 +10:00
// use zstd compression, it can be 10x+ faster for saving.
const u32 compression = EmuConfig . SavestateZstdCompression ? ZIP_CM_ZSTD : ZIP_CM_DEFLATE ;
const u32 compression_level = 0 ;
2022-04-11 22:15:44 +10:00
// version indicator
{
2022-05-08 16:45:38 +10:00
zip_source_t * const zs = zip_source_buffer ( zf , & g_SaveVersion , sizeof ( g_SaveVersion ), 0 );
2022-04-11 22:15:44 +10:00
if ( ! zs )
2022-05-08 16:45:38 +10:00
return false ;
2022-04-11 22:15:44 +10:00
// NOTE: Source should not be freed if successful.
2022-05-08 16:45:38 +10:00
const s64 fi = zip_file_add ( zf , EntryFilename_StateVersion , zs , ZIP_FL_ENC_UTF_8 );
2022-04-11 22:15:44 +10:00
if ( fi < 0 )
{
zip_source_free ( zs );
2022-05-08 16:45:38 +10:00
return false ;
2022-04-11 22:15:44 +10:00
}
2022-05-08 16:45:38 +10:00
zip_set_file_compression ( zf , fi , ZIP_CM_STORE , 0 );
2022-04-11 22:15:44 +10:00
}
const uint listlen = srclist -> GetLength ();
2021-09-18 13:09:31 +10:00
for ( uint i = 0 ; i < listlen ; ++ i )
{
const ArchiveEntry & entry = ( * srclist )[ i ];
if ( ! entry . GetDataSize ())
continue ;
2021-09-18 09:39:52 +10:00
2022-05-08 16:45:38 +10:00
zip_source_t * const zs = zip_source_buffer ( zf , srclist -> GetPtr ( entry . GetDataIndex ()), entry . GetDataSize (), 0 );
2022-04-11 22:15:44 +10:00
if ( ! zs )
2022-05-08 16:45:38 +10:00
return false ;
2021-09-18 13:09:31 +10:00
2022-05-08 16:45:38 +10:00
const s64 fi = zip_file_add ( zf , entry . GetFilename (). c_str (), zs , ZIP_FL_ENC_UTF_8 );
2022-04-11 22:15:44 +10:00
if ( fi < 0 )
2021-09-18 13:09:31 +10:00
{
2022-04-11 22:15:44 +10:00
zip_source_free ( zs );
2022-05-08 16:45:38 +10:00
return false ;
2022-04-11 22:15:44 +10:00
}
2021-09-18 13:09:31 +10:00
2022-05-08 16:45:38 +10:00
zip_set_file_compression ( zf , fi , compression , compression_level );
2021-09-18 13:09:31 +10:00
}
2022-04-11 22:15:44 +10:00
if ( screenshot )
2022-05-08 16:45:38 +10:00
{
if ( ! SaveState_CompressScreenshot ( screenshot , zf ))
return false ;
}
2021-09-18 13:09:31 +10:00
2022-05-08 16:45:38 +10:00
return true ;
2021-09-18 13:09:31 +10:00
}
2022-05-08 16:45:38 +10:00
bool SaveState_ZipToDisk ( std :: unique_ptr < ArchiveEntryList > srclist , std :: unique_ptr < SaveStateScreenshotData > screenshot , const char * filename )
2021-09-18 13:09:31 +10:00
{
2022-05-08 16:45:38 +10:00
zip_error_t ze = {};
zip_source_t * zs = zip_source_file_create ( filename , 0 , 0 , & ze );
zip_t * zf = nullptr ;
if ( zs && ! ( zf = zip_open_from_source ( zs , ZIP_CREATE | ZIP_TRUNCATE , & ze )))
{
Console . Error ( "Failed to open zip file '%s' for save state: %s" , filename , zip_error_strerror ( & ze ));
// have to clean up source
zip_source_free ( zs );
return false ;
}
// discard zip file if we fail saving something
if ( ! SaveState_AddToZip ( zf , srclist . get (), screenshot . get ()))
{
Console . Error ( "Failed to save state to zip file '%s'" , filename );
zip_discard ( zf );
return false ;
}
// force the zip to close, this is the expensive part with libzip.
zip_close ( zf );
return true ;
}
bool SaveState_ReadScreenshot ( const std :: string & filename , u32 * out_width , u32 * out_height , std :: vector < u32 >* out_pixels )
{
zip_error_t ze = {};
auto zf = zip_open_managed ( filename . c_str (), ZIP_RDONLY , & ze );
if ( ! zf )
{
Console . Error ( "Failed to open zip file '%s' for save state screenshot: %s" , filename . c_str (), zip_error_strerror ( & ze ));
return false ;
}
return SaveState_ReadScreenshot ( zf . get (), out_width , out_height , out_pixels );
2021-09-18 09:39:52 +10:00
}
2022-04-11 22:15:44 +10:00
static void CheckVersion ( const std :: string & filename , zip_t * zf )
2021-09-18 09:39:52 +10:00
{
2022-04-11 22:15:44 +10:00
u32 savever ;
2021-09-18 09:39:52 +10:00
2022-04-11 22:15:44 +10:00
auto zff = zip_fopen_managed ( zf , EntryFilename_StateVersion , 0 );
if ( ! zff || zip_fread ( zff . get (), & savever , sizeof ( savever )) != sizeof ( savever ))
2021-09-18 09:39:52 +10:00
{
2022-05-18 23:27:23 +10:00
throw Exception :: SaveStateLoadError ( filename )
. SetDiagMsg ( "Savestate file does not contain version indicator." )
. SetUserMsg ( "This file is not a valid PCSX2 savestate. See the logfile for details." );
2022-04-11 22:15:44 +10:00
}
// Major version mismatch. Means we can't load this savestate at all. Support for it
// was removed entirely.
if ( savever > g_SaveVersion )
2022-05-18 23:27:23 +10:00
throw Exception :: SaveStateLoadError ( filename )
. SetDiagMsg ( fmt :: format ( "Savestate uses an unsupported or unknown savestate version. \n (PCSX2 ver={:x}, state ver={:x})" , g_SaveVersion , savever ))
2022-12-07 02:49:54 +01:00
. SetUserMsg ( "Cannot load this savestate. The state is an unsupported version. \n Option 1: Download an older PCSX2 version from pcsx2.net and make a memcard save like on the physical PS2. \n Option 2: Delete the savestates." );
2022-04-11 22:15:44 +10:00
// check for a "minor" version incompatibility; which happens if the savestate being loaded is a newer version
// than the emulator recognizes. 99% chance that trying to load it will just corrupt emulation or crash.
if (( savever >> 16 ) != ( g_SaveVersion >> 16 ))
2022-05-18 23:27:23 +10:00
throw Exception :: SaveStateLoadError ( filename )
. SetDiagMsg ( fmt :: format ( "Savestate uses an unknown savestate version. \n (PCSX2 ver={:x}, state ver={:x})" , g_SaveVersion , savever ))
2022-12-07 02:49:54 +01:00
. SetUserMsg ( "Cannot load this savestate. The state is an unsupported version. \n Option 1: Download an older PCSX2 version from pcsx2.net and make a memcard save like on the physical PS2. \n Option 2: Delete the savestates." );}
2022-04-11 22:15:44 +10:00
2022-04-18 23:35:14 +10:00
static zip_int64_t CheckFileExistsInState ( zip_t * zf , const char * name , bool required )
2022-04-11 22:15:44 +10:00
{
zip_int64_t index = zip_name_locate ( zf , name , /*ZIP_FL_NOCASE*/ 0 );
if ( index >= 0 )
{
DevCon . WriteLn ( Color_Green , " ... found '%s'" , name );
return index ;
}
2022-04-18 23:35:14 +10:00
if ( required )
Console . WriteLn ( Color_Red , " ... not found '%s'!" , name );
else
DevCon . WriteLn ( Color_Red , " ... not found '%s'!" , name );
2022-04-11 22:15:44 +10:00
return index ;
}
static bool LoadInternalStructuresState ( zip_t * zf , s64 index )
{
zip_stat_t zst ;
if ( zip_stat_index ( zf , index , 0 , & zst ) != 0 || zst . size > std :: numeric_limits < int >:: max ())
return false ;
// Load all the internal data
auto zff = zip_fopen_index_managed ( zf , index , 0 );
if ( ! zff )
return false ;
2022-05-18 23:27:23 +10:00
VmStateBuffer buffer ( static_cast < int > ( zst . size ), "StateBuffer_UnzipFromDisk" ); // start with an 8 meg buffer to avoid frequent reallocation.
2022-04-11 22:15:44 +10:00
if ( zip_fread ( zff . get (), buffer . GetPtr (), buffer . GetSizeInBytes ()) != buffer . GetSizeInBytes ())
return false ;
memLoadingState ( buffer ). FreezeBios (). FreezeInternals ();
return true ;
}
void SaveState_UnzipFromDisk ( const std :: string & filename )
{
zip_error_t ze = {};
auto zf = zip_open_managed ( filename . c_str (), ZIP_RDONLY , & ze );
if ( ! zf )
{
Console . Error ( "Failed to open zip file '%s' for save state load: %s" , filename . c_str (), zip_error_strerror ( & ze ));
2022-05-18 23:27:23 +10:00
throw Exception :: SaveStateLoadError ( filename )
. SetDiagMsg ( "Savestate file is not a valid gzip archive." )
. SetUserMsg ( "This savestate cannot be loaded because it is not a valid gzip archive. It may have been created by an older unsupported version of PCSX2, or it may be corrupted." );
2021-09-18 09:39:52 +10:00
}
// look for version and screenshot information in the zip stream:
2022-04-11 22:15:44 +10:00
CheckVersion ( filename , zf . get ());
2021-09-18 09:39:52 +10:00
2022-04-11 22:15:44 +10:00
// check that all parts are included
2022-04-18 23:35:14 +10:00
const s64 internal_index = CheckFileExistsInState ( zf . get (), EntryFilename_InternalStructures , true );
2022-04-11 22:15:44 +10:00
s64 entryIndices [ std :: size ( SavestateEntries )];
2021-09-18 09:39:52 +10:00
// Log any parts and pieces that are missing, and then generate an exception.
2022-04-11 22:15:44 +10:00
bool throwIt = ( internal_index < 0 );
for ( u32 i = 0 ; i < std :: size ( SavestateEntries ); i ++ )
2021-09-18 09:39:52 +10:00
{
2022-04-18 23:35:14 +10:00
const bool required = SavestateEntries [ i ] -> IsRequired ();
entryIndices [ i ] = CheckFileExistsInState ( zf . get (), SavestateEntries [ i ] -> GetFilename (), required );
if ( entryIndices [ i ] < 0 && required )
2021-09-18 09:39:52 +10:00
throwIt = true ;
2022-04-11 22:15:44 +10:00
}
if ( ! throwIt )
{
PreLoadPrep ();
throwIt = ! LoadInternalStructuresState ( zf . get (), internal_index );
}
if ( ! throwIt )
{
for ( u32 i = 0 ; i < std :: size ( SavestateEntries ); ++ i )
{
if ( entryIndices [ i ] < 0 )
2022-04-18 23:35:14 +10:00
{
SavestateEntries [ i ] -> FreezeIn ( nullptr );
2022-04-11 22:15:44 +10:00
continue ;
2022-04-18 23:35:14 +10:00
}
2022-04-11 22:15:44 +10:00
auto zff = zip_fopen_index_managed ( zf . get (), entryIndices [ i ], 0 );
if ( ! zff )
{
throwIt = true ;
break ;
}
SavestateEntries [ i ] -> FreezeIn ( zff . get ());
2021-09-18 09:39:52 +10:00
}
}
if ( throwIt )
2022-04-11 22:15:44 +10:00
{
2022-05-18 23:27:23 +10:00
throw Exception :: SaveStateLoadError ( filename )
. SetDiagMsg ( "Savestate cannot be loaded: some required components were not found or are incomplete." )
. SetUserMsg ( "This savestate cannot be loaded due to missing critical components. See the log file for details." );
2021-09-18 09:39:52 +10:00
}
2022-04-11 22:15:44 +10:00
PostLoadPrep ();
2021-11-04 21:21:57 -05:00
}