1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
4 #include <CryCore/smartptr.h>
6 #include "ZipFileFormat.h"
7 #include "zipdirstructures.h"
8 #include "ZipDirTree.h"
9 #include "ZipDirList.h"
10 #include "ZipDirCache.h"
11 #include "ZipDirCacheRW.h"
12 #include "ZipDirCacheFactory.h"
13 #include "ZipDirFindRW.h"
15 #include "ThreadUtils.h"
17 #include <zlib.h> // declaration of Z_OK for ZipRawDecompress
19 using namespace ZipFile
;
40 int compressionMethod
;
48 , compressionMethod(0)
60 const char* relativePathSrc
;
61 const char* realFilename
;
63 unsigned int existingCRC
;
66 unsigned long compressedSize
;
67 unsigned long compressedSizePreviously
;
69 void* uncompressedData
;
70 unsigned long uncompressedSize
;
71 unsigned long uncompressedSizePreviously
;
74 ZipDir::ErrorEnum zdError
;
75 PackFileStatus status
;
86 , compressedSizePreviously(0)
89 , uncompressedSizePreviously(0)
91 , zdError(ZipDir::ZD_ERROR_NOT_IMPLEMENTED
)
92 , status(PACKFILE_FAILED
)
96 void DetachUncompressedData()
98 if (uncompressedData
&& uncompressedData
== compressedData
)
104 uncompressedData
= 0;
105 uncompressedSize
= 0;
110 if (compressedData
&& compressedData
!= uncompressedData
)
112 free(compressedData
);
116 if (uncompressedData
)
118 free(uncompressedData
);
119 uncompressedData
= 0;
125 // ---------------------------------------------------------------------------
127 static void PackFileFromDisc(PackFileJob
* job
);
132 PackFilePool(int numFiles
, size_t memoryLimit
)
136 , m_memoryLimit(memoryLimit
)
137 , m_allocatedMemory(0)
139 m_files
.reserve(numFiles
);
146 void Submit(int key
, const PackFileJob
& job
)
148 PackFileJob
* newJob
= new PackFileJob(job
);
150 // index in queue, and custom key for identification
151 newJob
->index
= int(m_files
.size());
154 m_files
.push_back(newJob
);
157 PackFileJob
* WaitForFile(int index
)
162 ThreadUtils::AutoLock
lock(m_filesLock
);
163 m_awaitedFile
= index
;
164 if (size_t(index
) >= m_files
.size())
167 return m_files
[index
];
176 void Start(int numThreads
)
178 for (size_t i
= 0; i
< m_files
.size(); ++i
)
180 PackFileJob
* job
= m_files
[i
];
182 m_pool
.Submit(&ProcessFile
, job
);
185 m_pool
.Start(numThreads
);
188 size_t GetJobCount() const
190 return m_files
.size();
193 void SkipPendingFiles()
198 void ReleaseFile(int index
)
200 assert(m_files
[index
] != 0);
203 if (m_memoryLimit
!= 0)
205 ThreadUtils::AutoLock
lock(m_filesLock
);
207 m_allocatedMemory
-= m_files
[index
]->uncompressedSize
;
208 m_allocatedMemory
-= m_files
[index
]->compressedSize
;
211 delete m_files
[index
];
218 // called from non-main thread
219 static void ProcessFile(PackFileJob
* job
)
221 PackFilePool
* self
= job
->batch
->pool
;
225 if (self
->m_memoryLimit
!= 0)
229 size_t allocatedMemory
= 0;
232 ThreadUtils::AutoLock
lock(self
->m_filesLock
);
233 allocatedMemory
= self
->m_allocatedMemory
;
234 awaitedFile
= self
->m_awaitedFile
;
237 if (allocatedMemory
> self
->m_memoryLimit
&& job
->index
> awaitedFile
+ 1)
238 Sleep(10); // give time to main thread to write data to file
244 PackFileFromDisc(job
);
247 self
->FileCompleted(job
);
250 // called from non-main thread
251 void FileCompleted(PackFileJob
* job
)
253 ThreadUtils::AutoLock
lock(m_filesLock
);
256 assert(job
->index
< m_files
.size());
257 assert(m_files
[job
->index
] == 0);
258 m_files
[job
->index
] = job
;
260 if (m_memoryLimit
!= 0)
262 m_allocatedMemory
+= job
->uncompressedSize
;
263 m_allocatedMemory
+= job
->compressedSize
;
267 size_t m_memoryLimit
;
269 ThreadUtils::CriticalSection m_filesLock
;
270 std::vector
<PackFileJob
*> m_files
;
272 size_t m_allocatedMemory
;
275 ThreadUtils::SimpleThreadPool m_pool
;
278 //////////////////////////////////////////////////////////////////////////
279 static size_t AlignTo(size_t offset
, size_t alignment
)
281 const size_t remainder
= offset
% alignment
;
282 return remainder
? offset
+ alignment
- remainder
: offset
;
284 //////////////////////////////////////////////////////////////////////////
285 // Calculates new offset of the header to make sure that following data are
287 static size_t CalculateAlignedHeaderOffset(const char* fileName
, size_t currentOffset
, size_t alignment
)
289 // Since file should start from header
290 if (currentOffset
== 0)
295 // Local header is followed by filename
296 const size_t totalHeaderSize
= sizeof(LocalFileHeader
) + strlen(fileName
);
298 // Align end of the header
299 const size_t dataOffset
= AlignTo(currentOffset
+ totalHeaderSize
, alignment
);
301 return dataOffset
- totalHeaderSize
;
304 //////////////////////////////////////////////////////////////////////////
305 ZipDir::CacheRW::CacheRW(bool encryptHeaders
, const EncryptionKey
& encryptionKey
)
309 , m_fileAlignment (1)
310 , m_bEncryptedHeaders(encryptHeaders
)
311 , m_bHeadersEncryptedOnClose(encryptHeaders
)
312 , m_encryptionKey(encryptionKey
)
317 //////////////////////////////////////////////////////////////////////////
318 ZipDir::CacheRW::~CacheRW()
322 //////////////////////////////////////////////////////////////////////////
323 void ZipDir::CacheRW::AddRef()
328 //////////////////////////////////////////////////////////////////////////
329 void ZipDir::CacheRW::Release()
331 if (--m_nRefCount
<= 0)
335 void ZipDir::CacheRW::Close()
339 if (!(m_nFlags
& FLAGS_READ_ONLY
))
341 if ((m_nFlags
& FLAGS_UNCOMPACTED
) && !(m_nFlags
&FLAGS_DONT_COMPACT
))
347 if (m_nFlags
& FLAGS_CDR_DIRTY
)
351 if(m_pFile
) // RelinkZip() might have closed the file
359 //////////////////////////////////////////////////////////////////////////
360 char* ZipDir::CacheRW::UnifyPath( char *const str
, const char *pPath
)
363 const char *src
= pPath
;
369 *trg
++ = ::tolower(*src
++);
381 //////////////////////////////////////////////////////////////////////////
382 char* ZipDir::CacheRW::ToUnixPath( char *const str
, const char *pPath
)
385 const char *src
= pPath
;
403 //////////////////////////////////////////////////////////////////////////
404 char* ZipDir::CacheRW::AllocPath( const char *pPath
)
407 char *temp
= ToUnixPath(str
, pPath
);
408 temp
= m_tempStringPool
.Append( temp
,strlen(temp
) );
412 static void PackFileFromMemory(PackFileJob
* job
)
414 if (job
->existingCRC
!= 0)
416 unsigned int crcCode
= (unsigned int)crc32(0, (unsigned char*)job
->uncompressedData
, job
->uncompressedSize
);
417 if (crcCode
== job
->existingCRC
)
419 job
->compressedData
= 0;
420 job
->compressedSize
= 0;
421 job
->status
= PACKFILE_UPTODATE
;
422 job
->zdError
= ZipDir::ZD_ERROR_SUCCESS
;
423 // This file with same data already in pak, skip it.
428 switch (job
->batch
->compressionMethod
)
430 case METHOD_DEFLATE_AND_ENCRYPT
:
433 // allocate memory for compression. Min is nSize * 1.001 + 12
434 if (job
->uncompressedSize
> 0)
436 job
->compressedSize
= job
->uncompressedSize
+ (job
->uncompressedSize
>> 3) + 32;
437 job
->compressedData
= malloc(job
->compressedSize
);
438 int error
= ZipDir::ZipRawCompress(job
->uncompressedData
, &job
->compressedSize
, job
->compressedData
, job
->uncompressedSize
, job
->batch
->compressionLevel
);
441 job
->status
= PACKFILE_COMPRESSED
;
442 job
->zdError
= ZipDir::ZD_ERROR_SUCCESS
;
446 job
->status
= PACKFILE_FAILED
;
447 job
->zdError
= ZipDir::ZD_ERROR_ZLIB_FAILED
;
452 job
->status
= PACKFILE_COMPRESSED
;
453 job
->zdError
= ZipDir::ZD_ERROR_SUCCESS
;
455 job
->compressedSize
= 0;
456 job
->compressedData
= 0;
461 job
->compressedData
= job
->uncompressedData
;
462 job
->compressedSize
= job
->uncompressedSize
;
463 job
->status
= PACKFILE_COMPRESSED
;
464 job
->zdError
= ZipDir::ZD_ERROR_SUCCESS
;
468 job
->status
= PACKFILE_FAILED
;
469 job
->zdError
= ZipDir::ZD_ERROR_UNSUPPORTED
;
474 bool ZipDir::CacheRW::WriteCompressedData(const char* data
, size_t size
, bool encrypt
, FILE* file
)
481 std::vector
<char> buffer
;
485 memcpy(&buffer
[0], data
, size
);
486 ZipDir::Encrypt(&buffer
[0], size
, m_encryptionKey
);
490 // Danny - writing a single large chunk (more than 6MB?) causes
491 // Windows fwrite to (silently?!) fail. So we're writing data
495 const size_t sizeToWrite
= Util::getMin(size
, size_t(1024 * 1024));
496 if (fwrite(data
, sizeToWrite
, 1, file
) != 1)
507 static bool WriteRandomData(FILE* file
, size_t size
)
514 const size_t bufferSize
= Util::getMin(size
, size_t(1024 * 1024));
515 std::vector
<char> buffer(bufferSize
);
519 const size_t sizeToWrite
= Util::getMin(size
, bufferSize
);
521 for (size_t i
= 0; i
< sizeToWrite
; ++i
)
523 buffer
[i
] = rand() & 0xff;
526 if (fwrite(&buffer
[0], sizeToWrite
, 1, file
) != 1)
537 bool ZipDir::CacheRW::WriteNullData(size_t size
)
544 const size_t bufferSize
= Util::getMin(size
, size_t(1024 * 1024));
545 std::vector
<char> buffer(bufferSize
, 0);
549 const size_t sizeToWrite
= Util::getMin(size
, bufferSize
);
551 if (fwrite(&buffer
[0], sizeToWrite
, 1, m_pFile
) != 1)
562 void ZipDir::CacheRW::StorePackedFile(PackFileJob
* job
)
564 if (job
->batch
->zipMaxSize
> 0 && GetTotalFileSize() > job
->batch
->zipMaxSize
)
566 job
->status
= PACKFILE_SKIPPED
;
567 job
->zdError
= ZipDir::ZD_ERROR_SUCCESS
;
571 job
->status
= PACKFILE_FAILED
;
574 char* relativePath
= UnifyPath(str
, job
->relativePathSrc
);
576 // create or find the file entry.. this object will rollback (delete the object
577 // if the operation fails) if needed.
578 FileEntryTransactionAdd
pFileEntry(this, AllocPath(job
->relativePathSrc
), AllocPath(relativePath
));
582 job
->zdError
= ZipDir::ZD_ERROR_INVALID_PATH
;
586 pFileEntry
->OnNewFileData(job
->uncompressedData
, job
->uncompressedSize
,
587 job
->compressedSize
, job
->batch
->compressionMethod
, false);
588 pFileEntry
->SetFromFileTimeNTFS(job
->modTime
);
590 // since we changed the time, we'll have to update CDR
591 m_nFlags
|= FLAGS_CDR_DIRTY
;
593 // the new CDR position, if the operation completes successfully
594 unsigned lNewCDROffset
= m_lCDROffset
;
596 if (pFileEntry
->IsInitialized())
598 // this file entry is already allocated in CDR
600 // check if the new compressed data fits into the old place
601 unsigned nFreeSpace
= pFileEntry
->nEOFOffset
- pFileEntry
->nFileHeaderOffset
- (unsigned)sizeof(ZipFile::LocalFileHeader
) - (unsigned)strlen(relativePath
);
603 if (nFreeSpace
!= job
->compressedSize
)
604 m_nFlags
|= FLAGS_UNCOMPACTED
;
606 if (nFreeSpace
>= job
->compressedSize
)
608 // and we can just override the compressed data in the file
609 ErrorEnum e
= WriteLocalHeader(m_pFile
, pFileEntry
, job
->relativePathSrc
, m_bEncryptedHeaders
);
610 if (e
!= ZipDir::ZD_ERROR_SUCCESS
)
618 // we need to write the file anew - in place of current CDR
619 pFileEntry
->nFileHeaderOffset
= CalculateAlignedHeaderOffset(job
->relativePathSrc
, m_lCDROffset
, m_fileAlignment
);
620 ErrorEnum e
= WriteLocalHeader(m_pFile
, pFileEntry
, job
->relativePathSrc
, m_bEncryptedHeaders
);
621 lNewCDROffset
= pFileEntry
->nEOFOffset
;
622 if (e
!= ZipDir::ZD_ERROR_SUCCESS
)
631 pFileEntry
->nFileHeaderOffset
= CalculateAlignedHeaderOffset(job
->relativePathSrc
, m_lCDROffset
, m_fileAlignment
);
632 ErrorEnum e
= WriteLocalHeader(m_pFile
, pFileEntry
, job
->relativePathSrc
, m_bEncryptedHeaders
);
633 if (e
!= ZipDir::ZD_ERROR_SUCCESS
)
639 lNewCDROffset
= pFileEntry
->nFileDataOffset
+ job
->compressedSize
;
641 m_nFlags
|= FLAGS_CDR_DIRTY
;
644 // now we have the fresh local header and data offset
646 #if CRY_PLATFORM_WINDOWS
647 if (_fseeki64 (m_pFile
, (__int64
)pFileEntry
->nFileDataOffset
, SEEK_SET
)!=0)
649 if (fseek (m_pFile
, pFileEntry
->nFileDataOffset
, SEEK_SET
)!=0)
652 job
->zdError
= ZD_ERROR_IO_FAILED
;
656 const bool encrypt
= pFileEntry
->nMethod
== METHOD_DEFLATE_AND_ENCRYPT
;
658 if (!WriteCompressedData((char*)job
->compressedData
, job
->compressedSize
, encrypt
, m_pFile
))
660 job
->zdError
= ZD_ERROR_IO_FAILED
;
664 // since we wrote the file successfully, update the new CDR position
665 m_lCDROffset
= lNewCDROffset
;
668 job
->status
= PACKFILE_ADDED
;
669 job
->zdError
= ZD_ERROR_SUCCESS
;
672 // Adds a new file to the zip or update an existing one
673 // adds a directory (creates several nested directories if needed)
674 ZipDir::ErrorEnum
ZipDir::CacheRW::UpdateFile (const char* szRelativePathSrc
, void* pUncompressed
, unsigned nSize
,
675 unsigned nCompressionMethod
, int nCompressionLevel
, __time64_t modTime
)
678 char *szRelativePath
= UnifyPath(str
, szRelativePathSrc
);
682 batch
.compressionMethod
= nCompressionMethod
;
683 batch
.compressionLevel
= nCompressionLevel
;
686 job
.relativePathSrc
= szRelativePathSrc
;
687 job
.modTime
= modTime
;
688 job
.uncompressedData
= pUncompressed
;
689 job
.uncompressedSize
= nSize
;
692 // crc will be used to check if this file need to be updated at all
693 ZipDir::FileEntry
* entry
= FindFile(szRelativePath
);
695 job
.existingCRC
= entry
->desc
.lCRC32
;
697 PackFileFromMemory(&job
);
701 case PACKFILE_SKIPPED
:
702 case PACKFILE_MISSING
:
703 case PACKFILE_FAILED
:
704 return ZD_ERROR_IO_FAILED
;
707 StorePackedFile(&job
);
708 job
.DetachUncompressedData();
712 static FILETIME
GetFileWriteTimeAndSize( uint64
* fileSize
, const char *filename
)
714 // Warning: FindFirstFile on NTFS may report file size that
715 // is not up-to-date with the actual file content.
716 // http://blogs.msdn.com/b/oldnewthing/archive/2011/12/26/10251026.aspx
720 WIN32_FIND_DATAA FindFileData
;
721 HANDLE hFind
= FindFirstFileA( filename
, &FindFileData
);
723 if (hFind
== INVALID_HANDLE_VALUE
)
725 fileTime
.dwLowDateTime
= 0;
726 fileTime
.dwHighDateTime
= 0;
734 fileTime
.dwLowDateTime
= FindFileData
.ftLastWriteTime
.dwLowDateTime
;
735 fileTime
.dwHighDateTime
= FindFileData
.ftLastWriteTime
.dwHighDateTime
;
738 *fileSize
= (uint64(FindFileData
.nFileSizeHigh
) << 32) + FindFileData
.nFileSizeLow
;
746 static void PackFileFromDisc(PackFileJob
* job
)
748 const FILETIME ft
= GetFileWriteTimeAndSize(0, job
->realFilename
);
750 lt
.HighPart
= ft
.dwHighDateTime
;
751 lt
.LowPart
= ft
.dwLowDateTime
;
752 job
->modTime
= lt
.QuadPart
;
754 FILE *f
= fopen(job
->realFilename
, "rb");
757 job
->status
= PACKFILE_FAILED
;
758 job
->zdError
= ZipDir::ZD_ERROR_FILE_NOT_FOUND
;
762 fseek(f
, 0, SEEK_END
);
763 size_t fileSize
= (size_t)ftell(f
);
765 if (fileSize
< job
->batch
->sourceMinSize
|| (job
->batch
->sourceMaxSize
> 0 && fileSize
> job
->batch
->sourceMaxSize
))
769 job
->status
= PACKFILE_SKIPPED
;
770 job
->zdError
= ZipDir::ZD_ERROR_SUCCESS
;
774 job
->uncompressedData
= malloc(fileSize
);
776 fseek(f
, 0, SEEK_SET
);
777 if (fread(job
->uncompressedData
, 1, fileSize
, f
) != fileSize
)
779 free(job
->uncompressedData
);
780 job
->uncompressedData
= 0;
783 job
->status
= PACKFILE_FAILED
;
784 job
->zdError
= ZipDir::ZD_ERROR_IO_FAILED
;
788 job
->uncompressedSize
= fileSize
;
790 PackFileFromMemory(job
);
793 bool ZipDir::CacheRW::UpdateMultipleFiles(const char** realFilenames
, const char** filenamesInZip
, size_t fileCount
,
794 int compressionLevel
, bool encryptContent
, size_t zipMaxSize
, int sourceMinSize
, int sourceMaxSize
,
795 int numExtraThreads
, ZipDir::IReporter
* reporter
, ZipDir::ISplitter
* splitter
)
797 int compressionMethod
= METHOD_DEFLATE
;
799 compressionMethod
= METHOD_DEFLATE_AND_ENCRYPT
;
800 else if (compressionLevel
== 0)
801 compressionMethod
= METHOD_STORE
;
803 uint64 totalSize
= 0;
805 clock_t startTime
= clock();
808 batch
.compressionLevel
= compressionLevel
;
809 batch
.compressionMethod
= compressionMethod
;
811 batch
.sourceMinSize
= sourceMinSize
;
812 batch
.sourceMaxSize
= sourceMaxSize
;
813 batch
.zipMaxSize
= zipMaxSize
;
815 if (numExtraThreads
== 0)
817 for (int i
= 0; i
< fileCount
; ++i
)
819 const char* realFilename
= realFilenames
[i
];
820 const char* filenameInZip
= filenamesInZip
[i
];
825 job
.relativePathSrc
= filenameInZip
;
826 job
.realFilename
= realFilename
;
830 // crc will be used to check if this file need to be updated at all
831 ZipDir::FileEntry
* entry
= FindFile(filenameInZip
);
836 const FILETIME ft
= GetFileWriteTimeAndSize(&fileSize
, realFilename
);
839 lt
.HighPart
= ft
.dwHighDateTime
;
840 lt
.LowPart
= ft
.dwLowDateTime
;
841 job
.modTime
= lt
.QuadPart
;
842 job
.existingCRC
= entry
->desc
.lCRC32
;
843 job
.compressedSizePreviously
= entry
->desc
.lSizeCompressed
;
844 job
.uncompressedSizePreviously
= entry
->desc
.lSizeUncompressed
;
846 // Check if file with the same name, timestamp and size already exists in pak.
847 if (entry
->CompareFileTimeNTFS(job
.modTime
) && fileSize
== entry
->desc
.lSizeUncompressed
)
850 reporter
->ReportUpToDate(filenameInZip
);
856 PackFileFromDisc(&job
);
858 if (job
.status
== PACKFILE_COMPRESSED
)
860 if (splitter
!= NULL
)
862 size_t dsk
= GetTotalFileSizeOnDiskSoFar();
867 bse
+= sizeof(ZipFile::CDRFileHeader
) + strlen(job
.relativePathSrc
);
868 bse
+= sizeof(ZipFile::LocalFileHeader
) + strlen(job
.relativePathSrc
);
870 if (job
.compressedSize
)
871 add
+= bse
+ job
.compressedSize
;
872 if (job
.compressedSizePreviously
)
873 sub
+= bse
+ job
.compressedSizePreviously
;
875 if (splitter
->CheckWriteLimit(dsk
, add
, sub
))
877 splitter
->SetLastFile(dsk
, add
, sub
, job
.key
- 1);
882 StorePackedFile(&job
);
889 reporter
->ReportAdded(filenameInZip
);
891 totalSize
+= job
.uncompressedSize
;
893 case PACKFILE_MISSING
:
895 reporter
->ReportMissing(realFilename
);
897 case PACKFILE_UPTODATE
:
899 reporter
->ReportUpToDate(realFilename
);
901 case PACKFILE_SKIPPED
:
903 reporter
->ReportSkipped(realFilename
);
907 reporter
->ReportFailed(realFilename
, ""); // TODO reason
915 const size_t memoryLimit
= 1024 * 1024 * 1024; // prevents threads from generating more than 1GB of data
916 PackFilePool
pool(fileCount
, memoryLimit
);
919 for (int i
= 0; i
< fileCount
; ++i
)
921 const char* realFilename
= realFilenames
[i
];
922 const char* filenameInZip
= filenamesInZip
[i
];
926 job
.relativePathSrc
= filenameInZip
;
927 job
.realFilename
= realFilename
;
931 // crc will be used to check if this file need to be updated at all
932 ZipDir::FileEntry
* entry
= FindFile(filenameInZip
);
936 const FILETIME ft
= GetFileWriteTimeAndSize(&fileSize
, realFilename
);
938 lt
.HighPart
= ft
.dwHighDateTime
;
939 lt
.LowPart
= ft
.dwLowDateTime
;
940 job
.modTime
= lt
.QuadPart
;
941 job
.existingCRC
= entry
->desc
.lCRC32
;
942 job
.compressedSizePreviously
= entry
->desc
.lSizeCompressed
;
943 job
.uncompressedSizePreviously
= entry
->desc
.lSizeUncompressed
;
945 // Check if file with the same name, timestamp and size already exists in pak.
946 if (entry
->CompareFileTimeNTFS(job
.modTime
) && fileSize
== entry
->desc
.lSizeUncompressed
)
949 reporter
->ReportUpToDate(filenameInZip
);
958 // Get the number of submitted jobs, which is at most
959 // as large as the largest successfully submitted file-index.
960 // Any number of files can be skipped for submittion.
961 const int jobCount
= pool
.GetJobCount();
967 pool
.Start(numExtraThreads
);
969 for (int i
= 0; i
< jobCount
; ++i
)
971 PackFileJob
* job
= pool
.WaitForFile(i
);
978 if (job
->status
== PACKFILE_COMPRESSED
)
980 if (splitter
!= NULL
)
982 size_t dsk
= GetTotalFileSizeOnDiskSoFar();
987 bse
+= sizeof(ZipFile::CDRFileHeader
) + strlen(job
->relativePathSrc
);
988 bse
+= sizeof(ZipFile::LocalFileHeader
) + strlen(job
->relativePathSrc
);
990 if (job
->compressedSize
)
991 add
+= bse
+ job
->compressedSize
;
992 if (job
->compressedSizePreviously
)
993 sub
+= bse
+ job
->compressedSizePreviously
;
995 if (splitter
->CheckWriteLimit(dsk
, add
, sub
))
997 splitter
->SetLastFile(dsk
, add
, sub
, job
->key
- 1);
999 // deplete the pool before leaving the loop
1000 pool
.SkipPendingFiles();
1001 for (; i
< jobCount
; ++i
)
1003 pool
.WaitForFile(i
);
1004 pool
.ReleaseFile(i
);
1011 StorePackedFile(job
);
1014 switch (job
->status
)
1016 case PACKFILE_ADDED
:
1018 reporter
->ReportAdded(job
->relativePathSrc
);
1020 totalSize
+= job
->uncompressedSize
;
1022 case PACKFILE_MISSING
:
1024 reporter
->ReportMissing(job
->realFilename
);
1026 case PACKFILE_UPTODATE
:
1028 reporter
->ReportUpToDate(job
->realFilename
);
1030 case PACKFILE_SKIPPED
:
1032 reporter
->ReportSkipped(job
->realFilename
);
1036 reporter
->ReportFailed(job
->realFilename
, ""); // TODO reason
1040 pool
.ReleaseFile(i
);
1044 clock_t endTime
= clock();
1045 double timeSeconds
= double(endTime
- startTime
) / CLOCKS_PER_SEC
;
1046 double speed
= (endTime
- startTime
) == 0 ? 0.0 : double(totalSize
) / timeSeconds
;
1049 reporter
->ReportSpeed(speed
);
1055 // Adds a new file to the zip or update an existing one if it is not compressed - just stored - start a big file
1056 ZipDir::ErrorEnum
ZipDir::CacheRW::StartContinuousFileUpdate( const char* szRelativePathSrc
, unsigned nSize
)
1058 char str
[_MAX_PATH
];
1059 char *szRelativePath
= UnifyPath(str
, szRelativePathSrc
);
1061 SmartPtr pBufferDestroyer
;
1063 // create or find the file entry.. this object will rollback (delete the object
1064 // if the operation fails) if needed.
1065 FileEntryTransactionAdd
pFileEntry(this, AllocPath(szRelativePathSrc
), AllocPath(szRelativePath
));
1068 return ZD_ERROR_INVALID_PATH
;
1070 pFileEntry
->OnNewFileData (NULL
, nSize
, nSize
, METHOD_STORE
, false );
1071 // since we changed the time, we'll have to update CDR
1072 m_nFlags
|= FLAGS_CDR_DIRTY
;
1074 // the new CDR position, if the operation completes successfully
1075 unsigned lNewCDROffset
= m_lCDROffset
;
1076 if (pFileEntry
->IsInitialized())
1078 // check if the new compressed data fits into the old place
1079 unsigned nFreeSpace
= pFileEntry
->nEOFOffset
- pFileEntry
->nFileHeaderOffset
- (unsigned)sizeof(ZipFile::LocalFileHeader
) - (unsigned)strlen(szRelativePath
);
1081 if (nFreeSpace
!= nSize
)
1082 m_nFlags
|= FLAGS_UNCOMPACTED
;
1084 if (nFreeSpace
>= nSize
)
1086 // and we can just override the compressed data in the file
1087 ErrorEnum e
= WriteLocalHeader(m_pFile
, pFileEntry
, szRelativePathSrc
, m_bEncryptedHeaders
);
1088 if (e
!= ZD_ERROR_SUCCESS
)
1093 // we need to write the file anew - in place of current CDR
1094 pFileEntry
->nFileHeaderOffset
= CalculateAlignedHeaderOffset(szRelativePathSrc
, m_lCDROffset
, m_fileAlignment
);
1095 ErrorEnum e
= WriteLocalHeader(m_pFile
, pFileEntry
, szRelativePathSrc
, m_bEncryptedHeaders
);
1096 lNewCDROffset
= pFileEntry
->nEOFOffset
;
1097 if (e
!= ZD_ERROR_SUCCESS
)
1103 pFileEntry
->nFileHeaderOffset
= CalculateAlignedHeaderOffset(szRelativePathSrc
, m_lCDROffset
, m_fileAlignment
);
1104 ErrorEnum e
= WriteLocalHeader(m_pFile
, pFileEntry
, szRelativePathSrc
, m_bEncryptedHeaders
);
1105 if (e
!= ZD_ERROR_SUCCESS
)
1108 lNewCDROffset
= pFileEntry
->nFileDataOffset
+ nSize
;
1110 m_nFlags
|= FLAGS_CDR_DIRTY
;
1113 #if CRY_PLATFORM_WINDOWS
1114 if (_fseeki64 (m_pFile
, (__int64
)pFileEntry
->nFileDataOffset
, SEEK_SET
)!=0)
1116 if (fseek (m_pFile
, pFileEntry
->nFileDataOffset
, SEEK_SET
)!=0)
1119 return ZD_ERROR_IO_FAILED
;
1122 if (!WriteNullData(nSize
))
1124 return ZD_ERROR_IO_FAILED
;
1127 pFileEntry
->nEOFOffset
= pFileEntry
->nFileDataOffset
;
1129 // since we wrote the file successfully, update the new CDR position
1130 m_lCDROffset
= lNewCDROffset
;
1131 pFileEntry
.Commit();
1133 return ZD_ERROR_SUCCESS
;
1136 // Adds a new file to the zip or update an existing's segment if it is not compressed - just stored
1137 // adds a directory (creates several nested directories if needed)
1138 ZipDir::ErrorEnum
ZipDir::CacheRW::UpdateFileContinuousSegment (const char* szRelativePathSrc
, unsigned nSize
, void* pUncompressed
, unsigned nSegmentSize
, unsigned nOverwriteSeekPos
)
1140 char str
[_MAX_PATH
];
1141 char *szRelativePath
= UnifyPath(str
, szRelativePathSrc
);
1143 SmartPtr pBufferDestroyer
;
1145 // create or find the file entry.. this object will rollback (delete the object
1146 // if the operation fails) if needed.
1147 FileEntryTransactionAdd
pFileEntry(this, AllocPath(szRelativePathSrc
), AllocPath(szRelativePath
));
1150 return ZD_ERROR_INVALID_PATH
;
1152 pFileEntry
->OnNewFileData (pUncompressed
, nSegmentSize
, nSegmentSize
, METHOD_STORE
, true);
1153 // since we changed the time, we'll have to update CDR
1154 m_nFlags
|= FLAGS_CDR_DIRTY
;
1156 // this file entry is already allocated in CDR
1157 unsigned lSegmentOffset
= pFileEntry
->nEOFOffset
;
1159 #if CRY_PLATFORM_WINDOWS
1160 if (_fseeki64 (m_pFile
, (__int64
)pFileEntry
->nFileHeaderOffset
, SEEK_SET
)!=0)
1162 if (fseek (m_pFile
, pFileEntry
->nFileHeaderOffset
, SEEK_SET
)!=0)
1164 return ZD_ERROR_IO_FAILED
;
1166 // and we can just override the compressed data in the file
1167 ErrorEnum e
= WriteLocalHeader(m_pFile
, pFileEntry
, szRelativePath
, m_bEncryptedHeaders
);
1168 if (e
!= ZD_ERROR_SUCCESS
)
1171 if(nOverwriteSeekPos
!=0xffffffff)
1172 lSegmentOffset
= pFileEntry
->nFileDataOffset
+ nOverwriteSeekPos
;
1174 // now we have the fresh local header and data offset
1175 #if CRY_PLATFORM_WINDOWS
1176 if (_fseeki64 (m_pFile
, (__int64
)lSegmentOffset
, SEEK_SET
)!=0)
1178 if (fseek (m_pFile
, lSegmentOffset
, SEEK_SET
)!=0)
1180 return ZD_ERROR_IO_FAILED
;
1182 const bool encrypt
= false; // encryption is not supported for continous updates
1183 if (!WriteCompressedData((char*)pUncompressed
, nSegmentSize
, encrypt
, m_pFile
))
1185 return ZD_ERROR_IO_FAILED
;
1188 if(nOverwriteSeekPos
==0xffffffff)
1189 pFileEntry
->nEOFOffset
= lSegmentOffset
+ nSegmentSize
;
1191 // since we wrote the file successfully, update CDR
1192 pFileEntry
.Commit();
1193 return ZD_ERROR_SUCCESS
;
1197 ZipDir::ErrorEnum
ZipDir::CacheRW::UpdateFileCRC (const char* szRelativePathSrc
, unsigned dwCRC32
)
1199 char str
[_MAX_PATH
];
1200 char *szRelativePath
= UnifyPath(str
, szRelativePathSrc
);
1202 SmartPtr pBufferDestroyer
;
1204 // create or find the file entry.. this object will rollback (delete the object
1205 // if the operation fails) if needed.
1206 FileEntryTransactionAdd
pFileEntry(this, AllocPath(szRelativePathSrc
), AllocPath(szRelativePath
));
1209 return ZD_ERROR_INVALID_PATH
;
1211 // since we changed the time, we'll have to update CDR
1212 m_nFlags
|= FLAGS_CDR_DIRTY
;
1214 pFileEntry
->desc
.lCRC32
=dwCRC32
;
1216 #if CRY_PLATFORM_WINDOWS
1217 if (_fseeki64 (m_pFile
, (__int64
)pFileEntry
->nFileHeaderOffset
, SEEK_SET
)!=0)
1219 if (fseek (m_pFile
, pFileEntry
->nFileHeaderOffset
, SEEK_SET
)!=0)
1221 return ZD_ERROR_IO_FAILED
;
1223 // and we can just override the compressed data in the file
1224 ErrorEnum e
= WriteLocalHeader(m_pFile
, pFileEntry
, szRelativePath
, m_bEncryptedHeaders
);
1225 if (e
!= ZD_ERROR_SUCCESS
)
1228 // since we wrote the file successfully, update
1229 pFileEntry
.Commit();
1230 return ZD_ERROR_SUCCESS
;
1234 // deletes the file from the archive
1235 ZipDir::ErrorEnum
ZipDir::CacheRW::RemoveFile (const char* szRelativePathSrc
)
1237 char str
[_MAX_PATH
];
1238 char *szRelativePath
= UnifyPath(str
, szRelativePathSrc
);
1240 // find the last slash in the path
1241 const char* pSlash
= (std::max
)(strrchr(szRelativePath
, '/'), strrchr(szRelativePath
, '\\'));
1243 const char* pFileName
; // the name of the file to delete
1245 FileEntryTree
* pDir
; // the dir from which the subdir will be deleted
1249 FindDirRW
fd (GetRoot());
1250 // the directory to remove
1251 pDir
= fd
.FindExact(string (szRelativePath
, pSlash
-szRelativePath
).c_str());
1253 return ZD_ERROR_DIR_NOT_FOUND
;// there is no such directory
1254 pFileName
= pSlash
+1;
1259 pFileName
= szRelativePath
;
1262 ErrorEnum e
= pDir
->RemoveFile (pFileName
);
1263 if (e
== ZD_ERROR_SUCCESS
)
1264 m_nFlags
|= FLAGS_UNCOMPACTED
|FLAGS_CDR_DIRTY
;
1269 // deletes the directory, with all its descendants (files and subdirs)
1270 ZipDir::ErrorEnum
ZipDir::CacheRW::RemoveDir (const char* szRelativePathSrc
)
1272 char str
[_MAX_PATH
];
1273 char *szRelativePath
= UnifyPath(str
, szRelativePathSrc
);
1275 // find the last slash in the path
1276 const char* pSlash
= (std::max
)(strrchr(szRelativePath
, '/'), strrchr(szRelativePath
, '\\'));
1278 const char* pDirName
; // the name of the dir to delete
1280 FileEntryTree
* pDir
; // the dir from which the subdir will be deleted
1284 FindDirRW
fd (GetRoot());
1285 // the directory to remove
1286 pDir
= fd
.FindExact(string (szRelativePath
, pSlash
-szRelativePath
).c_str());
1288 return ZD_ERROR_DIR_NOT_FOUND
;// there is no such directory
1289 pDirName
= pSlash
+1;
1294 pDirName
= szRelativePath
;
1297 ErrorEnum e
= pDir
->RemoveDir (pDirName
);
1298 if (e
== ZD_ERROR_SUCCESS
)
1299 m_nFlags
|= FLAGS_UNCOMPACTED
|FLAGS_CDR_DIRTY
;
1303 // deletes all files and directories in this archive
1304 ZipDir::ErrorEnum
ZipDir::CacheRW::RemoveAll()
1306 ErrorEnum e
= m_treeDir
.RemoveAll();
1307 if (e
== ZD_ERROR_SUCCESS
)
1308 m_nFlags
|= FLAGS_UNCOMPACTED
|FLAGS_CDR_DIRTY
;
1312 ZipDir::ErrorEnum
ZipDir::CacheRW::ReadFile (FileEntry
* pFileEntry
, void* pCompressed
, void* pUncompressed
)
1315 return ZD_ERROR_INVALID_CALL
;
1317 if (pFileEntry
->desc
.lSizeUncompressed
== 0)
1319 assert (pFileEntry
->desc
.lSizeCompressed
== 0);
1320 return ZD_ERROR_SUCCESS
;
1323 assert (pFileEntry
->desc
.lSizeCompressed
> 0);
1325 ErrorEnum nError
= Refresh(pFileEntry
);
1326 if (nError
!= ZD_ERROR_SUCCESS
)
1329 #if CRY_PLATFORM_WINDOWS
1330 if (_fseeki64 (m_pFile
, (__int64
)pFileEntry
->nFileDataOffset
, SEEK_SET
))
1332 if (fseek (m_pFile
, pFileEntry
->nFileDataOffset
, SEEK_SET
))
1334 return ZD_ERROR_IO_FAILED
;
1336 SmartPtr pBufferDestroyer
;
1338 void* pBuffer
= pCompressed
; // the buffer where the compressed data will go
1340 if (pFileEntry
->nMethod
== 0 && pUncompressed
)
1342 // we can directly read into the uncompress buffer
1343 pBuffer
= pUncompressed
;
1349 // what's the sense of it - no buffers at all?
1350 return ZD_ERROR_INVALID_CALL
;
1352 pBuffer
= malloc(pFileEntry
->desc
.lSizeCompressed
);
1353 pBufferDestroyer
.Attach(pBuffer
); // we want it auto-freed once we return
1356 if (fread((char*)pBuffer
, pFileEntry
->desc
.lSizeCompressed
, 1, m_pFile
) != 1)
1358 return ZD_ERROR_IO_FAILED
;
1361 if (pFileEntry
->nMethod
== METHOD_DEFLATE_AND_ENCRYPT
)
1363 ZipDir::Decrypt((char*)pBuffer
, pFileEntry
->desc
.lSizeCompressed
, m_encryptionKey
);
1366 // if there's a buffer for uncompressed data, uncompress it to that buffer
1369 if (pFileEntry
->nMethod
== 0)
1371 assert (pBuffer
== pUncompressed
);
1372 //assert (pFileEntry->nSizeCompressed == pFileEntry->nSizeUncompressed);
1373 //memcpy (pUncompressed, pBuffer, pFileEntry->nSizeCompressed);
1377 unsigned long nSizeUncompressed
= pFileEntry
->desc
.lSizeUncompressed
;
1378 if (nSizeUncompressed
> 0)
1380 if (Z_OK
!= ZipRawUncompress(pUncompressed
, &nSizeUncompressed
, pBuffer
, pFileEntry
->desc
.lSizeCompressed
))
1381 return ZD_ERROR_CORRUPTED_DATA
;
1386 return ZD_ERROR_SUCCESS
;
1390 //////////////////////////////////////////////////////////////////////////
1391 // finds the file by exact path
1392 ZipDir::FileEntry
* ZipDir::CacheRW::FindFile (const char* szPathSrc
, bool bFullInfo
)
1394 char str
[_MAX_PATH
];
1395 char *szPath
= UnifyPath(str
, szPathSrc
);
1399 ZipDir::FindFileRW
fd (GetRoot());
1400 if (!fd
.FindExact(szPath
))
1402 assert (!fd
.GetFileEntry());
1405 ZipDir::FileEntry
* const p
= fd
.GetFileEntry();
1410 // returns the size of memory occupied by the instance referred to by this cache
1411 size_t ZipDir::CacheRW::GetSize() const
1413 return sizeof(*this) + m_strFilePath
.capacity() + m_treeDir
.GetSize() - sizeof(m_treeDir
);
1416 // returns the compressed size of all the entries
1417 size_t ZipDir::CacheRW::GetCompressedSize() const
1419 return m_treeDir
.GetCompressedFileSize();
1422 // returns the total size of memory occupied by the instance of this cache and all the compressed files
1423 size_t ZipDir::CacheRW::GetTotalFileSize() const
1425 return GetSize() + GetCompressedSize();
1428 // returns the total size of space occupied on disk by the instance of this cache and all the compressed files
1429 size_t ZipDir::CacheRW::GetTotalFileSizeOnDiskSoFar()
1431 FileRecordList
arrFiles(GetRoot());
1432 FileRecordList::ZipStats statFiles
= arrFiles
.GetStats();
1434 return m_lCDROffset
+ statFiles
.nSizeCDR
;
1437 // refreshes information about the given file entry into this file entry
1438 ZipDir::ErrorEnum
ZipDir::CacheRW::Refresh (FileEntry
* pFileEntry
)
1441 return ZD_ERROR_INVALID_CALL
;
1443 if (pFileEntry
->nFileDataOffset
!= pFileEntry
->INVALID_DATA_OFFSET
)
1444 return ZD_ERROR_SUCCESS
; // the data offset has been successfully read..
1447 return ZD_ERROR_INVALID_CALL
; // from which cache is this file entry???
1449 return ZipDir::Refresh(m_pFile
, pFileEntry
, m_bEncryptedHeaders
);
1453 // writes the CDR to the disk
1454 bool ZipDir::CacheRW::WriteCDR(FILE* fTarget
, bool encryptCDR
)
1459 #if CRY_PLATFORM_WINDOWS
1460 if (_fseeki64(fTarget
, (__int64
)m_lCDROffset
, SEEK_SET
))
1462 if (fseek(fTarget
, m_lCDROffset
, SEEK_SET
))
1466 FileRecordList
arrFiles(GetRoot());
1467 //arrFiles.SortByFileOffset();
1468 size_t nSizeCDR
= arrFiles
.GetStats().nSizeCDR
;
1469 void* pCDR
= malloc(nSizeCDR
);
1470 size_t nSizeCDRSerialized
= arrFiles
.MakeZipCDR(m_lCDROffset
, pCDR
, encryptCDR
);
1471 assert (nSizeCDRSerialized
== nSizeCDR
);
1475 // We do not encrypt CDREnd, so we could find it by signature
1476 ZipDir::Encrypt((char*)pCDR
, nSizeCDR
- sizeof(ZipFile::CDREnd
), m_encryptionKey
);
1479 size_t nWriteRes
= fwrite (pCDR
, nSizeCDR
, 1, fTarget
);
1481 return nWriteRes
== 1;
1484 // generates random file name
1485 string
ZipDir::CacheRW::GetRandomName(int nAttempt
)
1491 for (i
= 0; i
< sizeof(szBuf
)-1;++i
)
1493 int r
= rand()%(10 + 'z' - 'a' + 1);
1494 szBuf
[i
] = r
> 9 ? (r
-10)+'a' : '0' + r
;
1503 bool ZipDir::CacheRW::RelinkZip()
1505 for (int nAttempt
= 0; nAttempt
< 32; ++nAttempt
)
1507 string strNewFilePath
= m_strFilePath
+ "$" + GetRandomName(nAttempt
);
1509 FILE* f
= fopen (strNewFilePath
.c_str(), "wb");
1512 bool bOk
= RelinkZip(f
);
1513 fclose (f
); // we don't need the temporary file handle anyway
1517 // we don't need the temporary file
1518 DeleteFileA(strNewFilePath
.c_str());
1522 // we successfully relinked, now copy the temporary file to the original file
1526 DeleteFileA(m_strFilePath
.c_str());
1527 if (MoveFileA(strNewFilePath
.c_str(), m_strFilePath
.c_str()) == 0)
1529 // successfully renamed - reopen
1530 m_pFile
= fopen (m_strFilePath
.c_str(), "r+b");
1531 return m_pFile
== NULL
;
1537 //m_pFile = fopen (strNewFilePath.c_str(), "r+b");
1543 // couldn't open temp file
1547 bool ZipDir::CacheRW::RelinkZip(FILE* fTmp
)
1549 FileRecordList
arrFiles(GetRoot());
1550 arrFiles
.SortByFileOffset();
1552 // we back up our file entries, because we'll need to restore them
1553 // in case the operation fails
1554 std::vector
<FileEntry
> arrFileEntryBackup
;
1555 arrFiles
.Backup (arrFileEntryBackup
);
1557 // this is the set of files that are to be written out - compressed data and the file record iterator
1558 std::vector
<FileDataRecordPtr
> queFiles
;
1559 queFiles
.reserve (g_nMaxItemsRelinkBuffer
);
1561 // the total size of data in the queue
1562 unsigned nQueueSize
= 0;
1564 for (FileRecordList::iterator it
= arrFiles
.begin(); it
!= arrFiles
.end(); ++it
)
1566 FileEntry
* entry
= it
->pFileEntry
;
1567 // find the file data offset
1568 if (ZD_ERROR_SUCCESS
!= Refresh(entry
))
1571 // go to the file data
1572 #if CRY_PLATFORM_WINDOWS
1573 if (_fseeki64 (m_pFile
, (__int64
)entry
->nFileDataOffset
, SEEK_SET
) != 0)
1575 if (fseek (m_pFile
, entry
->nFileDataOffset
, SEEK_SET
) != 0)
1579 // allocate memory for the file compressed data
1580 FileDataRecordPtr pFile
= FileDataRecord::New (*it
);
1585 // read the compressed data
1586 if (entry
->desc
.lSizeCompressed
&& fread (pFile
->GetData(), entry
->desc
.lSizeCompressed
, 1, m_pFile
) != 1)
1589 if (entry
->nMethod
== METHOD_DEFLATE_AND_ENCRYPT
)
1591 ZipDir::Decrypt((char*)pFile
->GetData(), entry
->desc
.lSizeCompressed
, m_encryptionKey
);
1594 // put the file into the queue for copying (writing)
1595 queFiles
.push_back(pFile
);
1596 nQueueSize
+= entry
->desc
.lSizeCompressed
;
1598 // if the queue is big enough, write it out
1599 if(nQueueSize
> g_nSizeRelinkBuffer
|| queFiles
.size() >= g_nMaxItemsRelinkBuffer
)
1602 if (!WriteZipFiles(queFiles
, fTmp
))
1607 if (!WriteZipFiles(queFiles
, fTmp
))
1610 ZipFile::ulong lOldCDROffset
= m_lCDROffset
;
1611 // the file data has now been written out. Now write the CDR
1612 #if CRY_PLATFORM_WINDOWS
1613 m_lCDROffset
= (ZipFile::ulong
)_ftelli64(fTmp
);
1615 m_lCDROffset
= ftell(fTmp
);
1617 if (m_lCDROffset
!= (ZipFile::ulong
)-1L && WriteCDR(fTmp
, m_bHeadersEncryptedOnClose
) && 0 == fflush (fTmp
))
1619 // the new file positions are already there - just discard the backup and return
1622 // recover from backup
1623 arrFiles
.Restore (arrFileEntryBackup
);
1624 m_lCDROffset
= lOldCDROffset
;
1625 m_bEncryptedHeaders
= m_bHeadersEncryptedOnClose
;
1629 // writes out the file data in the queue into the given file. Empties the queue
1630 bool ZipDir::CacheRW::WriteZipFiles(std::vector
<FileDataRecordPtr
>& queFiles
, FILE* fTmp
)
1632 for (std::vector
<FileDataRecordPtr
>::iterator it
= queFiles
.begin(); it
!= queFiles
.end(); ++it
)
1634 // set the new header offset to the file entry - we won't need it
1635 #if CRY_PLATFORM_WINDOWS
1636 const unsigned long currentPos
= (unsigned long)_ftelli64 (fTmp
);
1638 const unsigned long currentPos
= ftell (fTmp
);
1640 (*it
)->pFileEntry
->nFileHeaderOffset
= CalculateAlignedHeaderOffset((*it
)->strPath
.c_str(), currentPos
, m_fileAlignment
);
1642 // while writing the local header, the data offset will also be calculated
1643 if (ZD_ERROR_SUCCESS
!= WriteLocalHeader(fTmp
, (*it
)->pFileEntry
, (*it
)->strPath
.c_str(), m_bHeadersEncryptedOnClose
))
1646 // write the compressed file data
1647 const bool encrypt
= (*it
)->pFileEntry
->nMethod
== METHOD_DEFLATE_AND_ENCRYPT
;
1648 if (!WriteCompressedData((char*)(*it
)->GetData(), (*it
)->pFileEntry
->desc
.lSizeCompressed
, encrypt
, fTmp
))
1651 #if CRY_PLATFORM_WINDOWS
1652 assert ((*it
)->pFileEntry
->nEOFOffset
== (unsigned long)_ftelli64 (fTmp
));
1654 assert ((*it
)->pFileEntry
->nEOFOffset
== ftell (fTmp
));
1658 queFiles
.reserve (g_nMaxItemsRelinkBuffer
);
1662 void TruncateFile(FILE* file
, size_t newLength
)
1664 int filedes
= _fileno(file
);
1665 _chsize_s(filedes
, newLength
);
1668 bool ZipDir::CacheRW::EncryptArchive(EncryptionChange change
, IEncryptPredicate
* encryptContentPredicate
, int* numChanged
, int* numSkipped
)
1670 FileRecordList
arrFiles(GetRoot());
1671 arrFiles
.SortByFileOffset();
1673 size_t unusedSpace
= 0;
1674 size_t lastDataEnd
= 0;
1676 for (FileRecordList::iterator it
= arrFiles
.begin(); it
!= arrFiles
.end(); ++it
)
1678 FileEntry
* entry
= it
->pFileEntry
;
1680 if (entry
->nFileHeaderOffset
> lastDataEnd
)
1682 fseek(m_pFile
, lastDataEnd
, SEEK_SET
);
1683 size_t gapLength
= entry
->nFileHeaderOffset
- lastDataEnd
;
1684 unusedSpace
+= gapLength
;
1685 if (change
== ENCRYPT
)
1687 if (!WriteRandomData(m_pFile
, gapLength
))
1694 if (!WriteNullData(gapLength
))
1700 lastDataEnd
= entry
->nEOFOffset
;
1707 // find the file data offset
1708 if (ZD_ERROR_SUCCESS
!= Refresh (entry
))
1713 ZipFile::ushort oldMethod
= entry
->nMethod
;
1714 ZipFile::ushort newMethod
= oldMethod
;
1715 if (change
== ENCRYPT
)
1717 if (entry
->nMethod
== METHOD_DEFLATE
)
1719 newMethod
= METHOD_DEFLATE_AND_ENCRYPT
;
1724 if (entry
->nMethod
== METHOD_DEFLATE_AND_ENCRYPT
)
1726 newMethod
= METHOD_DEFLATE
;
1730 // allow encryption only for matching files
1731 if (newMethod
== METHOD_DEFLATE_AND_ENCRYPT
&&
1732 (!encryptContentPredicate
|| !encryptContentPredicate
->Match(it
->strPath
.c_str())))
1734 newMethod
= METHOD_DEFLATE
;
1737 entry
->nMethod
= newMethod
;
1739 const bool encryptHeaders
= change
== ENCRYPT
;
1740 // encryption is toggled or compression method changed...
1741 if (newMethod
!= oldMethod
|| encryptHeaders
!= m_bEncryptedHeaders
)
1743 // ... update header
1744 if (ZipDir::WriteLocalHeader(m_pFile
, entry
, it
->strPath
.c_str(), encryptHeaders
) != ZD_ERROR_SUCCESS
)
1750 if (newMethod
== oldMethod
)
1752 // no need to update file content
1756 // go to the file data
1757 #if CRY_PLATFORM_WINDOWS
1758 if (_fseeki64 (m_pFile
, (__int64
)entry
->nFileDataOffset
, SEEK_SET
) != 0)
1760 if (fseek (m_pFile
, entry
->nFileDataOffset
, SEEK_SET
) != 0)
1764 // allocate memory for the file compressed data
1765 FileDataRecordPtr pFile
= FileDataRecord::New(*it
);
1771 // read the compressed data
1772 if (entry
->desc
.lSizeCompressed
&& fread (pFile
->GetData(), entry
->desc
.lSizeCompressed
, 1, m_pFile
) != 1)
1777 if (oldMethod
== METHOD_DEFLATE_AND_ENCRYPT
)
1779 ZipDir::Decrypt((char*)pFile
->GetData(), entry
->desc
.lSizeCompressed
, m_encryptionKey
);
1782 #if CRY_PLATFORM_WINDOWS
1783 if (_fseeki64 (m_pFile
, (__int64
)entry
->nFileDataOffset
, SEEK_SET
) != 0)
1785 if (fseek (m_pFile
, entry
->nFileDataOffset
, SEEK_SET
) != 0)
1791 const bool encryptContent
= newMethod
== METHOD_DEFLATE_AND_ENCRYPT
;
1792 if (!WriteCompressedData((const char*)pFile
->GetData(), entry
->desc
.lSizeCompressed
, encryptContent
, m_pFile
))
1807 m_bEncryptedHeaders
= change
== ENCRYPT
;
1808 m_bHeadersEncryptedOnClose
= m_bEncryptedHeaders
;
1810 if (!WriteCDR(m_pFile
, m_bEncryptedHeaders
))
1815 if (fflush (m_pFile
) != 0)
1820 size_t endOfCDR
= (size_t)ftell(m_pFile
);
1822 fseek(m_pFile
, 0, SEEK_END
);
1823 size_t fileSize
= (size_t)ftell(m_pFile
);
1825 if (fileSize
!= endOfCDR
)
1827 TruncateFile(m_pFile
, endOfCDR
);
1831 if (unusedSpace
> 0)
1833 // RCLog("Archive contains %i bytes of uncompacted space.", (int)unusedSpace);