mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
558 lines
15 KiB
ObjectPascal
558 lines
15 KiB
ObjectPascal
{
|
|
Double Commander
|
|
-------------------------------------------------------------------------
|
|
Implementation of multi archiver support
|
|
|
|
Copyright (C) 2010-2013 Koblov Alexander (Alexx2000@mail.ru)
|
|
|
|
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 uMultiArc;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, DCBasicTypes;
|
|
|
|
const
|
|
MaxSignSize = 1024;
|
|
SignSeekRange = 1024 * 1024;
|
|
|
|
type
|
|
|
|
TSignature = record
|
|
Value: array[0..Pred(MaxSignSize)] of Byte;
|
|
Size: LongInt;
|
|
end;
|
|
PSignature = ^TSignature;
|
|
|
|
{ TSignatureList }
|
|
|
|
TSignatureList = class(TFPList)
|
|
private
|
|
function GetSignature(Index: Integer): PSignature;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure Clean;
|
|
property Items[Index: Integer]: PSignature read GetSignature; default;
|
|
end;
|
|
|
|
TSignaturePosition = record
|
|
Value: LongInt;
|
|
Sign: Boolean;
|
|
end;
|
|
PSignaturePosition = ^TSignaturePosition;
|
|
|
|
{ TSignaturePositionList }
|
|
|
|
TSignaturePositionList = class(TFPList)
|
|
private
|
|
function GetSignaturePosition(Index: Integer): PSignaturePosition;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure Clean;
|
|
property Items[Index: Integer]: PSignaturePosition read GetSignaturePosition; default;
|
|
end;
|
|
|
|
{ TArchiveItem }
|
|
|
|
TArchiveItem = class
|
|
FileName,
|
|
FileExt,
|
|
FileLink: UTF8String;
|
|
PackSize,
|
|
UnpSize: Int64;
|
|
Year,
|
|
Month,
|
|
Day,
|
|
Hour,
|
|
Minute,
|
|
Second: Word;
|
|
Attributes: TFileAttrs;
|
|
end;
|
|
|
|
{ TMultiArcItem }
|
|
|
|
TMultiArcItem = class
|
|
private
|
|
FSeekAfterSignPos: Boolean;
|
|
FSignature,
|
|
FSignaturePosition: AnsiString;
|
|
FSignatureSeekRange: LongInt;
|
|
FSignatureList: TSignatureList;
|
|
FSignaturePositionList: TSignaturePositionList;
|
|
function GetSignatureSeekRange: AnsiString;
|
|
procedure SetSignature(const AValue: AnsiString);
|
|
procedure SetSignaturePosition(const AValue: AnsiString);
|
|
procedure SetSignatureSeekRange(const AValue: AnsiString);
|
|
public
|
|
FArchiver,
|
|
FDescription,
|
|
FExtension,
|
|
FStart,
|
|
FEnd: UTF8String;
|
|
FFormat: TStringList;
|
|
FList,
|
|
FExtract,
|
|
FExtractWithoutPath,
|
|
FTest,
|
|
FDelete,
|
|
FAdd,
|
|
FAddSelfExtract,
|
|
FPasswordQuery: UTF8String;
|
|
FFormMode: Integer;
|
|
public
|
|
FEnabled: Boolean;
|
|
FOutput: Boolean;
|
|
FDebug: Boolean;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function CanYouHandleThisFile(const FileName: UTF8String): Boolean;
|
|
property FID: AnsiString read FSignature write SetSignature;
|
|
property FIDPos: AnsiString read FSignaturePosition write SetSignaturePosition;
|
|
property FIDSeekRange: AnsiString read GetSignatureSeekRange write SetSignatureSeekRange;
|
|
end;
|
|
|
|
{ TMultiArcList }
|
|
|
|
TMultiArcList = class
|
|
FList: TStringList;
|
|
private
|
|
function GetCount: LongInt;
|
|
function GetItem(Index: Integer): TMultiArcItem;
|
|
function GetName(Index: Integer): UTF8String;
|
|
procedure SetName(Index: Integer; const AValue: UTF8String);
|
|
public
|
|
constructor Create; virtual;
|
|
destructor Destroy; override;
|
|
procedure AutoConfigure;
|
|
procedure Clear;
|
|
procedure LoadFromFile(const FileName: UTF8String);
|
|
procedure SaveToFile(const FileName: UTF8String);
|
|
function Add(const S: UTF8String; aMultiArcItem: TMultiArcItem): Integer;
|
|
procedure Delete(Index: Integer);
|
|
property Names[Index: Integer]: UTF8String read GetName write SetName;
|
|
property Items[Index: Integer]: TMultiArcItem read GetItem; default;
|
|
property Count: LongInt read GetCount;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
LCLProc, StrUtils, Math, FileUtil, DCClassesUtf8, uDCUtils, DCOSUtils;
|
|
|
|
{ TMultiArcList }
|
|
|
|
function TMultiArcList.GetCount: LongInt;
|
|
begin
|
|
Result:= FList.Count;
|
|
end;
|
|
|
|
function TMultiArcList.GetItem(Index: Integer): TMultiArcItem;
|
|
begin
|
|
Result:= TMultiArcItem(FList.Objects[Index]);
|
|
end;
|
|
|
|
function TMultiArcList.GetName(Index: Integer): UTF8String;
|
|
begin
|
|
Result:= FList.Strings[Index];
|
|
end;
|
|
|
|
procedure TMultiArcList.SetName(Index: Integer; const AValue: UTF8String);
|
|
begin
|
|
FList.Strings[Index]:= AValue;
|
|
end;
|
|
|
|
constructor TMultiArcList.Create;
|
|
begin
|
|
FList:= TStringList.Create;
|
|
end;
|
|
|
|
destructor TMultiArcList.Destroy;
|
|
begin
|
|
Clear;
|
|
FreeThenNil(FList);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TMultiArcList.AutoConfigure;
|
|
var
|
|
I: Integer;
|
|
ExePath: UTF8String;
|
|
begin
|
|
for I:= 0 to Count - 1 do
|
|
begin
|
|
ExePath:= Items[I].FArchiver;
|
|
if not mbFileExists(ReplaceEnvVars(ExePath)) then
|
|
ExePath:= FindDefaultExecutablePath(ExePath);
|
|
if ExePath = EmptyStr then
|
|
Items[I].FEnabled:= False
|
|
else
|
|
begin
|
|
Items[I].FArchiver:= ExePath;
|
|
Items[I].FEnabled:= True;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMultiArcList.Clear;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I:= FList.Count - 1 downto 0 do
|
|
if Assigned(FList.Objects[I]) then
|
|
begin
|
|
FList.Objects[I].Free;
|
|
FList.Objects[I]:= nil;
|
|
FList.Delete(I);
|
|
end;
|
|
end;
|
|
|
|
procedure TMultiArcList.LoadFromFile(const FileName: UTF8String);
|
|
var
|
|
I, J: Integer;
|
|
IniFile: TIniFileEx = nil;
|
|
Sections: TStringList = nil;
|
|
Section,
|
|
Format: UTF8String;
|
|
FirstTime: Boolean = True;
|
|
MultiArcItem: TMultiArcItem;
|
|
begin
|
|
IniFile:= TIniFileEx.Create(FileName, fmOpenRead);
|
|
try
|
|
Sections:= TStringList.Create;
|
|
IniFile.ReadSections(Sections);
|
|
for I:= 0 to Sections.Count - 1 do
|
|
begin
|
|
Section:= Sections[I];
|
|
if SameText(Section, 'MultiArc') then
|
|
begin
|
|
FirstTime:= IniFile.ReadBool(Section, 'FirstTime', True);
|
|
Continue;
|
|
end;
|
|
MultiArcItem:= TMultiArcItem.Create;
|
|
with MultiArcItem do
|
|
begin
|
|
FArchiver:= FixExeExt(TrimQuotes(IniFile.ReadString(Section, 'Archiver', EmptyStr)));
|
|
FDescription:= TrimQuotes(IniFile.ReadString(Section, 'Description', EmptyStr));
|
|
FID:= TrimQuotes(IniFile.ReadString(Section, 'ID', EmptyStr));
|
|
FIDPos:= TrimQuotes(IniFile.ReadString(Section, 'IDPos', EmptyStr));
|
|
FIDSeekRange:= IniFile.ReadString(Section, 'IDSeekRange', EmptyStr);
|
|
FExtension:= TrimQuotes(IniFile.ReadString(Section, 'Extension', EmptyStr));
|
|
FStart:= TrimQuotes(IniFile.ReadString(Section, 'Start', EmptyStr));
|
|
FEnd:= TrimQuotes(IniFile.ReadString(Section, 'End', EmptyStr));
|
|
for J:= 0 to 50 do
|
|
begin
|
|
Format:= TrimQuotes(IniFile.ReadString(Section, 'Format' + IntToStr(J), EmptyStr));
|
|
if Format <> EmptyStr then
|
|
FFormat.Add(Format)
|
|
else
|
|
Break;
|
|
end;
|
|
FList:= TrimQuotes(IniFile.ReadString(Section, 'List', EmptyStr));
|
|
FExtract:= TrimQuotes(IniFile.ReadString(Section, 'Extract', EmptyStr));
|
|
FExtractWithoutPath:= TrimQuotes(IniFile.ReadString(Section, 'ExtractWithoutPath', EmptyStr));
|
|
FTest:= TrimQuotes(IniFile.ReadString(Section, 'Test', EmptyStr));
|
|
FDelete:= TrimQuotes(IniFile.ReadString(Section, 'Delete', EmptyStr));
|
|
FAdd:= TrimQuotes(IniFile.ReadString(Section, 'Add', EmptyStr));
|
|
FAddSelfExtract:= TrimQuotes(IniFile.ReadString(Section, 'AddSelfExtract', EmptyStr));
|
|
FPasswordQuery:= IniFile.ReadString(Section, 'PasswordQuery', EmptyStr);
|
|
// optional
|
|
FFormMode:= IniFile.ReadInteger(Section, 'FormMode', 0);
|
|
FEnabled:= IniFile.ReadBool(Section, 'Enabled', True);
|
|
FOutput:= IniFile.ReadBool(Section, 'Output', False);
|
|
FDebug:= IniFile.ReadBool(Section, 'Debug', False);
|
|
end;
|
|
FList.AddObject(Section, MultiArcItem);
|
|
end;
|
|
if FirstTime then AutoConfigure;
|
|
finally
|
|
FreeThenNil(IniFile);
|
|
FreeThenNil(Sections);
|
|
end;
|
|
end;
|
|
|
|
procedure TMultiArcList.SaveToFile(const FileName: UTF8String);
|
|
var
|
|
I, J: Integer;
|
|
IniFile: TIniFileEx;
|
|
Section: UTF8String;
|
|
MultiArcItem: TMultiArcItem;
|
|
begin
|
|
IniFile:= TIniFileEx.Create(FileName, fmOpenWrite);
|
|
try
|
|
IniFile.Clear;
|
|
for I:= 0 to FList.Count - 1 do
|
|
begin
|
|
Section:= FList.Strings[I];
|
|
MultiArcItem:= TMultiArcItem(FList.Objects[I]);
|
|
with MultiArcItem do
|
|
begin
|
|
IniFile.WriteString(Section, 'Archiver', FArchiver);
|
|
IniFile.WriteString(Section, 'Description', FDescription);
|
|
IniFile.WriteString(Section, 'ID', FID);
|
|
IniFile.WriteString(Section, 'IDPos', FIDPos);
|
|
IniFile.WriteString(Section, 'IDSeekRange', FIDSeekRange);
|
|
IniFile.WriteString(Section, 'Extension', FExtension);
|
|
IniFile.WriteString(Section, 'Start', FStart);
|
|
IniFile.WriteString(Section, 'End', FEnd);
|
|
for J:= 0 to FFormat.Count - 1 do
|
|
begin
|
|
IniFile.WriteString(Section, 'Format' + IntToStr(J), FFormat[J]);
|
|
end;
|
|
IniFile.WriteString(Section, 'List', FList);
|
|
IniFile.WriteString(Section, 'Extract', FExtract);
|
|
IniFile.WriteString(Section, 'ExtractWithoutPath', FExtractWithoutPath);
|
|
IniFile.WriteString(Section, 'Test', FTest);
|
|
IniFile.WriteString(Section, 'Delete', FDelete);
|
|
IniFile.WriteString(Section, 'Add', FAdd);
|
|
IniFile.WriteString(Section, 'AddSelfExtract', FAddSelfExtract);
|
|
IniFile.WriteString(Section, 'PasswordQuery', FPasswordQuery);
|
|
// optional
|
|
IniFile.WriteInteger(Section, 'FormMode', FFormMode);
|
|
IniFile.WriteBool(Section, 'Enabled', FEnabled);
|
|
IniFile.WriteBool(Section, 'Output', FOutput);
|
|
IniFile.WriteBool(Section, 'Debug', FDebug);
|
|
end;
|
|
end;
|
|
IniFile.WriteBool('MultiArc', 'FirstTime', False);
|
|
finally
|
|
IniFile.Free;
|
|
end;
|
|
end;
|
|
|
|
function TMultiArcList.Add(const S: UTF8String; aMultiArcItem: TMultiArcItem): Integer;
|
|
begin
|
|
Result := FList.AddObject(S, aMultiArcItem);
|
|
end;
|
|
|
|
procedure TMultiArcList.Delete(Index: Integer);
|
|
begin
|
|
Items[Index].Free;
|
|
FList.Delete(Index);
|
|
end;
|
|
|
|
{ TMultiArcItem }
|
|
|
|
function TMultiArcItem.GetSignatureSeekRange: AnsiString;
|
|
begin
|
|
if FSignatureSeekRange = SignSeekRange then
|
|
Result:= EmptyStr
|
|
else
|
|
Result:= IntToStr(FSignatureSeekRange);
|
|
end;
|
|
|
|
procedure TMultiArcItem.SetSignature(const AValue: AnsiString);
|
|
var
|
|
I: Integer;
|
|
Sign: AnsiString;
|
|
Value: AnsiString;
|
|
Signature: PSignature;
|
|
begin
|
|
FSignature:= AValue;
|
|
FSignatureList.Clean;
|
|
if AValue = EmptyStr then Exit;
|
|
Value:= AValue;
|
|
repeat
|
|
I:= 0;
|
|
New(Signature);
|
|
Sign:= Trim(Copy2SymbDel(Value, ','));
|
|
try
|
|
while (Sign <> EmptyStr) and (I < MaxSignSize) do
|
|
begin
|
|
Signature^.Value[I]:= StrToInt('$' + Copy2SymbDel(Sign, #32));
|
|
Inc(I);
|
|
end;
|
|
Signature^.Size:= I;
|
|
FSignatureList.Add(Signature);
|
|
except
|
|
Dispose(Signature);
|
|
end;
|
|
until Value = EmptyStr;
|
|
end;
|
|
|
|
procedure TMultiArcItem.SetSignaturePosition(const AValue: AnsiString);
|
|
var
|
|
SignPos,
|
|
Value: AnsiString;
|
|
SignaturePosition: PSignaturePosition;
|
|
begin
|
|
FSignaturePosition:= AValue;
|
|
FSignaturePositionList.Clean;
|
|
if AValue = EmptyStr then Exit;
|
|
Value:= StringReplace(AValue, '0x', '$', [rfReplaceAll]);
|
|
repeat
|
|
SignPos:= Trim(Copy2SymbDel(Value, ','));
|
|
if SignPos = '<SeekID>' then
|
|
FSeekAfterSignPos:= True
|
|
else
|
|
try
|
|
New(SignaturePosition);
|
|
SignaturePosition^.Value:= StrToInt(SignPos);
|
|
SignaturePosition^.Sign:= not (SignaturePosition^.Value < 0);
|
|
SignaturePosition^.Value:= abs(SignaturePosition^.Value);
|
|
FSignaturePositionList.Add(SignaturePosition);
|
|
except
|
|
Dispose(SignaturePosition);
|
|
end;
|
|
until Value = EmptyStr;
|
|
end;
|
|
|
|
procedure TMultiArcItem.SetSignatureSeekRange(const AValue: AnsiString);
|
|
begin
|
|
if not TryStrToInt(AValue, FSignatureSeekRange) then
|
|
FSignatureSeekRange:= SignSeekRange;
|
|
end;
|
|
|
|
constructor TMultiArcItem.Create;
|
|
begin
|
|
FSignatureList:= TSignatureList.Create;
|
|
FSignaturePositionList:= TSignaturePositionList.Create;
|
|
FFormat:= TStringList.Create;
|
|
end;
|
|
|
|
destructor TMultiArcItem.Destroy;
|
|
begin
|
|
FreeThenNil(FSignatureList);
|
|
FreeThenNil(FSignaturePositionList);
|
|
FreeThenNil(FFormat);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TMultiArcItem.CanYouHandleThisFile(const FileName: UTF8String): Boolean;
|
|
var
|
|
FileMapRec : TFileMapRec;
|
|
hFile: THandle;
|
|
I, J: LongInt;
|
|
lpBuffer: PByte = nil;
|
|
Origin: LongInt;
|
|
dwMaxSignSize: LongWord = 0;
|
|
dwReaded: LongWord;
|
|
dwOffset: LongWord = 0;
|
|
begin
|
|
Result:= False;
|
|
hFile:= mbFileOpen(FileName, fmOpenRead or fmShareDenyNone);
|
|
if hFile <> feInvalidHandle then
|
|
begin
|
|
// Determine maximum signature size
|
|
for J:= 0 to FSignatureList.Count - 1 do
|
|
dwMaxSignSize := Max(FSignatureList[J]^.Size, dwMaxSignSize);
|
|
{
|
|
if (FSkipSfxPart) then
|
|
dwOffset := FSfxOffset
|
|
}
|
|
lpBuffer:= GetMem(dwMaxSignSize);
|
|
if Assigned(lpBuffer) then
|
|
try
|
|
// Try to determine by IDPOS
|
|
for I:= 0 to FSignaturePositionList.Count - 1 do
|
|
begin
|
|
case FSignaturePositionList[I]^.Sign of
|
|
True: Origin:= fsFromBeginning;
|
|
False: Origin:= fsFromEnd;
|
|
end;
|
|
if (FileSeek(hFile, dwOffset + FSignaturePositionList[I]^.Value, Origin) <> -1) then
|
|
begin
|
|
dwReaded:= FileRead(hFile, lpBuffer^, dwMaxSignSize);
|
|
if (dwReaded = dwMaxSignSize) then
|
|
begin
|
|
for J := 0 to FSignatureList.Count - 1 do
|
|
begin
|
|
if(CompareByte(lpBuffer^, FSignatureList[J]^.Value, FSignatureList[J]^.Size) = 0) then
|
|
Exit(True);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
FreeMem(lpBuffer);
|
|
FileClose(hFile);
|
|
end; // if Assigned(lpBuffer)
|
|
end;
|
|
|
|
// Try raw seek id
|
|
if (Result = False) and FSeekAfterSignPos then
|
|
begin
|
|
FillByte(FileMapRec, SizeOf(FileMapRec), 0);
|
|
if MapFile(FileName, FileMapRec) then
|
|
try
|
|
dwOffset:= Min(FSignatureSeekRange, FileMapRec.FileSize);
|
|
for I:= 0 to dwOffset do
|
|
begin
|
|
for J:= 0 to FSignatureList.Count - 1 do
|
|
begin
|
|
if(CompareByte((FileMapRec.MappedFile + I)^, FSignatureList[J]^.Value, FSignatureList[J]^.Size) = 0) then
|
|
Exit(True);
|
|
end;
|
|
end;
|
|
finally
|
|
UnMapFile(FileMapRec);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TSignatureList }
|
|
|
|
function TSignatureList.GetSignature(Index: Integer): PSignature;
|
|
begin
|
|
Result:= PSignature(Get(Index));
|
|
end;
|
|
|
|
destructor TSignatureList.Destroy;
|
|
begin
|
|
Clean;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSignatureList.Clean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I:= Count - 1 downto 0 do
|
|
begin
|
|
Dispose(Items[I]);
|
|
Delete(I);
|
|
end;
|
|
end;
|
|
|
|
{ TSignaturePositionList }
|
|
|
|
function TSignaturePositionList.GetSignaturePosition(Index: Integer): PSignaturePosition;
|
|
begin
|
|
Result:= PSignaturePosition(Get(Index));
|
|
end;
|
|
|
|
destructor TSignaturePositionList.Destroy;
|
|
begin
|
|
Clean;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSignaturePositionList.Clean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I:= Count - 1 downto 0 do
|
|
begin
|
|
Dispose(Items[I]);
|
|
Delete(I);
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|