Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 181 additions & 2 deletions System.Zip.TestCase.pas
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@

interface

uses TestFramework, System.Zip2;
uses System.Classes, System.SysUtils, TestFramework, System.Zip2;

type
TTestCase_Zip = class(TTestCase)
private
class function ReadFile (const APath : string) : TBytes; static;
class procedure ReadBuffer (AStream : TStream;
var Buffer : TBytes;
Offset, Count : Int64); static;
function CreateFile(const AExtension: string): string;
protected
function GetCompression: TZipCompression; virtual; abstract;
published
procedure TestCase_Zip_File;
{$IFDEF WIN64}
procedure TestCase_BigZip_File;
{$ENDIF}
procedure TestCase_MultipleFile;
end;

TTestCase_Zip_zcStored = class(TTestCase_Zip)
Expand All @@ -29,7 +39,176 @@ TTestCase_Zip_zcLZMA = class(TTestCase_Zip)

implementation

uses Classes, System.SysUtils, System.IOUtils, System.Zip.LZMA;
uses System.IOUtils, System.RTLConsts, System.Types, System.Zip.LZMA;

function TTestCase_Zip.CreateFile(const AExtension: string): string;
var
LCheminTemp : string;
BEGIN
LCheminTemp := TPath.GetTempFileName;
try
Result := TPath.ChangeExtension(LCheminTemp, AExtension);
finally
TFile.Delete(LCheminTemp);
end;
end;

class procedure TTestCase_Zip.ReadBuffer(AStream : TStream;
var Buffer : TBytes;
Offset, Count : Int64);
var
LTotalCount,
LReadCount : NativeInt;
begin
{ Perform a read directly. Most of the time this will succeed
without the need to go into the WHILE loop. }
LTotalCount := AStream.Read64(Buffer, Offset, Count);
{ Check if there was an error }
if LTotalCount < 0 then
raise EReadError.CreateRes(@SReadError);

while (LTotalCount < Count) DO
begin
{ Try to read a contiguous block of <Count> size }
LReadCount := AStream.Read64(Buffer, Offset + LTotalCount, (Count - LTotalCount));
{ Check if we read something and decrease the number of bytes left to read }
if LReadCount <= 0 then
raise EReadError.CreateRes(@SReadError)
else
Inc(LTotalCount, LReadCount);
end;
end;

class function TTestCase_Zip.ReadFile(const APath: string): TBytes;
var
LFileStream : TFileStream;
begin
LFileStream := NIL;
try
LFileStream := TFileStream.Create(APath, fmOpenRead or fmShareDenyWrite);
SetLength(Result, LFileStream.Size);

ReadBuffer(LFileStream, Result, 0, Length(Result));
finally
LFileStream.Free;
end;
end;

{$IFDEF WIN64}
procedure TTestCase_Zip.TestCase_BigZip_File;
var
LCheminZip,
LCheminFichier : string;
LZipFile : TZipFile;
LOutput : TBytes;
LFileStream : TFileStream;
LLongueurVoulue,
LLongueur : Int64;
BEGIN
LCheminZip := CreateFile ('.zip');
LCheminFichier := CreateFile ('.bin');

LFileStream := NIL;
try
LFileStream := TFile.Create (LCheminFichier);
LLongueurVoulue := $100000000;
LFileStream.Size := LLongueurVoulue;
finally
LFileStream.Free;
end;

// Compress File
LZipFile := TZipFile.Create;
LZipFile.Open(LCheminZip, zmWrite);
try
try
LZipFile.Add(LCheminFichier, '', GetCompression);
finally
LZipFile.Close;
LZipFile.Free;
TFile.Delete(LCheminFichier);
end;

// Decompress File
TZipFile.ExtractZipFile(LCheminZip, TPath.GetTempPath);
finally
TFile.Delete(LCheminZip);
end;

CheckTrue(TFile.Exists(LCheminFichier));

try
LOutput := ReadFile(LCheminFichier);
LLongueur := Length(LOutput);
CheckEquals(LLongueurVoulue, LLongueur);
finally
TFile.Delete(LCheminFichier);
end;
end;
{$ENDIF}

procedure TTestCase_Zip.TestCase_MultipleFile;
const
C_NbFichier : Integer = 65536;
var
LCheminZip,
LCheminTemp,
LNomFichier,
LCheminFichier : string;
LI : Integer;
LZipFile : TZipFile;
LInput, LOutput : TBytes;
LFiles : TStringDynArray;
BEGIN
LCheminZip := CreateFile ('.zip');
LCheminFichier := CreateFile ('.bin');

Randomize;
SetLength(LInput, 10);
for LI := Low(LInput) to High(LInput) do
LInput[LI] := Random(128);

TFile.WriteAllBytes(LCheminFichier, LInput);

// Compress File
LZipFile := TZipFile.Create;
LZipFile.Open(LCheminZip, zmWrite);
try
try
LNomFichier := TPath.GetFileName(LCheminFichier);
for LI := 1 to C_NbFichier do
LZipFile.Add (LCheminFichier,
string.Format('%0:s.%1:d', [LNomFichier, LI]),
GetCompression);
finally
LZipFile.Close;
LZipFile.Free;
TFile.Delete(LCheminFichier);
end;

// Decompress File
LCheminTemp := LCheminFichier.Substring(0, length (LCheminFichier) - 4);
TDirectory.CreateDirectory(LCheminTemp);

