Files
FC1/CrySystem/CryPak.h
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

626 lines
20 KiB
C++

//////////////////////////////////////////////////////////////////////
//
// Crytek CryENGINE Source code
//
// File:CryPak.h
//
// History:
// -Feb 2,2001:Created by Honich Andrey
// -June 12, 2003: Taken over by Sergiy Migdalskiy.
// Got rid of unzip usage, now using ZipDir for much more effective
// memory usage (~3-6 times less memory, and no allocator overhead)
// to keep the directory of the zip file; better overall effectiveness and
// more readable and manageable code, made the connection to Streaming Engine
//
//////////////////////////////////////////////////////////////////////
#ifndef CRYPAK_H
#define CRYPAK_H
#include <ICryPak.h>
#include "IMiniLog.h"
#include "ZipDir.h"
#include "MTSafeAllocator.h"
#include "CritSection.h"
#include "StlUtils.h"
#include "PakVars.h"
extern CMTSafeHeap* g_pSmallHeap;
extern CMTSafeHeap* g_pBigHeap;
// this is the header in the cache of the file data
struct CCachedFileData: public _reference_target_MT_novtbl<CCachedFileData>
{
CCachedFileData (class CCryPak* pPak, ZipDir::Cache*pZip, ZipDir::FileEntry* pFileEntry);
~CCachedFileData();
// return the data in the file, or NULL if error
// by default, if bRefreshCache is true, and the data isn't in the cache already,m
// the cache is refreshed. Otherwise, it returns whatever cache is (NULL if the data isn't cached yet)
void* GetData(bool bRefreshCache = true);
ZipDir::Cache* GetZip(){return m_pZip;}
ZipDir::FileEntry* GetFileEntry() {return m_pFileEntry;}
// the memory needs to be allocated out of a MT-safe heap here
void * __cdecl operator new (size_t size) { return g_pSmallHeap->Alloc(size, "CCachedFileData::new"); }
void __cdecl operator delete (void *p) { g_pSmallHeap->Free(p); };
unsigned GetFileDataOffset()
{
if (m_pFileEntry->nFileDataOffset == m_pFileEntry->INVALID_DATA_OFFSET)
m_pZip->Refresh (m_pFileEntry);
return m_pFileEntry->nFileDataOffset;
}
unsigned sizeofThis()const
{
return sizeof(*this) + (m_pFileData&&m_pFileEntry?m_pFileEntry->desc.lSizeUncompressed:0);
}
protected:
void* m_pFileData;
volatile LONG m_nRefCounter;
// the zip file in which this file is opened
ZipDir::CachePtr m_pZip;
// the file entry : if this is NULL, the entry is free and all the other fields are meaningless
ZipDir::FileEntry* m_pFileEntry;
class CCryPak* m_pPak;
friend struct CCachedFileDataOrder;
};
TYPEDEF_AUTOPTR(CCachedFileData);
typedef CCachedFileData_AutoPtr CCachedFileDataPtr;
// the cached data pointers are sorted by the FileEntries
struct CCachedFileDataOrder
{
bool operator () (const CCachedFileData* left, const CCachedFileData* right)const
{
return left->m_pFileEntry < right->m_pFileEntry;
}
};
// an (inside zip) emultated open file
struct CZipPseudoFile
{
CZipPseudoFile()
{
Construct();
}
enum
{
_O_COMMIT_FLUSH_MODE = 1 << 31,
_O_DIRECT_OPERATION = 1 << 30
};
// this object must be constructed before usage
// nFlags is a combination of _O_... flags
void Construct(CCachedFileData* pFileData = NULL, unsigned nFlags = 0)
{
m_pFileData = pFileData;
m_nFlags = nFlags;
m_nCurSeek = 0;
if ((nFlags & _O_DIRECT_OPERATION)&&pFileData&&pFileData->GetFileEntry()->nMethod == ZipFile::METHOD_STORE)
{
m_fDirect = fopen (pFileData->GetZip()->GetFilePath(), "rb"); // "rb" is the only supported mode
FSeek(0,SEEK_SET); // the user assumes offset 0 right after opening the file
}
else
m_fDirect = NULL;
}
// this object needs to be freed manually when the CryPak shuts down..
void Destruct()
{
assert ((bool)m_pFileData);
// mark it free, and deallocate the pseudofile memory
m_pFileData = NULL;
if (m_fDirect)
fclose (m_fDirect);
}
CCachedFileData* GetFile() {return m_pFileData;}
long FTell() {return m_nCurSeek;}
unsigned GetFileSize()
{
return GetFile()?GetFile()->GetFileEntry()->desc.lSizeUncompressed:0;
}
int FSeek (long nOffset, int nMode);
size_t FRead (void* pDest, size_t nSize, size_t nCount, FILE* hFile);
int FEof();
int FScanfv (const char* szFormat, va_list args);
char* FGets(char* pBuf, int n);
int Getc();
int Ungetc(int c);
FILETIME GetModificationTime()
{
return m_pFileData->GetFileEntry()->GetModificationTime();
}
const char* GetArchivePath()
{
return m_pFileData->GetZip()->GetFilePath();
}
protected:
long m_nCurSeek;
CCachedFileDataPtr m_pFileData;
// nFlags is a combination of _O_... flags
unsigned m_nFlags;
FILE* m_fDirect;
};
struct CIStringOrder
{
bool operator () (const string& left, const string& right) const
{
return stricmp(left.c_str(), right.c_str()) < 0;
}
};
class CCryPakFindData: public _reference_target_t
{
public:
// the directory wildcard must already be adjusted
CCryPakFindData (class CCryPak*pPak, const char* szDir);
bool empty() const;
bool Fetch(_finddata_t* pfd);
void Scan(CCryPak*pPak, const char* szDir);
size_t sizeofThis()const;
protected:
void ScanFS(CCryPak*pPak, const char* szDir);
void ScanZips(CCryPak*pPak, const char* szDir);
struct FileDesc
{
unsigned nAttrib;
unsigned nSize;
time_t tAccess;
time_t tCreate;
time_t tWrite;
FileDesc (struct _finddata_t* fd);
FileDesc (struct __finddata64_t* fd);
FileDesc (ZipDir::FileEntry* fe);
// default initialization is as a directory entry
FileDesc ();
};
typedef std::map<string, FileDesc, CIStringOrder> FileMap;
FileMap m_mapFiles;
};
TYPEDEF_AUTOPTR(CCryPakFindData);
//////////////////////////////////////////////////////////////////////
class CCryPak : public ICryPak
{
// the array of pseudo-files : emulated files in the virtual zip file system
// the handle to the file is its index inside this array.
// some of the entries can be free. The entries need to be destructed manually
typedef std::vector<CZipPseudoFile> ZipPseudoFileArray;
ZipPseudoFileArray m_arrOpenFiles;
// the array of file datas; they are relatively self-contained and can
// read and cache the file data on-demand. It's up to the clients
// to use caching or access the zips directly
CCritSection m_csCachedFiles;
typedef CMTSafeAllocator<CCachedFileData*> CachedFileDataAllocator;
typedef std::set<CCachedFileData*,CCachedFileDataOrder, CachedFileDataAllocator > CachedFileDataSet;
CachedFileDataSet m_setCachedFiles;
// The F* emulation functions critical sectio: protects all F* functions
// that don't have a chance to be called recursively (to avoid deadlocks)
CCritSection m_csMain;
// open zip cache objects that can be reused. They're self-[un]registered
// they're sorted by the path and
typedef std::vector<ICryArchive*> ArchiveArray;
ArchiveArray m_arrArchives;
// the array of opened caches - they get destructed by themselves (these are auto-pointers, see the ZipDir::Cache documentation)
struct PackDesc
{
string strBindRoot; // the zip binding root WITH the trailing native slash
//string strFileName; // the zip file name (no path)
const char* GetFullPath()const {return pZip->GetFilePath();}
ICryArchive_AutoPtr pArchive;
ZipDir::CachePtr pZip;
size_t sizeofThis()
{
return strBindRoot.capacity() + pZip->GetSize();
}
};
typedef std::vector<PackDesc > ZipArray;
CCritSection m_csZips;
ZipArray m_arrZips;
friend class CCryPakFindData;
typedef std::set<CCryPakFindData_AutoPtr> CryPakFindDataSet;
CryPakFindDataSet m_setFindData;
IMiniLog *m_pLog;
// the root: "C:\MasterCD\"
string m_strMasterCDRoot;
// this is the list of MOD subdirectories that will be prepended to the actual relative file path
// they all have trailing forward slash. "" means the root dir
std::vector<string> m_arrMods;
//////////////////////////////////////////////////////////////////////////
// Opened files collector.
//////////////////////////////////////////////////////////////////////////
bool m_bRememberOpenedFiles;
typedef std::set<string,stl::less_stricmp<string> > RecordedFilesSet;
RecordedFilesSet m_recordedFilesSet;
const PakVars* m_pPakVars;
bool InitPack(const char *szBasePath, unsigned nFlags = FLAGS_PATH_REAL);
public:
// given the source relative path, constructs the full path to the file according to the flags
const char* AdjustFileName (const char *src, char dst[g_nMaxPath], unsigned nFlags,bool *bFoundInPak=NULL);
// this is the start of indexation of pseudofiles:
// to the actual index , this offset is added to get the valid handle
enum {g_nPseudoFileIdxOffset = 1};
// this defines which slash will be kept internally
enum {g_cNativeSlash = '\\', g_cNonNativeSlash = '/'};
// makes the path lower-case and removes the duplicate and non native slashes
// may make some other fool-proof stuff
// may NOT write beyond the string buffer (may not make it longer)
// returns: the pointer to the ending terminator \0
static char* BeautifyPath(char* dst);
CCryPak(IMiniLog* pLog,PakVars* pPakVars = NULL);
~CCryPak();
const PakVars* GetPakVars()const {return m_pPakVars;}
public:
PakInfo* GetPakInfo();
void FreePakInfo (PakInfo*);
//! adds a mod to the list of mods
void AddMod(const char* szMod);
//! removes a mod from the list of mods
void RemoveMod(const char* szMod);
//! Puts the memory statistics into the given sizer object
//! According to the specifications in interface ICrySizer
void GetMemoryStatistics(ICrySizer *pSizer);
// open the physical archive file - creates if it doesn't exist
// returns NULL if it's invalid or can't open the file
virtual ICryArchive* OpenArchive (const char* szPath, unsigned nFlags = 0);
// returns the path to the archive in which the file was opened
virtual const char* GetFileArchivePath (FILE* f);
CCritSection& GetCachedFileLock() {return m_csCachedFiles;}
void Register (CCachedFileData* p)
{
// actually, registration may only happen when the set is already locked, but for generality..
AUTO_LOCK(m_csCachedFiles);
m_setCachedFiles.insert (p);
}
void Unregister (CCachedFileData* p)
{
AUTO_LOCK(m_csCachedFiles);
m_setCachedFiles.erase (p);
}
// ICryPak interface
virtual bool Init (const char *szBasePath);
virtual void Release();
virtual bool OpenPack(const char *pName, unsigned nFlags = 0);
virtual bool OpenPack(const char* szBindRoot, const char *pName, unsigned nFlags = 0);
virtual bool OpenPackCommon(const char* szBindRoot, const char *pName, unsigned nFlags = 0);
// after this call, the file will be unlocked and closed, and its contents won't be used to search for files
virtual bool ClosePack(const char* pName, unsigned nFlags = 0);
virtual bool OpenPacks(const char *pWildcard, unsigned nFlags = 0);
virtual bool OpenPacks(const char* szBindRoot, const char *pWildcard, unsigned nFlags = 0);
bool OpenPacksCommon(const char* szDir, char *cWork, unsigned nFlags);
// closes pack files by the path and wildcard
virtual bool ClosePacks(const char* pWildcard, unsigned nFlags = 0);
// returns the file modification time
virtual FILETIME GetModificationTime(FILE* f);
// this function gets the file data for the given file, if found.
// The file data object may be created in this function,
// and it's important that the autoptr is returned: another thread may release the existing
// cached data before the function returns
// the path must be absolute normalized lower-case with forward-slashes
CCachedFileDataPtr GetFileData(const char* szName);
// tests if the given file path refers to an existing file inside registered (opened) packs
// the path must be absolute normalized lower-case with forward-slashes
bool HasFileEntry (const char* szPath);
virtual FILE *FOpen(const char *pName, const char *mode, unsigned nFlags);
virtual FILE *FOpen(const char *pName, const char *mode,char *szFileGamePath,int nLen);
virtual size_t FRead(void *data, size_t length, size_t elems, FILE *handle);
virtual size_t FWrite(void *data, size_t length, size_t elems, FILE *handle);
virtual int FSeek(FILE *handle, long seek, int mode);
virtual long FTell(FILE *handle);
virtual int FFlush(FILE *handle);
virtual int FClose(FILE *handle);
virtual intptr_t FindFirst(const char *pDir, struct _finddata_t *fd);
virtual int FindNext(intptr_t handle, struct _finddata_t *fd);
virtual int FindClose(intptr_t handle);
virtual int FEof(FILE *handle);
virtual int FScanf(FILE *, const char *, ...);
virtual char *FGets(char *, int, FILE *);
virtual int Getc(FILE *);
virtual int Ungetc(int c, FILE *);
virtual int FPrintf(FILE *handle, const char *format, ...);
unsigned FGetSize(FILE* f);
// virtual bool IsOutOfDate(const char * szFileName, const char * szMasterFile);
//virtual FILETIME GetFileTime(const char * szFileName);
// creates a directory
virtual bool MakeDir (const char* szPath);
// returns the current game directory, with trailing slash (or empty string if it's right in MasterCD)
// this is used to support Resource Compiler which doesn't have access to this interface:
// in case all the contents is located in a subdirectory of MasterCD, this string is the subdirectory name with slash
//virtual const char* GetGameDir();
void Register (ICryArchive* pArchive);
void Unregister (ICryArchive* pArchive);
ICryArchive* FindArchive (const char* szFullPath);
// compresses the raw data into raw data. The buffer for compressed data itself with the heap passed. Uses method 8 (deflate)
// returns one of the Z_* errors (Z_OK upon success)
// MT-safe
int RawCompress (const void* pUncompressed, unsigned long* pDestSize, void* pCompressed, unsigned long nSrcSize, int nLevel = -1);
// Uncompresses raw (without wrapping) data that is compressed with method 8 (deflated) in the Zip file
// returns one of the Z_* errors (Z_OK upon success)
// This function just mimics the standard uncompress (with modification taken from unzReadCurrentFile)
// with 2 differences: there are no 16-bit checks, and
// it initializes the inflation to start without waiting for compression method byte, as this is the
// way it's stored into zip file
int RawUncompress (void* pUncompressed, unsigned long* pDestSize, const void* pCompressed, unsigned long nSrcSize) ;
//////////////////////////////////////////////////////////////////////////
// Files opening recorder.
//////////////////////////////////////////////////////////////////////////
void RecordFileOpen( bool bEnable );
void RecordFile( const char *szFilename );
void EnumerateRecordedFiles( RecordedFilesEnumCallback enumCallback );
void OnMissingFile (const char* szPath);
// missing file -> count of missing files
typedef CMTSafeAllocator<std::pair<string, unsigned> > MissingFileMapAllocator;
typedef std::map<string, unsigned, std::less<string>, MissingFileMapAllocator > MissingFileMap;
MissingFileMap m_mapMissingFiles;
};
struct CryArchiveSortByName
{
bool operator () (const ICryArchive* left, const ICryArchive* right)const
{
return stricmp(left->GetFullPath(), right->GetFullPath()) < 0;
}
bool operator () (const char* left, const ICryArchive* right)const
{
return stricmp(left, right->GetFullPath()) < 0;
}
bool operator () (const ICryArchive* left, const char* right)const
{
return stricmp(left->GetFullPath(), right) < 0;
}
};
template <class Cache>
class TCryArchive: public ICryArchive
{
public:
TCryArchive (CCryPak*pPak, const string& strBindRoot, Cache* pCache, unsigned nFlags = 0):
m_pCache(pCache),
m_strBindRoot (strBindRoot),
m_pPak(pPak),
m_nFlags (nFlags)
{
pPak->Register(this);
}
~TCryArchive()
{
m_pPak->Unregister (this);
}
// finds the file; you don't have to close the returned handle
Handle FindFile (const char* szRelativePath)
{
char szFullPath[CCryPak::g_nMaxPath];
const char*pPath = AdjustPath (szRelativePath, szFullPath);
if (!pPath)
return NULL;
return m_pCache->FindFile(pPath);
}
// returns the size of the file (unpacked) by the handle
unsigned GetFileSize (Handle h)
{
assert (m_pCache->IsOwnerOf((ZipDir::FileEntry*)h));
return ((ZipDir::FileEntry*)h)->desc.lSizeUncompressed;
}
// reads the file into the preallocated buffer (must be at least the size of GetFileSize())
int ReadFile (Handle h, void* pBuffer)
{
assert (m_pCache->IsOwnerOf((ZipDir::FileEntry*)h));
return m_pCache->ReadFile ((ZipDir::FileEntry*)h, NULL, pBuffer);
}
// returns the full path to the archive file
const char* GetFullPath() const
{
return m_pCache->GetFilePath();
}
unsigned GetFlags() const {return m_nFlags;}
bool SetFlags(unsigned nFlagsToSet)
{
if (nFlagsToSet & FLAGS_RELATIVE_PATHS_ONLY)
m_nFlags |= FLAGS_RELATIVE_PATHS_ONLY;
if (nFlagsToSet & ~(FLAGS_RELATIVE_PATHS_ONLY))
{
// we don't support changing of any other flags
return false;
}
return true;
}
bool ResetFlags (unsigned nFlagsToReset)
{
if (nFlagsToReset & FLAGS_RELATIVE_PATHS_ONLY)
m_nFlags &= ~FLAGS_RELATIVE_PATHS_ONLY;
if (nFlagsToReset & ~(FLAGS_RELATIVE_PATHS_ONLY))
{
// we don't support changing of any other flags
return false;
}
return true;
}
Cache* GetCache() {return m_pCache;}
protected:
// returns the pointer to the relative file path to be passed
// to the underlying Cache pointer. Uses the given buffer to construct the path.
// returns NULL if the file path is invalid
const char* AdjustPath (const char* szRelativePath, char szFullPathBuf[CCryPak::g_nMaxPath])
{
if (!szRelativePath[0])
return NULL;
if (m_nFlags & FLAGS_RELATIVE_PATHS_ONLY)
return szRelativePath;
if (szRelativePath[1] == ':' || (m_nFlags & FLAGS_ABSOLUTE_PATHS))
{
// make the normalized full path and try to match it against the binding root of this object
const char* szFullPath = m_pPak->AdjustFileName (szRelativePath, szFullPathBuf, m_nFlags & FLAGS_IGNORE_MODS ? ICryPak::FLAGS_PATH_REAL : 0);
size_t nPathLen = strlen(szFullPath);
if (nPathLen <= m_strBindRoot.length())
return NULL;
// you should access exactly the file under the directly in which the zip is situated
if (szFullPath[m_strBindRoot.length()] != '/' && szFullPath[m_strBindRoot.length()] != '\\')
return NULL;
#if defined(LINUX)
if (comparePathNames(szFullPath, m_strBindRoot.c_str(), m_strBindRoot.length()))
#else
if (memicmp(szFullPath, m_strBindRoot.c_str(), m_strBindRoot.length()))
#endif
return NULL; // the roots don't match
return szFullPath + m_strBindRoot.length() + 1;
}
return szRelativePath;
}
protected:
_smart_ptr<Cache> m_pCache;
// the binding root may be empty string - in this case, the absolute path binding won't work
string m_strBindRoot;
CCryPak* m_pPak;
unsigned m_nFlags;
};
class CryArchiveRW: public TCryArchive<ZipDir::CacheRW>
{
public:
CryArchiveRW (CCryPak*pPak, const string& strBindRoot, ZipDir::CacheRW* pCache, unsigned nFlags = 0):
TCryArchive<ZipDir::CacheRW>(pPak, strBindRoot, pCache, nFlags)
{
}
~CryArchiveRW()
{
}
// Adds a new file to the zip or update an existing one
// adds a directory (creates several nested directories if needed)
// compression methods supported are 0 (store) and 8 (deflate) , compression level is 0..9 or -1 for default (like in zlib)
int UpdateFile (const char* szRelativePath, void* pUncompressed, unsigned nSize, unsigned nCompressionMethod = 0, int nCompressionLevel = -1);
// deletes the file from the archive
int RemoveFile (const char* szRelativePath);
// deletes the directory, with all its descendants (files and subdirs)
int RemoveDir (const char* szRelativePath);
int RemoveAll();
enum {gClassId = 1};
unsigned GetClassId()const {return gClassId;}
};
class CryArchive: public TCryArchive<ZipDir::Cache>
{
public:
CryArchive (CCryPak* pPak, const string& strBindRoot, ZipDir::Cache* pCache, unsigned nFlags):
TCryArchive<ZipDir::Cache>(pPak, strBindRoot, pCache, nFlags)
{ }
~CryArchive(){}
// Adds a new file to the zip or update an existing one
// adds a directory (creates several nested directories if needed)
// compression methods supported are METHOD_STORE == 0 (store) and
// METHOD_DEFLATE == METHOD_COMPRESS == 8 (deflate) , compression
// level is LEVEL_FASTEST == 0 till LEVEL_BEST == 9 or LEVEL_DEFAULT == -1
// for default (like in zlib)
int UpdateFile (const char* szRelativePath, void* pUncompressed, unsigned nSize, unsigned nCompressionMethod = 0, int nCompressionLevel = -1) {return ZipDir::ZD_ERROR_INVALID_CALL;}
// deletes the file from the archive
int RemoveFile (const char* szRelativePath) {return ZipDir::ZD_ERROR_INVALID_CALL;}
int RemoveAll();
// deletes the directory, with all its descendants (files and subdirs)
int RemoveDir (const char* szRelativePath) {return ZipDir::ZD_ERROR_INVALID_CALL;}
enum {gClassId = 2};
unsigned GetClassId()const {return gClassId;}
};
#endif