Jump to content


Photo

FileFormat : ZIP : ZIP_CentralDir()


  • Please log in to reply
7 replies to this topic

#1 SKAN

SKAN
  • Administrators
  • 9063 posts

Posted 28 June 2012 - 11:14 PM

    gAHK32    
Generic AutoHotkey 32bit

Finding files residing in ZIP container is now easier and faster!

ZIP_CentralDir() returns the 'Central Directory' available in ZIP files in a 'Human Readable' and 'AHK Parseable text format.

A single row consists of following fields:

[*:1jhl19em]File Index
[*:1jhl19em]Data Offset ( from BOF )
[*:1jhl19em]Data Size
[*:1jhl19em]Data Size Original
[*:1jhl19em]File Date (Last Modified)
[*:1jhl19em]File Attributes
[*:1jhl19em]Compression Method
[*:1jhl19em]Filepath/Name
A sample output of simple ZIP follows:

1    	0         	10931     	10931     	20120629025030	A    	Store     	decoy.png
2    	10970     	6613      	18120     	20070828030736	A    	Deflate   	GPL 2.0.txt
3    	17624     	774       	1483      	20120629025352	A    	Deflate   	MyLicense.txt

The fields are tab-delimited and rows end with linefeed. All fields except Filename are of fixed length.
I will add additional fields if anyone requests. For eg. CRC32 could help finding identical files.

An another sample ( ZIP with folder tree ):
Central Directory Listing for AutoHotkey104805.zip



The Function:

