Что нового

Package UDF - Создание собственных PKR архивов

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
AutoIt: 3.3.6.1+
Версия: 1.0

Категория: Файловая система, Процессы

Описание: Я думаю, что многие из вас хотели бы объединить некоторые данные или файлы ваших проектов, например изображения скина, в один файл (архив), и при необходимости извлекать их оттуда. Кроме того, было бы лучше избежать создания временных файлов на диске. Да, конечно, вы можете использовать ресурсы исполняемого файла или функцию FileInstall(), но в первом случае, вы не сможете добавлять данные после компиляции скрипта, а второй неизбежно приводит к записи данных на диск, что не есть хорошо . Да, еще вы можете применить, например, ZIP архивы, но и здесь вы будите ограничены использованием только файлов. По этой причине я решил придумать свой собственный архив (PKR) для хранения любых данных (это могут быть как файлы, так и непосредственно данные из памяти) и лишенный всех вышеперечисленных недостатков.

Как вы можете увидеть ниже, прочитав спецификацию файлов, PKR архивы имеют простую структуру, состоящую из последовательных блоков данных. А специально для удобства использования таких архивов, я написал UDF библиотеку, которую вы можете скачать ниже. Подробное описание каждой функции (на английском) можно найти внутри библиотеки. Кроме того, ZIP архив включает в себя все примеры и вспомогательные файлы. В качестве дополнительного примера, вы можете скачать Package.pkr файл, который содержит те же самые файлы, что и ZIP архив, но только созданный с помощью этой библиотеки.

Спецификация:

Как видно на скриншоте, PKR архив состоит из заголовка и одного или нескольких пакетов данных, следующих друг за другом. Заголовок архива имеет длину 256 байт и представляет PKHEADER структуру, которая содержит основные сведения о самом архиве, в том числе и краткий текстовый комментарий.


--------------------------------------------------------------------------------------------------
| PKHEADER |
|--------------------------------------------------------------------------------------------------|
| Offset | Length | Purpose |
|--------|--------|--------------------------------------------------------------------------------|
| 0 | 4 | The file signature (0x504B5221) |
|--------|--------|--------------------------------------------------------------------------------|
| 4 | 4 | The package version, 1.0 |
|--------|--------|--------------------------------------------------------------------------------|
| 8 | 8 | The file size, in bytes |
|--------|--------|--------------------------------------------------------------------------------|
| 16 | 4 | The number of packets in the package |
|--------|--------|--------------------------------------------------------------------------------|
| 20 | 4 | Reserved |
|--------|--------|--------------------------------------------------------------------------------|
| 24 | 8 | The absolute offset, in bytes, of the first packet in package |
|--------|--------|--------------------------------------------------------------------------------|
| 32 | 224 | The package comment, max 224 bytes (112 characters) |
--------------------------------------------------------------------------------------------------

Первые четыре байта .pkr файла всегда содержат одну и ту же последовательность байт (сигнатуру) - 0x504B5221 ("PKR!" в ASCII символах). Это позволяет однозначно идентифицировать PKR архив. Затем следует DWORD значение, представляющее версию архива, в настоящее время 1.0 (0x00000100). Далее идет размер файла (INT64) в байтах. Несмотря на то, что размер архива не ограничивается, длина одного пакета в нем не может превышать немногим более 4 ГБ (см. ниже). Важно заметить, что значение этого параметра должно быть равно фактическому размеру файла, в противном случае считается, что архив поврежден. Следующий член структуры (DWORD) содержит количество пакетов в архиве. Оно не должно быть равно нулю, так как создание пустых архивов (не содержащих ни одного пакета) не допускается. Следующие четыре байта зарезервированы и должны быть равны нулю. Шестой член PKHEADER структуры (INT64) является наиболее важным и содержит смещение первого пакета в архиве от начала файла в байтах. Это означает, что первый пакет не обязательно следует сразу же после заголовка. Последним в заголовке архива идет текстовый комментарий. Длина комментария ограничена 224 байтами (112 Unicode символами, включая нулевой символ завершения).

После заголовка архива может располагаться Packet Relocation Table (PRT) произвольного размера, которая содержит информацию для быстрого поиска пакетов в архиве, но в настоящее время не используется и имеет нулевую длину.

Далее в .pkr файле, после PKHEADER и PRT, идут пакеты данных. Каждый пакет имеет свой собственный заголовок и три раздела с данными: Description, Info и Data. Почему три? Потому что так гораздо легче классифицировать данные в пакете. Вы поймете это, когда попробуете использовать библиотеку в своих скриптах. Подробное описание пакета (PKPACKET структура) представлено в следующей таблице.


--------------------------------------------------------------------------------------------------
| PKPACKET |
|--------------------------------------------------------------------------------------------------|
| Offset | Length | Purpose |
|--------|--------|--------------------------------------------------------------------------------|
| 0 | 4 | The size, in bytes, of the packet header structure (40 bytes) |
|--------|--------|--------------------------------------------------------------------------------|
| 4 | 4 | The size, in bytes, of the description block, max 8192 bytes (4096 characters) |
|--------|--------|--------------------------------------------------------------------------------|
| 8 | 4 | The size, in bytes, of the information block, max 64 KB |
|--------|--------|--------------------------------------------------------------------------------|
| 12 | 4 | The size, in bytes, of the data block, max 4 GB |
|--------|--------|--------------------------------------------------------------------------------|
| 16 | 8 | The 64-bit unique identifier of the packet |
|--------|--------|--------------------------------------------------------------------------------|
| 24 | 4 | The checksum (CRC32) of the compressed data, or zero if no compression |
|--------|--------|--------------------------------------------------------------------------------|
| 28 | 4 | The uncompressed data size, in bytes, or zero if no compression |
|--------|--------|--------------------------------------------------------------------------------|
| 32 | 8 | Reserved |
|--------------------------------------------------------------------------------------------------|
| Description |
|--------------------------------------------------------------------------------------------------|
| Information |
|--------------------------------------------------------------------------------------------------|
| Data |
--------------------------------------------------------------------------------------------------

