Browse Source

+ TUnZipper: added support for zip file comments

git-svn-id: trunk@22284 -
nickysn 13 years ago
parent
commit
cf29cdf1f3
1 changed files with 65 additions and 13 deletions
  1. 65 13
      packages/paszlib/src/zipper.pp

+ 65 - 13
packages/paszlib/src/zipper.pp

@@ -393,6 +393,7 @@ Type
     FBufSize    : LongWord;
     FFileName   :  String;         { Name of resulting Zip file                 }
     FOutputPath : String;
+    FFileComment: String;
     FEntries    : TFullZipFileEntries;
     FFiles      : TStrings;
     FZipStream  : TStream;     { I/O file variables                         }
@@ -438,6 +439,7 @@ Type
     Property OnEndFile : TOnEndOfFileEvent Read FOnEndOfFile Write FOnEndOfFile;
     Property FileName : String Read FFileName Write SetFileName;
     Property OutputPath : String Read FOutputPath Write SetOutputPath;
+    Property FileComment: String Read FFileComment;
     Property Files : TStrings Read FFiles;
     Property Entries : TFullZipFileEntries Read FEntries;
   end;
@@ -1668,31 +1670,81 @@ Begin
     end;
 End;
 
+procedure FindEndHeader(AZip: TStream; out AEndHdr: End_of_Central_Dir_Type; out AEndHdrPos: Int64; out AZipFileComment: string);
+var
+  Buf: PByte;
+  BufSize: Integer;
+  I: Integer;
+begin
+  AZipFileComment := '';
+  AEndHdrPos := AZip.Size - SizeOf(AEndHdr);
+  if AEndHdrPos < 0 then
+  begin
+    AEndHdrPos := -1;
+    FillChar(AEndHdr, SizeOf(AEndHdr), 0);
+    exit;
+  end;
+  AZip.Seek(AEndHdrPos, soFromBeginning);
+  AZip.ReadBuffer(AEndHdr, SizeOf(AEndHdr));
+  {$IFDEF FPC_BIG_ENDIAN}
+  AEndHdr := SwapECD(AEndHdr);
+  {$ENDIF}
+  if (AEndHdr.Signature = END_OF_CENTRAL_DIR_SIGNATURE) and
+     (AEndHdr.ZipFile_Comment_Length = 0) then
+    exit;
+
+  // scan the last (64k + something) bytes for the END_OF_CENTRAL_DIR_SIGNATURE
+  // (zip file comments are 64k max)
+  BufSize := 65536 + SizeOf(AEndHdr) + 128;
+  if AZip.Size < BufSize then
+    BufSize := AZip.Size;
+
+  Buf := GetMem(BufSize);
+  try
+    AZip.Seek(AZip.Size - BufSize, soFromBeginning);
+    AZip.ReadBuffer(Buf^, BufSize);
+
+    for I := BufSize - SizeOf(AEndHdr) downto 0 do
+    begin
+      if (Buf[I] or (Buf[I + 1] shl 8) or (Buf[I + 2] shl 16) or (Buf[I + 3] shl 24)) = END_OF_CENTRAL_DIR_SIGNATURE then
+      begin
+        Move(Buf[I], AEndHdr, SizeOf(AEndHdr));
+        {$IFDEF FPC_BIG_ENDIAN}
+        AEndHdr := SwapECD(AEndHdr);
+        {$ENDIF}
+        if (AEndHdr.Signature = END_OF_CENTRAL_DIR_SIGNATURE) and
+           (I + SizeOf(AEndHdr) + AEndHdr.ZipFile_Comment_Length = BufSize) then
+        begin
+          AEndHdrPos := AZip.Size - BufSize + I;
+          AZip.Seek(AEndHdrPos + SizeOf(AEndHdr), soFromBeginning);
+          SetLength(AZipFileComment, AEndHdr.ZipFile_Comment_Length);
+          AZip.ReadBuffer(AZipFileComment[1], Length(AZipFileComment));
+          exit;
+        end;
+      end;
+    end;
+
+    AEndHdrPos := -1;
+    FillChar(AEndHdr, SizeOf(AEndHdr), 0);
+  finally
+    FreeMem(Buf);
+  end;
+end;
 
 Procedure TUnZipper.ReadZipDirectory;
 
 Var
   i,
   EndHdrPos,
-  CenDirPos : LongInt;
+  CenDirPos : Int64;
   NewNode   : TFullZipFileEntry;
   D : TDateTime;
   S : String;
 Begin
-  EndHdrPos:=FZipStream.Size-SizeOf(EndHdr);
+  FindEndHeader(FZipStream, EndHdr, EndHdrPos, FFileComment);
   if EndHdrPos < 0 then
     raise EZipError.CreateFmt(SErrCorruptZIP,[FileName]);
-  FZipStream.Seek(EndHdrPos,soFromBeginning);
-  FZipStream.ReadBuffer(EndHdr, SizeOf(EndHdr));
-{$IFDEF FPC_BIG_ENDIAN}
-  EndHdr := SwapECD(EndHdr);
-{$ENDIF}
-  With EndHdr do
-    begin
-    if Signature <> END_OF_CENTRAL_DIR_SIGNATURE then
-      raise EZipError.CreateFmt(SErrCorruptZIP,[FileName]);
-    CenDirPos:=Start_Disk_Offset;
-    end;
+  CenDirPos := EndHdr.Start_Disk_Offset;
   FZipStream.Seek(CenDirPos,soFrombeginning);
   FEntries.Clear;
   for i:=0 to EndHdr.Entries_This_Disk-1 do