unit ZIPTools;
{
ZIP Tools V1.00
Written by Gerald J Holdsworth
This source is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public Licence as published by the Free
Software Foundation; either version 3 of the Licence, or (at your option)
any later version.
This code is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public Licence for more
details.
A copy of the GNU General Public Licence is available on the World Wide Web
at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
Boston, MA 02110-1335, USA.
}
{$mode ObjFPC}{$H+}
interface
uses
Classes,SysUtils;
function FindCL(var EoCL: Integer;var buffer: array of Byte): Integer;
function ValidateZIPFile(var buffer: array of Byte): Boolean;
function CombineZIP(files: array of String; outputfile: String): Boolean;
implementation
//Finds the central library and returns the end of central library
function FindCL(var EoCL: Integer;var buffer: array of Byte): Integer;
var
i: Integer=0;
begin
//Start with a default (i.e., not found)
Result:=-1;
EoCL:=-1;
//Can't have a file smaller than 22 bytes
if Length(buffer)<22 then exit;
//Start here
i:=Length(buffer)-3;
//And work backwards until we find the EoCL marker
repeat
dec(i);
until((buffer[i ]=$50)
and(buffer[i+1]=$4B)
and(buffer[i+2]=$05)
and(buffer[i+3]=$06))
or (i=0);
//Found OK?
if (buffer[i ]=$50)
and(buffer[i+1]=$4B)
and(buffer[i+2]=$05)
and(buffer[i+3]=$06) then
begin
//Mark it
EoCL:=i;
//Retreive where the central library starts
Result:=buffer[i+$10]
+buffer[i+$11]<<8
+buffer[i+$12]<<16
+buffer[i+$13]<<24;
end;
end;
//Validate a ZIP file
function ValidateZIPFile(var buffer: array of Byte): Boolean;
var
Lfile : TFileStream;
CL : Integer=-1;
EoCL : Integer=-1;
check : Boolean=False;
temp : Cardinal=0;
numfiles: Integer=0;
index : Integer=0;
ptr : Cardinal=0;
data : Cardinal=0;
size : Cardinal=0;
usize : Cardinal=0;
//Check the length of the extra field
function CheckExtraField(addr,len: Cardinal): Boolean;
var
total: Cardinal=0;
begin
Result:=False;
while(total<len)and(addr<Length(buffer)-4)do
begin
//Extra field has pairs of 2 byte tag + 2 byte length
inc(total,4+buffer[addr+2]+buffer[addr+3]<<8);
inc(addr,4+buffer[addr+2]+buffer[addr+3]<<8);
end;
Result:=total=len;
end;
begin
Result:=False;
//Minimum size of a ZIP file is 22 bytes
if Length(buffer)>=22 then
begin
//Find the central library
CL:=FindCL(EoCL,buffer);
if(CL<>-1)and(EoCL<>-1)then
begin
Result:=True;
//Size of Central Library
temp:=buffer[EoCL+$C]
+buffer[EoCL+$D]<<8
+buffer[EoCL+$E]<<16
+buffer[EoCL+$F]<<24;
//Test to see if it matches the references already found
Result:=(temp=EoCL-CL)AND(Result);
//Total number of files
numfiles:=buffer[EoCL+$A]
+buffer[EoCL+$B]<<8;
ptr:=CL;
index:=0;
while(index<numfiles)and(ptr<EoCL)do
begin
//Check the signature
Result:=(buffer[ptr ]=$50)
and(buffer[ptr+1]=$4B)
and(buffer[ptr+2]=$01)
and(buffer[ptr+3]=$02)
AND(Result);
//Check the extra field
temp:=buffer[ptr+$1E]+buffer[ptr+$1F]<<8;
if temp>0 then
Result:=(CheckExtraField(ptr+$2E+buffer[ptr+$1C]+buffer[ptr+$1D]<<8,temp))
AND(Result);
//Compressed size
size:=buffer[ptr+$14]
+buffer[ptr+$15]<<8
+buffer[ptr+$16]<<16
+buffer[ptr+$17]<<24;
//Uncompressed size
usize:=buffer[ptr+$18]
+buffer[ptr+$19]<<8
+buffer[ptr+$1A]<<16
+buffer[ptr+$1B]<<24;
//Data pointer
data:=buffer[ptr+$2A]
+buffer[ptr+$2B]<<8
+buffer[ptr+$2C]<<16
+buffer[ptr+$2D]<<24;
//Check the local file entry
Result:=(buffer[data]=$50)
and(buffer[data+1]=$4B)
and(buffer[data+2]=$03)
and(buffer[data+3]=$04)
AND(Result);
//Check compressed size
Result:=(buffer[data+$12]
+buffer[data+$13]<<8
+buffer[data+$14]<<16
+buffer[data+$15]<<24=size)
AND(Result);
//Check uncompressed size
Result:=(buffer[data+$16]
+buffer[data+$17]<<8
+buffer[data+$18]<<16
+buffer[data+$19]<<24=usize)
AND(Result);
//Check the extra field
temp:=buffer[data+$1C]+buffer[data+$1D]<<8;
if temp>0 then
Result:=(CheckExtraField(data+$1E+buffer[data+$1A]+buffer[data+$1B]<<8,temp))
AND(Result);
//Next entry - here we work out where it should be
temp:=ptr+$2E
+buffer[ptr+$1C]+buffer[ptr+$1D]<<8
+buffer[ptr+$1E]+buffer[ptr+$1F]<<8
+buffer[ptr+$20]+buffer[ptr+$21]<<8;
//Now we find the next marker
repeat
inc(ptr);
until(ptr=EoCL)or((buffer[ptr ]=$50)
and(buffer[ptr+1]=$4B)
and(buffer[ptr+2]=$01)
and(buffer[ptr+3]=$02));
//And check if they match
Result:=(ptr=temp)AND(Result);
//Next file
inc(index);
end;
//Check we got all the files
Result:=(index=numfiles)AND(Result);
end;
end;
end;
//Merges two ZIP files
function CombineZIP(files: array of String; outputfile: String): Boolean;
var
input : array[0..1] of TFileStream;
output : TFileStream;
inbuffer : array[0..1] of array of Byte;
outbuffer : array of Byte;
ptr : Integer=0;
cnt : Integer=0;
fileptr : Cardinal=0;
CL : array[0..1] of Integer;
EoCL : array[0..1] of Integer;
temp : Cardinal=0;
numfiles : Cardinal=0;
CLsize : Cardinal=0;
filesize : Cardinal=0;
begin
Result:=False;
if Length(files)>=2 then //Only works with two files, the rest are ignored
begin
Result:=True;
//Read in the files
for ptr:=0 to 1 do
begin
input[ptr]:=TFileStream.Create(files[ptr],fmOpenRead OR fmShareDenyNone);
input[ptr].Position:=0;
SetLength(inbuffer[ptr],input[ptr].Size);
input[ptr].Read(inbuffer[ptr][0],input[ptr].Size);
input[ptr].Free;
//Get the position of the central library for each
CL[ptr]:=FindCL(EoCL[ptr],inbuffer[ptr]);
Result:=(Result)AND(CL[ptr]<>-1)AND(EoCL[ptr]<>-1);
//Count the number of files stored
if EoCL[ptr]<>-1 then
inc(numfiles,inbuffer[ptr][EoCL[ptr]+$A]+inbuffer[ptr][EoCL[ptr]+$B]<<8);
end;
//Create the output file
if Result then
begin
//This will be the eventual central library size
CLsize:=(EoCL[0]-CL[0])+(EoCL[1]-CL[1]);
//This will be the eventual file size
filesize:=CL[0]+CL[1]+CLsize+22;
SetLength(outbuffer,filesize);
//Write the files. The files from the second ZIP goes where the first CL was
fileptr:=0;
for cnt:=0 to 1 do
begin
for ptr:=0 to CL[cnt]-1 do outbuffer[fileptr+ptr]:=inbuffer[cnt][ptr];
inc(fileptr,CL[cnt]);
end;
//Write the CLs
for cnt:=0 to 1 do
begin
//We'll need to find each file entry and adjust by adding CL1 to the adddress
for ptr:=CL[cnt] to EoCL[cnt]-1 do
begin
outbuffer[fileptr-CL[cnt]+ptr]:=inbuffer[cnt][ptr];
if cnt>0 then
//Found a file?
if (inbuffer[cnt][ptr-$2E]=$50)
and(inbuffer[cnt][ptr-$2D]=$4B)
and(inbuffer[cnt][ptr-$2C]=$01)
and(inbuffer[cnt][ptr-$2B]=$02)then
begin
//Get the data offset
temp:=inbuffer[cnt][ptr-4]
+inbuffer[cnt][ptr-3]<<8
+inbuffer[cnt][ptr-2]<<16
+inbuffer[cnt][ptr-1]<<24;
//Adjust the data offset
inc(temp,CL[cnt-1]);
//Save back
outbuffer[(fileptr-CL[cnt]+ptr)-4]:= temp AND $000000FF;
outbuffer[(fileptr-CL[cnt]+ptr)-3]:=(temp AND $0000FF00)>>8;
outbuffer[(fileptr-CL[cnt]+ptr)-2]:=(temp AND $00FF0000)>>16;
outbuffer[(fileptr-CL[cnt]+ptr)-1]:=(temp AND $FF000000)>>24;
end;
end;
inc(fileptr,CL[cnt]);
end;
//Write the central directory
fileptr:=filesize-22;
outbuffer[fileptr ]:=$50;
outbuffer[fileptr+$01]:=$4B;
outbuffer[fileptr+$02]:=$05;
outbuffer[fileptr+$03]:=$06;
outbuffer[fileptr+$08]:= numfiles AND $00FF;
outbuffer[fileptr+$09]:=(numfiles AND $FF00)>>8;
outbuffer[fileptr+$0A]:= numfiles AND $00FF;
outbuffer[fileptr+$0B]:=(numfiles AND $FF00)>>8;
outbuffer[fileptr+$0C]:= CLsize AND $000000FF;
outbuffer[fileptr+$0D]:=(CLsize AND $0000FF00)>>8;
outbuffer[fileptr+$0E]:=(CLsize AND $00FF0000)>>16;
outbuffer[fileptr+$0F]:=(CLsize AND $FF000000)>>24;
outbuffer[fileptr+$10]:= (CL[0]+CL[1]) AND $000000FF;
outbuffer[fileptr+$11]:=((CL[0]+CL[1]) AND $0000FF00)>>8;
outbuffer[fileptr+$12]:=((CL[0]+CL[1]) AND $00FF0000)>>16;
outbuffer[fileptr+$13]:=((CL[0]+CL[1]) AND $FF000000)>>24;
//Save the data to a file
output:=TFileStream.Create(outputfile,fmCreate OR fmShareDenyNone);
output.Position:=0;
output.Write(outbuffer[0],Length(outbuffer));
output.Free;
end;
end;
end;
end.