Первый член PKPACKET структуры (DWORD) всегда содержит длину заголовка пакета в байтах и в настоящее время составляет 40 байт, но может быть изменен в будущем. Второй, третий и четвертый члены структуры (DWORD) содержат длины соответствующих разделов данных в байтах. Если какой-либо раздел в пакете отсутствует, то значение его длины равно нулю. Полная длина пакета в байтах вычисляется путем суммирования перечисленных выше четырех значений. Пятый член PKPACKET структуры (INT64), это уникальный идентификатор пакета (ID) и представляет собой 64-разрядное положительное число, которое однозначно идентифицирует пакет в архиве. Шестой и седьмой члены структуры (DWORD) используются только, если данные Data раздела сжаты, в противном случае содержат нулевые значения. В случае сжатия, шестой член структуры содержит точный размер Data раздела в байтах после распаковки. Последний параметр в заголовке пакета (INT64) зарезервирован и должен быть равен нулю. Сразу же после заголовка пакета следуют три раздела с данными, которые более подробно описаны ниже.

Раздел Description идет первым в пакет и предназначен для хранения любой текстовой информации. Это может быть, например, имя файла, в случае добавления файлов, или просто краткое описание данных, которые находятся в пакете. Максимальная длина этого раздела - 8 КБ (8192 байт) или 4096 Unicode символов (включая нулевой символ завершения).

Раздел Info следует сразу же за разделом Description. Здесь вы можете хранить любые вспомогательные двоичные данные, например атрибуты файла, даты создания, изменения и последнего доступа к файлу, или любую другую информацию. Кроме того, вы можете хранить в этом разделе, небольшие файлы, например файлы курсоров (.cur), иконок (.ico) и т.д. Максимальная длина Info раздела составляет 64 КБ (65,535 байт).

Раздел Data является последним (третьим) разделом в пакете и используется для хранения основных данных. Максимальная длина этого раздела может быть до 4 ГБ (4,294,967,295 байт). Кроме того, данные Data раздела могут быть сжаты с использованием LZ алгоритма (не самый оптимальный, но достаточно быстрый).

Эти три раздела данных представляет собой один пакет и должны следовать непрерывно друг за другом, как показано выше. Кроме того, любой (или все одновременно) из этих разделов могут отсутствовать в пакете.

Далее, после первого пакета, сразу же следует другой пакет, если таковой присутствует в архиве, и т.д.

Список функций:
_PK_AddPacket
_PK_AddPacketFromFile
_PK_Close
_PK_Create
_PK_DisplayPackage
_PK_EnumPackets
_PK_ExtractPacketData
_PK_ExtractPacketDataToFile
_PK_FindPacket
_PK_Flush
_PK_GetComment
_PK_GetCount
_PK_GetInfo
_PK_GetPacketCompression
_PK_GetPacketDataLength
_PK_GetPacketDescription
_PK_GetPacketDescriptionLength
_PK_GetPacketID
_PK_GetPacketIndex
_PK_GetPacketInfo
_PK_GetPacketInfoLength
_PK_GetPath
_PK_GetVersion
_PK_Open
_PK_Purge
_PK_QueryPacket
_PK_QueryPacketEx
_PK_ReadPacketData
_PK_RemovePacket
_PK_SetBuffer
_PK_SetComment
_PK_Test

Примеры:
Добавление файла (простой)
Код:
#Include "Package.au3"

Global Const $sPackage = @ScriptDir & '\MyFile.pkr'

$hPackage = _PK_Create($sPackage, 'Demonstration Package', 1)
_PK_AddPacketFromFile($hPackage, @ScriptFullPath, @ScriptName, 1)
_PK_DisplayPackage($hPackage)
_PK_Close($hPackage)

Извлечение файла (простой)
Код:
#Include "Package.au3"

Global Const $sPackage = @ScriptDir & '\MyFile.pkr'

$hPackage = _PK_Open($sPackage)
_PK_ExtractPacketDataToFile($hPackage, -1, StringRegExpReplace(_PK_GetPacketDescription($hPackage, -1), '\.[^.]*\Z', '.bak'), 1)
_PK_Close($hPackage)

Добавление двоичных данных (простой)
Код:
#Include "Package.au3"

Global Const $sPackage = @ScriptDir & '\MyData.pkr'

$hPackage = _PK_Create($sPackage, 'Demonstration Package', 1)
$tData = DllStructCreate('byte[16]')
DllStructSetData($tData, 1, '0x00112233445566778899AABBCCDDEEFF')
_PK_AddPacket($hPackage, DllStructGetPtr($tData), DllStructGetSize($tData), 'Binary data', 1)
_PK_DisplayPackage($hPackage)
_PK_Close($hPackage)

Извлечение двоичных данных (простой)
Код:
#Include "Package.au3"

Global Const $sPackage = @ScriptDir & '\MyData.pkr'