[color=#225599]/*            ____ _  ___      ___             _             _  ___  _
             |_  /| || . \    |  _> ___ ._ _ _| |_ _ _  ___ | || . \<_> _ _
              / / | ||  _/___ | <__/ ._>| ' | | | | '_><_> || || | || || '_>
             /___||_||_| |___|`___/\___.|_|_| |_| |_|  <___||_||___/|_||_|
             ZIP_CentralDir()  - Central Directory Listing for PK-ZIP Files
               http://www.autohotkey.com/community/viewtopic.php?t=88182
               Author: SKAN  ( A.N.Suresh Kumar, arian.suresh@gmail.com )
                      Created: 28-Jun-2012 / LastMod: 28-Jun-2012
*/
[/color]
ZIP_CentralDir( Zip ) {    ;   by SKAN, www.autohotkey.com/community/viewtopic.php?t=88182
                           ;   CD: 28-Jun-2012 / LM: 28-Jun-2012 / Rev.01

 Static StrGet = "StrGet",  InBufRev
 Static GENERIC_READ = 0x80000000, GENERIC_WRITE = 0x40000000
 Static FILE_SHARE_READ = 0x00000001, FILE_SHARE_WRITE = 0x00000002
 Static OPEN_EXISTING = 3, CREATE_NEW = 1
 Static FILE_BEGIN = 0, FILE_CURRENT = 1, FILE_END = 2

 ;  [color=#BF0000]InBufRev()  -  Machine code binary buffer searching regardless of NULL - By wOxxOm[/color]
 ;  See Topic   :  www.autohotkey.com/community/viewtopic.php?t=25925
 ;                 Machine code was adapted below with the kind permission from author

 If ! VarSetCapacity( InBufRev ) {
   FiH := "530CEC83E58955|5D8B9C57565251|DE8E0F00FB8314|4810458B000000|0FFFF983184D8B|C84"
   . "C0FC839C844|8E0F41CF89D929|087D03000000C1|FC00E0830C758B|2A744B22744BAC|4B43744B357"
   . "44B|F2FD93AD934E75|0000009B850FAE|81E9F375025F39|0FAEF2FD000000|76EB0000008885|7F75"
   . "AEF2268AFD|68EBF775026738|AEF2FD93AD6693|75025F39666F75|F2FDAD4E57EBF6|750147396075"
   . "AE|89AD434E49EBF7|02EBC1DA89FC75|8903E283F45D89|FDD187DF87F855|75AEF2CA87FB87|FCF77"
   . "501473938|05C783CA89FB89|85F44D8BFC758B|DC75A7F30474C9|0474C985F84D8B|47DF89D175A6F"
   . "3|5F9D08452BF889|14C2C95B595A5E|F0EBD0F7C03100|", VarSetCapacity( InBufRev, 252, 0 )
   Loop, Parse, FiH, |
     NumPut( "0x" A_LoopField,  InBufRev, ( 7 * ( A_Index-1 ) ),  "Int64" )
   VarSetCapacity( FiH, 0 )
 }

 IfNotExist, %Zip%, Return "", ErrorLevel := 1


 hFil := DllCall( "CreateFile", Str, Zip, UInt,GENERIC_READ, UInt,FILE_SHARE_READ, Int,0
                              , UInt,OPEN_EXISTING, Int, 0, Int,0 )

 IfLess, hFil, 0, Return "",   ErrorLevel := 2

 ; Read into Buffer '65557 tail bytes' or 'the complete file', whichever is lesser
 
 FilSz := DllCall( "SetFilePointer", UInt,hFil, Int,0, Int,0, UInt,FILE_END, UInt )
 VarSetCapacity( Buf, BufSz := ( FilSz <= 65557 ? FilSz : 65557 ), 0 )
 
 fPtr := DllCall( "SetFilePointer", UInt,hFil, Int,0-BufSz, Int,0, UInt,FILE_END, UInt )
 DllCall( "ReadFile", UInt,hFil, UInt,&Buf, UInt,BufSz, UIntP,BR, UInt,0, UInt )

 ; Scan the Buffer for Signature /x50/x4B/x05/x06 ( End of central directory record )
 
 VarSetCapacity( N,4,0 ), NumPut( 0x06054B50,N )
 FoundPos := DllCall( &InBufRev, UInt,&Buf, UInt,&N, UInt,BufSz, UInt,4, Int,-1, Int )

 If ( FoundPos < 0 )
   Return "", DllCall( "CloseHandle", UInt,hFil ), ErrorLevel := 3

 TotalFiles := NumGet( Buf, FoundPos+8, "UShort" )
 SizeOfCD   := NumGet( Buf, FoundPos+12,  "UInt" )
 OffsetToCD := NumGet( Buf, FoundPos+16,  "UInt" )

 ; Central Directory has been located, yet check the validity of signature location.
 ; There is a remote possibility that Signature could be a part of zip comment
 
 If ( FoundPos + fPtr <> OffsetToCD + SizeOfCD )
   Return "", DllCall( "CloseHandle", UInt,hFil ), ErrorLevel := 3

 ; Move File pointer to 'Central Directory' and parse 'File Headers' one at a time
 
 DllCall( "SetFilePointer", UInt,hFil, UInt,OffsetToCD, Int,0, UInt,FILE_BEGIN )
 Sps10 := A_Space A_Space A_Space A_Space A_Space A_Space A_Space A_Space A_Space A_Space
 
 Loop % ( TotalFiles ) {

   ; File Header should be 46 + ExtraBytes. ExtraBytes is unknown, so Read 46 bytes first
   
   DllCall( "ReadFile", UInt,hFil, UInt,&Buf, UInt,46, UIntP,BR, UInt,0, UInt )

   FileNameLen    := NumGet( Buf, 28, "UShort" )
   ExtraFieldLen  := NumGet( Buf, 30, "UShort" )
   FileCommentLen := NumGet( Buf, 32, "UShort" )

   ExtraBytes := FileNameLen + ExtraFieldLen + FileCommentLen
   
   ; Extra Bytes has been determined, so lets append it to Buffer.
   
   DllCall( "ReadFile", UInt,hFil, UInt,&Buf+46, UInt,ExtraBytes, UIntP,BR, UInt,0 )
   
   ; The complete header is available. Gathering Information...

   ; Extract DOS Date & Time
   
   DosTime := NumGet( Buf,12, "UShort" )
   DosDate := NumGet( Buf,14, "UShort" )

   VarSetCapacity( T$,24,0 )
   DllCall( "DosDateTimeToFileTime", UShort,DosDate, UShort,DosTime, Uint,&T$+16 )
   DllCall( "FileTimeToSystemTime" , UInt,&T$+16, UInt,&T$ )

   DT_LastMod := NumGet( T$, 0,"Short" )       . SubStr( "0" NumGet( T$, 2,"Short" ), -1 )
   . SubStr( "0" NumGet( T$, 6,"Short" ), -1 ) . SubStr( "0" NumGet( T$, 8,"Short" ), -1 )
   . SubStr( "0" NumGet( T$,10,"Short" ), -1 ) . SubStr( "0" NumGet( T$,12,"Short" ), -1 )

   ; Extract File Attibutes

   nAttr := NumGet( Buf, 38, "UInt" )

   FileAttr := ( nAttr & 0x01  ? "R" : "" )    ;   FILE_ATTRIBUTE_READONLY
            .  ( nAttr & 0x20  ? "A" : "" )    ;   FILE_ATTRIBUTE_ARCHIVE
            .  ( nAttr & 0x04  ? "S" : "" )    ;   FILE_ATTRIBUTE_SYSTEM
            .  ( nAttr & 0x02  ? "H" : "" )    ;   FILE_ATTRIBUTE_HIDDEN
            .  ( nAttr & 0x10  ? "D" : "" )    ;   FILE_ATTRIBUTE_DIRECTORY

   ; Determine compression method
   
   nComp := NumGet( Buf, 10, "UShort" )

   FileComp := ( nComp =  0 ) ? "Store     "   ;   No Compression
            :  ( nComp =  1 ) ? "Shrunk    "
            :  ( nComp =  2 ) ? "Reduce CF1"   ;   Reduced with compression factor 1
            :  ( nComp =  3 ) ? "Reduce CF2"   ;   Reduced with compression factor 2
            :  ( nComp =  4 ) ? "Reduce CF3"   ;   Reduced with compression factor 3
            :  ( nComp =  5 ) ? "Reduce CF4"   ;   Reduced with compression factor 4
            :  ( nComp =  6 ) ? "Implode   "
            :  ( nComp =  8 ) ? "Deflate   "   ;   The common method
            :  ( nComp =  9 ) ? "E.Deflate "   ;   Enhanced Deflate
            :  ( nComp = 10 ) ? "PKW DCL Im"   ;   PKWare DCL imploded
            :  ( nComp = 12 ) ? "BZIP2     "
            :  ( nComp = 14 ) ? "LZMA      "
            :  ( nComp = 18 ) ? "IBM Terse "
            :  ( nComp = 19 ) ? "IBM LZ777z"   ;   IBM LZ77 z
            :  ( nComp = 19 ) ? "PPMd vI.1 "   ;   PPMd version I, Rev 1
            :                   "Unknown   "   ;   Reserved/Newer
            
   ; Extract Filename

   If A_IsUnicode
      FileName := %StrGet%( &Buf+46, FileNameLen, ""  )
   Else
      VarSetCapacity( FileName, FileNameLen, 0 )
    , DllCall( "lstrcpynA", Str,FileName, UInt,&Buf+46, UInt,FileNameLen + 1 )


   ; Append the gathered information to List
   
   List .= SubStr( A_Index Sps10 , 1, 5 )        .  A_Tab ; File Index
        .  SubStr( NumGet(Buf,42) Sps10, 1, 10 ) .  A_Tab ; Data Offset
        .  SubStr( NumGet(Buf,20) Sps10, 1, 10 ) .  A_Tab ; Data Size
        .  SubStr( NumGet(Buf,24) Sps10, 1, 10 ) .  A_Tab ; Data Size Original
        .  DT_LastMod                            .  A_Tab ; File Date (Last Modified)
        .  SubStr( FileAttr Sps10, 1, 5 )        .  A_Tab ; File Attributes
        .  FileComp                              .  A_Tab ; Compression Method
        .  FileName                              .  "`n"  ; Filepath/Name
   
  Continue ; Repeat the Loop
  
 }

 StringTrimRight, List, List, 1
 DllCall( "CloseHandle", UInt,hFil )
