Aktuelle Zeit: 21.05.2018 22:35

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]




Ein neues Thema erstellen Auf das Thema antworten  [ 1 Beitrag ] 
Autor Nachricht
 Betreff des Beitrags: [Module] ZIP - z.B. ZIP::RemoveFile()
BeitragVerfasst: 24.09.2017 16:25 
Offline
Benutzeravatar

Registriert: 04.02.2005 15:40
Wohnort: Kaufbeuren
Habe im englischen Forum ein interessantes Modul gefunden und etwas optimiert.
(http://www.purebasic.fr/english/viewtop ... 12&t=66053)

Code:
;/ === 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

_________________
Meine PureBasic-Projekte:
EasySetup - Setups für eigene Programme erstellen
___________________________________________
Windows 10 - 64Bit | PureBasic V5.61 & V5.3
Bild


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 1 Beitrag ] 

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 4 Gäste


Sie dürfen keine neuen Themen in diesem Forum erstellen.
Sie dürfen keine Antworten zu Themen in diesem Forum erstellen.
Sie dürfen Ihre Beiträge in diesem Forum nicht ändern.
Sie dürfen Ihre Beiträge in diesem Forum nicht löschen.

Suche nach:
Gehe zu:  

 


Powered by phpBB © 2008 phpBB Group | Deutsche Übersetzung durch phpBB.de
subSilver+ theme by Canver Software, sponsor Sanal Modifiye