$hPackage = _PK_Open($sPackage)
$tData = DllStructCreate('byte[' & _PK_GetPacketDataLength($hPackage, -1) & ']')
_PK_ExtractPacketData($hPackage, -1, DllStructGetPtr($tData), DllStructGetSize($tData))
ConsoleWrite(DllStructGetData($tData, 1) & @CR)
_PK_Close($hPackage)

Добавление файлов
Код:
#Include <WinAPIEx.au3>

#Include "Package.au3"

Opt('MustDeclareVars', 1)

Global Const $sPackage = @ScriptDir & '\MyPackage.pkr'

Global $hPackage, $ID, $File

If Not FileExists($sPackage) Then
	$hPackage = _PK_Create($sPackage, 'Demonstration Package')
Else
	$hPackage = _PK_Open($sPackage)
EndIf

Do
	$File = FileOpenDialog('Add File', @WorkingDir, 'All Files (*.*)', 3)
	If @Error Then
		ExitLoop
	EndIf
	$ID = _PK_AddPacketFromFile($hPackage, $File, _WinAPI_PathStripPath($File))
	If @Error Then
		$ID = MsgBox(20, 'Package', 'An error occurred while adding file to the package.' & @CR & @CR & 'Error: ' & @Error & @CR & @CR & 'Do you want to add another file?')
	Else
		$ID = MsgBox(68, 'Package', 'File added successfully.' & @CR & @CR & 'ID: ' & $ID & @CR & @CR & 'Do you want to add another file?')
	EndIf
Until $ID <> 6

_PK_DisplayPackage($hPackage)

_PK_Close($hPackage)

Извлечение файлов
Код:
#Include <Array.au3>
#Include <WinAPIEx.au3>

#Include "Package.au3"

Opt('MustDeclareVars', 1)

Global Const $sPackage = @ScriptDir & '\MyPackage.pkr'

Global $hPackage, $ID, $Data, $File, $Path

$hPackage = _PK_Open($sPackage)
If @Error Then
	MsgBox(16, 'Package', _WinAPI_PathStripPath($sPackage) & ' not found.')
	Exit
EndIf

$Data = _PK_EnumPackets($hPackage)
If @Error Then
	MsgBox(16, 'Package', 'Unable to enumerate packets in the package.' & @CR & @CR & 'Error: ' & @Error)
