2061 lines
74 KiB
C++
2061 lines
74 KiB
C++
|
//========= Copyright <20> 2006, Electonic Arts(C) 2006 - All Rights Reserved ============//
|
|||
|
|
|||
|
#include "tier0/platform.h"
|
|||
|
|
|||
|
#ifdef _PS3
|
|||
|
#include "FileGroup.h"
|
|||
|
#include <unistd.h>
|
|||
|
#include <sys/timer.h>
|
|||
|
#include "memalloc.h"
|
|||
|
// #include "debug/inc/debug.h"
|
|||
|
// #include <tty.h>
|
|||
|
#include "generichash.h"
|
|||
|
#include <ctype.h>
|
|||
|
#include "ps3/ps3_console.h"
|
|||
|
#include <sys/stat.h>
|
|||
|
#include "tier1/strtools.h"
|
|||
|
#include "tier0/dbg.h"
|
|||
|
//#include <libsntuner.h>
|
|||
|
#ifdef _DEBUG
|
|||
|
int CFileGroupOpenedFile::refCount = 0;
|
|||
|
int CFileGroupSystem::refCount = 0;
|
|||
|
int CFileGroup::refCount = 0;
|
|||
|
#endif
|
|||
|
|
|||
|
const int MAX_OPENED_FILES = 8;
|
|||
|
const int FILEGROUP_READ_CMD_PRIO = 1000;
|
|||
|
const int FILEGROUP_READ_CMD_STACKSIZE = 16*1024;
|
|||
|
|
|||
|
#define DebugPrint(fmt, ...) Msg(fmt, ## __VA_ARGS__)
|
|||
|
|
|||
|
static void TidyUpSynchObjects(FileGroupReadBuffer* pBuf)
|
|||
|
{
|
|||
|
int ret = sys_lwmutex_destroy(&pBuf->mBufferStartEndPosMutex);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
printf("TidyUpSynchObjects sys_lwmutex_destroy failed: %d\n", ret);
|
|||
|
exit(ret);
|
|||
|
}
|
|||
|
ret = sys_cond_destroy(pBuf->mReadThreadSignal);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
printf("TidyUpSynchObjects sys_cond_destroy failed: %d\n", ret);
|
|||
|
exit(ret);
|
|||
|
}
|
|||
|
ret = sys_mutex_destroy(pBuf->mReadThreadSignalMutex);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
printf("TidyUpSynchObjects sys_mutex_destroy failed: %d\n", ret);
|
|||
|
exit(ret);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void FileGroupReadBuffer::WaitOnDiskEjectRecovery()
|
|||
|
{
|
|||
|
//Grab mutex
|
|||
|
int ret = sys_lwmutex_trylock(&mEjectRecoveryMutex);
|
|||
|
|
|||
|
if (ret == CELL_OK)
|
|||
|
{
|
|||
|
int fileGroupHandle = 0;
|
|||
|
CellFsErrno retFs;
|
|||
|
while(retFs != CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
if(fileGroupHandle)
|
|||
|
{
|
|||
|
cellFsClose(fileGroupHandle);
|
|||
|
}
|
|||
|
retFs = PS3_RetryOpenWhileDiskEjected(mFileName, CELL_FS_O_RDONLY, &fileGroupHandle);
|
|||
|
if (retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
mFile = fileGroupHandle;
|
|||
|
uint64_t pos = 0;
|
|||
|
retFs = cellFsLseek(mFile,(uint64_t)mCurrentPos,CELL_FS_SEEK_SET,&pos);
|
|||
|
if(retFs == CELL_FS_EIO)
|
|||
|
{
|
|||
|
PS3_DisplayDiskErrorDialog(); //Assume dirty disk, rather than disk eject if this point is reached
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Msg("FileGroupReadBuffer::WaitOnDiskEjectRecovery: Fatal disk error\n");
|
|||
|
exit(-1);
|
|||
|
}
|
|||
|
}
|
|||
|
PS3_ClearDiskErrorDialog();
|
|||
|
sys_lwmutex_unlock(&mEjectRecoveryMutex);
|
|||
|
}
|
|||
|
else //i.e. another thread is running
|
|||
|
{
|
|||
|
Msg("Disk eject waiting on retry\n");
|
|||
|
ret = sys_lwmutex_lock(&mEjectRecoveryMutex,0); //Wait for disk eject recovery to complete
|
|||
|
ret = sys_lwmutex_unlock(&mEjectRecoveryMutex);
|
|||
|
Msg("Disk eject waiting on retry complete\n");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void FileGroupReadBuffer::CloseFile()
|
|||
|
{
|
|||
|
if (mFile)
|
|||
|
{
|
|||
|
cellFsClose(mFile);
|
|||
|
mFile = NULL;
|
|||
|
int ret = sys_lwmutex_destroy(&mEjectRecoveryMutex);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
Msg("sys_lwmutex_destroy failed for mEjectRecoveryMutex: %d\n", ret);
|
|||
|
}
|
|||
|
ret = sys_lwmutex_destroy(&mBufferMutex);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
printf("TidyUpSynchObjects sys_lwmutex_destroy failed: %d\n", ret);
|
|||
|
}
|
|||
|
ret = sys_lwmutex_destroy(&mNextConsumeByteMutex);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
printf("TidyUpSynchObjects sys_lwmutex_destroy failed: %d\n", ret);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void FileGroupReadBuffer::AllocateBuffer(size_t readBufferSize)
|
|||
|
{
|
|||
|
MEM_ALLOC_CREDIT();
|
|||
|
if (mBuffer)
|
|||
|
{
|
|||
|
FreeBuffer();
|
|||
|
}
|
|||
|
|
|||
|
mBuffer = new unsigned char[readBufferSize];
|
|||
|
mBufferSize = readBufferSize;
|
|||
|
mCurrentByte = mBuffer;
|
|||
|
}
|
|||
|
|
|||
|
void FileGroupReadBuffer::FreeBuffer()
|
|||
|
{
|
|||
|
if (mBuffer)
|
|||
|
{
|
|||
|
delete[] mBuffer;
|
|||
|
mBuffer = NULL;
|
|||
|
mBufferSize = 0;
|
|||
|
mCurrentByte = NULL;
|
|||
|
mNextConsumePos = 0;
|
|||
|
mAwaitingConsumer = false;
|
|||
|
mBufferEndPos = 0;
|
|||
|
mBufferStartPos = 0;
|
|||
|
mCurrentPos = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void thr_FileGroupRead(uint64_t arg)
|
|||
|
{
|
|||
|
FileGroupReadBuffer* pBuf = reinterpret_cast<FileGroupReadBuffer*>(arg);
|
|||
|
while (1) {
|
|||
|
int ret = sys_mutex_lock(pBuf->mReadThreadSignalMutex, 0);
|
|||
|
while (!pBuf->mAwaitingConsumer && !pBuf->mFileGroupDeleted) // Data ready for read and filegroup not flagged for deletion
|
|||
|
{
|
|||
|
ret = sys_lwmutex_lock(&pBuf->mBufferMutex,0);
|
|||
|
//Read next chunk of data
|
|||
|
if ((pBuf->mBufferEndPos > 0) && //i.e. we're overwriting existing data
|
|||
|
(pBuf->mNextConsumePos > 0) &&
|
|||
|
((pBuf->mCurrentPos + FILEGROUP_READ_CHUNK_SIZE) - pBuf->mNextConsumePos) > (pBuf->mBufferSize - FILEGROUP_READ_STORE_SIZE))
|
|||
|
{
|
|||
|
pBuf->mAwaitingConsumer = true; //Need to wait for consumer to read more data
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
|
|||
|
//Do the seek and read
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
system_time_t time1 = sys_time_get_system_time();
|
|||
|
#endif
|
|||
|
uint64_t pos;
|
|||
|
uint64_t bytesRead;
|
|||
|
ret = cellFsLseek(pBuf->mFile,(int64_t)pBuf->mCurrentPos,CELL_FS_SEEK_SET,&pos);
|
|||
|
if (ret == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
ret = cellFsRead(pBuf->mFile,pBuf->mCurrentByte,FILEGROUP_READ_CHUNK_SIZE,&bytesRead);
|
|||
|
}
|
|||
|
while (ret!= CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
cellFsClose(pBuf->mFile);
|
|||
|
sys_timer_usleep(250000); //Sleep for 0.25 seconds
|
|||
|
pBuf->WaitOnDiskEjectRecovery();
|
|||
|
ret = cellFsLseek(pBuf->mFile,(int64_t)pBuf->mCurrentPos,CELL_FS_SEEK_SET,&pos);
|
|||
|
if(ret == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
ret = cellFsRead(pBuf->mFile,pBuf->mCurrentByte,FILEGROUP_READ_CHUNK_SIZE,&bytesRead);
|
|||
|
}
|
|||
|
if(ret == CELL_FS_EIO) //Assume dirty disk
|
|||
|
{
|
|||
|
PS3_DisplayDiskErrorDialog();
|
|||
|
}
|
|||
|
else if (ret == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
PS3_ClearDiskErrorDialog();
|
|||
|
}
|
|||
|
}
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
system_time_t time2 = sys_time_get_system_time();
|
|||
|
pBuf->m_fReadTime += (time2-time1);
|
|||
|
pBuf->m_fReadBytes += bytesRead;
|
|||
|
#endif
|
|||
|
|
|||
|
if (pBuf->mCurrentByte == pBuf->mBuffer)
|
|||
|
{
|
|||
|
ret = sys_lwmutex_lock(&pBuf->mBufferStartEndPosMutex,0);
|
|||
|
pBuf->mBufferStartPos = pBuf->mCurrentPos;
|
|||
|
ret = sys_lwmutex_unlock(&pBuf->mBufferStartEndPosMutex);
|
|||
|
}
|
|||
|
pBuf->mCurrentPos += bytesRead;
|
|||
|
pBuf->mCurrentByte += bytesRead;
|
|||
|
if (bytesRead == FILEGROUP_READ_CHUNK_SIZE)
|
|||
|
{
|
|||
|
if ((pBuf->mCurrentByte - pBuf->mBuffer) >= pBuf->mBufferSize)
|
|||
|
{
|
|||
|
//We've reached the end of the buffer
|
|||
|
sys_lwmutex_lock(&pBuf->mBufferStartEndPosMutex,0); //Prevent any 'consumes' from calculating available data while buffer is reset
|
|||
|
pBuf->mBufferEndPos = pBuf->mCurrentPos;
|
|||
|
pBuf->mCurrentByte = pBuf->mBuffer;
|
|||
|
if (pBuf->mNextConsumePos == 0)
|
|||
|
{
|
|||
|
pBuf->mAwaitingConsumer = true;
|
|||
|
}
|
|||
|
sys_lwmutex_unlock(&pBuf->mBufferStartEndPosMutex);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//Assume EOF - await signal from consumer that there is more data to read
|
|||
|
pBuf->mAwaitingConsumer = true;
|
|||
|
}
|
|||
|
}
|
|||
|
ret = sys_lwmutex_unlock(&pBuf->mBufferMutex);
|
|||
|
}
|
|||
|
if (pBuf->mFileGroupDeleted)
|
|||
|
{
|
|||
|
int ret = sys_mutex_unlock(pBuf->mReadThreadSignalMutex);
|
|||
|
TidyUpSynchObjects(pBuf);
|
|||
|
pBuf->CloseFile();
|
|||
|
pBuf->FreeBuffer();
|
|||
|
pBuf->mReadThread = NULL;
|
|||
|
pBuf->mFileGroupDeleted = false;
|
|||
|
sys_ppu_thread_exit(1);
|
|||
|
}
|
|||
|
ret = sys_cond_wait(pBuf->mReadThreadSignal, 0);
|
|||
|
if (ret == CELL_OK)
|
|||
|
{
|
|||
|
pBuf->mAwaitingConsumer = false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CFileGroupSystem::CFileGroupSystem()
|
|||
|
{
|
|||
|
m_numFileGroups = 0;
|
|||
|
m_lastGroupPopulated = -1;
|
|||
|
m_currentFileGroup = 0;
|
|||
|
for(int i=0;i<MAX_FILE_GROUPS;i++)
|
|||
|
{
|
|||
|
m_fileGroups[i]=NULL;
|
|||
|
}
|
|||
|
|
|||
|
//Create the filesystem mutex
|
|||
|
sys_lwmutex_attribute_t mutexAttr;
|
|||
|
sys_lwmutex_attribute_initialize(mutexAttr);
|
|||
|
mutexAttr.attr_recursive = SYS_SYNC_RECURSIVE;
|
|||
|
int ret = sys_lwmutex_create(&mFileGroupSystemMutex, &mutexAttr);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
printf("CFileGroupSystem::CFileGroupSystem sys_lwmutex_create failed: %d\n", ret);
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
m_openedFilePool = NULL;
|
|||
|
m_availableFileNodes = NULL;
|
|||
|
m_openedFileNodes = NULL;
|
|||
|
m_totalOpenedFiles = 0;
|
|||
|
m_initComplete = false;
|
|||
|
|
|||
|
#ifdef _DEBUG
|
|||
|
refCount++;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
CFileGroupSystem::~CFileGroupSystem()
|
|||
|
{
|
|||
|
for(int i=0;i<MAX_FILE_GROUPS;i++)
|
|||
|
{
|
|||
|
if (m_fileGroups[i])
|
|||
|
{
|
|||
|
m_fileGroups[i]->Clear();
|
|||
|
delete m_fileGroups[i];
|
|||
|
}
|
|||
|
}
|
|||
|
int ret = sys_lwmutex_destroy(&mFileGroupSystemMutex);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
printf("CFileGroupSystem::~CFileGroupSystem sys_lwmutex_destroy failed: %d\n", ret);
|
|||
|
}
|
|||
|
#ifdef _DEBUG
|
|||
|
refCount--;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroupSystem::Init()
|
|||
|
{
|
|||
|
MEM_ALLOC_CREDIT();
|
|||
|
|
|||
|
//Initialise pool of CFileGroupOpenedFile objects:
|
|||
|
DebugPrint("Initialising pool of %d FileGroupFilePoolNodes for filegroupsystem\n", MAX_OPENED_FILES);
|
|||
|
m_openedFilePool = new FileGroupFilePoolNode[MAX_OPENED_FILES];
|
|||
|
m_availableFileNodes = m_openedFilePool;
|
|||
|
for(int i=0;i<(MAX_OPENED_FILES-1);i++)
|
|||
|
{
|
|||
|
m_openedFilePool[i].nextNode = &m_openedFilePool[i+1];
|
|||
|
}
|
|||
|
m_openedFilePool[MAX_OPENED_FILES-1].nextNode = NULL;
|
|||
|
|
|||
|
//DebugPrint("Initialising filegroup objects for filegroupsystem\n");
|
|||
|
for (int i = 0; i<(MAX_FILE_GROUPS); i++)
|
|||
|
{
|
|||
|
m_fileGroups[i] = new CFileGroup(this);
|
|||
|
m_fileGroups[i]->Clear();
|
|||
|
}
|
|||
|
//int fileSystemMemAllocation = sizeof(FileGroupFilePoolNode)*MAX_OPENED_FILES + sizeof(CFileGroup)*(MAX_FILE_GROUPS);
|
|||
|
//DebugPrint("Approx filegroupsystem memory usage: %d\n", fileSystemMemAllocation);
|
|||
|
m_initComplete = true;
|
|||
|
}
|
|||
|
|
|||
|
int CFileGroupSystem::AddFileGroup(const char* fileGroupName, bool useReadThread /* = true */, int* group_index /* = NULL */, bool usePreload /* = false */, int bufferSize /* = FILEGROUP_READ_CHUNK_SIZE */, int readChunkSize /* = FILEGROUP_READ_CHUNK_SIZE */)
|
|||
|
{
|
|||
|
Lock(); //Critical Section
|
|||
|
|
|||
|
int retVal = -1;
|
|||
|
int fileGroupIndex = -1;
|
|||
|
if (group_index)
|
|||
|
{
|
|||
|
*group_index = -1;
|
|||
|
}
|
|||
|
if( bufferSize < FILEGROUP_READ_CHUNK_SIZE * 4 )
|
|||
|
{
|
|||
|
bufferSize = FILEGROUP_READ_CHUNK_SIZE * 4;
|
|||
|
}
|
|||
|
Assert(readChunkSize <= bufferSize);
|
|||
|
if(readChunkSize <= bufferSize)
|
|||
|
{
|
|||
|
if (!m_initComplete) //Delay initialisation until this point so that we don't allocate buffer memory unless it's needed
|
|||
|
{
|
|||
|
Init();
|
|||
|
}
|
|||
|
CFileGroup* newGroup = NULL;
|
|||
|
//Get free filegroup object
|
|||
|
for(int i=0; (i<MAX_FILE_GROUPS) && !newGroup ;i++)
|
|||
|
{
|
|||
|
if (!m_fileGroups[i]->IsPopulated())
|
|||
|
{
|
|||
|
newGroup = m_fileGroups[i];
|
|||
|
fileGroupIndex = i;
|
|||
|
}
|
|||
|
}
|
|||
|
if (newGroup)
|
|||
|
{
|
|||
|
newGroup->Clear();
|
|||
|
int entriesPopulated = newGroup->Populate(fileGroupName, usePreload);
|
|||
|
if (entriesPopulated > 0)
|
|||
|
{
|
|||
|
retVal = entriesPopulated;
|
|||
|
|
|||
|
if (useReadThread)
|
|||
|
{
|
|||
|
newGroup->AllocateReadBuffer(bufferSize);
|
|||
|
newGroup->StartReadThread();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
newGroup->AllocateReadBuffer(bufferSize);
|
|||
|
newGroup->SetReadChunkSize(readChunkSize);
|
|||
|
}
|
|||
|
|
|||
|
if (fileGroupIndex > -1)
|
|||
|
{
|
|||
|
m_numFileGroups++;
|
|||
|
m_currentFileGroup = fileGroupIndex;
|
|||
|
if (group_index)
|
|||
|
{
|
|||
|
*group_index = fileGroupIndex; //No need to set last group populated - assume caller will specify the index on calls to deletefilegroup
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_lastGroupPopulated = fileGroupIndex;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
DebugPrint("CFileGroupSystem: Successfully opened file group named %s as index %d\n", fileGroupName, fileGroupIndex);
|
|||
|
if(usePreload)
|
|||
|
{
|
|||
|
DebugPrint(" using preload\n");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DebugPrint("\n");
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if(entriesPopulated < 0)
|
|||
|
{
|
|||
|
DebugPrint("CFileGroupSystem: Error opening file group named %s\n", fileGroupName);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DebugPrint("Error opening file group %s, no entries found.\n", fileGroupName);
|
|||
|
}
|
|||
|
newGroup->Clear();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DebugPrint("CFileGroupSystem: Error opening file group %s. All %d file groups are already in use.\n", fileGroupName, MAX_FILE_GROUPS);
|
|||
|
}
|
|||
|
}
|
|||
|
Unlock(); //Critical Section End
|
|||
|
return retVal;
|
|||
|
}
|
|||
|
|
|||
|
int CFileGroupSystem::CurrentFileGroup()
|
|||
|
{
|
|||
|
return m_lastGroupPopulated;
|
|||
|
}
|
|||
|
|
|||
|
//Delete most recently added file group
|
|||
|
void CFileGroupSystem::DeleteFileGroup()
|
|||
|
{
|
|||
|
Lock();
|
|||
|
if (m_lastGroupPopulated > -1)
|
|||
|
{
|
|||
|
DeleteFileGroup(m_lastGroupPopulated);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DebugPrint("CFileGroupSystem::deleteFileGroup Error, no filegroups exist\n");
|
|||
|
}
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroupSystem::DeleteFileGroup(int groupIndex)
|
|||
|
{
|
|||
|
Lock();
|
|||
|
if (groupIndex > -1 && m_fileGroups[groupIndex])
|
|||
|
{
|
|||
|
DeleteFileGroup(m_fileGroups[groupIndex], groupIndex);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DebugPrint("CFileGroupSystem::deleteFileGroup Error, filegroup %d does not exist\n", groupIndex);
|
|||
|
}
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroupSystem::DeleteFileGroup(CFileGroup* fileGroup)
|
|||
|
{
|
|||
|
Lock();
|
|||
|
for(int i=0; i<MAX_FILE_GROUPS; i++)
|
|||
|
{
|
|||
|
if (m_fileGroups[i] == fileGroup)
|
|||
|
{
|
|||
|
DeleteFileGroup(fileGroup, i);
|
|||
|
}
|
|||
|
}
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
void CFileGroup::PrintFileGroupStats()
|
|||
|
{
|
|||
|
DebugPrint("\n");
|
|||
|
DebugPrint("Stats for file group %s:\n", mReadBuffer.mFileName);
|
|||
|
DebugPrint("filegroup, Memcpy(s), fread(s), inflate(s), fsreads, fgets, inflates, rewinds, filejumps, bytes from buffer, bytes from file, fread(bytes)\n");
|
|||
|
DebugPrint("Level,%5.2f,%5.2f,%5.2f,%d,%d,%d,%d,%d,%d,%d,%d\n",
|
|||
|
float(m_memCopyTime)/1000000.0, float(GetfReadTime())/1000000.0, float(m_uncompressTime)/1000000.0,
|
|||
|
m_fsReads, m_fgets, m_uncompressCalls, m_rewinds, m_fileJumps,
|
|||
|
m_bytesReadFromBuffer, m_bytesReadFromFileByConsumer, GetfReadBytes());
|
|||
|
DebugPrint("\n");
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
void CFileGroupSystem::DeleteFileGroup(CFileGroup* fileGroup, int groupIndex)
|
|||
|
{
|
|||
|
Lock();
|
|||
|
if (fileGroup->GetOpenedCount() == 0)
|
|||
|
{
|
|||
|
DebugPrint("Deleting file group %d\n", groupIndex);
|
|||
|
fileGroup->Clear();
|
|||
|
m_numFileGroups--;
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
fileGroup->PrintFileGroupStats();
|
|||
|
#endif
|
|||
|
if(fileGroup->UsingReadThread())
|
|||
|
{
|
|||
|
fileGroup->StopReadThread();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fileGroup->CloseFile();
|
|||
|
fileGroup->FreeReadBuffer();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DebugPrint("Flagging file group %d for deletion, %d files still open\n", groupIndex, fileGroup->GetOpenedCount());
|
|||
|
fileGroup->FlagForDeletion();
|
|||
|
}
|
|||
|
if (m_currentFileGroup == groupIndex)
|
|||
|
{
|
|||
|
m_currentFileGroup = 0;
|
|||
|
}
|
|||
|
if (m_lastGroupPopulated == groupIndex)
|
|||
|
{
|
|||
|
m_lastGroupPopulated = -1;
|
|||
|
}
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
|
|||
|
CFileGroupOpenedFile* CFileGroupSystem::FS_fopen( FileIdentifier *filename, const char *options, unsigned int flags, __int64 *size )
|
|||
|
{
|
|||
|
Lock(); //Critical section
|
|||
|
CFileGroupOpenedFile* retVal = NULL;
|
|||
|
if ( options[0] == 'r' ) //Filegroup files can only be opened for read
|
|||
|
{
|
|||
|
int iFileGroup = m_currentFileGroup; //Start at the current file group
|
|||
|
for(int i=0; (i<MAX_FILE_GROUPS) && (!retVal) ;i++, iFileGroup++)
|
|||
|
{
|
|||
|
//At the end of the list, loop back to the beginning:
|
|||
|
if (iFileGroup==MAX_FILE_GROUPS)
|
|||
|
{
|
|||
|
iFileGroup=0;
|
|||
|
}
|
|||
|
if (m_fileGroups[iFileGroup] && m_fileGroups[iFileGroup]->IsPopulated())
|
|||
|
{
|
|||
|
retVal = m_fileGroups[iFileGroup]->FS_fopen(filename,options,flags,size);
|
|||
|
if (retVal)
|
|||
|
{
|
|||
|
m_currentFileGroup = iFileGroup;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Unlock();
|
|||
|
return retVal;
|
|||
|
}
|
|||
|
|
|||
|
CFileGroupOpenedFile* CFileGroupSystem::FS_fopen( int filegroup_index, FileIdentifier *filename, const char *options, unsigned int flags, __int64 *size )
|
|||
|
{
|
|||
|
Lock(); //Critical section
|
|||
|
CFileGroupOpenedFile* retVal = NULL;
|
|||
|
if ( options[0] == 'r' ) //Filegroup files can only be opened for read
|
|||
|
{
|
|||
|
if (filegroup_index >= 0 && filegroup_index < MAX_FILE_GROUPS)
|
|||
|
{
|
|||
|
if (m_fileGroups[filegroup_index] && m_fileGroups[filegroup_index]->IsPopulated())
|
|||
|
{
|
|||
|
retVal = m_fileGroups[filegroup_index]->FS_fopen(filename,options,flags,size);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
Unlock();
|
|||
|
return retVal;
|
|||
|
}
|
|||
|
|
|||
|
int CFileGroupSystem::FS_stat( FileIdentifier *path, CellFsStat *buf )
|
|||
|
{
|
|||
|
Lock(); //Critical section
|
|||
|
int retVal = -1;
|
|||
|
int iFileGroup = m_currentFileGroup; //Start at the current file group
|
|||
|
for(int i=0; (i<MAX_FILE_GROUPS) && (retVal == -1) ;i++, iFileGroup++)
|
|||
|
{
|
|||
|
//At the end of the list, loop back to the beginning:
|
|||
|
if (iFileGroup==MAX_FILE_GROUPS)
|
|||
|
{
|
|||
|
iFileGroup=0;
|
|||
|
}
|
|||
|
if (m_fileGroups[i] && m_fileGroups[i]->IsPopulated())
|
|||
|
{
|
|||
|
retVal = m_fileGroups[i]->FS_stat(path,buf);
|
|||
|
}
|
|||
|
}
|
|||
|
Unlock();
|
|||
|
return retVal;
|
|||
|
}
|
|||
|
|
|||
|
int CFileGroupSystem::FS_stat( int filegroup_index, FileIdentifier *path, CellFsStat *buf )
|
|||
|
{
|
|||
|
Lock(); //Critical section
|
|||
|
int retVal = -1;
|
|||
|
if (filegroup_index >= 0 && filegroup_index < MAX_FILE_GROUPS)
|
|||
|
{
|
|||
|
if (m_fileGroups[filegroup_index] && m_fileGroups[filegroup_index]->IsPopulated())
|
|||
|
{
|
|||
|
retVal = m_fileGroups[filegroup_index]->FS_stat(path,buf);
|
|||
|
}
|
|||
|
}
|
|||
|
Unlock();
|
|||
|
return retVal;
|
|||
|
}
|
|||
|
|
|||
|
int CFileGroupSystem::FS_stat( FileIdentifier *path, struct stat *buf )
|
|||
|
{
|
|||
|
int ret;
|
|||
|
CellFsStat cellBuf;
|
|||
|
CellFsErrno retFs = FS_stat(path, &cellBuf);
|
|||
|
if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
buf->st_atime = cellBuf.st_atime;
|
|||
|
buf->st_blksize = cellBuf.st_blksize;
|
|||
|
buf->st_ctime = cellBuf.st_ctime;
|
|||
|
buf->st_gid = cellBuf.st_gid;
|
|||
|
buf->st_mode = cellBuf.st_mode;
|
|||
|
buf->st_mtime = cellBuf.st_mtime;
|
|||
|
buf->st_size = cellBuf.st_size;
|
|||
|
buf->st_uid = cellBuf.st_uid;
|
|||
|
buf->st_dev = 0;
|
|||
|
buf->st_ino = 0;
|
|||
|
buf->st_nlink = 0;
|
|||
|
buf->st_rdev = 0;
|
|||
|
buf->st_blocks = 0;
|
|||
|
ret = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ret = -1;
|
|||
|
//TBD: SET ERRNO
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
int CFileGroupSystem::FS_stat( int filegroup_index, FileIdentifier *path, struct stat *buf )
|
|||
|
{
|
|||
|
int ret;
|
|||
|
CellFsStat cellBuf;
|
|||
|
CellFsErrno retFs = FS_stat(filegroup_index, path, &cellBuf);
|
|||
|
if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
buf->st_atime = cellBuf.st_atime;
|
|||
|
buf->st_blksize = cellBuf.st_blksize;
|
|||
|
buf->st_ctime = cellBuf.st_ctime;
|
|||
|
buf->st_gid = cellBuf.st_gid;
|
|||
|
buf->st_mode = cellBuf.st_mode;
|
|||
|
buf->st_mtime = cellBuf.st_mtime;
|
|||
|
buf->st_size = cellBuf.st_size;
|
|||
|
buf->st_uid = cellBuf.st_uid;
|
|||
|
buf->st_dev = 0;
|
|||
|
buf->st_ino = 0;
|
|||
|
buf->st_nlink = 0;
|
|||
|
buf->st_rdev = 0;
|
|||
|
buf->st_blocks = 0;
|
|||
|
ret = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ret = -1;
|
|||
|
//TBD: SET ERRNO
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
CFileGroupOpenedFile* CFileGroupSystem::GetOpenedFile() //Get next available CFileGroupOpenedFile from pool
|
|||
|
{
|
|||
|
CFileGroupOpenedFile* retVal = NULL;
|
|||
|
Assert(m_availableFileNodes);
|
|||
|
if (m_availableFileNodes) //Take the next available node from the head of the list
|
|||
|
{
|
|||
|
FileGroupFilePoolNode* thisNode = m_availableFileNodes;
|
|||
|
retVal = &thisNode->thisFile;
|
|||
|
//Remove from list of available nodes
|
|||
|
m_availableFileNodes = thisNode->nextNode;
|
|||
|
//Add to list of opened nodes
|
|||
|
thisNode->nextNode = m_openedFileNodes;
|
|||
|
m_openedFileNodes = thisNode;
|
|||
|
m_totalOpenedFiles++;
|
|||
|
}
|
|||
|
return retVal;
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroupSystem::FreeOpenedFile(CFileGroupOpenedFile* freeFile) //Get next available CFileGroupOpenedFile from pool
|
|||
|
{
|
|||
|
FileGroupFilePoolNode* thisNode = m_openedFileNodes;
|
|||
|
FileGroupFilePoolNode* prevNode = NULL;
|
|||
|
//Find the node object in the list of opened files
|
|||
|
while (thisNode && (&thisNode->thisFile != freeFile))
|
|||
|
{
|
|||
|
prevNode = thisNode;
|
|||
|
thisNode = thisNode->nextNode;
|
|||
|
}
|
|||
|
Assert(thisNode); //Check that the node object was found
|
|||
|
if (thisNode)
|
|||
|
{
|
|||
|
//Remove from list of opened nodes
|
|||
|
if (prevNode)
|
|||
|
{
|
|||
|
prevNode->nextNode = thisNode->nextNode;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_openedFileNodes = thisNode->nextNode;
|
|||
|
}
|
|||
|
m_totalOpenedFiles--;
|
|||
|
//Add to list of available nodes
|
|||
|
thisNode->nextNode = m_availableFileNodes;
|
|||
|
m_availableFileNodes = thisNode;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CFileGroup::CFileGroup(CFileGroupSystem* pFs):
|
|||
|
mDirectoryEntries(NULL),
|
|||
|
mFs(pFs),
|
|||
|
mOpenedCount(0),
|
|||
|
mPreloadSection(NULL),
|
|||
|
mPreloadEntries(0)
|
|||
|
{
|
|||
|
#ifdef _DEBUG
|
|||
|
refCount++;
|
|||
|
#endif
|
|||
|
mReadBuffer.mFile = NULL;
|
|||
|
mReadBuffer.mFileBlockSize = 0;
|
|||
|
mReadBuffer.mFileSectorSize = 0;
|
|||
|
mReadBuffer.mReadThread = NULL;
|
|||
|
mReadBuffer.mAwaitingConsumer = false;
|
|||
|
mReadBuffer.mFileGroupDeleted = false;
|
|||
|
mReadBuffer.mNextConsumePos = 0;
|
|||
|
mReadBuffer.mBuffer = NULL;
|
|||
|
mReadBuffer.mBufferSize = 0;
|
|||
|
mReadBuffer.mCurrentByte = NULL;
|
|||
|
mReadBuffer.mBufferEndPos = 0;
|
|||
|
mReadBuffer.mBufferStartPos = 0;
|
|||
|
mReadBuffer.mCurrentPos = 0;
|
|||
|
mReadBuffer.mReadChunkSize = FILEGROUP_READ_CHUNK_SIZE;
|
|||
|
mFlaggedForDeletion = false;
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
ResetFileTimings();
|
|||
|
#endif
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
mDirectoryExtraInfo = NULL;
|
|||
|
#endif
|
|||
|
#ifdef FILEGROUP_USE_HASH_DIRECTORY
|
|||
|
mHashDirectory = NULL;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
CFileGroup::~CFileGroup()
|
|||
|
{
|
|||
|
if (mDirectoryEntries)
|
|||
|
{
|
|||
|
delete[] mDirectoryEntries;
|
|||
|
}
|
|||
|
if (mReadBuffer.mFile)
|
|||
|
{
|
|||
|
cellFsClose(mReadBuffer.mFile);
|
|||
|
}
|
|||
|
#ifdef _DEBUG
|
|||
|
refCount--;
|
|||
|
#endif
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
if (mDirectoryExtraInfo)
|
|||
|
{
|
|||
|
delete[] mDirectoryExtraInfo;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
unsigned int CFileGroup::HashFileName(const char * fName)
|
|||
|
{
|
|||
|
return HashStringCaselessConventional(fName);
|
|||
|
}
|
|||
|
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
void CFileGroup::ResetFileTimings()
|
|||
|
{
|
|||
|
m_memCopyTime = 0;
|
|||
|
mReadBuffer.m_fReadTime = 0;
|
|||
|
mReadBuffer.m_fReadBytes = 0;
|
|||
|
m_uncompressTime = 0;
|
|||
|
m_fsReads = 0;
|
|||
|
m_fgets = 0;
|
|||
|
m_uncompressCalls = 0;
|
|||
|
m_rewinds = 0;
|
|||
|
m_fileJumps = 0;
|
|||
|
m_bytesReadFromFileByConsumer = 0;
|
|||
|
m_bytesReadFromBuffer = 0;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
void CFileGroup::StartReadThread()
|
|||
|
{
|
|||
|
sys_lwmutex_attribute_initialize(mReadBuffer.mBufferStartEndPosMutexAttr);
|
|||
|
mReadBuffer.mBufferStartEndPosMutexAttr.attr_recursive = SYS_SYNC_RECURSIVE;
|
|||
|
int ret = sys_lwmutex_create(&mReadBuffer.mBufferStartEndPosMutex, &mReadBuffer.mBufferStartEndPosMutexAttr);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
printf("CFileGroup::StartReadThread sys_lwmutex_create failed: %d\n", ret);
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
//Create the condition object
|
|||
|
sys_mutex_attribute_initialize(mReadBuffer.mReadThreadSignalMutexAttr);
|
|||
|
ret = sys_mutex_create(&mReadBuffer.mReadThreadSignalMutex, &mReadBuffer.mReadThreadSignalMutexAttr);
|
|||
|
sys_cond_attribute_initialize(mReadBuffer.mReadThreadSignalAttr);
|
|||
|
ret = sys_cond_create(&mReadBuffer.mReadThreadSignal, mReadBuffer.mReadThreadSignalMutex, &mReadBuffer.mReadThreadSignalAttr);
|
|||
|
|
|||
|
mReadBuffer.mCurrentPos = mPosOffset;
|
|||
|
mReadBuffer.mFileGroupDeleted = false;
|
|||
|
|
|||
|
sys_ppu_thread_create(&mReadBuffer.mReadThread,
|
|||
|
thr_FileGroupRead, reinterpret_cast<uint64_t>(&mReadBuffer),
|
|||
|
FILEGROUP_READ_CMD_PRIO, FILEGROUP_READ_CMD_STACKSIZE,
|
|||
|
NULL, "Filegroup_ReadThread");
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroup::StopReadThread()
|
|||
|
{
|
|||
|
mReadBuffer.mFileGroupDeleted = true;
|
|||
|
sys_cond_signal_to(mReadBuffer.mReadThreadSignal,mReadBuffer.mReadThread);
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroup::Clear()
|
|||
|
{
|
|||
|
Lock();
|
|||
|
if (mOpenedCount > 0)
|
|||
|
{
|
|||
|
DebugPrint("WARNING: Clearing filegroup while %d entries are still open\n", mOpenedCount);
|
|||
|
}
|
|||
|
if (mDirectoryEntries)
|
|||
|
{
|
|||
|
delete[] mDirectoryEntries;
|
|||
|
mDirectoryEntries = NULL;
|
|||
|
}
|
|||
|
#ifdef FILEGROUP_USE_HASH_DIRECTORY
|
|||
|
if (mHashDirectory)
|
|||
|
{
|
|||
|
delete[] mHashDirectory;
|
|||
|
mHashDirectory = NULL;
|
|||
|
}
|
|||
|
memset(mHashBuckets,0,FILEGROUP_DIRECTORY_BUCKETS*sizeof(HashBucket));
|
|||
|
Assert((FILEGROUP_DIRECTORY_BUCKETS & FILEGROUP_BUCKET_MOD) == 0); //Confirm FILEGROUP_DIRECTORY_BUCKETS is power of two
|
|||
|
#endif
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
if (mDirectoryExtraInfo)
|
|||
|
{
|
|||
|
delete[] mDirectoryExtraInfo;
|
|||
|
mDirectoryExtraInfo = NULL;
|
|||
|
}
|
|||
|
#endif
|
|||
|
if (mPreloadSection)
|
|||
|
{
|
|||
|
free(mPreloadSection);
|
|||
|
mPreloadSection=NULL;
|
|||
|
}
|
|||
|
mPreloadEntries = 0;
|
|||
|
mLastFailedId = 0;
|
|||
|
mNumDirectoryEntries = 0;
|
|||
|
mLastOpenedDirectoryIndex = 0;
|
|||
|
mFlaggedForDeletion = false;
|
|||
|
mOpenedCount = 0;
|
|||
|
mReadBuffer.mReadChunkSize = FILEGROUP_READ_CHUNK_SIZE;
|
|||
|
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
|
|||
|
char* CFileGroup::ReformatFileName(char* inputFileName)
|
|||
|
{
|
|||
|
//Sort out filename...
|
|||
|
//Fix slashes
|
|||
|
char* fname = inputFileName;
|
|||
|
char correctSeparator = '/';
|
|||
|
char incorrectSeparator = '\\';
|
|||
|
while ( *fname )
|
|||
|
{
|
|||
|
if ( *fname == incorrectSeparator || *fname == correctSeparator)
|
|||
|
{
|
|||
|
*fname = correctSeparator;
|
|||
|
if(fname[1] == incorrectSeparator || fname[1] == correctSeparator)
|
|||
|
{
|
|||
|
strcpy(fname+1,fname+2); //Remove multiple slashes from filenames
|
|||
|
}
|
|||
|
}
|
|||
|
fname++;
|
|||
|
}
|
|||
|
|
|||
|
//Adjust filename to remove the initial "/...path.../game/...gamemode.../"
|
|||
|
fname = inputFileName;
|
|||
|
char* ptr = NULL;
|
|||
|
while ((ptr = strstr(fname,"/game/")) ||
|
|||
|
(ptr = strstr(fname,"/GAME/")))
|
|||
|
{
|
|||
|
fname = ptr + strlen("/game");
|
|||
|
}
|
|||
|
if (fname != inputFileName) //i.e. we located a game directory
|
|||
|
{
|
|||
|
if (ptr=strchr(fname+1,'/'))
|
|||
|
{
|
|||
|
fname = ptr+1; //Ignore subdirectory
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//instead search for stdshaders directory (to handle the special case where the shaders filegroup is generated within the src directory)
|
|||
|
while ((ptr = strstr(fname,"stdshaders/")) ||
|
|||
|
(ptr = strstr(fname,"STDSHADERS/")))
|
|||
|
{
|
|||
|
fname = ptr + strlen("stdshaders/");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//Convert to upper case
|
|||
|
char *str = fname;
|
|||
|
while( str && *str )
|
|||
|
{
|
|||
|
*str = (char)toupper(*str);
|
|||
|
str++;
|
|||
|
}
|
|||
|
|
|||
|
return fname;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
CellFsErrno CFileGroup::PopulateDirectory(DirectoryHeader* hdr, int* entriesPopulated)
|
|||
|
{
|
|||
|
DirectoryEntry fullDirEntry;
|
|||
|
uint64_t bytesRead;
|
|||
|
mDirectoryEntries = new SmallDirectoryEntry[hdr->mNumEntries];
|
|||
|
mNumDirectoryEntries = hdr->mNumEntries;
|
|||
|
#ifdef FILEGROUP_USE_HASH_DIRECTORY
|
|||
|
mHashDirectory = new unsigned int[hdr->mNumEntries];
|
|||
|
#endif
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
mDirectoryExtraInfo = new DirectoryEntryExtraInfo[hdr->mNumEntries];
|
|||
|
#endif
|
|||
|
CellFsErrno retFs = CELL_FS_SUCCEEDED;
|
|||
|
*entriesPopulated = 0;
|
|||
|
if(hdr->mVersion == FILEGROUP_FORMAT_VERSION_COMPILED_DIRECTORY)
|
|||
|
{
|
|||
|
retFs = cellFsRead(mReadBuffer.mFile,mDirectoryEntries,(uint64_t)(sizeof(SmallDirectoryEntry)*hdr->mNumEntries),&bytesRead);
|
|||
|
mPosOffset += bytesRead;
|
|||
|
#ifdef FILEGROUP_USE_HASH_DIRECTORY
|
|||
|
if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
for(int i=0;i<hdr->mNumEntries;i++)
|
|||
|
{
|
|||
|
int bucketId = mDirectoryEntries[i].mNameId & FILEGROUP_BUCKET_MOD;
|
|||
|
++(mHashBuckets[bucketId].mCount);
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
for(int i=0;(i<hdr->mNumEntries)&&(mNumDirectoryEntries>0)&&(retFs == CELL_FS_SUCCEEDED);i++)
|
|||
|
{
|
|||
|
//bytesRead = fread(&fullDirEntry,1,sizeof(DirectoryEntry),mReadBuffer.mFile);
|
|||
|
retFs = cellFsRead(mReadBuffer.mFile,&fullDirEntry,(uint64_t)sizeof(DirectoryEntry),&bytesRead);
|
|||
|
if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
strcpy(mDirectoryExtraInfo[i].mFullFileName,fullDirEntry.mName);
|
|||
|
#endif
|
|||
|
if (bytesRead != sizeof(DirectoryEntry))
|
|||
|
{
|
|||
|
//ERROR: abort at this point
|
|||
|
DebugPrint("ERROR: FilegroupSystem error reading directory entry\n");
|
|||
|
delete[] mDirectoryEntries;
|
|||
|
mDirectoryEntries = NULL;
|
|||
|
mNumDirectoryEntries = 0;
|
|||
|
return retFs;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
mPosOffset += bytesRead;
|
|||
|
mDirectoryEntries[i].mCompressedLength = fullDirEntry.mCompressedLength;
|
|||
|
mDirectoryEntries[i].mLength = fullDirEntry.mLength;
|
|||
|
mDirectoryEntries[i].mPosition = fullDirEntry.mPosition;
|
|||
|
|
|||
|
char* fname = ReformatFileName(fullDirEntry.mName);
|
|||
|
|
|||
|
mDirectoryEntries[i].mNameId = HashFileName(fname);
|
|||
|
#ifdef FILEGROUP_USE_HASH_DIRECTORY
|
|||
|
int bucketId = mDirectoryEntries[i].mNameId & FILEGROUP_BUCKET_MOD;
|
|||
|
++(mHashBuckets[bucketId].mCount);
|
|||
|
#endif
|
|||
|
} //if (bytesRead != sizeof(DirectoryEntry))
|
|||
|
} //if (retFs == CELL_FS_SUCCEEDED)
|
|||
|
} //for loop
|
|||
|
}
|
|||
|
|
|||
|
if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
#ifdef FILEGROUP_USE_HASH_DIRECTORY
|
|||
|
//populate hash directory buckets
|
|||
|
int currentPos = 0;
|
|||
|
for(int j = 0; j<FILEGROUP_DIRECTORY_BUCKETS; j++)
|
|||
|
{
|
|||
|
if (mHashBuckets[j].mCount>0)
|
|||
|
{
|
|||
|
mHashBuckets[j].mPosition = currentPos;
|
|||
|
currentPos += mHashBuckets[j].mCount;
|
|||
|
mHashBuckets[j].mCount=0; //Reset for use as a counter in the next section
|
|||
|
}
|
|||
|
}
|
|||
|
for(int i = 0; i<mNumDirectoryEntries; i++)
|
|||
|
{
|
|||
|
int bucketId = mDirectoryEntries[i].mNameId & FILEGROUP_BUCKET_MOD;
|
|||
|
mHashDirectory[mHashBuckets[bucketId].mPosition + mHashBuckets[bucketId].mCount] = i;
|
|||
|
++(mHashBuckets[bucketId].mCount);
|
|||
|
}
|
|||
|
#endif
|
|||
|
*entriesPopulated = mNumDirectoryEntries;
|
|||
|
}
|
|||
|
return retFs;
|
|||
|
}
|
|||
|
|
|||
|
int CFileGroup::Populate(const char* fileGroupName, bool usePreload /*=false*/)
|
|||
|
{
|
|||
|
MEM_ALLOC_CREDIT();
|
|||
|
Lock();
|
|||
|
|
|||
|
int entriesPopulated = 0;
|
|||
|
CellFsStat fileGroupStat;
|
|||
|
int fileGroupHandle = 0;
|
|||
|
DirectoryHeader hdr;
|
|||
|
uint64_t pos;
|
|||
|
bool firstFail = true;
|
|||
|
|
|||
|
CellFsErrno retFs = CELL_FS_SUCCEEDED-1; //Initialise to a non-success value
|
|||
|
while(retFs != CELL_FS_SUCCEEDED) //Loop is repeated following any stat/read errors
|
|||
|
{
|
|||
|
if(fileGroupHandle)
|
|||
|
{
|
|||
|
//Clear up any previous half completed attempt
|
|||
|
cellFsClose(fileGroupHandle);
|
|||
|
Clear();
|
|||
|
sys_timer_usleep(250000); //Sleep for 0.25 seconds
|
|||
|
if(!firstFail)
|
|||
|
{
|
|||
|
PS3_DisplayDiskErrorDialog(); //Probably dirty disk if we hit this point
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
firstFail = false;
|
|||
|
}
|
|||
|
}
|
|||
|
retFs = PS3_RetryStatWhileDiskEjected(fileGroupName, &fileGroupStat);
|
|||
|
if(retFs != CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
entriesPopulated = -1;
|
|||
|
goto xit;
|
|||
|
}
|
|||
|
retFs = PS3_RetryOpenWhileDiskEjected(fileGroupName, CELL_FS_O_RDONLY, &fileGroupHandle);
|
|||
|
if(retFs != CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
entriesPopulated = -1;
|
|||
|
goto xit;
|
|||
|
}
|
|||
|
|
|||
|
retFs = cellFsFGetBlockSize(fileGroupHandle,&mReadBuffer.mFileSectorSize,&mReadBuffer.mFileBlockSize);
|
|||
|
if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
//DebugPrint("Filegroup %s has blocksize %d, sectorsize %d\n", mReadBuffer.mFileName, mReadBuffer.mFileBlockSize, mReadBuffer.mFileSectorSize);
|
|||
|
mReadBuffer.mFile = fileGroupHandle;
|
|||
|
Q_strncpy(mReadBuffer.mFileName, fileGroupName, MAX_PATH);
|
|||
|
mFileStat = fileGroupStat;
|
|||
|
mPosOffset = 0;
|
|||
|
|
|||
|
retFs = cellFsLseek(mReadBuffer.mFile,0,CELL_FS_SEEK_SET, &pos); //Ensure we are at the beginning of the file
|
|||
|
|
|||
|
if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
uint64_t bytesRead;
|
|||
|
retFs = cellFsRead(mReadBuffer.mFile,&hdr,(uint64_t)sizeof(hdr),&bytesRead);
|
|||
|
if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
mPosOffset += bytesRead;
|
|||
|
if (!usePreload)
|
|||
|
{
|
|||
|
hdr.mNumPreloadEntries = 0;
|
|||
|
}
|
|||
|
|
|||
|
if ((hdr.mVersion >= FILEGROUP_FORMAT_VERSION) && (hdr.mNumEntries > 0) && (!mDirectoryEntries)) //Check that filegroup is not already populated, and that there are entries available to read
|
|||
|
{
|
|||
|
retFs = PopulateDirectory(&hdr,&entriesPopulated);
|
|||
|
if(retFs == CELL_FS_SUCCEEDED && entriesPopulated > 0)
|
|||
|
{
|
|||
|
if (hdr.mNumPreloadEntries > 0)
|
|||
|
{
|
|||
|
if(mPreloadSection != NULL)
|
|||
|
{
|
|||
|
free(mPreloadSection);
|
|||
|
}
|
|||
|
mPreloadSection = malloc(mDirectoryEntries[hdr.mNumPreloadEntries].mPosition);
|
|||
|
retFs = cellFsRead(mReadBuffer.mFile,mPreloadSection,(uint64_t)mDirectoryEntries[hdr.mNumPreloadEntries].mPosition,&bytesRead);
|
|||
|
if ((retFs == CELL_FS_SUCCEEDED) && (bytesRead==mDirectoryEntries[hdr.mNumPreloadEntries].mPosition))
|
|||
|
{
|
|||
|
mPreloadEntries = hdr.mNumPreloadEntries;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
printf("ERROR: Unable to read %d bytes of filegroup preload section. Preload disabled\n", mDirectoryEntries[hdr.mNumPreloadEntries].mPosition);
|
|||
|
}
|
|||
|
}
|
|||
|
} //if(retFs == CELL_FS_SUCCEEDED && entriesPopulated > 0)
|
|||
|
}//if ((hdr.mVersion == FILEGROUP_FORMAT_VERSION) && (hdr.mNumEntries > 0) && (!mDirectoryEntries))
|
|||
|
} //if (retFs == CELL_FS_SUCCEEDED) cellfsread hdr
|
|||
|
} //if (retFs == CELL_FS_SUCCEEDED) cellFsLseek
|
|||
|
} //if (retFs == CELL_FS_SUCCEEDED) cellFsFGetBlockSize
|
|||
|
} //while(retFs != CELL_FS_SUCCEEDED)
|
|||
|
PS3_ClearDiskErrorDialog();
|
|||
|
|
|||
|
if(entriesPopulated > 0)
|
|||
|
{
|
|||
|
//Initialise mutex used for synchronising recovery from disk eject
|
|||
|
sys_lwmutex_attribute_initialize(mReadBuffer.mEjectRecoveryMutexAttr);
|
|||
|
int ret = sys_lwmutex_create(&mReadBuffer.mEjectRecoveryMutex, &mReadBuffer.mEjectRecoveryMutexAttr);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
Msg("CFileGroup::Populate sys_lwmutex_create of mEjectRecoveryMutex failed: %d\n", ret);
|
|||
|
}
|
|||
|
|
|||
|
//Initialise read buffer mutex
|
|||
|
sys_lwmutex_attribute_initialize(mReadBuffer.mBufferMutexAttr);
|
|||
|
mReadBuffer.mBufferMutexAttr.attr_recursive = SYS_SYNC_RECURSIVE;
|
|||
|
ret = sys_lwmutex_create(&mReadBuffer.mBufferMutex, &mReadBuffer.mBufferMutexAttr);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
printf("CFileGroup::Populate sys_lwmutex_create failed: %d\n", ret);
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
//Initialise read consumer mutex
|
|||
|
sys_lwmutex_attribute_initialize(mReadBuffer.mNextConsumeByteMutexAttr);
|
|||
|
mReadBuffer.mNextConsumeByteMutexAttr.attr_recursive = SYS_SYNC_RECURSIVE;
|
|||
|
ret = sys_lwmutex_create(&mReadBuffer.mNextConsumeByteMutex, &mReadBuffer.mNextConsumeByteMutexAttr);
|
|||
|
if (ret != CELL_OK) {
|
|||
|
printf("CFileGroup::Populate sys_lwmutex_create failed: %d\n", ret);
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
mLastOpenedDirectoryIndex = 0;
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
ResetFileTimings();
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
xit:
|
|||
|
Unlock();
|
|||
|
return entriesPopulated;
|
|||
|
}
|
|||
|
|
|||
|
int CFileGroup::FindFile(FileIdentifier *pFileName)
|
|||
|
{
|
|||
|
int foundIndex = -1;
|
|||
|
|
|||
|
if (!mFlaggedForDeletion && !mReadBuffer.mFileGroupDeleted)
|
|||
|
{
|
|||
|
if(!pFileName->mNameIdSet)
|
|||
|
{
|
|||
|
pFileName->mNameId = HashFileName(pFileName->mName);
|
|||
|
pFileName->mNameIdSet = true;
|
|||
|
}
|
|||
|
unsigned int index = mLastOpenedDirectoryIndex;
|
|||
|
//Don't repeat the search if we already know this file doesn't exist
|
|||
|
if (!mLastFailedId || (pFileName->mNameId != mLastFailedId))
|
|||
|
{
|
|||
|
//First check the current and next positions in the ordered list
|
|||
|
if ((mDirectoryEntries[index].mNameId == pFileName->mNameId) || //current
|
|||
|
((++index < mNumDirectoryEntries) && (mDirectoryEntries[index].mNameId == pFileName->mNameId))) //next
|
|||
|
{
|
|||
|
foundIndex = index;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
#ifdef FILEGROUP_USE_HASH_DIRECTORY
|
|||
|
//At this point resort to the hash directory
|
|||
|
int bucketId = pFileName->mNameId & FILEGROUP_BUCKET_MOD;
|
|||
|
//printf("Using hash table to search %d entries for filename %d in bucket %d\n", mHashBuckets[bucketId].mCount, fileNameId, bucketId);
|
|||
|
//Search through the relevant bucket
|
|||
|
for (int i=mHashBuckets[bucketId].mPosition; i< (mHashBuckets[bucketId].mPosition + mHashBuckets[bucketId].mCount) && foundIndex < 0; i++)
|
|||
|
{
|
|||
|
if (mDirectoryEntries[mHashDirectory[i]].mNameId == pFileName->mNameId)
|
|||
|
{
|
|||
|
foundIndex = mHashDirectory[i];
|
|||
|
}
|
|||
|
}
|
|||
|
#else
|
|||
|
//Loop through the remaining directory entries, starting with the current entry
|
|||
|
for(int i=index, j=0; j<(mNumDirectoryEntries-2) && foundIndex < 0; i++,j++)
|
|||
|
{
|
|||
|
//At the end of the list, loop back to the beginning:
|
|||
|
if (i==mNumDirectoryEntries)
|
|||
|
{
|
|||
|
i=0;
|
|||
|
}
|
|||
|
if (mDirectoryEntries[i].mNameId == fileNameId)
|
|||
|
{
|
|||
|
foundIndex = i;
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
if(foundIndex < 0)
|
|||
|
{
|
|||
|
mLastFailedId = pFileName->mNameId;
|
|||
|
}
|
|||
|
}
|
|||
|
return foundIndex;
|
|||
|
}
|
|||
|
|
|||
|
CFileGroupOpenedFile* CFileGroup::FS_fopen( FileIdentifier *pFileName, const char *pOptions, unsigned int flags, __int64 *size )
|
|||
|
{
|
|||
|
Lock();
|
|||
|
|
|||
|
CFileGroupOpenedFile* retVal = NULL;
|
|||
|
int foundIndex = FindFile(pFileName);
|
|||
|
|
|||
|
if (foundIndex>=0)
|
|||
|
{
|
|||
|
CFileGroupOpenedFile* openedFile = mFs->GetOpenedFile();
|
|||
|
Assert(openedFile);
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
openedFile->Init(mDirectoryEntries[foundIndex],this,&mReadBuffer,mDirectoryExtraInfo[foundIndex].mFullFileName), (foundIndex<mPreloadEntries);
|
|||
|
#else
|
|||
|
openedFile->Init(mDirectoryEntries[foundIndex],this,&mReadBuffer, (foundIndex<mPreloadEntries));
|
|||
|
#endif
|
|||
|
if(!openedFile->IsPreloaded())
|
|||
|
{
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
if (!((foundIndex == mLastOpenedDirectoryIndex) || (foundIndex == mLastOpenedDirectoryIndex+1)))
|
|||
|
{
|
|||
|
++m_fileJumps;
|
|||
|
// if(mPreloadEntries>0)
|
|||
|
// {
|
|||
|
// DebugPrint("Jump from level load filegroup index %d to index %d\n", mLastOpenedDirectoryIndex, foundIndex );
|
|||
|
// }
|
|||
|
}
|
|||
|
#endif
|
|||
|
mLastOpenedDirectoryIndex = foundIndex;
|
|||
|
}
|
|||
|
|
|||
|
if (size)
|
|||
|
{
|
|||
|
*size = mDirectoryEntries[foundIndex].mLength;
|
|||
|
}
|
|||
|
retVal = openedFile;
|
|||
|
}
|
|||
|
|
|||
|
Unlock();
|
|||
|
return retVal;
|
|||
|
}
|
|||
|
|
|||
|
int CFileGroup::FS_stat( FileIdentifier *path, CellFsStat *buf )
|
|||
|
{
|
|||
|
Lock();
|
|||
|
int retVal = -1;
|
|||
|
int foundIndex = FindFile(path);
|
|||
|
|
|||
|
if (foundIndex > -1)
|
|||
|
{
|
|||
|
//Default the stat details to those of the parent group file
|
|||
|
*buf = mFileStat;
|
|||
|
buf->st_size = mDirectoryEntries[foundIndex].mLength; //Set the correct filesize
|
|||
|
buf->st_mode = S_IFREG | S_IRUSR ; //Set the bitmask to indicate a read only file
|
|||
|
//CM:TODO Set Block count????
|
|||
|
retVal = 0;
|
|||
|
}
|
|||
|
|
|||
|
Unlock();
|
|||
|
return retVal;
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroup::DecrementOpenedCount()
|
|||
|
{
|
|||
|
Lock();
|
|||
|
mOpenedCount--;
|
|||
|
if (mFlaggedForDeletion && (mOpenedCount == 0))
|
|||
|
{
|
|||
|
mFs->DeleteFileGroup(this);
|
|||
|
}
|
|||
|
Unlock();
|
|||
|
}
|
|||
|
|
|||
|
CFileGroupOpenedFile::CFileGroupOpenedFile()
|
|||
|
: mEof(false), mSeekPosIndicator(-1), mActualPosIndicator(-1), mParentFileGroup(NULL), mDirEntry(NULL),
|
|||
|
mPreloaded(false)
|
|||
|
{
|
|||
|
#ifdef _DEBUG
|
|||
|
refCount++;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
CFileGroupOpenedFile::~CFileGroupOpenedFile()
|
|||
|
{
|
|||
|
#ifdef _DEBUG
|
|||
|
refCount--;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
void CFileGroupOpenedFile::Init(const SmallDirectoryEntry& dirEntry, CFileGroup* parentFileGroup, FileGroupReadBuffer* parentBuffer, char* fullFileName, bool preloaded)
|
|||
|
#else
|
|||
|
void CFileGroupOpenedFile::Init(const SmallDirectoryEntry& dirEntry, CFileGroup* parentFileGroup, FileGroupReadBuffer* parentBuffer, bool preloaded)
|
|||
|
#endif
|
|||
|
{
|
|||
|
mPreloaded=preloaded;
|
|||
|
mDirEntry = &dirEntry;
|
|||
|
mParentFileGroup = parentFileGroup;
|
|||
|
mParentReadBuffer = parentBuffer;
|
|||
|
mSeekPosIndicator = 0;
|
|||
|
mActualPosIndicator = 0;
|
|||
|
mCompressedPosIndicator = 0;
|
|||
|
mUncompressedBufferStartPos = -1;
|
|||
|
if (mDirEntry->mCompressedLength > 0)
|
|||
|
{
|
|||
|
//Zlib initialisation
|
|||
|
mStrm.zalloc = Z_NULL;
|
|||
|
mStrm.zfree = Z_NULL;
|
|||
|
mStrm.opaque = Z_NULL;
|
|||
|
mStrm.avail_in = 0;
|
|||
|
mStrm.next_in = Z_NULL;
|
|||
|
Assert( 0 );
|
|||
|
//int InfRet = inflateInit(&mStrm);
|
|||
|
}
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
if (!(strstr(fullFileName,"/Resource/")))
|
|||
|
{
|
|||
|
mOrdinaryFile = fopen(fullFileName,"rb");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
mOrdinaryFile = NULL;
|
|||
|
}
|
|||
|
#endif
|
|||
|
mParentFileGroup->IncrementOpenedCount();
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroupOpenedFile::FS_fclose()
|
|||
|
{
|
|||
|
if (mDirEntry->mCompressedLength > 0)
|
|||
|
{
|
|||
|
Assert( 0 );
|
|||
|
//inflateEnd(&mStrm);
|
|||
|
}
|
|||
|
mParentFileGroup->Lock();
|
|||
|
mParentFileGroup->DecrementOpenedCount();
|
|||
|
mParentFileGroup->FreeOpenedFile(this);
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
if (mOrdinaryFile)
|
|||
|
{
|
|||
|
fclose(mOrdinaryFile);
|
|||
|
mOrdinaryFile = NULL;
|
|||
|
}
|
|||
|
#endif
|
|||
|
mParentFileGroup->Unlock();
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroupOpenedFile::FS_fseek( __int64 pos, int seekType )
|
|||
|
{
|
|||
|
//mParentFileGroup->Lock();
|
|||
|
//CM:TODO Possibly change this so that seek isn't actually done until read????
|
|||
|
__int64 absPosition;
|
|||
|
switch (seekType)
|
|||
|
{
|
|||
|
case SEEK_CUR:
|
|||
|
absPosition = mSeekPosIndicator + pos;
|
|||
|
break;
|
|||
|
case SEEK_END:
|
|||
|
absPosition = mDirEntry->mLength + pos;
|
|||
|
break;
|
|||
|
default:
|
|||
|
absPosition = pos;
|
|||
|
}
|
|||
|
|
|||
|
bool eofSeek = false; //Record eof setting at this point, but only set mEof following a successful seek
|
|||
|
if(absPosition > mDirEntry->mLength)
|
|||
|
{
|
|||
|
absPosition = mDirEntry->mLength;
|
|||
|
eofSeek = true;
|
|||
|
//CM:TODO Raise error condition?
|
|||
|
}
|
|||
|
|
|||
|
//Don't actually do the seek at this point, wait until next read
|
|||
|
|
|||
|
mSeekPosIndicator = absPosition;
|
|||
|
mEof = eofSeek;
|
|||
|
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
if (mOrdinaryFile)
|
|||
|
{
|
|||
|
fseek(mOrdinaryFile,pos,seekType);
|
|||
|
}
|
|||
|
#endif
|
|||
|
//if (!isCurrent)
|
|||
|
//{
|
|||
|
// mParentFileGroup->MakeCurrentEntry(this);
|
|||
|
//}
|
|||
|
//mParentFileGroup->Unlock();
|
|||
|
}
|
|||
|
|
|||
|
long CFileGroupOpenedFile::FS_ftell()
|
|||
|
{
|
|||
|
return mSeekPosIndicator;
|
|||
|
//CM:TODO If an error occurs, -1L is returned, and the global variable errno is set to a positive value.
|
|||
|
}
|
|||
|
|
|||
|
int CFileGroupOpenedFile::FS_feof()
|
|||
|
{
|
|||
|
return mEof;
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroupOpenedFile::Rewind()
|
|||
|
{
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
mParentFileGroup->m_rewinds++;
|
|||
|
#endif
|
|||
|
Assert( 0 );
|
|||
|
//inflateEnd(&mStrm);
|
|||
|
mStrm.zalloc = Z_NULL;
|
|||
|
mStrm.zfree = Z_NULL;
|
|||
|
mStrm.opaque = Z_NULL;
|
|||
|
mStrm.avail_in = 0;
|
|||
|
mStrm.next_in = Z_NULL;
|
|||
|
Assert( 0 );
|
|||
|
//int InfRet = inflateInit(&mStrm);
|
|||
|
mCompressedPosIndicator = 0;
|
|||
|
mActualPosIndicator = 0;
|
|||
|
mUncompressedBufferStartPos = -1;
|
|||
|
}
|
|||
|
|
|||
|
void CFileGroupOpenedFile::TraceMemCopy(void* pDest, const void* pSource, size_t nBytes)
|
|||
|
{
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
system_time_t time1 = sys_time_get_system_time();
|
|||
|
#endif
|
|||
|
|
|||
|
memcpy(pDest,pSource,nBytes);
|
|||
|
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
system_time_t time2 = sys_time_get_system_time();
|
|||
|
mParentFileGroup->m_memCopyTime += (time2-time1);
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//ASSUMPTIONS: destination has readSize bytes available
|
|||
|
size_t CFileGroupOpenedFile::ReadFromCompressedData( void *dest, size_t readSize)
|
|||
|
{
|
|||
|
uint64_t bytesRead = 0;
|
|||
|
bool dataCopied = false;
|
|||
|
size_t destBytesWritten = 0;
|
|||
|
size_t bytesToIgnore = 0;
|
|||
|
|
|||
|
if (mSeekPosIndicator < mActualPosIndicator) //Check for Rewind
|
|||
|
{
|
|||
|
//First check if there is any data left over from a previous inflate
|
|||
|
if ((mUncompressedBufferStartPos > -1) && (mSeekPosIndicator >= mUncompressedBufferStartPos))
|
|||
|
{
|
|||
|
unsigned char* seekPosUncompressedBuffer = mUncompressedBuffer + (mSeekPosIndicator - mUncompressedBufferStartPos);
|
|||
|
int bytesToCopy = MIN(mActualPosIndicator - mSeekPosIndicator, readSize);
|
|||
|
TraceMemCopy(dest,seekPosUncompressedBuffer,bytesToCopy);
|
|||
|
destBytesWritten += bytesToCopy;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Rewind();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
bytesToIgnore = mSeekPosIndicator - mActualPosIndicator;
|
|||
|
}
|
|||
|
|
|||
|
size_t parentCompressedPos = mParentFileGroup->GetPosOffset() + mDirEntry->mPosition + mCompressedPosIndicator;
|
|||
|
size_t remainingBytesToIgnore;
|
|||
|
size_t avail_in_before_inflate;
|
|||
|
size_t remainingCompressedData = mDirEntry->mCompressedLength - mCompressedPosIndicator;
|
|||
|
|
|||
|
while((destBytesWritten < readSize) && (remainingCompressedData > 0))
|
|||
|
{
|
|||
|
bool compressedDataFound = false;
|
|||
|
|
|||
|
if (mPreloaded)
|
|||
|
{
|
|||
|
compressedDataFound=true;
|
|||
|
mStrm.avail_in = remainingCompressedData;
|
|||
|
mStrm.next_in = (Bytef*)mParentFileGroup->GetPreloadData() + mDirEntry->mPosition + mCompressedPosIndicator;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sys_lwmutex_lock(&mParentReadBuffer->mNextConsumeByteMutex,0); //Ensure that no other consumer updates the 'next consume byte'
|
|||
|
if (parentCompressedPos < mParentReadBuffer->mCurrentPos)
|
|||
|
{
|
|||
|
if (mParentFileGroup->UsingReadThread())
|
|||
|
{
|
|||
|
size_t availablePos = 0;
|
|||
|
sys_lwmutex_lock(&mParentReadBuffer->mBufferStartEndPosMutex,0); //Don't want read thread to 'wrap around' while we calculate the available data
|
|||
|
//Compressed data may exist in read thread buffer
|
|||
|
//First find the start of the section of the buffer which won't be overwritten:
|
|||
|
if ((mParentReadBuffer->mBufferEndPos > 0) && (mParentReadBuffer->mBufferEndPos == mParentReadBuffer->mBufferStartPos)) //Include data from previous buffer cycle
|
|||
|
{
|
|||
|
if (mParentReadBuffer->mNextConsumePos > 0) //Read thread won't overwrite FILEGROUP_READ_STORE_SIZE bytes up to NextConsumeByte
|
|||
|
{
|
|||
|
if (mParentReadBuffer->mNextConsumePos > (mParentFileGroup->GetPosOffset() + FILEGROUP_READ_STORE_SIZE))
|
|||
|
{
|
|||
|
availablePos = mParentReadBuffer->mNextConsumePos - FILEGROUP_READ_STORE_SIZE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
availablePos = mParentFileGroup->GetPosOffset();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//This should only occur when read thread has filled buffer and no bytes have been read
|
|||
|
Assert(mParentReadBuffer->mCurrentByte == mParentReadBuffer->mBuffer);
|
|||
|
Assert(mParentReadBuffer->mAwaitingConsumer);
|
|||
|
availablePos = mParentReadBuffer->mBufferStartPos;
|
|||
|
}
|
|||
|
}
|
|||
|
else //No data available from previous cycle
|
|||
|
{
|
|||
|
if (mParentReadBuffer->mNextConsumePos > 0)
|
|||
|
{
|
|||
|
Assert(mParentReadBuffer->mNextConsumePos <= mParentReadBuffer->mCurrentPos);
|
|||
|
if ((mParentReadBuffer->mNextConsumePos - mParentReadBuffer->mBufferStartPos) > FILEGROUP_READ_STORE_SIZE)
|
|||
|
{
|
|||
|
availablePos = mParentReadBuffer->mNextConsumePos - FILEGROUP_READ_STORE_SIZE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
availablePos = mParentReadBuffer->mBufferStartPos;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
availablePos = mParentReadBuffer->mBufferStartPos;
|
|||
|
}
|
|||
|
}
|
|||
|
//Now check if compressed data exists within the 'safe' portion of the buffer:
|
|||
|
if (parentCompressedPos >= availablePos)
|
|||
|
{
|
|||
|
compressedDataFound = true;
|
|||
|
if ((mParentReadBuffer->mBufferEndPos > 0) && (mParentReadBuffer->mBufferEndPos == mParentReadBuffer->mBufferStartPos) &&
|
|||
|
(parentCompressedPos < mParentReadBuffer->mBufferEndPos)) //Data wraps round end of buffer
|
|||
|
{
|
|||
|
mStrm.avail_in = MIN((mParentReadBuffer->mBufferEndPos - parentCompressedPos),remainingCompressedData);
|
|||
|
mStrm.next_in = (mParentReadBuffer->mBuffer + mParentReadBuffer->mBufferSize) - (mParentReadBuffer->mBufferEndPos - parentCompressedPos);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
mStrm.avail_in = MIN((mParentReadBuffer->mCurrentPos - parentCompressedPos),remainingCompressedData);
|
|||
|
mStrm.next_in = mParentReadBuffer->mBuffer + (parentCompressedPos - mParentReadBuffer->mBufferStartPos);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
sys_lwmutex_unlock(&mParentReadBuffer->mBufferStartEndPosMutex); //Happy for read thread to 'wrap around' from this point on
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//If not using read thread, we don't need to worry about data being overwritten, check whether the data exists anywhere in the buffer
|
|||
|
if (parentCompressedPos >= mParentReadBuffer->mBufferStartPos)
|
|||
|
{
|
|||
|
compressedDataFound = true;
|
|||
|
mStrm.avail_in = MIN((mParentReadBuffer->mCurrentPos - parentCompressedPos),remainingCompressedData);
|
|||
|
mStrm.next_in = mParentReadBuffer->mBuffer + (parentCompressedPos - mParentReadBuffer->mBufferStartPos);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!compressedDataFound)
|
|||
|
{
|
|||
|
//Read compressed data from file
|
|||
|
sys_lwmutex_lock(&mParentReadBuffer->mBufferMutex,0); //Lock buffer for write
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
system_time_t time1 = sys_time_get_system_time();
|
|||
|
#endif
|
|||
|
uint64_t pos;
|
|||
|
uint64_t alignedSeekPos = parentCompressedPos - parentCompressedPos%mParentReadBuffer->mFileBlockSize;
|
|||
|
CellFsErrno retFs = cellFsLseek(mParentReadBuffer->mFile,alignedSeekPos,CELL_FS_SEEK_SET,&pos);
|
|||
|
if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
retFs = cellFsRead(mParentReadBuffer->mFile,mParentReadBuffer->mBuffer,(uint64_t)FILEGROUP_READ_CHUNK_SIZE,&bytesRead);
|
|||
|
}
|
|||
|
while(retFs != CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
cellFsClose(mParentReadBuffer->mFile);
|
|||
|
sys_timer_usleep(250000); //Sleep for 0.25 seconds
|
|||
|
mParentReadBuffer->WaitOnDiskEjectRecovery();
|
|||
|
retFs = cellFsLseek(mParentReadBuffer->mFile,(int64_t)alignedSeekPos,CELL_FS_SEEK_SET,&pos);
|
|||
|
if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
retFs = cellFsRead(mParentReadBuffer->mFile,mParentReadBuffer->mBuffer,(uint64_t)FILEGROUP_READ_CHUNK_SIZE,&bytesRead);
|
|||
|
}
|
|||
|
if(retFs == CELL_FS_EIO)
|
|||
|
{
|
|||
|
PS3_DisplayDiskErrorDialog(); //Assume dirty disk, rather than disk eject if this point is reached
|
|||
|
}
|
|||
|
else if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
PS3_ClearDiskErrorDialog();
|
|||
|
}
|
|||
|
}
|
|||
|
if (bytesRead > 0)
|
|||
|
{
|
|||
|
//Reset buffer data so that read thread continues from this point
|
|||
|
mParentReadBuffer->mBufferEndPos = 0;
|
|||
|
mParentReadBuffer->mBufferStartPos = alignedSeekPos;
|
|||
|
mParentReadBuffer->mCurrentByte = mParentReadBuffer->mBuffer + (int)bytesRead;
|
|||
|
mParentReadBuffer->mCurrentPos = alignedSeekPos + (int)bytesRead;
|
|||
|
mParentReadBuffer->mNextConsumePos = 0;
|
|||
|
if (mParentFileGroup->UsingReadThread() &&
|
|||
|
mParentReadBuffer->mAwaitingConsumer &&
|
|||
|
mParentReadBuffer->mCurrentPos < mParentFileGroup->Size())
|
|||
|
{
|
|||
|
int ret = sys_cond_signal_to(mParentReadBuffer->mReadThreadSignal,mParentFileGroup->GetReadThread());
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
system_time_t time2 = sys_time_get_system_time();
|
|||
|
mParentFileGroup->IncrReadTime(time2-time1);
|
|||
|
mParentFileGroup->m_bytesReadFromFileByConsumer += (int)bytesRead;
|
|||
|
mParentFileGroup->IncrReadBytes(bytesRead);
|
|||
|
#endif
|
|||
|
mStrm.avail_in = MIN((int)(bytesRead - (parentCompressedPos-alignedSeekPos)),remainingCompressedData);
|
|||
|
mStrm.next_in = mParentReadBuffer->mBuffer+(parentCompressedPos-alignedSeekPos);
|
|||
|
sys_lwmutex_unlock(&mParentReadBuffer->mBufferMutex); //Now safe for other threads to continue updating buffer
|
|||
|
}
|
|||
|
//mParentReadBuffer->mNextConsumeByte = mStrm.next_in; //This ensures bytes aren't overwritten during inflate SHOULD ONLY EVER INCREASE NEXT CONSUME BYTE
|
|||
|
//Assert(((mParentReadBuffer->mNextConsumeByte - mParentReadBuffer->mBuffer) < FILEGROUP_READ_THREAD_BUFFER_SIZE));
|
|||
|
|
|||
|
// mStrm.avail_out = FILEGROUP_UNCOMPRESSED_BUFFER_SIZE;
|
|||
|
// bool directInflate = ((bytesToIgnore <= 0) && (readSize - destBytesWritten) >= FILEGROUP_UNCOMPRESSED_BUFFER_SIZE);
|
|||
|
// if (directInflate)
|
|||
|
// {
|
|||
|
// mStrm.next_out = (unsigned char*)dest + destBytesWritten;
|
|||
|
// }
|
|||
|
// else
|
|||
|
// {
|
|||
|
// mStrm.next_out = mUncompressedBuffer;
|
|||
|
// }
|
|||
|
|
|||
|
// while((mStrm.avail_in > 0)&&(destBytesWritten < readSize))
|
|||
|
// {
|
|||
|
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
system_time_t time1 = sys_time_get_system_time();
|
|||
|
#endif
|
|||
|
|
|||
|
mStrm.avail_out = FILEGROUP_UNCOMPRESSED_BUFFER_SIZE;
|
|||
|
mStrm.next_out = mUncompressedBuffer;
|
|||
|
avail_in_before_inflate = mStrm.avail_in;
|
|||
|
Assert( 0 );
|
|||
|
int ret = 0;//inflate(&mStrm, Z_NO_FLUSH);
|
|||
|
|
|||
|
Assert(ret != Z_STREAM_ERROR); /* state not clobbered */
|
|||
|
switch (ret)
|
|||
|
{case Z_NEED_DICT:
|
|||
|
ret = Z_DATA_ERROR; /* and fall through */
|
|||
|
case Z_DATA_ERROR:
|
|||
|
case Z_MEM_ERROR:
|
|||
|
//(void)inflateEnd(&mStrm);
|
|||
|
Assert(0);
|
|||
|
printf("Error %d decompressing data, returning %d\n", ret, destBytesWritten);
|
|||
|
return destBytesWritten;
|
|||
|
}
|
|||
|
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
system_time_t time2 = sys_time_get_system_time();
|
|||
|
mParentFileGroup->m_uncompressCalls++;
|
|||
|
mParentFileGroup->m_bytesReadFromBuffer += (avail_in_before_inflate - mStrm.avail_in);
|
|||
|
mParentFileGroup->m_uncompressTime += (time2-time1);
|
|||
|
#endif
|
|||
|
|
|||
|
int bytesInflated = FILEGROUP_UNCOMPRESSED_BUFFER_SIZE - mStrm.avail_out;
|
|||
|
// if (directInflate)
|
|||
|
// {
|
|||
|
mUncompressedBufferStartPos = mActualPosIndicator;
|
|||
|
// }
|
|||
|
// else
|
|||
|
// {
|
|||
|
// mUncompressedBufferStartPos = -1;
|
|||
|
// }
|
|||
|
mActualPosIndicator += bytesInflated;
|
|||
|
|
|||
|
if (bytesToIgnore > 0)
|
|||
|
{
|
|||
|
if (bytesInflated > bytesToIgnore)
|
|||
|
{
|
|||
|
int bytesOfInterest = MIN(readSize,(bytesInflated - bytesToIgnore));
|
|||
|
// if (!directInflate)
|
|||
|
// {
|
|||
|
TraceMemCopy(dest, mUncompressedBuffer + bytesToIgnore, bytesOfInterest);
|
|||
|
// }
|
|||
|
destBytesWritten += bytesOfInterest;
|
|||
|
bytesToIgnore = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
bytesToIgnore -= bytesInflated;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
int bytesOfInterest = MIN((readSize-destBytesWritten),bytesInflated);
|
|||
|
// if (!directInflate)
|
|||
|
// {
|
|||
|
TraceMemCopy((unsigned char*)dest + destBytesWritten, mUncompressedBuffer, bytesOfInterest);
|
|||
|
// }
|
|||
|
destBytesWritten += bytesOfInterest;
|
|||
|
}
|
|||
|
|
|||
|
mCompressedPosIndicator += (avail_in_before_inflate - mStrm.avail_in);
|
|||
|
parentCompressedPos += (avail_in_before_inflate - mStrm.avail_in);
|
|||
|
remainingCompressedData -= (avail_in_before_inflate - mStrm.avail_in);
|
|||
|
Assert(mStrm.next_in);
|
|||
|
|
|||
|
// } //while inflate loop
|
|||
|
|
|||
|
//Wait until this point before updating NextConsumePos, otherwise data may be overwritten during inflate.
|
|||
|
//Only update "NextConsumePos" if it has increased, otherwise it is possible to get into a situation
|
|||
|
// where some of the FILEGROUP_READ_STORE_SIZE bytes leading up to NextConsumePos are overwritten by the read thread.
|
|||
|
if ((!mPreloaded) && (parentCompressedPos > mParentReadBuffer->mNextConsumePos))
|
|||
|
{
|
|||
|
mParentReadBuffer->mNextConsumePos = parentCompressedPos;
|
|||
|
|
|||
|
//Check if need to signal to read thread
|
|||
|
if (mParentFileGroup->UsingReadThread() &&
|
|||
|
mParentReadBuffer->mAwaitingConsumer &&
|
|||
|
mParentReadBuffer->mCurrentPos < mParentFileGroup->Size() &&
|
|||
|
((mParentReadBuffer->mCurrentPos + FILEGROUP_READ_CHUNK_SIZE) - mParentReadBuffer->mNextConsumePos) <= (mParentReadBuffer->mBufferSize - FILEGROUP_READ_SIGNAL_SIZE))
|
|||
|
{
|
|||
|
int ret = sys_cond_signal_to(mParentReadBuffer->mReadThreadSignal,mParentFileGroup->GetReadThread());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!mPreloaded)
|
|||
|
{
|
|||
|
sys_lwmutex_unlock(&mParentReadBuffer->mNextConsumeByteMutex); //Release consumer lock
|
|||
|
}
|
|||
|
} //while check available data loop
|
|||
|
|
|||
|
return destBytesWritten;
|
|||
|
}
|
|||
|
|
|||
|
size_t CFileGroupOpenedFile::ReadFromUncompressedData( void *dest, size_t readSize)
|
|||
|
{
|
|||
|
uint64_t bytesRead = 0;
|
|||
|
bool dataCopied = false;
|
|||
|
size_t dataSeekPos = mDirEntry->mPosition + mSeekPosIndicator;
|
|||
|
size_t parentSeekPos = mParentFileGroup->GetPosOffset() + dataSeekPos;
|
|||
|
size_t destBytesWritten = 0;
|
|||
|
int ret;
|
|||
|
|
|||
|
if(mPreloaded)
|
|||
|
{
|
|||
|
TraceMemCopy((unsigned char*)dest, (Bytef*)mParentFileGroup->GetPreloadData() + dataSeekPos, readSize);
|
|||
|
destBytesWritten = readSize;
|
|||
|
mSeekPosIndicator += readSize;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sys_lwmutex_lock(&mParentReadBuffer->mNextConsumeByteMutex,0); //Ensure that no other consumer updates the 'next consume byte'
|
|||
|
|
|||
|
size_t dataAvailable = 0;
|
|||
|
void* dataLocation = NULL;
|
|||
|
size_t remainingData = mDirEntry->mLength - mSeekPosIndicator;
|
|||
|
|
|||
|
while(destBytesWritten < readSize)
|
|||
|
{
|
|||
|
bool dataFound = false;
|
|||
|
|
|||
|
if (parentSeekPos < mParentReadBuffer->mCurrentPos && remainingData > 0)
|
|||
|
{
|
|||
|
if (mParentFileGroup->UsingReadThread())
|
|||
|
{
|
|||
|
size_t availablePos = 0;
|
|||
|
sys_lwmutex_lock(&mParentReadBuffer->mBufferStartEndPosMutex,0); //Don't want read thread to 'wrap around' while we calculate the available data
|
|||
|
//Data may exist in read thread buffer
|
|||
|
//First find the start of the section of the buffer which won't be overwritten:
|
|||
|
if ((mParentReadBuffer->mBufferEndPos > 0) && (mParentReadBuffer->mBufferEndPos == mParentReadBuffer->mBufferStartPos)) //Include data from previous buffer cycle
|
|||
|
{
|
|||
|
if (mParentReadBuffer->mNextConsumePos > 0) //Read thread won't overwrite FILEGROUP_READ_STORE_SIZE bytes up to NextConsumeByte
|
|||
|
{
|
|||
|
if (mParentReadBuffer->mNextConsumePos > (mParentFileGroup->GetPosOffset() + FILEGROUP_READ_STORE_SIZE))
|
|||
|
{
|
|||
|
availablePos = mParentReadBuffer->mNextConsumePos - FILEGROUP_READ_STORE_SIZE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
availablePos = mParentFileGroup->GetPosOffset();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//This should only occur when read thread has filled buffer and no bytes have been read
|
|||
|
Assert(mParentReadBuffer->mCurrentByte == mParentReadBuffer->mBuffer);
|
|||
|
Assert(mParentReadBuffer->mAwaitingConsumer);
|
|||
|
availablePos = mParentReadBuffer->mBufferStartPos;
|
|||
|
}
|
|||
|
}
|
|||
|
else //No data available from previous cycle
|
|||
|
{
|
|||
|
if (mParentReadBuffer->mNextConsumePos > 0)
|
|||
|
{
|
|||
|
Assert(mParentReadBuffer->mNextConsumePos <= mParentReadBuffer->mCurrentPos);
|
|||
|
if ((mParentReadBuffer->mNextConsumePos - mParentReadBuffer->mBufferStartPos) > FILEGROUP_READ_STORE_SIZE)
|
|||
|
{
|
|||
|
availablePos = mParentReadBuffer->mNextConsumePos - FILEGROUP_READ_STORE_SIZE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
availablePos = mParentReadBuffer->mBufferStartPos;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
availablePos = mParentReadBuffer->mBufferStartPos;
|
|||
|
}
|
|||
|
}
|
|||
|
//Now check if compressed data exists within the 'safe' portion of the buffer:
|
|||
|
if (parentSeekPos >= availablePos)
|
|||
|
{
|
|||
|
dataFound = true;
|
|||
|
if ((mParentReadBuffer->mBufferEndPos > 0) && (mParentReadBuffer->mBufferEndPos == mParentReadBuffer->mBufferStartPos) &&
|
|||
|
(parentSeekPos < mParentReadBuffer->mBufferEndPos)) //Data wraps round end of buffer
|
|||
|
{
|
|||
|
dataAvailable = MIN((mParentReadBuffer->mBufferEndPos - parentSeekPos),remainingData);
|
|||
|
dataLocation = (mParentReadBuffer->mBuffer + mParentReadBuffer->mBufferSize) - (mParentReadBuffer->mBufferEndPos - parentSeekPos);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
dataAvailable = MIN((mParentReadBuffer->mCurrentPos - parentSeekPos),remainingData);
|
|||
|
dataLocation = mParentReadBuffer->mBuffer + (parentSeekPos - mParentReadBuffer->mBufferStartPos);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
sys_lwmutex_unlock(&mParentReadBuffer->mBufferStartEndPosMutex); //Happy for read thread to 'wrap around' from this point on
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//Don't need to worry about read thread overwriting the buffer, check whether the data exists anywhere in the buffer
|
|||
|
if (parentSeekPos >= mParentReadBuffer->mBufferStartPos)
|
|||
|
{
|
|||
|
dataFound = true;
|
|||
|
dataAvailable = MIN((mParentReadBuffer->mCurrentPos - parentSeekPos),remainingData);
|
|||
|
dataLocation = mParentReadBuffer->mBuffer + (parentSeekPos - mParentReadBuffer->mBufferStartPos);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!dataFound)
|
|||
|
{
|
|||
|
//Read data from file
|
|||
|
sys_lwmutex_lock(&mParentReadBuffer->mBufferMutex,0); //Lock buffer for write
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
system_time_t time1 = sys_time_get_system_time();
|
|||
|
#endif
|
|||
|
uint64_t pos;
|
|||
|
uint64_t alignedSeekPos = parentSeekPos - parentSeekPos%mParentReadBuffer->mFileBlockSize;
|
|||
|
uint64_t alignedReadSize = FILEGROUP_READ_CHUNK_SIZE;
|
|||
|
if(mParentReadBuffer->mReadChunkSize>FILEGROUP_READ_CHUNK_SIZE)
|
|||
|
{
|
|||
|
uint64_t alignedRemainingData = (parentSeekPos + remainingData) - alignedSeekPos; //Locate the remaining data from the alignedseekpos up to the end of the file
|
|||
|
if(alignedRemainingData > FILEGROUP_READ_CHUNK_SIZE)
|
|||
|
{
|
|||
|
alignedReadSize = MIN(mParentReadBuffer->mReadChunkSize,((((parentSeekPos + remainingData) - alignedSeekPos)/FILEGROUP_READ_CHUNK_SIZE)+1)*FILEGROUP_READ_CHUNK_SIZE);
|
|||
|
}
|
|||
|
}
|
|||
|
CellFsErrno retFs = cellFsLseek(mParentReadBuffer->mFile,(int64_t)alignedSeekPos,CELL_FS_SEEK_SET,&pos);
|
|||
|
if (retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
//snStopMarker(3);
|
|||
|
//char tempText[100];
|
|||
|
//snprintf(tempText, 100, "cellfsread aligned pos %d, seek pos %d, read size %d", alignedSeekPos, parentSeekPos, alignedReadSize);
|
|||
|
//snStartMarker((unsigned int)mParentReadBuffer->mFile, tempText);
|
|||
|
retFs = cellFsRead(mParentReadBuffer->mFile,mParentReadBuffer->mBuffer,alignedReadSize,&bytesRead);
|
|||
|
//snStopMarker((unsigned int)mParentReadBuffer->mFile);
|
|||
|
//snStartMarker(3, "SyncRead continue");
|
|||
|
//DebugPrint("ReadFromUncompressedData: pos %d\n", parentSeekPos);
|
|||
|
}
|
|||
|
while(retFs != CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
cellFsClose(mParentReadBuffer->mFile);
|
|||
|
sys_timer_usleep(250000); //Sleep for 0.25 seconds
|
|||
|
mParentReadBuffer->WaitOnDiskEjectRecovery();
|
|||
|
retFs = cellFsLseek(mParentReadBuffer->mFile,(int64_t)alignedSeekPos,CELL_FS_SEEK_SET,&pos);
|
|||
|
if (retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
retFs = cellFsRead(mParentReadBuffer->mFile,mParentReadBuffer->mBuffer,alignedReadSize,&bytesRead);
|
|||
|
}
|
|||
|
if(retFs == CELL_FS_EIO)
|
|||
|
{
|
|||
|
PS3_DisplayDiskErrorDialog();
|
|||
|
}
|
|||
|
else if(retFs == CELL_FS_SUCCEEDED)
|
|||
|
{
|
|||
|
PS3_ClearDiskErrorDialog();
|
|||
|
}
|
|||
|
}
|
|||
|
if (bytesRead > 0)
|
|||
|
{
|
|||
|
//Reset buffer data so that read thread continues from this point
|
|||
|
mParentReadBuffer->mBufferEndPos = 0;
|
|||
|
mParentReadBuffer->mBufferStartPos = alignedSeekPos;
|
|||
|
mParentReadBuffer->mCurrentByte = mParentReadBuffer->mBuffer + (int)bytesRead;
|
|||
|
mParentReadBuffer->mCurrentPos = alignedSeekPos + (int)bytesRead;
|
|||
|
mParentReadBuffer->mNextConsumePos = 0;
|
|||
|
if (mParentFileGroup->UsingReadThread() &&
|
|||
|
mParentReadBuffer->mAwaitingConsumer &&
|
|||
|
mParentReadBuffer->mCurrentPos < mParentFileGroup->Size())
|
|||
|
{
|
|||
|
int ret = sys_cond_signal_to(mParentReadBuffer->mReadThreadSignal,mParentFileGroup->GetReadThread());
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
system_time_t time2 = sys_time_get_system_time();
|
|||
|
mParentFileGroup->IncrReadTime(time2-time1);
|
|||
|
mParentFileGroup->m_bytesReadFromFileByConsumer += (int)bytesRead;
|
|||
|
mParentFileGroup->IncrReadBytes((int)bytesRead);
|
|||
|
#endif
|
|||
|
dataAvailable = MIN((int)(bytesRead-(parentSeekPos-alignedSeekPos)),remainingData);
|
|||
|
dataLocation = mParentReadBuffer->mBuffer + (parentSeekPos-alignedSeekPos);
|
|||
|
sys_lwmutex_unlock(&mParentReadBuffer->mBufferMutex); //Now safe for other threads to continue updating buffer
|
|||
|
}
|
|||
|
|
|||
|
int bytesOfInterest = MIN((readSize-destBytesWritten),dataAvailable);
|
|||
|
//snStartMarker(1, "MemCpy");
|
|||
|
TraceMemCopy((unsigned char*)dest + destBytesWritten, dataLocation, bytesOfInterest);
|
|||
|
//snStopMarker(1);
|
|||
|
destBytesWritten += bytesOfInterest;
|
|||
|
mSeekPosIndicator += bytesOfInterest;
|
|||
|
parentSeekPos += bytesOfInterest;
|
|||
|
remainingData = mDirEntry->mLength - mSeekPosIndicator;
|
|||
|
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
mParentFileGroup->m_bytesReadFromBuffer += bytesOfInterest;
|
|||
|
#endif
|
|||
|
|
|||
|
//Wait until this point before updating NextConsumePos, otherwise data may be overwritten during inflate.
|
|||
|
//Only update "NextConsumePos" if it has increased, otherwise it is possible to get into a situation
|
|||
|
// where some of the FILEGROUP_READ_STORE_SIZE bytes leading up to NextConsumePos are overwritten by the read thread.
|
|||
|
if (parentSeekPos > mParentReadBuffer->mNextConsumePos)
|
|||
|
{
|
|||
|
mParentReadBuffer->mNextConsumePos = parentSeekPos;
|
|||
|
|
|||
|
//Check if need to signal to read thread
|
|||
|
if (mParentFileGroup->UsingReadThread() &&
|
|||
|
mParentReadBuffer->mAwaitingConsumer &&
|
|||
|
mParentReadBuffer->mCurrentPos < mParentFileGroup->Size() &&
|
|||
|
((mParentReadBuffer->mCurrentPos + FILEGROUP_READ_CHUNK_SIZE) - mParentReadBuffer->mNextConsumePos) <= (mParentReadBuffer->mBufferSize - FILEGROUP_READ_SIGNAL_SIZE))
|
|||
|
{
|
|||
|
int ret = sys_cond_signal_to(mParentReadBuffer->mReadThreadSignal,mParentFileGroup->GetReadThread());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} //while check available data loop
|
|||
|
sys_lwmutex_unlock(&mParentReadBuffer->mNextConsumeByteMutex); //Release consumer lock
|
|||
|
}
|
|||
|
|
|||
|
return destBytesWritten;
|
|||
|
}
|
|||
|
|
|||
|
size_t CFileGroupOpenedFile::FS_fread( void *dest, size_t destSize, size_t size)
|
|||
|
{
|
|||
|
MEM_ALLOC_CREDIT();
|
|||
|
// Assert(V_stristr(mDirEntry.mName,"bars001c") == NULL);
|
|||
|
// mParentFileGroup->Lock();
|
|||
|
|
|||
|
// int readSize = MIN(destSize,size);//Ensure that we don't read more than destSize
|
|||
|
if (mParentFileGroup->HasBeenDeleted())
|
|||
|
{
|
|||
|
printf("ERROR: Attempting to read from file after filegroup has been deleted\n");
|
|||
|
}
|
|||
|
int readSize = size; //Need to ignore destsize to ensure that we get the same behaviour from filegroups that we get from the ordinary filesystem
|
|||
|
|
|||
|
bool eofRead = false; //Record eof setting at this point, but only set mEof following a successful read
|
|||
|
if(readSize > (mDirEntry->mLength - mSeekPosIndicator))
|
|||
|
{
|
|||
|
readSize = (mDirEntry->mLength - mSeekPosIndicator);
|
|||
|
eofRead = true;
|
|||
|
//CM:TODO Raise error condition?
|
|||
|
}
|
|||
|
|
|||
|
size_t bytesRead;
|
|||
|
if (mDirEntry->mCompressedLength > 0)
|
|||
|
{
|
|||
|
bytesRead = ReadFromCompressedData(dest,readSize);
|
|||
|
mSeekPosIndicator = mSeekPosIndicator + bytesRead;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
bytesRead = ReadFromUncompressedData(dest,readSize);
|
|||
|
mActualPosIndicator = mSeekPosIndicator + bytesRead;
|
|||
|
}
|
|||
|
|
|||
|
//CM:TODO Error handling
|
|||
|
//CM:TODO If this number differs from the bytes requested, set ferror and feof accordingly.
|
|||
|
//otherwise...
|
|||
|
//mActualPosIndicator = mSeekPosIndicator + bytesRead;
|
|||
|
mEof = eofRead;
|
|||
|
//if (!mParentFileGroup->IsCurrentFile(this))
|
|||
|
//{
|
|||
|
// mParentFileGroup->MakeCurrentFile(this);
|
|||
|
//}
|
|||
|
//mParentFileGroup->Unlock();
|
|||
|
|
|||
|
#ifdef MEMCMP_FILE_OPERATIONS
|
|||
|
if (mOrdinaryFile)
|
|||
|
{
|
|||
|
unsigned char* tmpDest = new unsigned char[size];
|
|||
|
fread(tmpDest,1,size,mOrdinaryFile);
|
|||
|
Assert((memcmp(tmpDest,dest,size)==0));
|
|||
|
delete[] tmpDest;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef TIME_FILE_OPERATIONS
|
|||
|
mParentFileGroup->m_fsReads++;
|
|||
|
#endif
|
|||
|
return bytesRead;
|
|||
|
}
|
|||
|
|
|||
|
char * CFileGroupOpenedFile::FS_fgets( char *dest, int destSize )
|
|||
|
{
|
|||
|
//mParentFileGroup->Lock();
|
|||
|
// char* retVal = NULL;
|
|||
|
|
|||
|
// int destLimit = MIN(destSize,((mDirEntry->mLength - mSeekPosIndicator) + 1));//Ensure that we don't read past the end of the file
|
|||
|
// mEof = (destLimit<= 1);
|
|||
|
// //fgets reads characters until (destLimit-1) characters have been read or either a newline or End-of-File is reached, whichever comes first.
|
|||
|
// if (mEof)
|
|||
|
// {
|
|||
|
// //CM:TODO Set error conditions
|
|||
|
// }
|
|||
|
// else
|
|||
|
// {
|
|||
|
// if (mDirEntry->mCompressedLength > 0)
|
|||
|
// {
|
|||
|
// retVal = FgetsFromCompressedData(dest,destLimit);
|
|||
|
// }
|
|||
|
// else
|
|||
|
// {
|
|||
|
// retVal = FgetsFromUncompressedData(dest,destLimit);
|
|||
|
// }
|
|||
|
// }
|
|||
|
// if (retVal)
|
|||
|
// {
|
|||
|
// mActualPosIndicator+=strlen(retVal);
|
|||
|
// mSeekPosIndicator = mActualPosIndicator;
|
|||
|
// //if (!mParentFileGroup->IsCurrentFile(this))
|
|||
|
// //{
|
|||
|
// // mParentFileGroup->MakeCurrentFile(this);
|
|||
|
// //}
|
|||
|
// }
|
|||
|
// //CM:TODO Error handlng, do ferror and feof need to be set?????.
|
|||
|
////mParentFileGroup->Unlock();
|
|||
|
//mParentFileGroup->m_fgets++;
|
|||
|
Assert(0); //fgets not implemented
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|