#include <Array.au3>
#include <GuiListView.au3>
#include <WindowsConstants.au3>
#include <ListViewConstants.au3>
#include <GUIConstantsEx.au3>

$sTitle = 'ZAP UnPacker v1.0    ;-)'
$hGui = GUICreate($sTitle, 740, 600, -1, -1, Default, $WS_EX_ACCEPTFILES)

$hPack = GUICtrlCreateButton('Packen', 10, 10, 80, 25)
$hPackPath = GUICtrlCreateInput(@ScriptDir & '\archiv.zap', 100, 11, 600, 23)
$hPackSelect = GUICtrlCreateButton('...', 700, 10, 30, 25)

$hPackProgress = GUICtrlCreateProgress(10, 40, 720, 10)

$hPackListview = GUICtrlCreateListView('Dateiname|Größe|Pfad', 10, 60, 720, 220, BitOR($LVS_SHOWSELALWAYS, $LVS_NOSORTHEADER))
$hPackContext = GUICtrlCreateContextMenu($hPackListview)
$hPackClear = GUICtrlCreateMenuItem('Packliste leeren', $hPackContext)
$hPackAll = GUICtrlCreateMenuItem('Alle Dateien auswählen', $hPackContext)
GUICtrlSetState($hPackListview, $GUI_DROPACCEPTED)
GUICtrlSendMsg($hPackListview, $LVM_SETCOLUMNWIDTH, 0, 200)
GUICtrlSendMsg($hPackListview, $LVM_SETCOLUMNWIDTH, 1, 80)
GUICtrlSendMsg($hPackListview, $LVM_SETCOLUMNWIDTH, 2, 430)

