csgo-2018-source/public/filegroup.h
2021-07-24 21:11:47 -07:00

327 lines
13 KiB
C++

//========= Copyright © 2006, Electonic Arts(C) 2006 - All Rights Reserved ============//
#ifndef FILEGROUP_H
#define FILEGROUP_H
#include <sys/stat.h>
#include "zlib/zlib.h"
#ifdef _PS3
// #ifndef _RETAIL
// #define TIME_FILE_OPERATIONS
// #endif
//#define MEMCMP_FILE_OPERATIONS
#include "tier0/platform.h"
#include <sys/synchronization.h>
#include <sys/types.h>
#include <sys/ppu_thread.h>
#include <sys/stat.h>
#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