//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //===========================================================================// #ifndef PACKFILE_H #define PACKFILE_H #ifdef _WIN32 #pragma once #endif // How many bytes compressed filehandles should hold for seeking backwards. Seeks beyond this limit will require // rewinding and restarting the compression, at significant performance penalty. (Warnings emitted when this occurs) #define PACKFILE_COMPRESSED_FILEHANDLE_SEEK_BUFFER 4096 // How many bytes compressed filehandles should attempt to read (and cache) at a time from the underlying compressed data. #define PACKFILE_COMPRESSED_FILEHANDLE_READ_BUFFER 4096 // Emit warnings in debug builds if we hold more than this many compressed file handles alive to alert to the poor // memory characteristics. #define PACKFILE_COMPRESSED_FILE_HANDLES_WARNING 20 #include "basefilesystem.h" #include "tier1/refcount.h" #include "tier1/utlbuffer.h" #include "tier1/lzmaDecoder.h" class CPackFile; class CZipPackFile; // A pack file handle - essentially represents a file inside the pack file. class CPackFileHandle { public: virtual ~CPackFileHandle() {}; virtual int Read( void* pBuffer, int nDestSize, int nBytes ) = 0; virtual int Seek( int nOffset, int nWhence ) = 0; virtual int Tell() = 0; virtual int Size() = 0; virtual void SetBufferSize( int nBytes ) = 0; virtual int GetSectorSize() = 0; virtual int64 AbsoluteBaseOffset() = 0; }; class CZipPackFileHandle : public CPackFileHandle { public: CZipPackFileHandle( CZipPackFile* pOwner, int64 nBase, unsigned int nLength, unsigned int nIndex = -1, unsigned int nFilePointer = 0 ); virtual ~CZipPackFileHandle(); virtual int Read( void* pBuffer, int nDestSize, int nBytes ) OVERRIDE; virtual int Seek( int nOffset, int nWhence ) OVERRIDE; virtual int Tell() OVERRIDE { return m_nFilePointer; }; virtual int Size() OVERRIDE { return m_nLength; }; virtual void SetBufferSize( int nBytes ) OVERRIDE; virtual int GetSectorSize() OVERRIDE; virtual int64 AbsoluteBaseOffset() OVERRIDE; protected: int64 m_nBase; // Base offset of the file inside the pack file. unsigned int m_nFilePointer; // Current seek pointer (0 based from the beginning of the file). CZipPackFile* m_pOwner; // Pack file that owns this handle unsigned int m_nLength; // Length of this file. unsigned int m_nIndex; // Index into the pack's directory table }; class CLZMAZipPackFileHandle : public CZipPackFileHandle { public: CLZMAZipPackFileHandle( CZipPackFile* pOwner, int64 nBase, unsigned int nOriginalSize, unsigned int nCompressedSize, unsigned int nIndex = -1, unsigned int nFilePointer = 0 ); ~CLZMAZipPackFileHandle(); virtual int Read( void* pBuffer, int nDestSize, int nBytes ) OVERRIDE; virtual int Seek( int nOffset, int nWhence ) OVERRIDE; virtual int Tell() OVERRIDE; virtual int Size() OVERRIDE; private: // Ensure there are bytes in the read buffer, assuming we're not at the end of the underlying data int FillReadBuffer(); // Reset buffers and underlying seek position to 0 void Reset(); // Contains the last PACKFILE_COMPRESSED_FILEHANDLE_SEEK_BUFFER decompressed bytes. The Put and Get locations mimic our // filehandle -- TellPut() == TelGet() when we are not back seeking. CUtlBuffer m_BackSeekBuffer; // The read buffer from the underlying compressed stream. We read PACKFILE_COMPRESSED_FILEHANDLE_READ_BUFFER bytes // into this buffer, then consume it via the buffer get position. CUtlBuffer m_ReadBuffer; // The decompress stream we feed our base filehandle into CLZMAStream *m_pLZMAStream; // Current seek position in uncompressed data int m_nSeekPosition; // Size of the decompressed data unsigned int m_nOriginalSize; }; //----------------------------------------------------------------------------- // An abstract pack file class CPackFile : public CRefCounted { public: CPackFile(); virtual ~CPackFile(); // The means by which you open files: virtual CFileHandle *OpenFile( const char *pFileName, const char *pOptions = "rb" ) = 0; // Check for existance in pack virtual bool ContainsFile( const char *pFileName ) = 0; // The two functions a pack file must provide virtual bool Prepare( int64 fileLen = -1, int64 nFileOfs = 0 ) = 0; // Returns the filename for a given file in the pack. Returns true if a filename is found, otherwise buffer is filled with "unknown" virtual bool IndexToFilename( int nIndex, char* buffer, int nBufferSize ) = 0; inline int GetSectorSize(); virtual void SetupPreloadData() {} virtual void DiscardPreloadData() {} virtual int64 GetPackFileBaseOffset() = 0; CBaseFileSystem *FileSystem() { return m_fs; } // Helper for the filesystem's FindFirst/FindNext() API which mimics the old windows equivalent. pWildcard is the // same pattern that you would pass to FindFirst, not a true wildcard. // Mirrors the VPK code's similar call. virtual void GetFileAndDirLists( const char *pFindWildCard, CUtlStringList &outDirnames, CUtlStringList &outFilenames, bool bSortedOutput ) = 0; // Note: threading model for pack files assumes that data // is segmented into pack files that aggregate files // meant to be read in one thread. Performance characteristics // tuned for that case CThreadFastMutex m_mutex; // Path management: void SetPath( const CUtlSymbol &path ) { m_Path = path; } const CUtlSymbol& GetPath() const { Assert( m_Path != UTL_INVAL_SYMBOL ); return m_Path; } CUtlSymbol m_Path; // possibly embedded pack int64 m_nBaseOffset; CUtlString m_ZipName; bool m_bIsMapPath; long m_lPackFileTime; int m_refCount; int m_nOpenFiles; FILE *m_hPackFileHandleFS; #if defined( SUPPORT_PACKED_STORE ) CPackedStoreFileHandle m_hPackFileHandleVPK; #endif bool m_bIsExcluded; int m_PackFileID; protected: // This is the core IO routine for reading anything from a pack file, everything should go through here at some point virtual int ReadFromPack( int nIndex, void* buffer, int nDestBytes, int nBytes, int64 nOffset ) = 0; int64 m_FileLength; CBaseFileSystem *m_fs; friend class CPackFileHandle; }; class CZipPackFile : public CPackFile { friend class CZipPackFileHandle; public: CZipPackFile( CBaseFileSystem* fs, void *pSection = NULL ); virtual ~CZipPackFile(); // Loads the pack file virtual bool Prepare( int64 fileLen = -1, int64 nFileOfs = 0 ) OVERRIDE; virtual bool ContainsFile( const char *pFileName ) OVERRIDE; virtual CFileHandle *OpenFile( const char *pFileName, const char *pOptions = "rb" ) OVERRIDE; virtual void GetFileAndDirLists( const char *pFindWildCard, CUtlStringList &outDirnames, CUtlStringList &outFilenames, bool bSortedOutput ) OVERRIDE; virtual int64 GetPackFileBaseOffset() OVERRIDE { return m_nBaseOffset; } virtual bool IndexToFilename( int nIndex, char *pBuffer, int nBufferSize ) OVERRIDE; protected: virtual int ReadFromPack( int nIndex, void* buffer, int nDestBytes, int nBytes, int64 nOffset ) OVERRIDE; #pragma pack(1) typedef struct { char name[ 112 ]; int64 filepos; int64 filelen; } packfile64_t; typedef struct { char id[ 4 ]; int64 dirofs; int64 dirlen; } packheader64_t; typedef struct { char id[ 8 ]; int64 packheaderpos; int64 originalfilesize; } packappenededheader_t; #pragma pack() // A Pack file directory entry: class CPackFileEntry { public: unsigned int m_nPosition; unsigned int m_nOriginalSize; unsigned int m_nCompressedSize; unsigned int m_HashName; unsigned short m_nPreloadIdx; unsigned short pad; unsigned short m_nCompressionMethod; FileNameHandle_t m_hFileName; }; class CPackFileLessFunc { public: bool Less( CPackFileEntry const& src1, CPackFileEntry const& src2, void *pCtx ); }; // Find a file inside a pack file: const CPackFileEntry* FindFile( const char* pFileName ); // Entries to the individual files stored inside the pack file. CUtlSortVector< CPackFileEntry, CPackFileLessFunc > m_PackFiles; bool GetFileInfo( const char *pFileName, int &nBaseIndex, int64 &nFileOffset, int &nOriginalSize, int &nCompressedSize, unsigned short &nCompressionMethod ); // Preload Support void SetupPreloadData() OVERRIDE; void DiscardPreloadData() OVERRIDE; ZIP_PreloadDirectoryEntry* GetPreloadEntry( int nEntryIndex ); int64 m_nPreloadSectionOffset; unsigned int m_nPreloadSectionSize; ZIP_PreloadHeader *m_pPreloadHeader; unsigned short* m_pPreloadRemapTable; ZIP_PreloadDirectoryEntry *m_pPreloadDirectory; void* m_pPreloadData; CByteswap m_swap; #if defined ( _X360 ) void *m_pSection; #endif }; #endif // PACKFILE_H