Seite 1 von 1

zipFC - Dateiformat (ZIP Datei-Container) UPDATE

Verfasst: 05.06.2013 21:25
von Thorsten1867
Nachdem mir das OpenDocument-Format für meine Projekte etwas zu komplex ist, habe ich mit dem zipFC-Dateiformat eine einfachere Variante eines Datei-Containers (ZIP) programmiert.
Zusätzlich ist es möglich die Dateien in einem AES-verschlüsselten Format (Header+verschlüsselte Datei) zu speichern.
  • - Die De- und Encodierung verschlüsselter Dateien erfolgt nach dem Entpacken direkt im Speicher (ohne den Umweg über die Festplatte).
    - Es können sowohl Infos zum Datei-Container als auch zu den einzelnen Dateien gespeichert und abgerufen werden. (-> content.xml)
    - Nach dem Öffnen des Datei-Containers stehen diese Infos direkt in einer Struktur zur Verfügung
    - In einer Map("Dateiname.ext") werden die Zeiger auf die einzelnen Nodes zu den Datei-Einträgen in der XML-Datei (content.xml) gespeichert
UPDATE:
  • - In der Map("Dateiname.ext") werden nun neben den XML-Nodes auch noch gespeichert, ob die Datei geöffnet und/oder verschlüsselt (AES) ist
    - Es können nun auch alle noch offenen Dateien geschlossen werden
    - Neben einzelnen Dateien können nun auch alle Dateien im Datei-Container geöffnet werden
    - Beim Schließen des Datei-Containers werden nun automatisch auch alle noch offenen Dateien geschlossen
    - Passwort und Ziel-Pfadkönnen (falls nötig) nun bei Öffnen des Datei-Containers angegeben werden, so dass man sich bei den einzelnen enthaltenen Dateien nicht mehr darum kümmern muss
    - Proceduren für normale und verschlüsselte Dateien wurden jeweils zu einer zusammerngefasst

Code: Alles auswählen

;/ *** zFC - Dateiformat ***
;/ (ZIP - FileContainer)
;/ PureBasic V5.1x
;/ Juni 2013 Thorsten Hoeppner (Thorsten1867)

UseZipPacker()

#AES = 1
#REMOVE = 1

#XmlZipFC = 0      ; #XML Nummer (ggf. anpassen)
#PackZipFC = 0     ; #Pack Nummer (ggf. anpassen)

#AESKey = "myProg1234AES5678Schluessel" ; eigenen Schlüssel festlegen !!!
#DESKey = "D12E34S"                     ; eigenen Schlüssel festlegen !!!

#AESFileExt = "acf"

Structure zipFCMapStructure
  *Node
  Open.b
  AES.b
EndStructure

Structure zipFCStructure
  ; --- Container-Infos ggf. anpassen ---
  Type.s
  Info.s
  Author.s
  ; -------------------------------------
  Path.s
  DESPassword.s
  Map File.zipFCMapStructure()
EndStructure
Global zipFC.zipFCStructure


;- Tools

Procedure.s FileNoExtension(File.s)               ; Pfad und Dateiname ohne Extension
  ProcedureReturn Left(File, Len(File)-Len(GetExtensionPart(File))-1)
EndProcedure

Procedure.s ValidPath(Path.s)                    ; gültige Pfad mit abschließendem "\"
  If Path
    If Right(Path,1) <> "\"
      ProcedureReturn Path+"\"
    Else
      ProcedureReturn Path
    EndIf
  EndIf
EndProcedure

Procedure.s zipFC_File(File.s)                    ; Datei ggf. mit Default-Pfad
  If GetPathPart(File) = ""
    ProcedureReturn zipFC\Path + File
  Else
    ProcedureReturn File
  EndIf
EndProcedure