Return List, ErrorLevel := TotalFiles
}


Credit:
The structure of a PKZip file by Florian Buchholz
InBufRev() as Assembly Machine code by wOxxOm

#2 Guests

  • Guests

Posted 29 June 2012 - 07:57 AM

Another master piece by the might SKAN, thanks :)

#3 SKAN

SKAN
  • Administrators
  • 9063 posts

Posted 29 June 2012 - 11:19 AM

This is just a prelude, my friend! :)

I have a working wrapper for MiniZip.dll (89. KiB), the most simplest ZIP/UNZIP solution I have ever seen.

ZIP format offers extra protection by maintaining two file headers. One is prefixed before actual data and an extra detailed copy is maintained in 'Central Directory'.
ZIP utilities like 7-ZIP lists you the 'Central Directory' whereas MiniZip provides only prefix header for 1 file that too with incomplete info.
While writing wrapper for MiniZip, I had to load the 'minizip created zip' in 7z everytime to see results - Got fed up, and created this function as an accessory tool for MiniZip wrapper.

I had some fun while writing this function:

I made a zip of my Avatar and a couple of text files:

1    	0         	[color=#FF0000]10931[/color]     	10931     	20120629025030	A    	[color=#FF0000]Store[/color]     	decoy.png
2    	[color=#FF0000]10970[/color]     	6613      	18120     	20070828030736	A    	Deflate   	GPL 2.0.txt
3    	17624     	774       	1483      	20120629025352	A    	Deflate   	MyLicense.txt

[*:217vwzgu]Since PNG is already in compressed format, it is just "stored" inside zip file.
[*:217vwzgu]Arranged in alphabetical order, PNG file occurs as first file ( offset 0 ) in the ZIP file.
[*:217vwzgu]The size of PNG is 10931 but the second file is starting at 10970
[*:217vwzgu]The difference ( 10970 - 10931 ) is 39 bytes which is the prefix header for PNG file
[*:217vwzgu]I wrote a small script to swap the leading 39 bytes ( header ) with the subsequent 10931 bytes ( actual data )
[*:217vwzgu]I renamed the zip file with .png extension and all software accepts it as png.
[*:217vwzgu]Since Central Directory is intact, zip utilities ( including windows explorer ) are able to uncompress/extract all files other than the first one.
I am now using the above modified zip file as avatar!!!

http://www.autohotke...avatar=1820.png

^ The above image is a ZIP file!!! Peace Love Happiness and a couple of licenses :p

#4 nlid

nlid
  • Guests

Posted 29 June 2012 - 12:31 PM

I have a working wrapper for MiniZip.dll (89. KiB), the most simplest ZIP/UNZIP solution I have ever seen.


:)

Code! Where is the code! 8)

