//========= Copyright © 2006, Electonic Arts(C) 2006 - All Rights Reserved ============// #ifndef FILEGROUP_H #define FILEGROUP_H #include #include "zlib/zlib.h" #ifdef _PS3 // #ifndef _RETAIL // #define TIME_FILE_OPERATIONS // #endif //#define MEMCMP_FILE_OPERATIONS #include "tier0/platform.h" #include #include #include #include #endif //_PS3 #define FILEGROUP_FORMAT_ID "FGP" #define PREVIOUS_FILEGROUP_FORMAT_VERSION 1 //TEMP: Used for backwards compatibility #define FILEGROUP_FORMAT_VERSION 2 //Preload section enhancement #define FILEGROUP_FORMAT_VERSION_COMPILED_DIRECTORY 3 //Preprocessed directory section const int MAX_FILE_GROUPS = 7; const int FILEGROUP_UNCOMPRESSED_BUFFER_SIZE = (8 * 1024); const int FILEGROUP_READ_CHUNK_SIZE = (64 * 1024); const int FILEGROUP_READ_STORE_SIZE = FILEGROUP_READ_CHUNK_SIZE * 2; const int FILEGROUP_READ_SIGNAL_SIZE = FILEGROUP_READ_CHUNK_SIZE * 2; const int FILEGROUP_READ_THREAD_BUFFER_SIZE = FILEGROUP_READ_CHUNK_SIZE * 4; #define FILEGROUP_USE_HASH_DIRECTORY #ifdef FILEGROUP_USE_HASH_DIRECTORY const int FILEGROUP_DIRECTORY_BUCKETS = 256; //Must be power of 2 const int FILEGROUP_BUCKET_MOD = FILEGROUP_DIRECTORY_BUCKETS-1; #endif struct FileIdentifier { char* mName; unsigned int mNameId; bool mNameIdSet; }; struct DirectoryHeader { char mId[4]; int mVersion; int mNumEntries; int mNumPreloadEntries; //Added for FILEGROUP_FORMAT_VERSION 2 }; struct PreviousDirectoryHeader //TEMP: Used for backwards compatibility { char mId[4]; int mVersion; int mNumEntries; }; struct DirectoryEntry { char mName[MAX_PATH]; size_t mPosition; size_t mLength; size_t mCompressedLength; //Set to zero if compression not used }; struct SmallDirectoryEntry { // char mName[116]; unsigned int mNameId; size_t mPosition; size_t mLength; size_t mCompressedLength; //Set to zero if compression not used }; #ifdef _PS3 #ifdef MEMCMP_FILE_OPERATIONS struct DirectoryEntryExtraInfo { char mFullFileName[MAX_PATH]; }; #endif struct FileGroupReadBuffer { void AllocateBuffer(size_t bufferSize); void FreeBuffer(); void CloseFile(); void WaitOnDiskEjectRecovery(); sys_ppu_thread_t mReadThread; unsigned char* mBuffer; size_t mBufferSize; size_t mReadChunkSize; // unsigned char mBuffer[FILEGROUP_READ_THREAD_BUFFER_SIZE]; int mFile; char mFileName[MAX_PATH]; //Need to store filegroup name so that file can be reopened following disk eject uint64_t mFileBlockSize; uint64_t mFileSectorSize; sys_lwmutex_t mBufferMutex; //Used for all updates to the buffer data sys_lwmutex_attribute_t mBufferMutexAttr; sys_lwmutex_t mNextConsumeByteMutex; //Used for synchronization of multiple consumer threads sys_lwmutex_attribute_t mNextConsumeByteMutexAttr; sys_lwmutex_t mBufferStartEndPosMutex; //Used for synchronization of buffer start and end positions - don't want to update these while in use by consumer sys_lwmutex_attribute_t mBufferStartEndPosMutexAttr; sys_mutex_t mReadThreadSignalMutex; sys_mutex_attribute_t mReadThreadSignalMutexAttr; sys_cond_t mReadThreadSignal; //Signaled by consumer thread indicating that either: //- there is space for the read thread to carry on reading //- or the filegroup has been deleted sys_cond_attribute_t mReadThreadSignalAttr; sys_lwmutex_t mEjectRecoveryMutex; //Used to ensure only one thread performs the disk eject recovery procedure sys_lwmutex_attribute_t mEjectRecoveryMutexAttr; bool mFileGroupDeleted; size_t mNextConsumePos; //Position of next byte due for read by the consumer thread //Read thread does not overwrite any bytes FILEGROUP_READ_STORE_SIZE bytes before this point. //unsigned char* mNextConsumeByte; //Position of next byte due for read by the consumer thread // //Read thread does not overwrite bytes beyond this point size_t mBufferStartPos; //Position in file corresponding to the beginning of the buffer size_t mCurrentPos; //Current position in file unsigned char* mCurrentByte; //Next byte to be read to in buffer size_t mBufferEndPos; //Position in file corresponding to the end of the buffer (set to zero until initial population of buffer) bool mAwaitingConsumer; //If set, indicates that the read thread is waiting for data to be read by the consumer thread #ifdef TIME_FILE_OPERATIONS system_time_t m_fReadTime; int m_fReadBytes; #endif }; class CFileGroup; class CFileGroupOpenedFile { public: CFileGroupOpenedFile(); ~CFileGroupOpenedFile(); #ifdef MEMCMP_FILE_OPERATIONS void Init(const SmallDirectoryEntry& dirEntry, CFileGroup* parentFileGroup, FileGroupReadBuffer* parentBuffer, char* fullFileName, bool preload); #else void Init(const SmallDirectoryEntry& dirEntry, CFileGroup* parentFileGroup, FileGroupReadBuffer* parentBuffer, bool preload); #endif virtual void FS_fseek( __int64 pos, int seekType ); virtual long FS_ftell(); virtual int FS_feof(); virtual size_t FS_fread( void *dest, size_t destSize, size_t size); virtual char *FS_fgets( char *dest, int destSize ); // const char* GetName() const {return mDirEntry->mName;} size_t GetPosition() const {return mDirEntry->mPosition;} size_t GetLength() const {return mDirEntry->mLength;} size_t GetCompressedLength() const {return mDirEntry->mCompressedLength;} bool IsPreloaded() const {return mPreloaded;} void FS_fclose(); #ifdef _DEBUG static int refCount; #endif private: void Rewind(); size_t ReadFromCompressedData( void *dest, size_t readSize); size_t ReadFromUncompressedData( void *dest, size_t readSize); void TraceMemCopy(void* pDest, const void* pSource, size_t nBytes); SmallDirectoryEntry const * mDirEntry; CFileGroup* mParentFileGroup; size_t mSeekPosIndicator;// This is current position as returned by FS_ftell. size_t mActualPosIndicator;// This is the uncompressed position within the file corresponding to the last read. size_t mCompressedPosIndicator; //This is the number of compressed bytes which have been read. bool mEof; //Set to true on attempting to read beyond the end of the file unsigned char mUncompressedBuffer[FILEGROUP_UNCOMPRESSED_BUFFER_SIZE]; //unsigned char* mRemainingUncompressedBuffer; //Pointer to bytes within the uncompressed buffer which have yet to be read //size_t mNumRemainingUncompressedBytes; //Number of bytes in the compressed buffer which have yet to be read int mUncompressedBufferStartPos; z_stream mStrm; //Zlib stream FileGroupReadBuffer* mParentReadBuffer; bool mPreloaded; #ifdef MEMCMP_FILE_OPERATIONS int mOrdinaryFile; #endif }; class CFileGroupSystem { public: CFileGroupSystem(); ~CFileGroupSystem(); void Init(); int AddFileGroup(const char* fileGroupName, bool useReadThread = true, int* filegroup_index = NULL, bool usepreload = false, int bufferSize = FILEGROUP_READ_CHUNK_SIZE, int readChunkSize = FILEGROUP_READ_CHUNK_SIZE); void DeleteFileGroup(); void DeleteFileGroup(int ID); void DeleteFileGroup(CFileGroup* fileGroup); void DeleteFileGroup(CFileGroup* fileGroup, int groupIndex); int CurrentFileGroup(); CFileGroupOpenedFile* FS_fopen( FileIdentifier *filename, const char *options, unsigned int flags, __int64 *size ); CFileGroupOpenedFile* FS_fopen( int filegroup_index, FileIdentifier *filename, const char *options, unsigned int flags, __int64 *size ); int FS_stat( FileIdentifier *path, struct stat *buf ); int FS_stat( FileIdentifier *path, CellFsStat *buf ); int FS_stat( int filegroup_index, FileIdentifier *path, struct stat *buf ); int FS_stat( int filegroup_index, FileIdentifier *path, CellFsStat *buf ); void Lock() {int ret = sys_lwmutex_lock( &mFileGroupSystemMutex, 0 ); if (ret != CELL_OK) printf("Error locking filegroup system mutex %d\n", ret);} void Unlock() {int ret = sys_lwmutex_unlock( &mFileGroupSystemMutex); if (ret != CELL_OK) printf("Error unlocking filegroup system mutex %d\n", ret);} void DecrementNumFileGroups(){m_numFileGroups--;} CFileGroupOpenedFile* GetOpenedFile(); //Get next available CFileGroupOpenedFile from pool void FreeOpenedFile(CFileGroupOpenedFile*); //Return CFileGroupOpenedFile to pool #ifdef _DEBUG static int refCount; #endif private: CFileGroup* m_mapGroup; CFileGroup* m_shaderGroup; CFileGroup* m_fileGroups[MAX_FILE_GROUPS]; int m_numFileGroups; int m_currentFileGroup; sys_lwmutex_t mFileGroupSystemMutex; int m_lastGroupPopulated; //Pool of CFileGroupOpenedFile objects struct FileGroupFilePoolNode { CFileGroupOpenedFile thisFile; FileGroupFilePoolNode* nextNode; }; FileGroupFilePoolNode* m_openedFilePool; FileGroupFilePoolNode* m_availableFileNodes; FileGroupFilePoolNode* m_openedFileNodes; int m_totalOpenedFiles; bool m_initComplete; }; class CFileGroup { public: CFileGroup(CFileGroupSystem* pFs); ~CFileGroup(); void Clear(); int Populate(const char* fileGroupName, bool usepreload = false); CFileGroupOpenedFile* FS_fopen( FileIdentifier *pFileName, const char *pOptions, unsigned int flags, __int64 *size ); int FS_stat( FileIdentifier *path, CellFsStat *buf ); void DecrementOpenedCount(); void IncrementOpenedCount(){mOpenedCount++;} int GetFile() const {return mReadBuffer.mFile;} size_t GetPosOffset() const {return mPosOffset;} int GetOpenedCount(){return mOpenedCount;} void* GetPreloadData(){return mPreloadSection;} void FlagForDeletion(){mFlaggedForDeletion = true;} void Lock() {mFs->Lock();} void Unlock() {mFs->Unlock();} bool IsPopulated() {return (mReadBuffer.mFile);} void FreeOpenedFile(CFileGroupOpenedFile* freeFile){mFs->FreeOpenedFile(freeFile);} void StartReadThread(); void StopReadThread(); void TidyUpSynchObjects(); sys_ppu_thread_t GetReadThread(){return mReadBuffer.mReadThread;} bool UsingReadThread(){return (mReadBuffer.mReadThread);} // unsigned char* GetIgnoreUncompressBuffer(){return mFs->GetIgnoreUncompressBuffer();} size_t Size(){return mFileStat.st_size;} bool HasBeenDeleted(){return mReadBuffer.mFileGroupDeleted;} void CloseFile(){mReadBuffer.CloseFile();} void AllocateReadBuffer(size_t bufSize){mReadBuffer.AllocateBuffer(bufSize);} void SetReadChunkSize(size_t readChunkSize){mReadBuffer.mReadChunkSize = readChunkSize;} void FreeReadBuffer(){mReadBuffer.FreeBuffer();} #ifdef _DEBUG static int refCount; #endif #ifdef TIME_FILE_OPERATIONS void PrintFileGroupStats(); void ResetFileTimings(); system_time_t GetfReadTime(){return mReadBuffer.m_fReadTime;} int GetfReadBytes(){return mReadBuffer.m_fReadBytes;} void IncrReadTime(system_time_t timeVal){mReadBuffer.m_fReadTime += timeVal;} void IncrReadBytes(int bytesVal){mReadBuffer.m_fReadBytes += bytesVal; /*if(mReadBuffer.m_fReadBytes%10485760 == 0) PrintFileGroupStats();*/}; system_time_t m_memCopyTime; system_time_t m_uncompressTime; int m_fsReads; int m_fgets; int m_uncompressCalls; int m_rewinds; int m_fileJumps; int m_bytesfRead; int m_bytesReadFromFileByConsumer; int m_bytesReadFromBuffer; #endif private: CellFsErrno PopulateDirectory(DirectoryHeader* hdr, int* entriesPopulated); int FindFile(FileIdentifier *pFileName); char* ReformatFileName(char* inputFileName); unsigned int HashFileName(const char * fName); SmallDirectoryEntry* mDirectoryEntries; unsigned int mNumDirectoryEntries; void* mPreloadSection; int mPreloadEntries; #ifdef FILEGROUP_USE_HASH_DIRECTORY struct HashBucket { unsigned int mPosition; unsigned int mCount; }; unsigned int* mHashDirectory; //ordered into buckets HashBucket mHashBuckets[FILEGROUP_DIRECTORY_BUCKETS]; #endif unsigned int mLastOpenedDirectoryIndex; unsigned int mLastFailedId; CellFsStat mFileStat; size_t mPosOffset; //Position at which the data starts CFileGroupSystem* mFs; int mOpenedCount; bool mFlaggedForDeletion; FileGroupReadBuffer mReadBuffer; #ifdef MEMCMP_FILE_OPERATIONS DirectoryEntryExtraInfo* mDirectoryExtraInfo; #endif }; #endif //_PS3 #endif //FILEGROUP_H