Else
	For $i = 1 To $Data[0][0]
		ConsoleWrite($i & ' - ' & $Data[$i][1] & @CR)
	Next
	While 1
		$ID = InputBox('Package', 'Type a one based index of the packet to extract.', '1', '', 274, 142)
		If @Error Then
			ExitLoop
		EndIf
		$File = _PK_GetPacketDescription($hPackage, -$ID)
		If @Error Then
			MsgBox(16, 'Package', 'Packet not found.')
		Else
			$Path = _WinAPI_PathYetAnotherMakeUniqueName(@ScriptDir & '\' & $File)
			If Not _PK_ExtractPacketDataToFile($hPackage, -$ID, $Path) Then
				$ID = MsgBox(20, 'Package', 'An error occurred while extracting file from the package.' & @CR & @CR & 'Error: ' & @Error & @CR & @CR & 'Do you want to extract another file?')
			Else
				$ID = MsgBox(68, 'Package', 'File has been saved to ' & _WinAPI_PathStripPath($Path) & '.' & @CR & @CR & 'Do you want to extract another file?')
			EndIf
			If $ID <> 6 Then
				ExitLoop
			EndIf
		EndIf
	WEnd
EndIf

_PK_Close($hPackage)

GUI (расширенный)
Код:
#NoTrayIcon

#Include <APIConstants.au3>
#Include <GUIListView.au3>
#Include <GUIConstantsEx.au3>
#Include <ListViewConstants.au3>
#Include <StaticConstants.au3>
#Include <WinAPIEx.au3>

#Include "Package.au3"

Opt('MustDeclareVars', 1)
Opt('WinWaitDelay', 0)

#Region Initialization

Global $hForm, $hDrop, $hPackage, $hLV, $LV, $Button[3], $Dummy[2], $Label, $Area, $Item

#EndRegion Initialization

#Region Body

_CreateForm()

While 1
	Switch GUIGetMsg()
		Case 0
			ContinueLoop
		Case $GUI_EVENT_CLOSE
			ExitLoop
		Case $GUI_EVENT_DROPPED
			_AddPacket(@GUI_DRAGFILE)
		Case $Button[0]
			_AddPacket()
		Case $Button[1], $Dummy[1]
			_RemovePacket()
		Case $Button[2], $Dummy[0]
			_ExtractPacket()
		Case Else

	EndSwitch
WEnd

_DeleteForm()

#EndRegion Body

#Region Additional Functions

Func _AddPacket($sFile = '')

	Local $tInfo, $ID, $Data, $Flag, $Error

	If Not $sFile Then
		$sFile = FileOpenDialog('Add File', @WorkingDir, 'All Files (*.*)', 3, '', $hDrop)
		If @Error Then
			Return
		EndIf
	EndIf
	If FileGetSize($sFile) > 104857600 Then
		If MsgBox(52, 'Package', _WinAPI_PathStripPath($sFile) & ' is too large (over 100 MB) and can not be compressed on the fly.' & @CR & @CR & 'Do you want to add this file without compression?', 0, $hDrop) <> 6 Then
			Return
		EndIf
		$Flag = 0
	Else
		$Flag = 1
	EndIf
	Opt('GUIOnEventMode', 1)
	GUISetState(@SW_DISABLE, $hForm)
	GUISetCursor(15, 1, $hForm)
	$tInfo = _GetFileInfo($sFile)
	$ID = _PK_AddPacketFromFile($hPackage, $sFile, _WinAPI_PathStripPath($sFile), $Flag, DllStructGetPtr($tInfo), DllStructGetSize($tInfo), '_Progress')
	$Error = @Error
	GUISetCursor(2, 0, $hForm)
	GUISetState(@SW_ENABLE, $hForm)
	Opt('GUIOnEventMode', 0)
	If $Error Then
		MsgBox(16, 'Package', 'An error occurred while adding file to the package.' & @CR & @CR & 'Error: ' & $Error, 0, $hDrop)
		Return
	EndIf
	$Data = _PK_QueryPacket($hPackage, $ID)
	If @Error Then
		Return
	EndIf
	_GUICtrlListView_BeginUpdate($hLV)
	$ID = _GUICtrlListView_AddItem($hLV, $Data[0])
	For $i = 1 To 4
		_GUICtrlListView_AddSubItem($hLV, $ID, $Data[$i], $i)
	Next
	_GUICtrlListView_SetItemParam($hLV, $ID, $Data[5])
	_GUICtrlListView_SetItemSelected($hLV, $ID, 1, 1)
	_GUICtrlListView_EnsureVisible($hLV, $ID)
	_GUICtrlListView_EndUpdate($hLV)
	GUICtrlSetState($LV, $GUI_FOCUS)
	WinActivate($hDrop)
EndFunc   ;==>_AddPacket

Func _CreateForm()

	Local $ID, $Path, $File, $Data

	$Path = FileOpenDialog('Open Package', @ScriptDir, 'Package Files (*.pkr)|All Files (*.*)', 10, 'MyPackage.pkr')
	If @Error Then
		Exit
	EndIf
	$File = _WinAPI_PathStripPath($Path)
	If Not FileExists($Path) Then
		$hPackage = _PK_Create($Path, 'Demonstration Package')
	Else
		$hPackage = _PK_Open($Path)
	EndIf
	If @Error Then
		MsgBox(16, 'Package', 'Unable to open ' & $File & '.' & @CR & @CR & 'Error: ' & @Error)
		Exit
	EndIf
	OnAutoItExitRegister('_Quit')
	If _PK_GetCount($hPackage) Then
		$Data = _PK_EnumPackets($hPackage)
		If @Error Then
			MsgBox(16, 'Package', 'Unexpected error.' & @CR & @CR & 'Error: ' & @Error)
			Exit
		EndIf
	Else
		$Data = 0
	EndIf
	$hForm = GUICreate($File & ' - ' & _PK_GetComment($hPackage), 660, 440, -1, -1, BitOR($WS_CAPTION, $WS_MINIMIZEBOX, $WS_MAXIMIZEBOX, $WS_POPUP, $WS_SIZEBOX, $WS_SYSMENU))
	$hDrop = GUICreate('', 660, 398, 0, 0, BitOR($WS_CHILD, $WS_TABSTOP, $WS_VISIBLE), $WS_EX_ACCEPTFILES, $hForm)
	$LV = GUICtrlCreateListView('', 0, 0, 660, 398, BitOR($LVS_DEFAULT, $LVS_NOSORTHEADER), BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT, $LVS_EX_INFOTIP))
	GUICtrlSetFont(-1, 8.5, 400, 0, 'Tahoma')
	GUICtrlSetState(-1, $GUI_DROPACCEPTED)
	$hLV = GUICtrlGetHandle(-1)
	_GUICtrlListView_AddColumn($hLV, '#', 30)
	_GUICtrlListView_AddColumn($hLV, 'ID', 140)
	_GUICtrlListView_AddColumn($hLV, 'Description', 313)
	_GUICtrlListView_AddColumn($hLV, 'Info Length', 80, 1)
	_GUICtrlListView_AddColumn($hLV, 'Data Length', 80, 1)
	If _WinAPI_GetVersion() >= '6.0' Then
		_WinAPI_SetWindowTheme($hLV, 'Explorer')
	EndIf
	If IsArray($Data) Then
		For $i = 1 To $Data[0][0]
			$ID = _GUICtrlListView_AddItem($hLV, $i)
			For $j = 0 To 3
				_GUICtrlListView_AddSubItem($hLV, $ID, $Data[$i][$j], $j + 1)
			Next
			_GUICtrlListView_SetItemParam($hLV, $ID, $Data[$i][4])
		Next
		_GUICtrlListView_SetItemSelected($hLV, 0, 1, 1)
		$Item = 0
	Else
		$Item =-1
	EndIf
	GUISwitch($hForm)
	GUISetIcon(@ScriptDir & '\Package.ico')
	$Label = GUICtrlCreateLabel('', 0, 398, 663, 2, $SS_ETCHEDHORZ)
	$Button[0] = GUICtrlCreateButton('Add...', 5, 405, 214, 30)
	$Button[1] = GUICtrlCreateButton('Remove', 223, 405, 214, 30)
	$Button[2] = GUICtrlCreateButton('Extract...', 441, 405, 214, 30)
	For $i = 0 To 1
		$Dummy[$i] = GUICtrlCreateDummy()
	Next
	$Area = WinGetPos($hForm)
	GUIRegisterMsg($WM_GETMINMAXINFO, 'WM_GETMINMAXINFO')
	GUIRegisterMsg($WM_NOTIFY, 'WM_NOTIFY')
	GUIRegisterMsg($WM_SIZE, 'WM_SIZE')
	GUICtrlSetState($LV, $GUI_FOCUS)
	GUISetState()
EndFunc   ;==>_CreateForm