#5 SKAN

SKAN
  • Administrators
  • 9063 posts

Posted 29 June 2012 - 12:58 PM

Code! Where is the code! 8)


I will post the wrapper in a few hours :)

But I am not sure if I can upload minizip package in autohotkey.net
Can you ( or someone fluent in German ), let me know if its against the author's wish to distribute the package:

<!-- m -->http://www.paules-pc... ... post811316<!-- m -->

Google German to English Translation is pretty bad, but I get the feeling that the author wants his website ( <!-- m -->http://www.realsource.de/<!-- m --> ) to be the only source for the complete package.


The license to distribute DLL is excellent:

Minizip license 

  Copyright (C) 2009 Thomas Schulz (ts-soft)
-----------------------------------------------------------------------  
This software is provided 'as-is', without any express Or implied  warranty.  
In no event will the authors be held liable For any damages  arising from 
the use of this software.

  Permission is granted To anyone To use this software For any purpose,  
including commercial applications, And To alter it And redistribute it  freely, 
subject To the following restrictions:

  1. The origin of this software must Not be misrepresented; you must not
claim that you wrote the original software. If you use this software     
in a product, an acknowledgment in the product documentation would be     
appreciated but is Not required.  

2. Altered source versions must be plainly marked As such, And must Not be     
misrepresented As being the original software.  

3. This notice may Not be removed Or altered from any source distribution.


#6 HotKeyIt

HotKeyIt
  • Fellows
  • 6191 posts

Posted 29 June 2012 - 01:14 PM

... but I get the feeling that the author wants his website ( <!-- m -->http://www.realsource.de/<!-- m --> ) to be the only source for the complete package.[/code]

It says you will need an approval from realsource.de if you want to host it.

#7 Lexikos

Lexikos
  • Administrators
  • 8919 posts

Posted 29 June 2012 - 03:05 PM

I'm all for respecting the author's wishes, but the license does explicitly allow redistribution. Btw, it appears to be the zlib license.

#8 SKAN

SKAN
  • Administrators
  • 9063 posts

Posted 29 June 2012 - 03:22 PM

Thanks HotkeyIt :)
Guess I should provide the official download link for now.

it appears to be the zlib license.


That makes sense since minizip is a fork of zlib
<!-- m -->http://www.winimage....ll/minizip.html<!-- m -->
.. and minizip.dll statically links the above library.

@nlid : MiniZIP Wrapper : <!-- l --><a class="postlink-local" href="http://www.autohotkey.com/community/viewtopic.php?t=88218">viewtopic.php?t=88218</a><!-- l -->