unit Main;
interface
uses
SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, LCLType;
type
{ TfrmMain }
TfrmMain = class(TForm)
eSrcFile: TEdit;
eDestFile: TEdit;
bCopy: TButton;
iProgress: TImage;
Label1: TLabel;
Label2: TLabel;
bSrc: TButton;
bDest: TButton;
Label5: TLabel;
odSrc: TOpenDialog;
sdDest: TSaveDialog;
bCancel: TButton;
mReport: TMemo;
procedure bCopyClick(Sender: TObject);
procedure bSrcClick(Sender: TObject);
procedure bDestClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure bCancelClick(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure FormResize(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure iProgressPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
PZone = ^TZone;
TZone = record
sStart : integer;
sEnd : integer;
BlkSize : integer;
Error : boolean;
Prev : PZone;
Next : PZone;
Current : boolean;
ErrZone : boolean;
end;
const
MaxBlkSize = 4096;
MinBlkSize = 4096;
var
frmMain: TfrmMain;
Cancel: boolean;
CurZone: PZone;
ErrZones: PZone;
fSize: integer;
InfoFile: string;
IniFile: string;
OldTime: double;
LockCopy: boolean;
CancelErrors: boolean;
implementation
{$R *.lfm}
function IntToStrEx(Value: integer): string;
var
str : string;
n : integer;
begin
str:=IntToStr(Value);
Result:=''; n:=0;
while length(str)>0 do begin
Result := str[length(str)]+Result;
str := copy(str, 1, length(str)-1);
Inc(n);
if (length(str)>0) and (n=3) then begin
n:=0;
Result := Chr(39)+Result;
end;
end;
end;
function UpInt(Value: Extended): integer;
begin
Result := trunc(Value);
if Result<Value then Inc(Result);
end;
procedure SaveIni;
var
f : textfile;
begin
AssignFile(f, IniFile); ReWrite(f);
WriteLn(f, frmMain.eSrcFile.Text);
WriteLn(f, frmMain.eDestFile.Text);
Write(f, frmMain.mReport.Lines.Text);
CloseFile(f);
end;
procedure LoadIni;
var
f : textfile;
str : string;
begin
if FileExists(IniFile) then begin
AssignFile(f, IniFile); Reset(f);
ReadLn(f, str);
frmMain.eSrcFile.Text := str;
ReadLn(f, str);
frmMain.eDestFile.Text := str;
while not eof(f) do begin
ReadLn(f, str);
frmMain.mReport.Lines.Add(str);
end;
CloseFile(f);
end;
end;
procedure AddZoneBefore(Zone, ZoneListItem: PZone);
begin
Zone.Next := ZoneListItem;
if Zone.Next<>nil then begin
Zone.Prev := Zone.Next.Prev;
Zone.Next.Prev := Zone;
if Zone.Prev<>nil then Zone.Prev.Next:=Zone;
end else Zone.Prev := nil;
end;
procedure AddZoneAfter(Zone, ZoneListItem: PZone);
begin
Zone.Prev := ZoneListItem;
if Zone.Prev<>nil then begin
Zone.Next := Zone.Prev.Next;
Zone.Prev.Next := Zone;
if Zone.Next<>nil then Zone.Next.Prev:=Zone;
end else Zone.Next := nil;
end;
procedure RemoveZone(Zone: PZone);
begin
if Zone.Prev<>nil then Zone.Prev.Next:=Zone.Next;
if Zone.Next<>nil then Zone.Next.Prev:=Zone.Prev;
end;
function FindFirstZone(Zone: PZone): PZone;
begin
Result := Zone;
if Result<>nil then while Result.Prev<>nil do Result:=Result.Prev;
end;
procedure DisposeZoneList(var Zone: PZone);
var
z: PZone;
begin
Zone := FindFirstZone(Zone);
while Zone<>nil do begin
z := Zone.Next;
Dispose(Zone);
Zone := z;
end;
end;
function FindMaxZone(Zone: PZone): PZone;
begin
Result := FindFirstZone(Zone);
Zone := Result;
while Zone<>nil do begin
if ((not Zone.Error) and Result.Error)
or ((Zone.Error=Result.Error) and ((Zone.sEnd-Zone.sStart)>(Result.sEnd-Result.sStart))) then
Result := Zone;
Zone := Zone.Next;
end;
end;
procedure MoveToError(Zone: PZone);
var
z : PZone;
begin
RemoveZone(Zone);
z:=ErrZones;
while (z<>nil) and (z.next<>nil) and (z.sEnd<Zone.sStart) do z:=z.Next;
if (z<>nil) and (z.sEnd<=Zone.sStart) then AddZoneAfter(Zone, z) else AddZoneBefore(Zone, z);
if (Zone.Prev<>nil) and (Zone.Prev.sEnd=Zone.sStart) then begin
Zone.sStart:=Zone.Prev.sStart;
z := Zone.Prev;
RemoveZone(z);
Dispose(z);
end;
if (Zone.Next<>nil) and (Zone.Next.sStart=Zone.sEnd) then begin
Zone.sEnd:=Zone.Next.sEnd;
z := Zone.Next;
RemoveZone(z);
Dispose(z);
end;
ErrZones := FindFirstZone(Zone);
end;
procedure GenerateReport;
var
z : PZone;
str : string;
Text, ns : string;
ncp, dmg : integer;
begin
if fSize=0 then exit;
z := FindFirstZone(CurZone);
ncp := 0;
ns := Chr(13);
Text := 'Remained Blocks:'+ns;
while z<>nil do begin
str := IntToHex(z.sStart, 8)+':'+IntToHex(z.sEnd, 8)+' - ';
if z.Error then str:=str+'Read Error' else str:=str+'Copying';
Text := Text+str+ns;
ncp := ncp+z.sEnd-z.sStart;
z:=z.Next;
end;
z := ErrZones;
dmg := 0;
Text := Text+'Damaged Blocks:'+ns;
while z<>nil do begin
str := IntToHex(z.sStart, 8)+':'+IntToHex(z.sEnd, 8);
Text := Text+str+ns;
dmg := dmg+z.sEnd-z.sStart;
z:=z.Next;
end;
str := 'Damaged: '+IntToStrEx(dmg)+' ('+FloatToStr(round((dmg)/fSize*10000)*0.01)+'%)';
Text := str+ns+Text;
str := 'Remained: '+IntToStrEx(ncp)+' ('+FloatToStr(round((ncp)/fSize*10000)*0.01)+'%)';
Text := str+ns+Text;
str := 'Copied: '+IntToStrEx(fSize-ncp-dmg)+' ('+FloatToStr(round((fSize-ncp-dmg)/fSize*10000)*0.01)+'%)';
Text := str+ns+Text;
str := 'File Size: '+IntToStrEx(fSize);
Text := str+ns+Text;
frmMain.mReport.Lines.Text := Text;
end;
procedure SaveCopyInfo;
var
f : file of TZone;
z : PZone;
begin
if InfoFile='' then exit;
if (CurZone<>nil) or (ErrZones<>nil) then begin
AssignFile(f, InfoFile);
ReWrite(f);
z := FindFirstZone(CurZone);
while z<>nil do begin
z.Current := (z=CurZone);
z.ErrZone := (z=ErrZones);
write(f, z^);
z:=z.Next;
end;
z := ErrZones;
while z<>nil do begin
z.Current := (z=CurZone);
z.ErrZone := (z=ErrZones);
write(f, z^);
z:=z.Next;
end;
CloseFile(f);
end else DeleteFile(InfoFile);
end;
procedure LoadCopyInfo;
var
f : file of TZone;
z, z2 : PZone;
asked : boolean;
begin
DisposeZoneList(CurZone);
DisposeZoneList(ErrZones);
asked:=false;
if FileExists(InfoFile) then begin
AssignFile(f, InfoFile);
Reset(f);
z2 := nil;
while not eof(f) do begin
New(z);
read(f, z^);
if (not asked) and (z.Error or z.ErrZone or (z.BlkSize<>MaxBlkSize)) then begin
CancelErrors := mrYes=MessageDlg('Reread error blocks?', mtConfirmation, [mbYes, mbNo], 0);
asked := true;
end;
if CancelErrors then begin
z.Error := False;
z.BlkSize := MaxBlkSize;
z.ErrZone := False;
end;
if z.Current then CurZone:=z;
if z.ErrZone then begin
ErrZones:=z;
z2:=nil;
end;
AddZoneAfter(z, z2);
z2:=z;
end;
if CancelErrors then CurZone:=FindMaxZone(z2);
CloseFile(f);
end else begin
New(CurZone);
CurZone.sStart := 0;
CurZone.sEnd := fSize;
CurZone.BlkSize := MaxBlkSize;
CurZone.Error := False;
CurZone.Prev := nil;
CurZone.Next := nil;
end;
end;
procedure DrawZones;
var
z : PZone;
s : integer;
r, r2 : TRect;
str : string;
bmp : TBitmap;
begin
with frmMain.iProgress do begin
r.Top:=1; r.Left:=1; r.Right:=Width-1; r.Bottom:=Height-1;
Canvas.Brush.Color := clBlue;
if fSize=0 then Canvas.Brush.Color := clWhite;
Canvas.FillRect(r);
Canvas.Brush.Color := clWhite;
z := FindFirstZone(CurZone);
s := 0;
while z<>nil do begin
r.Left := 1+trunc(z.sStart/fSize*(Width-2));
r.Right := 1+trunc(z.sEnd/fSize*(Width-2));
s := s+z.sEnd-z.sStart;
Canvas.FillRect(r);
z:=z.Next;
end;
Canvas.Brush.Color := clRed;
z := ErrZones;
while z<>nil do begin
r.Left := 1+trunc(z.sStart/fSize*(Width-2));
r.Right := r.Left+UpInt((z.sEnd-z.sStart)/fSize*(Width-2));
Canvas.FillRect(r);
z:=z.Next;
end;
Canvas.Brush.Color := clRed;
z := FindFirstZone(CurZone);
while z<>nil do begin
if z.Error then begin
r.Left := 1+trunc(z.sStart/fSize*(Width-2));
r.Right := r.Left+1;
Canvas.FillRect(r);
end;
z:=z.Next;
end;
if fSize<>0 then begin
bmp := TBitmap.Create;
str:=FloatToStr(round((1-s/fSize)*1000)*0.1)+'%';
bmp.Width := bmp.Canvas.TextWidth(str);
bmp.Height := bmp.Canvas.TextHeight(str);
bmp.Canvas.Brush.Color := clBlack;
bmp.Canvas.Font.Color := clWhite;
bmp.Canvas.TextOut(0, 0, str);
r.Left:=trunc((Width-bmp.Width)/2);
r.Top:=trunc((Height-bmp.Height)/2);
r.Right:=r.Left+bmp.Width;
r.Bottom:=r.Top+bmp.Height;
r2.Left:=0; r2.Top:=0; r2.Right:=bmp.Width; r2.Bottom:=bmp.Height;
Canvas.CopyMode := cmSrcInvert;
Canvas.CopyRect(r, bmp.Canvas, r2);
bmp.Free;
end;
Canvas.Brush.Style := bsClear;
Canvas.Pen.Color := clBlack;
r.Top:=0; r.Left:=0; r.Right:=Width; r.Bottom:=Height;
Canvas.Rectangle(r);
Canvas.Brush.Style := bsSolid;
end;
SaveCopyInfo;
GenerateReport;
LockCopy:=True;
Application.ProcessMessages;
LockCopy:=False;
OldTime:=Time*24*3600;
end;
procedure CopyFile(Src, Dest: string);
var
SrcFile : file;
DestFile : file;
e : integer;
Res, IORes : integer;
z : PZone;
Buffer : array[0..MaxBlkSize] of byte;
DlgRes : Word;
rewr : boolean;
begin
InfoFile := Dest+'.copyinfo';
if not FileExists(Src) then begin
ShowMessage('Source file not found');
exit;
end;
rewr:=true;
if FileExists(Dest) then begin
if FileExists(InfoFile) then begin
DlgRes := MessageDlg('Do you want to continue last copying of this file?', mtConfirmation, mbYesNoCancel, 0);
if DlgRes=mrNo then DeleteFile(InfoFile) else
if DlgRes=mrYes then rewr:=false else exit;
end else begin
DlgRes := MessageDlg('Destination file already exists. Do you want to overwrite it?', mtConfirmation, mbOkCancel, 0);
if DlgRes<>mrOk then exit;
end;
end;
AssignFile(SrcFile, Src);
AssignFile(DestFile, Dest);
FileMode := fmOpenRead;
Reset(SrcFile, 1);
fSize:=FileSize(SrcFile);
FileMode := fmOpenReadWrite;
if rewr then ReWrite(DestFile, 1) else Reset(DestFile, 1);
LoadCopyInfo;
{$IOCHECKS OFF}
while CurZone<>nil do begin
if Cancel then break;
if CurZone.Error then CurZone:=FindMaxZone(CurZone);
if CurZone.Error then begin
e := (trunc((CurZone.sStart+CurZone.sEnd)/2) div MinBlkSize)*MinBlkSize;
if e<=CurZone.sStart then e:=e+MinBlkSize;
if e>=CurZone.sEnd then begin
if CurZone.Prev<>nil then z:=CurZone.Prev else z:=CurZone.Next;
MoveToError(CurZone);
CurZone:=FindMaxZone(z);
end else begin
New(z);
z.BlkSize := MaxBlkSize;
z.sStart := e;
z.sEnd := CurZone.sEnd;
z.Error := False;
CurZone.sEnd := e;
AddZoneAfter(z, CurZone);
if CurZone.sEnd-CurZone.sStart<=MinBlkSize then MoveToError(CurZone);
CurZone := FindMaxZone(z);
end;
DrawZones;
end else begin
if CurZone.sEnd-CurZone.sStart>0 then begin
Seek(SrcFile, CurZone.sStart);
if CurZone.sEnd-CurZone.sStart<CurZone.BlkSize then CurZone.BlkSize:=CurZone.sEnd-CurZone.sStart;
BlockRead(SrcFile, Buffer, CurZone.BlkSize, Res);
IORes := IOResult;
if IORes<>0 then Res:=0;
if Res=0 then begin
if CurZone.BlkSize<=MinBlkSize then CurZone.Error:=True else CurZone.BlkSize:=MinBlkSize;
end else begin
Seek(DestFile, CurZone.sStart);
BlockWrite(DestFile, Buffer, Res);
end;
CurZone.sStart:=CurZone.sStart+Res;
if Time*24*3600-OldTime>=0.05 then DrawZones;
end;
if CurZone.sEnd-CurZone.sStart<=0 then begin
if CurZone.Prev<>nil then z:=CurZone.Prev else z:=CurZone.Next;
RemoveZone(CurZone);
Dispose(CurZone);
CurZone := FindMaxZone(z);
DrawZones;
end;
end;
end;
{$IOCHECKS ON}
CloseFile(SrcFile);
CloseFile(DestFile);
DrawZones;
DisposeZoneList(CurZone);
DisposeZoneList(ErrZones);
if not Cancel then beep;
end;
procedure ClearVars;
begin
Cancel:=False;
CurZone:=nil;
ErrZones:=nil;
LockCopy:=False;
CancelErrors:=False;
InfoFile:='';
fSize:=0;
end;
procedure TfrmMain.bCopyClick(Sender: TObject);
begin
if not LockCopy then begin
ClearVars;
SaveIni;
CopyFile(eSrcFile.Text, eDestFile.Text);
ClearVars;
DrawZones;
SaveIni;
end;
end;
procedure TfrmMain.bSrcClick(Sender: TObject);
begin
odSrc.FileName:=eSrcFile.Text;
if odSrc.Execute then eSrcFile.Text:=odSrc.FileName;
end;
procedure TfrmMain.bDestClick(Sender: TObject);
var
n: integer;
path, fname: string;
begin
n:=Length(eSrcFile.Text);
while n>0 do
if eSrcFile.Text[n]='\' then break else Dec(n);
fname:=copy(eSrcFile.Text, n+1, Length(eSrcFile.Text)-n);
n:=Length(eDestFile.Text);
while n>0 do
if eDestFile.Text[n]='\' then break else Dec(n);
path:=copy(eDestFile.Text, 1, n);
sdDest.FileName:=path+fname;
if sdDest.Execute then eDestFile.Text:=sdDest.FileName;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
ClearVars;
DrawZones;
IniFile := copy(Application.ExeName, 1, length(Application.ExeName)-3)+'ini';
LoadIni;
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
SaveIni;
end;
procedure TfrmMain.bCancelClick(Sender: TObject);
begin
Cancel:=true;
end;
procedure TfrmMain.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
// if (Key=VK_F2) and (CurZone<>nil) then CurZone.Error:=true;
if Key=VK_ESCAPE then Cancel:=true;
end;
procedure TfrmMain.FormResize(Sender: TObject);
begin
DrawZones;
end;
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Cancel := true;
end;
procedure TfrmMain.iProgressPaint(Sender: TObject);
begin
DrawZones;
end;
end.