Procedure zipFC_GetPackFileSize(FileName.s)      ; unkomprimierte Größe der Datei in der ZIP
  If ExaminePack(#PackZipFC)
    While NextPackEntry(#PackZipFC)
      If PackEntryName(#PackZipFC) = FileName
        ProcedureReturn PackEntrySize(#PackZipFC)
      EndIf
    Wend
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure zipFC_ClearFileInfo()                  ; Struktur & Map zurücksetzen
  ; --- Container-Infos ggf. anpassen ---
  zipFC\Type = ""
  zipFC\Info = ""
  zipFC\Author = ""
  ; -------------------------------------
  ClearMap(zipFC\File())
EndProcedure


;- Map / XML abfragen

Procedure.s zipFC_GetFileInfo(File.s, Attribut.s="")  ; Infos über Datei auslesen
  *File = zipFC\File(GetFilePart(File))\node
  If *File
    If Attribut
      ProcedureReturn GetXMLAttribute(*File, Attribut)
    Else
      ProcedureReturn GetXMLNodeText(*File)
    EndIf
  EndIf
EndProcedure


Procedure zipFC_AES(File.s)                           ; Datei verschlüsselt?
  ProcedureReturn zipFC\File(GetFilePart(File))\AES
EndProcedure

Procedure zipFC_Open(File.s)                          ; Datei geöffnet?
  ProcedureReturn zipFC\File(GetFilePart(File))\Open
EndProcedure


;- AES - Verschlüsselung

; Mittels FileID kann überprüft werden, ob es sich um eine gültige Datei für das Programm bzw. die Dateiaktion handelt. 

Procedure.b zipFC_WriteAESPack(File.s, DESPassword.s, FileID.s="myProg", quiet.b=#False)
  Protected FileLength.l, SFId.l, key$= #AESKey, result.b = #False
  Protected *FileMemory, *Memory, *MemoryID
  Protected FileName.s = GetFilePart(File)
  Protected AesFile.s = FileNoExtension(FileName)+"."+#AESFileExt
  ;Protected DESPassword.s = DESFingerprint(Password, #DESKey)
  
  If FileSize(File) <= 0 : ProcedureReturn #False : EndIf
  
  SFId = ReadFile(#PB_Any, File)
  If SFId
    FileLength = Lof(SFId)                                     ; Dateilänge
    *FileMemory = AllocateMemory(FileLength)
    If *FileMemory
      If ReadData(SFId, *FileMemory, FileLength)
        ; Headergröße berechnen
        HeaderLen = StringByteLength(FileID) + StringByteLength(DESPassword) + StringByteLength(FileName) + 11
        ; Speicher für Header & Datei reservieren
        MemSize = HeaderLen + MemorySize(*FileMemory)
        *Memory = AllocateMemory(MemSize)
        If *Memory
          *MemoryID = *Memory
          ;{ Header schreiben
          PokeS(*MemoryID, FileID)                       ; FileID (z.B. "myProg")
          *MemoryID + StringByteLength(FileID) + 1
          PokeL(*MemoryID, FileLength)                   ; Länge der eigentlichen Datei
          *MemoryID + 4
          PokeS(*MemoryID, DESPassword)                  ; DES-Passwort
          *MemoryID + StringByteLength(DESPassword) + 1
          PokeS(*MemoryID, FileName)                     ; Dateiname
          *MemoryID + StringByteLength(FileName) + 1
          ;}
          If AESEncoder(*FileMemory, *MemoryID, MemorySize(*FileMemory), @key$, 128, 0, #PB_Cipher_ECB)
            If AddPackMemory(#PackZipFC, *Memory, MemSize, AesFile)
              result = #True
            EndIf
          EndIf
          FreeMemory(*Memory)
        EndIf
      ElseIf quiet = #False
        MessageRequester(" zipFC - Datei öffnen", "Datei konnte nicht gelesen werden!", #MB_OK|#MB_ICONERROR)
      EndIf
      FreeMemory(*FileMemory)
    EndIf
    CloseFile(SFId)
  EndIf
  ProcedureReturn result
EndProcedure

Procedure.s zipFC_ReadAESPack(AesFile.s, DESPassword.s, TargetPath.s="", FileID.s="myProg", quiet.b=#False)
  Protected FileSize.l, MemFileID.s, MemDESPassword.s, FileName.s, FileLength.l, TFId.l, key$ = #AESKey
  Protected *FileMemory, *Memory, *MemoryID
  ; Protected DESPassword.s = DESFingerprint(Password, #DESKey)
  
  If TargetPath = "" : TargetPath = GetPathPart(AesFile) : EndIf
  
  FileSize = zipFC_GetPackFileSize(GetFilePart(AesFile))
  If FileSize
    *Memory = AllocateMemory(FileSize)
    If *Memory
      *MemoryID = *Memory
      If UncompressPackMemory(#PackZipFC, *Memory, FileSize, GetFilePart(AesFile))
        ; FileID auslesen
        MemFileID = PeekS(*MemoryID)
        *MemoryID + StringByteLength(MemFileID) + 1
        If FileID = MemFileID                         ; FileID (z.B. myProg) überprüfen
          FileLength = PeekL(*MemoryID)               ; Größe der eigentlichen Datei
          *MemoryID + 4
          ; Passwort auslesen
          MemDESPassword = PeekS(*MemoryID)
          *MemoryID + StringByteLength(MemDESPassword) + 1
          If DESPassword = MemDESPassword             ; Passwort überprüfen
            FileName = PeekS(*MemoryID)
            *MemoryID + StringByteLength(FileName) + 1
            ; --- Datei entschlüsseln ---
            *FileMemory = AllocateMemory(FileLength)
            If *FileMemory
              If AESDecoder(*MemoryID, *FileMemory, FileLength, @key$, 128, 0, #PB_Cipher_ECB)
                ; Datei auf HD schreiben
                TFId = CreateFile(#PB_Any, TargetPath+FileName)
                If TFId
                  WriteData(TFId, *FileMemory, FileLength)
                  CloseFile(TFId)
                Else
                  FileName = ""
                  If quiet = #False ;{ Datei konnte nicht erstellt werden
                    MessageRequester(" zipFC - Datei öffnen", "Fehler beim Zugriff auf die zipFC-Datei!"+#LF$+"Überprüfen Sie, ob Sie die nötigen Zugriffsrechte (RW) für folgendes Verzeichnis haben:"+#LF$+GetPathPart(AesFile), #MB_OK|#MB_ICONERROR)
                  EndIf ;}
                EndIf
              Else
                FileName = ""
                If quiet = #False ;{ Entschlüsseln/Entpacken fehlgeschlagen
                  MessageRequester(" zipFC - Datei öffnen", "Datei konnte nicht entschlüsselt werden!", #MB_OK|#MB_ICONERROR)
                EndIf ;}
              EndIf
              FreeMemory(*FileMemory)
            EndIf
          Else  
            FileName = ""
            If quiet = #False ;{ falsches Passwort
              MessageRequester(" zipFC - Datei öffnen", "Das Passwort der Datei ist ungültig! Zugriff auf die Datei wurde verweigert!", #MB_OK|#MB_ICONWARNING)
            EndIf ;}  
          EndIf
        Else ;
          FileName = ""
          If quiet = #False ;{ falsche FileID
            MessageRequester(" zipFC - Datei öffnen", "Ungültige Datei ("+FileID+")!", #MB_OK|#MB_ICONERROR)
          EndIf ;} 
        EndIf
      EndIf
      FreeMemory(*Memory)
    EndIf
  Else
    FileName = ""
    If quiet = #False ;{ Datei nicht im FileContainer
      MessageRequester(" zipFC - Datei öffnen", "Die Datei wurde nicht in der Packetdatei gefunden!", #MB_OK|#MB_ICONERROR)
    EndIf ;}
  EndIf
  ProcedureReturn FileName
EndProcedure


; Eine verschlüsselte Datei dekodieren (außerhalb des FileContainers)
Procedure.s zipFC_DecodeAESFile(AesFile.s, DESPassword.s, TargetPath.s="", FileID.s="myProg", quiet.b=#False)
  Protected AesFileSize.l, MemFileID.s, MemDESPassword.s, FileName.s, FileLength.l, SFId.l, TFId.l, key$ = #AESKey
  Protected *FileMemory, *Memory, *MemoryID
  
  If FileSize(AesFile) <= 0 : ProcedureReturn "" : EndIf
  If TargetPath = "" : TargetPath = GetPathPart(AesFile) : EndIf
  
  SFId = ReadFile(#PB_Any, AesFile)
  If SFId
    AesFileSize = Lof(SFId)
    *Memory = AllocateMemory(AesFileSize)
    If *Memory
      *MemoryID = *Memory
      If ReadData(SFId, *Memory, AesFileSize)
        MemFileID = PeekS(*MemoryID)                  ; FileID auslesen
        *MemoryID + StringByteLength(MemFileID) + 1
        If FileID = MemFileID
          FileLength = PeekL(*MemoryID)               ; Größe der eigentlichen Datei
          *MemoryID + 4
          MemDESPassword = PeekS(*MemoryID)           ; Passwort auslesen
          *MemoryID + StringByteLength(MemDESPassword) + 1
          If DESPassword = MemDESPassword             ; Passwort überprüfen
            FileName = PeekS(*MemoryID)
            *MemoryID + StringByteLength(FileName) + 1
            *FileMemory = AllocateMemory(FileLength)  ; --- Datei entschlüsseln ---
            If *FileMemory
              If AESDecoder(*MemoryID, *FileMemory, FileLength, @key$, 128, 0, #PB_Cipher_ECB)
                TFId = CreateFile(#PB_Any, TargetPath+FileName)
                If TFId
                  WriteData(TFId, *FileMemory, FileLength)
                  CloseFile(TFId)
                Else
                  FileName = "ERROR:CreateFile"
                EndIf
              Else
                FileName = "ERROR:AES"
              EndIf
              FreeMemory(*FileMemory)
            EndIf
          Else  
            FileName = "ERROR:Password" 
          EndIf
        Else ;
          FileName = "ERROR:FileID"
        EndIf
      Else
        FileName = "ERROR"
      EndIf
      FreeMemory(*Memory)
    EndIf
    CloseFile(SFId)
  Else
    FileName = "ERROR:AESFile" 
  EndIf
  
  If quiet = #False
    If Left(FileName, 5) = "ERROR" ;{ Fehlermeldung
      Select Mid(FileName, 7)
        Case "AESFile"
          FileName = ""
          MessageRequester(" zipFC - Datei öffnen", "Datei konnte nicht gelesen werden!", #MB_OK|#MB_ICONERROR)
        Case "FileID"
          FileName = ""
          MessageRequester(" zipFC - Datei öffnen", "Ungültige Datei ("+FileID+")!", #MB_OK|#MB_ICONERROR)
        Case "Password"
          FileName = ""
          MessageRequester(" zipFC - Datei öffnen", "Das Passwort der Datei ist ungültig! Zugriff auf die Datei wurde verweigert!", #MB_OK|#MB_ICONWARNING)
        Case "AES"
          FileName = ""
          MessageRequester(" zipFC - Datei öffnen", "Datei konnte nicht entschlüsselt werden!", #MB_OK|#MB_ICONERROR)
        Case "CreateFile"
          FileName = ""
          MessageRequester(" zipFC - Datei öffnen", "Fehler beim Zugriff auf die zipFC-Datei!"+#LF$+"Überprüfen Sie, ob Sie die nötigen Zugriffsrechte (RW) für folgendes Verzeichnis haben:"+#LF$+GetPathPart(AesFile), #MB_OK|#MB_ICONERROR)
        Default
          FileName = ""
          MessageRequester(" KvGS - Datei öffnen", "Beim Öffnen der Datei ist ein Fehler aufgetreten!", #MB_OK|#MB_ICONERROR)
      EndSelect
    EndIf ;}
  EndIf
  
  ProcedureReturn FileName
EndProcedure
;- "content.xml" verwalten (Infos über FileContainer-Inhalt)

Procedure zipFC_CreateContentXML(Type.s, Info.s, Author.s)             ; Neue XML-Datei erzeugnen
  ; Parameter ggf. an zipFCStructure anpassen (siehe unten)
  If CreateXML(#XmlZipFC)
    *MainNode = CreateXMLNode(RootXMLNode(#XmlZipFC))
    If *MainNode
      SetXMLNodeName(*MainNode, "Content")
      ; --- Container-Infos ggf. anpassen ---
      SetXMLAttribute(*MainNode, "type", Type)
      SetXMLAttribute(*MainNode, "info", Info)
      SetXMLAttribute(*MainNode, "author", Author)
      ; -------------------------------------
      zipFC\Type = Type
      zipFC\Info = Info
      zipFC\Author = Author
      ; -------------------------------------
      ProcedureReturn #True
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure


Procedure zipFC_OpenContentXML()                                       ; XML-Datei auslesen und in Struktur speichern
  ; Parameter nur nötig, falls 
  Protected *XmlMem, XmlSize.l, FileName.s
  
  zipFC_ClearFileInfo() ; Strukur & Map zurücksetzen/leeren
  
  ;{ Entpacken und XML laden
  XmlSize = zipFC_GetPackFileSize("content.xml")
  If XmlSize
    *XmlMem = AllocateMemory(XmlSize)
    If *XmlMem
      If UncompressPackMemory(#PackZipFC, *XmlMem, XmlSize, "content.xml")
        CatchXML(#XmlZipFC, *XmlMem, XmlSize)
      EndIf
      FreeMemory(*XmlMem)
    EndIf
  Else
    ProcedureReturn #False
  EndIf ;}
  
  ;{ "content.xml" auslesen
  If IsXML(#XmlZipFC)
    If XMLStatus(#XmlZipFC) = #PB_XML_Success
      *MainNode = MainXMLNode(#XmlZipFC)
      If *MainNode
        ; --- Container-Infos ggf. anpassen ---
        zipFC\Type = GetXMLAttribute(*MainNode, "type")
        zipFC\Info = GetXMLAttribute(*MainNode, "info")
        zipFC\Author = GetXMLAttribute(*MainNode, "author")
        ; -------------------------------------
        *Content = ChildXMLNode(*MainNode) ; Dateien im FileContainer
        While *Content
          AddMapElement(zipFC\File(), GetXMLNodeText(*Content))
          zipFC\File()\node = *Content
          zipFC\File()\AES = Val(GetXMLAttribute(*Content, "aes"))
          zipFC\File()\Open = #False
          *Content = NextXMLNode(*Content)
        Wend
        ProcedureReturn #True
      EndIf
    Else
      Debug "XML: "+XMLError(#XmlZipFC)
    EndIf
  Else
    Debug "XML: Keine gültige XML-Datei"
  EndIf ;} 
  
  ProcedureReturn #False
EndProcedure

Procedure zipFC_CloseContentXML()                                      ; XML-Datei (mit ggf. aktualiserten Daten) im FileContainer zurückschreiben 
  Protected *XmlMem, XmlSize.l, result.l = #False
  If IsXML(#XmlZipFC)
    XmlSize = ExportXMLSize(#XmlZipFC)
    *XmlMem = AllocateMemory(XmlSize)
    If *XmlMem
      If ExportXML(#XmlZipFC, *XmlMem, XmlSize)
        RemovePackFile(#PackZipFC, "content.xml")
        result = AddPackMemory(#PackZipFC, *XmlMem, XmlSize, "content.xml")
      EndIf  
      FreeMemory(*XmlMem)
    EndIf
    FreeXML(#XmlZipFC)
  EndIf
EndProcedure


Procedure zipFC_UpdateContentXML(File.s, remove.b=#False, AES.b=#False)              ; XML-Datei & Struktur aktualisieren
  If IsXML(#XmlZipFC)
    If remove ;{ Eintrag zur Datei entfernen
      *File = zipFC\File(GetFilePart(File))\node
      If *File
        If DeleteXMLNode(*File)
          DeleteMapElement(zipFC\File(), GetFilePart(File))
          ProcedureReturn #True
        EndIf 
      EndIf
      ;}
    Else      ;{ Eintrag aktualisieren oder hinzufügen
      *File = zipFC\File(GetFilePart(File))\node
      If *File
        SetXMLAttribute(*File, "modified", Str(GetFileDate(File, #PB_Date_Modified)))
        SetXMLAttribute(*File, "aes", Str(AES))
        zipFC\File(GetFilePart(File))\AES = AES
        ProcedureReturn #True
      Else
        *MainNode = MainXMLNode(#XmlZipFC)
        If *MainNode
          *Node = CreateXMLNode(*MainNode, -1)
          If *Node
            SetXMLNodeName(*Node, "File")
            SetXMLAttribute(*Node, "type", GetExtensionPart(File))
            SetXMLAttribute(*Node, "aes", Str(AES))
            SetXMLAttribute(*Node, "modified", Str(GetFileDate(File, #PB_Date_Modified)))
            SetXMLNodeText(*Node, GetFilePart(File))
            AddMapElement(zipFC\File(), GetFilePart(File))
            zipFC\File()\node = *Node
            zipFC\File()\AES = AES
            zipFC\File()\Open = #False
            ProcedureReturn #True
          EndIf  
        EndIf
      EndIf ;}
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure


;- ===== Dateien im FileContainer =====

Procedure zipFC_AddFile(File.s, AES.b=#False)    ; Neue Datei zum Container hinzufügen
  File = zipFC_File(File)
  If AES ; Datei verschlüsseln
    If zipFC_WriteAESPack(File, zipFC\DESPassword)
      zipFC_UpdateContentXML(File, #False, #AES)
      zipFC\File(GetFilePart(File))\Open = #True
      ProcedureReturn #True
    EndIf
  Else
    If AddPackFile(#PackZipFC, File, GetFilePart(File))
      zipFC_UpdateContentXML(File)
      zipFC\File(GetFilePart(File))\Open = #True
      ProcedureReturn #True
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure zipFC_RemoveFile(File.s)               ; Datei aus dem Container entfernen
  File = zipFC_File(File)
  If zipFC\File(GetFilePart(File))\AES
    If RemovePackFile(#PackZipFC, GetFilePart(FileNoExtension(File)+"."+#AESFileExt)) 
      zipFC_UpdateContentXML(File, #REMOVE)
      ProcedureReturn #True
    EndIf
  Else
    If RemovePackFile(#PackZipFC, GetFilePart(File)) 
      zipFC_UpdateContentXML(File, #REMOVE)
      ProcedureReturn #True
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure


Procedure zipFC_CloseFile(File.s)                ; Datei in Container verschieben
  File = zipFC_File(File) ; ggf. Default-Pfad benutzen
  If FileSize(File) > 0   ; nur existierende Dateien schließen
    If zipFC\File(GetFilePart(File))\AES
      RemovePackFile(#PackZipFC, GetFilePart(FileNoExtension(File)+"."+#AESFileExt))
      If zipFC_WriteAESPack(File, zipFC\DESPassword)
        zipFC_UpdateContentXML(File, #False, #AES)
        zipFC\File(GetFilePart(File))\Open = #False
        DeleteFile(File)
        ProcedureReturn #True
      EndIf
    Else  
      RemovePackFile(#PackZipFC, GetFilePart(File))
      If AddPackFile(#PackZipFC, File, GetFilePart(File))
        zipFC_UpdateContentXML(File)
        zipFC\File(GetFilePart(File))\Open = #False
        DeleteFile(File)
        ProcedureReturn #True
      EndIf
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure.s zipFC_OpenFile(File.s)               ; Datei aus Container laden
  Protected FileName.s = ""
  File = zipFC_File(File) ; ggf. Default-Pfad benutzen
  If zipFC\File(GetFilePart(File))\AES
    FileName = zipFC_ReadAESPack(FileNoExtension(File)+"."+#AESFileExt, zipFC\DESPassword, GetPathPart(File))
    If FileName
      zipFC\File(GetFilePart(File))\Open = #True
    EndIf
  Else
    If UncompressPackFile(#PackZipFC, File, GetFilePart(File))
      zipFC\File(GetFilePart(File))\Open = #True
      FileName = GetFilePart(File)
    EndIf
  EndIf
  ProcedureReturn FileName
EndProcedure


Procedure zipFC_CloseAllFiles()                  ; alle geöffneten Dateien schließen
  ForEach zipFC\File()
    If zipFC\File()\Open = #True
      zipFC_CloseFile(zipFC\Path+MapKey(zipFC\File()))
    EndIf
  Next
EndProcedure

Procedure zipFC_OpenAllFiles()                   ; alle (nicht geöffneten) Dateien öffnen
  ForEach zipFC\File()
    If zipFC\File()\Open = #False
      zipFC_OpenFile(zipFC\Path+MapKey(zipFC\File()))
    EndIf  
  Next
EndProcedure


;- ===== FileContainer =====

Procedure zipFC_CloseContainer()                                     ; FileContainer schließen
  zipFC_CloseAllFiles()
  zipFC_CloseContentXML() ; content.xml speichern
  zipFC_ClearFileInfo()   ; Struktur & Map zurücksetzen
  zipFC\Path = ""
  zipFC\DESPassword = ""
  ProcedureReturn ClosePack(#PackZipFC)
EndProcedure

Procedure zipFC_OpenContainer(Container.s, Password.s="", Path.s="") ; Datei-Container öffnen
  zipFC_CloseContainer() ; ggf. offenen Container schließen
  If FileSize(Container) > 0
    If OpenPack(#PackZipFC, Container, #PB_Packer_Zip)
      If Path
        zipFC\Path = ValidPath(Path)
      Else
        zipFC\Path = GetPathPart(Container)
      EndIf
      zipFC\Path = GetPathPart(Container)
      zipFC\DESPassword = DESFingerprint(Password, #DESKey)
      ProcedureReturn zipFC_OpenContentXML()
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure


Procedure zipFC_CreateContainer(Container.s, Type.s, Info.s, Author.s, Password.s="", Path.s="") ; Neuen Datei-Container erstellen
  zipFC_CloseContainer() ; ggf. offenen Container schließen
  If FileSize(Container) < 0
    If CreatePack(#PackZipFC, Container, #PB_Packer_Zip)
      If Path
        zipFC\Path = ValidPath(Path)
      Else
        zipFC\Path = GetPathPart(Container)
      EndIf
      zipFC\DESPassword = DESFingerprint(Password, #DESKey)
      zipFC_CreateContentXML(Type, Info, Author)
      ProcedureReturn #True
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure


;- ***** Beispiele *****

; Neue zipFC-Datei (FileContainer) erstellen
If zipFC_CreateContainer("E:\Temp\TestContainerDatei.zfc", "myProg", "Dateien für myProg", "Thorsten Hoeppner", "passwort")
  
  ; Datei hinzufügen
  zipFC_AddFile("E:\Temp\Test.txt")
  ; verschlüsselte Datei hinzufügen
  zipFC_AddFile("E:\Temp\Test2.txt", #AES)
  
  ; Infos für alle Dateien mittels Map anzeigen
  ForEach zipFC\File()
    ; nur Dateiname (=MapKey)
    Debug "Datei (Map): " + MapKey(zipFC\File())
    ; weitere Infos aus content.xml auslesen
    *Node = zipFC\File()\node
    If *Node
      Debug "Datei (XML): " + GetXMLNodeText(*Node) + " (Type: " + GetXMLAttribute(*Node, "type") + ")" 
    EndIf
  Next
  
  ; Info für Datei mittels Map direkt anzeigen
  *Node = zipFC\File("Test2.txt")\node
  If *Node
    Debug "Datei (XML): " + GetXMLNodeText(*Node) + " (Type: " + GetXMLAttribute(*Node, "type") + ")" 
  EndIf
  ; --- ODER ---
  Debug "Datei-Typ: "+zipFC_GetFileInfo("Test2.txt", "type") ; einfacher Weg ;-)
  
  zipFC_CloseContainer()
Else
  Debug "zipFC-Datei existiert bereits!"
EndIf

; Vorhandenen zipFC-Datei (FileContainer) öffnen
If zipFC_OpenContainer("E:\Temp\TestContainerDatei.zfc", "passwort")
  ; direkter Zugriff auf Infos über zipFC-Datei
  Debug "----- zipFC-Datei -----"
  Debug "Typ: " + zipFC\Type
  Debug "Info: " + zipFC\Info
  Debug "Autor: " + zipFC\Author
  
  ; Dateien öffnen
  If zipFC_OpenFile("Test.txt")
    Debug "Datei entpackt: "+Str(FileSize("E:\Temp\Test.txt"))
    ;zipFC_CloseFile("Test.txt") ; geänderte Datei zurück in zipFC-Datei verschieben
  EndIf
  
  If zipFC_OpenFile("Test2.txt") ; verschlüssselte Datei (Test2.acf)
    Debug "Datei entpackt & enschlüsselt: "+Str(FileSize("E:\Temp\Test2.txt"))
    ;zipFC_CloseAESFile("Test2.txt") ; geänderte Datei verschlüsseln & zurück in zipFC-Datei verschieben
  EndIf
  
  ; verschlüsselte Datei nur entpacken
  zipFC_OpenFile("Test2.acf") ; verschlüsselte Datei auf (Un)Lesbarkeit prüfen
  
  ; Warten bevor alle Dateien mit dem Container geschlossen werden
  MessageRequester("zipFC-Datei - Test", "zipFC-Container wird geschlossen (incl. aller noch offenen Dateien)", #MB_OK|#MB_ICONINFORMATION)
  
  zipFC_CloseContainer() ; alle noch offenen Dateien werden zurück in zipFC-Datei verschoben & ggf. verschlüsselt
EndIf