265 lines
6 KiB
ObjectPascal
265 lines
6 KiB
ObjectPascal
unit CheckSum;
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, Forms;
|
|
|
|
type
|
|
TChecksumCRC32 = class
|
|
public
|
|
constructor Create;
|
|
procedure Reset;
|
|
procedure Provide(Data: PByte; Length: Integer);
|
|
function Checksum: Integer;
|
|
private
|
|
FCrc: Integer;
|
|
FCrcTable: array[0..255] of Integer;
|
|
end;
|
|
|
|
TFile = class
|
|
public
|
|
FileName: String;
|
|
Missing: Boolean;
|
|
FileSize: Integer;
|
|
Crc: Integer;
|
|
end;
|
|
|
|
TChecksums = class
|
|
public
|
|
constructor Create(const PathName: String);
|
|
destructor Destroy; override;
|
|
|
|
procedure Scan;
|
|
procedure Load;
|
|
procedure Save;
|
|
|
|
procedure Compare(BaseLine: TChecksums; Log: TStrings);
|
|
private
|
|
FFiles: TStringList;
|
|
FCrc: TChecksumCRC32;
|
|
FPathName: String;
|
|
|
|
procedure Process(const FileName: String);
|
|
procedure InnerScan(const PathName: String);
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses SysUtils, StrUtils;
|
|
|
|
constructor TChecksumCRC32.Create;
|
|
var
|
|
c, n, k: Integer;
|
|
begin
|
|
Reset;
|
|
for n := 0 to 255 do begin
|
|
c := n;
|
|
for k := 0 to 7 do begin
|
|
if c and 1 = 1 then
|
|
c := Integer($edb88320) xor Integer(Cardinal(c) shr 1)
|
|
else
|
|
c := (c shr 1);
|
|
end;
|
|
FCrcTable[n] := c;
|
|
end;
|
|
end;
|
|
|
|
procedure TChecksumCRC32.Reset;
|
|
begin
|
|
FCrc := Integer($ffffffff);
|
|
end;
|
|
|
|
procedure TChecksumCRC32.Provide(Data: PByte; Length: Integer);
|
|
var
|
|
c, n: Integer;
|
|
begin
|
|
c := FCrc;
|
|
for n := 0 to Length-1 do
|
|
c := FCrcTable[(c xor PByte(Integer(Data) + n)^) and $ff] xor Integer(Cardinal(c) shr 8);
|
|
FCrc := c;
|
|
end;
|
|
|
|
function TChecksumCRC32.Checksum: Integer;
|
|
begin
|
|
Result := FCrc xor Integer($ffffffff);
|
|
end;
|
|
|
|
constructor TChecksums.Create(const PathName: String);
|
|
begin
|
|
FCrc := TChecksumCRC32.Create;
|
|
FFiles := TStringList.Create;
|
|
FFiles.Sorted := True;
|
|
FFiles.Duplicates := dupIgnore;
|
|
FFiles.CaseSensitive := False;
|
|
FPathName := IncludeTrailingPathDelimiter(PathName);
|
|
end;
|
|
|
|
destructor TChecksums.Destroy;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to FFiles.Count -1 do
|
|
FFiles.Objects[I].Free;
|
|
FreeAndNil(FFiles);
|
|
FreeAndNil(FCrc);
|
|
end;
|
|
|
|
function FileSize(fileName : wideString) : Int64;
|
|
var
|
|
Sr : TSearchRec;
|
|
begin
|
|
Result := 0;
|
|
if FindFirst(fileName, faAnyFile, Sr) = 0 then
|
|
Result := Sr.FindData.nFileSizeLow;
|
|
FindClose(Sr) ;
|
|
end;
|
|
|
|
procedure TChecksums.Scan;
|
|
begin
|
|
InnerScan(FPathName);
|
|
end;
|
|
|
|
procedure TChecksums.InnerScan(const PathName: String);
|
|
var
|
|
Sr : TSearchRec;
|
|
begin
|
|
Application.ProcessMessages;
|
|
if FindFirst(PathName + '*', faAnyFile, Sr) = 0 then begin
|
|
try
|
|
repeat
|
|
if not AnsiStartsStr('.', Sr.Name) and not AnsiEndsText('.checksum', Sr.Name) then begin
|
|
if Sr.Attr and faDirectory <> 0 then begin
|
|
InnerScan(IncludeTrailingPathDelimiter(PathName + Sr.Name));
|
|
end else begin
|
|
Process(PathName + Sr.Name);
|
|
end;
|
|
end;
|
|
until FindNext(Sr) <> 0;
|
|
finally
|
|
FindClose(Sr);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TChecksums.Process(const FileName: String);
|
|
var
|
|
FS: TFileStream;
|
|
Len: Integer;
|
|
Buffer: array[0..4095] of Byte;
|
|
Entry: TFile;
|
|
begin
|
|
Entry := TFile.Create;
|
|
Entry.FileName := AnsiLowerCase(FileName);
|
|
Entry.Missing := not FileExists(Filename);
|
|
|
|
Entry.FileSize := 0;
|
|
Entry.Crc := 0;
|
|
|
|
if not Entry.Missing then begin
|
|
Entry.FileSize := FileSize(FileName);
|
|
try
|
|
FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
|
|
try
|
|
FCrc.Reset;
|
|
while FS.Position <> FS.Size do begin
|
|
Len := FS.Read(Buffer[0], 4096);
|
|
FCrc.Provide(@Buffer[0], Len);
|
|
end;
|
|
Entry.Crc := FCrc.Checksum;
|
|
finally
|
|
FreeAndNil(FS);
|
|
end;
|
|
except
|
|
end;
|
|
end;
|
|
FFiles.AddObject(Entry.FileName, Entry);
|
|
end;
|
|
|
|
procedure Split(const S: String; Parts: TStringList);
|
|
begin
|
|
Parts.Clear;
|
|
ExtractStrings(['|'], [], PChar(S), Parts);
|
|
end;
|
|
|
|
procedure TChecksums.Load;
|
|
var
|
|
Data: TStringList;
|
|
Parts: TStringList;
|
|
I : Integer;
|
|
Entry: TFile;
|
|
begin
|
|
if not FileExists(FPathName+'sb.checksum') then
|
|
Exit;
|
|
Data := TStringList.Create;
|
|
Parts := TStringList.Create;
|
|
Data.LoadFromFile(FPathName+'sb.checksum');
|
|
for I := 0 to Data.Count -1 do begin
|
|
Split(Data.Strings[I], Parts);
|
|
if Parts.Count <> 3 then
|
|
raise Exception.Create('Checksum file parse error');
|
|
Entry := TFile.Create;
|
|
Entry.FileName := FPathName + Parts[0];
|
|
Entry.Missing := False;
|
|
Entry.FileSize := StrToInt(Parts[1]);
|
|
Entry.Crc := StrToInt(Parts[2]);
|
|
FFiles.AddObject(Entry.FileName, Entry);
|
|
end;
|
|
Parts.Free;
|
|
Data.Free;
|
|
end;
|
|
|
|
procedure TChecksums.Save;
|
|
var
|
|
Data: TStringList;
|
|
I : Integer;
|
|
Entry: TFile;
|
|
PathName: String;
|
|
begin
|
|
PathName := AnsiLowerCase(FPathName);
|
|
Data := TStringList.Create;
|
|
for I := 0 to FFiles.Count -1 do begin
|
|
Entry := TFile(FFiles.Objects[I]);
|
|
if not Entry.Missing then
|
|
Data.Append(AnsiReplaceStr(Entry.FileName, PathName, '') + '|' + IntToStr(Entry.FileSize) + '|0x' + IntToHex(Entry.Crc, 8));
|
|
end;
|
|
Data.SaveToFile(FPathName+'sb.checksum');
|
|
Data.Free;
|
|
end;
|
|
|
|
procedure TChecksums.Compare(BaseLine: TChecksums; Log: TStrings);
|
|
var
|
|
I: Integer;
|
|
L: TStringList;
|
|
Base: TFile;
|
|
Local: TFile;
|
|
A: Integer;
|
|
begin
|
|
L := TStringList.Create;
|
|
L.Sorted := True;
|
|
L.Duplicates := dupIgnore;
|
|
L.CaseSensitive := False;
|
|
for I := 0 to FFiles.Count-1 do
|
|
L.Add(FFiles.Strings[I]);
|
|
for I := 0 to BaseLine.FFiles.Count-1 do
|
|
L.Add(BaseLine.FFiles.Strings[I]);
|
|
for I := 0 to L.Count-1 do begin
|
|
Base := nil;
|
|
Local := nil;
|
|
if FFiles.Find(L[I], A) then
|
|
Local := TFile(FFiles.Objects[A]);
|
|
if BaseLine.FFiles.Find(L[I], A) then
|
|
Base := TFile(BaseLine.FFiles.Objects[A]);
|
|
|
|
if Local = nil then
|
|
Log.Append('File ' + L[I] + ' is missing.')
|
|
else if Base = nil then
|
|
Log.Append('File ' + L[I] + ' is not part of the baseline.')
|
|
else if Base.FileSize <> Local.FileSize then
|
|
Log.Append('File ' + L[I] + ' is the wrong size, expected ' +IntToStr(Base.FileSize) + ' but found ' + IntToStr(Local.FileSize) + '.')
|
|
else if Base.Crc <> Local.Crc then
|
|
Log.Append('File ' + L[I] + ' did not match checksum.');
|
|
end;
|
|
end;
|
|
|
|
end.
|