csgo-2018-source/filesystem/filegroup.cpp
2021-07-24 21:11:47 -07:00

2061 lines
74 KiB
C++

//========= Copyright © 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