You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
649 lines
22 KiB
649 lines
22 KiB
//===== 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 );
|
|
}
|
|
|