ZipDirStructures.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. // This file contains only the support definitions for CZipDir class
  9. // implementation. This it to unload the ZipDir.h from secondary stuff.
  10. #pragma once
  11. #include <AzCore/base.h>
  12. #include <AzCore/IO/FileIO.h>
  13. #include <AzCore/IO/SystemFile.h>
  14. #include <AzCore/std/smart_ptr/intrusive_ptr.h>
  15. #include <AzFramework/Archive/ZipFileFormat.h>
  16. #if AZ_TRAIT_USE_WINDOWS_FILE_API && AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
  17. #define SUPPORT_UNBUFFERED_IO
  18. #endif
  19. struct z_stream_s;
  20. namespace AZ::IO
  21. {
  22. class FileIOBase;
  23. struct MemoryBlock;
  24. }
  25. namespace AZ::IO::ZipDir
  26. {
  27. struct FileEntry;
  28. struct CZipFile
  29. {
  30. AZ::IO::HandleType m_fileHandle;
  31. #ifdef SUPPORT_UNBUFFERED_IO
  32. AZ::IO::SystemFile m_unbufferedFile;
  33. size_t m_nSectorSize;
  34. void* m_pReadTarget;
  35. #endif
  36. int64_t m_nSize;
  37. int64_t m_nCursor;
  38. const char* m_szFilename;
  39. AZStd::intrusive_ptr<AZ::IO::MemoryBlock> m_pInMemoryData;
  40. AZ::IO::FileIOBase* m_fileIOBase = nullptr;
  41. CZipFile();
  42. CZipFile(const CZipFile& file) = delete;
  43. CZipFile & operator=(const CZipFile&) = delete;
  44. void Swap(CZipFile& other);
  45. bool IsInMemory() const { return m_pInMemoryData != nullptr; }
  46. void LoadToMemory(AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = {});
  47. void UnloadFromMemory();
  48. void Close(bool bUnloadFromMem = true);
  49. #ifdef SUPPORT_UNBUFFERED_IO
  50. bool OpenUnbuffered(const char* filename);
  51. bool EvaluateSectorSize(const char* filename);
  52. #endif
  53. };
  54. // possible errors occurring during the method execution
  55. // to avoid clashing with the global Windows defines, we prefix these with ZD_
  56. enum ErrorEnum
  57. {
  58. ZD_ERROR_SUCCESS = 0,
  59. ZD_ERROR_IO_FAILED,
  60. ZD_ERROR_UNEXPECTED,
  61. ZD_ERROR_UNSUPPORTED,
  62. ZD_ERROR_INVALID_SIGNATURE,
  63. ZD_ERROR_ZIP_FILE_IS_CORRUPT,
  64. ZD_ERROR_DATA_IS_CORRUPT,
  65. ZD_ERROR_NO_CDR,
  66. ZD_ERROR_CDR_IS_CORRUPT,
  67. ZD_ERROR_NO_MEMORY,
  68. ZD_ERROR_VALIDATION_FAILED,
  69. ZD_ERROR_CRC32_CHECK,
  70. ZD_ERROR_ZLIB_FAILED,
  71. ZD_ERROR_ZLIB_CORRUPTED_DATA,
  72. ZD_ERROR_ZLIB_NO_MEMORY,
  73. ZD_ERROR_CORRUPTED_DATA,
  74. ZD_ERROR_INVALID_CALL,
  75. ZD_ERROR_NOT_IMPLEMENTED,
  76. ZD_ERROR_FILE_NOT_FOUND,
  77. ZD_ERROR_DIR_NOT_FOUND,
  78. ZD_ERROR_NAME_TOO_LONG,
  79. ZD_ERROR_INVALID_PATH,
  80. ZD_ERROR_FILE_ALREADY_EXISTS,
  81. ZD_ERROR_ARCHIVE_TOO_LARGE,
  82. };
  83. // the error describes the reason of the error, as well as the error code, line of code where it happened etc.
  84. struct Error
  85. {
  86. Error(ErrorEnum _nError, const char* _szDescription, const char* _szFunction, const char* _szFile, unsigned _nLine)
  87. : nError(_nError)
  88. , m_szDescription(_szDescription)
  89. , szFunction(_szFunction)
  90. , szFile(_szFile)
  91. , nLine(_nLine)
  92. {
  93. }
  94. ErrorEnum nError;
  95. const char* getError();
  96. const char* getDescription() {return m_szDescription; }
  97. const char* szFunction, * szFile;
  98. unsigned nLine;
  99. protected:
  100. // the description of the error; if needed, will be made as a dynamic string
  101. const char* m_szDescription;
  102. };
  103. #if defined(_RELEASE)
  104. inline static constexpr bool IsReleaseConfig{ true };
  105. #else
  106. inline static constexpr bool IsReleaseConfig{};
  107. #endif // _RELEASE
  108. // possible initialization methods
  109. enum class InitMethod
  110. {
  111. // initializes without any sort of extra validation steps
  112. Default,
  113. // initializes with extra validation steps
  114. // not available in RELEASE
  115. // will check CDR and local headers data match
  116. ValidateHeaders,
  117. // initializes with extra validation steps
  118. // not available in RELEASE
  119. // will check CDR and local headers data match
  120. // will check file data CRC matches (when file is read)
  121. FullValidation,
  122. };
  123. // Uncompresses raw (without wrapping) data that is compressed with method 8 (deflated) in the Zip file
  124. // returns one of the Z_* errors (Z_OK upon success)
  125. int ZipRawUncompress(void* pUncompressed, size_t* pDestSize, const void* pCompressed, size_t nSrcSize);
  126. // compresses the raw data into raw data. The buffer for compressed data itself with the heap passed. Uses method 8 (deflate)
  127. // returns one of the Z_* errors (Z_OK upon success), and the size in *pDestSize. the pCompressed buffer must be at least nSrcSize*1.001+12 size
  128. int ZipRawCompress(const void* pUncompressed, size_t* pDestSize, void* pCompressed, size_t nSrcSize, int nLevel);
  129. int ZipRawCompressZSTD(const void* pUncompressed, size_t* pDestSize, void* pCompressed, size_t nSrcSize, int nLevel);
  130. int ZipRawCompressLZ4(const void* pUncompressed, size_t* pDestSize, void* pCompressed, size_t nSrcSize, int nLevel);
  131. // fseek wrapper with memory in file support.
  132. int64_t FSeek(CZipFile* zipFile, int64_t origin, int command);
  133. // fread wrapper with file in memory support
  134. int64_t FRead(CZipFile* zipFile, void* data, size_t nElemSize, size_t nCount);
  135. // ftell wrapper with file in memory support
  136. int64_t FTell(CZipFile* zipFile);
  137. int FEof(CZipFile* zipFile);
  138. //////////////////////////////////////////////////////////////////////////
  139. struct SExtraZipFileData
  140. {
  141. uint64_t nLastModifyTime{};
  142. };
  143. struct FileEntryBase
  144. {
  145. FileEntryBase() = default;
  146. FileEntryBase(const ZipFile::CDRFileHeader& header, const SExtraZipFileData& extra);
  147. inline static constexpr uint32_t INVALID_DATA_OFFSET = 0xFFFFFFFF;
  148. ZipFile::DataDescriptor desc{};
  149. uint32_t nFileDataOffset{ INVALID_DATA_OFFSET }; // offset of the packed info inside the file; NOTE: this can be INVALID_DATA_OFFSET, if not calculated yet!
  150. uint32_t nFileHeaderOffset{ INVALID_DATA_OFFSET }; // offset of the local file header
  151. uint32_t nNameOffset{}; // offset of the file name in the name pool for the directory
  152. uint16_t nMethod{}; // the method of compression (0 if no compression/store)
  153. uint16_t nReserved0{}; // Reserved
  154. // the file modification times
  155. uint16_t nLastModTime{};
  156. uint16_t nLastModDate{};
  157. uint64_t nNTFS_LastModifyTime{};
  158. // the offset to the start of the next file's header - this
  159. // can be used to calculate the available space in zip file
  160. uint32_t nEOFOffset{};
  161. // whether to check the CRC upon the next data read
  162. bool bCheckCRCNextRead{};
  163. };
  164. // this is the record about the file in the Zip file.
  165. struct FileEntry
  166. : FileEntryBase
  167. {
  168. AZ_CLASS_ALLOCATOR(FileEntry, AZ::SystemAllocator, 0);
  169. // mutex that can be used to product reads for the current file entry
  170. AZStd::mutex m_readLock;
  171. using FileEntryBase::FileEntryBase;
  172. FileEntry(const FileEntry&) = delete;
  173. FileEntry& operator=(const FileEntry&) = delete;
  174. bool IsInitialized()
  175. {
  176. // structure marked as non-initialized should have nFileHeaderOffset == INVALID_DATA_OFFSET
  177. return nFileHeaderOffset != INVALID_DATA_OFFSET;
  178. }
  179. // returns the name of this file, given the pointer to the name pool
  180. const char* GetName(const char* pNamePool) const
  181. {
  182. return pNamePool + nNameOffset;
  183. }
  184. // sets the current time to modification time
  185. // calculates CRC32 for the new data
  186. void OnNewFileData(const void* pUncompressed, uint64_t nSize, uint64_t nCompressedSize, uint32_t nCompressionMethod, bool bContinuous);
  187. uint64_t GetModificationTime();
  188. bool IsCompressed() const
  189. {
  190. return (
  191. nMethod != ZipFile::METHOD_STORE_AND_STREAMCIPHER_KEYTABLE &&
  192. nMethod != ZipFile::METHOD_STORE
  193. );
  194. }
  195. };
  196. // tries to refresh the file entry from the given file (reads from there if needed)
  197. // returns the error code if the operation was impossible to complete
  198. ErrorEnum Refresh(CZipFile* f, FileEntryBase* pFileEntry);
  199. // writes into the file local header (NOT including the name, only the header structure)
  200. // the file must be opened both for reading and writing
  201. ErrorEnum UpdateLocalHeader(AZ::IO::HandleType fileHandle, FileEntryBase* pFileEntry);
  202. // writes into the file local header - without Extra data
  203. // puts the new offset to the file data to the file entry
  204. // in case of error can put INVALID_DATA_OFFSET into the data offset field of file entry
  205. ErrorEnum WriteLocalHeader(AZ::IO::HandleType fileHandle, FileEntryBase* pFileEntry, AZStd::string_view szRelativePath);
  206. // conversion routines for the date/time fields used in Zip
  207. uint16_t DOSDate(tm*);
  208. uint16_t DOSTime(tm*);
  209. const char* DOSTimeCStr(uint16_t nTime);
  210. const char* DOSDateCStr(uint16_t nTime);
  211. struct DirHeader;
  212. // this structure represents a subdirectory descriptor in the directory record.
  213. // it points to the actual directory info (list of its subdirs and files), as well
  214. // as on its name
  215. struct DirEntry
  216. {
  217. AZ_CLASS_ALLOCATOR(DirEntry, AZ::SystemAllocator, 0);
  218. uint32_t nDirHeaderOffset{}; // offset, in bytes, relative to this object, of the actual directory record header
  219. uint32_t nNameOffset{}; // offset of the dir name in the name pool of the parent directory
  220. // returns the name of this directory, given the pointer to the name pool of hte parent directory
  221. const char* GetName(const char* pNamePool) const
  222. {
  223. return pNamePool + nNameOffset;
  224. }
  225. // returns the pointer to the actual directory record.
  226. // call this function only for the actual structure instance contained in a directory record and
  227. // followed by the other directory records
  228. const DirHeader* GetDirectory() const
  229. {
  230. return (const DirHeader*)(((const char*)this) + nDirHeaderOffset);
  231. }
  232. DirHeader* GetDirectory()
  233. {
  234. return (DirHeader*)(((char*)this) + nDirHeaderOffset);
  235. }
  236. };
  237. // this is the head of the directory record
  238. // the name pool follows straight the directory and file entries.
  239. struct DirHeader
  240. {
  241. uint16_t numDirs{}; // number of directory entries - DirEntry structures
  242. uint16_t numFiles{}; // number of file entries - FileEntry structures
  243. // returns the pointer to the name pool that follows this object
  244. // you can only call this method for the structure instance actually followed by the dir record
  245. const char* GetNamePool() const
  246. {
  247. return ((char*)(this + 1)) + (size_t)this->numDirs * sizeof(DirEntry) + (size_t)this->numFiles * sizeof(FileEntry);
  248. }
  249. char* GetNamePool()
  250. {
  251. return ((char*)(this + 1)) + (size_t)this->numDirs * sizeof(DirEntry) + (size_t)this->numFiles * sizeof(FileEntry);
  252. }
  253. // returns the pointer to the i-th directory
  254. // call this only for the actual instance of the structure at the head of dir record
  255. const DirEntry* GetSubdirEntry(uint32_t i) const
  256. {
  257. AZ_Assert(i < numDirs, "Index %u is out of bounds to lookup subdirectory entry . Index must be less than %hu", i, numDirs);
  258. return ((const DirEntry*)(this + 1)) + i;
  259. }
  260. DirEntry* GetSubdirEntry(uint32_t i)
  261. {
  262. return const_cast<DirEntry*>(const_cast<const DirHeader*>(this)->GetSubdirEntry(i));
  263. }
  264. // returns the pointer to the i-th file
  265. // call this only for the actual instance of the structure at the head of dir record
  266. const FileEntry* GetFileEntry(uint32_t i) const
  267. {
  268. AZ_Assert(i < numFiles, "Index %u is out of range of number of file entries %hu", i, numFiles);
  269. return (const FileEntry*)(((const DirEntry*)(this + 1)) + numDirs) + i;
  270. }
  271. FileEntry* GetFileEntry (uint32_t i)
  272. {
  273. return const_cast<FileEntry*>(const_cast<const DirHeader*>(this)->GetFileEntry(i));
  274. }
  275. // finds the subdirectory entry by the name, using the names from the name pool
  276. // assumes: all directories are sorted in alphabetical order.
  277. // case-sensitive (must be lower-case if case-insensitive search in Win32 is performed)
  278. DirEntry* FindSubdirEntry(AZStd::string_view szName);
  279. // finds the file entry by the name, using the names from the name pool
  280. // assumes: all directories are sorted in alphabetical order.
  281. // case-sensitive (must be lower-case if case-insensitive search in Win32 is performed)
  282. FileEntry* FindFileEntry(AZStd::string_view szName);
  283. };
  284. // this is the sorting predicate for directory entries
  285. struct DirEntrySortPred
  286. {
  287. DirEntrySortPred(const char* pNamePool)
  288. : m_pNamePool{ pNamePool }
  289. {
  290. }
  291. bool operator()(const FileEntry& left, const FileEntry& right) const
  292. {
  293. return strcmp(left.GetName(m_pNamePool), right.GetName(m_pNamePool)) < 0;
  294. }
  295. bool operator()(const FileEntry& left, AZStd::string_view szRight) const
  296. {
  297. return left.GetName(m_pNamePool) < szRight;
  298. }
  299. bool operator()(AZStd::string_view szLeft, const FileEntry& right) const
  300. {
  301. return szLeft < right.GetName(m_pNamePool);
  302. }
  303. bool operator()(const DirEntry& left, const DirEntry& right) const
  304. {
  305. return strcmp(left.GetName(m_pNamePool), right.GetName(m_pNamePool)) < 0;
  306. }
  307. bool operator()(const DirEntry& left, AZStd::string_view szName) const
  308. {
  309. return left.GetName(m_pNamePool) < szName;
  310. }
  311. bool operator()(const char* szLeft, const DirEntry& right) const
  312. {
  313. return szLeft < right.GetName(m_pNamePool);
  314. }
  315. const char* m_pNamePool;
  316. };
  317. struct UncompressLookahead
  318. {
  319. inline static constexpr size_t Capacity = 16384;
  320. UncompressLookahead()
  321. : cachedStartIdx(0)
  322. , cachedEndIdx(0)
  323. {
  324. }
  325. char buffer[Capacity];
  326. uint32_t cachedStartIdx;
  327. uint32_t cachedEndIdx;
  328. };
  329. }