$hUnPack = GUICtrlCreateButton('Entpacken', 10, 310, 80, 25)
$hUnPackPath = GUICtrlCreateInput(@ScriptDir & '\entpackt\', 100, 311, 600, 23)
$hUnpackSelect = GUICtrlCreateButton('...', 700, 310, 30, 25)

$hUnPackProgress = GUICtrlCreateProgress(10, 340, 720, 10)

$hUnPackListview = GUICtrlCreateListView('Dateiname|Größe|Archiv|Offset', 10, 360, 720, 220, BitOR($LVS_SHOWSELALWAYS, $LVS_NOSORTHEADER))
$hUnPackContext = GUICtrlCreateContextMenu($hUnPackListview)
$hUnPackClear = GUICtrlCreateMenuItem('Entpackliste leeren', $hUnPackContext)
$hUnPackAll = GUICtrlCreateMenuItem('Alle Dateien auswählen', $hUnPackContext)
GUICtrlSetState($hUnPackListview, $GUI_DROPACCEPTED)
GUICtrlSendMsg($hUnPackListview, $LVM_SETCOLUMNWIDTH, 0, 200)
GUICtrlSendMsg($hUnPackListview, $LVM_SETCOLUMNWIDTH, 1, 80)
GUICtrlSendMsg($hUnPackListview, $LVM_SETCOLUMNWIDTH, 2, 350)
GUICtrlSendMsg($hUnPackListview, $LVM_SETCOLUMNWIDTH, 3, 80)

GUISetState()
WinSetOnTop($hGui, '', 1)

Global $gaDropFiles[1]
Global Const $WM_DROPFILES = 0x233
GUIRegisterMsg($WM_DROPFILES, '_WM_DROPFILES')

While True
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Exit
		Case $GUI_EVENT_DROPPED
			_DropFiles()
		Case $hPackSelect
			_ChooseDestFile()
		Case $hUnpackSelect
			_ChooseDestFolder()
		Case $hPack
			_PackFiles()
		Case $hUnPack
			_UnpackFiles()
		Case $hPackClear
			_GUICtrlListView_DeleteAllItems($hPackListview)
			GUICtrlSetData($hPackProgress, 0)
		Case $hUnPackClear
			_GUICtrlListView_DeleteAllItems($hUnPackListview)
			GUICtrlSetData($hUnPackProgress, 0)
		Case $hPackAll
			_GUICtrlListView_SetItemSelected($hPackListview, -1, True)
		Case $hUnPackAll
			_GUICtrlListView_SetItemSelected($hUnPackListview, -1, True)
	EndSwitch
WEnd

Func _ChooseDestFile()
	Local $sPath
	$sPath = FileSaveDialog('Datei zum packen auswählen', '', 'Archiv (*.zap)', 16, '', $hGui)
	If @error Then Return
	$sPath = StringRegExpReplace($sPath & '.zap', '(.+\\.+?)\..+', '$1' & '.zap')
	GUICtrlSetData($hPackPath, $sPath)
EndFunc   ;==>_ChooseDestFile

Func _ChooseDestFolder()
	Local $sPath
	$sPath = FileSelectFolder('Verzeichnis zum entpacken wählen', '', 3, '', $hGui)
	If @error Then Return
	GUICtrlSetData($hUnPackPath, $sPath)
EndFunc   ;==>_ChooseDestFolder

Func _PackFiles()
	Local $sWritePath, $hWriteFile, $aSelected, $sReadPath, $hReadFile, $sBuffer
	$sWritePath = GUICtrlRead($hPackPath) ; Speicherpfad holen
	If $sWritePath = '' Then Return MsgBox(48 + 262144, 'Fehler', 'keine Archivdatei ausgewählt') ; wenn nicht ausgewählt, dann Funktion verlassen
	$aSelected = _GUICtrlListView_GetSelectedIndices($hPackListview, True) ; Markierte Listview-Einträge holen
	If $aSelected[0] = 0 Then Return MsgBox(48 + 262144, 'Fehler', 'keine Dateien zum packen ausgewählt') ; wenn nicht ausgewählt, dann Funktion verlassen
	$hWriteFile = FileOpen($sWritePath, 2 + 8 + 16) ; Archivdatei im Binärmodus zum speichern öffnen
	If $hWriteFile <> -1 Then ; wenn erfolgreich, dann...
		GUICtrlSetData($hPackProgress, 1) ; Progressbar aktualisieren
		FileWrite($hWriteFile, 'zap|') ; Dateikennung schreiben
		For $i = 1 To $aSelected[0] ; Header erstellen (alle Listview-Einträge durchgehen)
			FileWrite($hWriteFile, _GUICtrlListView_GetItemText($hPackListview, $aSelected[$i], 1) & '|') ; Dateigröße
			FileWrite($hWriteFile, _GUICtrlListView_GetItemText($hPackListview, $aSelected[$i], 0) & '|') ; Dateiname
		Next
		GUICtrlSetData($hPackProgress, 3) ; Progressbar aktualisieren
		FileWrite($hWriteFile, 'end') ; Header-Ende-kennung schreiben
		For $i = 1 To $aSelected[0] ; die eigentlichen Dateien schreiben (alle Listview-Einträge durchgehen)
			$sReadPath = _GUICtrlListView_GetItemText($hPackListview, $aSelected[$i], 2) ; den Pfad holen
			$hReadFile = FileOpen($sReadPath, 16) ; Datei im Binärmodus zum lesen öffnen
			$sBuffer = FileRead($hReadFile) ; Datei lesen
			FileClose($hReadFile) ; Datei schliessen
			FileWrite($hWriteFile, $sBuffer) ; Datei ins Archiv schreiben
			GUICtrlSetData($hPackProgress, 3 + (97 / $aSelected[0] * $i)) ; Progressbar aktualisieren
		Next
		FileClose($hWriteFile) ; Archivdatei schliessen
		Beep(800, 100)
		Sleep(500)
		GUICtrlSetData($hPackProgress, 0)
	EndIf
EndFunc   ;==>_PackFiles

Func _UnpackFiles()
	Local $sOutFolder, $aSelected, $hFile, $sReadPath, $iOffset, $sFileName, $iFileSize, $sFile
	$sOutFolder = GUICtrlRead($hUnPackPath) ; Entpackverzeichnis holen
	If $sOutFolder = '' Then Return MsgBox(48 + 262144, 'Fehler', 'kein Entpackverzeichnis ausgewählt') ; wenn nicht ausgewählt, dann Funktion verlassen
	$aSelected = _GUICtrlListView_GetSelectedIndices($hUnPackListview, True) ; Markierte Listview-Einträge holen
	If $aSelected[0] = 0 Then Return MsgBox(48 + 262144, 'Fehler', 'keine Dateien zum entpacken ausgewählt') ; wenn nicht ausgewählt, dann Funktion verlassen
	If $sOutFolder = '' Or $aSelected[0] = 0 Then Return ; wenn nichts ausgewählt, dann Funktion verlassen
	If StringRight($sOutFolder, 1) <> '\' Then $sOutFolder &= '\' ; Backslash anfügen, wenn nicht vorhanden
	For $i = 1 To $aSelected[0] ; alle markierten Einträge durchgehen
		$sFileName = _GUICtrlListView_GetItemText($hUnPackListview, $aSelected[$i], 0) ; Dateinamen holen
		$iFileSize = _GUICtrlListView_GetItemText($hUnPackListview, $aSelected[$i], 1) ; Dateigröße
		$sReadPath = _GUICtrlListView_GetItemText($hUnPackListview, $aSelected[$i], 2) ; Archivdatei
		$iOffset = _GUICtrlListView_GetItemText($hUnPackListview, $aSelected[$i], 3) ; Offset zur Datei
		ConsoleWrite($i & ' = >' & $sFileName & '<' & @CR)
		$hFile = FileOpen($sReadPath, 16) ; Archivdatei im Binärmodus zu lesen öffnen
		If $hFile <> -1 Then ; wenn erfolgreich, dann...
			FileRead($hFile, $iOffset) ; Dateizeiger auf den Offset setzen
			$sFile = FileRead($hFile, $iFileSize) ; Datei aus dem Archiv auslesen
			FileClose($hFile) ; Archivdatei schliessen
			$hFile = FileOpen($sOutFolder & $sFileName, 2 + 8 + 16) ; Datei im Entpackverzeichnis erstellen und zum schreiben öffnen
			If $hFile <> -1 Then ; wenn erfolgreich, dann...
				FileWrite($hFile, $sFile) ; Dateiinhalt schreiben
				FileClose($hFile) ; Datei schliessen
			EndIf
		EndIf
		GUICtrlSetData($hUnPackProgress, 100 / $aSelected[0] * $i) ; Progressbar aktualisieren
	Next
	Beep(800, 100)
	Sleep(500)
	GUICtrlSetData($hUnPackProgress, 0)
EndFunc   ;==>_UnpackFiles

Func _WM_DROPFILES($hWnd, $msgID, $wParam, $lParam)
	Local $nSize, $pFileName
	Local $nAmt = DllCall('shell32.dll', 'int', 'DragQueryFileW', 'hwnd', $wParam, 'int', 0xFFFFFFFF, 'ptr', 0, 'int', 255)
	For $i = 0 To $nAmt[0] - 1
		$nSize = DllCall('shell32.dll', 'int', 'DragQueryFileW', 'hwnd', $wParam, 'int', $i, 'ptr', 0, 'int', 0)
		$nSize = $nSize[0] + 1
		$pFileName = DllStructCreate('wchar[' & $nSize & ']')
		DllCall('shell32.dll', 'int', 'DragQueryFileW', 'hwnd', $wParam, 'int', $i, 'ptr', DllStructGetPtr($pFileName), 'int', $nSize)
		ReDim $gaDropFiles[$i + 1]
		$gaDropFiles[$i] = DllStructGetData($pFileName, 1)
		$pFileName = 0
	Next
	Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_DROPFILES

Func _DropFiles()
	For $i = 0 To UBound($gaDropFiles) - 1
		If FileRead($gaDropFiles[$i], 4) = 'zap|' Then
			_DropUnPack($gaDropFiles[$i])
		Else
			_DropPack($gaDropFiles[$i])
		EndIf
	Next
EndFunc   ;==>_DropFiles

Func _DropPack($sDropFile)
	Local $sFilePath, $sFileName, $sFileSize, $aFiles
	If StringInStr(FileGetAttrib($sDropFile), 'D') Then
		$aFiles = _RecursiveFileListToArray($sDropFile, '.+', 1)
		For $i = 1 To $aFiles[0]
			$sFilePath = StringLeft($sDropFile, StringInStr($sDropFile, '\', 0, -1))
			$sFileName = StringTrimLeft($aFiles[$i], StringLen($sFilePath))
			$sFileSize = FileGetSize($aFiles[$i])
			GUICtrlCreateListViewItem($sFileName & '|' & $sFileSize & '|' & $aFiles[$i], $hPackListview)
		Next
	Else
		$sFileName = StringTrimLeft($sDropFile, StringInStr($sDropFile, "\", 0, -1))
		$sFileSize = FileGetSize($sDropFile)
		GUICtrlCreateListViewItem($sFileName & '|' & $sFileSize & '|' & $sDropFile, $hPackListview)
	EndIf
EndFunc   ;==>_DropPack

Func _DropUnPack($sDropFile)
	Local $hFile, $sHeader, $aHeader, $iOffset
	$hFile = FileOpen($sDropFile, 16)
	If $hFile <> -1 Then
		FileRead($hFile, 4)
		$sHeader = ''
		$iOffset = 4
		Do
			$iOffset += 1
			$sHeader &= BinaryToString(FileRead($hFile, 1))
		Until StringRight($sHeader, 4) = '|end'
		FileClose($hFile)
		$aHeader = StringSplit(StringTrimRight($sHeader, 4), '|', 2)
		For $j = 0 To UBound($aHeader) - 1 Step 2
			GUICtrlCreateListViewItem($aHeader[$j + 1] & '|' & $aHeader[$j] & '|' & $sDropFile & '|' & $iOffset, $hUnPackListview)
			$iOffset += $aHeader[$j]
		Next
	EndIf
EndFunc   ;==>_DropUnPack

;===============================================================================
; Function Name:   _RecursiveFileListToArray($sPath[, $sPattern][, $iFlag][, $iFormat][, $iRecursion][, $sDelim])
; Description::    gibt Verzeichnisse und/oder Dateien (rekursiv) zurück, die einem RegExp-Pattern entsprechen
; Parameter(s):    $sPath = Startverzeichnis
;                  $sPattern = ein beliebiges RexExp-Pattern für die Auswahl
;                  $iFlag = Auswahl
;                           0 = Dateien & Verzeichnisse
;                           1 = nur Dateien
;                           2 = nur Verzeichnisse
;                  $iFormat = Rückgabeformat
;                             0 = String
;                             1 = Array mit [0] = Anzahl
;                             2 = Nullbasiertes Array
;                  $iRecursion = Verzeichnisse rekursiv durchsuchen
;                                0 = Nein
;                                1 = Ja
;                  $sDelim = Trennzeichen für die String-Rückgabe
; Requirement(s):  AutoIt 3.3.0.0
; Return Value(s): Array/String mit den gefundenen Dateien/Verzeichnissen
; Author(s):       Oscar (www.autoit.de)
;                  Anregungen von: bernd670 (www.autoit.de)
;===============================================================================
Func _RecursiveFileListToArray($sPath, $sPattern = '', $iFlag = 0, $iFormat = 1, $iRecursion = 1, $sDelim = @CRLF)
	Local $hSearch, $sFile, $sReturn = ''
	If StringRight($sPath, 1) <> '\' Then $sPath &= '\'
	$hSearch = FileFindFirstFile($sPath & '*.*')
	If @error Or $hSearch = -1 Then Return SetError(0, 0, $sReturn)
	While True
		$sFile = FileFindNextFile($hSearch)
		If @error Then ExitLoop
		If StringInStr(FileGetAttrib($sPath & $sFile), 'D') Then
			If StringRegExp($sPath & $sFile, $sPattern) And ($iFlag = 0 Or $iFlag = 2) Then $sReturn &= $sPath & $sFile & '\' & $sDelim
			If $iRecursion Then $sReturn &= _RecursiveFileListToArray($sPath & $sFile & '\', $sPattern, $iFlag, 0)
			ContinueLoop
		EndIf
		If StringRegExp($sFile, $sPattern) And ($iFlag = 0 Or $iFlag = 1) Then $sReturn &= $sPath & $sFile & $sDelim
	WEnd
	FileClose($hSearch)
	If $iFormat And $sReturn = '' Then Return StringSplit($sReturn, '', $iFormat)
	If $iFormat Then Return StringSplit(StringTrimRight($sReturn, StringLen($sDelim)), $sDelim, $iFormat)
	Return $sReturn
EndFunc   ;==>_RecursiveFileListToArray