Func _DeleteForm()
	GUIDelete($hForm)
	GUIDelete($hDrop)
EndFunc   ;==>_DeleteForm

Func _ExtractPacket()

	Local $tInfo, $File, $Error

	$File = FileSaveDialog('Extract File', @WorkingDir, 'All Files (*.*)', 18, _PK_GetPacketDescription($hPackage, -($Item + 1)), $hDrop)
	If @Error Then
		Return
	EndIf
	Opt('GUIOnEventMode', 1)
	GUISetState(@SW_DISABLE, $hForm)
	GUISetCursor(15, 1, $hForm)
	_PK_ExtractPacketDataToFile($hPackage, -($Item + 1), $File, 1, '_Progress')
	$Error = @Error
	If $Error Then

	Else
		$tInfo = _PK_GetPacketInfo($hPackage, -($Item + 1))
		If Not _SetFileInfo($File, $tInfo) Then
			; Nothing
		EndIf
	EndIf
	GUISetCursor(2, 0, $hForm)
	GUISetState(@SW_ENABLE, $hForm)
	Opt('GUIOnEventMode', 0)
	If $Error Then
		MsgBox(16, 'Package', 'An error occurred while adding file to the package.' & @CR & @CR & 'Error: ' & $Error, 0, $hDrop)
	EndIf
EndFunc   ;==>_ExtractPacket

Func _GetFileInfo($sFile)

	Local $hFile, $tInfo

	$hFile = _WinAPI_CreateFileEx($sFile, $OPEN_EXISTING, $GENERIC_READ, BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE))
	If @Error Then
		Return 0
	EndIf
	$tInfo = _WinAPI_GetFileInformationByHandleEx($hFile)
	_WinAPI_CloseHandle($hFile)
	Return $tInfo
EndFunc   ;==>_GetFileInfo

Func _Quit()
	_PK_Close($hPackage)
EndFunc   ;==>_Quit

Func _RemovePacket()

	Local $Count, $Error, $Sel = $Item

	If MsgBox(36, 'Package', 'Are you sure you want to remove ' & _PK_GetPacketDescription($hPackage, -($Item + 1)) & ' from the package?', 0, $hDrop) <> 6 Then
		Return
	EndIf
	Opt('GUIOnEventMode', 1)
	GUISetState(@SW_DISABLE, $hForm)
	GUISetCursor(15, 1, $hForm)
	_PK_RemovePacket($hPackage, -($Item + 1))
	$Error = @Error
	GUISetCursor(2, 0, $hForm)
	GUISetState(@SW_ENABLE, $hForm)
	Opt('GUIOnEventMode', 0)
	If $Error Then
		MsgBox(16, 'Package', 'An error occurred while remove file from the package.' & @CR & @CR & 'Error: ' & $Error, 0, $hDrop)
		Return
	EndIf
	_GUICtrlListView_BeginUpdate($hLV)
	_GUICtrlListView_DeleteItem($hLV, $Item)
	$Count = _GUICtrlListView_GetItemCount($hLV)
	If $Count Then
		If $Sel > $Count - 1 Then
			$Sel -= 1
		Else
			For $i = $Sel To $Count - 1
				_GUICtrlListView_SetItemText($hLV, $i, $i + 1)
			Next
		EndIf
		_GUICtrlListView_SetItemSelected($hLV, $Sel, 1, 1)
	EndIf
	_GUICtrlListView_EndUpdate($hLV)
	GUICtrlSetState($LV, $GUI_FOCUS)
	WinActivate($hDrop)
EndFunc   ;==>_RemovePacket

Func _SetFileInfo($sFile, $tInfo)

	Local $hFile

	If DllStructGetSize($tInfo) < 40 Then
		Return 0
	EndIf
	$hFile = _WinAPI_CreateFileEx($sFile, $OPEN_EXISTING, $GENERIC_WRITE, BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE))
	If @Error Then
		Return 0
	EndIf
	_WinAPI_SetFileInformationByHandleEx($hFile, $tInfo)
	_WinAPI_CloseHandle($hFile)
	Return 1
EndFunc   ;==>_SetFileInfo

#EndRegion Additional Functions

#Region Callback Functions

Func _Progress($hPackage, $ID, $iTotalSize, $iTotalBytesTransferred, $vData)

	#forceref $hPackage, $ID, $vData

	Static $hDlg = Ptr(0), $Progress

	Local $Pos, $Percent

	If $iTotalSize <> $iTotalBytesTransferred Then
		$Percent = Round($iTotalBytesTransferred / $iTotalSize * 100)
	Else
		$Percent = 100
	EndIf
	If Not $hDlg Then
		If $Percent < 100 Then
			$Pos = WinGetPos($hDrop)
			$hDlg = GUICreate('', 300, 44, $Pos[0] + ($Pos[2] - 300) / 2, $Pos[1] + ($Pos[3] - 44) / 2, BitOR($WS_BORDER, $WS_DISABLED, $WS_POPUP), 0, $hForm)
			$Progress = GUICtrlCreateProgress(15, 15, 270, 14)
			GUICtrlSetData(-1, $Percent)
			GUISetCursor(15, 1)
			GUISetState()
		EndIf
	Else
		GUICtrlSetData($Progress, $Percent)
		If $Percent = 100 Then
			GUISetState(@SW_ENABLE, $hForm)
			GUIDelete($hDlg)
			$hDlg = 0
		EndIf
	EndIf
	Return $PK_PROGRESS_CONTINUE
EndFunc   ;==>_Progress

#EndRegion Callback Functions