try
TZipFile.ExtractZipFile(LCheminZip, LCheminTemp);

LFiles := TDirectory.GetFiles(LCheminTemp);
CheckEquals(C_NbFichier, Length (LFiles));

for LI:= 0 to C_NbFichier - 1 do
begin
LOutput := TFile.ReadAllBytes(LFiles[LI]);
CheckEquals(Length(LInput), Length(LOutput));
CheckTrue(CompareMem(LInput, LOutput, Length(LInput)));
end;
finally
TDirectory.Delete(LCheminTemp, True);
end;
finally
TFile.Delete(LCheminZip);
end;
end;

procedure TTestCase_Zip.TestCase_Zip_File;
var fZip, fData: string;
Expand Down
51 changes: 38 additions & 13 deletions System.Zip2.pas
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ function TZipCompressionToString(Compression: TZipCompression): string;
EXTRAFIELD_ID_NTFS: UInt16 = $000A;

ZIP64 = $FFFFFFFF;
C_MaxFileCount = $FFFF;

type
/// <summary> Final block written to zip file</summary>
Expand Down Expand Up @@ -1015,25 +1016,39 @@ procedure TZipFile.ReadCentralHeader;
LEndHeader: TZipEndOfCentralHeader;
LHeader: TZipHeader;
Z64: TZip64_EndOfCentralDirectory;
LFileCount : UInt64;
LCentralDirOffset : Int64;
begin
FFiles.Clear;
if FStream.Size = 0 then
Exit;
// Read End Of Centeral Direcotry Header
if not LocateEndOfCentralHeader(LEndHeader) then
raise EZipException.CreateRes(@SZipErrorRead);
// Move to the beginning of the CentralDirectory
FStream.Position := LEndHeader.CentralDirOffset;
if LEndHeader.CentralDirOffset = ZIP64 then begin

// Read from End Of Central Directory Header
LCentralDirOffset := LEndHeader.CentralDirOffset;
LFileCount := LEndHeader.CentralDirEntries;

if (LCentralDirOffset = ZIP64) OR
(LFileCount = C_MaxFileCount) then
begin
// On essaye de trouver le EOCD64
if ZIP64_LocateEndOfCentralHeader(Z64) then
FStream.Position := Z64.DirectoryOffset;
begin
// Lecture du EOCD64
LCentralDirOffset := Z64.DirectoryOffset;
LFileCount := Z64.EntriesOnDisk;
end;
end;
// Move to the beginning of the CentralDirectory
FStream.Position := LCentralDirOffset;
// Save Begginning of Central Directory. This is where new files
// get written to, and where the new central directory gets written when
// closing.
FEndFileData := LEndHeader.CentralDirOffset;
FEndFileData := LCentralDirOffset;
// Read File Headers
for I := 0 to LEndHeader.CentralDirEntries - 1 do
for I := 0 to LFileCount - 1 do
begin
// Verify Central Header signature
FStream.Read(Signature, Sizeof(Signature));
Expand Down Expand Up @@ -1432,7 +1447,7 @@ procedure TZipFile.Close;
LEndOfHeader: TZipEndOfCentralHeader;
I: Integer;
Signature: UInt32;
iCentralDirSize: UInt32;
iCentralDirSize: UInt64;
Z64_End: TZip64_EndOfCentralDirectory;
Z64_EndLocator: TZip64_EndOfCentralDirectoryLocator;
bIsZIP64: Boolean;
Expand All @@ -1441,7 +1456,7 @@ procedure TZipFile.Close;
// Only need to write Central Directory and End Of Central Directory if writing
if (FMode = zmReadWrite) or (FMode = zmWrite) then
begin
bIsZIP64 := False;
bIsZIP64 := (FFiles.Count > C_MaxFileCount);
FStream.Position := FEndFileData;
Signature := SIGNATURE_CENTRALHEADER;
// Write File Signatures
Expand Down Expand Up @@ -1501,12 +1516,22 @@ procedure TZipFile.Close;

// Only support writing single disk .ZIP files
FillChar(LEndOfHeader, Sizeof(LEndOfHeader), 0);
LEndOfHeader.CentralDirEntries := FFiles.Count;
LEndOfHeader.NumEntriesThisDisk := FFiles.Count;
LEndOfHeader.CentralDirSize := iCentralDirSize;
LEndOfHeader.CentralDirOffset := FEndFileData;
if FFiles.Count > C_MaxFileCount then
LEndOfHeader.CentralDirEntries := C_MaxFileCount
else
LEndOfHeader.CentralDirEntries := FFiles.Count;
if FFiles.Count > C_MaxFileCount then
LEndOfHeader.NumEntriesThisDisk := C_MaxFileCount
else
LEndOfHeader.NumEntriesThisDisk := FFiles.Count;
IF iCentralDirSize > $FFFFFFFF then
LEndOfHeader.CentralDirSize := $FFFFFFFF
else
LEndOfHeader.CentralDirSize := iCentralDirSize;
if FEndFileData > ZIP64 then
LEndOfHeader.CentralDirOffset := ZIP64;
LEndOfHeader.CentralDirOffset := ZIP64
else
LEndOfHeader.CentralDirOffset := FEndFileData;
// Truncate comment if it's too long
if Length(FComment) > $FFFF then
SetLength(FComment, $FFFF);
Expand Down