Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIR_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIR_H
#pragma once
#include "smartptr.h"
#include "ZipFileFormat.h"
#include "zipdirstructures.h"
#include "smartptr.h"
#include "ZipDirTree.h"
#include "ZipDirList.h"
#include "ZipDirCache.h"
#include "ZipDirCacheRW.h"
#include "ZipDirCacheFactory.h"
#include "ZipDirFind.h"
#include "ZipDirFindRW.h"
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIR_H
@@ -0,0 +1,298 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <smartptr.h>
#include "FileUtil.h"
#include "ZipFileFormat.h"
#include "zipdirstructures.h"
#include "ZipDirTree.h"
#include "ZipDirCache.h"
#include "ZipDirFind.h"
#include "ZipDirCacheFactory.h"
#include <zlib.h>
#include <AzCore/IO/SystemFile.h>
#include "PathHelpers.h"
#include <ZipDir/ZipDir_Traits_Platform.h>
using namespace ZipFile;
// initializes the instance structure
void ZipDir::Cache::Construct(FILE* fNew, size_t nDataSizeIn, const EncryptionKey& key)
{
m_nRefCount = 0;
m_pFile = fNew;
m_nDataSize = nDataSizeIn;
m_nZipPathOffset = nDataSizeIn;
m_bEncryptHeaders = false;
m_encryptionKey = key;
}
// self-destruct when ref count drops to 0
void ZipDir::Cache::Delete()
{
if (m_pFile)
{
fclose (m_pFile);
}
free(this);
}
// looks for the given file record in the Central Directory. If there's none, returns NULL.
// if there is some, returns the pointer to it.
// the Path must be the relative path to the file inside the Zip
// if the file handle is passed, it will be used to find the file data offset, if one hasn't been initialized yet
ZipDir::FileEntry* ZipDir::Cache::FindFile (const char* szPath, [[maybe_unused]] bool bRefresh)
{
ZipDir::FindFile fd (this);
if (!fd.FindExact(szPath))
{
assert (!fd.GetFileEntry());
return NULL;
}
assert (fd.GetFileEntry());
return fd.GetFileEntry();
}
// loads the given file into the pCompressed buffer (the actual compressed data)
// if the pUncompressed buffer is supplied, uncompresses the data there
// buffers must have enough memory allocated, according to the info in the FileEntry
// NOTE: there's no need to decompress if the method is 0 (store)
// returns 0 if successful or error code if couldn't do something
ZipDir::ErrorEnum ZipDir::Cache::ReadFile (FileEntry* pFileEntry, void* pCompressed, void* pUncompressed)
{
if (!pFileEntry)
{
return ZD_ERROR_INVALID_CALL;
}
if (pFileEntry->desc.lSizeUncompressed == 0)
{
assert (pFileEntry->desc.lSizeCompressed == 0);
return ZD_ERROR_SUCCESS;
}
assert (pFileEntry->desc.lSizeCompressed > 0);
ErrorEnum nError = Refresh(pFileEntry);
if (nError != ZD_ERROR_SUCCESS)
{
return nError;
}
if (AZ_TRAIT_CRYCOMMONTOOLS_FSEEK(m_pFile, pFileEntry->nFileDataOffset, SEEK_SET))
{
return ZD_ERROR_IO_FAILED;
}
SmartPtr pBufferDestroyer;
void* pBuffer = pCompressed; // the buffer where the compressed data will go
if (pFileEntry->nMethod == 0 && pUncompressed)
{
// we can directly read into the uncompress buffer
pBuffer = pUncompressed;
}
if (!pBuffer)
{
if (!pUncompressed)
{
// what's the sense of it - no buffers at all?
return ZD_ERROR_INVALID_CALL;
}
pBuffer = malloc(pFileEntry->desc.lSizeCompressed);
pBufferDestroyer.Attach(pBuffer); // we want it auto-freed once we return
}
if (fread (pBuffer, pFileEntry->desc.lSizeCompressed, 1, m_pFile) != 1)
{
return ZD_ERROR_IO_FAILED;
}
if (pFileEntry->nMethod == METHOD_DEFLATE_AND_ENCRYPT)
{
ZipDir::Decrypt((char*)pBuffer, pFileEntry->desc.lSizeCompressed, m_encryptionKey);
}
// if there's a buffer for uncompressed data, uncompress it to that buffer
if (pUncompressed)
{
if (pFileEntry->nMethod == 0)
{
assert (pBuffer == pUncompressed);
//assert (pFileEntry->desc.lSizeCompressed == pFileEntry->nSizeUncompressed);
//memcpy (pUncompressed, pBuffer, pFileEntry->desc.lSizeCompressed);
}
else
{
unsigned long nSizeUncompressed = pFileEntry->desc.lSizeUncompressed;
if (Z_OK != ZipRawUncompress(pUncompressed, &nSizeUncompressed, pBuffer, pFileEntry->desc.lSizeCompressed))
{
return ZD_ERROR_CORRUPTED_DATA;
}
}
}
return ZD_ERROR_SUCCESS;
}
// loads and unpacks the file into a newly created buffer (that must be subsequently freed with
// Free()) Returns NULL if failed
void* ZipDir::Cache::AllocAndReadFile (FileEntry* pFileEntry)
{
if (!pFileEntry)
{
return NULL;
}
void* pData = malloc(pFileEntry->desc.lSizeUncompressed);
if (pData)
{
if (ZD_ERROR_SUCCESS != ReadFile (pFileEntry, NULL, pData))
{
free(pData);
pData = NULL;
}
}
return pData;
}
// frees the memory block that was previously allocated by AllocAndReadFile
void ZipDir::Cache::Free (void* pData)
{
free(pData);
}
// refreshes information about the given file entry into this file entry
ZipDir::ErrorEnum ZipDir::Cache::Refresh (FileEntry* pFileEntry)
{
if (!pFileEntry)
{
return ZD_ERROR_INVALID_CALL;
}
if (pFileEntry->nFileDataOffset != pFileEntry->INVALID_DATA_OFFSET)
{
return ZD_ERROR_SUCCESS; // the data offset has been successfully read..
}
return ZipDir::Refresh(m_pFile, pFileEntry, m_bEncryptHeaders);
}
//////////////////////////////////////////////////////////////////////////
uint32 ZipDir::Cache::GetFileDataOffset(FileEntry* pFileEntry)
{
if (pFileEntry->nFileDataOffset == pFileEntry->INVALID_DATA_OFFSET)
{
ZipDir::Refresh (m_pFile, pFileEntry, m_bEncryptHeaders);
}
return pFileEntry->nFileDataOffset;
}
// returns the size of memory occupied by the instance referred to by this cache
// must be exact, because it's used by CacheRW to reallocate this cache
size_t ZipDir::Cache::GetSize() const
{
return m_nDataSize + sizeof(Cache) + strlen(GetFilePath());
}
// QUICK check to determine whether the file entry belongs to this object
bool ZipDir::Cache::IsOwnerOf (const FileEntry* pFileEntry) const
{
// just check whether the pointer is within the memory block of this cache instance
return ((ULONG_PTR)pFileEntry >= (ULONG_PTR)(GetRoot() + 1)
&& (ULONG_PTR)pFileEntry <= ((ULONG_PTR)GetRoot()) + m_nDataSize - sizeof(FileEntry));
}
bool ZipDir::Cache::UnpakToDisk(const string& destFolder)
{
return UnpakToDiskInternal(GetRoot(), destFolder);
}
bool ZipDir::Cache::UnpakToDiskInternal(ZipDir::DirHeader* folder, const string& destFolder)
{
if (!folder)
{
return false;
}
if (!FileUtil::EnsureDirectoryExists(destFolder.c_str()))
{
return false;
}
bool result = true;
for (ZipFile::ushort fileNum = 0; fileNum < folder->numFiles; ++fileNum)
{
ZipDir::FileEntry* fileEntry = folder->GetFileEntry(fileNum);
if (!fileEntry)
{
result = false;
continue;
}
string filePath = PathHelpers::Join(destFolder, fileEntry->GetName(folder->GetNamePool()));
AZ::IO::SystemFile file;
if (!file.Open(filePath.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_WRITE | AZ::IO::SystemFile::SF_OPEN_CREATE))
{
result = false;
continue;
}
if (!fileEntry->desc.lSizeUncompressed)
{
// Nothing to write. Just close the file.
file.Close();
continue;
}
AZStd::vector<AZ::u8> buffer(fileEntry->desc.lSizeUncompressed);
if (ReadFile(fileEntry, nullptr, buffer.data()) == ZD_ERROR_SUCCESS)
{
file.Write(buffer.data(), buffer.size());
file.Close();
}
else
{
file.Close();
AZ::IO::SystemFile::Delete(filePath.c_str());
result = false;
continue;
}
}
for (ZipFile::ushort dirNum = 0; dirNum < folder->numDirs; ++dirNum)
{
ZipDir::DirEntry* entry = folder->GetSubdirEntry(dirNum);
if (!entry)
{
result = false;
continue;
}
string newPath = PathHelpers::Join(destFolder, entry->GetName(folder->GetNamePool()));
if (!UnpakToDiskInternal(entry->GetDirectory(), newPath))
{
result = false;
continue;
}
}
return result;
}
@@ -0,0 +1,140 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Declarations of the class used to parse and cache Zipped directory.
// This class is actually an auto-pointer to the instance of the cache, so it can
// be easily passed by value.
// The cache instance contains the optimized for memory usage and fast search tree
// of the files/directories inside the zip; each file has a descriptor with the
// info about where its compressed data lies within the file
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRCACHE_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRCACHE_H
#pragma once
/////////////////////////////////////////////////////////////
// THe Zip Dir uses a special memory layout for keeping the structure of zip file.
// This layout is optimized for small memory footprint (for big zip files)
// and quick binary-search access to the individual files.
//
// The serialized layout consists of a number of directory records.
// Each directory record starts with the DirHeader structure, then
// it has an array of DirEntry structures (sorted by name),
// array of FileEntry structures (sorted by name) and then
// the pool of names, followed by pad bytes to align the whole directory
// record on 4-byte boundray.
namespace ZipDir
{
// this is the header of the instance data allocated dynamically
// it contains the FILE* : it owns it and closes upon destruction
struct Cache
{
void AddRef() { ++m_nRefCount; }
void Release()
{
if (--m_nRefCount <= 0)
{
Delete();
}
}
int NumRefs() const { return m_nRefCount; }
// looks for the given file record in the Central Directory. If there's none, returns NULL.
// if there is some, returns the pointer to it.
// the Path must be the relative path to the file inside the Zip
// if the file handle is passed, it will be used to find the file data offset, if one hasn't been initialized yet
// if bFull is true, then the full information about the file is returned (the offset to the data may be unknown at this point)-
// if needed, the file is accessed and the information is loaded
FileEntry* FindFile (const char* szPath, bool bFullInfo = false);
// loads the given file into the pCompressed buffer (the actual compressed data)
// if the pUncompressed buffer is supplied, uncompresses the data there
// buffers must have enough memory allocated, according to the info in the FileEntry
// NOTE: there's no need to decompress if the method is 0 (store)
// returns 0 if successful or error code if couldn't do something
ErrorEnum ReadFile (FileEntry* pFileEntry, void* pCompressed, void* pUncompressed);
// loads and unpacks the file into a newly created buffer (that must be subsequently freed with
// Free()) Returns NULL if failed
void* AllocAndReadFile (FileEntry* pFileEntry);
// frees the memory block that was previously allocated by AllocAndReadFile
void Free (void*);
// refreshes information about the given file entry into this file entry
ErrorEnum Refresh (FileEntry* pFileEntry);
// Return FileEntity data offset inside zip file.
uint32 GetFileDataOffset(FileEntry* pFileEntry);
// returns the root directory record;
// through this directory record, user can traverse the whole tree
DirHeader* GetRoot() const
{
return (DirHeader*)(this + 1);
}
// returns the size of memory occupied by the instance referred to by this cache
// must be exact, because it's used by CacheRW to reallocate this cache
size_t GetSize() const;
// QUICK check to determine whether the file entry belongs to this object
bool IsOwnerOf (const FileEntry* pFileEntry) const;
// returns the string - path to the zip file from which this object was constructed.
// this will be "" if the object was constructed with a factory that wasn't created with FLAGS_MEMORIZE_ZIP_PATH
const char* GetFilePath() const
{
return ((const char*)(this + 1)) + m_nZipPathOffset;
}
// Unpak the file into a destination folder
bool UnpakToDisk(const string& destFolder);
friend class CacheFactory; // the factory class creates instances of this class
friend class CacheRW; // the Read-Write 2-way cache can modify this cache directly during write operations
protected:
volatile signed int m_nRefCount; // the reference count
FILE* m_pFile; // the opened file
// the size of the serialized data following this instance (not including the extra fields after the serialized tree data)
size_t m_nDataSize;
// the offset to the path/name of the zip file relative to (char*)(this+1) pointer in bytes
size_t m_nZipPathOffset;
// tells if encryption used for zip-file
EncryptionKey m_encryptionKey;
bool m_bEncryptHeaders;
public:
// initializes the instance structure
void Construct(FILE* fNew, size_t nDataSize, const EncryptionKey& key);
void Delete();
private:
bool ReadCompressedData(char* data, size_t size);
bool UnpakToDiskInternal(ZipDir::DirHeader* dirHeader, const string& destFolder);
// the constructor/destructor cannot be called at all - everything will go through the factory class
Cache() { m_nRefCount = 0; }
~Cache(){}
};
TYPEDEF_AUTOPTR(Cache);
typedef Cache_AutoPtr CachePtr;
}
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRCACHE_H
@@ -0,0 +1,802 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <zlib.h>
#include "smartptr.h"
#include "ZipFileFormat.h"
#include "zipdirstructures.h"
#include "ZipDirTree.h"
#include "ZipDirCache.h"
#include "ZipDirCacheRW.h"
#include "ZipDirCacheFactory.h"
#include "ZipDirList.h"
#include <ZipDir/ZipDir_Traits_Platform.h>
static uint32 g_defaultEncryptionKey[4] = { 0xc968fb67, 0x8f9b4267, 0x85399e84, 0xf9b99dc4 };
ZipDir::CacheFactory::CacheFactory (InitMethodEnum nInitMethod, unsigned nFlags)
{
m_nCDREndPos = 0;
m_f = NULL;
m_bBuildFileEntryMap = false; // we only need it for validation/debugging
m_bBuildFileEntryTree = true; // we need it to actually build the optimized structure of directories
m_bEncryptedHeaders = false;
m_nInitMethod = nInitMethod;
m_nFlags = nFlags;
}
ZipDir::CacheFactory::~CacheFactory()
{
Clear();
}
ZipDir::CachePtr ZipDir::CacheFactory::New (const char* szFile, const uint32 key[4])
{
m_encryptionKey = EncryptionKey(g_defaultEncryptionKey);
if (key)
{
m_encryptionKey = EncryptionKey(key);
}
Clear();
m_f = nullptr;
azfopen(&m_f, szFile, "rb");
if (m_f)
{
return MakeCache (szFile);
}
Clear();
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Cannot open file in binary mode for reading, probably missing file");
return 0;
/*
if (!m_f)
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED,"Cannot open file in binary mode for reading, probably missing file");
try
{
return MakeCache (szFile);
}
catch(Error)
{
Clear();
throw;
}
*/
}
ZipDir::CacheRWPtr ZipDir::CacheFactory::NewRW(const char* szFileName, size_t fileAlignment, bool encrypted, const uint32* key)
{
m_encryptionKey = EncryptionKey(g_defaultEncryptionKey);
if (key)
{
m_encryptionKey = EncryptionKey(key);
}
CacheRWPtr pCache = new CacheRW(encrypted, m_encryptionKey);
// opens the given zip file and connects to it. Creates a new file if no such file exists
// if successful, returns true.
if (!(m_nFlags & FLAGS_DONT_MEMORIZE_ZIP_PATH))
{
pCache->m_strFilePath = szFileName;
}
if (m_nFlags & FLAGS_DONT_COMPACT)
{
pCache->m_nFlags |= CacheRW::FLAGS_DONT_COMPACT;
}
// first, try to open the file for reading or reading/writing
if (m_nFlags & FLAGS_READ_ONLY)
{
m_f = nullptr;
azfopen(&m_f, szFileName, "rb");
pCache->m_nFlags |= CacheRW::FLAGS_CDR_DIRTY | CacheRW::FLAGS_READ_ONLY;
if (!m_f)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not open file in binary mode for reading");
return 0;
}
}
else
{
m_f = NULL;
if (!(m_nFlags & FLAGS_CREATE_NEW))
{
m_f = nullptr;
azfopen(&m_f, szFileName, "r+b");
}
bool bOpenForWriting = true;
if (m_f)
{
// get file size
fseek(m_f, 0, SEEK_END);
size_t nFileSize = AZ_TRAIT_CRYCOMMONTOOLS_FTELL(m_f);
fseek(m_f, 0, SEEK_SET);
if (nFileSize)
{
if (!ReadCacheRW(*pCache))
{
fclose(m_f);
m_f = NULL;
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not read archive");
return 0;
}
bOpenForWriting = false;
}
else
{
// if file has 0 bytes (e.g. crash during saving) we don't want to open it
assert(0); // you can ignore, the system shold handle this gracefully
}
}
if (bOpenForWriting)
{
m_f = nullptr;
azfopen(&m_f, szFileName, "w+b");
if (m_f)
{
// there's no such file, but we'll create one. We'll need to write out the CDR here
pCache->m_lCDROffset = 0;
pCache->m_nFlags |= CacheRW::FLAGS_CDR_DIRTY;
}
pCache->m_fileAlignment = fileAlignment;
}
if (!m_f)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not open file in binary mode for appending (read/write)");
return 0;
}
}
// give the cache the file handle:
pCache->m_pFile = m_f;
// the factory doesn't own it after that
m_f = NULL;
return pCache;
}
bool ZipDir::CacheFactory::ReadCacheRW (CacheRW& rwCache)
{
m_bBuildFileEntryTree = true;
if (!Prepare())
{
return false;
}
// since it's open for R/W, we need to know exactly how much space
// we have for each file to use the gaps efficiently
FileEntryList Adjuster (&m_treeFileEntries, m_CDREnd.lCDROffset);
Adjuster.RefreshEOFOffsets();
m_treeFileEntries.Swap(rwCache.m_treeDir);
m_CDR_buffer.swap(rwCache.m_CDR_buffer); // CDR Buffer contain actually the string pool for the tree directory.
m_unifiedNameBuffer.swap(rwCache.m_unifiedNameBuffer); // string pool for unified names
// very important: we need this offset to be able to add to the zip file
rwCache.m_lCDROffset = m_CDREnd.lCDROffset;
if (m_bEncryptedHeaders != rwCache.m_bEncryptedHeaders)
{
// force to relink and update all headers on close
rwCache.m_nFlags |= ZipDir::CacheRW::FLAGS_UNCOMPACTED;
rwCache.m_bHeadersEncryptedOnClose = rwCache.m_bEncryptedHeaders;
rwCache.m_bEncryptedHeaders = m_bEncryptedHeaders;
}
return true;
}
// reads everything and prepares the maps
bool ZipDir::CacheFactory::Prepare ()
{
if (!FindCDREnd())
{
return false;
}
m_bEncryptedHeaders = (m_CDREnd.nDisk & (1 << 15)) != 0;
m_CDREnd.nDisk = m_CDREnd.nDisk & 0x7fff;
// we don't support multivolume archives
if (m_CDREnd.nDisk != 0
|| m_CDREnd.nCDRStartDisk != 0
|| m_CDREnd.numEntriesOnDisk != m_CDREnd.numEntriesTotal)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_UNSUPPORTED, "Multivolume archive detected. Current version of ZipDir does not support multivolume archives");
return false;
}
// if the central directory offset or size are out of range,
// the CDREnd record is probably corrupt
if (m_CDREnd.lCDROffset > m_nCDREndPos
|| m_CDREnd.lCDRSize > m_nCDREndPos
|| m_CDREnd.lCDROffset + m_CDREnd.lCDRSize > m_nCDREndPos)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_DATA_IS_CORRUPT, "The central directory offset or size are out of range, the pak is probably corrupt, try to repair or delete the file");
return false;
}
if (!BuildFileEntryMap())
{
return false;
}
// the number of parsed files MUST be the declared number of entries
// in the central directory
if (m_bBuildFileEntryMap && m_CDREnd.numEntriesTotal != m_mapFileEntries.size())
{
THROW_ZIPDIR_ERROR (ZD_ERROR_CDR_IS_CORRUPT, "The number of parsed files does not match the declared number of entries in the central directory, the pak is probably corrupt, try to repair or delete the file");
}
const size_t numFilesFound = m_treeFileEntries.NumFilesTotal();
if (m_bBuildFileEntryTree && m_CDREnd.numEntriesTotal != numFilesFound)
{
const size_t numDirsFound = m_treeFileEntries.NumDirsTotal();
// Other zip tools create entries for directories.
// These entires don't have representation in our tree.
// FIXME: Proper calculation of entry count should be implemented.
if (m_CDREnd.numEntriesTotal != numFilesFound + numDirsFound)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_CDR_IS_CORRUPT, "The number of parsed files does not match the declared number of entries in the central directory. The pak does not appear to be corrupt, but perhaps there are some duplicated or missing file entries, try to repair the file");
}
}
return true;
}
ZipDir::CachePtr ZipDir::CacheFactory::MakeCache (const char* szFile)
{
if (!Prepare())
{
return CachePtr();
}
// initializes this object from the given tree, which is a convenient representation of the file tree
size_t nSizeRequired = m_treeFileEntries.GetSizeSerialized();
size_t nSizeZipPath = 1; // we need to remember the terminating 0
if (!(m_nFlags & FLAGS_DONT_MEMORIZE_ZIP_PATH))
{
nSizeZipPath += strlen(szFile);
}
// allocate and initialize the memory that'll be the root now
size_t nCacheInstanceSize = sizeof(Cache) + nSizeRequired + nSizeZipPath;
Cache* pCacheInstance = (Cache*)malloc(nCacheInstanceSize); // Do not use pools for this allocation
pCacheInstance->Construct(m_f, nSizeRequired, m_encryptionKey);
CachePtr cache = pCacheInstance;
m_f = NULL; // we don't own the file anymore - it's in possession of the cache instance
// try to serialize into the memory
size_t nSizeSerialized = m_treeFileEntries.Serialize (cache->GetRoot());
assert (nSizeSerialized == nSizeRequired);
char* pZipPath = ((char*)(pCacheInstance + 1)) + nSizeRequired;
if (!(m_nFlags & FLAGS_DONT_MEMORIZE_ZIP_PATH))
{
memcpy (pZipPath, szFile, nSizeZipPath);
}
else
{
pZipPath[0] = '\0';
}
Clear();
return cache;
}
void ZipDir::CacheFactory::Clear()
{
if (m_f)
{
fclose (m_f);
}
m_nCDREndPos = 0;
memset (&m_CDREnd, 0, sizeof(m_CDREnd));
m_mapFileEntries.clear();
m_treeFileEntries.Clear();
m_bEncryptedHeaders = false;
}
//////////////////////////////////////////////////////////////////////////
// searches for CDREnd record in the given file
bool ZipDir::CacheFactory::FindCDREnd()
{
// this buffer will be used to find the CDR End record
// the additional bytes are required to store the potential tail of the CDREnd structure
// when moving the window to the next position in the file
char pReservedBuffer[g_nCDRSearchWindowSize + sizeof(ZipFile::CDREnd) - 1];
Seek (0, SEEK_END);
unsigned long nFileSize = Tell();
if (nFileSize < sizeof(ZipFile::CDREnd))
{
THROW_ZIPDIR_ERROR (ZD_ERROR_NO_CDR, "The file is too small, it doesn't even contain the CDREnd structure. Please check and delete the file. Truncated files are not deleted automatically");
return false;
}
// this will point to the place where the buffer was loaded
unsigned int nOldBufPos = nFileSize;
// start scanning well before the end of the file to avoid reading beyond the end
unsigned int nScanPos = nFileSize - sizeof(ZipFile::CDREnd);
m_CDREnd.lSignature = 0; // invalid signature as the flag of not-found CDR End structure
while (true)
{
unsigned int nNewBufPos; // the new buf pos
char* pWindow = pReservedBuffer; // the window pointer into which data will be read (takes into account the possible tail-of-CDREnd)
if (nOldBufPos <= g_nCDRSearchWindowSize)
{
// the old buffer position doesn't let us read the full search window size
// therefore the new buffer pos will be 0 (instead of negative beyond the start of the file)
// and the window pointer will be closer tot he end of the buffer because the end of the buffer
// contains the data from the previous iteration (possibly)
nNewBufPos = 0;
pWindow = pReservedBuffer + g_nCDRSearchWindowSize - (nOldBufPos - nNewBufPos);
}
else
{
nNewBufPos = nOldBufPos - g_nCDRSearchWindowSize;
assert (nNewBufPos > 0);
}
// since dealing with 32bit unsigned, check that filesize is bigger than
// CDREnd plus comment before the following check occurs.
if (nFileSize > (sizeof(ZipFile::CDREnd) + 0xFFFF))
{
// if the new buffer pos is beyond 64k limit for the comment size
if (nNewBufPos < (unsigned int)(nFileSize - sizeof(ZipFile::CDREnd) - 0xFFFF))
{
nNewBufPos = nFileSize - sizeof(ZipFile::CDREnd) - 0xFFFF;
}
}
// if there's nothing to search
if (nNewBufPos >= nOldBufPos)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_NO_CDR, "Cannot find Central Directory Record in pak. This is either not a pak file, or a pak file without Central Directory. It does not mean that the data is permanently lost, but it may be severely damaged. Please repair the file with external tools, there may be enough information left to recover the file completely"); // we didn't find anything
return false;
}
// seek to the start of the new window and read it
Seek (nNewBufPos);
Read (pWindow, nOldBufPos - nNewBufPos);
while (nScanPos >= nNewBufPos)
{
ZipFile::CDREnd* pEnd = (ZipFile::CDREnd*)(pWindow + nScanPos - nNewBufPos);
if (pEnd->lSignature == pEnd->SIGNATURE)
{
if (pEnd->nCommentLength == nFileSize - nScanPos - sizeof(ZipFile::CDREnd))
{
// the comment length is exactly what we expected
m_CDREnd = *pEnd;
m_nCDREndPos = nScanPos;
break;
}
else
{
THROW_ZIPDIR_ERROR (ZD_ERROR_DATA_IS_CORRUPT, "Central Directory Record is followed by a comment of inconsistent length. This might be a minor misconsistency, please try to repair the file. However, it is dangerous to open the file because I will have to guess some structure offsets, which can lead to permanent unrecoverable damage of the archive content");
return false;
}
}
if (nScanPos == 0)
{
break;
}
--nScanPos;
}
if (m_CDREnd.lSignature == m_CDREnd.SIGNATURE)
{
return true; // we've found it
}
nOldBufPos = nNewBufPos;
memmove (pReservedBuffer + g_nCDRSearchWindowSize, pWindow, sizeof(ZipFile::CDREnd) - 1);
}
THROW_ZIPDIR_ERROR (ZD_ERROR_UNEXPECTED, "The program flow may not have possibly lead here. This error is unexplainable"); // we shouldn't be here
return false;
}
//////////////////////////////////////////////////////////////////////////
// uses the found CDREnd to scan the CDR and probably the Zip file itself
// builds up the m_mapFileEntries
bool ZipDir::CacheFactory::BuildFileEntryMap()
{
Seek (m_CDREnd.lCDROffset);
if (m_CDREnd.lCDRSize == 0)
{
return true;
}
DynArray<char>& pBuffer = m_CDR_buffer; // Use persistent buffer.
pBuffer.resize(m_CDREnd.lCDRSize + 1); // Allocate one more because we use this memory as a strings pool.
if (pBuffer.empty()) // couldn't allocate enough memory for temporary copy of CDR
{
THROW_ZIPDIR_ERROR (ZD_ERROR_NO_MEMORY, "Not enough memory to cache Central Directory record for fast initialization. This error may not happen on non-console systems");
return false;
}
// Calculate buffer size for unified filenames
const size_t headersSize = sizeof(ZipFile::CDRFileHeader) * m_CDREnd.numEntriesTotal;
const size_t terminatingZeros = m_CDREnd.numEntriesTotal;
if (headersSize > m_CDREnd.lCDRSize + terminatingZeros)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_CORRUPTED_DATA, "Number of entries in Central Directory seems to be wrong");
return false;
}
const size_t nameBufferSize = m_CDREnd.lCDRSize + terminatingZeros - headersSize; // numEntriesTotal for terminating zeroes
// Allocate buffer for unified filenames
m_unifiedNameBuffer.resize(nameBufferSize);
if (m_unifiedNameBuffer.empty() && nameBufferSize != 0)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_NO_MEMORY, "Not enough memory to allocate unified names buffer");
return false;
}
char* pUnifiedName = m_unifiedNameBuffer.empty() ? 0 : &m_unifiedNameBuffer[0];
const char* const pUnifiedNameEnd = pUnifiedName + m_unifiedNameBuffer.size();
ReadHeaderData(&pBuffer[0], m_CDREnd.lCDRSize);
// now we've read the complete CDR - parse it.
ZipFile::CDRFileHeader* pFile = (ZipFile::CDRFileHeader*)(&pBuffer[0]);
const char* const pEndOfData = &pBuffer[0] + m_CDREnd.lCDRSize;
const char* const pEndOfBuffer = &pBuffer[0] + pBuffer.size();
char* pFileName;
// check signature of first entry
if ((const char*)(pFile + 1) <= pEndOfData)
{
if (pFile->lSignature != pFile->SIGNATURE)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_CDR_IS_CORRUPT, m_bEncryptedHeaders
? "Signature of CDR entry is corrupt. Wrong decryption key was used or archive is corrupt."
: "Signature of CDR entry is corrupt. Archive is corrupt.");
return false;
}
}
while ((pFileName = (char*)(pFile + 1)) <= pEndOfData)
{
// Hacky way to use CDR memory block as a string pool.
pFile->lSignature = 0; // Force signature to always be 0 (First byte of signature maybe a zero termination of the previous file filename).
if (pFile->nVersionNeeded > 20)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_UNSUPPORTED, "Reading file header with unsupported version (nVersionNeeded > 20).");
return false;
}
//if (pFile->lSignature != pFile->SIGNATURE) // Timur, Dont compare signatures as signatue in memory can be overwritten by the code below
//break;
// the end of this file record
const char* pEndOfRecord = (pFileName + pFile->nFileNameLength + pFile->nExtraFieldLength + pFile->nFileCommentLength);
// if the record overlaps with the End Of CDR structure, something is wrong
if (pEndOfRecord > pEndOfData)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_CDR_IS_CORRUPT, "Central Directory record is either corrupt, or truncated, or missing. Cannot read the archive directory");
return false;
}
//////////////////////////////////////////////////////////////////////////
// Analyze advanced section.
//////////////////////////////////////////////////////////////////////////
SExtraZipFileData extra;
const char* pExtraField = (pFileName + pFile->nFileNameLength);
const char* pExtraEnd = pExtraField + pFile->nExtraFieldLength;
while (pExtraField < pExtraEnd)
{
const char* pAttrData = pExtraField + sizeof(ZipFile::ExtraFieldHeader);
ZipFile::ExtraFieldHeader& hdr = *(ZipFile::ExtraFieldHeader*)pExtraField;
switch (hdr.headerID)
{
case ZipFile::EXTRA_NTFS:
{
ZipFile::ExtraNTFSHeader& ntfsHdr = *(ZipFile::ExtraNTFSHeader*)pAttrData;
extra.nLastModifyTime = *(uint64*)(pAttrData + sizeof(ZipFile::ExtraNTFSHeader));
uint64 accTime = *(uint64*)(pAttrData + sizeof(ZipFile::ExtraNTFSHeader) + 8);
uint64 crtTime = *(uint64*)(pAttrData + sizeof(ZipFile::ExtraNTFSHeader) + 16);
}
break;
}
pExtraField += sizeof(ZipFile::ExtraFieldHeader) + hdr.dataSize;
}
bool bDirectory = false;
if (pFile->nFileNameLength > 0 && (pFileName[pFile->nFileNameLength - 1] == '/' || pFileName[pFile->nFileNameLength - 1] == '\\'))
{
bDirectory = true;
}
if (!bDirectory)
{
const size_t fileNameLen = pFile->nFileNameLength;
pFileName[fileNameLen] = 0; // Not standard!, may overwrite signature of the next memory record data in zip.
// generate unified name
if (pFileName + fileNameLen + 1 > pEndOfBuffer ||
pUnifiedName + fileNameLen + 1 > pUnifiedNameEnd)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_CORRUPTED_DATA, "Filename length exceeds estimated size. Try to repair the archive.");
return false;
}
for (int i = 0; i < fileNameLen + 1; i++)
{
pUnifiedName[i] = ::tolower(pFileName[i]);
}
// put this entry into the map
AddFileEntry (pFileName, pUnifiedName, pFile, extra);
pUnifiedName += fileNameLen + 1;
}
// move to the next file
pFile = (ZipFile::CDRFileHeader*)pEndOfRecord;
}
// finished reading CDR
return true;
}
//////////////////////////////////////////////////////////////////////////
// give the CDR File Header entry, reads the local file header to validate
// and determine where the actual file lies
void ZipDir::CacheFactory::AddFileEntry (char* strFilePath, char* strUnifiedPath, const ZipFile::CDRFileHeader* pFileHeader, const SExtraZipFileData& extra)
{
if (pFileHeader->lLocalHeaderOffset > m_CDREnd.lCDROffset)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_CDR_IS_CORRUPT, "Central Directory contains file descriptors pointing outside the archive file boundaries. The archive file is either truncated or damaged. Please try to repair the file"); // the file offset is beyond the CDR: impossible
return;
}
if (pFileHeader->nMethod == ZipFile::METHOD_STORE && pFileHeader->desc.lSizeUncompressed != pFileHeader->desc.lSizeCompressed)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_VALIDATION_FAILED, "File with STORE compression method declares its compressed size not matching its uncompressed size. File descriptor is inconsistent, archive content may be damaged, please try to repair the archive");
return;
}
FileEntry fileEntry (*pFileHeader, extra);
if ((m_bEncryptedHeaders || m_nInitMethod >= ZD_INIT_FULL) && pFileHeader->desc.lSizeCompressed)
{
InitDataOffset(fileEntry, pFileHeader);
}
if (m_bBuildFileEntryMap)
{
m_mapFileEntries.insert (FileEntryMap::value_type(strFilePath, fileEntry));
}
if (m_bBuildFileEntryTree)
{
m_treeFileEntries.Add(strFilePath, strUnifiedPath, fileEntry);
}
}
//////////////////////////////////////////////////////////////////////////
// initializes the actual data offset in the file in the fileEntry structure
// searches to the local file header, reads it and calculates the actual offset in the file
void ZipDir::CacheFactory::InitDataOffset (FileEntry& fileEntry, const ZipFile::CDRFileHeader* pFileHeader)
{
// make sure it's the same file and the fileEntry structure is properly initialized
assert (fileEntry.nFileHeaderOffset == pFileHeader->lLocalHeaderOffset);
/*
// without validation, it would be like this:
ErrorEnum nError = Refresh(&fileEntry);
if (nError != ZD_ERROR_SUCCESS)
THROW_ZIPDIR_ERROR(nError,"Cannot refresh file entry. Probably corrupted file header inside zip file");
*/
if (m_bEncryptedHeaders)
{
// ignore local header
fileEntry.nFileDataOffset = pFileHeader->lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + pFileHeader->nFileNameLength + pFileHeader->nExtraFieldLength;
}
else
{
Seek(pFileHeader->lLocalHeaderOffset);
// read the local file header and the name (for validation) into the buffer
DynArray<char>pBuffer;
unsigned nBufferLength = sizeof(ZipFile::LocalFileHeader) + pFileHeader->nFileNameLength;
pBuffer.resize(nBufferLength);
Read (&pBuffer[0], nBufferLength);
// validate the local file header (compare with the CDR file header - they should contain basically the same information)
const ZipFile::LocalFileHeader* pLocalFileHeader = (const ZipFile::LocalFileHeader*)&pBuffer[0];
if (pFileHeader->desc != pLocalFileHeader->desc
|| pFileHeader->nMethod != pLocalFileHeader->nMethod
|| pFileHeader->nFileNameLength != pLocalFileHeader->nFileNameLength
// for a tough validation, we can compare the timestamps of the local and central directory entries
// but we won't do that for backward compatibility with ZipDir
//|| pFileHeader->nLastModDate != pLocalFileHeader->nLastModDate
//|| pFileHeader->nLastModTime != pLocalFileHeader->nLastModTime
)
{
THROW_ZIPDIR_ERROR (ZD_ERROR_VALIDATION_FAILED, "The local file header descriptor doesn't match the basic parameters declared in the global file header in the file. The archive content is misconsistent and may be damaged. Please try to repair the archive");
return;
}
// now compare the local file name with the one recorded in CDR: they must match.
if (azmemicmp((const char*)&pBuffer[sizeof(ZipFile::LocalFileHeader)], (const char*)pFileHeader + 1, pFileHeader->nFileNameLength))
{
// either file name, or the extra field do not match
THROW_ZIPDIR_ERROR(ZD_ERROR_VALIDATION_FAILED, "The local file header contains file name which does not match the file name of the global file header. The archive content is misconsistent with its directory. Please repair the archive");
return;
}
fileEntry.nFileDataOffset = pFileHeader->lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + pLocalFileHeader->nFileNameLength + pLocalFileHeader->nExtraFieldLength;
}
if (fileEntry.nFileDataOffset >= m_nCDREndPos)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_VALIDATION_FAILED, "The global file header declares the file which crosses the boundaries of the archive. The archive is either corrupted or truncated, please try to repair it");
return;
}
if (m_nInitMethod >= ZD_INIT_VALIDATE)
{
Validate (fileEntry);
}
}
//////////////////////////////////////////////////////////////////////////
// reads the file pointed by the given header and entry (they must be coherent)
// and decompresses it; then calculates and validates its CRC32
void ZipDir::CacheFactory::Validate(const FileEntry& fileEntry)
{
DynArray<char> pBuffer;
// validate the file contents
// allocate memory for both the compressed data and uncompressed data
pBuffer.resize(fileEntry.desc.lSizeCompressed + fileEntry.desc.lSizeUncompressed);
char* pUncompressed = &pBuffer[fileEntry.desc.lSizeCompressed];
char* pCompressed = &pBuffer[0];
assert (fileEntry.nFileDataOffset != FileEntry::INVALID_DATA_OFFSET);
Seek(fileEntry.nFileDataOffset);
Read(pCompressed, fileEntry.desc.lSizeCompressed);
if (fileEntry.nMethod == ZipFile::METHOD_DEFLATE_AND_ENCRYPT)
{
ZipDir::Decrypt(pCompressed, fileEntry.desc.lSizeCompressed, m_encryptionKey);
}
unsigned long nDestSize = fileEntry.desc.lSizeUncompressed;
int nError = Z_OK;
if (fileEntry.nMethod)
{
nError = ZipRawUncompress (pUncompressed, &nDestSize, pCompressed, fileEntry.desc.lSizeCompressed);
}
else
{
assert (fileEntry.desc.lSizeCompressed == fileEntry.desc.lSizeUncompressed);
memcpy (pUncompressed, pCompressed, fileEntry.desc.lSizeUncompressed);
}
switch (nError)
{
case Z_OK:
break;
case Z_MEM_ERROR:
THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_NO_MEMORY, "ZLib reported out-of-memory error");
return;
case Z_BUF_ERROR:
THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_CORRUPTED_DATA, "ZLib reported compressed stream buffer error");
return;
case Z_DATA_ERROR:
THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_CORRUPTED_DATA, "ZLib reported compressed stream data error");
return;
default:
THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_FAILED, "ZLib reported an unexpected unknown error");
return;
}
if (nDestSize != fileEntry.desc.lSizeUncompressed)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_CORRUPTED_DATA, "Uncompressed stream doesn't match the size of uncompressed file stored in the archive file headers");
return;
}
uLong uCRC32 = crc32(0L, Z_NULL, 0);
uCRC32 = crc32(uCRC32, (Bytef*)pUncompressed, nDestSize);
if (uCRC32 != fileEntry.desc.lCRC32)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_CRC32_CHECK, "Uncompressed stream CRC32 check failed");
return;
}
}
//////////////////////////////////////////////////////////////////////////
// extracts the file path from the file header with subsequent information
// may, or may not, put all letters to lower-case (depending on whether the system is to be case-sensitive or not)
// it's the responsibility of the caller to ensure that the file name is in readable valid memory
char* ZipDir::CacheFactory::GetFilePath (const char* pFileName, ZipFile::ushort nFileNameLength)
{
static char strResult[_MAX_PATH];
assert(nFileNameLength < _MAX_PATH);
memcpy(strResult, pFileName, nFileNameLength);
strResult[nFileNameLength] = 0;
for (int i = 0; i < nFileNameLength; i++)
{
strResult[i] = ::tolower(strResult[i]);
}
return strResult;
}
// seeks in the file relative to the starting position
void ZipDir::CacheFactory::Seek (ZipFile::ulong nPos, int nOrigin) // throw
{
if (AZ_TRAIT_CRYCOMMONTOOLS_FSEEK(m_f, nPos, nOrigin))
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Cannot fseek() to the new position in the file. This is unexpected error and should not happen under any circumstances. Perhaps some network or disk failure error has caused this");
return;
}
}
unsigned long ZipDir::CacheFactory::Tell () // throw
{
AZ::s64 nPos = AZ_TRAIT_CRYCOMMONTOOLS_FTELL(m_f);
if (nPos == -1)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Cannot ftell() position in the archive. This is unexpected error and should not happen under any circumstances. Perhaps some network or disk failure error has caused this");
return 0;
}
return (unsigned long)nPos;
}
void ZipDir::CacheFactory::Read (void* pDest, unsigned nSize) // throw
{
if (fread (pDest, nSize, 1, m_f) != 1)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Cannot fread() a portion of data from archive");
}
}
void ZipDir::CacheFactory::ReadHeaderData (void* pDest, unsigned nSize) // throw
{
Read(pDest, nSize);
if (m_bEncryptedHeaders)
{
ZipDir::Decrypt((char*)pDest, nSize, m_encryptionKey);
}
}
@@ -0,0 +1,143 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// This is the class that can read the directory from Zip file,
// and store it into the directory cache
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRCACHEFACTORY_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRCACHEFACTORY_H
#pragma once
namespace ZipDir
{
class CacheRW;
TYPEDEF_AUTOPTR(CacheRW);
typedef CacheRW_AutoPtr CacheRWPtr;
// an instance of this class is temporarily created on stack to initialize the CZipFile instance
class CacheFactory
{
public:
enum
{
// open RW cache in read-only mode
FLAGS_READ_ONLY = 1,
// do not compact RW-cached zip upon destruction
FLAGS_DONT_COMPACT = 1 << 1,
// if this is set, then the zip paths won't be memorized in the cache objects
FLAGS_DONT_MEMORIZE_ZIP_PATH = 1 << 2,
// if this is set, the archive will be created anew (the existing file will be overwritten)
FLAGS_CREATE_NEW = 1 << 3
};
// initializes the internal structures
// nFlags can have FLAGS_READ_ONLY flag, in this case the object will be opened only for reading
CacheFactory (InitMethodEnum nInitMethod, unsigned nFlags = 0);
~CacheFactory();
// the new function creates a new cache
CachePtr New(const char* szFileName, const uint32 decryptionKey[4]);// throw (ErrorEnum);
CacheRWPtr NewRW(const char* szFileName, size_t fileAlignment, bool encrypted, const uint32 encryptionKey[4]);
protected:
// reads the zip file into the file entry tree.
bool ReadCacheRW (CacheRW& rwCache);
// creates from the m_f file
// reserves the given number of bytes for future expansion of the object
// upon return, pReserve contains the actual number of bytes that were allocated (more might have been allocated)
CachePtr MakeCache (const char* szFile);
// this sets the window size of the blocks of data read from the end of the file to find the Central Directory Record
// since normally there are no
enum
{
g_nCDRSearchWindowSize = 0x100
};
void Clear();
// reads everything and prepares the maps
bool Prepare();
// searches for CDREnd record in the given file
bool FindCDREnd();// throw(ErrorEnum);
// uses the found CDREnd to scan the CDR and probably the Zip file itself
// builds up the m_mapFileEntries
bool BuildFileEntryMap();// throw (ErrorEnum);
// give the CDR File Header entry, reads the local file header to validate and determine where
// the actual file lies
// This function can actually modify strFilePath and strUnifiedPath variables, make sure you use copies of real paths.
void AddFileEntry (char* strFilePath, char* strUnifiedPath, const ZipFile::CDRFileHeader* pFileHeader, const SExtraZipFileData& extra);// throw (ErrorEnum);
// extracts the file path from the file header with subsequent information
// may, or may not, put all letters to lower-case (depending on whether the system is to be case-sensitive or not)
// it's the responsibility of the caller to ensure that the file name is in readable valid memory
char* GetFilePath (const ZipFile::CDRFileHeader* pFileHeader)
{
return GetFilePath((const char*)(pFileHeader + 1), pFileHeader->nFileNameLength);
}
// extracts the file path from the file header with subsequent information
// may, or may not, put all letters to lower-case (depending on whether the system is to be case-sensitive or not)
// it's the responsibility of the caller to ensure that the file name is in readable valid memory
char* GetFilePath (const ZipFile::LocalFileHeader* pFileHeader)
{
return GetFilePath((const char*)(pFileHeader + 1), pFileHeader->nFileNameLength);
}
// extracts the file path from the file header with subsequent information
// may, or may not, put all letters to lower-case (depending on whether the system is to be case-sensitive or not)
// it's the responsibility of the caller to ensure that the file name is in readable valid memory
char* GetFilePath (const char* pFileName, ZipFile::ushort nFileNameLength);
// validates (if the init method has the corresponding value) the given file/header
void Validate(const FileEntry& fileEntry);
// initializes the actual data offset in the file in the fileEntry structure
// searches to the local file header, reads it and calculates the actual offset in the file
void InitDataOffset (FileEntry& fileEntry, const ZipFile::CDRFileHeader* pFileHeader);
// seeks in the file relative to the starting position
void Seek (ZipFile::ulong nPos, int nOrigin = SEEK_SET); // throw
unsigned long Tell (); // throw
void Read (void* pDest, unsigned nSize); // throw
void ReadHeaderData (void* pDest, unsigned nSize);// throw
protected:
FILE* m_f;
InitMethodEnum m_nInitMethod;
unsigned m_nFlags;
ZipFile::CDREnd m_CDREnd;
unsigned m_nCDREndPos; // position of the CDR End in the file
// Map: Relative file path => file entry info
typedef std::map<string, ZipDir::FileEntry> FileEntryMap;
FileEntryMap m_mapFileEntries;
FileEntryTree m_treeFileEntries;
DynArray<char> m_CDR_buffer;
DynArray<char> m_unifiedNameBuffer;
EncryptionKey m_encryptionKey;
bool m_bEncryptedHeaders;
bool m_bBuildFileEntryMap;
bool m_bBuildFileEntryTree;
};
}
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRCACHEFACTORY_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,283 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
//////////////////////////////////////////////////////////////////////////
// Declaration of the class that will keep the ZipDir Cache object
// and will provide all its services to access Zip file, plus it will
// provide services to write to the zip file efficiently
// Time to time, the contained Cache object will be recreated during
// an archive add operation
#pragma once
#include "SimpleStringPool.h"
#include "StringUtils.h"
struct PackFileJob;
namespace ZipDir
{
struct FileDataRecord;
TYPEDEF_AUTOPTR(FileDataRecord);
typedef FileDataRecord_AutoPtr FileDataRecordPtr;
static constexpr int TARGET_MIN_TEST_COMPRESS_BYTES = 128 * 1024;
struct IReporter
{
virtual void ReportAdded(const char* filename) = 0;
virtual void ReportMissing(const char* filename) = 0;
virtual void ReportUpToDate(const char* filename) = 0;
virtual void ReportSkipped(const char* filename) = 0;
virtual void ReportFailed(const char* filename, const char* error) = 0;
virtual void ReportSpeed(double bytesPerSecond) = 0;
};
struct ISplitter
{
// Arguments:
// total - the current size of the pak
// add - the size of the file to add
// sub - the size of the old version of the file which will be removed from the pak
// Return:
// true if adding the current file to the current pak is still permitted.
virtual bool CheckWriteLimit(size_t total, size_t add, size_t sub) const = 0;
// Arguments:
// total - the current size of the pak
// add - the size of the file to add
// sub - the size of the old version of the file which will be removed from the pak
// offset - the position of the first file which has not been added to the pak
// in the array passed to "UpdateMultipleFiles()"
virtual void SetLastFile(size_t total, size_t add, size_t sub, int offset) = 0;
};
struct IEncryptPredicate
{
virtual ~IEncryptPredicate() = default;
virtual bool Match(const char* filename) = 0;
};
class CacheRW
{
public:
enum EncryptionChange
{
ENCRYPT,
DECRYPT
};
// the size of the buffer that's using during re-linking the zip file
enum
{
g_nSizeRelinkBuffer = 128 * 1024 * 1024, // 128 Mbytes
g_nMaxItemsRelinkBuffer = 1024 // max number of files to read before (without) writing
};
void AddRef();
void Release();
CacheRW(bool encryptHeaders, const EncryptionKey& encryptionKey);
~CacheRW();
bool IsValid () const
{
return m_pFile != NULL;
}
static char* UnifyPath(char* const str, const char* pPath);
static char* ToUnixPath(char* const str, const char* pPath);
char* AllocPath(const char* pPath);
// opens the given zip file and connects to it. Creates a new file if no such file exists
// if successful, returns true.
//ErrorEnum Open (CMTSafeHeap* pHeap, InitMethodEnum nInitMethod, unsigned nFlags, const char* szFile);
// Adds a new file to the zip or update an existing one
// adds a directory (creates several nested directories if needed)
ErrorEnum UpdateFile(const char* szRelativePath, void* pUncompressed, unsigned nSize, unsigned nCompressionMethod, int nCompressionLevel, int64 modTime);
// Sets if Archive should be encrypted or decrypted on close.
bool EncryptArchive(EncryptionChange change, IEncryptPredicate* encryptContentPredicate, int* numChanged, int* numSkipped);
// Adds or updates a bunch of files. Creates directories if needed. Multithreaded when numExtraThreads > 0
bool UpdateMultipleFiles(const char** realFilenames, const char** filenamesInZip, size_t fileCount,
int compressionLevel, bool encryptContent, size_t zipMaxSize, int sourceMinSize, int sourceMaxSize,
unsigned numExtraThreads, ZipDir::IReporter* reporter, ZipDir::ISplitter* splitter = nullptr, bool useFastestDecompressionCodec = false);
// Adds a new file to the zip or update an existing one if it is not compressed - just stored - start a big file
ErrorEnum StartContinuousFileUpdate(const char* szRelativePath, unsigned nSize);
// Adds a new file to the zip or update an existing's segment if it is not compressed - just stored
// adds a directory (creates several nested directories if needed)
// Arguments:
// nOverwriteSeekPos - 0xffffffff means the seek pos should not be overwritten
ErrorEnum UpdateFileContinuousSegment (const char* szRelativePath, unsigned nSize, void* pUncompressed, unsigned nSegmentSize, unsigned nOverwriteSeekPos);
ErrorEnum UpdateFileCRC(const char* szRelativePath, unsigned dwCRC32);
// deletes the file from the archive
ErrorEnum RemoveFile(const char* szRelativePath);
// deletes the directory, with all its descendants (files and subdirs)
ErrorEnum RemoveDir(const char* szRelativePath);
// deletes all files and directories in this archive
ErrorEnum RemoveAll();
// closes the current zip file
void Close();
FileEntry* FindFile(const char* szPath, bool bFullInfo = false);
ErrorEnum ReadFile(FileEntry* pFileEntry, void* pCompressed, void* pUncompressed);
void* AllocAndReadFile (FileEntry* pFileEntry);
void Free (void* p)
{
free(p);
}
// refreshes information about the given file entry into this file entry
ErrorEnum Refresh (FileEntry* pFileEntry);
// returns the size of memory occupied by the instance of this cache
size_t GetSize() const;
// returns the compressed size of all the entries
size_t GetCompressedSize() const;
// returns the total size of memory occupied by the instance of this cache and all the compressed files
size_t GetTotalFileSize() const;
// returns the total size of space occupied on disk by the instance of this cache and all the compressed files
size_t GetTotalFileSizeOnDiskSoFar();
// QUICK check to determine whether the file entry belongs to this object
bool IsOwnerOf (const FileEntry* pFileEntry) const
{
return m_treeDir.IsOwnerOf(pFileEntry);
}
// returns the string - path to the zip file from which this object was constructed.
// this will be "" if the object was constructed with a factory that wasn't created with FLAGS_MEMORIZE_ZIP_PATH
const char* GetFilePath() const
{
return m_strFilePath.c_str();
}
FileEntryTree* GetRoot()
{
return &m_treeDir;
}
const FileEntryTree* GetRoot() const
{
return &m_treeDir;
}
// writes the CDR to the disk
bool WriteCDR() {return WriteCDR(m_pFile, m_bEncryptedHeaders); }
bool WriteCDR(FILE* fTarget, bool encryptHeaders);
bool RelinkZip();
protected:
bool RelinkZip(FILE* fTmp);
// writes out the file data in the queue into the given file. Empties the queue
bool WriteZipFiles(std::vector<FileDataRecordPtr>& queFiles, FILE* fTmp);
// generates random file name
string GetRandomName(int nAttempt);
bool ReadCompressedData(char* data, size_t size);
bool WriteCompressedData(const char* data, size_t size, bool encrypt, FILE* file);
bool WriteNullData(size_t size);
void StorePackedFile(PackFileJob* job);
protected:
friend class CacheFactory;
volatile signed int m_nRefCount; // the reference count
FileEntryTree m_treeDir;
FILE* m_pFile;
string m_strFilePath;
// offset to the start of CDR in the file,even if there's no CDR there currently
// when a new file is added, it can start from here, but this value will need to be updated then
ZipFile::ulong m_lCDROffset;
CSimpleStringPool m_tempStringPool;
enum
{
// if this is set, the file needs to be compacted before it can be used by
// all standard zip tools, because gaps between file datas can be present
FLAGS_UNCOMPACTED = 1 << 0,
// if this is set, the CDR needs to be written to the file
FLAGS_CDR_DIRTY = 1 << 1,
// if this is set, the file is opened in read-only mode. no write operations are to be performed
FLAGS_READ_ONLY = 1 << 2,
// when this is set, compact operation is not performed
FLAGS_DONT_COMPACT = 1 << 3
};
unsigned m_nFlags;
size_t m_fileAlignment;
// CDR buffer.
DynArray<char> m_CDR_buffer;
// unified names buffer
DynArray<char> m_unifiedNameBuffer;
EncryptionKey m_encryptionKey;
bool m_bEncryptedHeaders;
bool m_bHeadersEncryptedOnClose;
};
TYPEDEF_AUTOPTR(CacheRW);
typedef CacheRW_AutoPtr CacheRWPtr;
// creates and if needed automatically destroys the file entry
class FileEntryTransactionAdd
{
class CacheRW* m_pCache;
char m_szPath[_MAX_PATH];
FileEntry* m_pFileEntry;
bool m_bComitted;
public:
operator FileEntry* () {
return m_pFileEntry;
}
operator bool() const{
return m_pFileEntry != NULL;
}
FileEntry* operator -> () {return m_pFileEntry; }
FileEntryTransactionAdd(class CacheRW* pCache, char* szPath, char* szUnifiedPath)
: m_pCache(pCache)
, m_bComitted (false)
{
// we need to copy path, because original one will be destroyed by FileEntryTree::Add call
cry_strcpy(m_szPath, szUnifiedPath);
m_pFileEntry = m_pCache->GetRoot()->Add(szPath, szUnifiedPath);
}
~FileEntryTransactionAdd()
{
if (m_pFileEntry && !m_bComitted)
{
m_pCache->RemoveFile(m_szPath);
}
}
void Commit()
{
m_bComitted = true;
}
};
}
@@ -0,0 +1,246 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "smartptr.h"
#include "ZipFileFormat.h"
#include "zipdirstructures.h"
#include "ZipDirCache.h"
#include "ZipDirFind.h"
#include "StringHelpers.h"
bool ZipDir::FindFile::FindFirst (const char* szWildcard)
{
if (!PreFind (szWildcard))
{
return false;
}
// finally, this is the name of the file
m_nFileEntry = 0;
return SkipNonMatchingFiles();
}
bool ZipDir::FindDir::FindFirst (const char* szWildcard)
{
if (!PreFind (szWildcard))
{
return false;
}
// finally, this is the name of the file
m_nDirEntry = 0;
return SkipNonMatchingDirs();
}
// matches the file wilcard in the m_szWildcard to the given file/dir name
// this takes into account the fact that xxx. is the alias name for xxx
bool ZipDir::FindData::MatchWildcard(const char* szName)
{
if (StringHelpers::MatchesWildcards(szName, m_szWildcard))
{
return true;
}
// check if the file object name contains extension sign (.)
const char* p;
for (p = szName; *p && *p != '.'; ++p)
{
continue;
}
if (*p)
{
// there's an extension sign in the object, but it wasn't matched..
assert (*p == '.');
return false;
}
// no extension sign - add it
char szAlias[_MAX_PATH + 2];
size_t nLength = p - szName;
if (nLength > _MAX_PATH)
{
nLength = _MAX_PATH;
}
memcpy (szAlias, szName, nLength);
szAlias[nLength] = '.'; // add the alias
szAlias[nLength + 1] = '\0'; // terminate the string
return StringHelpers::MatchesWildcards(szAlias, m_szWildcard);
}
ZipDir::FileEntry* ZipDir::FindFile::FindExact (const char* szPath)
{
if (!PreFind (szPath))
{
return NULL;
}
FileEntry* pFileEntry = m_pDirHeader->FindFileEntry(m_szWildcard);
if (pFileEntry)
{
m_nFileEntry = (unsigned)(pFileEntry - m_pDirHeader->GetFileEntry(0));
}
else
{
m_pDirHeader = NULL; // we didn't find it, fail the search
}
return pFileEntry;
}
//////////////////////////////////////////////////////////////////////////
// after this call returns successfully (with true returned), the m_szWildcard
// contains the file name/wildcard and m_pDirHeader contains the directory where
// the file (s) are to be found
bool ZipDir::FindData::PreFind (const char* szWildcard)
{
if (!m_pRoot)
{
return false;
}
// start the search from the root
m_pDirHeader = m_pRoot;
// for each path dir name, copy it into the buffer and try to find the subdirectory
const char* pPath = szWildcard;
for (;; )
{
char* pName = m_szWildcard;
// at first we'll use the wildcard memory to save the directory names
for (; *pPath && *pPath != '/' && *pPath != '\\' && pName < m_szWildcard + sizeof(m_szWildcard) - 1; ++pPath, ++pName)
{
*pName = ::tolower(*pPath);
}
*pName = '\0';
if (*pPath)
{
if (*pPath != '/' && *pPath != '\\')
{
return false;//ZD_ERROR_NAME_TOO_LONG;
}
// this is the name of the directory
DirEntry* pDirEntry = m_pDirHeader->FindSubdirEntry(m_szWildcard);
if (!pDirEntry)
{
m_pDirHeader = NULL; // finish the search
return false;
}
m_pDirHeader = pDirEntry->GetDirectory();
++pPath;
assert(m_pDirHeader);
}
else
{
// finally, this is the name of the file (or directory)
return true;
}
}
}
// goes on to the next entry
bool ZipDir::FindFile::FindNext ()
{
if (m_pDirHeader && m_nFileEntry < m_pDirHeader->numFiles)
{
++m_nFileEntry;
return SkipNonMatchingFiles();
}
else
{
return false;
}
}
// goes on to the next entry
bool ZipDir::FindDir::FindNext ()
{
if (m_pDirHeader && m_nDirEntry < m_pDirHeader->numDirs)
{
++m_nDirEntry;
return SkipNonMatchingDirs();
}
else
{
return false;
}
}
bool ZipDir::FindFile::SkipNonMatchingFiles()
{
assert(m_pDirHeader && m_nFileEntry <= m_pDirHeader->numFiles);
for (; m_nFileEntry < m_pDirHeader->numFiles; ++m_nFileEntry)
{
if (MatchWildcard(GetFileName()))
{
return true;
}
}
// we didn't find anything other file else
return false;
}
bool ZipDir::FindDir::SkipNonMatchingDirs()
{
assert(m_pDirHeader && m_nDirEntry <= m_pDirHeader->numDirs);
for (; m_nDirEntry < m_pDirHeader->numDirs; ++m_nDirEntry)
{
if (MatchWildcard(GetDirName()))
{
return true;
}
}
// we didn't find anything other file else
return false;
}
ZipDir::FileEntry* ZipDir::FindFile::GetFileEntry()
{
return m_pDirHeader && m_nFileEntry < m_pDirHeader->numFiles ? m_pDirHeader->GetFileEntry(m_nFileEntry) : NULL;
}
ZipDir::DirEntry* ZipDir::FindDir::GetDirEntry()
{
return m_pDirHeader && m_nDirEntry < m_pDirHeader->numDirs ? m_pDirHeader->GetSubdirEntry(m_nDirEntry) : NULL;
}
const char* ZipDir::FindFile::GetFileName ()
{
if (m_pDirHeader && m_nFileEntry < m_pDirHeader->numFiles)
{
const char* pNamePool = m_pDirHeader->GetNamePool();
return m_pDirHeader->GetFileEntry(m_nFileEntry)->GetName(pNamePool);
}
else
{
return ""; // default name
}
}
const char* ZipDir::FindDir::GetDirName ()
{
if (m_pDirHeader && m_nDirEntry < m_pDirHeader->numDirs)
{
const char* pNamePool = m_pDirHeader->GetNamePool();
return m_pDirHeader->GetSubdirEntry(m_nDirEntry)->GetName(pNamePool);
}
else
{
return ""; // default name
}
}
@@ -0,0 +1,109 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRFIND_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRFIND_H
#pragma once
namespace ZipDir
{
// create this structure and loop:
// FindData fd (pZip);
// for (fd.FindFirst("*.cgf"); fd.GetFileEntry(); fd.FindNext())
// {} // inside the loop, use GetFileEntry() and GetFileName() to get the file entry and name records
class FindData
{
public:
FindData (DirHeader* pRoot)
: m_pRoot (pRoot)
, m_pDirHeader (NULL)
{
}
protected:
// initializes everything until the point where the file must be searched for
// after this call returns successfully (with true returned), the m_szWildcard
// contains the file name/wildcard and m_pDirHeader contains the directory where
// the file (s) are to be found
bool PreFind (const char* szWildcard);
// matches the file wilcard in the m_szWildcard to the given file/dir name
// this takes into account the fact that xxx. is the alias name for xxx
bool MatchWildcard(const char* szName);
DirHeader* m_pRoot; // the zip file inwhich the search is performed
DirHeader* m_pDirHeader; // the header of the directory in which the files reside
//unsigned m_nDirEntry; // the current directory entry inside the parent directory
// the actual wildcard being used in the current scan - the file name wildcard only!
char m_szWildcard[_MAX_PATH];
};
class FindFile
: public FindData
{
public:
FindFile (Cache* pCache)
: FindData(pCache->GetRoot())
{
}
FindFile (DirHeader* pRoot)
: FindData(pRoot)
{
}
// if bExactFile is passed, only the file is searched, and besides with the exact name as passed (no wildcards)
bool FindFirst (const char* szWildcard);
FileEntry* FindExact (const char* szPath);
// goes on to the next file entry
bool FindNext ();
FileEntry* GetFileEntry();
const char* GetFileName ();
protected:
bool SkipNonMatchingFiles();
unsigned m_nFileEntry; // the current file index inside the parent directory
};
class FindDir
: public FindData
{
public:
FindDir (Cache* pCache)
: FindData(pCache->GetRoot())
{
}
FindDir (DirHeader* pRoot)
: FindData(pRoot)
{
}
// if bExactFile is passed, only the file is searched, and besides with the exact name as passed (no wildcards)
bool FindFirst (const char* szWildcard);
// goes on to the next file entry
bool FindNext ();
DirEntry* GetDirEntry();
const char* GetDirName ();
protected:
bool SkipNonMatchingDirs();
unsigned m_nDirEntry; // the current dir index inside the parent directory
};
}
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRFIND_H
@@ -0,0 +1,253 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "smartptr.h"
#include "ZipFileFormat.h"
#include "zipdirstructures.h"
#include "ZipDirTree.h"
#include "ZipDirCacheRW.h"
#include "ZipDirFindRW.h"
#include "StringHelpers.h"
bool ZipDir::FindFileRW::FindFirst (const char* szWildcard)
{
if (!PreFind (szWildcard))
{
return false;
}
// finally, this is the name of the file
m_itFile = m_pDirHeader->GetFileBegin();
return SkipNonMatchingFiles();
}
bool ZipDir::FindDirRW::FindFirst (const char* szWildcard)
{
if (!PreFind (szWildcard))
{
return false;
}
// finally, this is the name of the file
m_itDir = m_pDirHeader->GetDirBegin();
return SkipNonMatchingDirs();
}
// matches the file wilcard in the m_szWildcard to the given file/dir name
// this takes into account the fact that xxx. is the alias name for xxx
bool ZipDir::FindDataRW::MatchWildcard(const char* szName)
{
if (StringHelpers::MatchesWildcards(szName, m_szWildcard))
{
return true;
}
// check if the file object name contains extension sign (.)
const char* p;
for (p = szName; *p && *p != '.'; ++p)
{
continue;
}
if (*p)
{
// there's an extension sign in the object, but it wasn't matched..
assert (*p == '.');
return false;
}
// no extension sign - add it
char szAlias[_MAX_PATH + 2];
size_t nLength = p - szName;
if (nLength > _MAX_PATH)
{
nLength = _MAX_PATH;
}
memcpy (szAlias, szName, nLength);
szAlias[nLength] = '.'; // add the alias
szAlias[nLength + 1] = '\0'; // terminate the string
return StringHelpers::MatchesWildcards(szAlias, m_szWildcard);
}
ZipDir::FileEntry* ZipDir::FindFileRW::FindExact (const char* szPath)
{
if (!PreFind (szPath))
{
return NULL;
}
FileEntryTree::FileMap::iterator itFile = m_pDirHeader->FindFile(m_szWildcard);
if (itFile != m_pDirHeader->GetFileEnd())
{
m_itFile = itFile;
}
else
{
m_pDirHeader = NULL; // we didn't find it, fail the search
}
return m_pDirHeader ? m_pDirHeader->GetFileEntry(itFile) : NULL;
}
ZipDir::FileEntryTree* ZipDir::FindDirRW::FindExact (const char* szPath)
{
if (!PreFind(szPath))
{
return NULL;
}
// the wildcard will contain the target directory name
return m_pDirHeader->FindDir(m_szWildcard);
}
//////////////////////////////////////////////////////////////////////////
// initializes everything until the point where the file must be searched for
// after this call returns successfully (with true returned), the m_szWildcard
// contains the file name/wildcard and m_pDirHeader contains the directory where
// the file (s) are to be found
bool ZipDir::FindDataRW::PreFind (const char* szWildcard)
{
if (!m_pRoot)
{
return false;
}
// start the search from the root
m_pDirHeader = m_pRoot;
// for each path dir name, copy it into the buffer and try to find the subdirectory
const char* pPath = szWildcard;
for (;; )
{
char* pName = m_szWildcard;
// at first we'll use the wildcard memory to save the directory names
for (; *pPath && *pPath != '/' && *pPath != '\\' && pName < m_szWildcard + sizeof(m_szWildcard) - 1; ++pPath, ++pName)
{
*pName = ::tolower(*pPath);
}
*pName = '\0';
if (*pPath)
{
// this is the name of the directory
FileEntryTree* pDirEntry = m_pDirHeader->FindDir(m_szWildcard);
if (!pDirEntry)
{
m_pDirHeader = NULL; // finish the search
return false;
}
m_pDirHeader = pDirEntry->GetDirectory();
++pPath;
assert(m_pDirHeader);
}
else
{
// finally, this is the name of the file (or directory)
return true;
}
}
}
// goes on to the next entry
bool ZipDir::FindFileRW::FindNext ()
{
if (m_pDirHeader && m_itFile != m_pDirHeader->GetFileEnd())
{
++m_itFile;
return SkipNonMatchingFiles();
}
else
{
return false;
}
}
// goes on to the next entry
bool ZipDir::FindDirRW::FindNext ()
{
if (m_pDirHeader && m_itDir != m_pDirHeader->GetDirEnd())
{
++m_itDir;
return SkipNonMatchingDirs();
}
else
{
return false;
}
}
bool ZipDir::FindFileRW::SkipNonMatchingFiles()
{
assert(m_pDirHeader);
for (; m_itFile != m_pDirHeader->GetFileEnd(); ++m_itFile)
{
if (MatchWildcard(GetFileName()))
{
return true;
}
}
// we didn't find anything other file else
return false;
}
bool ZipDir::FindDirRW::SkipNonMatchingDirs()
{
assert(m_pDirHeader);
for (; m_itDir != m_pDirHeader->GetDirEnd(); ++m_itDir)
{
if (MatchWildcard(GetDirName()))
{
return true;
}
}
// we didn't find anything other file else
return false;
}
ZipDir::FileEntry* ZipDir::FindFileRW::GetFileEntry()
{
return m_pDirHeader && m_itFile != m_pDirHeader->GetFileEnd() ? m_pDirHeader->GetFileEntry(m_itFile) : NULL;
}
ZipDir::FileEntryTree* ZipDir::FindDirRW::GetDirEntry()
{
return m_pDirHeader && m_itDir != m_pDirHeader->GetDirEnd() ? m_pDirHeader->GetDirEntry(m_itDir) : NULL;
}
const char* ZipDir::FindFileRW::GetFileName ()
{
if (m_pDirHeader && m_itFile != m_pDirHeader->GetFileEnd())
{
return m_pDirHeader->GetFileName(m_itFile);
}
else
{
return ""; // default name
}
}
const char* ZipDir::FindDirRW::GetDirName ()
{
if (m_pDirHeader && m_itDir != m_pDirHeader->GetDirEnd())
{
return m_pDirHeader->GetDirName(m_itDir);
}
else
{
return ""; // default name
}
}
@@ -0,0 +1,110 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Declaration of the class that can be used to search for the entries
// in a zip dir cache
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRFINDRW_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRFINDRW_H
#pragma once
namespace ZipDir
{
// create this structure and loop:
// FindData fd (pZip);
// for (fd.FindFirst("*.cgf"); fd.GetFileEntry(); fd.FindNext())
// {} // inside the loop, use GetFileEntry() and GetFileName() to get the file entry and name records
class FindDataRW
{
public:
FindDataRW (FileEntryTree* pRoot)
: m_pRoot (pRoot)
, m_pDirHeader (NULL)
{
}
// returns the directory to which the current object belongs
FileEntryTree* GetParentDir() {return m_pDirHeader; }
protected:
// initializes everything until the point where the file must be searched for
// after this call returns successfully (with true returned), the m_szWildcard
// contains the file name/wildcard and m_pDirHeader contains the directory where
// the file (s) are to be found
bool PreFind (const char* szWildcard);
// matches the file wilcard in the m_szWildcard to the given file/dir name
// this takes into account the fact that xxx. is the alias name for xxx
bool MatchWildcard(const char* szName);
// the directory inside which the current object (file or directory) is being searched
FileEntryTree* m_pDirHeader;
FileEntryTree* m_pRoot; // the root of the zip file in which to search
// the actual wildcard being used in the current scan - the file name wildcard only!
char m_szWildcard[_MAX_PATH];
};
class FindFileRW
: public FindDataRW
{
public:
FindFileRW (FileEntryTree* pRoot)
: FindDataRW(pRoot)
{
}
// if bExactFile is passed, only the file is searched, and besides with the exact name as passed (no wildcards)
bool FindFirst (const char* szWildcard);
FileEntry* FindExact (const char* szPath);
// goes on to the next file entry
bool FindNext ();
FileEntry* GetFileEntry();
const char* GetFileName ();
protected:
bool SkipNonMatchingFiles();
FileEntryTree::FileMap::iterator m_itFile; // the current file iterator inside the parent directory
};
class FindDirRW
: public FindDataRW
{
public:
FindDirRW (FileEntryTree* pRoot)
: FindDataRW(pRoot)
{
}
// if bExactFile is passed, only the file is searched, and besides with the exact name as passed (no wildcards)
bool FindFirst (const char* szWildcard);
FileEntryTree* FindExact (const char* szPath);
// goes on to the next file entry
bool FindNext ();
FileEntryTree* GetDirEntry();
const char* GetDirName ();
protected:
bool SkipNonMatchingDirs();
FileEntryTree::SubdirMap::iterator m_itDir; // the current dir index inside the parent directory
};
}
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRFINDRW_H
@@ -0,0 +1,174 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#undef max
#include <algorithm>
#include "ZipFileFormat.h"
#include "zipdirstructures.h"
#include "ZipDirList.h"
#include "ZipDirTree.h"
ZipDir::FileRecordList::FileRecordList(FileEntryTree* pTree)
{
clear();
reserve(pTree->NumFilesTotal());
AddAllFiles(pTree);
}
//recursively adds the files from this directory and subdirectories
// the strRoot contains the trailing slash
void ZipDir::FileRecordList::AddAllFiles(FileEntryTree* pTree, string strRoot)
{
for (FileEntryTree::SubdirMap::iterator it = pTree->GetDirBegin(); it != pTree->GetDirEnd(); ++it)
{
AddAllFiles (it->second, strRoot + it->second->GetOriginalName() + "/");
}
for (FileEntryTree::FileMap::iterator it = pTree->GetFileBegin(); it != pTree->GetFileEnd(); ++it)
{
FileRecord rec;
rec.pFileEntry = pTree->GetFileEntry(it);
const char* filename = rec.pFileEntry->szOriginalFileName ? rec.pFileEntry->szOriginalFileName : it->first;
rec.strPath = strRoot + filename;
push_back(rec);
}
}
// sorts the files by the physical offset in the zip file
void ZipDir::FileRecordList::SortByFileOffset()
{
std::sort (begin(), end(), FileRecordFileOffsetOrder());
}
// returns the size of CDR in the zip file
ZipDir::FileRecordList::ZipStats ZipDir::FileRecordList::GetStats() const
{
ZipStats Stats;
Stats.nSizeCDR = sizeof(ZipFile::CDREnd);
Stats.nSizeCompactData = 0;
// for each file, we'll need to store only its CDR header and the name
for (const_iterator it = begin(); it != end(); ++it)
{
Stats.nSizeCDR += sizeof(ZipFile::CDRFileHeader) + it->strPath.length();
Stats.nSizeCompactData += sizeof(ZipFile::LocalFileHeader) + it->strPath.length() + it->pFileEntry->desc.lSizeCompressed;
}
return Stats;
}
// puts the CDR into the given block of mem
size_t ZipDir::FileRecordList::MakeZipCDR(ZipFile::ulong lCDROffset, void* pBuffer, bool encryptedFlag) const
{
const ZipFile::ushort nBaseVersion = std::max(encryptedFlag ? ZipFile::VERSION_ENCRYPTION_PKWARE : ZipFile::VERSION_DEFAULT, ZipFile::VERSION_COMPRESSION_DEFLATE);
char* pCur = (char*)pBuffer;
for (const_iterator it = begin(); it != end(); ++it)
{
ZipFile::CDRFileHeader& h = *(ZipFile::CDRFileHeader*)pCur;
pCur = (char*)(&h + 1);
h.lSignature = h.SIGNATURE;
h.nVersionMadeBy = nBaseVersion + (ZipFile::CREATOR_MSDOS << 8);
h.nVersionNeeded = nBaseVersion;
h.nFlags = 0;
h.nMethod = it->pFileEntry->nMethod;
h.nLastModTime = it->pFileEntry->nLastModTime;
h.nLastModDate = it->pFileEntry->nLastModDate;
h.desc = it->pFileEntry->desc;
h.nFileNameLength = (ZipFile::ushort)it->strPath.length();
h.nExtraFieldLength = 0;
h.nFileCommentLength = 0;
h.nDiskNumberStart = 0;
h.nAttrInternal = 0;
h.lAttrExternal = 0;
h.lLocalHeaderOffset = it->pFileEntry->nFileHeaderOffset;
memcpy (pCur, it->strPath.c_str(), it->strPath.length());
pCur += it->strPath.length();
}
ZipFile::CDREnd& e = *(ZipFile::CDREnd*)pCur;
e.lSignature = e.SIGNATURE;
e.nDisk = encryptedFlag ? (1 << 15) : 0;
e.nCDRStartDisk = 0;
e.numEntriesOnDisk = (ZipFile::ushort)this->size();
e.numEntriesTotal = (ZipFile::ushort)this->size();
e.lCDRSize = (ZipFile::ulong)(pCur - (char*)pBuffer);
e.lCDROffset = lCDROffset;
e.nCommentLength = 0;
pCur = (char*)(&e + 1);
return pCur - (char*)pBuffer;
}
ZipDir::FileEntryList::FileEntryList (FileEntryTree* pTree, unsigned lCDROffset)
: m_lCDROffset (lCDROffset)
{
Add (pTree);
}
void ZipDir::FileEntryList::Add(FileEntryTree* pTree)
{
for (FileEntryTree::SubdirMap::iterator itDir = pTree->GetDirBegin(); itDir != pTree->GetDirEnd(); ++itDir)
{
Add(pTree->GetDirEntry(itDir));
}
for (FileEntryTree::FileMap::iterator itFile = pTree->GetFileBegin(); itFile != pTree->GetFileEnd(); ++itFile)
{
insert(pTree->GetFileEntry(itFile));
}
}
// updates each file entry's info about the next file entry
void ZipDir::FileEntryList::RefreshEOFOffsets()
{
iterator it, itNext = begin();
if (itNext != end())
{
while ((it = itNext, ++itNext) != end())
{
// start scan
(*it)->nEOFOffset = (*itNext)->nFileHeaderOffset;
}
// it is the last one..
(*it)->nEOFOffset = m_lCDROffset;
}
}
void ZipDir::FileRecordList::Backup(std::vector<FileEntry>& arrFiles) const
{
arrFiles.resize (size());
std::vector<FileEntry>::iterator itTgt = arrFiles.begin();
for (const_iterator it = begin(); it != end(); ++it, ++itTgt)
{
*itTgt = *it->pFileEntry;
}
}
void ZipDir::FileRecordList::Restore(const std::vector<FileEntry>& arrFiles)
{
if (arrFiles.size() == size())
{
std::vector<FileEntry>::const_iterator itTgt = arrFiles.begin();
for (iterator it = begin(); it != end(); ++it, ++itTgt)
{
*it->pFileEntry = *itTgt;
}
}
}
@@ -0,0 +1,138 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRLIST_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRLIST_H
#pragma once
#include <smartptr.h>
namespace ZipDir
{
// this is the array of file entries that's convenient to use to construct CDR
struct FileRecord
{
string strPath; // relative path to the file inside zip
FileEntry* pFileEntry; // the file entry itself
void ConstructFileRecord()
{
new (&strPath)string();
}
};
struct FileDataRecord
: public FileRecord
{
FileDataRecord() { m_nRefCount = 0; }
void AddRef() { ++m_nRefCount; }
void Release()
{
if (--m_nRefCount <= 0)
{
Delete();
}
}
void Delete()
{
free (this);
}
static FileDataRecord* New(const FileRecord& rThat)
{
FileDataRecord* pThis = (FileDataRecord*)malloc(sizeof(FileDataRecord) + rThat.pFileEntry->desc.lSizeCompressed);
if (pThis)
{
pThis->m_nRefCount = 0;
pThis->ConstructFileRecord();
*static_cast<FileRecord*>(pThis) = rThat;
}
return pThis;
}
void* GetData() {return this + 1; }
volatile signed int m_nRefCount; // the reference count
};
TYPEDEF_AUTOPTR(FileDataRecord);
typedef FileDataRecord_AutoPtr FileDataRecordPtr;
struct FileRecordFileOffsetOrder
{
bool operator () (const FileRecord& left, const FileRecord& right)
{
return left.pFileEntry->nFileHeaderOffset < right.pFileEntry->nFileHeaderOffset;
}
};
// this is used for construction of CDR
class FileRecordList
: public std::vector<FileRecord>
{
public:
FileRecordList(class FileEntryTree* pTree);
struct ZipStats
{
// the size of the CDR in the file
size_t nSizeCDR;
// the size of the file data part (local file descriptors and file datas)
// if it's compacted
size_t nSizeCompactData;
};
// sorts the files by the physical offset in the zip file
void SortByFileOffset ();
// returns the size of CDR in the zip file
ZipStats GetStats() const;
// puts the CDR into the given block of mem
size_t MakeZipCDR(ZipFile::ulong lCDROffset, void* p, bool encryptedFlag) const;
void Backup(std::vector<FileEntry>& arrFiles) const;
void Restore(const std::vector<FileEntry>& arrFiles);
protected:
// recursively adds the files from this directory and subdirectories
// the strRoot contains the trailing slash
void AddAllFiles(class FileEntryTree* pTree, string strRoot = string());
};
struct FileEntryFileOffsetOrder
{
bool operator () (FileEntry* pLeft, FileEntry* pRight) const
{
return pLeft->nFileHeaderOffset < pRight->nFileHeaderOffset;
}
};
// this is used for refreshing EOFOffsets
class FileEntryList
: public std::set<FileEntry*, FileEntryFileOffsetOrder>
{
public:
FileEntryList (class FileEntryTree* pTree, unsigned lCDROffset);
// updates each file entry's info about the next file entry
void RefreshEOFOffsets();
protected:
void Add (class FileEntryTree* pTree);
unsigned m_lCDROffset;
};
}
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRLIST_H
@@ -0,0 +1,670 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "smartptr.h"
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Casting/numeric_cast.h>
#include <zlib.h>
#include "ZipFileFormat.h"
#include "zipdirstructures.h"
#include <time.h>
#include <AzCore/std/time.h>
#include <zstd.h>
#include <lz4frame.h>
using namespace ZipFile;
ZipDir::FileEntry::FileEntry(const CDRFileHeader& header, const SExtraZipFileData& extra)
{
this->desc = header.desc;
this->nFileHeaderOffset = header.lLocalHeaderOffset;
this->nFileDataOffset = INVALID_DATA_OFFSET; // we don't know yet
this->nMethod = header.nMethod;
this->nNameOffset = 0; // we don't know yet
#if defined(AZ_PLATFORM_WINDOWS)
this->nLastModTime = header.nLastModTime;
this->nLastModDate = header.nLastModDate;
#endif
this->nNTFS_LastModifyTime = extra.nLastModifyTime;
this->szOriginalFileName = 0;
// make an estimation (at least this offset should be there), but we don't actually know yet
this->nEOFOffset = header.lLocalHeaderOffset + sizeof (ZipFile::LocalFileHeader) + header.nFileNameLength + header.desc.lSizeCompressed;
}
// 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 ZipDir::ZipRawUncompress (void* pUncompressed, unsigned long* pDestSize, const void* pCompressed, unsigned long nSrcSize)
{
int nReturnCode = Z_OK;
//check first 4 bytes to see what compression codec was used
if (CompressionCodec::TestForZSTDMagic(pCompressed))
{
size_t result = ZSTD_decompress(pUncompressed, *pDestSize, pCompressed, nSrcSize);
if (ZSTD_isError(result))
{
AZ_Error("ZipDirStructures", false, "Error decompressing using zstd: %s", ZSTD_getErrorName(result));
nReturnCode = Z_BUF_ERROR;
}
else
{
*pDestSize = result;
}
return nReturnCode;
}
else if (CompressionCodec::TestForLZ4Magic(pCompressed))
{
size_t result;
LZ4F_decompressionContext_t dctx;
result = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
if (LZ4F_isError(result))
{
AZ_Error("ZipDirStructures", false, "Error creating lz4 decompression context: %s", LZ4F_getErrorName(result));
return Z_BUF_ERROR;
}
size_t dstSize = (size_t)*pDestSize;
size_t srcSize = (size_t)nSrcSize;
result = LZ4F_decompress(dctx, pUncompressed, &dstSize, pCompressed, &srcSize, nullptr);
if (LZ4F_isError(result))
{
AZ_Error("ZipDirStructures", false, "Error decompressing using lz4: %s", LZ4F_getErrorName(result));
nReturnCode = Z_BUF_ERROR;
}
else
{
*pDestSize = (long)dstSize;
}
size_t freeCode = LZ4F_freeDecompressionContext(dctx);
if (LZ4F_isError(freeCode))
{
//We are not changing the return code in this case, but it is good to record that releasing the
//decompression context failed.
AZ_Error("ZipDirStructures", false, "Error releasing lz4 decompression context: %s", LZ4F_getErrorName(freeCode));
}
return nReturnCode;
}
//Default to Zlib
z_stream stream;
stream.next_in = (Bytef*)pCompressed;
stream.avail_in = (uInt)nSrcSize;
int err;
stream.next_out = (Bytef*)pUncompressed;
stream.avail_out = (uInt) * pDestSize;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
err = inflateInit2(&stream, -MAX_WBITS);
if (err != Z_OK)
{
return err;
}
// for some strange reason, passing Z_FINISH doesn't work -
// it seems the stream isn't finished for some files and
// inflate returns an error due to stream-end-not-reached (though expected) problem
err = inflate(&stream, Z_SYNC_FLUSH);
if (err != Z_STREAM_END && err != Z_OK)
{
inflateEnd(&stream);
return err == Z_OK ? Z_BUF_ERROR : err;
}
*pDestSize = stream.total_out;
err = inflateEnd(&stream);
return err;
}
// 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)
int ZipDir::ZipRawCompress(const void* pUncompressed, unsigned long* pDestSize, void* pCompressed, unsigned long nSrcSize, int nLevel)
{
z_stream stream;
int err;
stream.next_out = reinterpret_cast<Bytef*>(pCompressed);
stream.next_in = const_cast<Bytef*>(static_cast<const Bytef*>(pUncompressed));
stream.avail_in = static_cast<uInt>(nSrcSize);
stream.avail_out = static_cast<uInt>(*pDestSize);
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
err = deflateInit2 (&stream, nLevel, Z_DEFLATED, -MAX_WBITS, 9, Z_DEFAULT_STRATEGY);
if (err != Z_OK)
{
return err;
}
err = deflate (&stream, Z_FINISH);
if (err != Z_STREAM_END)
{
deflateEnd(&stream);
return err == Z_OK ? Z_BUF_ERROR : err;
}
*pDestSize = stream.total_out;
err = deflateEnd(&stream);
return err;
}
int ZipDir::ZipRawCompressZSTD(const void* pUncompressed, unsigned long* pDestSize, void* pCompressed, unsigned long nSrcSize, int nLevel)
{
size_t result = ZSTD_compress(pCompressed, *pDestSize, pUncompressed, nSrcSize, nLevel);
int err = Z_OK;
if (ZSTD_isError(result))
{
err = Z_BUF_ERROR;
}
else
{
*pDestSize = static_cast<unsigned long>(result);
}
return err;
}
int ZipDir::ZipRawCompressLZ4(const void* pUncompressed, unsigned long* pDestSize, void* pCompressed, unsigned long nSrcSize, [[maybe_unused]] int nLevel)
{
int returnCode = Z_OK;
const size_t compressedBufferMaxSize = aznumeric_caster(*pDestSize);
size_t lz4_code = LZ4F_compressFrame(pCompressed, compressedBufferMaxSize, pUncompressed, aznumeric_caster(nSrcSize), nullptr);
if (LZ4F_isError(lz4_code))
{
returnCode = Z_BUF_ERROR;
}
else
{
*pDestSize = aznumeric_caster(lz4_code);
}
return returnCode;
}
int ZipDir::GetCompressedSizeEstimate(unsigned long uncompressedSize, CompressionCodec::Codec codec)
{
switch (codec)
{
case CompressionCodec::Codec::ZLIB:
return (uncompressedSize + (uncompressedSize >> 3) + 32);
case CompressionCodec::Codec::ZSTD:
return ZSTD_compressBound(uncompressedSize);
case CompressionCodec::Codec::LZ4:
return LZ4F_compressFrameBound(uncompressedSize, nullptr);
default:
break;
}
return 0;
}
ZipDir::ValidationResult ZipDir::ValidateZSTDCompressedDataWithOriginalData(const void* pUncompressed, unsigned long uncompressedSize, const void* pCompressed, unsigned long compressedSize)
{
auto decompressedSize = ZSTD_getDecompressedSize(pCompressed, compressedSize);
ZipDir::ValidationResult testResult = ValidationResult::OK;
if (decompressedSize != uncompressedSize)
{
testResult = ValidationResult::SIZE_MISMATCH;
}
else
{
void* decompressionBuffer = azmalloc(decompressedSize);
size_t result = ZSTD_decompress(decompressionBuffer, decompressedSize, pCompressed, compressedSize);
if (ZSTD_isError(result))
{
AZ_Warning("Debug", false, "Error decompressing data with zstd: %s", ZSTD_getErrorName(result));
testResult = ValidationResult::DATA_CORRUPTED;
}
else
{
if (memcmp(decompressionBuffer, pUncompressed, decompressedSize) != 0)
{
testResult = ValidationResult::DATA_NO_MATCH;
}
}
azfree(decompressionBuffer);
}
return testResult;
}
// finds the subdirectory entry by the name, using the names from the name pool
// assumes: all directories are sorted in alphabetical order.
// case-sensitive (must be lower-case if case-insensitive search in Win32 is performed)
ZipDir::DirEntry* ZipDir::DirHeader::FindSubdirEntry(const char* szName)
{
if (this->numDirs)
{
const char* pNamePool = GetNamePool();
DirEntrySortPred pred(pNamePool);
DirEntry* pBegin = GetSubdirEntry(0);
DirEntry* pEnd = pBegin + this->numDirs;
DirEntry* pEntry = std::lower_bound(pBegin, pEnd, szName, pred);
#if defined(LINUX)
if (pEntry != pEnd && !strcasecmp(szName, pEntry->GetName(pNamePool)))
#else
if (pEntry != pEnd && !strcmp(szName, pEntry->GetName(pNamePool)))
#endif
{
return pEntry;
}
}
return NULL;
}
// finds the file entry by the name, using the names from the name pool
// assumes: all directories are sorted in alphabetical order.
// case-sensitive (must be lower-case if case-insensitive search in Win32 is performed)
ZipDir::FileEntry* ZipDir::DirHeader::FindFileEntry(const char* szName)
{
if (this->numFiles)
{
const char* pNamePool = GetNamePool();
DirEntrySortPred pred(pNamePool);
FileEntry* pBegin = GetFileEntry(0);
FileEntry* pEnd = pBegin + this->numFiles;
FileEntry* pEntry = std::lower_bound(pBegin, pEnd, szName, pred);
#if defined(LINUX)
if (pEntry != pEnd && !strcasecmp(szName, pEntry->GetName(pNamePool)))
#else
if (pEntry != pEnd && !strcmp(szName, pEntry->GetName(pNamePool)))
#endif
{
return pEntry;
}
}
return NULL;
}
// tries to refresh the file entry from the given file (reads fromthere if needed)
// returns the error code if the operation was impossible to complete
ZipDir::ErrorEnum ZipDir::Refresh(FILE* f, FileEntry* pFileEntry, bool encryptedHeaders)
{
if (pFileEntry->nFileDataOffset != pFileEntry->INVALID_DATA_OFFSET)
{
return ZD_ERROR_SUCCESS;
}
if (pFileEntry->desc.lSizeCompressed == 0)
{
return ZD_ERROR_SUCCESS;
}
#ifdef WIN32
if (_fseeki64(f, (__int64)pFileEntry->nFileHeaderOffset, SEEK_SET))
#else
if (fseek(f, pFileEntry->nFileHeaderOffset, SEEK_SET))
#endif
{
return ZD_ERROR_IO_FAILED;
}
if (encryptedHeaders)
{
// with encrypted headers FileEntries should always be initialized from CDR.
return ZD_ERROR_IO_FAILED;
}
// read the local file header and the name (for validation) into the buffer
LocalFileHeader fileHeader;
if (1 != fread (&fileHeader, sizeof(fileHeader), 1, f))
{
return ZD_ERROR_IO_FAILED;
}
if (fileHeader.desc != pFileEntry->desc
|| fileHeader.nMethod != pFileEntry->nMethod)
{
return ZD_ERROR_IO_FAILED;
}
pFileEntry->nFileDataOffset = pFileEntry->nFileHeaderOffset + sizeof(LocalFileHeader) + fileHeader.nFileNameLength + fileHeader.nExtraFieldLength;
pFileEntry->nEOFOffset = pFileEntry->nFileDataOffset + pFileEntry->desc.lSizeCompressed;
return ZD_ERROR_SUCCESS;
}
// writes into the file local header - without Extra data
// puts the new offset to the file data to the file entry
// in case of error can put INVALID_DATA_OFFSET into the data offset field of file entry
ZipDir::ErrorEnum ZipDir::WriteLocalHeader (FILE* f, FileEntry* pFileEntry, const char* szRelativePath, bool encrypt)
{
size_t nFileNameLength = strlen(szRelativePath);
size_t nHeaderSize = sizeof(LocalFileHeader) + nFileNameLength;
pFileEntry->nFileDataOffset = pFileEntry->nFileHeaderOffset + nHeaderSize;
pFileEntry->nEOFOffset = pFileEntry->nFileDataOffset + pFileEntry->desc.lSizeCompressed;
#ifdef WIN32
if (_fseeki64 (f, (__int64)pFileEntry->nFileHeaderOffset, SEEK_SET))
#else
if (fseek (f, pFileEntry->nFileHeaderOffset, SEEK_SET))
#endif
{
return ZD_ERROR_IO_FAILED;
}
if (encrypt)
{
std::vector<uint8> garbage;
garbage.resize(nHeaderSize);
for (size_t i = 0; i < nHeaderSize; ++i)
{
garbage[i] = rand() & 0xff;
}
if (fwrite(&garbage[0], nHeaderSize, 1, f) != 1)
{
return ZD_ERROR_IO_FAILED;
}
}
else
{
LocalFileHeader h;
memset(&h, 0, sizeof(h));
h.lSignature = h.SIGNATURE;
h.nVersionNeeded = 10;
h.nFlags = 0;
h.nMethod = pFileEntry->nMethod;
#if defined(AZ_PLATFORM_WINDOWS)
h.nLastModDate = pFileEntry->nLastModDate;
h.nLastModTime = pFileEntry->nLastModTime;
#endif
h.desc = pFileEntry->desc;
h.nFileNameLength = (unsigned short)nFileNameLength;
h.nExtraFieldLength = 0;
if (1 != fwrite(&h, sizeof(h), 1, f))
{
return ZD_ERROR_IO_FAILED;
}
if (nFileNameLength > 0)
{
if (1 != fwrite (szRelativePath, nFileNameLength, 1, f))
{
return ZD_ERROR_IO_FAILED;
}
}
}
return ZD_ERROR_SUCCESS;
}
// conversion routines for the date/time fields used in Zip
ZipFile::ushort ZipDir::DOSDate(tm* t)
{
return
((t->tm_year - 80) << 9)
| (t->tm_mon << 5)
| t->tm_mday;
}
ZipFile::ushort ZipDir::DOSTime(tm* t)
{
return
((t->tm_hour) << 11)
| ((t->tm_min) << 5)
| ((t->tm_sec) >> 1);
}
// sets the current time to modification time
// calculates CRC32 for the new data
void ZipDir::FileEntry::OnNewFileData(void* pUncompressed, unsigned nSize, unsigned nCompressedSize, unsigned nCompressionMethod, bool bContinuous)
{
time_t nTime;
time(&nTime);
#if defined(AZ_PLATFORM_WINDOWS)
tm t;
localtime_s(&t, &nTime);
this->nLastModTime = DOSTime(&t);
this->nLastModDate = DOSDate(&t);
#else
#endif
this->nNTFS_LastModifyTime = AZStd::GetTimeUTCMilliSecond();
if (!bContinuous)
{
this->desc.lCRC32 = crc32(0L, Z_NULL, 0);
this->desc.lSizeCompressed = nCompressedSize;
this->desc.lSizeUncompressed = nSize;
}
// we'll need CRC32 of the file to pack it
this->desc.lCRC32 = crc32(this->desc.lCRC32, (Bytef*)pUncompressed, nSize);
this->nMethod = nCompressionMethod;
}
const char* ZipDir::DOSTimeCStr(ZipFile::ushort nTime)
{
static char szBuf[16];
azsprintf(szBuf, "%02d:%02d.%02d", (nTime >> 11), ((nTime & ((1 << 11) - 1)) >> 5), ((nTime & ((1 << 5) - 1)) << 1));
return szBuf;
}
const char* ZipDir::DOSDateCStr(ZipFile::ushort nTime)
{
static char szBuf[32];
azsprintf(szBuf, "%02d.%02d.%04d", (nTime & 0x1F), (nTime >> 5) & 0xF, (nTime >> 9) + 1980);
return szBuf;
}
uint64 ZipDir::FileEntry::GetModificationTime()
{
if (nNTFS_LastModifyTime != 0)
{
return nNTFS_LastModifyTime;
}
#if defined(AZ_PLATFORM_WINDOWS)
// TODO/TIME: check and test
SYSTEMTIME st;
st.wYear = (nLastModDate >> 9) + 1980;
st.wMonth = ((nLastModDate >> 5) & 0xF);
st.wDay = (nLastModDate & 0x1F);
st.wHour = (nLastModTime >> 11);
st.wMinute = (nLastModTime >> 5) & 0x3F;
st.wSecond = (nLastModTime << 1) & 0x3F;
st.wMilliseconds = 0;
FILETIME ft;
SystemTimeToFileTime(&st, &ft);
LARGE_INTEGER lt;
lt.HighPart = ft.dwHighDateTime;
lt.LowPart = ft.dwLowDateTime;
return lt.QuadPart;
#else
return 0;
#endif
}
void ZipDir::FileEntry::SetFromFileTimeNTFS(int64 timestamp)
{
#if defined(AZ_PLATFORM_WINDOWS)
FILETIME ft;
ft.dwHighDateTime = timestamp >> 32;
ft.dwLowDateTime = timestamp & 0xFFFFFFFF;
WORD dosTime, dosDate;
FileTimeToDosDateTime(&ft, &dosDate, &dosTime);
nLastModDate = dosDate;
nLastModTime = dosTime;
#endif
nNTFS_LastModifyTime = timestamp;
}
bool ZipDir::FileEntry::CompareFileTimeNTFS(int64 timestamp)
{
#if defined(AZ_PLATFORM_WINDOWS)
FILETIME ft;
ft.dwHighDateTime = timestamp >> 32;
ft.dwLowDateTime = timestamp & 0xFFFFFFFF;
WORD dosTime, dosDate;
FileTimeToDosDateTime(&ft, &dosDate, &dosTime);
return (nLastModTime == dosTime && nLastModDate == dosDate);
#else
return (nNTFS_LastModifyTime == timestamp);
#endif
}
const char* ZipDir::Error::getError()
{
switch (this->nError)
{
#define DECLARE_ERROR(x) case ZD_ERROR_##x: \
return #x;
DECLARE_ERROR(SUCCESS);
DECLARE_ERROR(IO_FAILED);
DECLARE_ERROR(UNEXPECTED);
DECLARE_ERROR(UNSUPPORTED);
DECLARE_ERROR(INVALID_SIGNATURE);
DECLARE_ERROR(ZIP_FILE_IS_CORRUPT);
DECLARE_ERROR(DATA_IS_CORRUPT);
DECLARE_ERROR(NO_CDR);
DECLARE_ERROR(CDR_IS_CORRUPT);
DECLARE_ERROR(NO_MEMORY);
DECLARE_ERROR(VALIDATION_FAILED);
DECLARE_ERROR(CRC32_CHECK);
DECLARE_ERROR(ZLIB_FAILED);
DECLARE_ERROR(ZLIB_CORRUPTED_DATA);
DECLARE_ERROR(ZLIB_NO_MEMORY);
DECLARE_ERROR(CORRUPTED_DATA);
DECLARE_ERROR(INVALID_CALL);
DECLARE_ERROR(NOT_IMPLEMENTED);
DECLARE_ERROR(FILE_NOT_FOUND);
DECLARE_ERROR(DIR_NOT_FOUND);
DECLARE_ERROR(NAME_TOO_LONG);
DECLARE_ERROR(INVALID_PATH);
DECLARE_ERROR(FILE_ALREADY_EXISTS);
#undef DECLARE_ERROR
default:
return "Unknown ZD_ERROR code";
}
}
inline void btea(uint32* v, int n, uint32 const k[4])
{
#define TEA_DELTA 0x9e3779b9
#define TEA_MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (k[(p & 3) ^ e] ^ z)))
uint32 y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52 / n;
sum = 0;
z = v[n - 1];
do
{
sum += TEA_DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++)
{
y = v[p + 1];
z = v[p] += TEA_MX;
}
y = v[0];
z = v[n - 1] += TEA_MX;
} while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52 / n;
sum = rounds * TEA_DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--)
{
z = v[p - 1];
y = v[p] -= TEA_MX;
}
z = v[n - 1];
y = v[0] -= TEA_MX;
} while ((sum -= TEA_DELTA) != 0);
}
#undef TEA_DELTA
#undef TEA_MX
}
static inline void SwapByteOrder(uint32* values, size_t count)
{
for (uint32* w = values, * e = values + count; w != e; ++w)
{
*w = (*w >> 24) + ((*w >> 8) & 0xff00) + ((*w & 0xff00) << 8) + (*w << 24);
}
}
//////////////////////////////////////////////////////////////////////////
void ZipDir::Encrypt(char* buffer, size_t size, const EncryptionKey& key)
{
uint32* intBuffer = (uint32*)buffer;
const int encryptedLen = size >> 2;
SwapByteOrder(intBuffer, encryptedLen);
btea(intBuffer, encryptedLen, key.key);
SwapByteOrder(intBuffer, encryptedLen);
}
//////////////////////////////////////////////////////////////////////////
void ZipDir::Decrypt(char* buffer, size_t size, const EncryptionKey& key)
{
uint32* intBuffer = (uint32*)buffer;
const int encryptedLen = size >> 2;
SwapByteOrder(intBuffer, encryptedLen);
btea(intBuffer, -encryptedLen, key.key);
SwapByteOrder(intBuffer, encryptedLen);
}
@@ -0,0 +1,356 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "ZipFileFormat.h"
#include "zipdirstructures.h"
#include "ZipDirTree.h"
// Adds or finds the file. Returns non-initialized structure if it was added,
// or an IsInitialized() structure if it was found
ZipDir::FileEntry* ZipDir::FileEntryTree::Add(char* szPath, char* szUnifiedPath)
{
// find the slash; if we found it, it's a subdirectory - add a subdirectory and
// add the file to it.
// if we didn't find it, it's a file - add the file to this dir
char* pSlash;
for (pSlash = szPath; *pSlash && *pSlash != '/' && *pSlash != '\\'; ++pSlash)
{
continue; // find the next slash
}
char* pUnifiedSlash = szUnifiedPath + (pSlash - szPath);
assert(*pUnifiedSlash == '\0' || *pUnifiedSlash == '\\' || *pUnifiedSlash == '/');
if (*pUnifiedSlash)
{
FileEntryTree* pSubdir;
// we have a subdirectory here - create the file in it
{
char* unifiedDir = szUnifiedPath;
*pUnifiedSlash = '\0';
char* dir = szPath;
*pSlash = '\0';
SubdirMap::iterator it = m_mapDirs.find (unifiedDir);
if (it == m_mapDirs.end())
{
pSubdir = new FileEntryTree(dir);
m_mapDirs.insert (SubdirMap::value_type(unifiedDir, pSubdir));
}
else
{
pSubdir = it->second;
}
}
return pSubdir->Add(pSlash + 1, pUnifiedSlash + 1);
}
else
{
ZipDir::FileEntry* result = &m_mapFiles[szUnifiedPath];
result->szOriginalFileName = szPath;
return result;
}
}
// adds a file to this directory
ZipDir::ErrorEnum ZipDir::FileEntryTree::Add (char* szPath, char* szUnifiedPath, const FileEntry& file)
{
FileEntry* pFile = Add (szPath, szUnifiedPath);
if (!pFile)
{
return ZD_ERROR_INVALID_PATH;
}
if (pFile->IsInitialized())
{
return ZD_ERROR_FILE_ALREADY_EXISTS;
}
// preserve original filename
const char* szOriginalFileName = pFile->szOriginalFileName;
*pFile = file;
pFile->szOriginalFileName = szOriginalFileName;
return ZD_ERROR_SUCCESS;
}
// returns the number of files in this tree, including this and sublevels
unsigned ZipDir::FileEntryTree::NumFilesTotal() const
{
unsigned numFiles = (unsigned)m_mapFiles.size();
for (SubdirMap::const_iterator it = m_mapDirs.begin(); it != m_mapDirs.end(); ++it)
{
numFiles += it->second->NumFilesTotal();
}
return numFiles;
}
#ifdef _TEST_
size_t g_nSF = 0, g_nSS = 0, g_nSN = 0, g_nSNa = 0, g_nSH;
size_t g_nGF = 0, g_nGS = 0, g_nGN = 0, g_nGNa = 0, g_nGH;
#endif
// returns the size required to serialize the tree
size_t ZipDir::FileEntryTree::GetSizeSerialized() const
{
// the total size of name pool gets aligned on 4-byte boundary
size_t nSizeOfNamePool = 0;
size_t nSizeOfFileEntries = 0, nSizeOfDirEntries = 0;
size_t nSizeOfSubdirs = 0;
for (SubdirMap::const_iterator itDir = m_mapDirs.begin(); itDir != m_mapDirs.end(); ++itDir)
{
nSizeOfDirEntries += sizeof(DirEntry);
const char* dirname = itDir->first;
nSizeOfNamePool += strlen(dirname) + 1;
nSizeOfSubdirs += itDir->second->GetSizeSerialized();
}
// for each file, we need to have an entry in the name pool and in the file list
for (FileMap::const_iterator itFile = m_mapFiles.begin(); itFile != m_mapFiles.end(); ++itFile)
{
nSizeOfFileEntries += sizeof(FileEntry);
const char* fname = itFile->first;
nSizeOfNamePool += strlen(fname) + 1;
}
if (nSizeOfNamePool > 0xFFFF)
{
// we don't support so long names/directories
THROW_ZIPDIR_ERROR(ZD_ERROR_UNSUPPORTED, "Name pool larger then 65536 bytes");
}
#ifdef _TEST_
g_nGF += nSizeOfFileEntries;
g_nGS += nSizeOfDirEntries;
g_nGN += nSizeOfNamePool;
g_nGNa += ((nSizeOfNamePool + 3) & ~3);
g_nGH += sizeof(DirHeader);
#endif
return sizeof(DirHeader) + ((nSizeOfNamePool + 3) & ~3) + nSizeOfDirEntries + nSizeOfFileEntries + nSizeOfSubdirs;
}
// serializes into the memory
size_t ZipDir::FileEntryTree::Serialize (DirHeader* pDirHeader) const
{
pDirHeader->numDirs = (ZipFile::ushort)m_mapDirs.size();
pDirHeader->numFiles = (ZipFile::ushort)m_mapFiles.size();
DirEntry* pDirEntries = (DirEntry*)(pDirHeader + 1);
FileEntry* pFileEntries = (FileEntry*)(pDirEntries + pDirHeader->numDirs);
char* pNamePool = (char*)(pFileEntries + pDirHeader->numFiles);
char* pName = pNamePool;
DirEntry* pDirEntry = pDirEntries;
FileEntry* pFileEntry = pFileEntries;
SubdirMap::const_iterator itDir;
for (itDir = m_mapDirs.begin(); itDir != m_mapDirs.end(); ++itDir)
{
pDirEntry->nNameOffset = (ZipFile::ulong)(pName - pNamePool);
size_t nNameLen = strlen(itDir->first);
memcpy (pName, itDir->first, nNameLen + 1);
pName += nNameLen + 1;
++pDirEntry;
}
assert ((FileEntry*)pDirEntry == pFileEntry);
// for each file, we need to have an entry in the name pool and in the file list
for (FileMap::const_iterator itFile = m_mapFiles.begin(); itFile != m_mapFiles.end(); ++itFile)
{
*pFileEntry = itFile->second;
const char* filename = itFile->first;
pFileEntry->nNameOffset = (ZipFile::ushort)(pName - pNamePool);
size_t nNameLen = strlen(filename);
memcpy (pName, filename, nNameLen + 1);
pName += nNameLen + 1;
++pFileEntry;
}
assert ((const char*)pFileEntry == pNamePool);
// now the name pool is full. Go on and fill the other directories
const char* pSubdirHeader = (const char*)(((UINT_PTR)(pName + 3)) & ~3);
#ifdef _TEST_
g_nSF += pDirHeader->numFiles * sizeof(FileEntry);
g_nSS += pDirHeader->numDirs * sizeof(DirEntry);
g_nSN += pName - pNamePool;
g_nSNa += pSubdirHeader - pNamePool;
g_nSH += sizeof(DirHeader);
#endif
pDirEntry = pDirEntries;
for (itDir = m_mapDirs.begin(); itDir != m_mapDirs.end(); ++itDir)
{
pDirEntry->nDirHeaderOffset = (ZipFile::ulong)(pSubdirHeader - (const char*)pDirEntry);
pSubdirHeader += itDir->second->Serialize ((DirHeader*)pSubdirHeader);
++pDirEntry;
}
return pSubdirHeader - (const char*)pDirHeader;
}
void ZipDir::FileEntryTree::Clear()
{
for (SubdirMap::iterator it = m_mapDirs.begin(); it != m_mapDirs.end(); ++it)
{
delete it->second;
}
m_mapDirs.clear();
m_mapFiles.clear();
}
size_t ZipDir::FileEntryTree::GetSize() const
{
size_t nSize = sizeof(*this);
for (SubdirMap::const_iterator itDir = m_mapDirs.begin(); itDir != m_mapDirs.end(); ++itDir)
{
nSize += strlen(itDir->first) + sizeof(*itDir) + itDir->second->GetSize();
}
for (FileMap::const_iterator itFile = m_mapFiles.begin(); itFile != m_mapFiles.end(); ++itFile)
{
nSize += strlen(itFile->first) + sizeof(*itFile);
}
return nSize;
}
size_t ZipDir::FileEntryTree::GetCompressedFileSize() const
{
size_t nSize = 0;
for (SubdirMap::const_iterator itDir = m_mapDirs.begin(); itDir != m_mapDirs.end(); ++itDir)
{
nSize += itDir->second->GetCompressedFileSize();
}
for (FileMap::const_iterator itFile = m_mapFiles.begin(); itFile != m_mapFiles.end(); ++itFile)
{
nSize += itFile->second.desc.lSizeCompressed;
}
return nSize;
}
size_t ZipDir::FileEntryTree::GetUncompressedFileSize() const
{
size_t nSize = 0;
for (SubdirMap::const_iterator itDir = m_mapDirs.begin(); itDir != m_mapDirs.end(); ++itDir)
{
nSize += itDir->second->GetUncompressedFileSize();
}
for (FileMap::const_iterator itFile = m_mapFiles.begin(); itFile != m_mapFiles.end(); ++itFile)
{
nSize += itFile->second.desc.lSizeUncompressed;
}
return nSize;
}
bool ZipDir::FileEntryTree::IsOwnerOf (const FileEntry* pFileEntry) const
{
for (FileMap::const_iterator itFile = m_mapFiles.begin(); itFile != m_mapFiles.end(); ++itFile)
{
if (pFileEntry == &itFile->second)
{
return true;
}
}
for (SubdirMap::const_iterator itDir = m_mapDirs.begin(); itDir != m_mapDirs.end(); ++itDir)
{
if (itDir->second->IsOwnerOf (pFileEntry))
{
return true;
}
}
return false;
}
ZipDir::FileEntryTree* ZipDir::FileEntryTree::FindDir(const char* szDirName)
{
SubdirMap::iterator it = m_mapDirs.find (szDirName);
if (it == m_mapDirs.end())
{
return NULL;
}
else
{
return it->second;
}
}
ZipDir::FileEntryTree::FileMap::iterator ZipDir::FileEntryTree::FindFile (const char* szFileName)
{
return m_mapFiles.find (szFileName);
}
ZipDir::FileEntry* ZipDir::FileEntryTree::GetFileEntry(FileMap::iterator it)
{
return it == GetFileEnd() ? NULL : &it->second;
}
ZipDir::FileEntryTree* ZipDir::FileEntryTree::GetDirEntry(SubdirMap::iterator it)
{
return it == GetDirEnd() ? NULL : it->second;
}
const ZipDir::FileEntry* ZipDir::FileEntryTree::GetFileEntry(FileMap::const_iterator it) const
{
return it == GetFileEnd() ? NULL : &it->second;
}
const ZipDir::FileEntryTree* ZipDir::FileEntryTree::GetDirEntry(SubdirMap::const_iterator it) const
{
return it == GetDirEnd() ? NULL : it->second;
}
ZipDir::ErrorEnum ZipDir::FileEntryTree::RemoveDir (const char* szDirName)
{
SubdirMap::iterator itRemove = m_mapDirs.find (szDirName);
if (itRemove == m_mapDirs.end())
{
return ZD_ERROR_FILE_NOT_FOUND;
}
delete itRemove->second;
m_mapDirs.erase (itRemove);
return ZD_ERROR_SUCCESS;
}
ZipDir::ErrorEnum ZipDir::FileEntryTree::RemoveFile (const char* szFileName)
{
FileMap::iterator itRemove = m_mapFiles.find (szFileName);
if (itRemove == m_mapFiles.end())
{
return ZD_ERROR_FILE_NOT_FOUND;
}
m_mapFiles.erase (itRemove);
return ZD_ERROR_SUCCESS;
}
size_t ZipDir::FileEntryTree::NumDirsTotal() const
{
size_t result = m_mapDirs.size();
SubdirMap::const_iterator it;
for (it = m_mapDirs.begin(); it != m_mapDirs.end(); ++it)
{
result += it->second->NumDirsTotal();
}
return result;
}
@@ -0,0 +1,103 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRTREE_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRTREE_H
#pragma once
namespace ZipDir
{
class FileEntryTree
{
public:
FileEntryTree()
: m_originalName(0) {}
FileEntryTree(const char* originalName)
: m_originalName(originalName) {}
~FileEntryTree () {Clear(); }
// adds a file to this directory
// Function can modify szPath input
ErrorEnum Add (char* szPath, char* szUnifiedPath, const FileEntry& file);
// Adds or finds the file. Returns non-initialized structure if it was added,
// or an IsInitialized() structure if it was found
// Function can modify szPath input
FileEntry* Add (char* szPath, char* szUnifiedPath);
// returns the number of files in this tree, including this and sublevels
unsigned NumFilesTotal() const;
// returns the size required to serialize the tree
size_t GetSizeSerialized() const;
// serializes into the memory
size_t Serialize (DirHeader* pDir) const;
void Clear();
void Swap (FileEntryTree& rThat)
{
m_mapDirs.swap (rThat.m_mapDirs);
m_mapFiles.swap (rThat.m_mapFiles);
}
size_t GetSize() const;
size_t GetCompressedFileSize() const;
size_t GetUncompressedFileSize() const;
bool IsOwnerOf (const FileEntry* pFileEntry) const;
// subdirectories
typedef std::map<const char*, FileEntryTree*, stl::less_strcmp<const char*> > SubdirMap;
// file entries
typedef std::map<const char*, FileEntry, stl::less_strcmp<const char*> > FileMap;
FileEntryTree* FindDir(const char* szDirName);
ErrorEnum RemoveDir (const char* szDirName);
ErrorEnum RemoveAll (){Clear(); return ZD_ERROR_SUCCESS; }
FileEntry* FindFileEntry (const char* szFileName);
FileMap::iterator FindFile (const char* szFileName);
ErrorEnum RemoveFile (const char* szFileName);
FileEntryTree* GetDirectory(){return this; } // the FileENtryTree is simultaneously an entry in the dir list AND the directory header
FileMap::iterator GetFileBegin() {return m_mapFiles.begin(); }
FileMap::iterator GetFileEnd() {return m_mapFiles.end(); }
FileMap::const_iterator GetFileBegin() const {return m_mapFiles.begin(); }
FileMap::const_iterator GetFileEnd() const {return m_mapFiles.end(); }
unsigned NumFiles() const {return (unsigned)m_mapFiles.size(); }
SubdirMap::iterator GetDirBegin() {return m_mapDirs.begin(); }
SubdirMap::iterator GetDirEnd() {return m_mapDirs.end(); }
SubdirMap::const_iterator GetDirBegin() const {return m_mapDirs.begin(); }
SubdirMap::const_iterator GetDirEnd() const {return m_mapDirs.end(); }
size_t NumDirsTotal() const;
const char* GetFileName(FileMap::iterator it) {return it->first; }
const char* GetDirName(SubdirMap::iterator it) {return it->first; }
const char* GetOriginalName() const{ return m_originalName; }
FileEntry* GetFileEntry(FileMap::iterator it);
FileEntryTree* GetDirEntry(SubdirMap::iterator it);
const FileEntry* GetFileEntry(FileMap::const_iterator it) const;
const FileEntryTree* GetDirEntry(SubdirMap::const_iterator it) const;
protected:
SubdirMap m_mapDirs;
FileMap m_mapFiles;
const char* m_originalName;
};
}
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRTREE_H
@@ -0,0 +1,19 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPFILE_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPFILE_H
#pragma once
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPFILE_H
@@ -0,0 +1,388 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPFILEFORMAT_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPFILEFORMAT_H
#pragma once
#include <platform.h>
#include <ZipDir/ZipDir_Traits_Platform.h>
#if AZ_TRAIT_CRYCOMMONTOOLS_PACK_1
#pragma pack(push)
#pragma pack(1)
#define PACK_GCC
#else
#define PACK_GCC __PACKED
#endif
namespace ZipFile
{
typedef unsigned int ulong;
typedef unsigned short ushort;
// General-purpose bit field flags
enum
{
GPF_ENCRYPTED = 1 << 0, // If set, indicates that the file is encrypted.
GPF_DATA_DESCRIPTOR = 1 << 3, // if set, the CRC32 and sizes aren't set in the file header, but only in the data descriptor following compressed data
GPF_RESERVED_8_ENHANCED_DEFLATING = 1 << 4, // Reserved for use with method 8, for enhanced deflating.
GPF_COMPRESSED_PATCHED = 1 << 5, // the file is compressed patched data
};
// compression methods
enum
{
METHOD_STORE = 0, // The file is stored (no compression)
METHOD_SHRINK = 1, // The file is Shrunk
METHOD_REDUCE_1 = 2, // The file is Reduced with compression factor 1
METHOD_REDUCE_2 = 3, // The file is Reduced with compression factor 2
METHOD_REDUCE_3 = 4, // The file is Reduced with compression factor 3
METHOD_REDUCE_4 = 5, // The file is Reduced with compression factor 4
METHOD_IMPLODE = 6, // The file is Imploded
METHOD_TOKENIZE = 7, // Reserved for Tokenizing compression algorithm
METHOD_DEFLATE = 8, // The file is Deflated
METHOD_DEFLATE64 = 9, // Enhanced Deflating using Deflate64(tm)
METHOD_IMPLODE_PKWARE = 10, // PKWARE Date Compression Library Imploding
METHOD_DEFLATE_AND_ENCRYPT = 11 // Deflate + Custom encryption
};
// version numbers
enum
{
VERSION_DEFAULT = 10, // Default value
VERSION_TYPE_VOLUMELABEL = 11, // File is a volume label
VERSION_TYPE_FOLDER = 20, // File is a folder (directory)
VERSION_TYPE_PATCHDATASET = 27, // File is a patch data set
VERSION_TYPE_ZIP64 = 45, // File uses ZIP64 format extensions
VERSION_COMPRESSION_DEFLATE = 20, // File is compressed using Deflate compression
VERSION_COMPRESSION_DEFLATE64 = 21, // File is compressed using Deflate64(tm)
VERSION_COMPRESSION_DCLIMPLODE = 25, // File is compressed using PKWARE DCL Implode
VERSION_COMPRESSION_BZIP2 = 46, // File is compressed using BZIP2 compression*
VERSION_COMPRESSION_LZMA = 63, // File is compressed using LZMA
VERSION_COMPRESSION_PPMD = 63, // File is compressed using PPMd+
VERSION_ENCRYPTION_PKWARE = 20, // File is encrypted using traditional PKWARE encryption
VERSION_ENCRYPTION_DES = 50, // File is encrypted using DES
VERSION_ENCRYPTION_3DES = 50, // File is encrypted using 3DES
VERSION_ENCRYPTION_RC2 = 50, // File is encrypted using original RC2 encryption
VERSION_ENCRYPTION_RC4 = 50, // File is encrypted using RC4 encryption
VERSION_ENCRYPTION_AES = 51, // File is encrypted using AES encryption
VERSION_ENCRYPTION_RC2C = 51, // File is encrypted using corrected RC2 encryption**
VERSION_ENCRYPTION_RC4C = 52, // File is encrypted using corrected RC2-64 encryption**
VERSION_ENCRYPTION_NOOAEP = 61, // File is encrypted using non-OAEP key wrapping***
VERSION_ENCRYPTION_CDR = 62, // Central directory encryption
VERSION_ENCRYPTION_BLOWFISH = 63, // File is encrypted using Blowfish
VERSION_ENCRYPTION_TWOFISH = 63, // File is encrypted using Twofish
};
// creator numbers
enum
{
CREATOR_MSDOS = 0, // MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)
CREATOR_AMIGA = 1, // Amiga
CREATOR_OpenVMS = 2, // OpenVMS
CREATOR_UNIX = 3, // UNIX
CREATOR_VM = 4, // VM/CMS
CREATOR_ATARI = 5, // Atari ST
CREATOR_OS2 = 6, // OS/2 H.P.F.S.
CREATOR_MACINTOSH = 7, // Macintosh
CREATOR_ZSYSTEM = 8, // Z-System
CREATOR_CPM = 9, // CP/M
CREATOR_WINDOWS = 10, // Windows NTFS
CREATOR_MVS = 11, // MVS (OS/390 - Z/OS)
CREATOR_VSE = 12, // VSE
CREATOR_ACORN = 13, // Acorn Risc
CREATOR_VFAT = 14, // VFAT
CREATOR_AMVS = 15, // alternate MVS
CREATOR_BEOS = 16, // BeOS
CREATOR_TANDEM = 17, // Tandem
CREATOR_OS400 = 18, // OS/400
CREATOR_OSX = 19, // OS X (Darwin)
CREATOR_UNUSED = 20, // 20 thru 255 - unused
};
enum
{
ZIP64_SEE_EXTENSION = -1 // If an archive is in ZIP64 format
// and a value in a field is 0xFFFFFFFF (or 0xFFFF), the size will be
// in the corresponding 8 byte (or 4 byte) ZIP64 extended information.
};
// end of Central Directory Record
// followed by the .zip file comment (variable size, can be empty, obtained from nCommentLength)
struct CDREnd
{
enum
{
SIGNATURE = 0x06054b50
};
ulong lSignature; // end of central dir signature 4 bytes (0x06054b50)
ushort nDisk; // number of this disk 2 bytes
ushort nCDRStartDisk; // number of the disk with the start of the central directory 2 bytes
ushort numEntriesOnDisk; // total number of entries in the central directory on this disk 2 bytes
ushort numEntriesTotal; // total number of entries in the central directory 2 bytes
ulong lCDRSize; // size of the central directory 4 bytes
ulong lCDROffset; // offset of start of central directory with respect to the starting disk number 4 bytes
ushort nCommentLength; // .ZIP file comment length 2 bytes
AUTO_STRUCT_INFO
// .ZIP file comment (variable size, can be empty) follows
} PACK_GCC;
// end of Central Directory Record
// followed by the zip64 extensible data sector (variable size, can be empty, obtained from nExtDataLength)
struct CDREnd_ZIP64
{
enum
{
SIGNATURE = 0x06064b50
};
ulong lSignature; // end of central dir signature 4 bytes (0x06064b50)
uint64 nExtDataLength; // The value stored into the "size of zip64 end of central directory record" should be the size of the remaining record and should not include the leading 12 bytes. 8 bytes
ushort nVersionMadeBy; // version made by 2 bytes
ushort nVersionNeeded; // version needed to extract 2 bytes
ulong nDisk; // number of this disk 4 bytes
ulong nCDRStartDisk; // number of the disk with the start of the central directory 4 bytes
uint64 numEntriesOnDisk; // total number of entries in the central directory on this disk 8 bytes
uint64 numEntriesTotal; // total number of entries in the central directory 8 bytes
uint64 lCDRSize; // size of the central directory 8 bytes
uint64 lCDROffset; // offset of start of central directory with respect to the starting disk number 8 bytes
AUTO_STRUCT_INFO
// zip64 extensible data sector (variable size, can be empty) follows
} PACK_GCC;
// end of Central Directory Locator
struct CDRLocator_ZIP64
{
enum
{
SIGNATURE = 0x07064b50
};
ulong lSignature; // end of central loc signature 4 bytes (0x07064b50)
ulong nCDR64StartDisk; // number of the disk with the start of the zip64 end of central directory 4 bytes
uint64 lCDR64EndOffset; // relative offset of the zip64 end of central directory record 8 bytes
ulong nDisks; // number of disks 4 bytes
AUTO_STRUCT_INFO
} PACK_GCC;
// This descriptor exists only if bit 3 of the general
// purpose bit flag is set (see below). It is byte aligned
// and immediately follows the last byte of compressed data.
// This descriptor is used only when it was not possible to
// seek in the output .ZIP file, e.g., when the output .ZIP file
// was standard output or a non seekable device. For Zip64 format
// archives, the compressed and uncompressed sizes are 8 bytes each.
struct DataDescriptor
{
ulong lCRC32; // crc-32 4 bytes
ulong lSizeCompressed; // compressed size 4 bytes
ulong lSizeUncompressed; // uncompressed size 4 bytes
bool operator == (const DataDescriptor& d) const
{
return lCRC32 == d.lCRC32 && lSizeCompressed == d.lSizeCompressed && lSizeUncompressed == d.lSizeUncompressed;
}
bool operator != (const DataDescriptor& d) const
{
return lCRC32 != d.lCRC32 || lSizeCompressed != d.lSizeCompressed || lSizeUncompressed != d.lSizeUncompressed;
}
bool IsZIP64([[maybe_unused]] const DataDescriptor& d) const
{
return lSizeCompressed == (ulong)ZIP64_SEE_EXTENSION || lSizeUncompressed == (ulong)ZIP64_SEE_EXTENSION;
}
AUTO_STRUCT_INFO
} PACK_GCC;
// When compressing files, compressed and uncompressed sizes
// should be stored in ZIP64 format (as 8 byte values) when a
// file's size exceeds 0xFFFFFFFF. However ZIP64 format may be
// used regardless of the size of a file. When extracting, if
// the zip64 extended information extra field is present for
// the file the compressed and uncompressed sizes will be 8
// byte values.
struct DataDescriptor_ZIP64
{
ulong lCRC32; // crc-32 4 bytes
uint64 lSizeCompressed; // compressed size 8 bytes
uint64 lSizeUncompressed; // uncompressed size 8 bytes
bool operator == (const DataDescriptor& d) const
{
return lCRC32 == d.lCRC32 && lSizeCompressed == d.lSizeCompressed && lSizeUncompressed == d.lSizeUncompressed;
}
bool operator != (const DataDescriptor& d) const
{
return lCRC32 != d.lCRC32 || lSizeCompressed != d.lSizeCompressed || lSizeUncompressed != d.lSizeUncompressed;
}
AUTO_STRUCT_INFO
} PACK_GCC;
// the File Header as it appears in the CDR
// followed by:
// file name (variable size)
// extra field (variable size)
// file comment (variable size)
struct CDRFileHeader
{
enum
{
SIGNATURE = 0x02014b50
};
ulong lSignature; // central file header signature 4 bytes (0x02014b50)
ushort nVersionMadeBy; // version made by 2 bytes
ushort nVersionNeeded; // version needed to extract 2 bytes
ushort nFlags; // general purpose bit flag 2 bytes
ushort nMethod; // compression method 2 bytes
ushort nLastModTime; // last mod file time 2 bytes
ushort nLastModDate; // last mod file date 2 bytes
DataDescriptor desc;
ushort nFileNameLength; // file name length 2 bytes
ushort nExtraFieldLength; // extra field length 2 bytes
ushort nFileCommentLength; // file comment length 2 bytes
ushort nDiskNumberStart; // disk number start 2 bytes
ushort nAttrInternal; // internal file attributes 2 bytes
ulong lAttrExternal; // external file attributes 4 bytes
// This is the offset from the start of the first disk on
// which this file appears, to where the local header should
// be found. If an archive is in zip64 format and the value
// in this field is 0xFFFFFFFF, the size will be in the
// corresponding 8 byte zip64 extended information extra field.
enum
{
ZIP64_LOCAL_HEADER_OFFSET = 0xFFFFFFFF
};
ulong lLocalHeaderOffset; // relative offset of local header 4 bytes
bool IsZIP64([[maybe_unused]] const CDRFileHeader& d) const
{
return desc.IsZIP64(desc) || nDiskNumberStart == (ushort)ZIP64_SEE_EXTENSION || lLocalHeaderOffset == (ulong)ZIP64_SEE_EXTENSION;
}
AUTO_STRUCT_INFO
} PACK_GCC;
// this is the local file header that appears before the compressed data
// followed by:
// file name (variable size)
// extra field (variable size)
struct LocalFileHeader
{
enum
{
SIGNATURE = 0x04034b50
};
ulong lSignature; // local file header signature 4 bytes (0x04034b50)
ushort nVersionNeeded; // version needed to extract 2 bytes
ushort nFlags; // general purpose bit flag 2 bytes
ushort nMethod; // compression method 2 bytes
ushort nLastModTime; // last mod file time 2 bytes
ushort nLastModDate; // last mod file date 2 bytes
DataDescriptor desc;
ushort nFileNameLength; // file name length 2 bytes
ushort nExtraFieldLength; // extra field length 2 bytes
bool IsZIP64([[maybe_unused]] const LocalFileHeader& d) const
{
return desc.IsZIP64(desc);
}
AUTO_STRUCT_INFO
} PACK_GCC;
// compression methods
enum EExtraHeaderID
{
EXTRA_ZIP64 = 0x0001, // ZIP64 extended information extra field
EXTRA_NTFS = 0x000a, // NTFS
EXTRA_UNIX = 0x000d, // UNIX
EXTRA_PATCH = 0x000f, // Patch Descriptor
};
//////////////////////////////////////////////////////////////////////////
// header1+data1 + header2+data2 . . .
// Each header should consist of:
// Header ID - 2 bytes
// Data Size - 2 bytes
struct ExtraFieldHeader
{
ushort headerID;
ushort dataSize;
AUTO_STRUCT_INFO
} PACK_GCC;
struct ExtraNTFSHeader
{
ulong reserved; // 4 bytes.
ushort attrTag; // 2 bytes.
ushort attrSize; // 2 bytes.
AUTO_STRUCT_INFO
} PACK_GCC;
//////////////////////////////////////////////////////////////////////////
// The following is the layout of the zip64 extended
// information "extra" block. If one of the size or
// offset fields in the Local or Central directory
// record is too small to hold the required data,
// a Zip64 extended information record is created.
// The order of the fields in the zip64 extended
// information record is fixed, but the fields MUST
// only appear if the corresponding Local or Central
// directory record field is set to 0xFFFF or 0xFFFFFFFF.
//
// The extended information in the Local header MUST include
// BOTH original and compressed file size fields.
struct ExtraZIP64LocalFileHeader
{
// LocalFileHeader overrides
uint64 lSizeUncompressed; // uncompressed size 4->8 bytes
uint64 lSizeCompressed; // compressed size 4->8 bytes
AUTO_STRUCT_INFO
} PACK_GCC;
struct ExtraZIP64CDRFileHeader
{
// CDRFileHeader overrides
uint64 lSizeUncompressed; // uncompressed size 4->8 bytes
uint64 lSizeCompressed; // compressed size 4->8 bytes
uint64 lLocalHeaderOffset; // relative offset of local header 4->8 bytes
ulong nDiskNumberStart; // Number of the disk on which this file starts 2->4 bytes
AUTO_STRUCT_INFO
} PACK_GCC;
}
#undef PACK_GCC
#if AZ_TRAIT_CRYCOMMONTOOLS_PACK_1
#pragma pack(pop)
#endif
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPFILEFORMAT_H
@@ -0,0 +1,112 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPFILEFORMAT_INFO_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPFILEFORMAT_INFO_H
#pragma once
#include "ZipFileFormat.h"
STRUCT_INFO_BEGIN(ZipFile::CDREnd)
STRUCT_VAR_INFO(lSignature, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(nDisk, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nCDRStartDisk, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(numEntriesOnDisk, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(numEntriesTotal, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(lCDRSize, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(lCDROffset, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(nCommentLength, TYPE_INFO(ZipFile::ushort))
STRUCT_INFO_END(ZipFile::CDREnd)
STRUCT_INFO_BEGIN(ZipFile::CDREnd_ZIP64)
STRUCT_VAR_INFO(lSignature, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(nExtDataLength, TYPE_INFO(ZipFile::uint64))
STRUCT_VAR_INFO(nVersionMadeBy, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nVersionNeeded, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nDisk, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(nCDRStartDisk, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(numEntriesOnDisk, TYPE_INFO(ZipFile::uint64))
STRUCT_VAR_INFO(numEntriesTotal, TYPE_INFO(ZipFile::uint64))
STRUCT_VAR_INFO(lCDRSize, TYPE_INFO(ZipFile::uint64))
STRUCT_VAR_INFO(lCDROffset, TYPE_INFO(ZipFile::uint64))
STRUCT_INFO_END(ZipFile::CDREnd_ZIP64)
STRUCT_INFO_BEGIN(ZipFile::CDRLocator_ZIP64)
STRUCT_VAR_INFO(lSignature, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(nCDR64StartDisk, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(lCDR64EndOffset, TYPE_INFO(ZipFile::uint64))
STRUCT_VAR_INFO(nDisks, TYPE_INFO(ZipFile::ulong))
STRUCT_INFO_END(ZipFile::CDRLocator_ZIP64)
STRUCT_INFO_BEGIN(ZipFile::DataDescriptor)
STRUCT_VAR_INFO(lCRC32, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(lSizeCompressed, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(lSizeUncompressed, TYPE_INFO(ZipFile::ulong))
STRUCT_INFO_END(ZipFile::DataDescriptor)
STRUCT_INFO_BEGIN(ZipFile::DataDescriptor_ZIP64)
STRUCT_VAR_INFO(lCRC32, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(lSizeCompressed, TYPE_INFO(ZipFile::uint64))
STRUCT_VAR_INFO(lSizeUncompressed, TYPE_INFO(ZipFile::uint64))
STRUCT_INFO_END(ZipFile::DataDescriptor_ZIP64)
STRUCT_INFO_BEGIN(ZipFile::CDRFileHeader)
STRUCT_VAR_INFO(lSignature, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(nVersionMadeBy, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nVersionNeeded, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nFlags, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nMethod, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nLastModTime, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nLastModDate, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(desc, TYPE_INFO(ZipFile::DataDescriptor))
STRUCT_VAR_INFO(nFileNameLength, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nExtraFieldLength, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nFileCommentLength, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nDiskNumberStart, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nAttrInternal, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(lAttrExternal, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(lLocalHeaderOffset, TYPE_INFO(ZipFile::ulong))
STRUCT_INFO_END(ZipFile::CDRFileHeader)
STRUCT_INFO_BEGIN(ZipFile::LocalFileHeader)
STRUCT_VAR_INFO(lSignature, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(nVersionNeeded, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nFlags, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nMethod, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nLastModTime, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nLastModDate, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(desc, TYPE_INFO(ZipFile::DataDescriptor))
STRUCT_VAR_INFO(nFileNameLength, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(nExtraFieldLength, TYPE_INFO(ZipFile::ushort))
STRUCT_INFO_END(ZipFile::LocalFileHeader)
STRUCT_INFO_BEGIN(ZipFile::ExtraFieldHeader)
STRUCT_VAR_INFO(headerID, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(dataSize, TYPE_INFO(ZipFile::ushort))
STRUCT_INFO_END(ZipFile::ExtraFieldHeader)
STRUCT_INFO_BEGIN(ZipFile::ExtraNTFSHeader)
STRUCT_VAR_INFO(reserved, TYPE_INFO(ZipFile::ulong))
STRUCT_VAR_INFO(attrTag, TYPE_INFO(ZipFile::ushort))
STRUCT_VAR_INFO(attrSize, TYPE_INFO(ZipFile::ushort))
STRUCT_INFO_END(ZipFile::ExtraNTFSHeader)
STRUCT_INFO_BEGIN(ZipFile::ExtraZIP64Data)
STRUCT_VAR_INFO(lSizeUncompressed, TYPE_INFO(ZipFile::uint64))
STRUCT_VAR_INFO(lSizeCompressed, TYPE_INFO(ZipFile::uint64))
STRUCT_VAR_INFO(lLocalHeaderOffset, TYPE_INFO(ZipFile::uint64))
STRUCT_VAR_INFO(nDiskNumberStart, TYPE_INFO(ZipFile::ulong))
STRUCT_INFO_END(ZipFile::ExtraZIP64Data)
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPFILEFORMAT_INFO_H
@@ -0,0 +1,426 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// This file contains only the support definitions for CZipDir class
// implementation. This it to unload the ZipDir.h from secondary stuff.
#ifndef CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRSTRUCTURES_H
#define CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRSTRUCTURES_H
#pragma once
#include <AzFramework/Archive/Codec.h>
namespace ZipDir
{
// possible errors occuring during the method execution
// to avoid clushing with the global Windows defines, we prefix these with ZD_
enum ErrorEnum
{
ZD_ERROR_SUCCESS = 0,
ZD_ERROR_IO_FAILED,
ZD_ERROR_UNEXPECTED,
ZD_ERROR_UNSUPPORTED,
ZD_ERROR_INVALID_SIGNATURE,
ZD_ERROR_ZIP_FILE_IS_CORRUPT,
ZD_ERROR_DATA_IS_CORRUPT,
ZD_ERROR_NO_CDR,
ZD_ERROR_CDR_IS_CORRUPT,
ZD_ERROR_NO_MEMORY,
ZD_ERROR_VALIDATION_FAILED,
ZD_ERROR_CRC32_CHECK,
ZD_ERROR_ZLIB_FAILED,
ZD_ERROR_ZLIB_CORRUPTED_DATA,
ZD_ERROR_ZLIB_NO_MEMORY,
ZD_ERROR_CORRUPTED_DATA,
ZD_ERROR_INVALID_CALL,
ZD_ERROR_NOT_IMPLEMENTED,
ZD_ERROR_FILE_NOT_FOUND,
ZD_ERROR_DIR_NOT_FOUND,
ZD_ERROR_NAME_TOO_LONG,
ZD_ERROR_INVALID_PATH,
ZD_ERROR_FILE_ALREADY_EXISTS
};
// the error describes the reason of the error, as well as the error code, line of code where it happened etc.
struct Error
{
Error(ErrorEnum _nError, const char* _szDescription, const char* _szFunction, const char* _szFile, unsigned _nLine)
: nError(_nError)
, m_szDescription(_szDescription)
, szFunction(_szFunction)
, szFile(_szFile)
, nLine(_nLine)
{
}
ErrorEnum nError;
const char* getError();
const char* getDescription() {return m_szDescription; }
const char* szFunction, * szFile;
unsigned nLine;
protected:
// the description of the error; if needed, will be made as a dynamic string
const char* m_szDescription;
};
//#define THROW_ZIPDIR_ERROR(ZD_ERR,DESC) throw Error (ZD_ERR, DESC, __FUNCTION__, __FILE__, __LINE__)
//#define THROW_ZIPDIR_ERROR(ZD_ERR,DESC) CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,DESC )
#define THROW_ZIPDIR_ERROR(ZD_ERR, DESC)
struct EncryptionKey
{
uint32 key[4];
explicit EncryptionKey(const uint32 data[4])
{
memcpy(key, data, sizeof(key));
}
EncryptionKey()
{
memset(key, 0, sizeof(key));
}
};
// possible initialization methods
enum InitMethodEnum
{
// initialize as fast as possible, with minimal validation
ZD_INIT_FAST,
// after initialization, scan through all file headers, precache the actual file data offset values and validate the headers
ZD_INIT_FULL,
// scan all file headers and try to decompress the data, searching for corrupted files
ZD_INIT_VALIDATE,
// maximum level of validation, checks for integrity of the archive
ZD_INIT_VALIDATE_MAX = ZD_INIT_VALIDATE
};
typedef void* (* FnAlloc) (void* pUserData, unsigned nItems, unsigned nSize);
typedef void (* FnFree) (void* pUserData, void* pAddress);
//////////////////////////////////////////////////////////////////////////
// This structure contains the pointers to functions for memory management
// by default, it's initialized to default malloc/free
#if 0
struct Allocator
{
FnAlloc fnAlloc;
FnFree fnFree;
void* pOpaque;
static void* DefaultAlloc (void*, unsigned nItems, unsigned nSize)
{
return malloc (nItems * nSize);
}
static void DefaultFree (void*, void* pAddress)
{
free (pAddress);
}
void* Alloc (unsigned nItems, unsigned nSize)
{
return this->fnAlloc(this->pOpaque, nItems, nSize);
}
void Free (void* pAddress)
{
this->fnFree (this->pOpaque, pAddress);
}
// constructs the allocator object; by default, the stdlib functions are used
Allocator (FnAlloc fnAllocIn = DefaultAlloc, FnFree fnFreeIn = DefaultFree, void* pOpaqueIn = NULL)
: fnAlloc(fnAllocIn)
, fnFree (fnFreeIn)
, pOpaque(pOpaqueIn)
{
}
};
#endif
// instance of this class just releases the memory when it's destructed
struct SmartHeapPtr
{
SmartHeapPtr()
: m_pAddress(NULL)
{
}
~SmartHeapPtr()
{
Release();
}
void Attach (void* p)
{
Release();
m_pAddress = p;
}
void* Detach()
{
void* p = m_pAddress;
m_pAddress = NULL;
return p;
}
void Release()
{
if (m_pAddress)
{
free(m_pAddress);
m_pAddress = NULL;
}
}
protected:
// the pointer to free
void* m_pAddress;
};
typedef SmartHeapPtr SmartPtr;
// 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)
extern int ZipRawUncompress (void* pUncompressed, unsigned long* pDestSize, const void* pCompressed, unsigned long nSrcSize);
// 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), and the size in *pDestSize. the pCompressed buffer must be at least nSrcSize*1.001+12 size
extern int ZipRawCompress (const void* pUncompressed, unsigned long* pDestSize, void* pCompressed, unsigned long nSrcSize, int nLevel);
extern int ZipRawCompressZSTD(const void* pUncompressed, unsigned long* pDestSize, void* pCompressed, unsigned long nSrcSize, int nLevel);
extern int ZipRawCompressLZ4(const void* pUncompressed, unsigned long* pDestSize, void* pCompressed, unsigned long nSrcSize, int nLevel);
//returns an estimate of the size of the data when compressed
extern int GetCompressedSizeEstimate(unsigned long uncompressedSize, CompressionCodec::Codec codec = CompressionCodec::Codec::ZLIB);
enum class ValidationResult
{
OK = 0,
SIZE_MISMATCH,
DATA_CORRUPTED,
DATA_NO_MATCH
};
//decompresses a zstd blob and compares with the original - returns true if original and uncompressed data match
ValidationResult ValidateZSTDCompressedDataWithOriginalData(const void* pUncompressed, unsigned long uncompressedSize, const void* pCompressed, unsigned long compressedSize);
//////////////////////////////////////////////////////////////////////////
struct SExtraZipFileData
{
SExtraZipFileData()
: nLastModifyTime(0) {}
uint64 nLastModifyTime;
};
// this is the record about the file in the Zip file.
struct FileEntry
{
enum
{
INVALID_DATA_OFFSET = 0xFFFFFFFF
};
ZipFile::DataDescriptor desc;
ZipFile::ulong nFileHeaderOffset; // offset of the local file header
ZipFile::ulong nFileDataOffset; // offset of the packed info inside the file; NOTE: this can be INVALID_DATA_OFFSET, if not calculated yet!
ZipFile::ushort nMethod; // the method of compression (0 if no compression/store)
ZipFile::ushort nNameOffset; // offset of the file name in the name pool for the directory
// the file modification times
ZipFile::ushort nLastModTime;
ZipFile::ushort nLastModDate;
uint64 nNTFS_LastModifyTime;
// the offset to the start of the next file's header - this
// can be used to calculate the available space in zip file
ZipFile::ulong nEOFOffset;
const char* szOriginalFileName; // original filename (for CacheRW)
FileEntry()
: nFileHeaderOffset(INVALID_DATA_OFFSET)
, szOriginalFileName(0){}
FileEntry(const ZipFile::CDRFileHeader& header, const SExtraZipFileData& extra);
bool IsInitialized ()
{
// structure marked as non-initialized should have nFileHeaderOffset == INVALID_DATA_OFFSET
return nFileHeaderOffset != INVALID_DATA_OFFSET;
}
// returns the name of this file, given the pointer to the name pool
const char* GetName(const char* pNamePool) const
{
return pNamePool + nNameOffset;
}
// sets the current time to modification time
// calculates CRC32 for the new data
void OnNewFileData(void* pUncompressed, unsigned nSize, unsigned nCompressedSize, unsigned nCompressionMethod, bool bContinuous);
uint64 GetModificationTime();
void SetFromFileTimeNTFS(int64 timestamp);
bool CompareFileTimeNTFS(int64 timestamp);
};
// tries to refresh the file entry from the given file (reads fromthere if needed)
// returns the error code if the operation was impossible to complete
extern ErrorEnum Refresh (FILE* f, FileEntry* pFileEntry, bool encrpytedHeaders);
// writes into the file local header - without Extra data
// puts the new offset to the file data to the file entry
// in case of error can put INVALID_DATA_OFFSET into the data offset field of file entry
extern ErrorEnum WriteLocalHeader (FILE* f, FileEntry* pFileEntry, const char* szRelativePath, bool encrypt);
// conversion routines for the date/time fields used in Zip
extern ZipFile::ushort DOSDate(tm*);
extern ZipFile::ushort DOSTime(tm*);
extern const char* DOSTimeCStr(ZipFile::ushort nTime);
extern const char* DOSDateCStr(ZipFile::ushort nTime);
struct DirHeader;
// this structure represents a subdirectory descriptor in the directory record.
// it points to the actual directory info (list of its subdirs and files), as well
// as on its name
struct DirEntry
{
ZipFile::ulong nDirHeaderOffset;// offset, in bytes, relative to this object, of the actual directory record header
ZipFile::ulong nNameOffset; // offset of the dir name in the name pool of the parent directory
// returns the name of this directory, given the pointer to the name pool of hte parent directory
const char* GetName(const char* pNamePool) const
{
return pNamePool + nNameOffset;
}
// returns the pointer to the actual directory record.
// call this function only for the actual structure instance contained in a directory record and
// followed by the other directory records
const DirHeader* GetDirectory () const
{
return (const DirHeader*)(((const char*)this) + nDirHeaderOffset);
}
DirHeader* GetDirectory ()
{
return (DirHeader*)(((char*)this) + nDirHeaderOffset);
}
};
// this is the head of the directory record
// the name pool follows straight the directory and file entries.
struct DirHeader
{
ZipFile::ushort numDirs; // number of directory entries - DirEntry structures
ZipFile::ushort numFiles; // number of file entries - FileEntry structures
// returns the pointer to the name pool that follows this object
// you can only call this method for the structure instance actually followed by the dir record
const char* GetNamePool() const
{
return ((char*)(this + 1)) + (size_t)this->numDirs * sizeof(DirEntry) + (size_t)this->numFiles * sizeof(FileEntry);
}
char* GetNamePool()
{
return ((char*)(this + 1)) + (size_t)this->numDirs * sizeof(DirEntry) + (size_t)this->numFiles * sizeof(FileEntry);
}
// returns the pointer to the i-th directory
// call this only for the actual instance of the structure at the head of dir record
const DirEntry* GetSubdirEntry(unsigned i) const
{
assert (i < numDirs);
return ((const DirEntry*)(this + 1)) + i;
}
DirEntry* GetSubdirEntry(unsigned i)
{
assert (i < numDirs);
return ((DirEntry*)(this + 1)) + i;
}
// returns the pointer to the i-th file
// call this only for the actual instance of the structure at the head of dir record
const FileEntry* GetFileEntry (unsigned i) const
{
assert (i < numFiles);
return (const FileEntry*)(((const DirEntry*)(this + 1)) + numDirs) + i;
}
FileEntry* GetFileEntry (unsigned i)
{
assert (i < numFiles);
return (FileEntry*)(((DirEntry*)(this + 1)) + numDirs) + i;
}
// finds the subdirectory entry by the name, using the names from the name pool
// assumes: all directories are sorted in alphabetical order.
// case-sensitive (must be lower-case if case-insensitive search in Win32 is performed)
DirEntry* FindSubdirEntry(const char* szName);
// finds the file entry by the name, using the names from the name pool
// assumes: all directories are sorted in alphabetical order.
// case-sensitive (must be lower-case if case-insensitive search in Win32 is performed)
FileEntry* FindFileEntry(const char* szName);
};
// this is the sorting predicate for directory entries
struct DirEntrySortPred
{
DirEntrySortPred (const char* pNamePool)
: m_pNamePool (pNamePool)
{
}
bool operator () (const FileEntry& left, const FileEntry& right) const
{
return strcmp(left.GetName(m_pNamePool), right.GetName(m_pNamePool)) < 0;
}
bool operator () (const FileEntry& left, const char* szRight) const
{
return strcmp(left.GetName(m_pNamePool), szRight) < 0;
}
bool operator () (const char* szLeft, const FileEntry& right) const
{
return strcmp(szLeft, right.GetName(m_pNamePool)) < 0;
}
bool operator () (const DirEntry& left, const DirEntry& right) const
{
return strcmp(left.GetName(m_pNamePool), right.GetName(m_pNamePool)) < 0;
}
bool operator () (const DirEntry& left, const char* szName) const
{
return strcmp(left.GetName(m_pNamePool), szName) < 0;
}
bool operator () (const char* szLeft, const DirEntry& right) const
{
return strcmp(szLeft, right.GetName(m_pNamePool)) < 0;
}
const char* m_pNamePool;
};
inline void tolower (string& str)
{
for (size_t i = 0; i < str.length(); ++i)
{
const_cast<char&>(str[i]) = ::tolower(str[i]);
}
}
void Encrypt(char* buffer, size_t size, const EncryptionKey& key);
void Decrypt(char* buffer, size_t size, const EncryptionKey& key);
}
#endif // CRYINCLUDE_CRYCOMMONTOOLS_ZIPDIR_ZIPDIRSTRUCTURES_H