#Region Windows Message Functions

Func WM_GETMINMAXINFO($hWnd, $iMsg, $wParam, $lParam)

	Local $tMMI = DllStructCreate('long Reserved[2];long MaxSize[2];long MaxPosition[2];long MinTrackSize[2];long MaxTrackSize[2]', $lParam)

	Switch $hWnd
		Case $hForm
			If IsArray($Area) Then
				DllStructSetData($tMMI, 'MinTrackSize', $Area[2], 1)
				DllStructSetData($tMMI, 'MinTrackSize', $Area[3], 2)
			EndIf
			Return 0
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_GETMINMAXINFO

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)

	Static $Flag = False

	If @AutoItX64 Then
		Local $tNMLV = DllStructCreate($tagNMHDR & ';uint Aligment;int Item;int SubItem;uint NewState;uint OldState;uint Changed;long ActionX;long ActionY;lparam Param', $lParam)
	Else
		Local $tNMLV = DllStructCreate($tagNMHDR & ';int Item;int SubItem;uint NewState;uint OldState;uint Changed;long ActionX;long ActionY;lparam Param', $lParam)
	EndIf

	Local $hTarget = DllStructGetData($tNMLV, 'hWndFrom')
	Local $ID = DllStructGetData($tNMLV, 'Code')

	Switch $hTarget
		Case $hLV
			Switch $ID
				Case $LVN_BEGINDRAG
					Return 0
				Case $LVN_ITEMACTIVATE
					If $Item <> -1 Then
						GUICtrlSendToDummy($Dummy[0])
					EndIf
				Case $LVN_ITEMCHANGED
					If (BitAND(DllStructGetData($tNMLV, 'Changed'), $LVIF_STATE)) And (BitXOR(DllStructGetData($tNMLV, 'NewState'), DllStructGetData($tNMLV, 'OldState'))) Then
						If BitAND(DllStructGetData($tNMLV, 'NewState'), $LVIS_SELECTED) Then
							$Item = DllStructGetData($tNMLV, 'Item')
							For $i = 1 To 2
								GUICtrlSetState($Button[$i], $GUI_ENABLE)
							Next
							$Flag = 0
						Else
							If BitAND(DllStructGetData($tNMLV, 'OldState'), $LVIS_FOCUSED) Then
								$Flag = 1
							Else
								If Not $Flag Then
									For $i = 1 To 2
										GUICtrlSetState($Button[$i], $GUI_DISABLE)
									Next
									$Item = -1
								EndIf
								$Flag = 0
							EndIf
						EndIf
					EndIf
				Case $NM_CUSTOMDRAW

					If @AutoItX64 Then
						Local $tNMLVCD = DllStructCreate($tagNMHDR & ';uint Aligment1;dword DrawStage;hwnd hDC;long Left;long Top;long Right;long Bottom;dword_ptr ItemSpec;uint ItemState;lparam ItemParam;uint Aligment2[5];dword clrText;dword clrTextBk;int SubItem;dword ItemType;dword clrFace;int IconEffect;int IconPhase;int PartId;int StateId;long TextLeft;long TextTop;long TextRight;long TextBottom;uint Align', $lParam)
					Else
						Local $tNMLVCD = DllStructCreate($tagNMHDR & ';dword DrawStage;hwnd hDC;long Left;long Top;long Right;long Bottom;dword_ptr ItemSpec;uint ItemState;lparam ItemParam;dword clrText;dword clrTextBk;int SubItem;dword ItemType;dword clrFace;int IconEffect;int IconPhase;int PartId;int StateId;long TextLeft;long TextTop;long TextRight;long TextBottom;uint Align', $lParam)
					EndIf

					Local $Stage = DllStructGetData($tNMLVCD, 'DrawStage')

					Switch $Stage
						Case $CDDS_ITEMPREPAINT
							Return $CDRF_NOTIFYSUBITEMDRAW
						Case BitOR($CDDS_ITEMPREPAINT, $CDDS_SUBITEM)
							If _GUICtrlListView_GetItemParam($hLV, DllStructGetData($tNMLVCD, 'ItemSpec')) Then
								DllStructSetData($tNMLVCD, 'clrText', 0xFF0000)
							EndIf
							Return $CDRF_DODEFAULT
						Case Else

					EndSwitch
				Case $LVN_KEYDOWN

					If @AutoItX64 Then
						Local $tNMLVKD = DllStructCreate($tagNMHDR & ';uint Aligment;ushort VKey;uint Flags', $lParam)
					Else
						Local $tNMLVKD = DllStructCreate($tagNMHDR & ';ushort VKey;uint Flags', $lParam)
					EndIf

					Local $Key = DllStructGetData($tNMLVKD, 'VKey')

					Switch BitAND($Key, 0xFF)
						Case 0x2E ; VK_DELETE
							If $Item <> -1 Then
								GUICtrlSendToDummy($Dummy[1])
							EndIf
						Case Else

					EndSwitch
				Case $NM_CLICK, $NM_DBLCLK, $NM_RCLICK, $NM_RDBLCLK
					Return 0
			EndSwitch
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam)

	Local $WC, $HC, $WT

	Switch $hWnd
		Case $hForm
			$WC = _WinAPI_LoWord($lParam)
			$HC = _WinAPI_HiWord($lParam)
			$WT = Floor(($WC - 18) / 3)
			WinMove($hDrop, '', 0, 0, $WC, $HC - 42)
			GUICtrlSetPos($LV, 0, 0, $WC, $HC - 42)
			GUICtrlSetPos($Label, 0, $HC - 42, $WC + 3)
			GUICtrlSetPos($Button[0], 5, $HC - 35, $WT)
			GUICtrlSetPos($Button[1], $WT + 9, $HC - 35, $WC - 2 * $WT - 18)
			GUICtrlSetPos($Button[2], $WC - $WT - 5, $HC - 35, $WT)
			Return 0
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_SIZE

