(http://www.purebasic.fr/english/viewtop ... 12&t=66053)
Code: Alles auswählen
;/ === ZipModule.pbi === [ PureBasic V5.6x ]
;/
;/ by nalor (optimized Thorsten1867)
;/ http://www.purebasic.fr/english/viewtopic.php?f=12&t=66053
; ===== Commands ==================
; AddFile(sZipfile.s, sFileToAdd.s, sZippedFileName.s)
; AddFileFromMem(sZipfile.s, *SrcMem, iSrcSize.i, sZippedFileName.s, iPreFileHdl.i=-1, lDateTime.l=-1)
; ChangeFileDateTime(sZipfile.s, sFileToModify.s, lUnixTimeStamp.l, iCaseSensitiv.i=#False)
; Examine(sZipfile.s, List EntryList.ZIP_EntryData(), iPreFileHdl.i=-1)
; RemoveFile(sZipfile.s, sFileToRemove.s, iCaseSensitiv.i=#False, iFileHdl.i=-1)
; GetErrorText(iError.i)
; =================================
;- ## 0-HISTORY:
; 20160306 .. nalor .. add support for PreFileHdl to speed up processing of multiple files inside a single zip file
; 20160501 .. nalor .. add ZIP_ChangeDateTime and insert it into ZIP_AddFileFromMem
; 20160627 .. nalor .. moved into a module
; 20160628 .. nalor .. created examples-section, corrected bug with subdirectories in _ZIP_Common
; 20160629 .. nalor .. add support to set UTF8-flag in header in case the filename requires UTF8 (Windows 8.1 zip engine relies on this flag)
DeclareModule ZIP
Enumeration
#ZIP_OK = #True
#ZIP_NOT_OK = #False
#ZIP_ERR_INVALID = -1
#ZIP_ERR_READ_FILE = -2
#ZIP_ERR_ALLOC_MEM = -3
#ZIP_ERR_MULTI_FILE = -4
#ZIP_ERR_DATA_DESCR = -5
#ZIP_ERR_ZIP64 = -6
#ZIP_ERR_WRITE_FILE = -7
#ZIP_ERR_DELETE_DATA = -8
#ZIP_ERR_SIGNATURE = -9
#ZIP_ERR_ENTRY_NOT_FOUND = -10
#ZIP_ERR_ADDELEMENT = -11
#ZIP_ERR_WRONG_PARAMETER = -12
#ZIP_ERR_UNSPEC_ERROR = -13
EndEnumeration
Structure ZIP_EntryData
EntryName.s
EntrySizeCompressed.q
EntrySizeUncompressed.q
EntryType.i ; #PB_Packer_File / #PB_Packer_Directory
EntryLastModFileDateTime.l
EntryCRC32.l
EndStructure
Declare.i AddFile(sZipfile.s, sFileToAdd.s, sZippedFileName.s)
Declare.i AddFileFromMem(sZipfile.s, *SrcMem, iSrcSize.i, sZippedFileName.s, iPreFileHdl.i=-1, lDateTime.l=-1)
Declare.i ChangeFileDateTime(sZipfile.s, sFileToModify.s, lUnixTimeStamp.l, iCaseSensitiv.i=#False)
Declare.i Examine(sZipfile.s, List EntryList.ZIP_EntryData(), iPreFileHdl.i=-1)
Declare.i RemoveFile(sZipfile.s, sFileToRemove.s, iCaseSensitiv.i=#False, iFileHdl.i=-1)
Declare.s GetErrorText(iError.i)
EndDeclareModule
Module ZIP
UseZipPacker()
EnableExplicit
DisableDebugger
Procedure.s GetErrorText(iError.i)
Select iError
Case #ZIP_OK : ProcedureReturn "ZIP OK"
Case #ZIP_NOT_OK : ProcedureReturn "ZIP NOT OK"
Case #ZIP_ERR_INVALID : ProcedureReturn "ZIP ERR INVALID"
Case #ZIP_ERR_READ_FILE : ProcedureReturn "ZIP ERR READ FILE"
Case #ZIP_ERR_ALLOC_MEM : ProcedureReturn "ZIP ERR ALLOC MEM"
Case #ZIP_ERR_MULTI_FILE : ProcedureReturn "ZIP ERR MULTI FILE"
Case #ZIP_ERR_DATA_DESCR : ProcedureReturn "ZIP ERR DATA DESCR"
Case #ZIP_ERR_ZIP64 : ProcedureReturn "ZIP ERR ZIP64"
Case #ZIP_ERR_WRITE_FILE : ProcedureReturn "ZIP ERR WRITE FILE"
Case #ZIP_ERR_DELETE_DATA : ProcedureReturn "ZIP ERR DELETE DATA"
Case #ZIP_ERR_SIGNATURE : ProcedureReturn "ZIP ERR SIGNATURE"
Case #ZIP_ERR_ENTRY_NOT_FOUND : ProcedureReturn "ZIP ERR ENTRY NOT FOUND"
Case #ZIP_ERR_ADDELEMENT : ProcedureReturn "ZIP ERR ADDELEMENT"
Case #ZIP_ERR_WRONG_PARAMETER : ProcedureReturn "ZIP ERR WRONG PARAMETER"
Case #ZIP_ERR_UNSPEC_ERROR : ProcedureReturn "ZIP ERR UNSPEC ERROR"
Default
ProcedureReturn "ZIP UNKNOWN ERROR"
EndSelect
EndProcedure
Procedure.s HexQ2(qInput.q)
ProcedureReturn "0x"+RSet(Hex(qInput, #PB_Quad), 16, "0")
EndProcedure
Procedure.l DosToUnixTime(uDosDate.u, uDosTime.u)
ProcedureReturn Date(((uDosDate>>9) & $7F)+1980, (uDosDate>>5) & $0F, uDosDate & $1F, (uDosTime>>11) & $1F, (uDosTime>>5) & $3F, (uDosTime<<1) & $3E)
EndProcedure
Procedure UnixToDosTime(UnixTime.l, *uDosDate.UNICODE, *uDosTime.UNICODE)
If Year(UnixTime)<1980
UnixTime=Date(1980, 1, 1, 0, 0, 0)
EndIf
*uDosDate\u=((Year(UnixTime) - 1980) << 9) | (Month(UnixTime) << 5) | (Day(UnixTime))
*uDosTime\u=(Hour(UnixTime) << 11) | (Minute(UnixTime) << 5) | (Second(UnixTime) >> 1)
EndProcedure
Procedure.q FindHexInFileHdl(iFileHdl.i, sHexToFind.s, qStartPosition.q=0, iDirectionFwd.i=#True)
Protected iSrchDataLen.i = Len(sHexToFind)/2
Protected *SrchData
Protected *TestData
Protected iTmp.i
Protected qFileSize.q
Protected qFileDataRead.q
Protected iChunkRead.i
Protected qSearchResult.q=-4 ; -4 = nothing found
Protected iChunkSize.i=10000000 ; 10 Mill. Byte ;)
If Not IsFile(iFileHdl)
ProcedureReturn -1
EndIf
If iSrchDataLen=0 Or qStartPosition<0 Or (Len(sHexToFind)%2)<>0
ProcedureReturn -1
EndIf
qFileSize=Lof(iFileHdl)
*SrchData=AllocateMemory(iSrchDataLen)
*TestData=AllocateMemory(iChunkSize)
If *SrchData=0 Or *TestData=0
If *SrchData : FreeMemory(*SrchData) : EndIf
If *TestData : FreeMemory(*TestData) : EndIf
ProcedureReturn -2
EndIf
For iTmp=0 To iSrchDataLen-1
PokeB(*SrchData+iTmp, Val("$"+Mid(sHexToFind,iTmp*2+1, 2)))
Next
If iDirectionFwd
qFileDataRead=qStartPosition
While qFileDataRead<qFileSize
FileSeek(iFileHdl, qFileDataRead)
iChunkRead=ReadData(iFileHdl, *TestData, iChunkSize)
If iChunkRead>iSrchDataLen
For iTmp=0 To iChunkRead-iSrchDataLen
If CompareMemory(*TestData+iTmp, *SrchData, iSrchDataLen)
qSearchResult=qFileDataRead+iTmp
Break 2
EndIf
Next
qFileDataRead+iTmp
Else
qFileDataRead+iChunkRead
EndIf
Wend
Else
If qStartPosition<>0
qFileDataRead=qStartPosition
Else
qFileDataRead=qFileSize
EndIf
If qFileDataRead>=iChunkSize
qFileDataRead-iChunkSize
Else
iChunkSize=qFileDataRead
qFileDataRead=0
EndIf
While qFileDataRead>=0
FileSeek(iFileHdl, qFileDataRead)
iChunkRead=ReadData(iFileHdl, *TestData, iChunkSize)
For iTmp=iChunkRead-iSrchDataLen To 0 Step -1
If CompareMemory(*TestData+iTmp, *SrchData, iSrchDataLen)
qSearchResult=qFileDataRead+iTmp
Break 2
EndIf
Next
If qFileDataRead>(iChunkSize+iSrchDataLen-1)
qFileDataRead-iChunkSize+iSrchDataLen-1
ElseIf qFileDataRead>0
iChunkSize=qFileDataRead+iSrchDataLen-1
qFileDataRead=0
Else
qFileDataRead=-1
EndIf
Wend
EndIf
FreeMemory(*SrchData)
FreeMemory(*TestData)
ProcedureReturn qSearchResult
EndProcedure
#ZIP_Signature_LFH = $04034b50 ; LocalFileHeader
#ZIP_Signature_LFDD = $08074b50 ; LocalFileDataDescriptor
#ZIP_Signature_CDH = $02014b50 ; CentralDirectoryHeader
#ZIP_Signature_EOCDR = $06054b50 ; EndOfCentralDirectoryRecord
#ZIP_Signature_AEDD = $08064b50 ; ArchiveExtraDataRecord
#ZIP_Signature_DS = $05054b50 ; DigitalSignature
#ZIP_Signature_ZIP64_EOCDR = $06064b50 ; Zip64 EndOfCentralDirectoryRecord
#ZIP_Signature_ZIP64_EOCDL = $07064b50 ; Zip64 EndOfCentralDirectoryLocator
Structure ZIP_LocalFileHeaderV2
Signature.l ; (0x 04 03 4b 50)
VersionNeededToExtract.u ; 2
GeneralPurpose.u ;
CompressionMethod.u
LastModFileTime.u
LastModFileDate.u
CRC32.l
CompressedSize.l ; ohne
UncompressedSize.l
FilenameLength.u
ExtraFieldLength.u
; filename (variable length)
; extra field (variable length)
EndStructure
Structure ZIP_DataDescriptor ; exist after the data-block if bit3 of the GeneralPurpose is set - eventually preceeded by a signature 0x08074b50
CRC32.l
CompressedSize.l
UncompressedSize.l
EndStructure
Structure ZIP64_DataDescriptor ; exist after the data-block if bit3 of the GeneralPurpose is set (APPNOTE 6.3.4 - section 4.3.9.1)
CRC32.l
CompressedSize.q
UncompressedSize.q
EndStructure
Structure ZIP_CentralDirectoryHeaderV2
Signature.l ; (0x 02 01 4b 50)
VersionMadeBy.u
VersionNeededToExtract.u
GeneralPurpose.u
CompressionMethod.u
LastModFileTime.u
LastModFileDate.u
CRC32.l
CompressedSize.l ; ohne
UncompressedSize.l
FilenameLength.u
ExtraFieldLength.u
FileCommentLength.u
DiskNumberStart.u
InternalFileAttributes.u
ExternalFileAttributes.l
OffsetOfLocalHeader.l ; Beginn des zugehörigen Datenblocks gemessen vom Fileanfang
EndStructure
Structure ZIP_EndOfCentralDirectoryRecordV2
Signature.l ; (0x 06 05 4b 50)
NumberOfDisk.u
NumberCDStart.u
NumberOfEntries.u
NumberOfTotalEntries.u
CentralDirSize.l
CentralDirStartOffset.l
CommentLength.u
EndStructure
Structure ZIP_MoveEntryData
CDH_Offset.q
CDH_Size.q
LFH_Offset.q
LFH_Size.q
EndStructure
Procedure seems_utf8(*StrMem, iLen.i)
Protected iCnt.i
Protected aCharVal.a
Protected iNext.i
Protected iCnt2.i
Protected iUtf8Detected.i=#False
For iCnt=0 To iLen-1
aCharVal = PeekA(*StrMem+iCnt)
If (aCharVal < $80) : iNext = 0 ; # 0bbbbbbb
ElseIf ((aCharVal & $E0) = $C0) : iNext=1 ; # 110bbbbb
ElseIf ((aCharVal & $F0) = $E0) : iNext=2 ; # 1110bbbb
ElseIf ((aCharVal & $F8) = $F0) : iNext=3 ; # 11110bbb
ElseIf ((aCharVal & $FC) = $F8) : iNext=4 ; # 111110bb
ElseIf ((aCharVal & $FE) = $FC) : iNext=5 ; # 1111110b
Else
ProcedureReturn #False ; # Does not match any model
EndIf
If iNext>0
iUtf8Detected=#True
For iCnt2=1 To iNext ;# n bytes matching 10bbbbbb follow ?
If iCnt+iCnt2>iLen Or (PeekA(*StrMem+iCnt+iCnt2) & $C0)<>$80
ProcedureReturn #False
EndIf
Next
iCnt+iCnt2-1
EndIf
Next
ProcedureReturn iUtf8Detected
EndProcedure
Procedure StringUtf8Required(sText.s)
If StringByteLength(sText, #PB_UTF8)>Len(sText) ; in case each character can be stored as ascii - the bytelen in utf8 would be identical to the characterlen, if this isn't >> UTF8 necessary
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
Procedure.i _ZIP_DropDataFromFileHdl(iFileHdl.i, qOffset.q, qDataSize.q)
Protected qChunkSize.q=10000000 ; 10MB
Protected *ChunkMem=0
Protected iResult.i=#ZIP_OK
Protected qProcessedData.q
Protected qTmp.q
Protected qReadOffset.q
Protected qWriteOffset.q
Repeat ; single repeat
If Not IsFile(iFileHdl)
iResult=#ZIP_ERR_INVALID
Break
EndIf
qReadOffset=qOffset+qDataSize
If qReadOffset>Lof(iFileHdl)
iResult=#ZIP_ERR_INVALID
Break
EndIf
*ChunkMem=AllocateMemory(qChunkSize)
If Not *ChunkMem
iResult=#ZIP_ERR_ALLOC_MEM
Break
EndIf
qWriteOffset=qOffset
qProcessedData=0
Repeat
FileSeek(iFileHdl, qReadOffset+qProcessedData)
qTmp=ReadData(iFileHdl, *ChunkMem, qChunkSize)
If Not qTmp
iResult=#ZIP_ERR_READ_FILE
Break 2
EndIf
FileSeek(iFileHdl, qWriteOffset+qProcessedData)
If Not WriteData(iFileHdl, *ChunkMem, qTmp)
iResult=#ZIP_ERR_WRITE_FILE
Break 2
EndIf
qProcessedData+qTmp
Until qReadOffset+qProcessedData>=Lof(iFileHdl) ; until the end of the file is reached
FileSeek(iFileHdl, qWriteOffset+qProcessedData)
TruncateFile(iFileHdl)
Until #True
If *ChunkMem : FreeMemory(*ChunkMem) : EndIf
ProcedureReturn iResult
EndProcedure
Procedure.i _ZIP_InsertGapIntoFileHdl(iFileHdl.i, qOffset.q, qSize.q, qChunkSize.q=0, *ChunkMem=0)
Protected iResult.i=#ZIP_OK
Protected qTmp.q
Protected aMemAlloc.a=#False
Protected qReadOffset.q
Protected qWriteOffset.q
Protected qReadSize.q
Repeat
If Not IsFile(iFileHdl)
iResult=#ZIP_ERR_INVALID
Break
EndIf
If qChunkSize=0 ; only allocate memory in case it's not preallocated
qChunkSize=10000000 ; 10MB
*ChunkMem=AllocateMemory(qChunkSize)
If Not *ChunkMem
iResult=#ZIP_ERR_ALLOC_MEM
Break
Else
aMemAlloc=#True
EndIf
EndIf
qReadOffset=Lof(iFileHdl)
qReadSize=qChunkSize
FileSeek(iFileHdl, 0)
FileSeek(iFileHdl, qReadOffset+qSize-1, #PB_Relative)
WriteByte(iFileHdl, $00)
Repeat
qReadOffset-qReadSize
If qReadOffset<qOffset
qReadSize-(qOffset-qReadOffset)
qReadOffset=qOffset
EndIf
FileSeek(iFileHdl, qReadOffset)
qTmp=ReadData(iFileHdl, *ChunkMem, qReadSize)
If Not qTmp
iResult=#ZIP_ERR_READ_FILE
Break 2
EndIf
FileSeek(iFileHdl, qReadOffset+qSize)
If Not WriteData(iFileHdl, *ChunkMem, qTmp)
iResult=#ZIP_ERR_WRITE_FILE
Break 2
EndIf
Until qReadOffset=qOffset
Until #True
If aMemAlloc : FreeMemory(*ChunkMem) : EndIf
ProcedureReturn iResult
EndProcedure
Procedure.i _ZIP_CopyDataFromFile2FileHdl(sSrcFile.s, qSrcOffset.q, qSrcSize.q, iDstFileHdl.i, qDstOffset.q)
Protected qChunkSize.q=10000000 ; 10MB
Protected *ChunkMem=0
Protected iResult.i=#ZIP_OK
Protected qTmp.q
Protected qReadOffset.q
Protected qWriteOffset.q
Protected qReadSize.q
Protected iSrcFileHdl.i
Repeat ; single repeat
If qSrcSize<=0
iResult=#ZIP_ERR_WRONG_PARAMETER
Break
EndIf
If Not IsFile(iDstFileHdl)
iResult=#ZIP_ERR_INVALID
Break
EndIf
iSrcFileHdl=ReadFile(#PB_Any, sSrcFile)
If Not iSrcFileHdl
iResult=#ZIP_ERR_READ_FILE
Break
EndIf
*ChunkMem=AllocateMemory(qChunkSize)
If Not *ChunkMem
iResult=#ZIP_ERR_ALLOC_MEM
Break
EndIf
If qDstOffset<Lof(iDstFileHdl) ; only in case the destination is inside the current file - otherwise there's no data that needs to be moved to the back
iResult=_ZIP_InsertGapIntoFileHdl(iDstFileHdl, qDstOffset, qSrcSize, qChunkSize, *ChunkMem)
If iResult<>#ZIP_OK : Break : EndIf
Else
FileSeek(iDstFileHdl, 0)
FileSeek(iDstFileHdl, qDstOffset-1, #PB_Relative)
WriteByte(iDstFileHdl, $00)
EndIf
qReadOffset=qSrcOffset
qWriteOffset=qDstOffset
qReadSize=qChunkSize
Repeat
If qReadOffset+qReadSize>qSrcOffset+qSrcSize
qReadSize=qSrcOffset+qSrcSize-qReadOffset
EndIf
FileSeek(iSrcFileHdl, qReadOffset)
qTmp=ReadData(iSrcFileHdl, *ChunkMem, qReadSize)
If Not qTmp
iResult=#ZIP_ERR_READ_FILE
Break 2
EndIf
FileSeek(iDstFileHdl, qWriteOffset)
If Not WriteData(iDstFileHdl, *ChunkMem, qTmp)
iResult=#ZIP_ERR_WRITE_FILE
Break 2
EndIf
qReadOffset+qReadSize
qWriteOffset+qReadSize
Until qReadOffset+qReadSize>=qSrcOffset+qSrcSize
Until #True
If *ChunkMem : FreeMemory(*ChunkMem) : EndIf
If IsFile(iSrcFileHdl)
CloseFile(iSrcFileHdl)
EndIf
ProcedureReturn iResult
EndProcedure
Procedure.s _ZIP_GetFilename(iFileHdl.i, qOffset.q, iFilenameLen.i, iGeneralPurpose.i, *Error.Integer)
Protected *FilenameMem
Protected sFilename.s=""
*FilenameMem=AllocateMemory(iFilenameLen+2)
If *FilenameMem
FileSeek(iFileHdl, qOffset)
If Not ReadData(iFileHdl, *FilenameMem, iFilenameLen)
*Error\i=#ZIP_ERR_READ_FILE
Else
If (iGeneralPurpose & $800) Or seems_utf8(*FilenameMem, iFilenameLen)
sFilename=PeekS(*FilenameMem, -1, #PB_UTF8)
Else
sFilename=PeekS(*FilenameMem, -1, #PB_Ascii)
EndIf
EndIf
FreeMemory(*FilenameMem)
Else
*Error\i=#ZIP_ERR_ALLOC_MEM
EndIf
ProcedureReturn sFilename
EndProcedure
Procedure.i _ZIP_VerifySignature(iFileHdl.i, qOffset.q, lSignature.l)
Protected lFileSignature.l
Protected iResult.i
FileSeek(iFileHdl, qOffset)
If Not ReadData(iFileHdl, @lFileSignature, 4)
iResult=#ZIP_ERR_READ_FILE
Else
If lSignature=lFileSignature
iResult=#ZIP_OK
Else
iResult=#ZIP_NOT_OK
EndIf
EndIf
ProcedureReturn iResult
EndProcedure
Procedure _ZIP_ReadDataDescriptor(iFileHdl, qOffset.q, *DataDescriptor.ZIP_DataDescriptor, *DataDescriptorSize.Integer)
Protected iResult.i
Protected iSignatureCnt.i
Protected qNewOffset.q
Protected aDataDescriptorFound.a
Protected aDataDescriptorSigFound.a
iResult=#ZIP_OK
iSignatureCnt=0
aDataDescriptorFound=#False
aDataDescriptorSigFound=#False
; Method 1 - search for a Signature '08074B50'
qNewOffset=FindHexInFileHdl(iFileHdl, "504B0708", qOffset)
If qNewOffset
qNewOffset+4 ; we don't need the signature
Debug "Signature of DataDescriptor found!", 3
aDataDescriptorFound=#True
aDataDescriptorSigFound=#True
EndIf
; Method 2 - search for another Signature and guess that the DataDescriptor has to be in the bytes before the next Signature!
If Not aDataDescriptorFound
qNewOffset=FindHexInFileHdl(iFileHdl, "504B0304", qOffset) ; search for another LocalFileHeader
If qNewOffset
qNewOffset-SizeOf(ZIP_DataDescriptor)
aDataDescriptorFound=#True
EndIf
EndIf
If Not aDataDescriptorFound
qNewOffset=FindHexInFileHdl(iFileHdl, "504B0608", qOffset) ; search for an ArchiveDecryptionHeader
If qNewOffset
qNewOffset-SizeOf(ZIP_DataDescriptor)
aDataDescriptorFound=#True
EndIf
EndIf
If Not aDataDescriptorFound
qNewOffset=FindHexInFileHdl(iFileHdl, "504B0102", qOffset) ; search for an CentralDirectoryHeader
If qNewOffset
qNewOffset-SizeOf(ZIP_DataDescriptor)
aDataDescriptorFound=#True
EndIf
EndIf
If aDataDescriptorFound ; read DataDescriptorValues
FileSeek(iFileHdl, qNewOffset)
If Not ReadData(iFileHdl, *DataDescriptor, SizeOf(ZIP_DataDescriptor))
iResult=#ZIP_ERR_READ_FILE
EndIf
If iResult=#ZIP_OK ; check if the DataDescriptionHeader is the correct one
If qOffset+*DataDescriptor\CompressedSize<>qNewOffset-aDataDescriptorSigFound*4
Debug "ERROR! Couldn't find correct DataDescriptor! Offset >"+qOffset+"< ComprSize >"+*DataDescriptor\CompressedSize+"< DataDescriptorOffset >"+Str(qNewOffset+aDataDescriptorSigFound*4)+"<"
iResult=#ZIP_ERR_INVALID
*DataDescriptor\CompressedSize=0
*DataDescriptor\UncompressedSize=0
*DataDescriptor\CRC32=0
EndIf
EndIf
If iResult=#ZIP_OK
*DataDescriptorSize\i=SizeOf(ZIP_DataDescriptor)+aDataDescriptorSigFound*4
EndIf
EndIf
ProcedureReturn iResult
EndProcedure
Enumeration
#ZIP_Mode_RemoveFile = 1
#ZIP_Mode_ExtractFile = 2
#ZIP_Mode_ExamineFile = 3
#ZIP_Mode_GetEntryData = 4
#ZIP_Mode_CopyEntry_File2File = 5
#ZIP_Mode_ModifyHeader = 6
EndEnumeration
Structure ZipEntryData
List EntryList.ZIP_EntryData()
EndStructure
Procedure _ZIP_Common(sZipFile.s, iProcessMode.i, sFileToProcess.s="", iCaseSensitiv.i=#False, *EntryList.ZipEntryData=0, *MoveEntryData.ZIP_MoveEntryData=0, iPreFileHdl.i=-1)
Protected ZIP_EOCDR.ZIP_EndOfCentralDirectoryRecordV2
Protected ZIP_CDH.ZIP_CentralDirectoryHeaderV2
Protected ZIP_LFH.ZIP_LocalFileHeaderV2
Protected ZIP_LFDD.ZIP_DataDescriptor
Protected iDataDescriptorSize.i
Protected *ZIP_CDH_Filename
Protected EndOfCentralDirectory_Offset.l
Protected iFileHdl.i
Protected iResult.i=#ZIP_OK
Protected qCurrentOffset.q
Protected iFileCnt.i
Protected sFilenameLFH.s
Protected sFilenameCDH.s
Protected qTemp.q
Protected iFileFound.i
Protected iCorrectHeaderOffset.i=#False
Protected qFileRemove_DataOffset.q
Protected qFileRemove_DataLen.q
Protected qFileRemove_CDH_Offset.q
Protected qFileRemove_CDH_Len.q
Protected qFileRemove_CompleteDataLen.q
Protected *CompressedMemory
Protected *UncompressedMemory
Protected DstFileEntry.ZIP_MoveEntryData
Protected iUnixTime.i
Protected iUtf8Filename.i
If iProcessMode=#ZIP_Mode_ExamineFile And *EntryList=0 : iResult=#ZIP_ERR_WRONG_PARAMETER : EndIf
If iProcessMode=#ZIP_Mode_GetEntryData And *MoveEntryData=0 : iResult=#ZIP_ERR_WRONG_PARAMETER : EndIf
If iProcessMode=#ZIP_Mode_CopyEntry_File2File And (sFileToProcess="" Or *MoveEntryData=0) : iResult=#ZIP_ERR_WRONG_PARAMETER : EndIf
If iProcessMode=#ZIP_Mode_ModifyHeader
iUnixTime=*EntryList
iUtf8Filename=*MoveEntryData
EndIf
If iProcessMode<>#ZIP_Mode_ExamineFile
ReplaceString(sFileToProcess, "\", "/", #PB_String_InPlace) ; all backslashes are converted to slashes to support subdirectories properly
EndIf
If iResult=#ZIP_OK
If iPreFileHdl=-1
iFileHdl=ReadFile(#PB_Any, sZipFile, #PB_File_SharedRead)
If iFileHdl=0 : iResult=#ZIP_ERR_READ_FILE : EndIf
Else
If IsFile(iPreFileHdl)
iFileHdl=iPreFileHdl
Else
iResult=#ZIP_ERR_READ_FILE
EndIf
EndIf
EndIf
If iResult=#ZIP_OK
EndOfCentralDirectory_Offset=FindHexInFileHdl(iFileHdl, "504B0506", 0, #False) ; find 'end of central dir signature'
If EndOfCentralDirectory_Offset<0
iResult=#ZIP_ERR_INVALID
EndIf
EndIf
If iResult=#ZIP_OK
FileSeek(iFileHdl, EndOfCentralDirectory_Offset)
If Not ReadData(iFileHdl, @ZIP_EOCDR, SizeOf(ZIP_EndOfCentralDirectoryRecordV2))
iResult=#ZIP_ERR_READ_FILE
EndIf
EndIf
If iResult=#ZIP_OK
If ZIP_EOCDR\NumberOfDisk>0 Or ZIP_EOCDR\NumberOfEntries<>ZIP_EOCDR\NumberOfTotalEntries
iResult=#ZIP_ERR_MULTI_FILE
EndIf
EndIf
If iResult=#ZIP_OK
If iProcessMode=#ZIP_Mode_ExamineFile
ClearList(*EntryList\EntryList())
EndIf
EndIf
If iResult=#ZIP_OK And iProcessMode<>#ZIP_Mode_RemoveFile ; we don't need to parse all CDH/LFH in the zip file to remove it
qCurrentOffset=ZIP_EOCDR\CentralDirStartOffset
iFileCnt=0
iFileFound=#False
Repeat
If _ZIP_VerifySignature(iFileHdl, qCurrentOffset, #ZIP_Signature_CDH)<#ZIP_OK ; verify CentralDirectoryHeader-Signature
iResult=#ZIP_ERR_SIGNATURE
Break
EndIf
FileSeek(iFileHdl, qCurrentOffset)
If Not ReadData(iFileHdl, @ZIP_CDH, SizeOf(ZIP_CentralDirectoryHeaderV2))
iResult=#ZIP_ERR_READ_FILE
Break
EndIf
sFilenameCDH=_ZIP_GetFilename(iFileHdl, qCurrentOffset+SizeOf(ZIP_CentralDirectoryHeaderV2), ZIP_CDH\FilenameLength, ZIP_CDH\GeneralPurpose, @iResult)
If iResult<>#ZIP_OK
Break
EndIf
If ZIP_CDH\CompressedSize=-1 Or ZIP_CDH\UncompressedSize=-1 Or ZIP_CDH\CRC32=-1
iResult=#ZIP_ERR_ZIP64
Break
EndIf
iDataDescriptorSize=0
If iProcessMode=#ZIP_Mode_ExamineFile
If AddElement(*EntryList\EntryList())
*EntryList\EntryList()\EntryName=sFilenameCDH
*EntryList\EntryList()\EntrySizeCompressed=ZIP_CDH\CompressedSize
*EntryList\EntryList()\EntrySizeUncompressed=ZIP_CDH\UncompressedSize
*EntryList\EntryList()\EntryLastModFileDateTime=DosToUnixTime(ZIP_CDH\LastModFileDate, ZIP_CDH\LastModFileTime)
*EntryList\EntryList()\EntryCRC32=ZIP_CDH\CRC32
If ZIP_CDH\CompressedSize=0
*EntryList\EntryList()\EntryType=#PB_Packer_Directory
Else
*EntryList\EntryList()\EntryType=#PB_Packer_File
EndIf
Else
iResult=#ZIP_ERR_ADDELEMENT
Break
EndIf
ElseIf ((iCaseSensitiv=#True And sFilenameCDH=sFileToProcess) Or (iCaseSensitiv=#False And LCase(sFilenameCDH)=LCase(sFileToProcess))) And iProcessMode<>#ZIP_Mode_CopyEntry_File2File
iFileFound=#True
If _ZIP_VerifySignature(iFileHdl, ZIP_CDH\OffsetOfLocalHeader, #ZIP_Signature_LFH)<#ZIP_OK ; verify LocalFileHeader-Signature
iResult=#ZIP_ERR_SIGNATURE
Break
EndIf
FileSeek(iFileHdl, ZIP_CDH\OffsetOfLocalHeader)
If Not ReadData(iFileHdl, @ZIP_LFH, SizeOf(ZIP_LocalFileHeaderV2))
iResult=#ZIP_ERR_READ_FILE
Break
EndIf
sFilenameLFH=_ZIP_GetFilename(iFileHdl, ZIP_CDH\OffsetOfLocalHeader+SizeOf(ZIP_LocalFileHeaderV2), ZIP_LFH\FilenameLength, ZIP_CDH\GeneralPurpose, @iResult)
If iResult<>#ZIP_OK
Break
EndIf
If ZIP_LFH\CompressedSize=-1 Or ZIP_LFH\UncompressedSize=-1 Or ZIP_LFH\CRC32=-1
iResult=#ZIP_ERR_ZIP64
Break
EndIf
If (ZIP_LFH\GeneralPurpose & $8)
iResult=_ZIP_ReadDataDescriptor(iFileHdl, ZIP_CDH\OffsetOfLocalHeader+SizeOf(ZIP_LocalFileHeaderV2)+ZIP_LFH\FilenameLength+ZIP_LFH\ExtraFieldLength, @ZIP_LFDD, @iDataDescriptorSize)
If iResult>0
ZIP_LFH\CRC32 = ZIP_LFDD\CRC32
ZIP_LFH\CompressedSize = ZIP_LFDD\CompressedSize
ZIP_LFH\UncompressedSize = ZIP_LFDD\UncompressedSize
Else
Break
EndIf
EndIf
If sFilenameCDH<>sFilenameLFH
iResult=#ZIP_ERR_INVALID
Break
EndIf
If iProcessMode=#ZIP_Mode_GetEntryData
*MoveEntryData\CDH_Offset = qCurrentOffset
*MoveEntryData\CDH_Size = SizeOf(ZIP_CentralDirectoryHeaderV2)+ZIP_CDH\FilenameLength+ZIP_CDH\ExtraFieldLength+ZIP_CDH\FileCommentLength
*MoveEntryData\LFH_Offset = ZIP_CDH\OffsetOfLocalHeader
*MoveEntryData\LFH_Size = SizeOf(ZIP_LocalFileHeaderV2)+ZIP_LFH\FilenameLength+ZIP_LFH\ExtraFieldLength+ZIP_LFH\CompressedSize+iDataDescriptorSize
Break
EndIf
If iProcessMode=#ZIP_Mode_ModifyHeader
If iUnixTime<>-1
UnixToDosTime(iUnixTime, @ZIP_LFH\LastModFileDate, @ZIP_LFH\LastModFileTime)
ZIP_CDH\LastModFileDate=ZIP_LFH\LastModFileDate
ZIP_CDH\LastModFileTime=ZIP_LFH\LastModFileTime
EndIf
If iUtf8Filename<>-1
If iUtf8Filename=1 ; in case filename is UTF8 encoded (set BIT11 to 1)
ZIP_LFH\GeneralPurpose=ZIP_LFH\GeneralPurpose | $800
ElseIf iUtf8Filename=0 ; in case filename is ASCII only (set BIT11 to 0)
ZIP_LFH\GeneralPurpose=ZIP_LFH\GeneralPurpose & ~$800
EndIf
ZIP_CDH\GeneralPurpose=ZIP_LFH\GeneralPurpose
EndIf
If iPreFileHdl=-1 ; only in case we opened the file ourselves
CloseFile(iFileHdl)
iFileHdl=OpenFile(#PB_Any, sZipFile, #PB_File_SharedRead)
If iFileHdl=0
iResult=#ZIP_ERR_READ_FILE
Break
EndIf
EndIf
FileSeek(iFileHdl, ZIP_CDH\OffsetOfLocalHeader)
If Not WriteData(iFileHdl, @ZIP_LFH, SizeOf(ZIP_LocalFileHeaderV2))
iResult=#ZIP_ERR_WRITE_FILE
Break
EndIf
FileSeek(iFileHdl, qCurrentOffset)
If Not WriteData(iFileHdl, @ZIP_CDH, SizeOf(ZIP_CentralDirectoryHeaderV2))
iResult=#ZIP_ERR_WRITE_FILE
Break
EndIf
Break
EndIf
EndIf
qCurrentOffset+SizeOf(ZIP_CentralDirectoryHeaderV2)+ZIP_CDH\FilenameLength+ZIP_CDH\ExtraFieldLength+ZIP_CDH\FileCommentLength
iFileCnt+1
Until qCurrentOffset>=ZIP_EOCDR\CentralDirStartOffset+ZIP_EOCDR\CentralDirSize Or iFileCnt>=ZIP_EOCDR\NumberOfEntries
If iProcessMode<>#ZIP_Mode_CopyEntry_File2File And iProcessMode<>#ZIP_Mode_ExamineFile And iFileFound=#False
iResult=#ZIP_ERR_ENTRY_NOT_FOUND
EndIf
EndIf
If iResult=#ZIP_OK And iProcessMode=#ZIP_Mode_CopyEntry_File2File
Repeat ; single repeat - allows to simply break in case of an error
iResult=_ZIP_Common(sZipfile, #ZIP_Mode_GetEntryData, sFilenameCDH, 0, 0, @DstFileEntry, iFileHdl) ; 'sFilenameCDH' has the name of the last entry in the CentralDirectory
If iResult<>#ZIP_OK : Break : EndIf
If iPreFileHdl=-1 ; only in case we opened the file ourselves
CloseFile(iFileHdl)
iFileHdl=OpenFile(#PB_Any, sZipFile, #PB_File_SharedRead)
If iFileHdl=0
iResult=#ZIP_ERR_READ_FILE
Break
EndIf
EndIf
iResult=_ZIP_CopyDataFromFile2FileHdl(sFileToProcess, *MoveEntryData\LFH_Offset, *MoveEntryData\LFH_Size, iFileHdl, DstFileEntry\LFH_Offset+DstFileEntry\LFH_Size)
If iResult<>#ZIP_OK : Break : EndIf
qCurrentOffset=ZIP_EOCDR\CentralDirStartOffset+*MoveEntryData\LFH_Size
If _ZIP_VerifySignature(iFileHdl, qCurrentOffset, #ZIP_Signature_CDH)<#ZIP_OK ; verify CentralDirectoryHeader-Signature
iResult=#ZIP_ERR_SIGNATURE
Break
EndIf
qCurrentOffset+ZIP_EOCDR\CentralDirSize ; add the current CentralDirSize to the currentPosition
iResult=_ZIP_CopyDataFromFile2FileHdl(sFileToProcess, *MoveEntryData\CDH_Offset, *MoveEntryData\CDH_Size, iFileHdl, qCurrentOffset)
If iResult<>#ZIP_OK : Break : EndIf
FileSeek(iFileHdl, qCurrentOffset)
If Not ReadData(iFileHdl, @ZIP_CDH, SizeOf(ZIP_CentralDirectoryHeaderV2))
iResult=#ZIP_ERR_READ_FILE
Break
EndIf
ZIP_CDH\OffsetOfLocalHeader=DstFileEntry\LFH_Offset+DstFileEntry\LFH_Size
FileSeek(iFileHdl, qCurrentOffset)
If Not WriteData(iFileHdl, @ZIP_CDH, SizeOf(ZIP_CentralDirectoryHeaderV2))
iResult=#ZIP_ERR_WRITE_FILE
Break
EndIf
ZIP_EOCDR\NumberOfEntries+1 ; increase entry count by one
ZIP_EOCDR\NumberOfTotalEntries+1 ; increase total entry count by one
ZIP_EOCDR\CentralDirStartOffset+*MoveEntryData\LFH_Size ; increase offset of beginning of central dir by len of new entry
ZIP_EOCDR\CentralDirSize+*MoveEntryData\CDH_Size ; increase central dir size by size of added directory entry
EndOfCentralDirectory_Offset+*MoveEntryData\LFH_Size+*MoveEntryData\CDH_Size ; increase the 'EndOfCentralDirectory_Offset' by the complete len of added bytes
If _ZIP_VerifySignature(iFileHdl, EndOfCentralDirectory_Offset, #ZIP_Signature_EOCDR)<#ZIP_OK ; verify EndOfCentralDirectory-Signature
iResult=#ZIP_ERR_SIGNATURE
Break
EndIf
FileSeek(iFileHdl, EndOfCentralDirectory_Offset)
If Not WriteData(iFileHdl, @ZIP_EOCDR, SizeOf(ZIP_EndOfCentralDirectoryRecordV2))
iResult=#ZIP_ERR_WRITE_FILE
Break
EndIf
If _ZIP_VerifySignature(iFileHdl, EndOfCentralDirectory_Offset, #ZIP_Signature_EOCDR)<#ZIP_OK ; verify EndOfCentralDirectory-Signature
iResult=#ZIP_ERR_SIGNATURE
Break
EndIf
Until #True
EndIf
If iResult=#ZIP_OK And iProcessMode=#ZIP_Mode_RemoveFile
Repeat ; single repeat - allows to simply break in case of an error
iResult=_ZIP_Common(sZipfile, #ZIP_Mode_GetEntryData, sFileToProcess, 0, 0, @DstFileEntry, iFileHdl)
If iResult<>#ZIP_OK : Break : EndIf
If iPreFileHdl=-1 ; only in case we opened the file ourselves
CloseFile(iFileHdl)
iFileHdl=OpenFile(#PB_Any, sZipFile, #PB_File_SharedRead)
If iFileHdl=0
iResult=#ZIP_ERR_READ_FILE
Break
EndIf
EndIf
iResult=_ZIP_DropDataFromFileHdl(iFileHdl, DstFileEntry\CDH_Offset, DstFileEntry\CDH_Size)
If iResult<>#ZIP_OK
iResult=#ZIP_ERR_DELETE_DATA
Break
EndIf
iResult=_ZIP_DropDataFromFileHdl(iFileHdl, DstFileEntry\LFH_Offset, DstFileEntry\LFH_Size)
If iResult<>#ZIP_OK
iResult=#ZIP_ERR_DELETE_DATA
Break
EndIf
ZIP_EOCDR\NumberOfEntries-1 ; reduce entry count by one
ZIP_EOCDR\NumberOfTotalEntries-1 ; reduce total entry count by one
ZIP_EOCDR\CentralDirStartOffset-DstFileEntry\LFH_Size ; reduce offset of beginning of central dir by deleted data len
ZIP_EOCDR\CentralDirSize-DstFileEntry\CDH_Size ; reduce central dir size by size of deleted directory entry
EndOfCentralDirectory_Offset-DstFileEntry\LFH_Size-DstFileEntry\CDH_Size ; reduce the 'EndOfCentralDirectory_Offset' by the complete len of removed bytes
If _ZIP_VerifySignature(iFileHdl, EndOfCentralDirectory_Offset, #ZIP_Signature_EOCDR)<#ZIP_OK ; verify EndOfCentralDirectory-Signature
iResult=#ZIP_ERR_SIGNATURE
Break
EndIf
FileSeek(iFileHdl, EndOfCentralDirectory_Offset)
If Not WriteData(iFileHdl, @ZIP_EOCDR, SizeOf(ZIP_EndOfCentralDirectoryRecordV2))
iResult=#ZIP_ERR_WRITE_FILE
Break
EndIf
qCurrentOffset=ZIP_EOCDR\CentralDirStartOffset
iFileCnt=0
Repeat
FileSeek(iFileHdl, qCurrentOffset)
If Not ReadData(iFileHdl, @ZIP_CDH, SizeOf(ZIP_CentralDirectoryHeaderV2))
iResult=#ZIP_ERR_READ_FILE
Break
EndIf
If ZIP_CDH\OffsetOfLocalHeader>DstFileEntry\LFH_Offset
ZIP_CDH\OffsetOfLocalHeader-DstFileEntry\LFH_Size
FileSeek(iFileHdl, qCurrentOffset)
If Not WriteData(iFileHdl, @ZIP_CDH, SizeOf(ZIP_CentralDirectoryHeaderV2))
iResult=#ZIP_ERR_WRITE_FILE
Break
EndIf
EndIf
qCurrentOffset+SizeOf(ZIP_CentralDirectoryHeaderV2)+ZIP_CDH\FilenameLength+ZIP_CDH\ExtraFieldLength+ZIP_CDH\FileCommentLength
iFileCnt+1
Until qCurrentOffset>=ZIP_EOCDR\CentralDirStartOffset+ZIP_EOCDR\CentralDirSize Or iFileCnt>=ZIP_EOCDR\NumberOfEntries
Until #True
EndIf
If iPreFileHdl=-1 ; only in case we opened the file ourselves
If IsFile(iFileHdl)
CloseFile(iFileHdl)
EndIf
EndIf
ProcedureReturn iResult
EndProcedure
Procedure RemoveFile(sZipfile.s, sFileToRemove.s, iCaseSensitiv.i=#False, iFileHdl.i=-1)
ProcedureReturn _ZIP_Common(sZipfile, #ZIP_Mode_RemoveFile, sFileToRemove, iCaseSensitiv, 0, 0, iFileHdl)
EndProcedure
Procedure ChangeFileDateTime(sZipfile.s, sFileToModify.s, lUnixTimeStamp.l, iCaseSensitiv.i=#False)
ProcedureReturn _ZIP_Common(sZipfile, #ZIP_Mode_ModifyHeader, sFileToModify, iCaseSensitiv, lUnixTimeStamp, -1)
EndProcedure
Procedure ZIP_ModifyHeader(sZipfile.s, sFileToModify.s, lUnixTimeStamp.l=-1, iUtf8Filename.i=-1, iCaseSensitiv.i=#False)
ProcedureReturn _ZIP_Common(sZipfile, #ZIP_Mode_ModifyHeader, sFileToModify, iCaseSensitiv, lUnixTimeStamp, iUtf8Filename)
EndProcedure
Procedure AddFile(sZipfile.s, sFileToAdd.s, sZippedFileName.s)
Protected iZipHdl.i
Protected sZipTempFile.s
Protected iResult.i
Protected SrcItemData.ZIP_MoveEntryData
iResult=#ZIP_OK
Repeat ; single repeat
If FileSize(sZipfile)<0 ; destination file does not exist - so we don't need a temp file
sZipTempFile=sZipfile
Else
sZipTempFile=GetTemporaryDirectory()+GetFilePart(sZipfile)+"_AddFileTemp"
EndIf
iZipHdl=CreatePack(#PB_Any, sZipTempFile, #PB_PackerPlugin_Zip)
If iZipHdl
If Not AddPackFile(iZipHdl, sFileToAdd, sZippedFileName)
iResult=#ZIP_ERR_ADDELEMENT
ClosePack(iZipHdl)
Break
EndIf
ClosePack(iZipHdl)
Else
iResult=#ZIP_ERR_WRITE_FILE
Break
EndIf
If StringUtf8Required(sZippedFileName)
If Not ZIP_ModifyHeader(sZipTempFile, sZippedFileName, -1, #True)
iResult=#ZIP_ERR_WRITE_FILE
Break
EndIf
EndIf
If sZipTempFile<>sZipfile ; in case the temp file is different from the final file we need to copy the added-entry from the temp file to the destination file
iResult=_ZIP_Common(sZipTempFile, #ZIP_Mode_GetEntryData, sZippedFileName, 0, 0, @SrcItemData)
If iResult<0 : Break : EndIf
iResult=_ZIP_Common(sZipfile, #ZIP_Mode_CopyEntry_File2File, sZipTempFile, 0, 0, @SrcItemData)
If iResult<0 : Break : EndIf
DeleteFile(sZipTempFile)
EndIf
Until #True
ProcedureReturn iResult
EndProcedure
Procedure AddFileFromMem(sZipfile.s, *SrcMem, iSrcSize.i, sZippedFileName.s, iPreFileHdl.i=-1, lDateTime.l=-1)
Protected iZipHdl.i
Protected sZipTempFile.s
Protected iResult.i
Protected SrcItemData.ZIP_MoveEntryData
iResult=#ZIP_OK
If lDateTime=-1 : lDateTime=Date() : EndIf
Repeat ; single repeat
If FileSize(sZipfile)<0 ; destination file does not exist - so we don't need a temp file
sZipTempFile=sZipfile
Else
sZipTempFile=GetTemporaryDirectory()+GetFilePart(sZipfile)+"_AddFileTemp"
EndIf
iZipHdl=CreatePack(#PB_Any, sZipTempFile, #PB_PackerPlugin_Zip)
If iZipHdl
If Not AddPackMemory(iZipHdl, *SrcMem, iSrcSize, sZippedFileName)
iResult=#ZIP_ERR_ADDELEMENT
ClosePack(iZipHdl)
Break
EndIf
ClosePack(iZipHdl)
Else
iResult=#ZIP_ERR_WRITE_FILE
Break
EndIf
If Not ZIP_ModifyHeader(sZipTempFile, sZippedFileName, lDateTime, StringUtf8Required(sZippedFileName) )
iResult=#ZIP_ERR_WRITE_FILE
Break
EndIf
If sZipTempFile<>sZipfile ; in case the temp file is different from the final file we need to copy the added-entry from the temp file to the destination file
iResult=_ZIP_Common(sZipTempFile, #ZIP_Mode_GetEntryData, sZippedFileName, 0, 0, @SrcItemData)
If iResult<0
Break
EndIf
iResult=_ZIP_Common(sZipfile, #ZIP_Mode_CopyEntry_File2File, sZipTempFile, 0, 0, @SrcItemData, iPreFileHdl)
If iResult<0 : Break : EndIf
DeleteFile(sZipTempFile)
EndIf
Until #True
ProcedureReturn iResult
EndProcedure
Procedure.i Examine(sZipfile.s, List EntryList.ZIP_EntryData(), iPreFileHdl.i=-1)
Protected ListHlp.ZipEntryData
Protected iResult.i
iResult=_ZIP_Common(sZipfile, #ZIP_Mode_ExamineFile, "", 0, @ListHlp, 0, iPreFileHdl)
If Not CopyList(ListHlp\EntryList(), EntryList())
iResult=#ZIP_ERR_UNSPEC_ERROR
EndIf
ProcedureReturn iResult
EndProcedure
EndModule
CompilerIf #PB_Compiler_IsMainFile
Procedure.i Mem_Dump2File(*StartAddr, sFilename.s)
Protected iResult.i
iResult=CreateFile(#PB_Any, sFilename)
If (iResult<>0)
WriteData(iResult, *StartAddr, MemorySize(*StartAddr))
CloseFile(iResult)
Else
Debug "Mem_Dump2File - CreateFile Error >"+Str(iResult)+"<"
EndIf
EndProcedure
Procedure.s FormatTime(iMilliseconds.i)
Protected iSeconds.i
Protected iMinutes.i
Protected iHours.i
iSeconds=iMilliseconds/1000
iMilliseconds-(iSeconds*1000)
iMinutes=iSeconds/60
iSeconds-(iMinutes*60)
iHours=iMinutes/60
iMinutes-(iHours*60)
ProcedureReturn StrU(iHours)+":"+Right("00"+StrU(iMinutes),2)+":"+Right("00"+StrU(iSeconds),2)+","+Right("000"+StrU(iMilliseconds),3)
EndProcedure
Define.i iResult, iCnt, iFileHdl, iStartime, iEndtime
Define.s sZipfile, sTmpFile, sRandom
Define *SrcMem
Define NewList ZipList.ZIP::ZIP_EntryData()
sZipfile=GetTemporaryDirectory()+"ZipFile.zip"
sTmpFile=GetTemporaryDirectory()+"FileFromFilesystem.dat"
*SrcMem=AllocateMemory(100000, #PB_Memory_NoClear)
Mem_Dump2File(*SrcMem, sTmpFile)
Debug "Example 1 - add files from mem"
iResult=ZIP::AddFileFromMem(sZipfile, *SrcMem, MemorySize(*SrcMem), "SubDirMem\FileFromMemWithSubdir.dat")
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
iResult=ZIP::AddFileFromMem(sZipfile, *SrcMem, MemorySize(*SrcMem), "FileFromMemWithoutSubdir.dat", -1, Date(2010,01,02,03,04,05)) ; date is stored in DOS format in ZIP file with a resolution of 2 seconds - so second '05' is stored as 04
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
iResult=ZIP::AddFileFromMem(sZipfile, *SrcMem, MemorySize(*SrcMem), "FileFromMemWithUTF8CharŪƝƗƇƠƉĖ.dat", -1, Date(2010,01,02,03,04,05)) ; date is stored in DOS format in ZIP file with a resolution of 2 seconds - so second '05' is stored as 04
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
Debug "Example 2 - add files from filesystem"
iResult=ZIP::AddFile(sZipfile, sTmpFile, "SubDirFS\"+GetFilePart(sTmpFile))
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
iResult=ZIP::AddFile(sZipfile, sTmpFile, "FileFromFilesystemWithoutSubdir.dat")
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
iResult=ZIP::AddFile(sZipfile, sTmpFile, "FileFromFilesystemWithUTF8CharŪƝƗƇƠƉĖ.dat")
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
Debug "Example 3 - show details of files in ZIP file"
iResult=ZIP::Examine(sZipfile, ZipList())
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
ForEach ZipList()
Debug "Filename >"+ZipList()\EntryName+"< Size Comp >"+ZipList()\EntrySizeCompressed+"< Size Uncomp >"+ZipList()\EntrySizeUncompressed+"< Type >"+ZipList()\EntryType+"< Date >"+FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss", ZipList()\EntryLastModFileDateTime)+"<"
Next
Debug "Example 4 - Delete files from ZIP"
iResult=ZIP::RemoveFile(sZipfile, "SubDirMem\FileFromMemWithSubdir.dat")
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
iResult=ZIP::RemoveFile(sZipfile, "FileFromMemWithoutSubdir.dat")
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
iResult=ZIP::RemoveFile(sZipfile, "FileFromMemWithUTF8CharŪƝƗƇƠƉĖ.dat")
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
Debug "Example 5 - Modify date of existing entry"
iResult=ZIP::ChangeFileDateTime(sZipfile, "SubDirFS\"+GetFilePart(sTmpFile), Date(2020,03,05,08,22,33))
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
iResult=ZIP::ChangeFileDateTime(sZipfile, "FileFromFilesystemWithoutSubdir.dat", Date(2021,03,05,08,22,33))
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
iResult=ZIP::ChangeFileDateTime(sZipfile, "FileFromFilesystemWithUTF8CharŪƝƗƇƠƉĖ.dat", Date(2022,03,05,08,22,33))
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
Debug "show details of files in ZIP file again"
iResult=ZIP::Examine(sZipfile, ZipList())
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
ForEach ZipList()
Debug "Filename >"+ZipList()\EntryName+"< Size Comp >"+ZipList()\EntrySizeCompressed+"< Size Uncomp >"+ZipList()\EntrySizeUncompressed+"< Type >"+ZipList()\EntryType+"< Date >"+FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss", ZipList()\EntryLastModFileDateTime)+"<"
Next
Debug "Example 6a - add a lot of files with PreFileHdl and remove it again" ; with PreFileHdl the purebasic file caching is used and it's a lot faster than opening/closing the zipfile for each add-operation (at least on slow devices like usb sticks or network drives)
DisableDebugger
iStartime=ElapsedMilliseconds()
iFileHdl=OpenFile(#PB_Any, sZipfile)
If iFileHdl
For iCnt=100 To 200 Step 1
sRandom=RSet(Str(iCnt),3,"0")+"-mem.txt"
iResult=ZIP::AddFileFromMem(sZipfile, *SrcMem, 1000, sRandom, iFileHdl)
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
Next
For iCnt=100 To 200 Step 1
sRandom=RSet(Str(iCnt),3,"0")+"-mem.txt"
iResult=ZIP::RemoveFile(sZipfile, sRandom, #False, iFileHdl)
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
Next
CloseFile(iFileHdl)
EndIf
iEndtime=ElapsedMilliseconds()
EnableDebugger
Debug "Duration >"+FormatTime(iEndtime-iStartime)+"<"
Debug "Example 6b - add a lot of files without PreFileHdl (and remove it again)"
DisableDebugger
iStartime=ElapsedMilliseconds()
For iCnt=300 To 400 Step 1
sRandom=RSet(Str(iCnt),3,"0")+"-mem.txt"
iResult=ZIP::AddFileFromMem(sZipfile, *SrcMem, 1000, sRandom)
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
Next
For iCnt=300 To 400 Step 1
sRandom=RSet(Str(iCnt),3,"0")+"-mem.txt"
iResult=ZIP::RemoveFile(sZipfile, sRandom)
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
Next
iEndtime=ElapsedMilliseconds()
EnableDebugger
Debug "Duration >"+FormatTime(iEndtime-iStartime)+"<"
Debug "show details of files in ZIP file again"
iResult=ZIP::Examine(sZipfile, ZipList())
If iResult<>ZIP::#ZIP_OK : Debug ZIP::GetErrorText(iResult) : EndIf
ForEach ZipList()
Debug "Filename >"+ZipList()\EntryName+"< Size Comp >"+ZipList()\EntrySizeCompressed+"< Size Uncomp >"+ZipList()\EntrySizeUncompressed+"< Type >"+ZipList()\EntryType+"< Date >"+FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss", ZipList()\EntryLastModFileDateTime)+"<"
Next
DeleteFile(sZipfile)
DeleteFile(sTmpFile)
CompilerEndIf