initial
This commit is contained in:
149
engine/ps3/ps3_saveutil_v2.h
Normal file
149
engine/ps3/ps3_saveutil_v2.h
Normal file
@@ -0,0 +1,149 @@
|
||||
//===== Copyright © 1996-2011, Valve Corporation, All rights reserved. ======//
|
||||
|
||||
#ifndef PS3_SAVEUTIL_V2_H
|
||||
#define PS3_SAVEUTIL_V2_H
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "tier1/utlbuffer.h"
|
||||
#include "common.h"
|
||||
#include "vstdlib/jobthread.h"
|
||||
|
||||
#include <sysutil/sysutil_userinfo.h>
|
||||
#include <sysutil/sysutil_savedata.h>
|
||||
#include <cell/sysmodule.h>
|
||||
#include <sys/fs_external.h>
|
||||
#include <ps3/saverestore_ps3_api_ui.h>
|
||||
#include <vjobs_interface.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern CPS3SaveRestoreAsyncStatus *g_pSaveUtilAsyncStatus;
|
||||
extern IPS3SaveSteamInfoProvider *g_pSteamInfoProvider;
|
||||
extern IThreadPool *g_pSaveUtilThreadPool;
|
||||
extern char const *g_pszSaveUtilContainerName;
|
||||
extern char const *g_pszSaveUtilSecureFileId;
|
||||
extern uint64 g_uiSteamCloudCryptoKey;
|
||||
|
||||
#define VALVE_CONTAINER_FILE_STEAM "STEAMDAT.BIN"
|
||||
|
||||
#define VALVE_CONTAINER_STRLEN 128
|
||||
#define VALVE_CONTAINER_FILENAME_LEN 64
|
||||
#define VALVE_CONTAINER_8_3_LEN 16
|
||||
#define VALVE_CONTAINER_COUNT 64
|
||||
#define VALVE_CONTAINER_FPARTS 2
|
||||
|
||||
void SaveUtilV2_Initialize( CPS3SaveRestoreAsyncStatus *pAsync, IPS3SaveSteamInfoProvider *pSteamInfoProvider, int nKBRequired );
|
||||
|
||||
void SaveUtilV2_Shutdown();
|
||||
bool SaveUtilV2_CanShutdown();
|
||||
|
||||
void SaveUtilV2_GetFileInfoSync( CUtlVector< IPS3SaveRestoreToUI::PS3SaveGameInfo_t > &saveGameInfos, bool bFindAll );
|
||||
|
||||
void SaveUtilV2_Write( CPS3SaveRestoreAsyncStatus *pAsync, const char *pSourcepath, const char *pScreenshotPath, const char *pComment );
|
||||
void SaveUtilV2_WriteAutosave( CPS3SaveRestoreAsyncStatus *pAsync,
|
||||
const char *pSourcePath, // eg "/dev_hdd1/tempsave/autosave.ps3.sav"
|
||||
const char *pComment, // the comment field for the new autosave.
|
||||
const unsigned int nMaxNumAutosaves );
|
||||
void SaveUtilV2_WriteCloudFile( CPS3SaveRestoreAsyncStatus *pAsync,
|
||||
const char *pSourcePath, // eg "/dev_hdd1/tempsave/autosave.ps3.sav"
|
||||
const unsigned int nMaxNumCloudFiles );
|
||||
|
||||
void SaveUtilV2_WriteSteamInfo( CPS3SaveRestoreAsyncStatus *pAsync );
|
||||
|
||||
void SaveUtilV2_Load( CPS3SaveRestoreAsyncStatus *pAsync, const char *pFilename, const char *pDestFullPath );
|
||||
|
||||
void SaveUtilV2_Delete( CPS3SaveRestoreAsyncStatus *pAsync, const char *pFilename );
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Helper definitions and declarations
|
||||
//
|
||||
|
||||
#define SONY_SAVEUTIL_STAT_PARAMS CellSaveDataCBResult *cbResult, CellSaveDataStatGet *get, CellSaveDataStatSet *set
|
||||
#define SONY_SAVEUTIL_FILE_PARAMS CellSaveDataCBResult *cbResult, CellSaveDataFileGet *get, CellSaveDataFileSet *set
|
||||
#define SONY_SAVEUTIL_PARAMS cbResult, get, set
|
||||
|
||||
class ISaveUtilV2Job : public CJob
|
||||
{
|
||||
public:
|
||||
ISaveUtilV2Job() { m_pfnDoDataFileCallback = 0; }
|
||||
|
||||
public:
|
||||
static void csDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS );
|
||||
static void csDataFileCallback( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
|
||||
public:
|
||||
virtual void DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS ) = 0;
|
||||
void (ISaveUtilV2Job::*m_pfnDoDataFileCallback)( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
template< typename T > inline void SetDataFileCallback( void (T::*pfnCallback)( SONY_SAVEUTIL_FILE_PARAMS ) ) { m_pfnDoDataFileCallback = reinterpret_cast< void (ISaveUtilV2Job::*)( SONY_SAVEUTIL_FILE_PARAMS ) >( pfnCallback ); }
|
||||
inline void SetDataFileCallbackFinalize() { m_pfnDoDataFileCallback = 0; }
|
||||
|
||||
public:
|
||||
CellSaveDataSetBuf m_SaveDirInfo;
|
||||
CUtlBuffer m_bufSaveDirList;
|
||||
};
|
||||
|
||||
bool SaveUtilV2_CanStartJob();
|
||||
void SaveUtilV2_EnqueueJob( CPS3SaveRestoreAsyncStatus *pAsync, ISaveUtilV2Job *pJob );
|
||||
JobStatus_t SaveUtilV2_JobDone( int nErrorCode );
|
||||
|
||||
uint32 SaveUtilV2_ComputeBufferHash( void const *pvData, uint32 numBytes );
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Container TOC
|
||||
//
|
||||
|
||||
class CSaveUtilV2ContainerTOC
|
||||
{
|
||||
public:
|
||||
CSaveUtilV2ContainerTOC() : m_idxNewSaveName( 0 ) {}
|
||||
|
||||
struct TocEntry_t
|
||||
{
|
||||
char m_chContainerName[VALVE_CONTAINER_8_3_LEN]; // 8.3 name inside container: 0000001A.SAV
|
||||
char m_chComment[VALVE_CONTAINER_STRLEN]; // description of file without needing to open file
|
||||
char m_chFile[VALVE_CONTAINER_FPARTS][VALVE_CONTAINER_FILENAME_LEN]; // names of the contained parts [sav+tga]
|
||||
uint32 m_numBytesFile[VALVE_CONTAINER_FPARTS]; // sizes of the parts inside container file [sav+tga]
|
||||
uint32 m_numBytesDecompressedFile[VALVE_CONTAINER_FPARTS]; // sizes of original decompressed parts [sav+tga], zero if uncompressed
|
||||
time_t m_timeModification; // timestamp of the container file
|
||||
};
|
||||
union TocStorageReserved_t
|
||||
{
|
||||
TocEntry_t m_entry;
|
||||
char m_chPadding[384];
|
||||
};
|
||||
CUtlVector< TocStorageReserved_t > m_arrEntries;
|
||||
CThreadFastMutex m_mtx;
|
||||
uint32 m_idxNewSaveName;
|
||||
|
||||
enum Capacity_t
|
||||
{
|
||||
kStorageCapacity =
|
||||
sizeof(uint32) + // new save name
|
||||
sizeof(uint32) + // number of valid entries
|
||||
VALVE_CONTAINER_COUNT*sizeof(TocStorageReserved_t) // actual TOC entries
|
||||
};
|
||||
|
||||
public:
|
||||
void SerializeIntoTocBuffer( void *pvBuffer );
|
||||
void SerializeFromTocBuffer( void *pvBuffer );
|
||||
void CopyInto( CSaveUtilV2ContainerTOC *pOther );
|
||||
int FindByEmbeddedFileName( char const *szFilename, int *pnPartIndex );
|
||||
};
|
||||
extern CSaveUtilV2ContainerTOC g_SaveUtilV2TOC;
|
||||
extern uint32 g_SaveUtilV2TOCVersion;
|
||||
|
||||
|
||||
class CSaveUtilVjobInstance : public VJobInstance
|
||||
{
|
||||
public:
|
||||
void Init();
|
||||
void Shutdown();
|
||||
};
|
||||
|
||||
extern CSaveUtilVjobInstance g_saveUtilVjobInstance;
|
||||
|
||||
#endif
|
||||
117
engine/ps3/ps3_saveutil_v2_delete.cpp
Normal file
117
engine/ps3/ps3_saveutil_v2_delete.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
//===== Copyright © 1996-2011, Valve Corporation, All rights reserved. ======//
|
||||
|
||||
#include "ps3_saveutil_v2.h"
|
||||
#include "memdbgon.h"
|
||||
|
||||
class CSaveUtilV2Job_Delete : public ISaveUtilV2Job
|
||||
{
|
||||
public: // Job entry point
|
||||
virtual JobStatus_t DoExecute();
|
||||
|
||||
public: // Data resolved from the main thread
|
||||
CSaveUtilV2ContainerTOC::TocEntry_t *m_pTocEntry;
|
||||
int m_nTocEntryIndex;
|
||||
|
||||
protected: // Stat callback
|
||||
virtual void DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS );
|
||||
|
||||
protected: // Delete the file and update TOC
|
||||
void DoDataFile_Delete( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
void DoDataFile_UpdateTOC( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SaveUtilV2_Delete( CPS3SaveRestoreAsyncStatus *pAsync, const char *pFilename )
|
||||
{
|
||||
if ( !SaveUtilV2_CanStartJob() )
|
||||
return;
|
||||
|
||||
// Find the file that the caller wants
|
||||
int k = g_SaveUtilV2TOC.FindByEmbeddedFileName( pFilename, NULL );
|
||||
if ( ( k < 0 ) || ( k >= g_SaveUtilV2TOC.m_arrEntries.Count() ) )
|
||||
{
|
||||
pAsync->m_nSonyRetValue = CELL_SAVEDATA_ERROR_FAILURE;
|
||||
pAsync->m_bDone = 1;
|
||||
Warning( "ERROR: SaveUtilV2_Delete: attempted to delete file '%s' which doesn't exist in container!\n", pFilename );
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the job
|
||||
CSaveUtilV2Job_Delete *pJob = new CSaveUtilV2Job_Delete;
|
||||
// It is safe to hold this pointer into the TOC for the duration of this job
|
||||
// Only jobs update TOC and this is the current job, main thread accesses TOC for read only
|
||||
pJob->m_pTocEntry = &g_SaveUtilV2TOC.m_arrEntries[k].m_entry;
|
||||
pJob->m_nTocEntryIndex = k;
|
||||
|
||||
SaveUtilV2_EnqueueJob( pAsync, pJob );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobStatus_t CSaveUtilV2Job_Delete::DoExecute()
|
||||
{
|
||||
float flTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Delete @%.3f\n", flTimeStamp );
|
||||
|
||||
// Call saveutil
|
||||
int retv = cellSaveDataAutoSave2(
|
||||
CELL_SAVEDATA_VERSION_CURRENT,
|
||||
g_pszSaveUtilContainerName,
|
||||
g_pSaveUtilAsyncStatus->m_bUseSystemDialogs ? CELL_SAVEDATA_ERRDIALOG_ALWAYS : CELL_SAVEDATA_ERRDIALOG_NONE,
|
||||
&m_SaveDirInfo,
|
||||
csDataStatCallback,
|
||||
csDataFileCallback,
|
||||
SYS_MEMORY_CONTAINER_ID_INVALID,
|
||||
this );
|
||||
|
||||
float flEndTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Delete: cellSaveDataAutoSave2 returned %x @%.3f ( total time = %.3f sec )\n", retv, flEndTimeStamp, flEndTimeStamp - flTimeStamp );
|
||||
|
||||
++ g_SaveUtilV2TOCVersion;
|
||||
return SaveUtilV2_JobDone( retv );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Delete::DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Delete::DoDataStatCallback @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Delete::DoDataFile_Delete );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Delete::DoDataFile_Delete( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Delete::DoDataFile_Delete @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Perform the delete
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_DELETE;
|
||||
set->fileBuf = NULL;
|
||||
set->fileSize = set->fileBufSize = 0;
|
||||
set->fileName = m_pTocEntry->m_chContainerName;
|
||||
set->fileOffset = 0;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_SECUREFILE;
|
||||
memcpy( set->secureFileId, g_pszSaveUtilSecureFileId, CELL_SAVEDATA_SECUREFILEID_SIZE );
|
||||
set->reserved = NULL;
|
||||
|
||||
// final write
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Delete::DoDataFile_UpdateTOC );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_Delete::DoDataFile_Delete will delete '%s'...\n", m_pTocEntry->m_chContainerName );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Delete::DoDataFile_UpdateTOC( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Delete::DoDataFile_UpdateTOC @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Update TOC since we successfully deleted the file
|
||||
{
|
||||
AUTO_LOCK( g_SaveUtilV2TOC.m_mtx );
|
||||
// Deleting an entry doesn't reallocate the memory
|
||||
g_SaveUtilV2TOC.m_arrEntries.Remove( m_nTocEntryIndex );
|
||||
}
|
||||
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_LAST;
|
||||
}
|
||||
|
||||
649
engine/ps3/ps3_saveutil_v2_initialize.cpp
Normal file
649
engine/ps3/ps3_saveutil_v2_initialize.cpp
Normal file
@@ -0,0 +1,649 @@
|
||||
//===== Copyright © 1996-2011, Valve Corporation, All rights reserved. ======//
|
||||
|
||||
#include "ps3_saveutil_v2.h"
|
||||
#include "vgui/ILocalize.h"
|
||||
#include "vstdlib/vstrtools.h"
|
||||
#include "memdbgon.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CPS3SaveRestoreAsyncStatus *g_pSaveUtilAsyncStatus = NULL;
|
||||
IPS3SaveSteamInfoProvider *g_pSteamInfoProvider = NULL;
|
||||
IThreadPool *g_pSaveUtilThreadPool = NULL;
|
||||
CSaveUtilVjobInstance g_saveUtilVjobInstance;
|
||||
|
||||
static char g_chSaveUtilContainerName[64];
|
||||
char const *g_pszSaveUtilContainerName = g_chSaveUtilContainerName;
|
||||
CSaveUtilV2ContainerTOC g_SaveUtilV2TOC;
|
||||
uint32 g_SaveUtilV2TOCVersion;
|
||||
extern IVJobs * g_pVJobs;
|
||||
|
||||
// this is our "SECURE FILE ID"
|
||||
static const char s_SaveUtilSecureFileId[CELL_SAVEDATA_SECUREFILEID_SIZE] =
|
||||
{ 0xDD, 0xE3, 0x80, 0x72, 0xC7, 0x9F, 0xAF, 0x2A, 0x2B, 0x68, 0xAF, 0xC9, 0x6D, 0x6A, 0xED, 0xC1 } ;
|
||||
char const *g_pszSaveUtilSecureFileId = s_SaveUtilSecureFileId;
|
||||
uint64 g_uiSteamCloudCryptoKey = 0ull;
|
||||
|
||||
ASSERT_INVARIANT( sizeof( CSaveUtilV2ContainerTOC::TocEntry_t ) < sizeof( CSaveUtilV2ContainerTOC::TocStorageReserved_t ) );
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class CSaveUtilV2Job_Initialize : public ISaveUtilV2Job
|
||||
{
|
||||
public: // Job entry point
|
||||
virtual JobStatus_t DoExecute();
|
||||
|
||||
public: // Caller passes data from main thread
|
||||
int m_nKBRequired;
|
||||
|
||||
protected: // Our buffer for interacting with filesystem
|
||||
CUtlBuffer m_bufScratch;
|
||||
CSaveUtilV2ContainerTOC m_newTOC;
|
||||
CUtlVector< CellSaveDataFileStat > m_arrFilesInContainer;
|
||||
|
||||
protected: // Stat callback
|
||||
virtual void DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS );
|
||||
bool DoDataStat_ValidateFreeSpace( SONY_SAVEUTIL_STAT_PARAMS );
|
||||
void DoDataStat_NewContainer( SONY_SAVEUTIL_STAT_PARAMS );
|
||||
|
||||
protected: // When Steam file exists we load it first
|
||||
CellSaveDataFileStat m_LoadSteamFileStat;
|
||||
void DoDataFile_LoadSteam( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
|
||||
protected: // When container exists we load TOC
|
||||
CellSaveDataFileStat m_LoadTocFileStat;
|
||||
void DoDataFile_LoadToc( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
void DoDataFile_DisplayToc( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
|
||||
protected: // TOC post-processing
|
||||
bool PostProcessToc_DeletedFiles();
|
||||
bool PostProcessToc_Validate();
|
||||
|
||||
protected: // When container is newly created we write initial data
|
||||
void DoDataFile_WriteEmptySteamFile( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
void DoDataFile_WriteIcon0png( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
void DoDataFile_WriteIcon1pam( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
void DoDataFile_WritePic1png( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void SaveUtilV2_InitAndClearSaveShadow()
|
||||
{
|
||||
char const *pszSaveDir = g_pPS3PathInfo->SaveShadowPath();
|
||||
g_pFullFileSystem->CreateDirHierarchy( pszSaveDir );
|
||||
// delete any files that are in the save dir already
|
||||
FileFindHandle_t handle;
|
||||
const char *pfname;
|
||||
char searchpath[255];
|
||||
V_snprintf( searchpath, sizeof(searchpath), "%s*", pszSaveDir );
|
||||
char deletepath[255];
|
||||
const int dirnamelen = V_strlen( pszSaveDir );
|
||||
Assert(dirnamelen < 255);
|
||||
memcpy(deletepath, pszSaveDir, dirnamelen);
|
||||
for ( pfname = g_pFullFileSystem->FindFirst( searchpath, &handle ) ;
|
||||
pfname ;
|
||||
pfname = g_pFullFileSystem->FindNext( handle ) )
|
||||
{
|
||||
V_strncpy( deletepath + dirnamelen, pfname, sizeof(deletepath) - dirnamelen );
|
||||
Msg( "Removing %s\n", deletepath );
|
||||
g_pFullFileSystem->RemoveFile( deletepath, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
void SaveUtilV2_Initialize( CPS3SaveRestoreAsyncStatus *pAsync, IPS3SaveSteamInfoProvider *pSteamInfoProvider, int nKBRequired )
|
||||
{
|
||||
if ( g_pSaveUtilThreadPool )
|
||||
return;
|
||||
|
||||
// Initialize SPU jobs instance
|
||||
g_saveUtilVjobInstance.Init();
|
||||
|
||||
// Indicate that we are running V2
|
||||
V_snprintf( g_chSaveUtilContainerName, 32, "%s-%s", g_pPS3PathInfo->GetWWMASTER_TitleID(), "PORTAL2-AUTOSAVE3" );
|
||||
SaveUtilV2_InitAndClearSaveShadow();
|
||||
|
||||
// First of all start our thread pool
|
||||
ThreadPoolStartParams_t params;
|
||||
params.nThreads = 1;
|
||||
params.nStackSize = 64*1024;
|
||||
params.fDistribute = TRS_FALSE;
|
||||
g_pSaveUtilThreadPool = CreateNewThreadPool();
|
||||
g_pSaveUtilThreadPool->Start( params, "SaveUtilV2" );
|
||||
|
||||
// Remember launch parameters
|
||||
g_pSteamInfoProvider = pSteamInfoProvider;
|
||||
|
||||
// Start the job
|
||||
CSaveUtilV2Job_Initialize *pJob = new CSaveUtilV2Job_Initialize;
|
||||
pJob->m_nKBRequired = nKBRequired;
|
||||
|
||||
SaveUtilV2_EnqueueJob( pAsync, pJob );
|
||||
}
|
||||
|
||||
void SaveUtilV2_Shutdown()
|
||||
{
|
||||
if ( !g_pSaveUtilThreadPool )
|
||||
return;
|
||||
|
||||
g_pSaveUtilThreadPool->Stop();
|
||||
DestroyThreadPool( g_pSaveUtilThreadPool );
|
||||
g_pSaveUtilThreadPool = NULL;
|
||||
|
||||
// Shutdown jobs instance after thread pool has been
|
||||
// stopped which ensures that jobs were finished and released
|
||||
g_saveUtilVjobInstance.Shutdown();
|
||||
}
|
||||
|
||||
bool SaveUtilV2_CanShutdown()
|
||||
{
|
||||
if ( !g_pSaveUtilThreadPool )
|
||||
return true;
|
||||
|
||||
if ( g_pSaveUtilAsyncStatus )
|
||||
// job in progress
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobStatus_t CSaveUtilV2Job_Initialize::DoExecute()
|
||||
{
|
||||
float flTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Initialize( %d KB ) @%.3f\n", m_nKBRequired, flTimeStamp );
|
||||
|
||||
// Prepare data for calling saveutil (we cannot allocate memory in the callbacks!)
|
||||
m_bufScratch.EnsureCapacity( 5 * 1024 * 1024 ); // we always have 5 MB on startup
|
||||
|
||||
m_newTOC.m_arrEntries.EnsureCapacity( VALVE_CONTAINER_COUNT );
|
||||
m_arrFilesInContainer.EnsureCapacity( VALVE_CONTAINER_COUNT );
|
||||
|
||||
// Call saveutil
|
||||
int retv = cellSaveDataAutoSave2(
|
||||
CELL_SAVEDATA_VERSION_CURRENT,
|
||||
g_pszSaveUtilContainerName,
|
||||
CELL_SAVEDATA_ERRDIALOG_NONE,
|
||||
&m_SaveDirInfo,
|
||||
csDataStatCallback,
|
||||
csDataFileCallback,
|
||||
SYS_MEMORY_CONTAINER_ID_INVALID,
|
||||
this );
|
||||
|
||||
// Set the global TOC as loaded
|
||||
{
|
||||
AUTO_LOCK( g_SaveUtilV2TOC.m_mtx );
|
||||
m_newTOC.CopyInto( &g_SaveUtilV2TOC );
|
||||
}
|
||||
|
||||
// Job finished
|
||||
float flEndTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Initialize: cellSaveDataAutoSave2 returned %x @%.3f ( total time = %.3f sec )\n", retv, flEndTimeStamp, flEndTimeStamp - flTimeStamp );
|
||||
|
||||
return SaveUtilV2_JobDone( retv );
|
||||
}
|
||||
|
||||
|
||||
void CSaveUtilV2Job_Initialize::DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataStatCallback @%.3f\n", Plat_FloatTime() );
|
||||
Msg("\tnKBRequired %d\n", m_nKBRequired );
|
||||
Msg("\tisNewData %d\n", get->isNewData );
|
||||
Msg("\thddFreeSizeKB %d\n", get->hddFreeSizeKB);
|
||||
Msg("\tsizeKB %d\n", get->sizeKB);
|
||||
Msg("\tsysSizeKB %d\n", get->sysSizeKB);
|
||||
Msg("\tbind %d\n", get->bind);
|
||||
|
||||
bool bCreateNew = false; // do we need to create a new container?
|
||||
if ( get->isNewData || get->bind != CELL_SAVEDATA_BINDSTAT_OK )
|
||||
{
|
||||
if ( get->isNewData )
|
||||
{ // if the data is just absent, we can always make a new container.
|
||||
bCreateNew = true;
|
||||
}
|
||||
else if ( ( get->bind & ( CELL_SAVEDATA_BINDSTAT_ERR_NOACCOUNTID | CELL_SAVEDATA_BINDSTAT_ERR_LOCALOWNER ) ) == get->bind )
|
||||
{
|
||||
// this is actually owned by the current user; since the account
|
||||
// id will added on the next write, we can safely keep going.
|
||||
get->bind = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is an ownership error
|
||||
set->reCreateMode = CELL_SAVEDATA_RECREATE_NO;
|
||||
g_pSaveUtilAsyncStatus->m_nSonyRetValue = CPS3SaveRestoreAsyncStatus::CELL_SAVEDATA_ERROR_WRONG_USER;
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_ERR_INVALID;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
set->setParam = NULL; // don't edit the PARAM.SFO
|
||||
|
||||
// Validate free space
|
||||
if ( !DoDataStat_ValidateFreeSpace( SONY_SAVEUTIL_PARAMS ) )
|
||||
return;
|
||||
|
||||
// if there's a toc, read it.
|
||||
if ( !get->isNewData )
|
||||
{
|
||||
V_memset( &m_LoadSteamFileStat, 0, sizeof( m_LoadSteamFileStat ) );
|
||||
V_memset( &m_LoadTocFileStat, 0, sizeof( m_LoadTocFileStat ) );
|
||||
|
||||
for ( int i = 0 ; i < get->fileListNum ; ++i )
|
||||
{
|
||||
// Icons and video have special filetypes, we are interested only in our data
|
||||
// our data has file type "securefile"
|
||||
if ( get->fileList[i].fileType != CELL_SAVEDATA_FILETYPE_SECUREFILE )
|
||||
continue;
|
||||
|
||||
// Steam configuration file
|
||||
if ( !V_strcmp( get->fileList[i].fileName, VALVE_CONTAINER_FILE_STEAM ) )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize found Steam file: %u bytes\n", get->fileList[i].st_size );
|
||||
V_memcpy( &m_LoadSteamFileStat, &get->fileList[i], sizeof( m_LoadSteamFileStat ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Every file will have full TOC prepended, makes no sense
|
||||
// to have files in container that are smaller than required size
|
||||
if ( get->fileList[i].st_size < CSaveUtilV2ContainerTOC::kStorageCapacity )
|
||||
continue;
|
||||
|
||||
// Remember the file as present in container
|
||||
m_arrFilesInContainer.AddToTail( get->fileList[i] );
|
||||
|
||||
// Add this file for TOC discovery (the latest TOC will be at the start of the file with the alphanumeric-highest filename)
|
||||
Msg( "CSaveUtilV2Job_Initialize found file '%s', %u bytes\n", get->fileList[i].fileName, get->fileList[i].st_size );
|
||||
if ( !m_LoadTocFileStat.fileName[0] || // TOC filename not set yet
|
||||
( V_strcmp( get->fileList[i].fileName, m_LoadTocFileStat.fileName ) > 0 ) ) // or bigger filename ( -> more recent file)
|
||||
{
|
||||
V_memcpy( &m_LoadTocFileStat, &get->fileList[i], sizeof( m_LoadTocFileStat ) );
|
||||
}
|
||||
}
|
||||
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Initialize::DoDataFile_LoadSteam );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
return;
|
||||
}
|
||||
else if ( bCreateNew )
|
||||
{
|
||||
// Create a new container
|
||||
DoDataStat_NewContainer( SONY_SAVEUTIL_PARAMS );
|
||||
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Initialize::DoDataFile_WriteEmptySteamFile );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
return;
|
||||
}
|
||||
|
||||
// this means "okay, I'm done"
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_LAST;
|
||||
}
|
||||
|
||||
bool CSaveUtilV2Job_Initialize::DoDataStat_ValidateFreeSpace( SONY_SAVEUTIL_STAT_PARAMS )
|
||||
{
|
||||
// do we have enough space?
|
||||
// system overhead + caller max size ? HDD free space + current container file
|
||||
int numKbRequired = ( get->sysSizeKB + m_nKBRequired ) - ( get->hddFreeSizeKB + get->sizeKB );
|
||||
if ( numKbRequired > 0 )
|
||||
{
|
||||
// inadequate space.
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_ERR_NOSPACE;
|
||||
g_pSaveUtilAsyncStatus->m_nSonyRetValue = CELL_SAVEDATA_ERROR_NOSPACE;
|
||||
cbResult->errNeedSizeKB = numKbRequired;
|
||||
g_pSaveUtilAsyncStatus->m_uiAdditionalDetails = numKbRequired;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CSaveUtilV2Job_Initialize::DoDataStat_NewContainer( SONY_SAVEUTIL_STAT_PARAMS )
|
||||
{
|
||||
set->reCreateMode = CELL_SAVEDATA_RECREATE_YES_RESET_OWNER;
|
||||
get->bind = 0;
|
||||
|
||||
// the convention for editing param.sfo is pointing at the struct in the get param, and then editing that. (jeez...)
|
||||
set->setParam = &get->getParam;
|
||||
|
||||
/* Set parameters of PARAM.SFO */
|
||||
V_strncpy( set->setParam->title, g_pPS3PathInfo->GetParamSFO_Title(), CELL_SAVEDATA_SYSP_TITLE_SIZE );
|
||||
|
||||
// the save game title
|
||||
wchar_t *szLocalizedSaveDataTitle = g_pLocalize->Find( "#CSGOPS3_SaveData" );
|
||||
|
||||
if ( szLocalizedSaveDataTitle )
|
||||
{
|
||||
V_UnicodeToUTF8( szLocalizedSaveDataTitle, set->setParam->subTitle, CELL_SAVEDATA_SYSP_SUBTITLE_SIZE );
|
||||
}
|
||||
else // failsafe
|
||||
{
|
||||
V_strncpy( set->setParam->subTitle, "Counter Strike: Global Offensive" , CELL_SAVEDATA_SYSP_SUBTITLE_SIZE );
|
||||
}
|
||||
|
||||
// the save game caption -- if missing, we just use an empty string here.
|
||||
szLocalizedSaveDataTitle = g_pLocalize->Find( "#CSGOPS3_SaveDetail" );
|
||||
if ( szLocalizedSaveDataTitle )
|
||||
{
|
||||
V_UnicodeToUTF8( szLocalizedSaveDataTitle, set->setParam->detail, CELL_SAVEDATA_SYSP_DETAIL_SIZE );
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( set->setParam->detail, 0, CELL_SAVEDATA_SYSP_DETAIL_SIZE );
|
||||
}
|
||||
|
||||
set->setParam->attribute = CELL_SAVEDATA_ATTR_NORMAL;
|
||||
|
||||
// listparam is available to the application inside stat callback
|
||||
Q_memset( set->setParam->listParam, 0, CELL_SAVEDATA_SYSP_LPARAM_SIZE );
|
||||
|
||||
memset( set->setParam->reserved, 0x0, sizeof(set->setParam->reserved) ); /* The reserved member must be zero-filled */
|
||||
memset( set->setParam->reserved2, 0x0, sizeof(set->setParam->reserved2) ); /* The reserved member must be zero-filled */
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Initialize::DoDataFile_WriteEmptySteamFile( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_WriteEmptySteamFile @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
const static char szEmptyToc[] = "EMPTY";
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_WRITE;
|
||||
set->fileBuf = const_cast<char *>(szEmptyToc);
|
||||
set->fileSize = set->fileBufSize = sizeof(szEmptyToc);
|
||||
set->fileName = VALVE_CONTAINER_FILE_STEAM;
|
||||
set->fileOffset = 0;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_SECUREFILE;
|
||||
memcpy( set->secureFileId, g_pszSaveUtilSecureFileId, CELL_SAVEDATA_SECUREFILEID_SIZE );
|
||||
set->reserved = NULL;
|
||||
|
||||
// call back again to write icon
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Initialize::DoDataFile_WriteIcon0png );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_WriteEmptySteamFile will write %d bytes...\n", set->fileSize );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Initialize::DoDataFile_WriteIcon0png( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_WriteIcon0png @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Load ICON0.PS3.PNG
|
||||
m_bufScratch.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
|
||||
if ( !g_pFullFileSystem->ReadFile( "ps3/ICON0.PS3.PNG", "GAME", m_bufScratch ) )
|
||||
{
|
||||
DoDataFile_WriteIcon1pam( SONY_SAVEUTIL_PARAMS );
|
||||
return;
|
||||
}
|
||||
|
||||
// Write it
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_WRITE;
|
||||
set->fileBuf = m_bufScratch.Base();
|
||||
set->fileSize = set->fileBufSize = m_bufScratch.TellPut();
|
||||
set->fileOffset = 0;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_CONTENT_ICON0;
|
||||
set->reserved = NULL;
|
||||
|
||||
// call back again to write icon
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Initialize::DoDataFile_WriteIcon1pam );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_WriteIcon0png will write %d bytes...\n", set->fileSize );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Initialize::DoDataFile_WriteIcon1pam( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_WriteIcon1pam @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Load ICON1.PS3.PAM
|
||||
m_bufScratch.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
|
||||
|
||||
#if defined( CSTRIKE15 )
|
||||
|
||||
// TODO($msmith): Remove this and put the PAM back in for CS:GO once it has been updated.
|
||||
if ( !g_pFullFileSystem->ReadFile( "ps3/not_found.PAM", "GAME", m_bufScratch ) )
|
||||
|
||||
#else
|
||||
|
||||
if ( !g_pFullFileSystem->ReadFile( "ps3/ICON1.PS3.PAM", "GAME", m_bufScratch ) )
|
||||
|
||||
#endif
|
||||
|
||||
{
|
||||
DoDataFile_WritePic1png( SONY_SAVEUTIL_PARAMS );
|
||||
return;
|
||||
}
|
||||
|
||||
// Write it
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_WRITE;
|
||||
set->fileBuf = m_bufScratch.Base();
|
||||
set->fileSize = set->fileBufSize = m_bufScratch.TellPut();
|
||||
set->fileOffset = 0;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_CONTENT_ICON1;
|
||||
set->reserved = NULL;
|
||||
|
||||
// call back again to write pic1
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Initialize::DoDataFile_WritePic1png );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_WriteIcon1pam will write %d bytes...\n", set->fileSize );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Initialize::DoDataFile_WritePic1png( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_WritePic1png @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Load PIC1.PS3.PNG
|
||||
m_bufScratch.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
|
||||
if ( !g_pFullFileSystem->ReadFile( "ps3/PIC1.PS3.PNG", "GAME", m_bufScratch ) )
|
||||
{
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_LAST;
|
||||
return;
|
||||
}
|
||||
|
||||
// Write it
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_WRITE;
|
||||
set->fileBuf = m_bufScratch.Base();
|
||||
set->fileSize = set->fileBufSize = m_bufScratch.TellPut();
|
||||
set->fileOffset = 0;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_CONTENT_PIC1;
|
||||
set->reserved = NULL;
|
||||
|
||||
// final write
|
||||
SetDataFileCallbackFinalize();
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_WritePic1png will write %d bytes...\n", set->fileSize );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CSaveUtilV2Job_Initialize::DoDataFile_LoadSteam( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_LoadSteam @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Check that Steam file is present
|
||||
if ( m_LoadSteamFileStat.st_size < 16 )
|
||||
{
|
||||
DoDataFile_LoadToc( SONY_SAVEUTIL_PARAMS );
|
||||
return;
|
||||
}
|
||||
|
||||
// Read into Steam buffer
|
||||
CUtlBuffer *pBuffer = g_pSteamInfoProvider->GetInitialLoadBuffer();
|
||||
if ( !pBuffer || ( pBuffer->Size() < m_LoadSteamFileStat.st_size ) )
|
||||
{
|
||||
Warning( "ERROR: CSaveUtilV2Job_Initialize::DoDataFile_LoadSteam: cannot load Steam config (size %llu bytes)!\n", m_LoadSteamFileStat.st_size );
|
||||
DoDataFile_LoadToc( SONY_SAVEUTIL_PARAMS );
|
||||
return;
|
||||
}
|
||||
|
||||
// Read Steam info file
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_READ;
|
||||
set->fileBuf = pBuffer->Base();
|
||||
set->fileSize = m_LoadSteamFileStat.st_size;
|
||||
set->fileBufSize = pBuffer->Size();
|
||||
set->fileName = VALVE_CONTAINER_FILE_STEAM;
|
||||
set->fileOffset = 0;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_SECUREFILE;
|
||||
memcpy( set->secureFileId, g_pszSaveUtilSecureFileId, CELL_SAVEDATA_SECUREFILEID_SIZE );
|
||||
set->reserved = NULL;
|
||||
|
||||
// Set the buffer size
|
||||
pBuffer->SeekPut( CUtlBuffer::SEEK_HEAD, set->fileSize );
|
||||
|
||||
// final read
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Initialize::DoDataFile_LoadToc );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_LoadSteam will load %d bytes...\n", set->fileSize );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Initialize::DoDataFile_LoadToc( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_LoadToc @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
if ( m_LoadTocFileStat.st_size < CSaveUtilV2ContainerTOC::kStorageCapacity )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_LoadToc -- no TOC available.\n" );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_LAST;
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the TOC entry
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_READ;
|
||||
set->fileBuf = m_bufScratch.Base();
|
||||
set->fileBufSize = set->fileSize = CSaveUtilV2ContainerTOC::kStorageCapacity;
|
||||
set->fileName = m_LoadTocFileStat.fileName;
|
||||
set->fileOffset = 0;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_SECUREFILE;
|
||||
memcpy( set->secureFileId, g_pszSaveUtilSecureFileId, CELL_SAVEDATA_SECUREFILEID_SIZE );
|
||||
set->reserved = NULL;
|
||||
|
||||
m_bufScratch.SeekPut( CUtlBuffer::SEEK_HEAD, CSaveUtilV2ContainerTOC::kStorageCapacity );
|
||||
|
||||
// keep reading
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Initialize::DoDataFile_DisplayToc );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_LoadToc will load %u bytes of TOC from '%s'...\n", set->fileSize, set->fileName );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Initialize::DoDataFile_DisplayToc( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::DoDataFile_DisplayToc @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
m_newTOC.SerializeFromTocBuffer( m_bufScratch.Base() );
|
||||
bool bTocValidationResult = PostProcessToc_DeletedFiles();
|
||||
|
||||
for ( int k = 0; k < m_newTOC.m_arrEntries.Count(); ++ k )
|
||||
{
|
||||
Msg( " %s %s %d/%d %s\n",
|
||||
m_newTOC.m_arrEntries[k].m_entry.m_chContainerName,
|
||||
m_newTOC.m_arrEntries[k].m_entry.m_chFile[0],
|
||||
m_newTOC.m_arrEntries[k].m_entry.m_numBytesFile[0],
|
||||
m_newTOC.m_arrEntries[k].m_entry.m_numBytesDecompressedFile[0],
|
||||
m_newTOC.m_arrEntries[k].m_entry.m_chComment
|
||||
);
|
||||
}
|
||||
Msg( " new save game will have index %u\n", m_newTOC.m_idxNewSaveName + 1 );
|
||||
Msg( " END OF TOC REPORT\n" );
|
||||
|
||||
if ( !PostProcessToc_Validate() || !bTocValidationResult )
|
||||
{
|
||||
// TOC is not matching contents of the container!
|
||||
Warning( " ERROR: TOC DOES NOT MATCH SAVE CONTAINER!\n" );
|
||||
g_pSaveUtilAsyncStatus->m_nSonyRetValue = CELL_SAVEDATA_CBRESULT_ERR_BROKEN;
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_ERR_BROKEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_LAST;
|
||||
}
|
||||
}
|
||||
|
||||
bool CSaveUtilV2Job_Initialize::PostProcessToc_DeletedFiles()
|
||||
{
|
||||
// Iterate over TOC and remove entries that were deleted from container previously
|
||||
bool bTocAndContainerValid = true;
|
||||
for ( int k = m_newTOC.m_arrEntries.Count(); k --> 0; )
|
||||
{
|
||||
bool bFoundInContainer = false;
|
||||
int jc = 0;
|
||||
for ( ; jc < m_arrFilesInContainer.Count(); ++ jc )
|
||||
{
|
||||
if ( !V_strcmp( m_newTOC.m_arrEntries[k].m_entry.m_chContainerName, m_arrFilesInContainer[jc].fileName ) )
|
||||
{
|
||||
bFoundInContainer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !bFoundInContainer )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::PostProcessToc_DeletedFiles discovered deleted file '%s'\n", m_newTOC.m_arrEntries[k].m_entry.m_chContainerName );
|
||||
m_newTOC.m_arrEntries.Remove( k );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure the TOC information matches container stat information
|
||||
int nExpectedSize = CSaveUtilV2ContainerTOC::kStorageCapacity;
|
||||
for ( int ipart = 0; ipart < VALVE_CONTAINER_FPARTS; ++ ipart )
|
||||
nExpectedSize += m_newTOC.m_arrEntries[k].m_entry.m_numBytesFile[ipart];
|
||||
if ( m_arrFilesInContainer[jc].st_size != nExpectedSize )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::PostProcessToc_DeletedFiles discovered size inconsistency in '%s' (TOC size = %d; Container size = %llu)\n",
|
||||
m_newTOC.m_arrEntries[k].m_entry.m_chContainerName, nExpectedSize, m_arrFilesInContainer[jc].st_size );
|
||||
bTocAndContainerValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bTocAndContainerValid;
|
||||
}
|
||||
|
||||
bool CSaveUtilV2Job_Initialize::PostProcessToc_Validate()
|
||||
{
|
||||
// Iterate over TOC and make sure every container file is in TOC
|
||||
for ( int jc = m_arrFilesInContainer.Count(); jc --> 0; )
|
||||
{
|
||||
bool bFoundInTOC = false;
|
||||
for ( int k = 0; k < m_newTOC.m_arrEntries.Count(); ++ k )
|
||||
{
|
||||
if ( !V_strcmp( m_newTOC.m_arrEntries[k].m_entry.m_chContainerName, m_arrFilesInContainer[jc].fileName ) )
|
||||
{
|
||||
bFoundInTOC = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( bFoundInTOC )
|
||||
{
|
||||
m_arrFilesInContainer.Remove( jc );
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Initialize::PostProcessToc_Validate discovered extra file '%s'\n", m_arrFilesInContainer[jc].fileName );
|
||||
}
|
||||
}
|
||||
|
||||
// We shouldn't have any extra unaccounted files in the container
|
||||
return ( !m_arrFilesInContainer.Count() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void CSaveUtilVjobInstance::Init()
|
||||
{
|
||||
g_pVJobs->Register( this );
|
||||
}
|
||||
|
||||
|
||||
void CSaveUtilVjobInstance::Shutdown()
|
||||
{
|
||||
g_pVJobs->Unregister( this );
|
||||
}
|
||||
|
||||
205
engine/ps3/ps3_saveutil_v2_interface.cpp
Normal file
205
engine/ps3/ps3_saveutil_v2_interface.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
//===== Copyright © 1996-2011, Valve Corporation, All rights reserved. ======//
|
||||
|
||||
#include "ps3_saveutil_v2.h"
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
// the interface class
|
||||
class CPS3SaveRestoreToUI : public CTier2AppSystem< IPS3SaveRestoreToUI >
|
||||
{
|
||||
public: // IAppSystem
|
||||
virtual bool Connect( CreateInterfaceFn factory );
|
||||
virtual void Disconnect();
|
||||
virtual void *QueryInterface( const char *pInterfaceName );
|
||||
virtual InitReturnVal_t Init();
|
||||
virtual void Shutdown();
|
||||
|
||||
public: // IPS3SaveRestoreToUI
|
||||
/// You have to call this before doing any other save operation. In particular,
|
||||
/// there may be an error opening the container. Poll on Async, and when it's done,
|
||||
/// look in the return value to see if it succeeded, or if not, why not.
|
||||
/// When bCreateIfMissing is set, it will create a new container where none exists.
|
||||
virtual void Initialize( CPS3SaveRestoreAsyncStatus *pAsync, IPS3SaveSteamInfoProvider *pSteamInfoProvider, bool bCreateIfMissing, int nKBRequired )
|
||||
{
|
||||
SaveUtilV2_Initialize( pAsync, pSteamInfoProvider, nKBRequired );
|
||||
}
|
||||
|
||||
// Save the given file into the container. (ie /dev_hdd1/tempsave/foo.ps3.sav will
|
||||
// be written into the container as foo.ps3.sav ). You can optionally specify a second
|
||||
// file to be written at the same time, eg a screenshot, because batching two writes
|
||||
// to happen at once is far far faster than having two batches one after another.
|
||||
// ALL game progress files must be
|
||||
// written as secure; for now, even the screenshots should be, as that puts the work
|
||||
// of CRC onto the operating system.
|
||||
virtual void Write( CPS3SaveRestoreAsyncStatus *pAsync, const char *pSourcepath, const char *pScreenshotPath, const char *pComment, FileSecurity_t nSecurity = kSECURE )
|
||||
{
|
||||
SaveUtilV2_Write( pAsync, pSourcepath, pScreenshotPath, pComment );
|
||||
}
|
||||
|
||||
// A more complicated and intelligent form of save writing, specifically for the case of autosaves.
|
||||
// The source filename given (as an absolute path) will be written to "autosave.ps3.sav".
|
||||
// Meanwhile, the existing "autosave.ps3.sav" will be renamed "autosave01.ps3.sav",
|
||||
// any "autosave01.ps3.sav" will be renamed "autosave02.ps3.sav", and so on up to a maximum
|
||||
// number of autosaves N, plus the base autosave.ps3.sav. The highest "autosave%02d.ps3.sav"
|
||||
// will therefore have a number N. Excess autosaves with numbers >N will be deleted.
|
||||
// If you specify a ScreenshotExtension (such as "tga"), the same operation is performed
|
||||
// for every file above, where ."sav" is replaced with the given extension.
|
||||
virtual void WriteAutosave( CPS3SaveRestoreAsyncStatus *pAsync,
|
||||
const char *pSourcePath, // eg "/dev_hdd1/tempsave/autosave.ps3.sav"
|
||||
const char *pComment, // the comment field for the new autosave.
|
||||
const unsigned int nMaxNumAutosaves ) // should be at least 1; the highest numbered autosave will be N-1.
|
||||
{
|
||||
SaveUtilV2_WriteAutosave( pAsync, pSourcePath, pComment, nMaxNumAutosaves );
|
||||
}
|
||||
|
||||
// A way of writing clouded files into container, clouded files over
|
||||
// a certain age are purged from container
|
||||
virtual void WriteCloudFile( CPS3SaveRestoreAsyncStatus *pAsync,
|
||||
const char *pSourcePath,
|
||||
const unsigned int nMaxNumCloudFiles ) // should be at least 1; the highest numbered cloud file will be N-1.
|
||||
{
|
||||
SaveUtilV2_WriteCloudFile( pAsync, pSourcePath, nMaxNumCloudFiles );
|
||||
}
|
||||
|
||||
// Load a file from the container into the given directory.
|
||||
// give it a pointer to a CPS3SaveRestoreAsyncStatus struct that you have created and
|
||||
// intend to poll.
|
||||
virtual void Load( CPS3SaveRestoreAsyncStatus *pAsync, const char *pFilename, const char *pDestFullPath )
|
||||
{
|
||||
SaveUtilV2_Load( pAsync, pFilename, pDestFullPath );
|
||||
}
|
||||
|
||||
// kill one or two files (eg, save and screenshot).
|
||||
// async will respond when done.
|
||||
virtual void Delete( CPS3SaveRestoreAsyncStatus *pAsync, const char *pFilename, const char *pOtherFilename = NULL )
|
||||
{
|
||||
SaveUtilV2_Delete( pAsync, pFilename );
|
||||
}
|
||||
|
||||
// synchronously retrieve information on the files in the container. Lacks some of the container-wide'
|
||||
// info of the above function, and may have slightly out of date information, but is a synchronous call
|
||||
// and returns precisely the structure needed by CBaseModPanel::GetSaveGameInfos().
|
||||
virtual void GetFileInfoSync( CUtlVector< PS3SaveGameInfo_t > &saveGameInfos, bool bFindAll )
|
||||
{
|
||||
SaveUtilV2_GetFileInfoSync( saveGameInfos, bFindAll );
|
||||
}
|
||||
|
||||
// try to find Steam's schema file for the user and stuff it into the container.
|
||||
// returns false if it couldn't find the file locally (in which case you should
|
||||
// not wait for the async object to be "done", as the job wasn't initiated);
|
||||
// true if it found it locally and queued up an async job to write it.
|
||||
virtual void WriteSteamInfo( CPS3SaveRestoreAsyncStatus *pAsync )
|
||||
{
|
||||
SaveUtilV2_WriteSteamInfo( pAsync );
|
||||
}
|
||||
|
||||
// returns whether save thread is busy
|
||||
virtual bool IsSaveUtilBusy()
|
||||
{
|
||||
return !!g_pSaveUtilAsyncStatus;
|
||||
}
|
||||
|
||||
// returns the m_nCurrentOperationTag field of the most recent async op to
|
||||
// have run, or kSAVE_TAG_UNKNOWN if none has been enqueued yet. This tag
|
||||
// changes the moment a job is made active and remains until the next job
|
||||
// starts.
|
||||
virtual uint32 GetCurrentOpTag()
|
||||
{
|
||||
CPS3SaveRestoreAsyncStatus *pAsync = g_pSaveUtilAsyncStatus;
|
||||
if ( pAsync )
|
||||
return pAsync->m_nCurrentOperationTag;
|
||||
else
|
||||
return kSAVE_TAG_UNKNOWN;
|
||||
}
|
||||
|
||||
// returns the version of container, used to fire off events when container
|
||||
// contents changes.
|
||||
virtual uint32 GetContainerModificationVersion()
|
||||
{
|
||||
return g_SaveUtilV2TOCVersion;
|
||||
}
|
||||
|
||||
// sets the cloud crypto key.
|
||||
virtual void SetCloudFileCryptoKey( uint64 uiCloudCryptoKey )
|
||||
{
|
||||
g_uiSteamCloudCryptoKey = uiCloudCryptoKey;
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CPS3SaveRestoreToUI g_PS3SaveToUI;
|
||||
|
||||
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CPS3SaveRestoreToUI, IPS3SaveRestoreToUI,
|
||||
IPS3SAVEUIAPI_VERSION_STRING, g_PS3SaveToUI );
|
||||
|
||||
|
||||
static CreateInterfaceFn s_pfnDelegateFactory;
|
||||
static void * InternalFactory( const char *pName, int *pReturnCode )
|
||||
{
|
||||
if ( pReturnCode )
|
||||
{
|
||||
*pReturnCode = IFACE_OK;
|
||||
}
|
||||
|
||||
// Try to get interface via delegate
|
||||
if ( void *pInterface = s_pfnDelegateFactory ? s_pfnDelegateFactory( pName, pReturnCode ) : NULL )
|
||||
{
|
||||
return pInterface;
|
||||
}
|
||||
|
||||
// Try to get internal interface
|
||||
if ( void *pInterface = Sys_GetFactoryThis()( pName, pReturnCode ) )
|
||||
{
|
||||
return pInterface;
|
||||
}
|
||||
|
||||
// Failed
|
||||
if ( pReturnCode )
|
||||
{
|
||||
*pReturnCode = IFACE_FAILED;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool CPS3SaveRestoreToUI::Connect( CreateInterfaceFn factory )
|
||||
{
|
||||
Assert( !s_pfnDelegateFactory );
|
||||
|
||||
s_pfnDelegateFactory = factory;
|
||||
|
||||
CreateInterfaceFn ourFactory = InternalFactory;
|
||||
ConnectTier1Libraries( &ourFactory, 1 );
|
||||
ConnectTier2Libraries( &ourFactory, 1 );
|
||||
|
||||
s_pfnDelegateFactory = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPS3SaveRestoreToUI::Disconnect()
|
||||
{
|
||||
DisconnectTier2Libraries();
|
||||
DisconnectTier1Libraries();
|
||||
}
|
||||
|
||||
void * CPS3SaveRestoreToUI::QueryInterface( const char *pInterfaceName )
|
||||
{
|
||||
if ( !Q_stricmp( pInterfaceName, IPS3SAVEUIAPI_VERSION_STRING ) )
|
||||
return static_cast< IPS3SaveRestoreToUI * >( this );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
InitReturnVal_t CPS3SaveRestoreToUI::Init()
|
||||
{
|
||||
return INIT_OK;
|
||||
}
|
||||
|
||||
void CPS3SaveRestoreToUI::Shutdown()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
298
engine/ps3/ps3_saveutil_v2_load.cpp
Normal file
298
engine/ps3/ps3_saveutil_v2_load.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
//===== Copyright © 1996-2011, Valve Corporation, All rights reserved. ======//
|
||||
|
||||
#include "ps3_saveutil_v2.h"
|
||||
#include "memdbgon.h"
|
||||
#include <vjobs/jobparams_shared.h>
|
||||
#include <vjobs/root.h>
|
||||
#include <ps3/vjobutils.h>
|
||||
|
||||
|
||||
class CSaveUtilV2Job_Load : public ISaveUtilV2Job
|
||||
{
|
||||
public: // Job entry point
|
||||
virtual JobStatus_t DoExecute();
|
||||
|
||||
public: // Data passed from main thread
|
||||
char m_chFileName[VALVE_CONTAINER_FILENAME_LEN];
|
||||
char m_chFullPathOut[MAX_PATH];
|
||||
bool m_bForCloud;
|
||||
|
||||
public: // Data resolved from the main thread
|
||||
CSaveUtilV2ContainerTOC::TocEntry_t *m_pTocEntry;
|
||||
int m_nSubFileIndex;
|
||||
job_zlibinflate::JobDescriptor_t * m_pJobInflate;
|
||||
|
||||
protected: // Data used for loading file contents
|
||||
CUtlBuffer m_bufScratch;
|
||||
int WriteFile( char const *szFile );
|
||||
|
||||
protected: // Stat callback
|
||||
virtual void DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS );
|
||||
|
||||
protected: // Load and write to disk
|
||||
void DoDataFile_LoadToBuffer( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
void DoDataFile_WriteToDisk( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SaveUtilV2_Load( CPS3SaveRestoreAsyncStatus *pAsync, const char *pFilename, const char *pDestFullPath )
|
||||
{
|
||||
if ( !SaveUtilV2_CanStartJob() )
|
||||
return;
|
||||
|
||||
// Find the file that the caller wants
|
||||
int nSubFileIndex = -1;
|
||||
int k = g_SaveUtilV2TOC.FindByEmbeddedFileName( pFilename, &nSubFileIndex );
|
||||
if ( ( nSubFileIndex < 0 ) || ( k < 0 ) || ( k >= g_SaveUtilV2TOC.m_arrEntries.Count() ) )
|
||||
{
|
||||
pAsync->m_nSonyRetValue = CELL_SAVEDATA_ERROR_FAILURE;
|
||||
pAsync->m_bDone = 1;
|
||||
Warning( "ERROR: SaveUtilV2_Load: attempted to load file '%s' which doesn't exist in container!\n", pFilename );
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the job
|
||||
CSaveUtilV2Job_Load *pJob = new CSaveUtilV2Job_Load;
|
||||
V_strncpy( pJob->m_chFileName, pFilename, sizeof( pJob->m_chFileName ) );
|
||||
|
||||
pJob->m_bForCloud = false;
|
||||
switch ( pDestFullPath[0] )
|
||||
{
|
||||
case '@':
|
||||
pJob->m_bForCloud = true;
|
||||
++ pDestFullPath;
|
||||
break;
|
||||
}
|
||||
|
||||
V_strncpy( pJob->m_chFullPathOut, pDestFullPath, sizeof( pJob->m_chFullPathOut ) );
|
||||
pJob->m_pTocEntry = &g_SaveUtilV2TOC.m_arrEntries[k].m_entry;
|
||||
pJob->m_nSubFileIndex = nSubFileIndex;
|
||||
|
||||
SaveUtilV2_EnqueueJob( pAsync, pJob );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobStatus_t CSaveUtilV2Job_Load::DoExecute()
|
||||
{
|
||||
float flTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Load @%.3f\n", flTimeStamp );
|
||||
|
||||
// Allocate required buffer
|
||||
if ( m_bForCloud )
|
||||
{
|
||||
int numBytesRequired = sizeof( CSaveUtilV2ContainerTOC::TocStorageReserved_t );
|
||||
for ( int iPart = 0; iPart < VALVE_CONTAINER_FPARTS; ++ iPart )
|
||||
numBytesRequired += m_pTocEntry->m_numBytesFile[iPart];
|
||||
m_bufScratch.EnsureCapacity( numBytesRequired );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bufScratch.EnsureCapacity( m_pTocEntry->m_numBytesFile[m_nSubFileIndex] + m_pTocEntry->m_numBytesDecompressedFile[m_nSubFileIndex] );
|
||||
}
|
||||
|
||||
m_pJobInflate = NewJob128( *g_saveUtilVjobInstance.m_pRoot->m_pJobZlibInflate );
|
||||
m_pJobInflate->header.sizeScratch = ( 16 * 1024 ) / 16 ;
|
||||
|
||||
// Call saveutil
|
||||
int retv = cellSaveDataAutoSave2(
|
||||
CELL_SAVEDATA_VERSION_CURRENT,
|
||||
g_pszSaveUtilContainerName,
|
||||
CELL_SAVEDATA_ERRDIALOG_NONE,
|
||||
&m_SaveDirInfo,
|
||||
csDataStatCallback,
|
||||
csDataFileCallback,
|
||||
SYS_MEMORY_CONTAINER_ID_INVALID,
|
||||
this );
|
||||
|
||||
DeleteJob( m_pJobInflate );
|
||||
|
||||
float flEndTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Load: cellSaveDataAutoSave2 returned %x @%.3f ( total time = %.3f sec )\n", retv, flEndTimeStamp, flEndTimeStamp - flTimeStamp );
|
||||
|
||||
return SaveUtilV2_JobDone( retv );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Load::DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Load::DoDataStatCallback @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Load::DoDataFile_LoadToBuffer );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Load::DoDataFile_LoadToBuffer( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Load::DoDataFile_LoadToBuffer @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Load the file contents
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_READ;
|
||||
set->fileName = m_pTocEntry->m_chContainerName;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_SECUREFILE;
|
||||
memcpy( set->secureFileId, g_pszSaveUtilSecureFileId, CELL_SAVEDATA_SECUREFILEID_SIZE );
|
||||
set->reserved = NULL;
|
||||
|
||||
set->fileOffset = CSaveUtilV2ContainerTOC::kStorageCapacity;
|
||||
if ( m_bForCloud )
|
||||
{
|
||||
set->fileSize = 0;
|
||||
for ( int iPart = 0; iPart < VALVE_CONTAINER_FPARTS; ++ iPart )
|
||||
set->fileSize += m_pTocEntry->m_numBytesFile[iPart];
|
||||
m_bufScratch.SeekPut( CUtlBuffer::SEEK_HEAD, sizeof( CSaveUtilV2ContainerTOC::TocStorageReserved_t ) + set->fileSize );
|
||||
set->fileBuf = ( ( uint8 * ) m_bufScratch.Base() ) + sizeof( CSaveUtilV2ContainerTOC::TocStorageReserved_t );
|
||||
set->fileBufSize = m_bufScratch.Size() - sizeof( CSaveUtilV2ContainerTOC::TocStorageReserved_t );
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int k = 0; k < m_nSubFileIndex; ++ k )
|
||||
set->fileOffset += m_pTocEntry->m_numBytesFile[k];
|
||||
set->fileSize = m_pTocEntry->m_numBytesFile[m_nSubFileIndex];
|
||||
m_bufScratch.SeekPut( CUtlBuffer::SEEK_HEAD, set->fileSize );
|
||||
set->fileBuf = m_bufScratch.Base();
|
||||
set->fileBufSize = m_bufScratch.Size();
|
||||
}
|
||||
|
||||
// keep reading
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Load::DoDataFile_WriteToDisk );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_Load::DoDataFile_LoadToBuffer will load %u bytes of '%s' from '%s'...\n", set->fileSize, m_chFileName, set->fileName );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Load::DoDataFile_WriteToDisk( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Load::DoDataFile_WriteToDisk '%s' @%.3f\n", m_chFileName, Plat_FloatTime() );
|
||||
|
||||
int ret = WriteFile( m_chFullPathOut );
|
||||
if ( ret < 0 )
|
||||
{
|
||||
Msg( "ERROR: CSaveUtilV2Job_Load::DoDataFile_WriteToDisk failed to write file to disk!\n" );
|
||||
g_pSaveUtilAsyncStatus->m_nSonyRetValue = CELL_SAVEDATA_ERROR_FAILURE;
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_ERR_FAILURE;
|
||||
return;
|
||||
}
|
||||
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_LAST;
|
||||
}
|
||||
|
||||
int CSaveUtilV2Job_Load::WriteFile( char const *szFile )
|
||||
{
|
||||
if ( !szFile || !*szFile )
|
||||
return 0;
|
||||
|
||||
float flTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Load::WriteFile : %s @%.3f\n", szFile, flTimeStamp );
|
||||
|
||||
unsigned char *pWriteData = ( unsigned char * ) m_bufScratch.Base();
|
||||
unsigned int numBytesWrite = m_pTocEntry->m_numBytesFile[m_nSubFileIndex]; // the compressed size
|
||||
if ( m_bForCloud )
|
||||
{
|
||||
numBytesWrite = m_bufScratch.TellPut();
|
||||
V_memcpy( pWriteData, m_pTocEntry, sizeof( CSaveUtilV2ContainerTOC::TocStorageReserved_t ) );
|
||||
|
||||
//
|
||||
// Signature
|
||||
//
|
||||
|
||||
// Generate sult into filename field
|
||||
CSaveUtilV2ContainerTOC::TocEntry_t *pSignature = ( CSaveUtilV2ContainerTOC::TocEntry_t * ) pWriteData;
|
||||
for ( int isult = 0; isult < sizeof( pSignature->m_chFile[0] ); ++ isult )
|
||||
pSignature->m_chFile[0][isult] = ( 1 + rand() ) % 220;
|
||||
|
||||
// Put the version of our save header
|
||||
V_memset( pSignature->m_chContainerName, 0, sizeof( pSignature->m_chContainerName ) );
|
||||
pSignature->m_chContainerName[0] = 'S';
|
||||
pSignature->m_chContainerName[1] = 'A';
|
||||
pSignature->m_chContainerName[2] = 'V';
|
||||
pSignature->m_chContainerName[3] = '1';
|
||||
|
||||
// Temporarily put our cryptokey in place of hash
|
||||
V_memcpy( pWriteData + 8, &g_uiSteamCloudCryptoKey, sizeof( g_uiSteamCloudCryptoKey ) );
|
||||
uint32 uiHash = SaveUtilV2_ComputeBufferHash( pWriteData, numBytesWrite );
|
||||
|
||||
// Store the hash
|
||||
for ( int isult = 0; isult < sizeof( g_uiSteamCloudCryptoKey ) - sizeof( uiHash ); ++ isult )
|
||||
pWriteData[8 + isult] = ( 1 + rand() ) % 220;
|
||||
V_memcpy( pWriteData + 8 + sizeof( g_uiSteamCloudCryptoKey ) - sizeof( uiHash ), &uiHash, sizeof( uiHash ) );
|
||||
}
|
||||
else if ( m_pTocEntry->m_numBytesDecompressedFile[m_nSubFileIndex] )
|
||||
{
|
||||
// The file is actually compressed
|
||||
|
||||
if( g_saveUtilVjobInstance.m_pRoot )
|
||||
{
|
||||
double flStartInflateJob = Plat_FloatTime();
|
||||
|
||||
job_zlibinflate::JobParams_t * pJobParams = job_zlibinflate::GetJobParams( m_pJobInflate );
|
||||
|
||||
pJobParams->m_eaUncompressedOutput = pWriteData + numBytesWrite;
|
||||
pJobParams->m_eaCompressed = pWriteData;
|
||||
pJobParams->m_nCompressedSize = numBytesWrite;
|
||||
pJobParams->m_nExpectedUncompressedSize = m_pTocEntry->m_numBytesDecompressedFile[m_nSubFileIndex];
|
||||
|
||||
int nError = g_saveUtilVjobInstance.m_pRoot->m_queuePortSound.pushJob( &m_pJobInflate->header, sizeof( *m_pJobInflate ), 0, CELL_SPURS_JOBQUEUE_FLAG_SYNC_JOB );
|
||||
if( nError != CELL_OK )
|
||||
{
|
||||
Warning("job_zlibinflate failed to push through port, error 0x%X\n", nError );
|
||||
return -1;
|
||||
}
|
||||
|
||||
while( !pJobParams->IsDone() )
|
||||
{
|
||||
ThreadSleep( 1 );
|
||||
}
|
||||
|
||||
double flEndInflateJob = Plat_FloatTime();
|
||||
|
||||
if( pJobParams->m_nError != 0 )
|
||||
{
|
||||
Warning( "CSaveUtilV2Job_Load::WriteFile failed to uncompress!\n" );
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "job_zlibInflate took %.3f sec : %u -> %u KiB (%.2f MiB/s)\n", flEndInflateJob - flStartInflateJob, pJobParams->m_nCompressedSize/1024, pJobParams->m_nExpectedUncompressedSize/1024, pJobParams->m_nExpectedUncompressedSize / ( 1024 * 1024 * ( flEndInflateJob - flStartInflateJob ) ) );
|
||||
|
||||
pWriteData += numBytesWrite;
|
||||
numBytesWrite = m_pTocEntry->m_numBytesDecompressedFile[m_nSubFileIndex];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
ret = cellFsOpen( szFile, CELL_FS_O_CREAT | CELL_FS_O_TRUNC | CELL_FS_O_WRONLY, &fd, NULL, 0 );
|
||||
if ( ret < 0 )
|
||||
{
|
||||
Msg( "ERROR: CSaveUtilV2Job_Load::DoDataFile_WriteToDisk : %s : cellFsOpen failed : %d\n", szFile, ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t numBytesActuallyWritten = 0;
|
||||
ret = cellFsWrite( fd, pWriteData, numBytesWrite, &numBytesActuallyWritten );
|
||||
cellFsClose( fd );
|
||||
if ( ret < 0 )
|
||||
{
|
||||
Msg( "ERROR: CSaveUtilV2Job_Load::DoDataFile_WriteToDisk : %s : cellFsWrite failed : %d\n", szFile, ret );
|
||||
return ret;
|
||||
}
|
||||
if ( numBytesActuallyWritten != numBytesWrite )
|
||||
{
|
||||
Msg( "ERROR: CSaveUtilV2Job_Load::DoDataFile_WriteToDisk : %s : cellFsWrite wrote incorrect file : %ull bytes written, %d bytes expected\n",
|
||||
szFile, numBytesActuallyWritten, numBytesWrite );
|
||||
return -1;
|
||||
}
|
||||
|
||||
float flEndTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Load::WriteFile finished writing %s @%.3f (%.3f sec)\n", szFile, flEndTimeStamp, flEndTimeStamp - flTimeStamp );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
589
engine/ps3/ps3_saveutil_v2_save.cpp
Normal file
589
engine/ps3/ps3_saveutil_v2_save.cpp
Normal file
@@ -0,0 +1,589 @@
|
||||
//===== Copyright © 1996-2011, Valve Corporation, All rights reserved. ======//
|
||||
|
||||
#include "ps3_saveutil_v2.h"
|
||||
#include "memdbgon.h"
|
||||
#include <vjobs/jobparams_shared.h>
|
||||
#include <vjobs/root.h>
|
||||
#include <ps3/vjobutils.h>
|
||||
|
||||
ConVar ps3_saveutil2_compress( "ps3_saveutil2_compress", "1", FCVAR_DEVELOPMENTONLY );
|
||||
|
||||
class CCellFsFileDescriptorAutoClose
|
||||
{
|
||||
public:
|
||||
CCellFsFileDescriptorAutoClose( int fd = -1 ) : m_fd( fd ), m_pJobDeflate( NULL ) {}
|
||||
~CCellFsFileDescriptorAutoClose() { Close(); EndDeflate(); }
|
||||
void Close()
|
||||
{
|
||||
if ( m_fd != -1 )
|
||||
{
|
||||
cellFsClose( m_fd ); m_fd = -1;
|
||||
}
|
||||
}
|
||||
int m_fd;
|
||||
job_zlibdeflate::JobDescriptor_t * m_pJobDeflate;
|
||||
|
||||
void BeginDeflate()
|
||||
{
|
||||
m_pJobDeflate = NewJob128( *g_saveUtilVjobInstance.m_pRoot->m_pJobZlibDeflate );
|
||||
m_pJobDeflate->header.sizeScratch = ( 32 * 1024 ) / 16 ;
|
||||
job_zlibdeflate::GetJobParams( m_pJobDeflate )->m_nStatus = 2; // status: the job isn't queued yet, so it's considered "done"
|
||||
}
|
||||
|
||||
void EndDeflate()
|
||||
{
|
||||
if( m_pJobDeflate )
|
||||
{
|
||||
while( !job_zlibdeflate::GetJobParams( m_pJobDeflate )->IsDone() )
|
||||
{
|
||||
ThreadSleep( 1 );
|
||||
}
|
||||
DeleteJob( m_pJobDeflate );
|
||||
m_pJobDeflate = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CSaveUtilV2Job_Save : public ISaveUtilV2Job
|
||||
{
|
||||
public: // Job entry point
|
||||
virtual JobStatus_t DoExecute();
|
||||
|
||||
public: // Data passed from main thread
|
||||
char m_chComment[VALVE_CONTAINER_STRLEN];
|
||||
char m_chFile[VALVE_CONTAINER_FPARTS][VALVE_CONTAINER_STRLEN];
|
||||
int m_numAutoSavesCount;
|
||||
int m_numCloudFiles;
|
||||
|
||||
protected: // Buffer used for file data
|
||||
CSaveUtilV2ContainerTOC m_newTOC;
|
||||
CUtlBuffer m_bufFiles;
|
||||
|
||||
CSaveUtilV2ContainerTOC::TocStorageReserved_t *m_pNewTOCEntry;
|
||||
struct OverwriteRequest_t
|
||||
{
|
||||
char m_chOverwriteContainerFile[VALVE_CONTAINER_8_3_LEN];
|
||||
};
|
||||
CUtlVector< OverwriteRequest_t > m_arrOverwriteRequests;
|
||||
OverwriteRequest_t m_owrDelete;
|
||||
|
||||
CCellFsFileDescriptorAutoClose m_fd[VALVE_CONTAINER_FPARTS];
|
||||
int OpenAndStatFiles();
|
||||
int LoadAndCompressFileData();
|
||||
void PrepareToc();
|
||||
|
||||
protected: // Stat callback
|
||||
virtual void DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS );
|
||||
|
||||
protected: // Write the file and update TOC
|
||||
void DoDataFile_DeleteOldFile( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
void DoDataFile_WriteFiles( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
void DoDataFile_UpdateTOC( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SaveUtilV2_Write( CPS3SaveRestoreAsyncStatus *pAsync, const char *pSourcepath, const char *pScreenshotPath, const char *pComment )
|
||||
{
|
||||
if ( !SaveUtilV2_CanStartJob() )
|
||||
return;
|
||||
|
||||
// Start the job
|
||||
CSaveUtilV2Job_Save *pJob = new CSaveUtilV2Job_Save;
|
||||
V_strncpy( pJob->m_chComment, pComment ? pComment : "", sizeof( pJob->m_chComment ) );
|
||||
V_strncpy( pJob->m_chFile[0], pSourcepath ? pSourcepath : "", sizeof( pJob->m_chFile[0] ) );
|
||||
V_strncpy( pJob->m_chFile[1], pScreenshotPath ? pScreenshotPath : "", sizeof( pJob->m_chFile[1] ) );
|
||||
pJob->m_numAutoSavesCount = 0;
|
||||
pJob->m_numCloudFiles = 0;
|
||||
|
||||
SaveUtilV2_EnqueueJob( pAsync, pJob );
|
||||
}
|
||||
|
||||
void SaveUtilV2_WriteAutosave( CPS3SaveRestoreAsyncStatus *pAsync,
|
||||
const char *pSourcepath, // eg "/dev_hdd1/tempsave/autosave.ps3.sav"
|
||||
const char *pComment, // the comment field for the new autosave.
|
||||
const unsigned int nMaxNumAutosaves )
|
||||
{
|
||||
if ( !SaveUtilV2_CanStartJob() )
|
||||
return;
|
||||
|
||||
// Start the job
|
||||
CSaveUtilV2Job_Save *pJob = new CSaveUtilV2Job_Save;
|
||||
V_strncpy( pJob->m_chComment, pComment ? pComment : "", sizeof( pJob->m_chComment ) );
|
||||
V_strncpy( pJob->m_chFile[0], pSourcepath ? pSourcepath : "", sizeof( pJob->m_chFile[0] ) );
|
||||
V_strncpy( pJob->m_chFile[1], "", sizeof( pJob->m_chFile[1] ) );
|
||||
pJob->m_numAutoSavesCount = nMaxNumAutosaves;
|
||||
pJob->m_numCloudFiles = 0;
|
||||
|
||||
SaveUtilV2_EnqueueJob( pAsync, pJob );
|
||||
}
|
||||
|
||||
void SaveUtilV2_WriteCloudFile( CPS3SaveRestoreAsyncStatus *pAsync,
|
||||
const char *pSourcepath, // eg "/dev_hdd1/tempsave/autosave.ps3.sav"
|
||||
const unsigned int nMaxNumCloudFiles )
|
||||
{
|
||||
if ( !SaveUtilV2_CanStartJob() )
|
||||
return;
|
||||
|
||||
// Start the job
|
||||
CSaveUtilV2Job_Save *pJob = new CSaveUtilV2Job_Save;
|
||||
V_strncpy( pJob->m_chComment, "", sizeof( pJob->m_chComment ) );
|
||||
V_strncpy( pJob->m_chFile[0], pSourcepath ? pSourcepath : "", sizeof( pJob->m_chFile[0] ) );
|
||||
V_strncpy( pJob->m_chFile[1], "", sizeof( pJob->m_chFile[1] ) );
|
||||
pJob->m_numAutoSavesCount = 0;
|
||||
pJob->m_numCloudFiles = nMaxNumCloudFiles;
|
||||
|
||||
SaveUtilV2_EnqueueJob( pAsync, pJob );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobStatus_t CSaveUtilV2Job_Save::DoExecute()
|
||||
{
|
||||
float flTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Save @%.3f\n", flTimeStamp );
|
||||
|
||||
// Prepare new TOC and attempt to determine if the operation is write new or overwrite
|
||||
PrepareToc();
|
||||
|
||||
// Fill out the rest of the TOC
|
||||
if ( OpenAndStatFiles() < 0 )
|
||||
{
|
||||
for ( int iPart = 0; iPart < VALVE_CONTAINER_FPARTS; ++ iPart )
|
||||
m_fd[iPart].Close();
|
||||
return SaveUtilV2_JobDone( CELL_SAVEDATA_ERROR_FAILURE );
|
||||
}
|
||||
|
||||
// Allocate our buffer
|
||||
int nCapacityRequired = 0;
|
||||
for ( int iPart = 0; iPart < VALVE_CONTAINER_FPARTS; ++ iPart )
|
||||
nCapacityRequired += m_pNewTOCEntry->m_entry.m_numBytesFile[iPart];
|
||||
m_bufFiles.EnsureCapacity( CSaveUtilV2ContainerTOC::kStorageCapacity +
|
||||
( ( ps3_saveutil2_compress.GetBool() && ( m_numCloudFiles <= 0 ) ) ? 2 : 1 ) * nCapacityRequired );
|
||||
|
||||
if ( ps3_saveutil2_compress.GetBool() && ( m_numCloudFiles <= 0 ) )
|
||||
{
|
||||
uint8 *pBaseRawData = ( ( uint8 * ) m_bufFiles.Base() ) + CSaveUtilV2ContainerTOC::kStorageCapacity;
|
||||
uint8 *pBaseOutData = pBaseRawData + nCapacityRequired;
|
||||
for ( int iPart = 0; iPart < VALVE_CONTAINER_FPARTS; ++ iPart )
|
||||
{
|
||||
if( m_fd[iPart].m_fd == -1 )
|
||||
continue;
|
||||
m_fd[iPart].BeginDeflate();
|
||||
uint64_t numBytesActuallyRead = 0, nUncompressedSize = m_pNewTOCEntry->m_entry.m_numBytesFile[iPart];
|
||||
// Read the file at the end of the buffer
|
||||
int ret = cellFsRead( m_fd[iPart].m_fd, pBaseRawData, nUncompressedSize, &numBytesActuallyRead );
|
||||
m_fd[iPart].Close();
|
||||
if ( ret < 0 || numBytesActuallyRead != nUncompressedSize )
|
||||
return SaveUtilV2_JobDone( CELL_SAVEDATA_ERROR_FAILURE );
|
||||
|
||||
// Compress the file
|
||||
|
||||
job_zlibdeflate::JobDescriptor_t * pJobDeflate = m_fd[iPart].m_pJobDeflate;
|
||||
job_zlibdeflate::JobParams_t * pJobParams = job_zlibdeflate::GetJobParams( pJobDeflate );
|
||||
pJobParams->m_eaInputUncompressedData = pBaseRawData;
|
||||
pJobParams->m_eaOutputCompressedData = pBaseOutData;
|
||||
pJobParams->m_nMaxCompressedOutputSize = nUncompressedSize;
|
||||
pJobParams->m_nUncompressedSize = nUncompressedSize;
|
||||
|
||||
pJobParams->m_nStatus = 0; // get ready to push the job
|
||||
int nError = g_saveUtilVjobInstance.m_pRoot->m_queuePortSound.pushJob( &pJobDeflate->header, sizeof( *pJobDeflate ), 0, CELL_SPURS_JOBQUEUE_FLAG_SYNC_JOB );
|
||||
if( nError != CELL_OK )
|
||||
{
|
||||
pJobParams->m_nStatus = 3; // done
|
||||
pJobParams->m_nError = 1; // error
|
||||
Warning( "Cannot push zlib job, ERROR 0x%X\n", nError );
|
||||
}
|
||||
|
||||
pBaseRawData += nUncompressedSize;
|
||||
pBaseOutData += nUncompressedSize;
|
||||
}
|
||||
Assert( pBaseOutData <= ((uint8*)m_bufFiles.Base())+m_bufFiles.Size() );
|
||||
}
|
||||
|
||||
// Call saveutil
|
||||
int retv = cellSaveDataAutoSave2(
|
||||
CELL_SAVEDATA_VERSION_CURRENT,
|
||||
g_pszSaveUtilContainerName,
|
||||
// autosaves report PS3 system dialog-errors
|
||||
(m_numAutoSavesCount||g_pSaveUtilAsyncStatus->m_bUseSystemDialogs) ? CELL_SAVEDATA_ERRDIALOG_ALWAYS : CELL_SAVEDATA_ERRDIALOG_NONE,
|
||||
&m_SaveDirInfo,
|
||||
csDataStatCallback,
|
||||
csDataFileCallback,
|
||||
SYS_MEMORY_CONTAINER_ID_INVALID,
|
||||
this );
|
||||
|
||||
for ( int iPart = 0; iPart < VALVE_CONTAINER_FPARTS; ++ iPart )
|
||||
m_fd[iPart].EndDeflate();
|
||||
|
||||
float flEndTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Save: cellSaveDataAutoSave2 returned %x @%.3f ( total time = %.3f sec )\n", retv, flEndTimeStamp, flEndTimeStamp - flTimeStamp );
|
||||
|
||||
// Close the file handles before returning so that we didn't hold the files locked
|
||||
// in case main thread resumes after job is done and tries to re-use the files
|
||||
for ( int iPart = 0; iPart < VALVE_CONTAINER_FPARTS; ++ iPart )
|
||||
m_fd[iPart].Close();
|
||||
++ g_SaveUtilV2TOCVersion;
|
||||
return SaveUtilV2_JobDone( retv );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Save::DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Save::DoDataStatCallback @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// TODO: investigate how to move delete after the write and maintain
|
||||
// consistent transactional state of TOC
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Save::DoDataFile_DeleteOldFile );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Save::DoDataFile_DeleteOldFile( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
if ( !m_arrOverwriteRequests.Count() )
|
||||
{
|
||||
DoDataFile_WriteFiles( SONY_SAVEUTIL_PARAMS );
|
||||
return;
|
||||
}
|
||||
m_owrDelete = m_arrOverwriteRequests[m_arrOverwriteRequests.Count() - 1];
|
||||
m_arrOverwriteRequests.SetCountNonDestructively( m_arrOverwriteRequests.Count() - 1 );
|
||||
|
||||
Msg( "CSaveUtilV2Job_Save::DoDataFile_DeleteOldFile @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Perform the delete
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_DELETE;
|
||||
set->fileBuf = NULL;
|
||||
set->fileSize = set->fileBufSize = 0;
|
||||
set->fileName = m_owrDelete.m_chOverwriteContainerFile;
|
||||
set->fileOffset = 0;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_SECUREFILE;
|
||||
memcpy( set->secureFileId, g_pszSaveUtilSecureFileId, CELL_SAVEDATA_SECUREFILEID_SIZE );
|
||||
set->reserved = NULL;
|
||||
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Save::DoDataFile_DeleteOldFile );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_Save::DoDataFile_DeleteOldFile will delete '%s'...\n", set->fileName );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Save::DoDataFile_WriteFiles( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Save::DoDataFile_WriteFiles @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Obtain the files data required to be written
|
||||
if ( LoadAndCompressFileData() < 0 )
|
||||
{
|
||||
Msg( "ERROR: CSaveUtilV2Job_Save::DoDataFile_WriteFiles failed to load file!\n" );
|
||||
g_pSaveUtilAsyncStatus->m_nSonyRetValue = CELL_SAVEDATA_ERROR_FAILURE;
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_ERR_FAILURE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the write
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_WRITE;
|
||||
set->fileBuf = m_bufFiles.Base();
|
||||
set->fileSize = set->fileBufSize = m_bufFiles.TellPut();
|
||||
set->fileName = m_pNewTOCEntry->m_entry.m_chContainerName;
|
||||
set->fileOffset = 0;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_SECUREFILE;
|
||||
memcpy( set->secureFileId, g_pszSaveUtilSecureFileId, CELL_SAVEDATA_SECUREFILEID_SIZE );
|
||||
set->reserved = NULL;
|
||||
|
||||
// update our TOC after the write succeeds
|
||||
SetDataFileCallback( &CSaveUtilV2Job_Save::DoDataFile_UpdateTOC );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_Save::DoDataFile_WriteFiles will write %d bytes...\n", set->fileSize );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Save::DoDataFile_UpdateTOC( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_Save::DoDataFile_UpdateTOC @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Update TOC since we successfully wrote the file
|
||||
{
|
||||
AUTO_LOCK( g_SaveUtilV2TOC.m_mtx );
|
||||
// Memory has been pre-reserved for the larger number of entries
|
||||
m_newTOC.CopyInto( &g_SaveUtilV2TOC );
|
||||
}
|
||||
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_LAST;
|
||||
}
|
||||
|
||||
int CSaveUtilV2Job_Save::OpenAndStatFiles()
|
||||
{
|
||||
float flTimeStamp = Plat_FloatTime();
|
||||
|
||||
if ( m_numCloudFiles > 0 )
|
||||
{
|
||||
// Cloud save
|
||||
int ret = cellFsOpen( m_chFile[0], CELL_FS_O_RDONLY, &m_fd[0].m_fd, NULL, 0 );
|
||||
if ( ret < 0 )
|
||||
return ret;
|
||||
CellFsStat cfs;
|
||||
ret = cellFsFstat( m_fd[0].m_fd, &cfs );
|
||||
if ( ret < 0 )
|
||||
return ret;
|
||||
if ( cfs.st_size <= sizeof( CSaveUtilV2ContainerTOC::TocStorageReserved_t ) )
|
||||
return -1;
|
||||
m_pNewTOCEntry->m_entry.m_numBytesFile[0] = cfs.st_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-cloud save
|
||||
Q_strncpy( m_pNewTOCEntry->m_entry.m_chComment, m_chComment, sizeof( m_pNewTOCEntry->m_entry.m_chComment ) );
|
||||
m_pNewTOCEntry->m_entry.m_timeModification = time( NULL );
|
||||
for ( int iPart = 0; iPart < VALVE_CONTAINER_FPARTS; ++ iPart )
|
||||
{
|
||||
if ( !m_chFile[iPart][0] )
|
||||
continue;
|
||||
|
||||
Q_strncpy( m_pNewTOCEntry->m_entry.m_chFile[iPart], V_GetFileName( m_chFile[iPart] ), VALVE_CONTAINER_FILENAME_LEN );
|
||||
int ret = cellFsOpen( m_chFile[iPart], CELL_FS_O_RDONLY, &m_fd[iPart].m_fd, NULL, 0 );
|
||||
if ( ret < 0 )
|
||||
return ret;
|
||||
CellFsStat cfs;
|
||||
ret = cellFsFstat( m_fd[iPart].m_fd, &cfs );
|
||||
if ( ret < 0 )
|
||||
return ret;
|
||||
m_pNewTOCEntry->m_entry.m_numBytesFile[iPart] = cfs.st_size;
|
||||
}
|
||||
}
|
||||
|
||||
float flEndTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Save::OpenAndStatFiles took %.3f sec [ %u + %u bytes ]\n",
|
||||
flEndTimeStamp - flTimeStamp, m_pNewTOCEntry->m_entry.m_numBytesFile[0], m_pNewTOCEntry->m_entry.m_numBytesFile[1] );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CSaveUtilV2Job_Save::LoadAndCompressFileData()
|
||||
{
|
||||
// Leave room for TOC
|
||||
m_bufFiles.SeekPut( CUtlBuffer::SEEK_HEAD, CSaveUtilV2ContainerTOC::kStorageCapacity );
|
||||
|
||||
float flTimeStamp = Plat_FloatTime();
|
||||
Assert( m_bufFiles.TellPut() == CSaveUtilV2ContainerTOC::kStorageCapacity ) ;
|
||||
uint8 *pBaseData = ( ( uint8 * ) m_bufFiles.Base() ) + CSaveUtilV2ContainerTOC::kStorageCapacity;
|
||||
|
||||
if ( m_numCloudFiles <= 0 )
|
||||
{
|
||||
for ( int iPart = 0; iPart < VALVE_CONTAINER_FPARTS; ++ iPart )
|
||||
{
|
||||
if ( ps3_saveutil2_compress.GetBool() )
|
||||
{
|
||||
job_zlibdeflate::JobDescriptor_t * pJobDeflate = m_fd[iPart].m_pJobDeflate;
|
||||
if( !pJobDeflate )
|
||||
continue;
|
||||
|
||||
job_zlibdeflate::JobParams_t * pJobParams = job_zlibdeflate::GetJobParams( pJobDeflate );
|
||||
uint nStallCount = 0;
|
||||
while( !pJobParams->IsDone() )
|
||||
{
|
||||
ThreadSleep( 1 );
|
||||
++nStallCount;
|
||||
}
|
||||
uint numCompressedBytes = pJobParams->m_nCompressedSizeOut & 0x7FFFFFFF;
|
||||
Msg( "job_zlibDeflate stalled ~%u ms : %u -> %u KiB\n", nStallCount, pJobParams->m_nUncompressedSize / 1024, ( numCompressedBytes & 0x7FFFFFFF ) / 1024 );
|
||||
|
||||
if ( pJobParams->m_nError == 0 && ( pJobParams->m_nCompressedSizeOut & 0x80000000 ) && numCompressedBytes < m_pNewTOCEntry->m_entry.m_numBytesFile[iPart] ) // we actually have deflated data
|
||||
{
|
||||
// file compressed successfully
|
||||
// remove MSB
|
||||
m_pNewTOCEntry->m_entry.m_numBytesDecompressedFile[iPart] = m_pNewTOCEntry->m_entry.m_numBytesFile[iPart];
|
||||
m_pNewTOCEntry->m_entry.m_numBytesFile[iPart] = numCompressedBytes;
|
||||
V_memcpy( pBaseData, pJobParams->m_eaOutputCompressedData, numCompressedBytes );
|
||||
Msg( "CSaveUtilV2Job_Save::LoadAndCompressFileData compresses '%s' %d/%d bytes\n", m_pNewTOCEntry->m_entry.m_chFile[iPart], m_pNewTOCEntry->m_entry.m_numBytesFile[iPart], m_pNewTOCEntry->m_entry.m_numBytesDecompressedFile[iPart] );
|
||||
}
|
||||
else
|
||||
{
|
||||
// there was an error during compression; use uncompressed data
|
||||
m_pNewTOCEntry->m_entry.m_numBytesDecompressedFile[iPart] = 0;
|
||||
if( pBaseData != pJobParams->m_eaInputUncompressedData )
|
||||
{
|
||||
V_memmove( pBaseData, pJobParams->m_eaInputUncompressedData, m_pNewTOCEntry->m_entry.m_numBytesFile[iPart] );
|
||||
}
|
||||
}
|
||||
pBaseData += m_pNewTOCEntry->m_entry.m_numBytesFile[iPart];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_fd[iPart].m_fd == -1 )
|
||||
continue;
|
||||
|
||||
uint64 numBytesActuallyRead;
|
||||
int ret = cellFsRead( m_fd[iPart].m_fd, ( ( uint8 * )m_bufFiles.Base() ) + m_bufFiles.TellPut(), m_bufFiles.Size() - m_bufFiles.TellPut(), &numBytesActuallyRead );
|
||||
m_fd[iPart].Close();
|
||||
if ( ret < 0 )
|
||||
return ret;
|
||||
|
||||
if ( numBytesActuallyRead != m_pNewTOCEntry->m_entry.m_numBytesFile[iPart] )
|
||||
return -1;
|
||||
|
||||
}
|
||||
m_bufFiles.SeekPut( CUtlBuffer::SEEK_HEAD, m_bufFiles.TellPut() + m_pNewTOCEntry->m_entry.m_numBytesFile[iPart] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_fd[0].m_fd == -1 )
|
||||
return -1;
|
||||
|
||||
uint64 numBytesActuallyRead;
|
||||
m_bufFiles.SeekPut( CUtlBuffer::SEEK_HEAD, CSaveUtilV2ContainerTOC::kStorageCapacity - sizeof( CSaveUtilV2ContainerTOC::TocStorageReserved_t ) );
|
||||
char unsigned *pbIncomingFileBase = ( ( char unsigned * ) m_bufFiles.Base() ) + m_bufFiles.TellPut();
|
||||
int ret = cellFsRead( m_fd[0].m_fd, pbIncomingFileBase, m_bufFiles.Size() - m_bufFiles.TellPut(), &numBytesActuallyRead );
|
||||
m_fd[0].Close();
|
||||
if ( ret < 0 )
|
||||
return ret;
|
||||
|
||||
if ( numBytesActuallyRead != m_pNewTOCEntry->m_entry.m_numBytesFile[0] )
|
||||
return -1;
|
||||
|
||||
m_bufFiles.SeekPut( CUtlBuffer::SEEK_HEAD, m_bufFiles.TellPut() + m_pNewTOCEntry->m_entry.m_numBytesFile[0] );
|
||||
|
||||
//
|
||||
// Signature
|
||||
//
|
||||
|
||||
// Version of our save header
|
||||
CSaveUtilV2ContainerTOC::TocEntry_t *pSignature = (CSaveUtilV2ContainerTOC::TocEntry_t *) pbIncomingFileBase;
|
||||
if ( pSignature->m_chContainerName[0] != 'S' ||
|
||||
pSignature->m_chContainerName[1] != 'A' ||
|
||||
pSignature->m_chContainerName[2] != 'V' ||
|
||||
pSignature->m_chContainerName[3] != '1' )
|
||||
{
|
||||
Msg( "ERROR: CSaveUtilV2Job_Save : header mismatch, expecting SAV1\n" );
|
||||
return -2; // header mismatch
|
||||
}
|
||||
|
||||
// Fetch the current hash
|
||||
uint32 uiFileHashCurrent = 0, uiHash = 0;
|
||||
V_memcpy( &uiFileHashCurrent, ( (char*) pSignature ) + 8 + sizeof( g_uiSteamCloudCryptoKey ) - sizeof( uiHash ), sizeof( uiHash ) );
|
||||
|
||||
// Temporarily put our cryptokey in place of hash
|
||||
V_memcpy( ( (char*) pSignature ) + 8, &g_uiSteamCloudCryptoKey, sizeof( g_uiSteamCloudCryptoKey ) );
|
||||
uiHash = SaveUtilV2_ComputeBufferHash( pSignature, m_pNewTOCEntry->m_entry.m_numBytesFile[0] );
|
||||
if ( uiHash != uiFileHashCurrent )
|
||||
{
|
||||
Msg( "ERROR: CSaveUtilV2Job_Save : signature hash mismatch\n" );
|
||||
return -3;
|
||||
}
|
||||
|
||||
// We only need to preserve container name
|
||||
char chContainerName[sizeof( m_pNewTOCEntry->m_entry.m_chContainerName )];
|
||||
V_memcpy( chContainerName, m_pNewTOCEntry->m_entry.m_chContainerName, sizeof( m_pNewTOCEntry->m_entry.m_chContainerName ) );
|
||||
V_memcpy( m_pNewTOCEntry, pbIncomingFileBase, sizeof( CSaveUtilV2ContainerTOC::TocStorageReserved_t ) );
|
||||
V_memcpy( m_pNewTOCEntry->m_entry.m_chContainerName, chContainerName, sizeof( m_pNewTOCEntry->m_entry.m_chContainerName ) );
|
||||
V_memset( m_pNewTOCEntry->m_entry.m_chFile[0], 0, sizeof( m_pNewTOCEntry->m_entry.m_chFile[0] ) );
|
||||
V_snprintf( m_pNewTOCEntry->m_entry.m_chFile[0], sizeof( m_pNewTOCEntry->m_entry.m_chFile[0] ), "cloudsave%016llx.ps3.sav", m_pNewTOCEntry->m_entry.m_timeModification );
|
||||
if ( *m_pNewTOCEntry->m_entry.m_chFile[1] )
|
||||
V_snprintf( m_pNewTOCEntry->m_entry.m_chFile[1], sizeof( m_pNewTOCEntry->m_entry.m_chFile[1] ), "cloudsave%016llx.ps3.tga", m_pNewTOCEntry->m_entry.m_timeModification );
|
||||
}
|
||||
|
||||
float flEndTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_Save::LoadFileToBuffer finished loading%s @%.3f (%.3f sec)\n", ps3_saveutil2_compress.GetBool() ? " and compressing" : "",
|
||||
flEndTimeStamp, flEndTimeStamp - flTimeStamp );
|
||||
|
||||
// New TOC is fully ready, serialize it for writing
|
||||
m_newTOC.SerializeIntoTocBuffer( m_bufFiles.Base() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int TocAutosavesSortFunc( CSaveUtilV2ContainerTOC::TocStorageReserved_t * const *a, CSaveUtilV2ContainerTOC::TocStorageReserved_t * const *b )
|
||||
{
|
||||
if ( (*a)->m_entry.m_timeModification != (*b)->m_entry.m_timeModification )
|
||||
return ( (*a)->m_entry.m_timeModification < (*b)->m_entry.m_timeModification ) ? -1 : 1;
|
||||
else
|
||||
return V_stricmp( (*a)->m_entry.m_chContainerName, (*b)->m_entry.m_chContainerName );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_Save::PrepareToc()
|
||||
{
|
||||
{
|
||||
AUTO_LOCK( g_SaveUtilV2TOC.m_mtx );
|
||||
m_newTOC.m_arrEntries.EnsureCapacity( g_SaveUtilV2TOC.m_arrEntries.Count() + 1 );
|
||||
g_SaveUtilV2TOC.CopyInto( &m_newTOC );
|
||||
|
||||
// Assuming all goes well, make room in the global TOC (need to do this now because we MUST NOT allocate inside the callback!):
|
||||
g_SaveUtilV2TOC.m_arrEntries.EnsureCapacity( g_SaveUtilV2TOC.m_arrEntries.Count() + 1 );
|
||||
}
|
||||
|
||||
// PrepareToc is running before callback so can use memory allocations
|
||||
int k = m_newTOC.FindByEmbeddedFileName( V_GetFileName( m_chFile[0] ), NULL );
|
||||
if ( ( k < 0 ) || ( k >= m_newTOC.m_arrEntries.Count() ) || ( m_numCloudFiles > 0 ) )
|
||||
{
|
||||
// AUTOSAVE NOTE: Even if this is an autosave, if there's no other autosave.sav file
|
||||
// in the container then we can freely write the the new file, since the total number
|
||||
// of autosaves will be below the threshold
|
||||
// CLOUD NOTE: All cloud file writes are controlled by cloud sync manager
|
||||
// when cloud sync manager requests a write it will always be a new file
|
||||
|
||||
m_pNewTOCEntry = &m_newTOC.m_arrEntries[ m_newTOC.m_arrEntries.AddToTail() ];
|
||||
}
|
||||
else if ( m_numAutoSavesCount <= 0 )
|
||||
{
|
||||
// It's a regular save, not autosave, overwrite the file inside container
|
||||
m_pNewTOCEntry = &m_newTOC.m_arrEntries[ k ];
|
||||
OverwriteRequest_t owr;
|
||||
V_strncpy( owr.m_chOverwriteContainerFile, m_pNewTOCEntry->m_entry.m_chContainerName, sizeof( owr.m_chOverwriteContainerFile ) );
|
||||
m_arrOverwriteRequests.AddToTail( owr );
|
||||
Msg( "CSaveUtilV2Job_Save will overwrite existing file '%s'\n", m_pNewTOCEntry->m_entry.m_chContainerName );
|
||||
}
|
||||
else // AUTOSAVE CASE
|
||||
{
|
||||
// It's an autosave and should overwrite the oldest autosave in this case
|
||||
// other autosaves must be aged and renamed
|
||||
char const *szSaveFileStringSearch = "autosave";
|
||||
int numPreserve = MAX( m_numAutoSavesCount, m_numCloudFiles );
|
||||
CUtlVector< CSaveUtilV2ContainerTOC::TocStorageReserved_t * > arrAutoSaves;
|
||||
arrAutoSaves.EnsureCapacity( numPreserve + 3 );
|
||||
for ( int ii = 0; ii < m_newTOC.m_arrEntries.Count(); ++ ii )
|
||||
{
|
||||
if ( V_stristr( m_newTOC.m_arrEntries[ii].m_entry.m_chFile[0], szSaveFileStringSearch ) )
|
||||
arrAutoSaves.AddToTail( &m_newTOC.m_arrEntries[ii] );
|
||||
}
|
||||
arrAutoSaves.Sort( TocAutosavesSortFunc );
|
||||
|
||||
// Now we have a sorted list of autosaves, first element is oldest, last element is newest
|
||||
// the list is guaranteed non-empty, otherwise we would be creating new file altogether
|
||||
// Walk the list backwards and rename the autosaves appropriately
|
||||
int nAutosaveNameIndex = 1;
|
||||
for ( int ii = arrAutoSaves.Count(); ii-- > 0; ++ nAutosaveNameIndex )
|
||||
{
|
||||
V_snprintf( arrAutoSaves[ii]->m_entry.m_chFile[0], sizeof( arrAutoSaves[ii]->m_entry.m_chFile[0] ),
|
||||
"%s%02d.ps3.sav", szSaveFileStringSearch, nAutosaveNameIndex );
|
||||
if ( *arrAutoSaves[ii]->m_entry.m_chFile[1] )
|
||||
{
|
||||
V_snprintf( arrAutoSaves[ii]->m_entry.m_chFile[1], sizeof( arrAutoSaves[ii]->m_entry.m_chFile[1] ),
|
||||
"%s%02d.ps3.tga", szSaveFileStringSearch, nAutosaveNameIndex );
|
||||
}
|
||||
}
|
||||
|
||||
// Now if the list of autosaves hasn't yet reached the max number, then just create a new file now
|
||||
if ( arrAutoSaves.Count() <= numPreserve )
|
||||
{
|
||||
// Generate filename to make inside container
|
||||
m_pNewTOCEntry = &m_newTOC.m_arrEntries[ m_newTOC.m_arrEntries.AddToTail() ];
|
||||
Msg( "CSaveUtilV2Job_Save will create new %s, %d existing %ss renamed\n",
|
||||
szSaveFileStringSearch, arrAutoSaves.Count(), szSaveFileStringSearch );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Overwrite the oldest autosave (the TOC entry is updated, and the old container file will be deleted)
|
||||
m_pNewTOCEntry = arrAutoSaves[0];
|
||||
OverwriteRequest_t owr;
|
||||
V_strncpy( owr.m_chOverwriteContainerFile, m_pNewTOCEntry->m_entry.m_chContainerName, sizeof( owr.m_chOverwriteContainerFile ) );
|
||||
m_arrOverwriteRequests.AddToTail( owr );
|
||||
Msg( "CSaveUtilV2Job_Save will overwrite oldest %s '%s', %d other existing %ss preserved and renamed\n",
|
||||
szSaveFileStringSearch, m_pNewTOCEntry->m_entry.m_chContainerName, arrAutoSaves.Count() - 1, szSaveFileStringSearch );
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the new/updated TOC entry
|
||||
Q_memset( m_pNewTOCEntry, 0, sizeof( CSaveUtilV2ContainerTOC::TocStorageReserved_t ) );
|
||||
int idxContainerIndex = ++ m_newTOC.m_idxNewSaveName;
|
||||
V_snprintf( m_pNewTOCEntry->m_entry.m_chContainerName, sizeof( m_pNewTOCEntry->m_entry.m_chContainerName ), "%08X.SAV", idxContainerIndex );
|
||||
Msg( "CSaveUtilV2Job_Save will create new file '%s'\n", m_pNewTOCEntry->m_entry.m_chContainerName );
|
||||
}
|
||||
|
||||
|
||||
95
engine/ps3/ps3_saveutil_v2_save_steam.cpp
Normal file
95
engine/ps3/ps3_saveutil_v2_save_steam.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
//===== Copyright © 1996-2011, Valve Corporation, All rights reserved. ======//
|
||||
|
||||
#include "ps3_saveutil_v2.h"
|
||||
#include "memdbgon.h"
|
||||
|
||||
class CSaveUtilV2Job_WriteSteamInfo : public ISaveUtilV2Job
|
||||
{
|
||||
public: // Job entry point
|
||||
virtual JobStatus_t DoExecute();
|
||||
|
||||
protected: // Stat callback
|
||||
virtual void DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS );
|
||||
|
||||
protected: // Write Steam info
|
||||
void DoDataFile_WriteSteamInfo( SONY_SAVEUTIL_FILE_PARAMS );
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SaveUtilV2_WriteSteamInfo( CPS3SaveRestoreAsyncStatus *pAsync )
|
||||
{
|
||||
if ( !SaveUtilV2_CanStartJob() )
|
||||
return;
|
||||
|
||||
// Make sure that Steam info is prepared on the main thread
|
||||
if ( !g_pSteamInfoProvider->PrepareSaveBufferForCommit() )
|
||||
return;
|
||||
|
||||
// Start the job
|
||||
CSaveUtilV2Job_WriteSteamInfo *pJob = new CSaveUtilV2Job_WriteSteamInfo;
|
||||
|
||||
SaveUtilV2_EnqueueJob( pAsync, pJob );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobStatus_t CSaveUtilV2Job_WriteSteamInfo::DoExecute()
|
||||
{
|
||||
float flTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_WriteSteamInfo @%.3f\n", flTimeStamp );
|
||||
|
||||
// Call saveutil
|
||||
int retv = cellSaveDataAutoSave2(
|
||||
CELL_SAVEDATA_VERSION_CURRENT,
|
||||
g_pszSaveUtilContainerName,
|
||||
CELL_SAVEDATA_ERRDIALOG_ALWAYS,
|
||||
&m_SaveDirInfo,
|
||||
csDataStatCallback,
|
||||
csDataFileCallback,
|
||||
SYS_MEMORY_CONTAINER_ID_INVALID,
|
||||
this );
|
||||
|
||||
float flEndTimeStamp = Plat_FloatTime();
|
||||
Msg( "CSaveUtilV2Job_WriteSteamInfo: cellSaveDataAutoSave2 returned %x @%.3f ( total time = %.3f sec )\n", retv, flEndTimeStamp, flEndTimeStamp - flTimeStamp );
|
||||
|
||||
return SaveUtilV2_JobDone( retv );
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_WriteSteamInfo::DoDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_WriteSteamInfo::DoDataStatCallback @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
SetDataFileCallback( &CSaveUtilV2Job_WriteSteamInfo::DoDataFile_WriteSteamInfo );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
}
|
||||
|
||||
void CSaveUtilV2Job_WriteSteamInfo::DoDataFile_WriteSteamInfo( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
Msg( "CSaveUtilV2Job_WriteSteamInfo::DoDataFile_WriteSteamInfo @%.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Obtain steam buffer
|
||||
CUtlBuffer *pBuffer = g_pSteamInfoProvider->GetSaveBufferForCommit();
|
||||
if ( !pBuffer )
|
||||
{
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_LAST;
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the write
|
||||
set->fileOperation = CELL_SAVEDATA_FILEOP_WRITE;
|
||||
set->fileBuf = pBuffer->Base();
|
||||
set->fileSize = set->fileBufSize = pBuffer->TellPut();
|
||||
set->fileName = VALVE_CONTAINER_FILE_STEAM;
|
||||
set->fileOffset = 0;
|
||||
set->fileType = CELL_SAVEDATA_FILETYPE_SECUREFILE;
|
||||
memcpy( set->secureFileId, g_pszSaveUtilSecureFileId, CELL_SAVEDATA_SECUREFILEID_SIZE );
|
||||
set->reserved = NULL;
|
||||
|
||||
// final write
|
||||
SetDataFileCallbackFinalize();
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_NEXT;
|
||||
|
||||
Msg( "CSaveUtilV2Job_WriteSteamInfo::DoDataFile_WriteSteamInfo will write %d bytes...\n", pBuffer->TellPut() );
|
||||
}
|
||||
|
||||
198
engine/ps3/ps3_saveutil_v2_thread.cpp
Normal file
198
engine/ps3/ps3_saveutil_v2_thread.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
//===== Copyright © 1996-2011, Valve Corporation, All rights reserved. ======//
|
||||
|
||||
#include "ps3_saveutil_v2.h"
|
||||
#include "fmtstr.h"
|
||||
#include "checksum_crc.h"
|
||||
#include "memdbgon.h"
|
||||
|
||||
|
||||
CON_COMMAND( ps3_saveutil_showtoc, "" )
|
||||
{
|
||||
AUTO_LOCK( g_SaveUtilV2TOC.m_mtx );
|
||||
|
||||
int numTocEntries = g_SaveUtilV2TOC.m_arrEntries.Count();
|
||||
Msg( "--------- SAVEUTILTOC -----------\n" );
|
||||
for ( int k = 0; k < numTocEntries; ++ k )
|
||||
{
|
||||
CSaveUtilV2ContainerTOC::TocEntry_t &e = g_SaveUtilV2TOC.m_arrEntries[k].m_entry;
|
||||
Msg( "%02d : %016llx %s\n"
|
||||
" '%s' %u/%u\n"
|
||||
" '%s' %u/%u\n"
|
||||
" %s\n",
|
||||
k + 1, e.m_timeModification, e.m_chContainerName,
|
||||
e.m_chFile[0], e.m_numBytesFile[0], e.m_numBytesDecompressedFile[0],
|
||||
e.m_chFile[1], e.m_numBytesFile[1], e.m_numBytesDecompressedFile[1],
|
||||
e.m_chComment
|
||||
);
|
||||
}
|
||||
Msg( "--------- %02d ENTRIES -----------\n", numTocEntries );
|
||||
}
|
||||
|
||||
void SaveUtilV2_GetFileInfoSync( CUtlVector< IPS3SaveRestoreToUI::PS3SaveGameInfo_t > &saveGameInfos, bool bFindAll )
|
||||
{
|
||||
// This can be called after starting a save op but before it completes, so this will return old data in that case.
|
||||
// The caller should be aware that if SaveUtil is busy then it can check the operation TAG and know what is in
|
||||
// progress and whether it can affect the TOC after it's finished.
|
||||
// Currently only UI queries the TOC and ensures that writes of savegames are completed before queries.
|
||||
AUTO_LOCK( g_SaveUtilV2TOC.m_mtx );
|
||||
|
||||
if ( !g_SaveUtilV2TOC.m_arrEntries.Count() )
|
||||
{
|
||||
saveGameInfos.RemoveAll();
|
||||
return;
|
||||
}
|
||||
|
||||
int numTocEntries = bFindAll ? g_SaveUtilV2TOC.m_arrEntries.Count() : 1;
|
||||
saveGameInfos.SetCount( numTocEntries );
|
||||
for ( int k = 0; k < numTocEntries; ++ k )
|
||||
{
|
||||
saveGameInfos[k].m_InternalName = CFmtStr( "!%s", g_SaveUtilV2TOC.m_arrEntries[k].m_entry.m_chContainerName );
|
||||
saveGameInfos[k].m_Comment = g_SaveUtilV2TOC.m_arrEntries[k].m_entry.m_chComment;
|
||||
saveGameInfos[k].m_Filename = g_SaveUtilV2TOC.m_arrEntries[k].m_entry.m_chFile[0];
|
||||
saveGameInfos[k].m_ScreenshotFilename = g_SaveUtilV2TOC.m_arrEntries[k].m_entry.m_chFile[1];
|
||||
saveGameInfos[k].m_nFileTime = g_SaveUtilV2TOC.m_arrEntries[k].m_entry.m_timeModification;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SaveUtilV2_CanStartJob()
|
||||
{
|
||||
bool bResult = ( g_pSaveUtilThreadPool && !g_pSaveUtilAsyncStatus );
|
||||
if ( !bResult )
|
||||
{
|
||||
Warning( "SaveUtilV2_CanStartJob : cannot start job now! Invalid usage!\n" );
|
||||
Assert( 0 );
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
|
||||
void SaveUtilV2_EnqueueJob( CPS3SaveRestoreAsyncStatus *pAsync, ISaveUtilV2Job *pJob )
|
||||
{
|
||||
if ( g_pSaveUtilAsyncStatus )
|
||||
Error( "SaveUtilV2_EnqueueJob while job already running ( %p running, %p attempted )!\n", g_pSaveUtilAsyncStatus, pAsync );
|
||||
|
||||
g_pSaveUtilAsyncStatus = pAsync;
|
||||
|
||||
// Prepare for saveutil operation
|
||||
const int numContainers = VALVE_CONTAINER_COUNT;
|
||||
pJob->m_bufSaveDirList.EnsureCapacity( numContainers * MAX( sizeof( CellSaveDataFileStat ), sizeof( CellSaveDataDirList ) ) );
|
||||
|
||||
// Prepare save dir info
|
||||
memset( &pJob->m_SaveDirInfo, 0, sizeof(CellSaveDataSetBuf) );
|
||||
pJob->m_SaveDirInfo.dirListMax = numContainers;
|
||||
pJob->m_SaveDirInfo.fileListMax = numContainers;
|
||||
pJob->m_SaveDirInfo.bufSize = pJob->m_bufSaveDirList.Size();
|
||||
pJob->m_SaveDirInfo.buf = pJob->m_bufSaveDirList.Base();
|
||||
|
||||
// Mark the job as pending
|
||||
g_pSaveUtilAsyncStatus->m_nSonyRetValue = CELL_SAVEDATA_ERROR_NOTSUPPORTED;
|
||||
g_pSaveUtilAsyncStatus->m_bDone = 0;
|
||||
|
||||
// Let's notify the file system that a save is starting, that way the file system can try to reduce HDD accesses and use BluRay instead
|
||||
g_pFullFileSystem->OnSaveStateChanged( true );
|
||||
|
||||
// Add the job to thread pool
|
||||
pJob->SetFlags( JF_SERIAL | JF_QUEUE );
|
||||
g_pSaveUtilThreadPool->AddJob( pJob );
|
||||
pJob->Release();
|
||||
}
|
||||
|
||||
JobStatus_t SaveUtilV2_JobDone( int nErrorCode )
|
||||
{
|
||||
// Let's notify the file system that a save is finished, that way the file system can restart using the HDD
|
||||
g_pFullFileSystem->OnSaveStateChanged( false );
|
||||
|
||||
// Set the job error code and set that the job is done
|
||||
if ( nErrorCode != CELL_SAVEDATA_ERROR_CBRESULT )
|
||||
g_pSaveUtilAsyncStatus->m_nSonyRetValue = nErrorCode;
|
||||
else if ( g_pSaveUtilAsyncStatus->m_nSonyRetValue >= 0 )
|
||||
g_pSaveUtilAsyncStatus->m_nSonyRetValue = CELL_SAVEDATA_ERROR_FAILURE;
|
||||
|
||||
CPS3SaveRestoreAsyncStatus *pAsync = g_pSaveUtilAsyncStatus;
|
||||
g_pSaveUtilAsyncStatus = NULL;
|
||||
pAsync->m_bDone = 1;
|
||||
return JOB_OK;
|
||||
}
|
||||
|
||||
uint32 SaveUtilV2_ComputeBufferHash( void const *pvData, uint32 numBytes )
|
||||
{
|
||||
return CRC32_ProcessSingleBuffer( pvData, numBytes );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ISaveUtilV2Job::csDataStatCallback( SONY_SAVEUTIL_STAT_PARAMS )
|
||||
{
|
||||
ISaveUtilV2Job *pSelf = static_cast<ISaveUtilV2Job*>( cbResult->userdata );
|
||||
pSelf->DoDataStatCallback( SONY_SAVEUTIL_PARAMS );
|
||||
}
|
||||
|
||||
void ISaveUtilV2Job::csDataFileCallback( SONY_SAVEUTIL_FILE_PARAMS )
|
||||
{
|
||||
ISaveUtilV2Job *pSelf = static_cast<ISaveUtilV2Job*>( cbResult->userdata );
|
||||
if ( pSelf->m_pfnDoDataFileCallback )
|
||||
{
|
||||
(pSelf->*(pSelf->m_pfnDoDataFileCallback))( SONY_SAVEUTIL_PARAMS );
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "ISaveUtilV2Job::csDataFileCallback finalizing save operation @%.3f\n", Plat_FloatTime() );
|
||||
cbResult->result = CELL_SAVEDATA_CBRESULT_OK_LAST;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CSaveUtilV2ContainerTOC::SerializeIntoTocBuffer( void *pvBuffer )
|
||||
{
|
||||
uint32 *pui32 = (uint32*) pvBuffer;
|
||||
*( pui32 ++ ) = m_idxNewSaveName;
|
||||
*( pui32 ++ ) = m_arrEntries.Count();
|
||||
V_memcpy( pui32, m_arrEntries.Base(), m_arrEntries.Count() * sizeof( TocStorageReserved_t ) );
|
||||
}
|
||||
|
||||
void CSaveUtilV2ContainerTOC::SerializeFromTocBuffer( void *pvBuffer )
|
||||
{
|
||||
uint32 *pui32 = (uint32*) pvBuffer;
|
||||
m_idxNewSaveName = *( pui32 ++ );
|
||||
uint32 uiEntriesCount = *( pui32 ++ );
|
||||
uiEntriesCount = MIN( uiEntriesCount, VALVE_CONTAINER_COUNT );
|
||||
m_arrEntries.AddMultipleToTail( uiEntriesCount, reinterpret_cast< TocStorageReserved_t * >( pui32 ) );
|
||||
}
|
||||
|
||||
void CSaveUtilV2ContainerTOC::CopyInto( CSaveUtilV2ContainerTOC *pOther )
|
||||
{
|
||||
pOther->m_idxNewSaveName = m_idxNewSaveName;
|
||||
pOther->m_arrEntries.RemoveAll();
|
||||
pOther->m_arrEntries.AddMultipleToTail( m_arrEntries.Count(), m_arrEntries.Base() );
|
||||
}
|
||||
|
||||
int CSaveUtilV2ContainerTOC::FindByEmbeddedFileName( char const *szFilename, int *pnPartIndex )
|
||||
{
|
||||
for ( int k = 0; k < m_arrEntries.Count(); ++ k )
|
||||
{
|
||||
if ( szFilename[0] == '!' )
|
||||
{
|
||||
if ( V_stricmp( m_arrEntries[k].m_entry.m_chContainerName, szFilename + 1 ) )
|
||||
continue;
|
||||
if ( pnPartIndex )
|
||||
*pnPartIndex = 0;
|
||||
return k;
|
||||
}
|
||||
for ( int iPart = 0; iPart < VALVE_CONTAINER_FPARTS; ++ iPart )
|
||||
{
|
||||
if ( !V_stricmp( m_arrEntries[k].m_entry.m_chFile[iPart], szFilename ) )
|
||||
{
|
||||
if ( pnPartIndex )
|
||||
*pnPartIndex = iPart;
|
||||
return k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( pnPartIndex )
|
||||
*pnPartIndex = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user