(* Tux Commander - UChecksumDruid - Checksum calculation druid Copyright (C) 2004 Tomas Bzatek Check for updates on tuxcmd.sourceforge.net This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) unit UChecksumDruid; interface uses glib2, gdk2, gtk2, SysUtils, Types, Classes, Variants, GTKControls, GTKForms, GTKStdCtrls, GTKExtCtrls, GTKConsts, GTKView, GTKText, GTKUtils, UEngines; type TFChecksumDruid = class(TGTKDialog) CancelButton, BackButton, ForwardButton: TGTKButton; ButtonBox: TGTKHButtonBox; TitleLabel, Page1Label, Page4Label, FilenameLabel, Page6Label, Page7Label, ProcessingLabel, Page6Spacer, ErrorLabel: TGTKLabel; TitleLabelBox, PageAreaColor, LeftSpacer, Page2Color, Page3Color, Page4Color, Page5Color, Page6Color, LeftSpacer7: TGTKEventBox; Page1, FileNameHBox, SeparateFileCheckHBox, Page7, ProgressHBox, ErrorTextViewHBox: TGTKHBox; Page2, Page3, Page4, Page5, Page6: TGTKEventBox; DruidArea, PageArea, Page2ClientArea, Page3ClientArea, Page5ClientArea, Page6ClientArea: TGTKVBox; FileNameEntry: TGTKEntry; SeparateFileCheckBox: TGTKCheckButton; SFVRadioButton, MD5RadioButton: TGTKRadioButton; Progress: TGTKProgressBar; ErrorLabelScrolledWindow: TGTKScrolledWindow; procedure FormCreate(Sender: TObject); override; procedure FormDestroy(Sender: TObject); procedure FormResponse(Sender: TObject; const ResponseID: integer); procedure CancelButtonClick(Sender: TObject); procedure BackButtonClick(Sender: TObject); procedure ForwardButtonClick(Sender: TObject); procedure FileNameEntryChanged(Sender: TObject); procedure SeparateFileCheckBoxToggled(Sender: TObject); procedure FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); private ColorFG, ColorBG, ColorGray: PGdkColor; CurrentPage: integer; Processing, Stop, StopBreak: boolean; Buffer: Pointer; FileDes: TEngineFileDes; BufferPos: integer; procedure CreatePages; procedure SetCurrentPage(const Num: integer; const GoingForward: boolean); procedure Process; function ProcessFile(const FName: string; const IsItSFV: boolean; var HashString: string): boolean; procedure CheckStop; procedure WriteLine(const FName, CheckedFName, HashString: string; const IsItSFV, CreateFile, CloseFile: boolean); procedure WriteSFVComment(const FName: string); public Engine: TPanelEngine; FileNames: TStringList; DirName: string; end; const NumPages = 7; var FChecksumDruid: TFChecksumDruid; implementation uses ULocale, UCoreUtils, Libc, UCore, DateUtils, UConfig, StrUtils; procedure TFChecksumDruid.FormCreate(Sender: TObject); begin CurrentPage := 0; Processing := False; Stop := False; StopBreak := False; Buffer := nil; BufferPos := 0; SetDefaultSize(400, 210); Caption := LANGCreateChecksumsCaption; OnDestroy := FormDestroy; OnResponse := FormResponse; FileNames := TStringList.Create; ShowSeparator := False; Buttons := []; ActionArea.SetSizeRequest(0, 0); DruidArea := TGTKVBox.Create(Self); DruidArea.BorderWidth := 2; ClientArea.AddControlEx(DruidArea, True, True, 0); CancelButton := TGTKButton.CreateFromStock(Self, 'gtk-cancel'); CancelButton.OnClick := CancelButtonClick; BackButton := TGTKButton.CreateFromStock(Self, 'gtk-go-back'); BackButton.OnClick := BackButtonClick; ForwardButton := TGTKButton.CreateFromStock(Self, 'gtk-go-forward'); ForwardButton.OnClick := ForwardButtonClick; ButtonBox := TGTKHButtonBox.Create(Self); ButtonBox.Layout := blEnd; ButtonBox.Spacing := 10; ButtonBox.BorderWidth := 5; ButtonBox.AddControlEx(CancelButton, False, False, 0); ButtonBox.AddControlEx(BackButton, False, False, 0); ButtonBox.AddControlEx(ForwardButton, False, False, 0); ClientArea.AddControlEndEx(ButtonBox, False, False, 0); ColorFG := GetDefaultForegroundColor(3); ColorBG := GetDefaultBackgroundColor(3); ColorGray := GetDefaultBackgroundColor(2); TitleLabel := TGTKLabel.Create(Self); TitleLabelBox := TGTKEventBox.Create(Self); TitleLabelBox.AddControl(TitleLabel); TitleLabelBox.BorderWidth := 0; TitleLabel.SetAlignment(0, 0.5); TitleLabel.SetPadding(5, 5); TitleLabel.SetForegroundColor(ColorFG); TitleLabelBox.SetBackgroundColor(ColorBG); DruidArea.AddControlEx(TitleLabelBox, False, False, 0); PageAreaColor := TGTKEventBox.Create(Self); PageAreaColor.SetBackgroundColor(ColorGray); PageArea := TGTKVBox.Create(Self); PageAreaColor.AddControl(PageArea); DruidArea.AddControlEx(PageAreaColor, True, True, 0); CreatePages; SetCurrentPage(1, True); OnKeyDown := FormKeyDown; end; procedure TFChecksumDruid.FormDestroy(Sender: TObject); begin FileNames.Free; end; procedure TFChecksumDruid.FormResponse(Sender: TObject; const ResponseID: integer); begin Stop := True; end; procedure TFChecksumDruid.FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); begin // if Key = GDK_ESCAPE then ModalResult := mbCancel; if (Key = GDK_RETURN) or (Key = GDK_KP_ENTER) then ForwardButtonClick(Sender); end; procedure TFChecksumDruid.CreatePages; begin Page1 := TGTKHBox.Create(Self); Page1.Homogeneous := False; LeftSpacer := TGTKEventBox.Create(Self); LeftSpacer.SetBackgroundColor(ColorBG); LeftSpacer.SetSizeRequest(100, -1); Page1Label := TGTKLabel.Create(Self); Page1Label.LineWrap := True; Page1Label.SetAlignment(0.5, 0.5); Page1Label.Caption := LANGCCHKSUMPage1Text; Page1.AddControlEx(LeftSpacer, False, False, 0); Page1.AddControlEx(Page1Label, True, True, 0); Page2 := TGTKEventBox.Create(Self); Page2.SetBackgroundColor(0, 0, 0); Page2Color := TGTKEventBox.Create(Self); Page2Color.BorderWidth := 1; Page2Color.SetBackgroundColor(ColorGray); Page2.AddControl(Page2Color); Page2ClientArea := TGTKVBox.Create(Self); Page2Color.AddControl(Page2ClientArea); SFVRadioButton := TGTKRadioButton.Create(Self); SFVRadioButton.Caption := LANGCCHKSUMSFVFile; MD5RadioButton := TGTKRadioButton.Create(Self); MD5RadioButton.Caption := LANGCCHKSUMMD5sumFile; MD5RadioButton.SetRadioGroup(SFVRadioButton); SFVRadioButton.Checked := True; Page2ClientArea.AddControlEx(SFVRadioButton, False, False, 3); Page2ClientArea.AddControlEx(MD5RadioButton, False, False, 3); Page2ClientArea.BorderWidth := 30; Page3 := TGTKEventBox.Create(Self); Page3.SetBackgroundColor(0, 0, 0); Page3Color := TGTKEventBox.Create(Self); Page3Color.BorderWidth := 1; Page3Color.SetBackgroundColor(ColorGray); Page3.AddControl(Page3Color); Page3ClientArea := TGTKVBox.Create(Self); Page3Color.AddControl(Page3ClientArea); FilenameLabel := TGTKLabel.Create(Self); FilenameLabel.Caption := LANGCCHKSUMFileName; FilenameLabel.UseUnderline := True; FileNameEntry := TGTKEntry.Create(Self); FileNameEntry.Text := 'checksums.sfv'; FileNameEntry.SetSizeRequest(215, -1); FileNameEntry.OnChanged := FileNameEntryChanged; FilenameLabel.FocusControl := FileNameEntry; SeparateFileCheckBox := TGTKCheckButton.CreateWithLabel(Self, LANGCCHKSUMCreateSeparateChecksumFiles); SeparateFileCheckBox.OnToggled := SeparateFileCheckBoxToggled; Page3ClientArea.BorderWidth := 30; FileNameHBox := TGTKHBox.Create(Self); FileNameHBox.Homogeneous := False; FileNameHBox.AddControlEx(FilenameLabel, False, False, 5); FileNameHBox.AddControlEx(FileNameEntry, False, False, 5); Page3ClientArea.AddControlEx(FileNameHBox, False, False, 0); SeparateFileCheckHBox := TGTKHBox.Create(Self); SeparateFileCheckHBox.Homogeneous := False; SeparateFileCheckHBox.AddControlEx(SeparateFileCheckBox, False, False, 20); SeparateFileCheckHBox.AddControlEx(TGTKLabel.Create(Self), True, True, 0); Page3ClientArea.AddControlEx(SeparateFileCheckHBox, False, False, 5); Page4 := TGTKEventBox.Create(Self); Page4.SetBackgroundColor(0, 0, 0); Page4Color := TGTKEventBox.Create(Self); Page4Color.BorderWidth := 1; Page4Color.SetBackgroundColor(ColorGray); Page4.AddControl(Page4Color); Page4Label := TGTKLabel.Create(Self); Page4Label.LineWrap := True; Page4Label.SetAlignment(0.5, 0.5); Page4Label.Caption := LANGCCHKSUMPage4Text; Page4Color.AddControl(Page4Label); Page5 := TGTKEventBox.Create(Self); Page5.SetBackgroundColor(0, 0, 0); Page5Color := TGTKEventBox.Create(Self); Page5Color.BorderWidth := 1; Page5Color.SetBackgroundColor(ColorGray); Page5.AddControl(Page5Color); Page5ClientArea := TGTKVBox.Create(Self); Page5Color.AddControl(Page5ClientArea); ProcessingLabel := TGTKLabel.Create(Self); ProcessingLabel.Caption := Format(LANGCCHKSUMNowProcessingFileS, ['---']); ProcessingLabel.XPadding := 20; ProcessingLabel.XAlign := 0; ProcessingLabel.YPadding := 10; ProcessingLabel.SetSizeRequest(10, -1); Page5ClientArea.AddControlEx(ProcessingLabel, False, False, 10); ProgressHBox := TGTKHBox.Create(Self); Progress := TGTKProgressBar.Create(Self); ProgressHBox.AddControlEx(Progress, True, True, 20); Page5ClientArea.AddControlEx(ProgressHBox, True, True, 10); Page5ClientArea.AddControlEx(TGTKLabel.Create(Self), True, True, 0); Page6 := TGTKEventBox.Create(Self); Page6.SetBackgroundColor(0, 0, 0); Page6Color := TGTKEventBox.Create(Self); Page6Color.BorderWidth := 1; Page6Color.SetBackgroundColor(ColorGray); Page6.AddControl(Page6Color); Page6Label := TGTKLabel.Create(Self); Page6Label.LineWrap := True; Page6Label.SetAlignment(0.5, 0.4); Page6Label.Caption := LANGCCHKSUMPage6Text; Page6Label.UseMarkup := True; Page6Label.XAlign := 0.05; Page6Label.YAlign := 0; Page6Spacer := TGTKLabel.Create(Self); Page6Spacer.SetSizeRequest(-1, 6); Page6ClientArea := TGTKVBox.Create(Self); Page6ClientArea.AddControlEx(Page6Spacer, False, False, 0); Page6ClientArea.AddControlEx(Page6Label, False, False, 0); Page6Color.AddControl(Page6ClientArea); ErrorLabel := TGTKLabel.Create(Self); ErrorLabel.Selectable := True; ErrorLabel.SetAlignment(0, 0); ErrorLabel.SetPadding(2, 2); ErrorLabel.Alignment := taLeftJustify; ErrorLabelScrolledWindow := TGTKScrolledWindow.Create(Self); ErrorLabelScrolledWindow.HorizScrollBarPolicy := sbAutomatic; ErrorLabelScrolledWindow.VertScrollBarPolicy := sbAutomatic; ErrorLabelScrolledWindow.AddWithViewPort(ErrorLabel); ErrorTextViewHBox := TGTKHBox.Create(Self); ErrorTextViewHBox.AddControlEx(ErrorLabelScrolledWindow, True, True, 25); Page6ClientArea.AddControlEx(ErrorTextViewHBox, True, True, 10); Page7 := TGTKHBox.Create(Self); Page7.Homogeneous := False; LeftSpacer7 := TGTKEventBox.Create(Self); LeftSpacer7.SetBackgroundColor(ColorBG); LeftSpacer7.SetSizeRequest(100, -1); Page7Label := TGTKLabel.Create(Self); Page7Label.LineWrap := True; Page1Label.SetAlignment(0.5, 0.5); Page7Label.Caption := LANGCCHKSUMPage7Text; Page7.AddControlEx(LeftSpacer7, False, False, 0); Page7.AddControlEx(Page7Label, True, True, 0); PageArea.AddControlEx(Page1, True, True, 0); PageArea.AddControlEx(Page2, True, True, 0); PageArea.AddControlEx(Page3, True, True, 0); PageArea.AddControlEx(Page4, True, True, 0); PageArea.AddControlEx(Page5, True, True, 0); PageArea.AddControlEx(Page6, True, True, 0); PageArea.AddControlEx(Page7, True, True, 0); end; (********************************************************************************************************************************) procedure TFChecksumDruid.SetCurrentPage(const Num: integer; const GoingForward: boolean); const Ext: array[boolean] of string = ('.md5', '.sfv'); var s: string; i: TEngineFileDes; Error: integer; begin // A simple test before we can continue if (CurrentPage = 3) and (not SeparateFileCheckBox.Checked) and GoingForward then begin s := UTF8ToStr(FileNameEntry.Text); i := Engine.OpenFile(s, omWrite, Error); if Error <> 0 then begin Application.MessageBox(Format(LANGCCHKSUMAnErrorOccuredWhileOpeningFileSS, [StrToUTF8(ExtractFileName(s)), GetErrorString(Error)]), [mbOK], mbError, mbNone, mbOK); Exit; end; Engine.CloseFile(i); end; // Changing page CurrentPage := Num; if CurrentPage < 1 then CurrentPage := 1; if CurrentPage > NumPages then CurrentPage := NumPages; case CurrentPage of 1: TitleLabel.SetMarkup(LANGCCHKSUMPage1Title); 2: TitleLabel.SetMarkup(LANGCCHKSUMPage2Title); 3: TitleLabel.SetMarkup(LANGCCHKSUMPage3Title); 4: TitleLabel.SetMarkup(LANGCCHKSUMPage4Title); 5: TitleLabel.SetMarkup(LANGCCHKSUMPage5Title); 6: TitleLabel.SetMarkup(LANGCCHKSUMPage6Title); 7: TitleLabel.SetMarkup(LANGCCHKSUMPage7Title); end; Page1.Visible := CurrentPage = 1; Page2.Visible := CurrentPage = 2; Page3.Visible := CurrentPage = 3; Page4.Visible := CurrentPage = 4; Page5.Visible := CurrentPage = 5; Page6.Visible := CurrentPage = 6; Page7.Visible := CurrentPage = 7; BackButton.Enabled := CurrentPage > 1; ForwardButton.Enabled := CurrentPage < NumPages; if CurrentPage < NumPages then begin if not ForwardButton.UseStock then ForwardButton.Caption := 'gtk-go-forward'; ForwardButton.UseStock := True; end else begin ForwardButton.UseStock := False; ForwardButton.Caption := LANGCCHKSUMFinishCaption; end; // after page changed case CurrentPage of 1: ForwardButton.SetFocus; 2: if SFVRadioButton.Checked then SFVRadioButton.SetFocus else MD5RadioButton.SetFocus; 3: begin SeparateFileCheckBox.Visible := MD5RadioButton.Checked and (FileNames.Count > 1); FileNameEntry.Enabled := (not SeparateFileCheckBox.Checked) or (not SeparateFileCheckBox.Visible); if FileNames.Count = 1 then FileNameEntry.Text := StrToUTF8(ExtractFileName(FileNames[0]) + Ext[SFVRadioButton.Checked]) else FileNameEntry.Text := StrToUTF8(DirName + Ext[SFVRadioButton.Checked]); FileNameEntry.SetFocus; FileNameEntryChanged(Self); end; 4: ForwardButton.SetFocus; 5: Process; 6: begin BackButton.SetFocus; ForwardButton.Enabled := False; s := ErrorLabel.Caption; if (Length(s) > 1) and (s[Length(s)] = #10) then Delete(s, Length(s), 1); ErrorLabel.Caption := s; end; 7: begin BackButton.Enabled := False; CancelButton.Enabled := False; ForwardButton.Enabled := True; ForwardButton.SetFocus; end; end; end; procedure TFChecksumDruid.CancelButtonClick(Sender: TObject); begin ModalResult := mbCancel; end; procedure TFChecksumDruid.BackButtonClick(Sender: TObject); begin SetCurrentPage(CurrentPage - 1 - Ord(CurrentPage = 6), False); end; procedure TFChecksumDruid.ForwardButtonClick(Sender: TObject); begin if CurrentPage = 7 then ModalResult := mbOK else SetCurrentPage(CurrentPage + 1, True); end; procedure TFChecksumDruid.FileNameEntryChanged(Sender: TObject); begin ForwardButton.Enabled := Length(Trim(FileNameEntry.Text)) > 0; end; procedure TFChecksumDruid.SeparateFileCheckBoxToggled(Sender: TObject); begin FileNameEntry.Enabled := not SeparateFileCheckBox.Checked; end; (********************************************************************************************************************************) procedure TFChecksumDruid.CheckStop; begin if Stop and (not StopBreak) then if Application.MessageBox(LANGCCHKSUMAreYouSureYouWantToAbortTheProcessing, [mbYes, mbNo], mbQuestion, mbNone, mbNo) = mbYes then StopBreak := True else begin Stop := False; StopBreak := False; end; end; procedure TFChecksumDruid.Process; const Ext: array[boolean] of string = ('.md5', '.sfv'); var i: integer; MaxSize, LastValue: Int64; Stat: PDataItemSL; s, s2: string; begin if FileNames.Count = 0 then begin SetCurrentPage(7, True); Exit; end; FileNames.Sorted := True; CancelButton.SetFocus; ForwardButton.Enabled := False; BackButton.Enabled := False; Processing := True; Stop := False; StopBreak := False; ErrorLabel.Caption := ''; Application.ProcessMessages; // Compute maximal size of selected files MaxSize := 0; for i := 0 to FileNames.Count - 1 do begin Stat := Engine.GetFileInfoSL(FileNames[i]); if Assigned(Stat) then Inc(MaxSize, Stat.Size); end; Progress.Max := MaxSize; Progress.Value := 0; Progress.Text := '0 %'; if SFVRadioButton.Checked then WriteSFVComment(ProcessPattern(Engine, UTF8ToStr(FileNameEntry.Text), IncludeTrailingPathDelimiter(ExtractFilePath(FileNames[0])), '', False)); // Process each file for i := 0 to FileNames.Count - 1 do begin ProcessingLabel.Caption := Format(LANGCCHKSUMNowProcessingFileS, [StrToUTF8(ExtractFileName(FileNames[i]))]); LastValue := Progress.Value; Stat := Engine.GetFileInfoSL(FileNames[i]); Application.ProcessMessages; try if ProcessFile(FileNames[i], SFVRadioButton.Checked, s) then begin if not SeparateFileCheckBox.Checked then s2 := ProcessPattern(Engine, StrToUTF8(FileNameEntry.Text), IncludeTrailingPathDelimiter(ExtractFilePath(FileNames[0])), '', False) else s2 := IncludeTrailingPathDelimiter(ExtractFilePath(FileNames[i])) + ExtractFileName(FileNames[i]) + Ext[SFVRadioButton.Checked]; WriteLine(s2, FileNames[i], s, SFVRadioButton.Checked, ((i = 0) and MD5RadioButton.Checked) or SeparateFileCheckBox.Checked, (i = FileNames.Count - 1) or SeparateFileCheckBox.Checked); end; except end; if Assigned(Stat) then Progress.Value := LastValue + Stat.Size; Progress.Text := Format('%d %%', [Trunc(Progress.Fraction * 100)]); Application.ProcessMessages; CheckStop; if StopBreak then Break; end; Application.ProcessMessages; Processing := False; Stop := False; StopBreak := False; Application.ProcessMessages; SetCurrentPage(7 - Ord(ErrorLabel.Caption <> ''), ErrorLabel.Caption = ''); end; function TFChecksumDruid.ProcessFile(const FName: string; const IsItSFV: boolean; var HashString: string): boolean; const ChksumBlockSize = 65536*4; var FD: TEngineFileDes; Error, Count: integer; Buffer: Pointer; CRC: LongWord; MD5Hash: THash_MD5; begin HashString := ''; Result := False; try Buffer := Libc.malloc(ChksumBlockSize); Libc.memset(Buffer, 0, ChksumBlockSize); except ErrorLabel.Caption := ErrorLabel.Caption + LANGAnErrorOccuredWhileInitializingMemoryBlock + #10; Exit; end; CRC := $FFFFFFFF; MD5Hash := nil; if not IsItSFV then MD5Hash := THash_MD5.Create; FD := Engine.OpenFile(FName, omRead, Error); if Error <> 0 then begin ErrorLabel.Caption := ErrorLabel.Caption + Format(LANGCCHKSUMAnErrorOccuredWhileOpeningFileSS, [StrToUTF8(ExtractFileName(FName)), GetErrorString(Error)]); Exit; end; repeat Count := Engine.ReadFile(FD, Buffer, ChksumBlockSize, Error); if Error <> 0 then begin ErrorLabel.Caption := ErrorLabel.Caption + Format(LANGCCHKSUMAnErrorOccuredWhileReadingFileSS, [StrToUTF8(ExtractFileName(FName)), GetErrorString(Error)]); Engine.CloseFile(FD); Exit; end; if IsItSFV then CRC := CRC32(CRC, Buffer, Count) else MD5Hash.Calc(Buffer^, Count); Progress.Value := Progress.Value + Count; Progress.Text := Format('%d %%', [Trunc(Progress.Fraction * 100)]); Application.ProcessMessages; CheckStop; until (Count < ChksumBlockSize) or Stop; Engine.CloseFile(FD); Libc.free(Buffer); if IsItSFV then HashString := IntToHex(not CRC, 8) else begin MD5Hash.Done; HashString := MD5Hash.GetKeyStrH; MD5Hash.Free; end; Result := True; end; const ChksumBlockSize = 32768; // Maximum of PByteArray procedure TFChecksumDruid.WriteLine(const FName, CheckedFName, HashString: string; const IsItSFV, CreateFile, CloseFile: boolean); var i, Error, Count: integer; s: string; begin if CreateFile then begin FileDes := Engine.OpenFile(FName, omWrite, Error); if Error <> 0 then begin ErrorLabel.Caption := ErrorLabel.Caption + Format(LANGCCHKSUMAnErrorOccuredWhileOpeningFileSS, [StrToUTF8(ExtractFileName(FName)), GetErrorString(Error)]); Exit; end; try Buffer := Libc.malloc(ChksumBlockSize); Libc.memset(Buffer, 0, ChksumBlockSize); except ErrorLabel.Caption := ErrorLabel.Caption + LANGAnErrorOccuredWhileInitializingMemoryBlock + #10; Exit; end; BufferPos := -1; end; if Buffer = nil then Exit; if IsItSFV then s := Format('%s %s'#13#10, [ExtractFileName(CheckedFName), Trim(WideUpperCase(HashString))]) else s := Format('%s %s'#10, [Trim(AnsiLowerCase(HashString)), ExtractFileName(CheckedFName)]); for i := 1 to Length(s) do begin if BufferPos + 1 >= ChksumBlockSize then begin Count := Engine.WriteFile(FileDes, Buffer, ChksumBlockSize, Error); BufferPos := -1; if (Error <> 0) or (Count <> ChksumBlockSize) then begin ErrorLabel.Caption := ErrorLabel.Caption + Format(LANGCCHKSUMAnErrorOccuredWhileWritingFileSS, [StrToUTF8(ExtractFileName(FName)), GetErrorString(Error)]); Exit; end; end; Inc(BufferPos); PByteArray(Buffer)^[BufferPos] := Byte(s[i]); end; if CloseFile then begin Inc(BufferPos); // Counting with zero-starting element Count := Engine.WriteFile(FileDes, Buffer, BufferPos, Error); if (Error <> 0) or (Count <> BufferPos) then begin ErrorLabel.Caption := ErrorLabel.Caption + Format(LANGCCHKSUMAnErrorOccuredWhileWritingFileSS, [StrToUTF8(ExtractFileName(FName)), GetErrorString(Error)]); Exit; end; BufferPos := -1; Engine.CloseFile(FileDes); Libc.free(Buffer); end; end; procedure TFChecksumDruid.WriteSFVComment(const FName: string); var i, Error, Count: integer; Stat: PDataItemSL; s: string; begin FileDes := Engine.OpenFile(FName, omWrite, Error); if Error <> 0 then begin ErrorLabel.Caption := ErrorLabel.Caption + Format(LANGCCHKSUMAnErrorOccuredWhileOpeningFileSS, [StrToUTF8(ExtractFileName(FName)), GetErrorString(Error)]); Exit; end; try Buffer := Libc.malloc(ChksumBlockSize); Libc.memset(Buffer, 0, ChksumBlockSize); except ErrorLabel.Caption := ErrorLabel.Caption + LANGAnErrorOccuredWhileInitializingMemoryBlock + #10; Exit; end; BufferPos := -1; if Buffer = nil then Exit; s := Format('; Generated by WIN-SFV32 v1 [added for sfv32 compatibility]'#13#10'; Using Tux Commander v%s [%s] on %s'#13#10 + '; http://tuxcmd.sourceforge.net/'#13#10'; '#13#10'; /----'#13#10, [ConstAboutVersion, ConstAboutBuildDate, SysUtils.FormatDateTime('mm.dd.yyyy "at" hh:nn:ss', Now)]); for i := 0 to FileNames.Count - 1 do begin Stat := Engine.GetFileInfoSL(FileNames[i]); if Assigned(Stat) then s := s + Format('; %s %s %s'#13#10, [PadRightStr(IntToStr(Stat^.Size), 11), SysUtils.FormatDateTime('hh:nn.ss yyyy-mm-dd', Stat^.ModifyTime), ExtractFileName(FileNames[i])]); end; s := s + '; \----'#13#10'; '#13#10; for i := 1 to Length(s) do begin if BufferPos + 1 >= ChksumBlockSize then begin Count := Engine.WriteFile(FileDes, Buffer, ChksumBlockSize, Error); BufferPos := -1; if (Error <> 0) or (Count <> ChksumBlockSize) then begin ErrorLabel.Caption := ErrorLabel.Caption + Format(LANGCCHKSUMAnErrorOccuredWhileWritingFileSS, [StrToUTF8(ExtractFileName(FName)), GetErrorString(Error)]); Exit; end; end; Inc(BufferPos); PByteArray(Buffer)^[BufferPos] := Byte(s[i]); end; end; end.