#EndRegion Windows Message Functions

Файл(ы): Package.zip (также необходима установленная WinAPIEx UDF библиотека версии 3.7 или выше)

Скриншот:

Pkr.png

Источник: Package UDF (оффициальный форум)
Автор: Yashied
 

Viktor1703

AutoIT Гуру
Сообщения
1,535
Репутация
413
Замечательная UDF, станет неотъемлемой частью моих будущих программ :smile:
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
967
Yashied отличная работа, думаю, будет многим полезна. Вы как всегда, радуете! :ok:

P.S. Я как-то делал что-то подобное с zip архивом. Меня на эту мысль натолкнули файлы к играм. К примеру, файлы .pk3 используемые в некоторых играх, на самом деле zip архивы. Идея была распаковывать файлы, минуя диска, как в играх. Но потом отложил эксперименты, что-то у меня не клеилось с графическими файлами.
 

Viktor1703

AutoIT Гуру
Сообщения
1,535
Репутация
413
Я с помощью Вашей UDF и с помощью примеров по работе с ресурсами свой Glue Joiner написал ;D, исходники покажу только если никто против не будет.
 

SECTOR

Продвинутый
Сообщения
399
Репутация
59
Я месяц пытался создать что-то подобное! Но ничего не получалось!
Гениально! Спасибо! :ok:
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Viktor1703 сказал(а):
...исходники покажу только если никто против не будет.

А почему кто-то должен быть против...
 

Viktor1703

AutoIT Гуру
Сообщения
1,535
Репутация
413
Ну не знаю, ведь joiner'ом можно склеить вредоносную программу с чем нибудь ещё и воспользоваться, хотя к моему примеру это не относится, в выходном файле всё добавленное хранится у него в ресурсах предварительно упаковано с помощью Вашей UDF, поэтому так и написал чтоб не получить выговор или бан. :smile:
 

Zaramot

I ♥ AutoIt
Сообщения
1,160
Репутация
660
Выкладывай, мне её нужно изменить под себя :smile:
 

ynbIpb

Скриптер
Сообщения
399
Репутация
110
Отличная идея, особенно меня интересует реализация:
Yashied [?]
например изображения скина, в один файл (архив)
Не могли бы Вы показать пример загрузки скина из такого архива и установку этих изображений на GUI?
Все варианты Binary Image to Control, которые встречал выглядели как-то громоздко и увесисто.
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
ynbIpb сказал(а):
Не могли бы Вы показать пример загрузки скина из такого архива и установку этих изображений на GUI?

На скорую руку...

Код:
#Include <APIConstants.au3>
#Include <GDIPlus.au3>
#Include <Memory.au3>
#Include <Package.au3>
#Include <WinAPIEx.au3>

$hPackage = _PK_Open(@ScriptDir & '\MyPackage.pkr')
$Length = _PK_GetPacketDataLength($hPackage, -1)
$pBuffer = _WinAPI_CreateBuffer($Length)
_PK_ExtractPacketData($hPackage, -1, $pBuffer, $Length)
_PK_Close($hPackage)

GUICreate('MyGUI', 400, 400)
GUICtrlCreatePic('', 0, 0, 400, 400)
_SetImageToPic(-1, $pBuffer, $Length)
_WinAPI_FreeMemory($pBuffer)
GUISetState()

Do
Until GUIGetMsg() = -3

Func _SetImageToPic($CtrlID, $pBuffer, $iLength, $fUpdate = False)

	Local $Ret, $hMem, $pStream, $hBitmap = 0

	$CtrlID = GUICtrlGetHandle($CtrlID)
	If Not $CtrlID Then
		Return 0
	EndIf
	$hMem = _MemGlobalAlloc($iLength, $GMEM_MOVEABLE)
	If Not $hMem Then
		Return 0
	EndIf
	_WinAPI_MoveMemory(_MemGlobalLock($hMem), $pBuffer, $iLength)
	_MemGlobalUnlock($hMem)
	$pStream = _WinAPI_CreateStreamOnHGlobal($hMem)
	_GDIPlus_Startup()
	$Ret = DllCall($ghGDIPDll, 'uint', 'GdipCreateBitmapFromStream', 'ptr', $pStream, 'ptr*', 0)
	If (Not @Error) And (Not $Ret[0]) Then
		$hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($Ret[2])
		_GDIPlus_ImageDispose($Ret[2])
	EndIf
	_WinAPI_ReleaseStream($pStream)
	_GDIPlus_Shutdown()
	If Not $hBitmap Then
		Return 0
	EndIf
	_WinAPI_DeleteObject(_SendMessage($CtrlID, 0x0172, 0, $hBitmap))
	If _SendMessage($CtrlID, 0x0173) <> $hBitmap Then
		_WinAPI_DeleteObject($hBitmap)
	EndIf
	If $fUpdate Then
		_WinAPI_UpdateWindow($CtrlID)
	EndIf
	Return 1
EndFunc   ;==>_SetImageToPic


Предполагается, что в MyPackage.pkr на первом месте (-1) находится какая-либо картинка размером 400x400.
 

Ganibal95

