You are not logged in.
Pages: 1
Hello Arnaud,
I'm using TZipRead (in SynZip.pas) but I have a file it couldn't read, whereas every other zip reader I've tried managed to unzip it without problem.
It appears that this file (a printer drivers package for a bar code label printing software, you can download it at "http://ftp.nicelabel.com/software/drivers/sartorius.exe") has 2 features that your code didn't support :
1) it's a self-extracting zip file
2) it's a digitally signed file
The thing is, it's a perfectly valid zip file :
- the extraction program is at the beginning of the file and the file addresses in the zip central directory take this offset into account
- the digital signature is at the end of the file, after the EOCD zip header, so it doesn't interfere with zip reading code
So I've done a couple of slight modifications into SynZip.pas that work great here.
Could you review/optimize them and if it suits you, include it in the Synopse's source ? Unless there is something I didn't have seen...
Thank you !
Note: I had to modify TZipRead.Create(aFile: THandle; ZipStartOffset: cardinal=0; Size: cardinal=0).
But I didn't quite understood the use case for the look-ups inside (ExeOffset part), so I removed them, leaving the checks to the main Create function.
Indeed, I understood that it was for a case where we provide a particularly crafted file with a raw concatenation of a zip stream inside another file.
So the zip files addresses in the zip central directory are not relative to the beginning of the file but to a particular point in the file.
In this case, I think that's the job of the function caller to provide the suitable ZipStartOffset/Size to find the zip file data inside the container file, no ?
constructor TZipRead.Create(BufZip: pByteArray; Size: cardinal);
var lhr: PLastHeader;
H: PFileHeader;
lfhr: PLocalFileHeader;
i,j: integer;
{$ifdef CONDITIONALEXPRESSIONS}
tmp: UTF8String;
{$else}
tmp: ZipString;
{$endif}
begin
// resources size may be rounded up to alignment
// or data can come from a signed file (with signature at the end)
lhr:=nil;
for i := Size-sizeof(TLastHeader) downto 0 do begin
lhr := @BufZip[i];
// +1 avoids false positive
if lhr^.signature+1=LASTHEADER_SIGNATURE_INC then
break;
end;
if (lhr=nil) or (lhr^.headerOffset>=Size) then begin
UnMap;
raise ESynZipException.Create('ZIP format');
end;
SetLength(Entry,lhr^.totalFiles); // fill Entry[] with the Zip headers
ReadOffset := lhr^.headerOffset;
FirstFileHeader := @BufZip[lhr^.headerOffset];
H := FirstFileHeader;
for i := 1 to lhr^.totalFiles do begin
if H^.signature+1<>ENTRY_SIGNATURE_INC then begin // +1 to avoid match in exe
UnMap;
raise ESynZipException.Create('ZIP format');
end;
lfhr := @BufZip[H^.localHeadOff];
with lfhr^.fileInfo do
if flags and (1 shl 3)<>0 then begin // crc+sizes in "data descriptor"
if (zcrc32<>0) or (zzipSize<>0) or (zfullSize<>0) then
raise ESynZipException.Create('ZIP extended format');
// UnZip() will call RetrieveFileInfo()
end else
if (zzipSize=cardinal(-1)) or (zfullSize=cardinal(-1)) then
raise ESynZipException.Create('ZIP64 format not supported');
with Entry[Count] do begin
infoLocal := @lfhr^.fileInfo;
infoDirectory := H;
storedName := PAnsiChar(lfhr)+sizeof(lfhr^);
data := storedName+infoLocal^.NameLen+infoLocal^.extraLen; // data mapped in memory
SetString(tmp,storedName,infoLocal^.nameLen);
for j := 0 to infoLocal^.nameLen-1 do
if storedName[j]='/' then // normalize path delimiter
PAnsiChar(Pointer(tmp))[j] := '\';
{$ifdef CONDITIONALEXPRESSIONS}
// Delphi 5 doesn't have UTF8Decode/UTF8Encode functions -> make 7 bit version
if infoLocal^.GetUTF8FileName then
// decode UTF-8 file name into native string/TFileName type
zipName := UTF8Decode(tmp) else
{$endif}
begin
// decode OEM/DOS file name into native string/TFileName type
SetLength(zipName,infoLocal^.nameLen);
OemToChar(Pointer(tmp),Pointer(zipName)); // OemToCharW/OemToCharA
end;
inc(PByte(H),sizeof(H^)+infoLocal^.NameLen+H^.fileInfo.extraLen+H^.commentLen);
if not(infoLocal^.zZipMethod in [Z_STORED,Z_DEFLATED]) then
raise ESynZipException.CreateFmt(
'Unsupported compression method %d for %s',[infoLocal^.zZipMethod,zipName]);
if (zipName='') or (zipName[length(zipName)]='\') then
continue; // ignore folder
if infoLocal^.flags and (1 shl 3)=0 then
if (infoLocal^.zzipSize=0) or (infoLocal^.zfullSize=0) then
raise ESynZipException.CreateFmt('"%s" size=0 in ZIP',[zipName]);
inc(Count); // add file to Entry[]
end;
end;
SetLength(Entry,Count); // remove unused entries (corresponding to ignored folders)
end;
constructor TZipRead.Create(aFile: THandle; ZipStartOffset: cardinal=0;
Size: cardinal=0);
begin
if aFile<=0 then
exit;
if Size=0 then
Size := GetFileSize(aFile, nil)-ZipStartOffset;
map := CreateFileMapping(aFile, nil, PAGE_READONLY, 0, 0, nil);
if map=0 then begin
Unmap;
raise ESynZipException.Create('Missing File');
end;
Buf := MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
Create(@Buf[ZipStartOffset], Size);
end;
Pages: 1