Files
GTASource/game/streaming/streaminginstall_psn.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

498 lines
15 KiB
C++

//
// streaming/streaminginstall_psn.cpp
//
// Copyright (C) 1999-2009 Rockstar Games. All Rights Reserved.
//
STREAMING_OPTIMISATIONS()
#if __PPU
#include <sys/memory.h>
#include "streaminginstall_psn.h"
#include "file/savegame.h"
#include "file/device.h"
#include "file/asset.h"
#include "math/amath.h"
#include "system/timer.h"
#include "system/nelem.h"
#include "system/system.h"
#include "system/param.h"
// #include "text/text.h"
#if !__FINAL
PARAM(update, "Development update folder");
#endif
// :TODO: use correct SFO info..
static const char* SAMPLE_PARAMSFO_VERSION = "01.00";
fiPsnUpdateDevice fiPsnUpdateDevice::m_device;
/////////////////////////////////////
//start of psn update device
/////////////////////////////////////
fiPsnUpdateDevice& fiPsnUpdateDevice::Instance()
{
static bool instanced = false;
if(!instanced)
{
instanced = true;
#if !__FINAL
const char* pUpdateFolder;
if(PARAM_update.Get(pUpdateFolder))
{
static fiDeviceRelative gUpdateDevice;
gUpdateDevice.Init(pUpdateFolder, false);
gUpdateDevice.MountAs("update:/");
Printf("Mount %s as update:", pUpdateFolder);
}
else
#endif
{
u32 result = cellGameDataCheckCreate2(
CELL_GAMEDATA_VERSION_CURRENT,
XEX_TITLE_ID,
CELL_GAMEDATA_ERRDIALOG_ALWAYS,
&fiPsnUpdateDevice::GameDataStatCallback,
SYS_MEMORY_CONTAINER_ID_INVALID);
if(result != CELL_OK)
streamErrorf("fiPsnUpdateDevice::Failed to retrieve the gamedata directory (%d)", result);
}
}
return m_device;
}
void fiPsnUpdateDevice::GameDataStatCallback(CellGameDataCBResult* result, CellGameDataStatGet* get, CellGameDataStatSet* UNUSED_PARAM(set))
{
m_device.Init(get->gameDataPath, false);
m_device.MountAs("update:/");
result->result = CELL_GAMEDATA_CBRESULT_OK_CANCEL ;
result->reserved = NULL;
}
/////////////////////////////////////
//end of psn update device
/////////////////////////////////////
fiPsnInstallerDevice::fiPsnInstallerDevice(bool forceReinstall)
: m_StartedInstalling(false)
, m_FinishedInstalling(false)
, m_InstallFailed(false)
, m_NotEnoughSpace(false)
, m_ForceReinstall(forceReinstall)
, m_BytesCopied(0)
, m_InstallSize(0)
{
}
fiHandle fiPsnInstallerDevice::InstallFile(const char* path, bool readOnly, const char* installedname, bool isContentInfo)
{
streamAssertf(!m_StartedInstalling, "Can't add an installable file after game installation");
fiHandle handle = (fiHandle)m_Files.GetCount();
fiInstalledFile& f = m_Files.Grow();
safecpy(f.m_SrcFilename, path, NELEM(f.m_SrcFilename));
if (!installedname || !installedname[0])
installedname = ASSET.FileName(path);
safecpy(f.m_InstallName, installedname, NELEM(f.m_InstallName));
f.m_Handle = fiHandleInvalid;
f.m_ReadOnly = readOnly;
f.m_IsContentInfo = isContentInfo;
f.m_SeekPos = 0;
f.m_Installed = false;
return handle;
}
fiHandle fiPsnInstallerDevice::LocalHandle(fiHandle handle) const
{
fiInstalledFile& f = m_Files[(int)handle];
streamAssertf(f.m_Installed, "Trying to access a file that hasn't finished installing");
if (f.m_Handle == fiHandleInvalid)
{
const fiDevice& localdev = fiDeviceLocal::GetInstance();
if (m_OpenFiles.IsFull())
{
// out of file handles, close the oldest one
fiInstalledFile* fileToClose = m_OpenFiles.Pop();
streamAssert(fileToClose->m_Handle != fiHandleInvalid);
fileToClose->m_SeekPos = localdev.Seek(fileToClose->m_Handle, 0, seekCur);
localdev.Close(fileToClose->m_Handle);
fileToClose->m_Handle = fiHandleInvalid;
}
char destname[2048];
GetInstalledPath(destname, NELEM(destname), f);
const_cast<fiInstalledFile&>(f).m_Handle = localdev.Open(destname, f.m_ReadOnly);
streamAssertf(f.m_Handle != fiHandleInvalid,
"Error opening installed file %s (installed from %s)", destname, f.m_SrcFilename);
localdev.Seek(f.m_Handle, f.m_SeekPos, seekSet);
m_OpenFiles.Push(&f);
}
return f.m_Handle;
}
int fiPsnInstallerDevice::Close(fiHandle handle) const
{
fiInstalledFile& f = m_Files[(int)handle];
if (f.m_Handle != fiHandleInvalid)
{
fiDeviceLocal::GetInstance().Close(f.m_Handle);
f.m_Handle = fiHandleInvalid;
int i = 0;
streamVerify(m_OpenFiles.Find(&f, &i));
m_OpenFiles.Delete(i);
}
return 0;
}
static fiPsnInstallerDevice* s_InstallerDevice = 0;
bool fiPsnInstallerDevice::InitInstall()
{
m_StartedInstalling = true;
s_InstallerDevice = this;
// create gamedata directory or find existing one
u32 result = cellGameDataCheckCreate2(
CELL_GAMEDATA_VERSION_CURRENT,
XEX_TITLE_ID,
CELL_GAMEDATA_ERRDIALOG_ALWAYS,
&fiPsnInstallerDevice::GameDataStatCallbackStub,
SYS_MEMORY_CONTAINER_ID_INVALID);
if(result != CELL_OK)
{
streamErrorf("fiPsnInstallerDevice::InstallOpenedFiles Failed to create the gamedata directory (%d)", result);
m_InstallFailed = true;
if (m_progressCallback)
m_progressCallback(InstallStatus(), InstallProgress());
return false;
}
return true;
}
void fiPsnInstallerDevice::InstallOpenedFiles()
{
// start the installer thread
if(InstallStatus() == INSTALL_OK)
m_Thread = sysIpcCreateThread(&fiPsnInstallerDevice::InstallerThreadStub, this, sysIpcMinThreadStackSize, PRIO_BELOW_NORMAL, "Installer");
}
bool fiPsnInstallerDevice::IsInstalled(fiHandle handle)
{
return m_Files[(int)handle].m_Installed;
}
bool fiPsnInstallerDevice::IsEverythingInstalled()
{
// go through list of files and check if they are all installed
for(atArray<fiInstalledFile>::iterator it = m_Files.begin(); it != m_Files.end(); ++it)
{
fiInstalledFile& f = *it;
if (!f.m_Installed)
return false;
}
return true;
}
bool fiPsnInstallerDevice::FinishInstallation()
{
streamAssert(m_StartedInstalling);
sysIpcWaitThreadExit(m_Thread);
return !m_InstallFailed;
}
void fiPsnInstallerDevice::InstallerThreadStub(void* ptr)
{
static_cast<fiPsnInstallerDevice*>(ptr)->InstallerThread();
}
void fiPsnInstallerDevice::InstallerThread()
{
// install any missing files
streamDisplayf("INSTALLING..");
sysTimer timer;
float nextProgressReport = 0.0f;
m_InstallFailed = false;
const fiDevice& localdev = fiDeviceLocal::GetInstance();
for(atArray<fiInstalledFile>::iterator it = m_Files.begin(); it != m_Files.end(); ++it)
{
fiInstalledFile& f = *it;
if (!f.m_Installed)
{
char destname[2048];
GetInstalledPath(destname, NELEM(destname), f);
streamDisplayf("INSTALLING %s to %s", f.m_SrcFilename, destname);
const fiDevice* srcdevice = fiDevice::GetDevice(f.m_SrcFilename, true);
fiHandle src = srcdevice->Open(f.m_SrcFilename, true);
streamAssertf(src != fiHandleInvalid, "Error opening %s for read", f.m_SrcFilename);
localdev.Delete(destname);
fiHandle dst = localdev.Create(destname);
streamAssertf(dst != fiHandleInvalid, "Error opening %s for write", destname);
u32 size = srcdevice->GetFileSize(f.m_SrcFilename);
u32 copied = 0;
while (copied < size)
{
char buffer[32768];
int amtRead = Min(sizeof(buffer), size - copied);
srcdevice->Read(src, buffer, amtRead);
if (localdev.Write(dst, buffer, amtRead) != amtRead)
break;
copied += amtRead;
m_BytesCopied += amtRead;
if (timer.GetTime() > nextProgressReport)
{
nextProgressReport += 1.0f;
/*streamDisplayf("Copied %llu/%lluMB (%llu%%)",
m_BytesCopied/(1024*1024), m_InstallSize/(1024*1024),
m_BytesCopied * 100 / m_InstallSize);*/
if (m_progressCallback)
m_progressCallback(InstallStatus(), InstallProgress());
}
if (CSystem::WantToExit())
break;
}
srcdevice->Close(src);
localdev.Close(dst);
if (copied == size)
{
// copy file timestamp
u64 timestamp = srcdevice->GetFileTime(f.m_SrcFilename);
localdev.SetFileTime(destname, timestamp);
f.m_Installed = true;
}
else
{
m_InstallFailed = true;
break;
}
}
}
m_progressCallback(InstallStatus(), 1.0f);
streamDisplayf("..INSTALLED in %i seconds!", (int)timer.GetTime());
if (m_InstallFailed)
{
streamErrorf("Game Data installation failed!");
}
}
#if __DEV
static void DumpCellGameDataStatGet(CellGameDataStatGet *get)
{
streamDisplayf("Dump CellGameDataStatGet in CellGameDataStatCallback--------------------" );
streamDisplayf("\tget->hddFreeSizeKB 0x%x", get->hddFreeSizeKB);
streamDisplayf("\tget->isNewData: %d", get->isNewData );
streamDisplayf("\tget->contentInfoPath : %s", get->contentInfoPath );
streamDisplayf("\tget->gameDataPath : %s", get->gameDataPath );
streamDisplayf("\tget->sizeKB : %d", get->sizeKB);
streamDisplayf("\tget->sysSizeKB : 0x%x", get->sysSizeKB);
streamDisplayf("\tget->%lld %lld %lld", get->st_atime, get->st_ctime, get->st_mtime );
streamDisplayf("\n\tPARAM.SFO:TITLE = [%s]", get->getParam.title );
// titleLang
for(int i=0 ; i < 16 ; i++)
{
streamDisplayf("\tPARAM.SFO:TITLE%02d = [%s]", i, get->getParam.titleLang[i]);
}
streamDisplayf("\tPARAM.SFO:TITLEID = [%s]", get->getParam.titleId );
streamDisplayf("\tPARAM.SFO:VERSION = [%s]", get->getParam.dataVersion );
//streamDisplayf("\tPARAM.SFO:ATTRIBUTE = [%d]", get->getParam.attribute ); // no longer used with 210 SDK - KS
//streamDisplayf("\tPARAM.SFO:PARENTAL_LEVEL = [%d]\n", get->getParam.parentalLevel );// no longer used with 210 SDK - KS
}
#endif
void fiPsnInstallerDevice::GetInstalledPath(char* buf, u32 bufsize, const fiInstalledFile& f) const
{
if (f.m_IsContentInfo)
safecpy(buf, m_ContentInfoDir, bufsize);
else
safecpy(buf, m_GameDataDir, bufsize);
safecat(buf, f.m_InstallName, bufsize);
}
void fiPsnInstallerDevice::GetInstalledPath(char* buf, u32 bufsize, const char* path) const
{
safecpy(buf, m_GameDataDir, bufsize);
safecat(buf, ASSET.FileName(path), bufsize);
}
void fiPsnInstallerDevice::GameDataStatCallbackStub(CellGameDataCBResult* result,
CellGameDataStatGet* get, CellGameDataStatSet* set)
{
s_InstallerDevice->GameDataStatCallback(result, get, set);
}
#if !__FINAL
PARAM(fakefreespace, "Fake amount of free hard disk space (in KB)");
#endif
void fiPsnInstallerDevice::GameDataStatCallback(CellGameDataCBResult* result,
CellGameDataStatGet* get, CellGameDataStatSet* set)
{
DEV_ONLY(DumpCellGameDataStatGet(get);)
// initialise the game data device to the correct path
safecpy(m_GameDataDir, get->gameDataPath, NELEM(m_GameDataDir));
char* end = &m_GameDataDir[strlen(m_GameDataDir)-1];
if (*end != '/' && *end != '\\')
{
*++end = '/'; *++end = 0;
}
safecpy(m_ContentInfoDir, get->contentInfoPath, NELEM(m_ContentInfoDir));
end = &m_ContentInfoDir[strlen(m_ContentInfoDir)-1];
if (*end != '/' && *end != '\\')
{
*++end = '/'; *++end = 0;
}
int requiredSpace = 0;
#if !__FINAL
if (PARAM_fakefreespace.Get(get->hddFreeSizeKB))
streamDisplayf("Faking %iKB free space", get->hddFreeSizeKB);
#endif
if (get->isNewData)
{
streamDisplayf("Creating new game data at %s", get->contentInfoPath);
requiredSpace = get->sysSizeKB;
}
PrepareNewGameData(get->getParam);
requiredSpace += CalculateSizeToInstall();
if (requiredSpace > get->hddFreeSizeKB)
{
result->errNeedSizeKB = requiredSpace - get->hddFreeSizeKB;
result->result = CELL_GAMEDATA_CBRESULT_ERR_NOSPACE;
m_NotEnoughSpace = true;
streamErrorf("HDD size check error. needs %d KB disc space more.", result->errNeedSizeKB);
return;
}
set->setParam = &get->getParam;
set->reserved = NULL;
result->result = CELL_GAMEDATA_CBRESULT_OK;
result->reserved = NULL;
}
void fiPsnInstallerDevice::PrepareNewGameData(CellGameDataSystemFileParam& param)
{
// may be better just to declare a constant CellGameDataSystemFileParam somewhere?
// GxtChar *pTempGxtString = TheText.Get("MO_JIM_LEVEL");
strcpy(param.title, (char *) "FRAMEWORK"); //SAMPLE_PARAMSFO_TITLE );
// strcpy(get->getParam.titleLang[0], SAMPLE_PARAMSFO_TITLE00 );
// strcpy(get->getParam.titleLang[1], SAMPLE_PARAMSFO_TITLE01 );
// strcpy(get->getParam.titleLang[2], SAMPLE_PARAMSFO_TITLE02 );
// strcpy(get->getParam.titleLang[3], SAMPLE_PARAMSFO_TITLE03 );
// strcpy(get->getParam.titleLang[4], SAMPLE_PARAMSFO_TITLE04 );
// strcpy(get->getParam.titleLang[5], SAMPLE_PARAMSFO_TITLE05 );
// strcpy(get->getParam.titleLang[6], SAMPLE_PARAMSFO_TITLE06 );
// strcpy(get->getParam.titleLang[7], SAMPLE_PARAMSFO_TITLE07 );
// strcpy(get->getParam.titleLang[8], SAMPLE_PARAMSFO_TITLE08 );
// strcpy(get->getParam.titleLang[9], SAMPLE_PARAMSFO_TITLE09 );
// strcpy(get->getParam.titleLang[10], SAMPLE_PARAMSFO_TITLE10 );
// strcpy(get->getParam.titleLang[11], SAMPLE_PARAMSFO_TITLE11 );
// strcpy(get->getParam.titleLang[12], SAMPLE_PARAMSFO_TITLE12 );
// strcpy(get->getParam.titleLang[13], SAMPLE_PARAMSFO_TITLE13 );
// strcpy(get->getParam.titleLang[14], SAMPLE_PARAMSFO_TITLE14 );
// strcpy(get->getParam.titleLang[15], SAMPLE_PARAMSFO_TITLE15 );
strcpy(param.titleId, XEX_TITLE_ID );
strcpy(param.dataVersion, SAMPLE_PARAMSFO_VERSION );
//param.parentalLevel = SAMPLE_PARAMSFO_PARENTALLEV; // no longer used with 210 SDK - KS
//param.attribute = SAMPLE_PARAMSFO_ATTRIBUTE; // no longer used with 210 SDK - KS
}
static bool TimeIsClose(u64 timeA,u64 timeB)
{
// Time is in 100ns units. FAT filesystem is only accurate to two seconds.
const u64 minTime = 3 * 10000000;
if (timeA > timeB)
return timeA - timeB < minTime;
else
return timeB - timeA < minTime;
}
u32 fiPsnInstallerDevice::CalculateSizeToInstall()
{
u32 requiredSpace = 0;
m_InstallSize = 0;
const fiDevice& localdev = fiDeviceLocal::GetInstance();
for(atArray<fiInstalledFile>::iterator it = m_Files.begin(); it != m_Files.end(); ++it)
{
fiInstalledFile& f = *it;
char destname[2048];
GetInstalledPath(destname, NELEM(destname), f);
u64 dstsize = localdev.GetFileSize(destname);
u64 dsttime = localdev.GetFileTime(destname);
const fiDevice* srcdevice = fiDevice::GetDevice(f.m_SrcFilename, true);
u64 srcsize = srcdevice->GetFileSize(f.m_SrcFilename);
u64 srctime = srcdevice->GetFileTime(f.m_SrcFilename);
if (dstsize != srcsize || !TimeIsClose(dsttime, srctime) || m_ForceReinstall)
{
requiredSpace -= (dstsize + 1023) / 1024; // space reclaimed by deleting old file
requiredSpace += (srcsize + 1023) / 1024;
m_InstallSize += srcsize;
}
else
{
streamDisplayf("%s already installed at %s", f.m_SrcFilename, destname);
f.m_Installed = true;
}
}
return requiredSpace;
}
fiHandle fiPsnInstallerDevice::InstallIconFile(const char* path)
{
return InstallFile(path, true, "ICON0.PNG", true);
}
void fiPsnInstallerDevice::SetProgressCallback(Functor2<eInstallStatus, float> fn)
{
m_progressCallback = fn;
}
void fiPsnInstallerDevice::SetProgress(eInstallStatus status, float progress)
{
if(m_progressCallback)
m_progressCallback(status, progress);
}
float fiPsnInstallerDevice::InstallProgress()
{
if (m_InstallSize == 0)
return 1.0f;
return float(m_BytesCopied) / m_InstallSize;
}
eInstallStatus fiPsnInstallerDevice::InstallStatus()
{
if(m_StartedInstalling == false)
return INSTALL_NOT_STARTED;
else if (m_NotEnoughSpace)
return INSTALL_ERROR_NOT_ENOUGH_SPACE;
else if (m_InstallFailed)
return INSTALL_ERROR_INSTALLING;
return INSTALL_OK;
}
#endif // __PPU