GreenBytes
Сообщения
877
Репутация
240
Yashied
Как можно узнать md5 файла не извлекая его???
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Ganibal95 сказал(а):
Как можно узнать md5 файла не извлекая его???

Записать его при помещении файла в архив, например в раздел Info.
 

Astel064

Помог мой пост, ставь +!
Сообщения
276
Репутация
51
Yashied [?]
Записать его при помещении файла в архив, например в раздел Info.

А как считать данные их раздела Info?

Спасибо, уже сам разобрался:
Код:
BinaryToString( DllStructGetData( _PK_GetPacketInfo($hPackage, $sPasketId ), 1) )
 

joiner

Модератор
Локальный модератор
Сообщения
3,556
Репутация
628
просмотрел udf. в описании функций упаковки и распаковки есть параметры,позволяющие отображать процесс.можно пример? а то слегка не получается :-[
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
joiner сказал(а):
просмотрел udf. в описании функций упаковки и распаковки есть параметры,позволяющие отображать процесс.можно пример? а то слегка не получается :-[

Пример есть в первом сообщении.
 

joiner

Модератор
Локальный модератор
Сообщения
3,556
Репутация
628
пример кода
Код:
#include <WinAPIEx.au3>
#include <FileOperations.au3>
#include "Package.au3"

Global Const $sPackage = @ScriptDir & '\MyPackage.pkr'

Global $hPackage

If Not FileExists($sPackage) Then
    $hPackage = _PK_Create($sPackage, 'Demonstration Package')
Else
    $hPackage = _PK_Open($sPackage)
EndIf

$File = FileSelectFolder('', '')
If @error Then Exit

$f_s = _FO_FileSearch($File, '*')
Global $folder_name = _FO_PathSplit($File)

If Not @error Then
    Global $array[$f_s[0]]
    For $i = 1 To UBound($f_s) - 1
		$maintext = StringRegExpReplace($f_s[$i], '.*\\', '')
        $array[$i - 1] = StringRegExpReplace($f_s[$i], '.*' & $folder_name[1] , '')
        _PK_AddPacketFromFile($hPackage, $f_s[$i], _WinAPI_PathStripPath($maintext), True)
         If @error Then ConsoleWrite(@error & @CRLF)
    Next
EndIf

_PK_Close($hPackage)

$path_unpack = FileSelectFolder('Путь распаковки', '')

For $i = 1 To UBound($array) - 1
    $n_f = _FO_PathSplit($array[$i])
    DirCreate($path_unpack & '\' & $folder_name[1] & $n_f[0])
Next

Global Const $sPackage1 = @ScriptDir & '\MyPackage.pkr'
Global $hPackage1, $File1

$hPackage1 = _PK_Open($sPackage1)
If @error Then
    MsgBox(16, 'Package', _WinAPI_PathStripPath($sPackage1) & ' not found.')
    Exit
EndIf

    For $x = 1 To UBound($array) - 1
        $n_f = _FO_PathSplit($array[$x])
        $File1 = _PK_GetPacketDescription($hPackage1, -$x)
        _PK_ExtractPacketDataToFile($hPackage1, -$x, $path_unpack & '\' & $folder_name[1] & $n_f[0] & $File1)
        If @error Then ConsoleWrite(@error & @CRLF & $path_unpack & '\' & $folder_name[1] & $n_f[0] & $File1 & @CRLF)
	Next

_PK_Close($hPackage1)

в этом коде я делаю поиск файлов в указанной папке ( есть вложенные папки)
размер общий примерно 700мб
упаковка происходит быстро и без ошибок(ни разу не было ошибки)
распаковку настроил так, что сначала создается структура папок, а потом в них распаковываются файлы. каждый файл в свою папку как было до упаковки. большая часть архива распаковывается быстро. потом несколько раз выпадает ошибка 9 и скрипт зависает на функции распаковки. ни ошибок не выдает и не распаковывает.
при проверке в папках файлы распаковались как положено. то есть структура и расположение файлов и папок получается как до упаковки в исходной папке.
не могу понять в чем ошибка
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Скорее всего проблема либо в неправильном пути, либо в его длине. Во-первых, поствьте ConsoleWrite() в код и покажите на каком файле виснит скрипт.

Код:
For $x = 1 To UBound($array) - 1
	$n_f = _FO_PathSplit($array[$x])
	$File1 = _PK_GetPacketDescription($hPackage1, -$x)
	ConsoleWrite($path_unpack & '\' & $folder_name[1] & $n_f[0] & $File1 & @CR)
	_PK_ExtractPacketDataToFile($hPackage1, -$x, $path_unpack & '\' & $folder_name[1] & $n_f[0] & $File1)
;	If @error Then ConsoleWrite(@error & @CRLF & $path_unpack & '\' & $folder_name[1] & $n_f[0] & $File1 & @CRLF)
Next


А во-вторых, попробуйте отключить компрессию в функции _PK_AddPacketFromFile().
 

joiner

Модератор
Локальный модератор
Сообщения
3,556
Репутация
628
при написании были варианты с ошибочными путями. функция распаковки быстро выкидывала ошибку и не зависала.
отключил компрессию. произошла распаковка более половины архива ( весь архив 700мб - более 4 000 файлов) и скорость распаковки упала раз в десять. процессор был загружен на 50%. я выгрузил скрипт.
ну и компрессия..очень ведь не плохо..не хотел использовать сторонние dll. твоя библиотека прямо то, что нужно
длина пути. ведь распаковка идет. только сильно замедляется и через раз вываливаются ошибки. точнее одна. номер 9. судя по описанию ошибки - это неопознанная ошибка
 
Верх