From 2838e1d07d01e6e1b4fc77845a6bb06180f011f8 Mon Sep 17 00:00:00 2001 From: Razor12911 Date: Tue, 5 Jul 2022 00:31:31 +0200 Subject: [PATCH] 0.6.0 major update --- changes.txt | 251 ++++++++ common/Utils.pas | 484 ++++++++++++--- imports/BrunsliDLL.pas | 87 +++ imports/FLACDLL.pas | 196 ++++++ imports/JoJpegDLL.pas | 69 +++ imports/LZ4DLL.pas | 3 + imports/LZMADLL.pas | 28 + imports/PackJPGDLL.pas | 67 +++ imports/TTADLL.pas | 53 ++ io/IODecode.pas | 2 +- precompressor/PrecompCrypto.pas | 10 +- precompressor/PrecompEXE.pas | 2 +- precompressor/PrecompLZ4.pas | 29 +- precompressor/PrecompLZO.pas | 6 +- precompressor/PrecompMain.pas | 606 +++++++++++-------- precompressor/PrecompMedia.pas | 1005 +++++++++++++++++++++++++++++++ precompressor/PrecompOodle.pas | 4 +- precompressor/PrecompUtils.pas | 45 +- precompressor/PrecompZLib.pas | 74 +-- precompressor/PrecompZSTD.pas | 5 +- resources/Win32/fast-lzma2.dll | Bin 0 -> 166400 bytes resources/Win64/fast-lzma2.dll | Bin 0 -> 196608 bytes xtool.dpr | 279 +-------- xtool.dproj | 13 + 24 files changed, 2633 insertions(+), 685 deletions(-) create mode 100644 changes.txt create mode 100644 imports/BrunsliDLL.pas create mode 100644 imports/FLACDLL.pas create mode 100644 imports/JoJpegDLL.pas create mode 100644 imports/LZMADLL.pas create mode 100644 imports/PackJPGDLL.pas create mode 100644 imports/TTADLL.pas create mode 100644 precompressor/PrecompMedia.pas create mode 100644 resources/Win32/fast-lzma2.dll create mode 100644 resources/Win64/fast-lzma2.dll diff --git a/changes.txt b/changes.txt new file mode 100644 index 0000000..c04878d --- /dev/null +++ b/changes.txt @@ -0,0 +1,251 @@ + ES_R35 (0.6.0) + - added wav stream detector + - added flac codec + - added jpg stream detector + - added packjpg, brunsli, jojpeg codec + - added fast lzma2 compression for portable mode + - added feature that allows input to be a directory + - added feature to inject libraries to main executable + - added feature to extract detected streams + - updated database feature + - updated deduplication feature + - IO function decode updated + + ES_R34 (0.5.3) + - added png stream preprocessor + - removed grittibanzli codec (since nobody uses it) + + ES_R33 (0.5.2) + - added IO functions (archive, execute) + - fixed issue in patch io function + - removed compression on patch diff files + + ES_R32 (0.5.1) + - added IO functions (find, extract, patch) + - generate database feature and IO functions now can search for streams larger than chunk size + + ES_R31 (0.5.0) + - added IO functions (erase, replace) + - fixed external executable support bugs + + ES_R30 (0.4.8) + - fixed issue with storing incorrect recompression information when stream patching is performed + + ES_R29 (0.4.7) + - updated oodle scanner + - updated external executable support + - updated configuration based plugin support to add depth information + - updated verbose mode + + ES_R28 (0.4.6) + - generate database feature fixed + - fixed external executable support issues + - fixed lz4f level setting bug + + ES_R28 (0.4.5) + - removed leviathan codec restriction + + ES_R27 (0.4.4) + - fixed issue of lz4 codec loading incorrect library + - fixed issue with handling endianess via configuration based plugins + - updated framework of library based plugins + + ES_R26 (0.4.3) + - added verbose mode + - added feature that allows you to enforce a different library to be loaded + - fixed issues related to imperfect stream patching + - fixed issues with old libraries with missing functions that cause xtool to crash on startup + - updated oodle codec + - updated reflate codec + - updated zstd codec + + ES_R25 (0.4.2) + - removed debugging code from encryption and executable codec + - fixed issue with depth when using search codec + - fixed external executable support issues + + ES_R24 (0.4.1) + - fixed issue of status not reporting when encoding + - added depth method support for search support + - fixed zlib encoding issues for different window bits + - fixed zlib memory leak issue + - updated all internal codecs to support information relayed by external codecs + - updated lz4f codec and removed temporarily removed support for universal scanning + - added option to change recompression level to be used by reflate + - updated external executable support + - generate database feature currently bugged, wait for next update + - search database structure changed, older database files will no longer work with newer releases + + ES_R23 (0.4.0) + - project made open source + - added external executable support + - added generate database feature + - fixed search support bug + + ES_R22 (0.3.22) + - updated search support (speed improvements) + - updated command line parser + - added partial universal scanner for lzo1x streams + - added universal scanner for lz4f streams + - fixed issue with configuration files failing to execute without conditions + + ES_R21 (0.3.21) + - updated search support + + ES_R20 (0.3.20) + - fixed library support bug + - x86 build discontinued (has bugs from nowhere) + + ES_R19 (0.3.19) + - updated lzo codec + + ES_R18 (0.3.18) + - fixed depth bug + - fixed library plugin bugs + + ES_R17 (0.3.17) + - fixed multi-threading bug + + ES_R16 (0.3.16) + - minor bug fixes + + ES_R15 (0.3.15) + - converted library support to unicode (don't know why I used ansi in the first place) + - added library support functions + - added rc4 encryption support + + ES_R14 (0.3.14) + - fixed library support bug + - updated library structure + + ES_R13 (0.3.13) + - updated lz4 codec + - updated library structure + - updated depth info functions + - updated depth feature + + ES_R12 (0.3.12) + - added depth info functions + - added support for oodle 2.9.0+ functions + - fixed data patching bug + - updated oodle codec + - updated command line parser + + ES_R11 (0.3.11) + - fixed x86 build bugs + - fixed config multi-threading bug + - fixed resource management bug + - fixed deduplication bug + + ES_R10 (0.3.10) + - minor bug fixes + - added diff tolerance parameter (--diff=) + - fixed plugin database bug + - fixed lz4 codec bug + - updated oodle codec + - updated library structure + - added resource management + - added direct use encryption codecs + - added embedded deduplication feature (--dedup) [makes temps during encoding] + + ES_R9 (0.3.9) + - fixed future stream bug + + ES_R8 (0.3.8) + - fixed command line parser bug + - updated library support + + ES_R7 (0.3.7) + - updated library structure + + ES_R6 (0.3.6) + - updated oodle codec (fixed more lzna bugs) + + ES_R5 (0.3.5) + - updated oodle codec (fixed lzna bug) + - added custom method configuration + + ES_R4 (0.3.4) + - fixed bug depthing + + ES_R3 (0.3.3) + - updated lz4 codec + - updated library support + + ES_R2 (0.3.2) + - improved depthing + - updated library support + - fixed zstd codec issues + - removed fast memory + + ES_R1 (0.3.1) + - updated library support + - updated command line parser + - included x86 build + - fixed depthing issues + + 2012_R2 (0.2.14) + - added library support + - added compress, decompress, encrypt, decrypt, hash, delta functions (used by library) + - added lzo codec placeholders + - fixed oodle bug + - fixed lz4 bug + - removed libdunia codec + + 2012_R1 (0.2.13) + - added oo2ext* dll support + - updated search support + + 2011_R1 (0.2.12) + - added temporary libdunia codec + + 2010_R5 (0.2.11) + - fixed search/config support bug + + 2010_R4 (0.2.10) + - updated search/config support + + 2010_R3 (0.2.9) + - added database search + - updated zlib scanner + - fixed reflate bug + - fixed 2GB memory limit + + 2010_R2 (0.2.8) + - fixed zstd codec + + 2010_R1 (0.2.7) + - added zstd codec + - added lz4, lz4hc, lzna, mermaid, selkie, hydra, leviathan codec placeholders + - added configuration support + - added xdelta support to handle crc mismatch streams + + 2009_R3 (0.2.6) + - documentation added + + 2009_R2 (0.2.5) + - added kraken codec + - fixed depthing issues + + 2009_R1 (0.2.4) + - added reflate forced verification + - updated deflate scanner + - fixed depthing issues + - fixed low memory mode issues + - fixed hanging issues when encoding + + 2008_R3 (0.2.3) + - fixed deduplication memory calculation error + - added virtual memory support for deduplication + - added --mem=# parameter to control deduplication memory usage + + 2008_R2 (0.2.2) + - fixed command line parser + - updated deflate scanner + - added stream deduplication + - added stream database + - added decompression memory limiter + - added grittibanzli (also handles deflate stream but slow af) + + 2008_R1 (0.2.1) + - initial release \ No newline at end of file diff --git a/common/Utils.pas b/common/Utils.pas index f48dabd..94c7042 100644 --- a/common/Utils.pas +++ b/common/Utils.pas @@ -44,6 +44,16 @@ procedure SetBits(var Data: UInt64; Value: Int64; Index: TInt64_BitIndex; Count: TInt64_BitCount); overload; type + PDynArrayRec = ^TDynArrayRec; + + TDynArrayRec = packed record +{$IFDEF CPUX64} + _Padding: LongInt; +{$ENDIF} + RefCnt: LongInt; + Length: NativeInt; + end; + TListEx = class(TList) private FIndex: Integer; @@ -85,6 +95,13 @@ type property Method: TSOMethod read FSOMethod write FSOMethod; end; + TNullStream = class(TStream) + public + constructor Create; + destructor Destroy; override; + function Write(const Buffer; Count: LongInt): LongInt; override; + end; + TArrayStream = class(TStream) private type _Stream = ^IStream; @@ -97,7 +114,7 @@ type FMaxStreamSize = $FFFFFFFFFF; protected function GetSize: Int64; override; - procedure SetSize(NewSize: Longint); override; + procedure SetSize(NewSize: LongInt); override; procedure SetSize(const NewSize: Int64); override; private FStreams: TArray; @@ -110,8 +127,8 @@ type public constructor Create; destructor Destroy; override; - function Read(var Buffer; Count: Longint): Longint; override; - function Write(const Buffer; Count: Longint): Longint; override; + function Read(var Buffer; Count: LongInt): LongInt; override; + function Write(const Buffer; Count: LongInt): LongInt; override; function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; procedure Clear; function Add(AStreamType: Pointer; MaxSize: Int64 = FMaxStreamSize) @@ -122,7 +139,7 @@ type TPointersStream = class(TStream) protected function GetSize: Int64; override; - procedure SetSize(NewSize: Longint); override; + procedure SetSize(NewSize: LongInt); override; procedure SetSize(const NewSize: Int64); override; private FPointers: TArray; @@ -135,8 +152,8 @@ type public constructor Create; destructor Destroy; override; - function Read(var Buffer; Count: Longint): Longint; override; - function Write(const Buffer; Count: Longint): Longint; override; + function Read(var Buffer; Count: LongInt): LongInt; override; + function Write(const Buffer; Count: LongInt): LongInt; override; function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; function Add(Ptr: Pointer; Size: NativeInt): Integer; procedure Delete(Index: Integer); @@ -157,13 +174,48 @@ type AMaxSize: NativeInt = 0); overload; destructor Destroy; override; procedure SetSize(const NewSize: Int64); override; - procedure SetSize(NewSize: Longint); override; - function Read(var Buffer; Count: Longint): Longint; override; - function Write(const Buffer; Count: Longint): Longint; override; + procedure SetSize(NewSize: LongInt); override; + function Read(var Buffer; Count: LongInt): LongInt; override; + function Write(const Buffer; Count: LongInt): LongInt; override; function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; procedure Update(const AMemory: Pointer = nil; AMaxSize: NativeInt = 0); end; + TDirInputStream = class(TStream) + protected type + TState = (iNone, iLength, iFilename, iSize, iData); + private + FState: TState; + FPath: String; + FBaseDir: String; + FList: TArray; + FIndex, FCount: Integer; + FLength: Word; + FBytes: TBytes; + FStream: TFileStream; + FPosition, FSize: Int64; + public + constructor Create(const APath: String); + destructor Destroy; override; + function Read(var Buffer; Count: LongInt): LongInt; override; + end; + + TDirOutputStream = class(TStream) + protected type + TState = (oNone, oLength, oFilename, oSize, oData); + private + FState: TState; + FPath: String; + FLength: Word; + FBytes: TBytes; + FStream: TFileStream; + FPosition, FSize: Int64; + public + constructor Create(const APath: String); + destructor Destroy; override; + function Write(const Buffer; Count: LongInt): LongInt; override; + end; + TSharedMemoryStream = class(TMemoryStreamEx) private const FIncSize = 64 * 1024 * 1024; @@ -179,7 +231,7 @@ type constructor Create(const AMapName: String; ASize: NativeInt); overload; destructor Destroy; override; procedure SetSize(const NewSize: Int64); override; - function Write(const Buffer; Count: Longint): Longint; override; + function Write(const Buffer; Count: LongInt): LongInt; override; end; TDownloadStream = class(TStream) @@ -195,7 +247,7 @@ type public constructor Create(Url: string); destructor Destroy; override; - function Read(var Buffer; Count: Longint): Longint; override; + function Read(var Buffer; Count: LongInt): LongInt; override; function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; end; @@ -297,23 +349,23 @@ type PBlockInfo = ^TBlockInfo; TBlockInfo = record + ID: Integer; Position, CurrSize, FullSize: Int64; Count: Integer; end; - - TBlockInfoDynArray = TArray; private - FSync: TSynLocker; - FDictionary: TSynDictionary; + FSearchList: TArray; FStream: TStream; - FStreamPos: Int64; + FStreamPos, FStreamSize: Int64; public - constructor Create(AStream: TStream; ACapacity: Integer = 0); + constructor Create(AStream: TStream); destructor Destroy; override; procedure Add(ID: Integer; Size: Int64; Count: Integer = Integer.MaxValue); - procedure Write(ID: Integer; const Buffer; Size: Integer); + procedure Write(ID: Integer; Buffer: Pointer; Size: Integer); procedure CopyData(ID: Integer; Stream: TStream); overload; - function CopyData(ID: Integer): Pointer; overload; + function CopyData(ID: Integer; Data: Pointer): Integer; overload; + procedure Update(ID: Integer; Count: Integer); + procedure Reset(ID: Integer); end; TArgParser = class(TObject) @@ -679,6 +731,21 @@ begin Result := FList.Count; end; +constructor TNullStream.Create; +begin + inherited Create; +end; + +destructor TNullStream.Destroy; +begin + inherited Destroy; +end; + +function TNullStream.Write(const Buffer; Count: LongInt): LongInt; +begin + Result := Count; +end; + constructor TArrayStream.Create; begin inherited Create; @@ -781,7 +848,7 @@ begin Result := FSize; end; -procedure TArrayStream.SetSize(NewSize: Longint); +procedure TArrayStream.SetSize(NewSize: LongInt); begin SetSize(Int64(NewSize)); end; @@ -794,7 +861,7 @@ begin Seek(0, soEnd); end; -function TArrayStream.Read(var Buffer; Count: Longint): Longint; +function TArrayStream.Read(var Buffer; Count: LongInt): LongInt; var LCount: Int64; begin @@ -808,7 +875,7 @@ begin Inc(FPosition, Result); end; -function TArrayStream.Write(const Buffer; Count: Longint): Longint; +function TArrayStream.Write(const Buffer; Count: LongInt): LongInt; var LCount: Int64; begin @@ -888,7 +955,7 @@ begin Result := FSize; end; -procedure TPointersStream.SetSize(NewSize: Longint); +procedure TPointersStream.SetSize(NewSize: LongInt); begin SetSize(Int64(NewSize)); end; @@ -917,12 +984,12 @@ begin FSize := FMaxSize; end; -function TPointersStream.Read(var Buffer; Count: Longint): Longint; +function TPointersStream.Read(var Buffer; Count: LongInt): LongInt; begin // 2121212 end; -function TPointersStream.Write(const Buffer; Count: Longint): Longint; +function TPointersStream.Write(const Buffer; Count: LongInt): LongInt; begin end; @@ -986,7 +1053,7 @@ begin inherited Destroy; end; -procedure TMemoryStreamEx.SetSize(NewSize: Longint); +procedure TMemoryStreamEx.SetSize(NewSize: LongInt); begin SetSize(Int64(NewSize)); end; @@ -1002,7 +1069,7 @@ begin Seek(0, soEnd); end; -function TMemoryStreamEx.Read(var Buffer; Count: Longint): Longint; +function TMemoryStreamEx.Read(var Buffer; Count: LongInt): LongInt; begin Result := 0; if (FPosition >= 0) and (Count >= 0) then @@ -1019,9 +1086,9 @@ begin end; end; -function TMemoryStreamEx.Write(const Buffer; Count: Longint): Longint; +function TMemoryStreamEx.Write(const Buffer; Count: LongInt): LongInt; var - FCount: Longint; + FCount: LongInt; begin Result := 0; FCount := Count; @@ -1077,6 +1144,210 @@ begin SetSize(LSize); end; +constructor TDirInputStream.Create(const APath: String); +begin + inherited Create; + FState := TState.iNone; + FPath := TPath.GetFullPath(APath); + if FileExists(FPath) then + FBaseDir := ExtractFilePath(TPath.GetFullPath(FPath)) + else if DirectoryExists(FPath) then + FBaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(FPath)) + else + FBaseDir := ExtractFilePath(TPath.GetFullPath(FPath)); + FList := GetFileList([FPath], True); + FCount := Length(FList); + if FCount = 0 then + raise EFOpenError.CreateRes(@SEmptyPath); + FIndex := -1; + FStream := nil; +end; + +destructor TDirInputStream.Destroy; +begin + if Assigned(FStream) then + FStream.Free; + FStream := nil; + inherited Destroy; +end; + +function TDirInputStream.Read(var Buffer; Count: LongInt): LongInt; +var + LCount: Integer; +begin + Result := 0; + if Count <= 0 then + exit; + if FState = TState.iNone then + begin + if Succ(FIndex) >= FCount then + exit; + Inc(FIndex); + FBytes := BytesOf(ReplaceText(FList[FIndex], FBaseDir, '')); + FLength := Length(FBytes); + FPosition := 0; + FSize := FileSize(FList[FIndex]); + FState := TState.iLength; + end; + if FState = TState.iLength then + if FPosition < FLength.Size then + begin + LCount := Min(FLength.Size - FPosition, Count); + Move(WordRec(FLength).Bytes[FPosition], Buffer, LCount); + Inc(FPosition, LCount); + if FPosition = FLength.Size then + begin + FState := TState.iFilename; + FPosition := 0; + end; + exit(LCount); + end; + if FState = TState.iFilename then + if FPosition < FLength then + begin + LCount := Min(FLength - FPosition, Count); + Move(FBytes[FPosition], Buffer, LCount); + Inc(FPosition, LCount); + if FPosition = FLength then + begin + FState := TState.iSize; + FPosition := 0; + end; + exit(LCount); + end; + if FState = TState.iSize then + if FPosition < FSize.Size then + begin + LCount := Min(FSize.Size - FPosition, Count); + Move(Int64Rec(FSize).Bytes[FPosition], Buffer, LCount); + Inc(FPosition, LCount); + if FPosition = FSize.Size then + begin + if FSize = 0 then + FState := TState.iNone + else + begin + FState := TState.iData; + FPosition := 0; + FStream := TFileStream.Create(FList[FIndex], fmShareDenyNone); + end; + end; + exit(LCount); + end; + if FState = TState.iData then + if FPosition < FSize then + begin + LCount := Min(FSize - FPosition, Count); + LCount := FStream.Read(Buffer, LCount); + Inc(FPosition, LCount); + if FPosition = FSize then + begin + FState := TState.iNone; + FStream.Free; + FStream := nil; + end; + exit(LCount); + end; +end; + +constructor TDirOutputStream.Create(const APath: String); +begin + inherited Create; + FState := TState.oNone; + FPath := IncludeTrailingBackSlash(TPath.GetFullPath(APath)); + FStream := nil; +end; + +destructor TDirOutputStream.Destroy; +begin + if Assigned(FStream) then + FStream.Free; + FStream := nil; + inherited Destroy; +end; + +function TDirOutputStream.Write(const Buffer; Count: LongInt): LongInt; +var + LCount: Integer; + LStr: String; +begin + Result := 0; + if Count <= 0 then + exit; + if FState = TState.oNone then + begin + FPosition := 0; + FState := TState.oLength; + end; + if FState = TState.oLength then + if FPosition < FLength.Size then + begin + LCount := Min(FLength.Size - FPosition, Count); + Move(Buffer, WordRec(FLength).Bytes[FPosition], LCount); + Inc(FPosition, LCount); + if FPosition = FLength.Size then + begin + SetLength(FBytes, FLength); + FState := TState.oFilename; + FPosition := 0; + end; + exit(LCount); + end; + if FState = TState.oFilename then + if FPosition < FLength then + begin + LCount := Min(FLength - FPosition, Count); + Move(Buffer, FBytes[FPosition], LCount); + Inc(FPosition, LCount); + if FPosition = FLength then + begin + FState := TState.oSize; + FPosition := 0; + end; + exit(LCount); + end; + if FState = TState.oSize then + if FPosition < FSize.Size then + begin + LCount := Min(FSize.Size - FPosition, Count); + Move(Buffer, Int64Rec(FSize).Bytes[FPosition], LCount); + Inc(FPosition, LCount); + if FPosition = FSize.Size then + begin + LStr := FPath + StringOf(FBytes); + if not DirectoryExists(ExtractFilePath(LStr)) then + ForceDirectories(ExtractFilePath(LStr)); + FStream := TFileStream.Create(LStr, fmCreate); + if FSize = 0 then + begin + FState := TState.oNone; + FStream.Free; + FStream := nil; + end + else + begin + FState := TState.oData; + FPosition := 0; + end; + end; + exit(LCount); + end; + if FState = TState.oData then + if FPosition < FSize then + begin + LCount := Min(FSize - FPosition, Count); + LCount := FStream.Write(Buffer, LCount); + Inc(FPosition, LCount); + if FPosition = FSize then + begin + FState := TState.oNone; + FStream.Free; + FStream := nil; + end; + exit(LCount); + end; +end; + constructor TSharedMemoryStream.Create(const AMapName: String; AFileName: string); @@ -1191,7 +1462,7 @@ begin inherited SetSize(NewSize); end; -function TSharedMemoryStream.Write(const Buffer; Count: Longint): Longint; +function TSharedMemoryStream.Write(const Buffer; Count: LongInt): LongInt; begin if FPosition + Count > FMaxSize then IncMemory(FPosition + Count); @@ -1227,7 +1498,7 @@ begin Abort := True; end; -function TDownloadStream.Read(var Buffer; Count: Longint): Longint; +function TDownloadStream.Read(var Buffer; Count: LongInt): LongInt; begin if (FPosition >= 0) and (Count >= 0) then begin @@ -1779,107 +2050,132 @@ begin FSizes[Index] := 0; end; -constructor TDataManager.Create(AStream: TStream; ACapacity: Integer); +constructor TDataManager.Create(AStream: TStream); begin inherited Create; - FSync.Init; - FDictionary := TSynDictionary.Create(TypeInfo(TIntegerDynArray), - TypeInfo(TBlockInfoDynArray)); - FDictionary.Capacity := ACapacity; FStream := AStream; FStreamPos := FStream.Position; + FStreamSize := 0; end; destructor TDataManager.Destroy; begin - FDictionary.Free; - FSync.Done; inherited Destroy; end; procedure TDataManager.Add(ID: Integer; Size: Int64; Count: Integer); var - LBlockInfo: TBlockInfo; - _BlockInfo: PBlockInfo; I: Integer; + LBlockInfo: TBlockInfo; begin if Count <= 0 then exit; - FSync.Lock; - try - LBlockInfo.Position := 0; - for I := 0 to FDictionary.Count - 1 do + for I := Low(FSearchList) to High(FSearchList) do + begin + if (FSearchList[I].Count <= 0) and (Size <= FSearchList[I].FullSize) then begin - _BlockInfo := PBlockInfo(FDictionary.Values.ElemPtr(I)); - LBlockInfo.Position := Max(LBlockInfo.Position, _BlockInfo^.Position + - _BlockInfo^.FullSize); + FSearchList[I].ID := ID; + FSearchList[I].CurrSize := 0; + FSearchList[I].Count := Count; + exit; end; - LBlockInfo.CurrSize := 0; - LBlockInfo.FullSize := Size; - LBlockInfo.Count := Count; - FStream.Size := Max(FStream.Size, FStreamPos + LBlockInfo.Position + - LBlockInfo.FullSize); - FDictionary.Add(ID, LBlockInfo); - finally - FSync.UnLock; end; - // once reads reaches 0, add list of all available spaces + LBlockInfo.ID := ID; + LBlockInfo.Position := FStreamPos + FStreamSize; + LBlockInfo.CurrSize := 0; + LBlockInfo.FullSize := Size; + LBlockInfo.Count := Count; + Insert(LBlockInfo, FSearchList, Length(FSearchList)); + Inc(FStreamSize, Size); end; -procedure TDataManager.Write(ID: Integer; const Buffer; Size: Integer); +procedure TDataManager.Write(ID: Integer; Buffer: Pointer; Size: Integer); var - _BlockInfo: PBlockInfo; + I: Integer; begin if Size <= 0 then exit; - FSync.Lock; - try - _BlockInfo := FDictionary.FindValue(ID); - if (_BlockInfo^.Position + _BlockInfo^.CurrSize + Size) > - (_BlockInfo^.Position + _BlockInfo^.FullSize) then - raise EWriteError.CreateRes(@SWriteError); - FStream.Position := FStreamPos + _BlockInfo^.Position + - _BlockInfo^.CurrSize; - FStream.WriteBuffer(Buffer, Size); - Inc(_BlockInfo^.CurrSize, Size); - finally - FSync.UnLock; + for I := Low(FSearchList) to High(FSearchList) do + begin + if (ID = FSearchList[I].ID) and (FSearchList[I].Count > 0) then + begin + if FSearchList[I].CurrSize + Size > FSearchList[I].FullSize then + raise EWriteError.CreateRes(@SWriteError); + FStream.Position := FSearchList[I].Position + FSearchList[I].CurrSize; + FStream.WriteBuffer(Buffer^, Size); + Inc(FSearchList[I].CurrSize, Size); + exit; + end; end; + raise Exception.CreateRes(@SGenericItemNotFound); end; procedure TDataManager.CopyData(ID: Integer; Stream: TStream); var - _BlockInfo: PBlockInfo; + I: Integer; begin - FSync.Lock; - try - _BlockInfo := FDictionary.FindValue(ID); - if _BlockInfo^.CurrSize <> _BlockInfo^.FullSize then - raise EReadError.CreateRes(@SReadError); - FStream.Position := FStreamPos + _BlockInfo^.Position; - CopyStreamEx(FStream, Stream, _BlockInfo^.FullSize); - Dec(_BlockInfo^.Count); - finally - FSync.UnLock; + for I := Low(FSearchList) to High(FSearchList) do + begin + if (ID = FSearchList[I].ID) and (FSearchList[I].Count > 0) then + begin + FStream.Position := FSearchList[I].Position; + CopyStreamEx(FStream, Stream, FSearchList[I].CurrSize); + Dec(FSearchList[I].Count); + exit; + end; end; + raise Exception.CreateRes(@SGenericItemNotFound); end; -function TDataManager.CopyData(ID: Integer): Pointer; +function TDataManager.CopyData(ID: Integer; Data: Pointer): Integer; var - _BlockInfo: PBlockInfo; + I: Integer; begin - FSync.Lock; - try - _BlockInfo := FDictionary.FindValue(ID); - if _BlockInfo^.CurrSize <> _BlockInfo^.FullSize then - raise EReadError.CreateRes(@SReadError); - FStream.Position := FStreamPos + _BlockInfo^.Position + - _BlockInfo^.CurrSize; - FStream.ReadBuffer(Result^, _BlockInfo^.FullSize); - Dec(_BlockInfo^.Count); - finally - FSync.UnLock; + Result := 0; + for I := Low(FSearchList) to High(FSearchList) do + begin + if (ID = FSearchList[I].ID) and (FSearchList[I].Count > 0) then + begin + FStream.Position := FSearchList[I].Position; + FStream.ReadBuffer(Data^, FSearchList[I].CurrSize); + Result := FSearchList[I].CurrSize; + Dec(FSearchList[I].Count); + if FSearchList[I].Count = 0 then + FSearchList[I].ID := -1; + exit; + end; end; + raise Exception.CreateRes(@SGenericItemNotFound); +end; + +procedure TDataManager.Update(ID: Integer; Count: Integer); +var + I: Integer; +begin + for I := Low(FSearchList) to High(FSearchList) do + begin + if (ID = FSearchList[I].ID) then + begin + FSearchList[I].Count := Count; + exit; + end; + end; + raise Exception.CreateRes(@SGenericItemNotFound); +end; + +procedure TDataManager.Reset(ID: Integer); +var + I: Integer; +begin + for I := Low(FSearchList) to High(FSearchList) do + begin + if (ID = FSearchList[I].ID) and (FSearchList[I].Count > 0) then + begin + FSearchList[I].CurrSize := 0; + exit; + end; + end; + raise Exception.CreateRes(@SGenericItemNotFound); end; constructor TArgParser.Create(Arguments: TStringDynArray); diff --git a/imports/BrunsliDLL.pas b/imports/BrunsliDLL.pas new file mode 100644 index 0000000..13bb944 --- /dev/null +++ b/imports/BrunsliDLL.pas @@ -0,0 +1,87 @@ +unit BrunsliDLL; + +interface + +uses + WinAPI.Windows, + System.SysUtils, System.Classes; + +const + BRUNSLI_OK = 0; + BRUNSLI_NON_REPRESENTABLE = 1; + BRUNSLI_MEMORY_ERROR = 2; + BRUNSLI_INVALID_PARAM = 3; + BRUNSLI_COMPRESSION_ERROR = 4; + BRUNSLI_INVALID_BRN = 5; + BRUNSLI_DECOMPRESSION_ERROR = 6; + BRUNSLI_NOT_ENOUGH_DATA = 7; + +type + TBrunsliWriter = function(ctx: Pointer; data: Pointer; Size: NativeUInt) + : Integer cdecl; + +var + brunsli_alloc_JPEGData: function: Pointer cdecl; + brunsli_free_JPEGData: procedure(P: Pointer)cdecl; + brunsli_GetMaximumEncodedSize: function(P: Pointer): Integer cdecl; + brunsli_ReadJpeg: function(P: Pointer; data: Pointer; len: Integer) + : Integer cdecl; + brunsli_EncodeJpeg: function(P: Pointer; data: Pointer; len: Integer) + : Integer cdecl; + brunsli_DecodeJpeg: function(P: Pointer; data: Pointer; len: Integer) + : Integer cdecl; + brunsli_alloc_JPEGOutput: function(P: TBrunsliWriter; data: Pointer) + : Pointer cdecl; + brunsli_free_JPEGOutput: procedure(P: Pointer)cdecl; + brunsli_WriteJpeg: function(P: Pointer; oup: Pointer): Integer cdecl; + DLLLoaded: Boolean = False; + +implementation + +var + DLLHandle: THandle; + S: String; + +procedure Init; +begin + S := 'brunsli.dll'; + DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + S)); + if DLLHandle >= 32 then + begin + @brunsli_alloc_JPEGData := GetProcAddress(DLLHandle, + 'brunsli_alloc_JPEGData'); + @brunsli_free_JPEGData := GetProcAddress(DLLHandle, + 'brunsli_free_JPEGData'); + @brunsli_GetMaximumEncodedSize := GetProcAddress(DLLHandle, + 'brunsli_GetMaximumEncodedSize'); + @brunsli_ReadJpeg := GetProcAddress(DLLHandle, 'brunsli_ReadJpeg'); + @brunsli_EncodeJpeg := GetProcAddress(DLLHandle, 'brunsli_EncodeJpeg'); + @brunsli_DecodeJpeg := GetProcAddress(DLLHandle, 'brunsli_DecodeJpeg'); + @brunsli_alloc_JPEGOutput := GetProcAddress(DLLHandle, + 'brunsli_alloc_JPEGOutput'); + @brunsli_free_JPEGOutput := GetProcAddress(DLLHandle, + 'brunsli_free_JPEGOutput'); + @brunsli_WriteJpeg := GetProcAddress(DLLHandle, 'brunsli_WriteJpeg'); + DLLLoaded := Assigned(brunsli_alloc_JPEGData) and + Assigned(brunsli_alloc_JPEGOutput); + end + else + DLLLoaded := False; +end; + +procedure Deinit; +begin + if not DLLLoaded then + exit; + FreeLibrary(DLLHandle); +end; + +initialization + +Init; + +finalization + +Deinit; + +end. diff --git a/imports/FLACDLL.pas b/imports/FLACDLL.pas new file mode 100644 index 0000000..4c14445 --- /dev/null +++ b/imports/FLACDLL.pas @@ -0,0 +1,196 @@ +unit FLACDLL; + +interface + +uses + WinAPI.Windows, + System.SysUtils, System.Classes; + +const + FLAC__MAX_CHANNELS = 8; + FLAC__MAX_FIXED_ORDER = 4; + FLAC__MAX_LPC_ORDER = 32; + +type + TFLAC__FrameHeader = record + blocksize, sample_rate, channels: Cardinal; + channel_assignment: Integer; + bits_per_sample: Cardinal; + case number_type: Integer of + 0: + (frame_number: Cardinal; crc1: Byte); + 1: + (sample_number: UInt64; crc2: Byte); + end; + + PFLAC__EntropyCodingMethod_PartitionedRiceContents = ^ + TFLAC__EntropyCodingMethod_PartitionedRiceContents; + + TFLAC__EntropyCodingMethod_PartitionedRiceContents = record + parameters, raw_bits: PCardinal; + capacity_by_order: Cardinal; + end; + + TFLAC__EntropyCodingMethod_PartitionedRice = record + order: Cardinal; + contents: PFLAC__EntropyCodingMethod_PartitionedRiceContents; + end; + + TFLAC__EntropyCodingMethod = record + case ftype: Integer of + 0: + (partitioned_rice: TFLAC__EntropyCodingMethod_PartitionedRice); + end; + + TFLAC__Subframe_Fixed = record + entropy_coding_method: TFLAC__EntropyCodingMethod; + order: Cardinal; + warmup: array [0 .. FLAC__MAX_CHANNELS - 1] of Integer; + residual: PInteger; + end; + + TFLAC__Subframe_LPC = record + entropy_coding_method: TFLAC__EntropyCodingMethod; + order, qlp_coeff_precision: Cardinal; + quantization_level: Integer; + qlp_coeff, warmup: array [0 .. FLAC__MAX_LPC_ORDER - 1] of Integer; + residual: PInteger; + end; + + TFLAC__Subframe = record + case ftype: Integer of + 0: + (constant: Integer; wb1: Cardinal); + 1: + (fixed: TFLAC__Subframe_Fixed; wb2: Cardinal); + 2: + (lpc: TFLAC__Subframe_LPC; wb3: Cardinal); + 3: + (verbatim: PInteger; wb4: Cardinal); + end; + + PFLAC__Frame = ^TFLAC__Frame; + + TFLAC__Frame = record + header: TFLAC__FrameHeader; + subframes: array [0 .. FLAC__MAX_CHANNELS - 1] of TFLAC__Subframe; + footer: Word; + end; + +var + FLAC__stream_encoder_new: function: Pointer cdecl; + FLAC__stream_encoder_set_verify: function(encoder: Pointer; value: Boolean) + : Boolean cdecl; + FLAC__stream_encoder_set_compression_level: function(encoder: Pointer; + value: Cardinal): Boolean cdecl; + FLAC__stream_encoder_set_channels: function(encoder: Pointer; value: Cardinal) + : Boolean cdecl; + FLAC__stream_encoder_set_bits_per_sample: function(encoder: Pointer; + value: Cardinal): Boolean cdecl; + FLAC__stream_encoder_set_sample_rate: function(encoder: Pointer; + value: Cardinal): Boolean cdecl; + FLAC__stream_encoder_set_total_samples_estimate: function(encoder: Pointer; + value: UInt64): Boolean cdecl; + FLAC__stream_encoder_init_stream: function(encoder: Pointer; + write_callback, seek_callback, tell_callback, metadata_callback: Pointer; + client_data: Pointer): Integer cdecl; + FLAC__stream_encoder_init_file: function(encoder: Pointer; + filename: PAnsiChar; progress_callback: Pointer; client_data: Pointer) + : Integer cdecl; + FLAC__stream_encoder_process_interleaved: function(encoder: Pointer; + const buffer; samples: Cardinal): Boolean cdecl; + FLAC__stream_encoder_finish: function(encoder: Pointer): Boolean cdecl; + FLAC__stream_encoder_delete: procedure(encoder: Pointer)cdecl; + FLAC__stream_decoder_new: function: Pointer cdecl; + FLAC__stream_decoder_init_stream: function(decoder: Pointer; + read_callback, seek_callback, tell_callback, length_callback, eof_callback, + write_callback, metadata_callback, error_callback: Pointer; + client_data: Pointer): Integer cdecl; + FLAC__stream_decoder_init_file: function(decoder: Pointer; + filename: PAnsiChar; write_callback, metadata_callback, error_callback + : Pointer; client_data: Pointer): Integer cdecl; + FLAC__stream_decoder_get_channels: function(decoder: Pointer): Cardinal cdecl; + FLAC__stream_decoder_get_bits_per_sample: function(decoder: Pointer) + : Cardinal cdecl; + FLAC__stream_decoder_process_until_end_of_stream: function(decoder: Pointer) + : Boolean cdecl; + FLAC__stream_decoder_finish: function(encoder: Pointer): Boolean cdecl; + FLAC__stream_decoder_delete: procedure(encoder: Pointer)cdecl; + DLLLoaded: Boolean = False; + +implementation + +var + DLLHandle: THandle; + +procedure Init; +begin + DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + + 'libFLAC_dynamic.dll')); + if DLLHandle >= 32 then + begin + @FLAC__stream_encoder_new := GetProcAddress(DLLHandle, + 'FLAC__stream_encoder_new'); + @FLAC__stream_encoder_set_verify := GetProcAddress(DLLHandle, + 'FLAC__stream_encoder_set_verify'); + @FLAC__stream_encoder_set_channels := GetProcAddress(DLLHandle, + 'FLAC__stream_encoder_set_channels'); + @FLAC__stream_encoder_set_compression_level := + GetProcAddress(DLLHandle, 'FLAC__stream_encoder_set_compression_level'); + @FLAC__stream_encoder_set_bits_per_sample := + GetProcAddress(DLLHandle, 'FLAC__stream_encoder_set_bits_per_sample'); + @FLAC__stream_encoder_set_sample_rate := + GetProcAddress(DLLHandle, 'FLAC__stream_encoder_set_sample_rate'); + @FLAC__stream_encoder_set_total_samples_estimate := + GetProcAddress(DLLHandle, + 'FLAC__stream_encoder_set_total_samples_estimate'); + @FLAC__stream_encoder_init_stream := GetProcAddress(DLLHandle, + 'FLAC__stream_encoder_init_stream'); + @FLAC__stream_encoder_init_file := GetProcAddress(DLLHandle, + 'FLAC__stream_encoder_init_file'); + @FLAC__stream_encoder_process_interleaved := + GetProcAddress(DLLHandle, 'FLAC__stream_encoder_process_interleaved'); + @FLAC__stream_encoder_finish := GetProcAddress(DLLHandle, + 'FLAC__stream_encoder_finish'); + @FLAC__stream_encoder_delete := GetProcAddress(DLLHandle, + 'FLAC__stream_encoder_delete'); + @FLAC__stream_decoder_new := GetProcAddress(DLLHandle, + 'FLAC__stream_decoder_new'); + @FLAC__stream_decoder_init_stream := GetProcAddress(DLLHandle, + 'FLAC__stream_decoder_init_stream'); + @FLAC__stream_decoder_init_file := GetProcAddress(DLLHandle, + 'FLAC__stream_decoder_init_file'); + @FLAC__stream_decoder_get_channels := GetProcAddress(DLLHandle, + 'FLAC__stream_decoder_get_channels'); + @FLAC__stream_decoder_get_bits_per_sample := + GetProcAddress(DLLHandle, 'FLAC__stream_decoder_get_bits_per_sample'); + @FLAC__stream_decoder_process_until_end_of_stream := + GetProcAddress(DLLHandle, + 'FLAC__stream_decoder_process_until_end_of_stream'); + @FLAC__stream_decoder_finish := GetProcAddress(DLLHandle, + 'FLAC__stream_decoder_finish'); + @FLAC__stream_decoder_delete := GetProcAddress(DLLHandle, + 'FLAC__stream_decoder_delete'); + DLLLoaded := Assigned(FLAC__stream_encoder_new) and + Assigned(FLAC__stream_decoder_new); + end + else + DLLLoaded := False; +end; + +procedure Deinit; +begin + if not DLLLoaded then + exit; + FreeLibrary(DLLHandle); +end; + +initialization + +Init; + +finalization + +Deinit; + +end. diff --git a/imports/JoJpegDLL.pas b/imports/JoJpegDLL.pas new file mode 100644 index 0000000..e2c7741 --- /dev/null +++ b/imports/JoJpegDLL.pas @@ -0,0 +1,69 @@ +unit JoJpegDLL; + +interface + +uses + WinAPI.Windows, + System.SysUtils, System.Classes; + +const + jojpeg_Size = 51320000; + + jojpeg_Compress = 0; + jojpeg_Decompress = 1; + + jojpeg_enc_Input = 1; + jojpeg_enc_Output1 = 2; + jojpeg_enc_Output2 = 3; + + jojpeg_dec_Input1 = 1; + jojpeg_dec_Input2 = 3; + jojpeg_dec_Output = 2; + +var + jojpeg_Init: function(p: Pointer; f_DEC: integer): integer stdcall; + jojpeg_Quit: procedure(p: Pointer; f_DEC: integer)stdcall; + jojpeg_Loop: function(p: Pointer; f_DEC: integer): integer stdcall; + jojpeg_Getvalue: function(p: Pointer; f_DEC, typ: integer): Int64; + jojpeg_Addbuf: procedure(p: Pointer; f_DEC: integer; buf: Pointer; + bufsize, state: integer)stdcall; + DLLLoaded: boolean = False; + +implementation + +var + DLLHandle: THandle; + +procedure Init; +begin + DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + + 'jojpeg_dll.dll')); + if DLLHandle >= 32 then + begin + @jojpeg_Init := GetProcAddress(DLLHandle, 'jojpeg_Init'); + @jojpeg_Quit := GetProcAddress(DLLHandle, 'jojpeg_Quit'); + @jojpeg_Loop := GetProcAddress(DLLHandle, 'jojpeg_Loop'); + @jojpeg_Getvalue := GetProcAddress(DLLHandle, 'jojpeg_Getvalue'); + @jojpeg_Addbuf := GetProcAddress(DLLHandle, 'jojpeg_Addbuf'); + DLLLoaded := Assigned(jojpeg_Init); + end + else + DLLLoaded := False; +end; + +procedure Deinit; +begin + if not DLLLoaded then + exit; + FreeLibrary(DLLHandle); +end; + +initialization + +Init; + +finalization + +Deinit; + +end. diff --git a/imports/LZ4DLL.pas b/imports/LZ4DLL.pas index 58cf0f0..1eccf75 100644 --- a/imports/LZ4DLL.pas +++ b/imports/LZ4DLL.pas @@ -69,6 +69,8 @@ var LZ4F_compressFrame: function(dstBuffer: Pointer; dstCapacity: size_t; srcBuffer: Pointer; srcSize: size_t; preferencesPtr: PLZ4F_preferences_t) : size_t cdecl; + LZ4_compressHC2: function(const src: Pointer; dst: Pointer; srcSize: Integer; + compressionLevel: Integer): Integer cdecl; LZ4F_compressFrameBound: function(srcSize: size_t; preferencesPtr: PLZ4F_preferences_t): size_t cdecl; LZ4F_createDecompressionContext: function(out dctxPtr: LZ4F_dctx; @@ -104,6 +106,7 @@ begin @LZ4_compress_default := GetProcAddress(DLLHandle, 'LZ4_compress_default'); @LZ4_compress_fast := GetProcAddress(DLLHandle, 'LZ4_compress_fast'); @LZ4_compress_HC := GetProcAddress(DLLHandle, 'LZ4_compress_HC'); + @LZ4_compressHC2 := GetProcAddress(DLLHandle, 'LZ4_compressHC2'); @LZ4F_compressFrame := GetProcAddress(DLLHandle, 'LZ4F_compressFrame'); @LZ4F_compressFrameBound := GetProcAddress(DLLHandle, 'LZ4F_compressFrameBound'); diff --git a/imports/LZMADLL.pas b/imports/LZMADLL.pas new file mode 100644 index 0000000..58ac065 --- /dev/null +++ b/imports/LZMADLL.pas @@ -0,0 +1,28 @@ +unit LZMADLL; + +interface + +uses + WinAPI.Windows, + System.SysUtils; + +type + PFL2_inBuffer = ^FL2_inBuffer; + + FL2_inBuffer = record + src: Pointer; + size: size_t; + pos: size_t; + end; + + PFL2_outBuffer = ^FL2_outBuffer; + + FL2_outBuffer = record + dst: Pointer; + size: size_t; + pos: size_t; + end; + +implementation + +end. diff --git a/imports/PackJPGDLL.pas b/imports/PackJPGDLL.pas new file mode 100644 index 0000000..b1dbe99 --- /dev/null +++ b/imports/PackJPGDLL.pas @@ -0,0 +1,67 @@ +unit PackJPGDLL; + +interface + +uses + WinAPI.Windows, + System.SysUtils, System.Classes; + +const + pjglib_file = 0; + pjglib_memory = 1; + pjglib_handle = 2; + +var + pjglib_convert_stream2stream: function(msg: PAnsiChar): Boolean cdecl; + pjglib_convert_file2file: function(ain, aout, msg: PAnsiChar): Boolean cdecl; + pjglib_convert_stream2mem: function(out_file: PPAnsiChar; out_size: PCardinal; + msg: PAnsiChar): Boolean cdecl; + pjglib_init_streams: procedure(in_src: Pointer; in_type: Integer; + in_size: Integer; out_dest: Pointer; out_type: Integer)cdecl; + pjglib_version_info: function: PAnsiChar cdecl; + pjglib_short_name: function: PAnsiChar cdecl; + DLLLoaded: Boolean = False; + +implementation + +var + DLLHandle: THandle; + +procedure Init; +begin + DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + + 'packjpg_dll.dll')); + if DLLHandle >= 32 then + begin + @pjglib_convert_stream2stream := GetProcAddress(DLLHandle, + 'pjglib_convert_stream2stream'); + @pjglib_convert_file2file := GetProcAddress(DLLHandle, + 'pjglib_convert_file2file'); + @pjglib_convert_stream2mem := GetProcAddress(DLLHandle, + 'pjglib_convert_stream2mem'); + @pjglib_init_streams := GetProcAddress(DLLHandle, 'pjglib_init_streams'); + @pjglib_version_info := GetProcAddress(DLLHandle, 'pjglib_version_info'); + @pjglib_short_name := GetProcAddress(DLLHandle, 'pjglib_short_name'); + DLLLoaded := Assigned(pjglib_init_streams) and + Assigned(pjglib_convert_stream2stream); + end + else + DLLLoaded := False; +end; + +procedure Deinit; +begin + if not DLLLoaded then + exit; + FreeLibrary(DLLHandle); +end; + +initialization + +Init; + +finalization + +Deinit; + +end. diff --git a/imports/TTADLL.pas b/imports/TTADLL.pas new file mode 100644 index 0000000..12ba17e --- /dev/null +++ b/imports/TTADLL.pas @@ -0,0 +1,53 @@ +unit TTADLL; + +interface + +uses + WinAPI.Windows, + System.SysUtils, System.Classes; + +var + tta_encode: function(const src: Pointer; srcSize: PInteger; dst: Pointer; + dstCapacity: PInteger): Boolean cdecl; + tta_decode: function(const src: Pointer; srcSize: Integer; dst: Pointer; + dstCapacity: PInteger): Boolean cdecl; + tta_getsize: function(const src: Pointer; srcSize: Integer; + headerSize: PInteger): Integer cdecl; + DLLLoaded: Boolean = False; + +implementation + +var + DLLHandle: THandle; + +procedure Init; +begin + DLLHandle := LoadLibrary(PChar(ExtractFilePath(ParamStr(0)) + + 'libtta_dll.dll')); + if DLLHandle >= 32 then + begin + @tta_encode := GetProcAddress(DLLHandle, 'encode'); + @tta_decode := GetProcAddress(DLLHandle, 'decode'); + @tta_getsize := GetProcAddress(DLLHandle, 'getsize'); + DLLLoaded := Assigned(tta_encode) and Assigned(tta_decode); + end + else + DLLLoaded := False; +end; + +procedure Deinit; +begin + if not DLLLoaded then + exit; + FreeLibrary(DLLHandle); +end; + +initialization + +Init; + +finalization + +Deinit; + +end. diff --git a/io/IODecode.pas b/io/IODecode.pas index 4ee9769..a82389b 100644 --- a/io/IODecode.pas +++ b/io/IODecode.pas @@ -128,7 +128,7 @@ begin '_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename); try Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^, - LEntry.Size); + Min(SS2.Size, LEntry.Size)); finally SS2.Free; end; diff --git a/precompressor/PrecompCrypto.pas b/precompressor/PrecompCrypto.pas index a16fb9b..26a198c 100644 --- a/precompressor/PrecompCrypto.pas +++ b/precompressor/PrecompCrypto.pas @@ -91,7 +91,7 @@ begin DI2.OldSize := SI.NewSize; DI2.NewSize := SI.NewSize; Funcs^.LogScan1(CryptoCodecs[GetBits(SI.Option, 0, 5)], SI.Position, - SI.OldSize, SI.NewSize); + SI.OldSize, -1); Add(Instance, @SI, DI1.Codec, @DI2); end; end; @@ -108,10 +108,10 @@ begin exit; if (Res > 0) and (StreamInfo^.OldSize > 0) then begin - StreamInfo^.NewSize := StreamInfo^.OldSize; Output(Instance, Input, StreamInfo^.OldSize); + StreamInfo^.NewSize := StreamInfo^.OldSize; Funcs^.LogScan2(CryptoCodecs[GetBits(StreamInfo^.Option, 0, 5)], - StreamInfo^.OldSize, StreamInfo^.NewSize); + StreamInfo^.OldSize, -1); Result := True; end; end; @@ -143,7 +143,7 @@ begin end; Result := True; Funcs^.LogProcess(CryptoCodecs[GetBits(StreamInfo^.Option, 0, 5)], nil, - StreamInfo^.OldSize, StreamInfo^.NewSize, StreamInfo^.OldSize, Result); + StreamInfo^.OldSize, -1, -1, Result); end; end; @@ -175,7 +175,7 @@ begin Output(Instance, Input, StreamInfo.OldSize); Result := True; Funcs^.LogRestore(CryptoCodecs[GetBits(StreamInfo.Option, 0, 5)], nil, - StreamInfo.OldSize, StreamInfo.NewSize, StreamInfo.OldSize, Result); + StreamInfo.OldSize, -1, -1, Result); end; end; diff --git a/precompressor/PrecompEXE.pas b/precompressor/PrecompEXE.pas index 3c9cd8a..9dfca77 100644 --- a/precompressor/PrecompEXE.pas +++ b/precompressor/PrecompEXE.pas @@ -589,7 +589,7 @@ begin StreamInfo^.OldSize); Funcs^.LogProcess(PChar(Codec.Names[X]), nil, StreamInfo^.OldSize, StreamInfo^.NewSize, Res1, Result); - if Result = False then + if (Result = False) and (DIFF_TOLERANCE > 0) then begin Buffer := Funcs^.Allocator(Instance, Res1 + Max(StreamInfo^.OldSize, Res1)); diff --git a/precompressor/PrecompLZ4.pas b/precompressor/PrecompLZ4.pas index f51b8b0..ff9005a 100644 --- a/precompressor/PrecompLZ4.pas +++ b/precompressor/PrecompLZ4.pas @@ -24,12 +24,14 @@ const L_MAXSIZE = 16 * 1024 * 1024; L_BLOCKSIZE = 0; L_BLOCKDEPENDENCY = 0; + L_ACCELERATION = 1; var SOList: array of array [0 .. CODEC_COUNT - 1] of TSOList; CodecAvailable, CodecEnabled: TArray; LBlockSize: Integer = L_BLOCKSIZE; LBlockDependency: Integer = L_BLOCKDEPENDENCY; + LAcceleration: Integer = L_ACCELERATION; function LZ4Init(Command: PChar; Count: Integer; Funcs: PPrecompFuncs): Boolean; var @@ -57,6 +59,8 @@ begin if (CompareText(S, LZ4Codecs[LZ4_CODEC]) = 0) and LZ4DLL.DLLLoaded then begin CodecEnabled[LZ4_CODEC] := True; + if Funcs^.GetParam(Command, X, 'a') <> '' then + LAcceleration := StrToInt(Funcs^.GetParam(Command, X, 'a')); end else if (CompareText(S, LZ4Codecs[LZ4HC_CODEC]) = 0) and LZ4DLL.DLLLoaded then @@ -86,7 +90,7 @@ begin if SOList[X, LZ4_CODEC].Count = 0 then SOList[X, LZ4_CODEC].Update([1]); SetLength(Options, 0); - for I := 3 to 12 do + for I := 2 to 12 do Insert(I, Options, Length(Options)); for X := Low(SOList) to High(SOList) do if SOList[X, LZ4HC_CODEC].Count = 0 then @@ -118,6 +122,7 @@ begin Option^ := 0; SetBits(Option^, LBlockSize, 12, 2); SetBits(Option^, LBlockDependency, 14, 1); + SetBits(Option^, LAcceleration, 15, 7); I := 0; while Funcs^.GetCodec(Command, I, False) <> '' do begin @@ -125,6 +130,8 @@ begin if (CompareText(S, LZ4Codecs[LZ4_CODEC]) = 0) and LZ4DLL.DLLLoaded then begin SetBits(Option^, LZ4_CODEC, 0, 5); + if Funcs^.GetParam(Command, I, 'a') <> '' then + SetBits(Option^, StrToInt(Funcs^.GetParam(Command, I, 'a')), 15, 7); Result := True; end else if (CompareText(S, LZ4Codecs[LZ4HC_CODEC]) = 0) and LZ4DLL.DLLLoaded @@ -188,6 +195,7 @@ begin SetBits(SI.Option, X, 0, 5); SetBits(SI.Option, LBlockSize, 12, 2); SetBits(SI.Option, LBlockDependency, 14, 1); + SetBits(SI.Option, LAcceleration, 15, 7); if System.Pos(SPrecompSep2, DI1.Codec) > 0 then SI.Status := TStreamStatus.Predicted else @@ -230,8 +238,8 @@ begin end; if Res > StreamInfo^.OldSize then begin - StreamInfo^.NewSize := Res; Output(Instance, Buffer, Res); + StreamInfo^.NewSize := Res; Funcs^.LogScan2(LZ4Codecs[GetBits(StreamInfo^.Option, 0, 5)], StreamInfo^.OldSize, StreamInfo^.NewSize); Result := True; @@ -261,12 +269,13 @@ begin if StreamInfo^.Status = TStreamStatus.Predicted then if GetBits(StreamInfo^.Option, 5, 7) <> I then continue; + Params := ''; case X of LZ4_CODEC: begin - Params := ''; - Res1 := LZ4_compress_default(NewInput, Buffer, - StreamInfo^.NewSize, Y); + Params := 'a' + GetBits(StreamInfo^.Option, 15, 7).ToString; + Res1 := LZ4_compress_fast(NewInput, Buffer, StreamInfo^.NewSize, Y, + GetBits(StreamInfo^.Option, 15, 7)); end; LZ4HC_CODEC: begin @@ -296,7 +305,7 @@ begin break; end; if (Result = False) and ((StreamInfo^.Status = TStreamStatus.Predicted) or - (SOList[Instance][X].Count = 1)) then + (SOList[Instance][X].Count = 1)) and (DIFF_TOLERANCE > 0) then begin Buffer := Funcs^.Allocator(Instance, Res1 + Max(StreamInfo^.OldSize, Res1)); Res2 := PrecompEncodePatch(OldInput, StreamInfo^.OldSize, Buffer, Res1, @@ -332,14 +341,16 @@ begin X := GetBits(StreamInfo.Option, 0, 5); if BoolArray(CodecAvailable, False) or (CodecAvailable[X] = False) then exit; + Params := ''; Buffer := Funcs^.Allocator(Instance, LZ4F_compressFrameBound(StreamInfo.NewSize, nil)); case X of LZ4_CODEC: begin - Params := ''; - Res1 := LZ4_compress_default(Input, Buffer, StreamInfo.NewSize, - LZ4F_compressFrameBound(StreamInfo.NewSize, nil)); + Params := 'a' + GetBits(StreamInfo.Option, 15, 7).ToString; + Res1 := LZ4_compress_fast(Input, Buffer, StreamInfo.NewSize, + LZ4F_compressFrameBound(StreamInfo.NewSize, nil), + GetBits(StreamInfo.Option, 15, 7)); end; LZ4HC_CODEC: begin diff --git a/precompressor/PrecompLZO.pas b/precompressor/PrecompLZO.pas index 472fbcf..f090292 100644 --- a/precompressor/PrecompLZO.pas +++ b/precompressor/PrecompLZO.pas @@ -272,8 +272,8 @@ begin end; if Res > StreamInfo^.OldSize then begin - StreamInfo^.NewSize := Res; Output(Instance, Buffer, Res); + StreamInfo^.NewSize := Res; Funcs^.LogScan2(LZOCodecs[GetBits(StreamInfo^.Option, 0, 5)], StreamInfo^.OldSize, StreamInfo^.NewSize); Result := True; @@ -301,6 +301,7 @@ begin if StreamInfo^.Status = TStreamStatus.Predicted then if GetBits(StreamInfo^.Option, 5, 7) <> I then continue; + Params := ''; Res1 := StreamInfo^.NewSize; case X of LZO1X_CODEC: @@ -326,7 +327,7 @@ begin break; end; if (Result = False) and ((StreamInfo^.Status = TStreamStatus.Predicted) or - (SOList[Instance][X].Count = 1)) then + (SOList[Instance][X].Count = 1)) and (DIFF_TOLERANCE > 0) then begin Buffer := Funcs^.Allocator(Instance, Res1 + Max(StreamInfo^.OldSize, Res1)); Res2 := PrecompEncodePatch(OldInput, StreamInfo^.OldSize, Buffer, Res1, @@ -361,6 +362,7 @@ begin X := GetBits(StreamInfo.Option, 0, 5); if BoolArray(CodecAvailable, False) or (CodecAvailable[X] = False) then exit; + Params := ''; Buffer := Funcs^.Allocator(Instance, StreamInfo.NewSize); Res1 := StreamInfo.NewSize; case X of diff --git a/precompressor/PrecompMain.pas b/precompressor/PrecompMain.pas index b7a1cba..aa53630 100644 --- a/precompressor/PrecompMain.pas +++ b/precompressor/PrecompMain.pas @@ -7,7 +7,7 @@ interface uses Threading, Utils, SynCommons, ParseClass, ParseExpr, PrecompUtils, PrecompCrypto, PrecompZLib, PrecompLZ4, PrecompLZO, PrecompZSTD, - PrecompOodle, PrecompINI, PrecompSearch, PrecompDLL, PrecompEXE, + PrecompOodle, PrecompMedia, PrecompINI, PrecompSearch, PrecompDLL, PrecompEXE, WinAPI.Windows, WinAPI.ShlObj, System.SysUtils, System.Classes, System.SyncObjs, System.Math, System.Types, System.StrUtils, System.RTLConsts, System.TimeSpan, System.Diagnostics, @@ -24,8 +24,7 @@ type ChunkSize, Threads: Integer; Depth: Integer; LowMem: Boolean; - DBaseFile: String; - DedupFile: String; + DBaseFile, ExtractDir: String; end; PDecodeOptions = ^TDecodeOptions; @@ -34,7 +33,6 @@ type Method: String; ChunkCount, Threads: Integer; Depth: Integer; - DedupFile: String; DedupSysMem, DedupGPUMem: Int64; end; @@ -53,10 +51,10 @@ function PrecompReadFuture(Index: Integer; Position: NativeInt; Buffer: Pointer; procedure PrecompLogScan1(Codec: PChar; Position: Int64; InSize, OutSize: Integer)cdecl; procedure PrecompLogScan2(Codec: PChar; InSize, OutSize: Integer)cdecl; -procedure PrecompLogProcess(Codec, Method: PChar; - OriginalSize, InSize, OutSize: Integer; Status: Boolean)cdecl; -procedure PrecompLogRestore(Codec, Method: PChar; - OriginalSize, InSize, OutSize: Integer; Status: Boolean)cdecl; +procedure PrecompLogProcess(Codec, Method: PChar; Size1, Size2, Size3: Integer; + Status: Boolean)cdecl; +procedure PrecompLogRestore(Codec, Method: PChar; Size1, Size2, Size3: Integer; + Status: Boolean)cdecl; procedure PrecompLogPatch1(OldSize, NewSize, PatchSize: Integer; Status: Boolean)cdecl; procedure PrecompLogPatch2(OldSize, NewSize, PatchSize: Integer; @@ -101,10 +99,11 @@ var IntArray: array [0 .. 1] of Int64; Codecs: array of TPrecompressor; DBFile: String = ''; + ExtDir: String = ''; UseDB: Boolean = False; - DupFile: String = ''; StoreDD: Boolean = False; - DupGUID: TGUID; + VERBOSE: Boolean = False; + EXTRACT: Boolean = False; DupSysMem: Int64 = 0; EncInfo: TEncInfo; ConTask: TTask; @@ -131,11 +130,14 @@ begin WriteLn(ErrOutput, ''); WriteLn(ErrOutput, 'Advanced parameters:'); WriteLn(ErrOutput, - ' --dbase=# - use database (#=filename to save db, optional)'); + ' --dbase=# - use database (#=filename to save db, optional)'); WriteLn(ErrOutput, - ' --dedup=# - use stream deduplication (#=filename to save db, optional)'); + ' --dedup=# - use stream deduplication (#=filename to save db, optional)'); WriteLn(ErrOutput, - ' --mem=# - deduplication ram usage limit (#=size) [75p]'); + ' --mem=# - deduplication ram usage limit (#=size) [75p]'); + WriteLn(ErrOutput, + ' --diff=# - set xdelta threshold to accept streams [5p]'); + WriteLn(ErrOutput, ' --extract=# - extract streams to directory path'); WriteLn(ErrOutput, ''); end; @@ -182,16 +184,18 @@ begin if Options.DBaseFile <> '' then UseDB := True; StoreDD := ArgParse.AsBoolean('--dedup'); - Options.DedupFile := ArgParse.AsString('--dedup='); S := ArgParse.AsString('--diff=', 0, '5p'); S := ReplaceText(S, 'p', '%'); DIFF_TOLERANCE := Max(0.00, ExpParse.Evaluate(S)); VERBOSE := ArgParse.AsBoolean('--verbose'); + Options.ExtractDir := ArgParse.AsString('--extract='); + if Options.ExtractDir <> '' then + EXTRACT := DirectoryExists(Options.ExtractDir); finally ArgParse.Free; ExpParse.Free; end; - if VERBOSE then + if VERBOSE or EXTRACT then Options.Threads := 1; end; @@ -210,7 +214,6 @@ begin S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + CPUCount.ToString); Options.Threads := Max(1, Round(ExpParse.Evaluate(S))); - Options.DedupFile := ArgParse.AsString('--dedup='); S := ArgParse.AsString('--mem=', 0, '75p'); S := ReplaceText(S, 'KB', '* 1024^1'); S := ReplaceText(S, 'MB', '* 1024^2'); @@ -267,14 +270,13 @@ type StrIdx: TArray; end; - TDupRec = record - Dict: TSynDictionary; - Index: Integer; - end; - var - Database: TSynDictionary; - Duplicates1: array [0 .. 1] of TDupRec; + DBInfo: TArray>; + DBCount: TArray; + DDInfo: TArray>; + DDCount1: TArray; + DDList1: TArray; + DDIndex: Integer; ComVars1: TArray; Tasks: TArray; CurCodec: TArray; @@ -285,6 +287,7 @@ var Scanned1, Scanned2, Processed: TArray; LogInt: Integer; LogInt64: Int64; + LogPtr: Pointer; procedure CodecInit(Count: Integer; Method: String); var @@ -303,6 +306,7 @@ begin Insert(PrecompLZO.Codec, Codecs, Length(Codecs)); Insert(PrecompZSTD.Codec, Codecs, Length(Codecs)); Insert(PrecompOodle.Codec, Codecs, Length(Codecs)); + Insert(PrecompMedia.Codec, Codecs, Length(Codecs)); for X := Low(Codecs) to High(Codecs) do for Y := Low(Codecs[X].Names) to High(Codecs[X].Names) do Insert(Codecs[X].Names[Y], List, Length(List)); @@ -394,64 +398,101 @@ end; procedure PrecompLogScan1(Codec: PChar; Position: Int64; InSize, OutSize: Integer); +var + S: String; begin if not VERBOSE then exit; with ComVars1[CurDepth[0]] do begin + if OutSize < 0 then + S := '(%d)' + else + S := '(%d >> %d)'; if (OutSize > 0) and (Position < DataStore.Size(0)) and (MemOutput1[0].Position - CurPos1[0] = OutSize) then - WriteLn(ErrOutput, Format('[%d] Actual %s stream found at %s (%d >> %d)', + WriteLn(ErrOutput, Format('[%d] Actual %s stream found at %s ' + S, [CurDepth[0], Codec, (DataStore.Position(0) + Position).ToHexString, InSize, OutSize])) else - WriteLn(ErrOutput, - Format('[%d] Possible %s stream located at %s (%d >> %d)', + WriteLn(ErrOutput, Format('[%d] Possible %s stream located at %s ' + S, [CurDepth[0], Codec, (DataStore.Position(0) + Position).ToHexString, InSize, OutSize])); end; end; procedure PrecompLogScan2(Codec: PChar; InSize, OutSize: Integer); +var + S: String; begin if not VERBOSE then exit; - WriteLn(ErrOutput, Format('[%d] Confirmed %s stream at %s (%d >> %d)', + if OutSize < 0 then + S := '(%d)' + else + S := '(%d >> %d)'; + WriteLn(ErrOutput, Format('[%d] Confirmed %s stream at %s ' + S, [CurDepth[0], Codec, LogInt64.ToHexString, InSize, OutSize])); end; -procedure PrecompLogProcess(Codec, Method: PChar; - OriginalSize, InSize, OutSize: Integer; Status: Boolean); +procedure PrecompLogProcess(Codec, Method: PChar; Size1, Size2, Size3: Integer; + Status: Boolean); var - S: String; + S1, S2: String; begin - if not VERBOSE then - exit; - if Status then - S := '[%d] Processed %s stream at %s (%d >> %d >> %d)' + - IfThen(String(Method) <> '', ' using %s', '') + ' successfully' - else - S := '[%d] Processing %s stream at %s (%d >> %d >> %d)' + - IfThen(String(Method) <> '', ' using %s', '') + ' has failed'; - WriteLn(ErrOutput, Format(S, [CurDepth[0], Codec, LogInt64.ToHexString, - OriginalSize, InSize, OutSize, Method])); + if VERBOSE then + begin + if Size2 < 0 then + S1 := '(%d)' + else if Size3 < 0 then + S1 := '(%d >> %d)' + else + S1 := '(%d >> %d >> %d)'; + if Status then + S2 := '[%d] Processed %s stream at %s ' + S1 + + IfThen(String(Method) <> '', ' using ' + String(Method), '') + + ' successfully' + else + S2 := '[%d] Processing %s stream at %s ' + S1 + + IfThen(String(Method) <> '', ' using ' + String(Method), '') + + ' has failed'; + WriteLn(ErrOutput, Format(S2, [CurDepth[0], Codec, LogInt64.ToHexString, + Size1, Size2, Size3])); + end; + if EXTRACT and (CurDepth[0] = 0) then + begin + S1 := '%s_%s.raw'; + with TFileStream.Create(ExtDir + Format(S1, [LogInt64.ToHexString, Codec]), + fmCreate) do + try + WriteBuffer(LogPtr^, Size1); + finally + Free; + end; + end; end; -procedure PrecompLogRestore(Codec, Method: PChar; - OriginalSize, InSize, OutSize: Integer; Status: Boolean); +procedure PrecompLogRestore(Codec, Method: PChar; Size1, Size2, Size3: Integer; + Status: Boolean); var - S: String; + S1, S2: String; begin if not VERBOSE then exit; - if Status then - S := '[%d] Restored %s stream at %s (%d >> %d >> %d)' + - IfThen(String(Method) <> '', ' using %s', '') + ' successfully' + if Size2 < 0 then + S1 := '(%d)' + else if Size3 < 0 then + S1 := '(%d >> %d)' else - S := '[%d] Restoring %s stream at %s (%d >> %d >> %d)' + - IfThen(String(Method) <> '', ' using %s', '') + ' has failed'; - WriteLn(ErrOutput, Format(S, [CurDepth[0], Codec, LogInt64.ToHexString, - OriginalSize, InSize, OutSize, Method])); + S1 := '(%d >> %d >> %d)'; + if Status then + S2 := '[%d] Restored %s stream at %s ' + S1 + IfThen(String(Method) <> '', + ' using ' + String(Method), '') + ' successfully' + else + S2 := '[%d] Restoring %s stream at %s ' + S1 + IfThen(String(Method) <> '', + ' using ' + String(Method), '') + ' has failed'; + WriteLn(ErrOutput, Format(S2, [CurDepth[0], Codec, LogInt64.ToHexString, + Size1, Size2, Size3])); end; procedure PrecompLogPatch1(OldSize, NewSize, PatchSize: Integer; @@ -553,7 +594,9 @@ begin begin MemOutput1[Instance].Position := CurPos1[Instance]; exit; - end; + end + else if LCodec = CurCodec[Instance] then + LOption := Info^.Option; end else begin @@ -616,53 +659,130 @@ begin CurTransfer[Instance] := String(Codec); end; -function CheckDB(Dictionary: TSynDictionary; const StreamInfo: TEncodeSI; - var Database: TDatabase): Boolean; +function CheckDB(StreamInfo: TEncodeSI; Database: PDatabase): Boolean; var - DBKey: Int64; + A: Word; + I: Integer; + LCount: Integer; + DB: PDatabase; begin Result := False; - Int64Rec(DBKey).Lo := StreamInfo.Checksum; - Int64Rec(DBKey).Hi := StreamInfo.OldSize; - Result := Dictionary.FindAndCopy(DBKey, Database); -end; - -procedure AddDB(Dictionary: TSynDictionary; const StreamInfo: TEncodeSI; - const Database: TDatabase); -var - DBKey: Int64; -begin - Int64Rec(DBKey).Lo := StreamInfo.Checksum; - Int64Rec(DBKey).Hi := StreamInfo.OldSize; - Dictionary.AddOrUpdate(DBKey, Database); -end; - -function CheckDup(var DupRec: TDupRec; const StreamInfo: TEncodeSI; - var StreamKey, DupCount: Integer): Boolean; -var - DupKey: Int64; - DupInfo: PDuplicate; - DupAdded: Boolean; -begin - Result := False; - Inc(DupRec.Index); - Int64Rec(DupKey).Lo := StreamInfo.Checksum; - Int64Rec(DupKey).Hi := StreamInfo.OldSize; - DupInfo := DupRec.Dict.FindValueOrAdd(DupKey, DupAdded); - if not DupAdded then + A := LongRec(StreamInfo.Checksum).Lo; + AtomicExchange(LCount, DBCount[A]); + for I := 0 to LCount - 1 do begin + DB := @DBInfo[A, I]; + if (DB^.Size = StreamInfo.OldSize) and (DB^.Checksum = StreamInfo.Checksum) + then + begin + if Assigned(Database) then + Move(DB^, Database^, SizeOf(TDatabase)); + Result := True; + break; + end; + end; +end; + +procedure AddDB(StreamInfo: TEncodeSI); +var + A: Word; + I: Integer; + DB: TDatabase; +begin + A := LongRec(StreamInfo.Checksum).Lo; + if not CheckDB(StreamInfo, nil) then + begin + GlobalSync.Acquire; + try + DB.Size := StreamInfo.OldSize; + DB.Codec := StreamInfo.Codec; + DB.Option := StreamInfo.Option; + DB.Checksum := StreamInfo.Checksum; + DB.Status := StreamInfo.Status; + Insert(DB, DBInfo[A], Length(DBInfo[A])); + Inc(DBCount[A]); + finally + GlobalSync.Release; + end; + end; +end; + +function CheckDD(StreamInfo: TEncodeSI; Database: PDuplicate1; + Index: PInteger): Boolean; +var + A: Word; + I: Integer; + LCount: Integer; + DD: PDuplicate1; +begin + Result := False; + A := LongRec(StreamInfo.Checksum).Lo; + LCount := DDCount1[A]; + for I := 0 to LCount - 1 do + begin + DD := @DDInfo[A, I]; + if (DD^.Size = StreamInfo.OldSize) and (DD^.Checksum = StreamInfo.Checksum) + then + begin + if Assigned(Database) then + Move(DD^, Database^, SizeOf(TDuplicate1)); + if Assigned(Index) then + Index^ := I; + Result := True; + break; + end; + end; +end; + +function FindDD(StreamInfo: TEncodeSI; Index, Count: PInteger): Boolean; +var + A: Word; + I: Integer; + DD: PDuplicate1; +begin + Result := False; + if CheckDD(StreamInfo, nil, @I) then + begin + A := LongRec(StreamInfo.Checksum).Lo; + DD := @DDInfo[A, I]; + if Assigned(Index) then + Index^ := DD^.Index; + if Assigned(Count) then + Count^ := DD^.Count; + Result := True; + end; +end; + +function FindOrAddDD(StreamInfo: TEncodeSI; Index, Count: PInteger): Boolean; +var + A: Word; + I: Integer; + DD: TDuplicate1; + I64: Int64; +begin + Result := False; + Inc(DDIndex); + A := LongRec(StreamInfo.Checksum).Lo; + if not CheckDD(StreamInfo, nil, @I) then + begin + DD.Size := StreamInfo.OldSize; + DD.Checksum := StreamInfo.Checksum; + DD.Index := DDIndex; + DD.Count := 0; + I := Length(DDInfo[A]); + Insert(DD, DDInfo[A], I); + Int64Rec(I64).Words[0] := A; + Int64Rec(I64).Hi := DDCount1[A]; + Insert(I64, DDList1, Length(DDList1)); + Inc(DDCount1[A]); Result := True; - Inc(DupInfo^.Count); - StreamKey := DupInfo^.Index; - DupCount := DupInfo^.Count; end else - begin - DupInfo^.Count := 0; - DupInfo^.Index := DupRec.Index; - StreamKey := -1; - DupCount := 0; - end; + Inc(DDInfo[A, I].Count); + if Assigned(Index) then + Index^ := DDInfo[A, I].Index; + if Assigned(Count) then + Count^ := DDInfo[A, I].Count; end; procedure Scan1(Index, Depth: Integer); @@ -729,6 +849,8 @@ begin X := DataStore.ActualSize(Index) - NativeInt(SI2.Position - DataStore.Position(Index)); LogInt64 := SI2.Position; + LogPtr := PByte(DataStore.Slot(Index).Memory) + + NativeInt(SI2.Position - DataStore.Position(Index)); if (SI1.OldSize <= X) and Codecs[SI2.Codec].Scan2(Index, Depth, PByte(DataStore.Slot(Index).Memory) + NativeInt(SI2.Position - DataStore.Position(Index)), X, @SI1, @J, @@ -830,10 +952,11 @@ begin SI1.Resource := SI2.Resource; SI1.Option := SI2.Option; SI1.Status := SI2.Status; - LogInt64 := DataStore.Position(0) + SI2.ActualPosition; + LogInt64 := DataStore.Position(ThreadIndex) + SI2.ActualPosition; + LogPtr := PByte(DataStore.Slot(ThreadIndex).Memory) + SI2.ActualPosition; if UseDB and (SI2.Codec > 2) then begin - DBBool := CheckDB(Database, SI2, DBTyp); + DBBool := CheckDB(SI2, @DBTyp); if DBBool and (SI2.Codec = DBTyp.Codec) then begin if DBTyp.Status = TStreamStatus.Invalid then @@ -841,7 +964,7 @@ begin else begin SI1.Option := DBTyp.Option; - SI1.Status := TStreamStatus.Predicted; + SI1.Status := TStreamStatus.Database; end; end; end; @@ -882,13 +1005,14 @@ begin if UseDB then if not DBBool then begin - DBTyp.Codec := SI2.Codec; - DBTyp.Option := SI1.Option; if Result then - DBTyp.Status := TStreamStatus.Predicted + begin + SI2.Option := SI1.Option; + SI2.Status := TStreamStatus.Predicted + end else - DBTyp.Status := TStreamStatus.Invalid; - AddDB(Database, SI2, DBTyp); + SI2.Status := TStreamStatus.Invalid; + AddDB(SI2); end; if Result then begin @@ -1033,10 +1157,9 @@ procedure EncInit(Input, Output: TStream; Options: PEncodeOptions); var UI32: UInt32; I, J, K: Integer; + W: Word; Bytes: TBytes; NI: NativeInt; - DBKey: Int64; - DBTyp: TDatabase; S: String; DupMethod: Boolean; begin @@ -1046,15 +1169,21 @@ begin ThreadSync[I] := TCriticalSection.Create; I := XTOOL_PRECOMP; Output.WriteBuffer(I, I.Size); - CreateGUID(DupGUID); - Output.WriteBuffer(DupGUID, SizeOf(TGUID)); - Database := TSynDictionary.Create(TypeInfo(TInt64DynArray), - TypeInfo(TDatabaseDynArray)); - for I := Low(Duplicates1) to High(Duplicates1) do + if UseDB then begin - Duplicates1[I].Dict := TSynDictionary.Create(TypeInfo(TInt64DynArray), - TypeInfo(TDuplicateDynArray)); - Duplicates1[I].Index := -1; + SetLength(DBInfo, $10000); + SetLength(DBCount, $10000); + for I := Low(DBInfo) to High(DBInfo) do + DBCount[I] := 0; + end; + if StoreDD then + begin + SetLength(DDInfo, $10000); + SetLength(DDCount1, $10000); + SetLength(DDList1, 0); + for I := Low(DDInfo) to High(DDInfo) do + DDCount1[I] := 0; + DDIndex := -1; end; SetLength(Tasks, Options^.Threads); SetLength(CurCodec, Options^.Threads); @@ -1126,7 +1255,6 @@ begin end; CodecInit(Options^.Threads, Options^.Method); DBFile := Options^.DBaseFile; - DupFile := Options^.DedupFile; if FileExists(ExtractFilePath(Utils.GetModuleName) + DBFile) then begin with TFileStream.Create(ExtractFilePath(Utils.GetModuleName) + DBFile, @@ -1140,16 +1268,19 @@ begin end; with WorkStream[0] do begin - J := PInteger(Memory)^; - for I := 0 to J - 1 do + Position := 0; + while Position < Size do begin - NI := Integer.Size + (I * (SizeOf(Int64) + SizeOf(TDatabase))); - DBKey := PInt64(PByte(Memory) + NI)^; - DBTyp := PDatabase(PByte(Memory) + NI + SizeOf(Int64))^; - Database.Add(DBKey, DBTyp); + ReadBuffer(W, W.Size); + ReadBuffer(J, J.Size); + DBCount[W] := J; + SetLength(DBInfo[W], J); + for K := 0 to J - 1 do + ReadBuffer(DBInfo[W, K], SizeOf(TDatabase)); end; end; end; + ExtDir := IncludeTrailingBackSlash(Options^.ExtractDir); Output.WriteBuffer(Options^.Depth, Options^.Depth.Size); S := ''; I := 0; @@ -1237,9 +1368,6 @@ begin Tasks[I].Free; WorkStream[I].Free; end; - Database.Free; - for I := Low(Duplicates1) to High(Duplicates1) do - Duplicates1[I].Dict.Free; FreeResources; GlobalSync.Free; for I := Low(ThreadSync) to High(ThreadSync) do @@ -1265,19 +1393,19 @@ var StreamCount: Integer; BlockSize: Int64; UI32: UInt32; - I, J, X: Integer; + I, J, K, X: Integer; + W: Word; + I64: Int64; LastStream, LastPos: Int64; LastIndex: Integer; CurrSize: Cardinal; DupBool: Boolean; - DupKey, DupCount: Integer; - DBKey: Int64; - DBTyp: TDatabase; - DupTyp: TDuplicate; + DupIdx1, DupIdx2, DupCount: Integer; + DupTyp: TDuplicate2; begin if (Depth = 0) then begin - if (DupFile = '') and StoreDD then + if StoreDD then TempOutput := TPrecompVMStream.Create else TempOutput := Output; @@ -1285,6 +1413,7 @@ begin else TempOutput := Output; Result := False; + DupIdx1 := 0; with ComVars1[Depth] do begin LastStream := 0; @@ -1381,16 +1510,15 @@ begin begin Inc(StreamCount); DupBool := False; - if (Depth = 0) and ((DupFile <> '') or StoreDD) then - DupBool := CheckDup(Duplicates1[0], StreamInfo, DupKey, - DupCount); + if (Depth = 0) and StoreDD then + DupBool := not FindOrAddDD(StreamInfo, @DupIdx2, @DupCount); if DupBool then begin - if DupCount = 2 then + if DupCount = 1 then Inc(EncInfo.DecMem2, StreamInfo.OldSize); FillChar(StreamHeader, SizeOf(TStreamHeader), 0); StreamHeader.Kind := DUPLICATED_STREAM; - StreamHeader.Option := DupKey; + StreamHeader.Option := DupIdx2; end else begin @@ -1427,19 +1555,19 @@ begin Result := True else if Depth > 0 then exit; + I64 := MemStream[I].Position; MemStream[I].Position := 0; MemStream[I].WriteBuffer(StreamCount, StreamCount.Size); MemStream[I].WriteBuffer(BlockSize, BlockSize.Size); - TempOutput.WriteBuffer(MemStream[I].Memory^, MemStream[I].Position + - StreamCount * SizeOf(TStreamHeader)); + TempOutput.WriteBuffer(MemStream[I].Memory^, I64); InfoStore1[I].Index := LastIndex; J := InfoStore1[I].Get(StreamInfo); while J >= 0 do begin DupBool := False; - if (Depth = 0) and ((DupFile <> '') or StoreDD) then - DupBool := CheckDup(Duplicates1[1], StreamInfo, DupKey, DupCount); - if not DupBool then + if (Depth = 0) and StoreDD then + DupBool := FindDD(StreamInfo, @DupIdx2, @DupCount); + if (DupBool = False) or (DupIdx1 = DupIdx2) then begin if StreamInfo.ExtSize < 0 then begin @@ -1465,6 +1593,7 @@ begin StreamInfo.ExtSize.Size); end; end; + Inc(DupIdx1); if Succ(J - LastIndex) = StreamCount then break; J := InfoStore1[I].Get(StreamInfo); @@ -1519,14 +1648,16 @@ begin with WorkStream[0] do begin Position := 0; - J := Database.Count; - WriteBuffer(J, J.Size); - for I := 0 to J - 1 do + for W := 0 to $10000 - 1 do begin - DBKey := PInt64(Database.Keys.ElemPtr(I))^; - WriteBuffer(DBKey, SizeOf(Int64)); - DBTyp := PDatabase(Database.Values.ElemPtr(I))^; - WriteBuffer(DBTyp, SizeOf(TDatabase)); + J := DBCount[I]; + if J > 0 then + begin + WriteBuffer(W, W.Size); + WriteBuffer(J, J.Size); + for K := 0 to J - 1 do + WriteBuffer(DBInfo[W, K], SizeOf(TDatabase)); + end; end; end; with TFileStream.Create(ExtractFilePath(Utils.GetModuleName) + DBFile, @@ -1536,40 +1667,27 @@ begin Free; end; end; - if (DupFile <> '') or StoreDD then + if StoreDD then begin - for I := Duplicates1[0].Dict.Count - 1 downto 0 do - begin - if PDuplicate(Duplicates1[0].Dict.Values.ElemPtr(I))^.Count < 1 then - Duplicates1[0].Dict.DeleteAt(I); - end; with WorkStream[0] do begin Position := 0; - WriteBuffer(DupGUID, SizeOf(TGUID)); - Duplicates1[0].Dict.Values.Sort(DuplicateSortCompare); - J := Duplicates1[0].Dict.Count; - WriteBuffer(J, J.Size); - for I := 0 to J - 1 do + UI32 := 0; + for I := Low(DDList1) to High(DDList1) do begin - DupTyp := PDuplicate(Duplicates1[0].Dict.Values.ElemPtr(I))^; - WriteBuffer(DupTyp, SizeOf(TDuplicate)); + J := Int64Rec(DDList1[I]).Words[0]; + X := Int64Rec(DDList1[I]).Hi; + if DDInfo[J, X].Count > 0 then + begin + DupTyp.Index := DDInfo[J, X].Index; + DupTyp.Count := DDInfo[J, X].Count; + WriteBuffer(DupTyp, SizeOf(TDuplicate2)); + Inc(UI32); + end; end; end; - if DupFile <> '' then - begin - with TFileStream.Create(ExtractFilePath(Utils.GetModuleName) + DupFile, - FSMode(FileExists(ExtractFilePath(Utils.GetModuleName) + DupFile))) do - begin - Position := Size; - WriteBuffer(WorkStream[0].Memory^, WorkStream[0].Position); - end; - end - else - Output.WriteBuffer(WorkStream[0].Memory^, WorkStream[0].Position); - end; - if (DupFile = '') and StoreDD then - begin + Output.WriteBuffer(UI32, UI32.Size); + Output.WriteBuffer(WorkStream[0].Memory^, WorkStream[0].Position); Output.CopyFrom(TempOutput, 0); TempOutput.Free; end; @@ -1633,10 +1751,9 @@ var NStream: TArrayStream; DataMgr: TDataManager; ComVars2: TArray; - Duplicates2: TSynDictionary; - DupIdx1: Integer; - DupIdx2: TArray; - DupBool: TArray; + DDList2: TArray; + DDCount2: Integer; + DDIndex1, DDIndex2: Integer; BlockPos: Int64; procedure PrecompOutput2(Instance: Integer; const Buffer: Pointer; @@ -1644,8 +1761,9 @@ procedure PrecompOutput2(Instance: Integer; const Buffer: Pointer; begin with ComVars2[CurDepth[Instance]] do DecOutput[Instance].WriteBuffer(Buffer^, Size); - if (CurDepth[Instance] = 0) and (DupBool[Instance]) then - DataMgr.Write(DupIdx2[Instance], Buffer^, Size); + if StoreDD and (CurDepth[Instance] = 0) then + if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) then + DataMgr.Write(DDIndex1, Buffer, Size); end; procedure PrecompOutput3(Instance: Integer; const Buffer: Pointer; @@ -1653,8 +1771,6 @@ procedure PrecompOutput3(Instance: Integer; const Buffer: Pointer; begin with ComVars2[CurDepth[Instance]] do MemOutput1[Instance].WriteBuffer(Buffer^, Size); - if (CurDepth[Instance] = 0) and (DupBool[Instance]) then - DataMgr.Write(DupIdx2[Instance], Buffer^, Size); end; procedure Restore(MT: Boolean; Index, Depth: Integer); @@ -1675,18 +1791,11 @@ begin while X < StreamCount[Index]^ do begin SH := PStreamHeader(MemStream1[Index].Memory) + X; - if (Depth = 0) then - begin - DupIdx2[Index] := DupIdx1 + X; - DupBool[Index] := Duplicates2.FindAndCopy(DupIdx2[Index], Y); - if DupBool[Index] then - DataMgr.Add(DupIdx2[Index], SH^.OldSize, Y); - end; if MT then begin LOutput := @PrecompOutput3; Pos := StreamInfo[Index]^.Pos[X]; - X64 := Pos + SH^.NewSize; + X64 := Pos + Max(SH^.OldSize, SH^.NewSize); while (BlockPos < X64) do begin if IsErrored(Tasks) or (BlockPos < 0) then @@ -1697,6 +1806,13 @@ begin end else begin + if StoreDD and (Depth = 0) then + begin + Inc(DDIndex1); + if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) + then + DataMgr.Add(DDIndex1, SH^.OldSize, DDList2[DDIndex2].Count); + end; LOutput := @PrecompOutput2; DecInput[Index].ReadBuffer(UI32, UI32.Size); if UI32 > 0 then @@ -1740,9 +1856,12 @@ begin end; CurCodec[Index] := SH^.Codec; CurDepth[Index] := Depth; + Y := GetBits(SI.Option, 0, 5); + if not InRange(Y, 0, Pred(Length(Codecs[SH^.Codec].Names))) then + Y := 0; if (Codecs[SH^.Codec].Restore(Index, Depth, Ptr1, Ptr2, SI, LOutput, @PrecompFunctions) = False) then - raise Exception.CreateFmt(SPrecompError3, [Codecs[SH^.Codec].Names[0]]); + raise Exception.CreateFmt(SPrecompError3, [Codecs[SH^.Codec].Names[Y]]); NStream.Update(0, CalcSysMem); if MT then begin @@ -1751,7 +1870,13 @@ begin StreamInfo[Index]^.Completed[X] := True; end else + begin + if StoreDD and (Depth = 0) then + if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) + then + Inc(DDIndex2); Inc(Pos, SH^.NewSize); + end; X := AtomicIncrement(StreamIdx[Index]^); end; end; @@ -1772,24 +1897,16 @@ var I, J: Integer; Bytes: TBytes; UI32: UInt32; - DupTyp: TDuplicate; - LStream: TStream; - LGUID: TGUID; - LResData: PResData; + DupTyp: TDuplicate1; + LResData: TResData; begin GlobalSync := TCriticalSection.Create; SetLength(ThreadSync, Options^.Threads); for I := Low(ThreadSync) to High(ThreadSync) do ThreadSync[I] := TCriticalSection.Create; DupSysMem := Options^.DedupSysMem; - NStream.Add(TypeInfo(TMemoryStream), CalcSysMem); - NStream.Add(TypeInfo(TPrecompVMStream)); - Duplicates2 := TSynDictionary.Create(TypeInfo(TIntegerDynArray), - TypeInfo(TIntegerDynArray)); - DupIdx1 := 0; - SetLength(DupIdx2, Options^.Threads); - SetLength(DupBool, Options^.Threads); - Input.ReadBuffer(DupGUID, SizeOf(TGUID)); + NStream.Add(TypeInfo(TMemoryStream) { , CalcSysMem } ); + // NStream.Add(TypeInfo(TPrecompVMStream)); Input.ReadBuffer(Options^.Depth, Options^.Depth.Size); Input.ReadBuffer(LongRec(I).Bytes[0], LongRec(I).Bytes[0].Size); SetLength(Bytes, LongRec(I).Bytes[0]); @@ -1798,15 +1915,14 @@ begin Input.ReadBuffer(I, I.Size); for J := 0 to I - 1 do begin - New(LResData); Input.ReadBuffer(LongRec(I).Bytes[0], LongRec(I).Bytes[0].Size); SetLength(Bytes, LongRec(I).Bytes[0]); Input.ReadBuffer(Bytes[0], LongRec(I).Bytes[0]); - LResData^.Name := StringOf(Bytes); - Input.ReadBuffer(LResData^.Size, LResData^.Size.Size); - GetMem(LResData^.Data, LResData^.Size); - Input.ReadBuffer(LResData^.Data^, LResData^.Size); - Insert(LResData^, Resources, Length(Resources)); + LResData.Name := StringOf(Bytes); + Input.ReadBuffer(LResData.Size, LResData.Size.Size); + GetMem(LResData.Data, LResData.Size); + Input.ReadBuffer(LResData.Data^, LResData.Size); + Insert(LResData, Resources, Length(Resources)); end; SetLength(Tasks, Options^.Threads); SetLength(CurCodec, Options^.Threads); @@ -1860,45 +1976,18 @@ begin end; end; Input.ReadBuffer(StoreDD, StoreDD.Size); - if StoreDD or FileExists(ExtractFilePath(Utils.GetModuleName) + - Options^.DedupFile) then + UI32 := 0; + if StoreDD then begin - if StoreDD then - LStream := Input - else - begin - LStream := TFileStream.Create(ExtractFilePath(Utils.GetModuleName) + - Options^.DedupFile, fmShareDenyNone); - LStream.Position := 0; - end; - while True do - begin - LStream.ReadBuffer(LGUID, SizeOf(TGUID)); - LStream.ReadBuffer(J, J.Size); - I := J * SizeOf(TDuplicate); - if CompareMem(@DupGUID, @LGUID, SizeOf(TGUID)) then - begin - if WorkStream[0].Size < I then - WorkStream[0].Size := I; - LStream.ReadBuffer(WorkStream[0].Memory^, I); - for I := 0 to J - 1 do - begin - DupTyp := (PDuplicate(WorkStream[0].Memory) + I)^; - Duplicates2.Add(DupTyp.Index, DupTyp.Count); - end; - break; - end - else if StoreDD then - raise EReadError.CreateRes(@SInvalidProperty) - else - LStream.Seek(I, TSeekOrigin.soCurrent); - if StoreDD or (LStream.Position >= LStream.Size) then - break; - end; - if not StoreDD then - LStream.Free; + Input.ReadBuffer(UI32, UI32.Size); + SetLength(DDList2, UI32); + DDCount2 := UI32; + for I := Low(DDList2) to High(DDList2) do + Input.ReadBuffer(DDList2[I], SizeOf(TDuplicate2)); + DDIndex1 := -1; + DDIndex2 := 0; end; - DataMgr := TDataManager.Create(NStream, Duplicates2.Count); + DataMgr := TDataManager.Create(NStream); end; procedure DecFree; @@ -1933,7 +2022,6 @@ begin WorkStream[I].Free; end; DataMgr.Free; - Duplicates2.Free; FreeResources; GlobalSync.Free; for I := Low(ThreadSync) to High(ThreadSync) do @@ -1981,8 +2069,17 @@ begin Inc(CurrPos, Max(StreamHeader^.OldSize, StreamHeader^.NewSize)); end; end; - if MemInput[Index].Size < BlockSize then - MemInput[Index].Size := BlockSize; + if (Depth = 0) and (Length(Tasks) > 1) and (StreamCount[Index]^ > 1) + then + begin + if MemInput[Index].Size < CurrPos then + MemInput[Index].Size := CurrPos; + end + else + begin + if MemInput[Index].Size < BlockSize then + MemInput[Index].Size := BlockSize; + end; MemInput[Index].Position := 0; StreamIdx[Index]^ := -1; if (Depth = 0) and (Length(Tasks) > 1) and (StreamCount[Index]^ > 1) @@ -1996,9 +2093,6 @@ begin for J := 0 to StreamCount[Index]^ - 1 do begin StreamHeader := PStreamHeader(MemStream1[Index].Memory) + J; - MemInput[Index].Size := Max(MemInput[Index].Size, - StreamInfo[Index]^.Pos[J] + Max(StreamHeader^.OldSize, - StreamHeader^.NewSize)); MemInput[Index].Position := StreamInfo[Index]^.Pos[J]; if CopyStream(DecInput[Index], MemInput[Index], StreamHeader^.NewSize) <> StreamHeader^.NewSize then @@ -2026,12 +2120,26 @@ begin if IsErrored(Tasks) then for I := Low(Tasks) to High(Tasks) do Tasks[I].RaiseLastError; + if StoreDD and (Depth = 0) then + begin + Inc(DDIndex1); + if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) + then + begin + DataMgr.Add(DDIndex1, StreamHeader^.OldSize, + DDList2[DDIndex2].Count); + DataMgr.Write(DDIndex1, + (PByte(MemInput[Index].Memory) + StreamInfo[Index]^.Pos[J]), + StreamHeader^.OldSize); + Inc(DDIndex2); + end; + end; if StreamHeader^.Kind and DUPLICATED_STREAM = DUPLICATED_STREAM then DataMgr.CopyData(StreamHeader^.Option, DecOutput[Index]) else DecOutput[Index].WriteBuffer ((PByte(MemInput[Index].Memory) + StreamInfo[Index]^.Pos[J])^, - (PStreamHeader(MemStream1[Index].Memory) + J)^.OldSize); + StreamHeader^.OldSize); end; WaitForAll(Tasks); end @@ -2041,8 +2149,6 @@ begin DecInput[Index].ReadBuffer(UI32, UI32.Size); if UI32 > 0 then CopyStreamEx(DecInput[Index], DecOutput[Index], UI32); - if Depth = 0 then - Inc(DupIdx1, StreamCount[Index]^); DecInput[Index].ReadBuffer(StreamCount[Index]^, StreamCount[Index]^.Size); end; end; diff --git a/precompressor/PrecompMedia.pas b/precompressor/PrecompMedia.pas new file mode 100644 index 0000000..5c7c616 --- /dev/null +++ b/precompressor/PrecompMedia.pas @@ -0,0 +1,1005 @@ +unit PrecompMedia; + +{$POINTERMATH ON} + +interface + +uses + BrunsliDLL, FLACDLL, PackJPGDLL, JoJpegDLL, XDeltaDLL, + Utils, + PrecompUtils, + System.SysUtils, System.Classes, System.Math; + +var + Codec: TPrecompressor; + +implementation + +const + MediaCodecs: array of PChar = ['flac', 'packjpg', 'brunsli', 'jojpeg']; + CODEC_COUNT = 4; + FLAC_CODEC = 0; + PACKJPG_CODEC = 1; + BRUNSLI_CODEC = 2; + JOJPEG_CODEC = 3; + +const + FLAC_LEVEL = 5; + J_WORKMEM = 262144; + +type + PFlacEncCD = ^TFlacEncCD; + + TFlacEncCD = record + Output: TMemoryStreamEx; + Error: Boolean; + end; + + PFlacDecCD = ^TFlacDecCD; + + TFlacDecCD = record + Input, Output: TMemoryStreamEx; + end; + +var + cctx, dctx: array of Pointer; + ccd: array of TFlacEncCD; + dcd: array of TFlacDecCD; + JJInst: TArray; + FlacLevel: Integer = FLAC_LEVEL; + CodecAvailable, CodecEnabled: TArray; + RestoreOut: TArray<_PrecompOutput>; + RestoreSize: TArray; + +const + RIFF_SIGN = $46464952; + WAVE_SIGN = $45564157; + fmt_SIGN = $20746D66; + data_SIGN = $61746164; + + WAVE_FORMAT_PCM = 1; + WAVE_FORMAT_EXTENSIBLE = $FFFE; + +type + PRIFF_hdr = ^TRIFF_hdr; + + TRIFF_hdr = packed record + chunk_id, chunk_size, form_type: Cardinal; + end; + + PWAVE_hdr = ^TWAVE_hdr; + + TWAVE_hdr = packed record + audio_format, num_channels: Word; + sample_rate, byte_rate: Cardinal; + block_align, bits_per_sample: Word; + end; + + PWAVE_subchunk_hdr = ^TWAVE_subchunk_hdr; + + TWAVE_subchunk_hdr = packed record + subchunk_id, subchunk_size: Cardinal; + end; + + PWAVE_subformat = ^TWAVE_subformat; + + TWAVE_subformat = packed record + f1: Cardinal; + f2, f3: Word; + f4: array [0 .. 7] of Byte; + end; + + PWAVE_ext_hdr = ^TWAVE_ext_hdr; + + TWAVE_ext_hdr = packed record + cb_size, valid_bits: Word; + ch_mask: Cardinal; + est: TWAVE_subformat; + end; + +function GetWAVEInfo(InBuff: PByte; InSize: NativeInt; wave_hdr: PWAVE_hdr; + chunk_size, header_size: PCardinal): Boolean; +var + Pos, LastPos: NativeInt; + riffhdr: TRIFF_hdr; + subchunk_hdr: TWAVE_subchunk_hdr; + wave_hdr_ex: TWAVE_ext_hdr; +begin + Result := False; + Pos := 0; + if InSize - Pos < SizeOf(TRIFF_hdr) then + exit; + Move((InBuff + Pos)^, riffhdr, SizeOf(TRIFF_hdr)); + Inc(Pos, SizeOf(TRIFF_hdr)); + if (riffhdr.chunk_id <> RIFF_SIGN) or (riffhdr.form_type <> WAVE_SIGN) then + exit; + while True do + begin + if InSize - Pos < SizeOf(TWAVE_subchunk_hdr) then + exit; + Move((InBuff + Pos)^, subchunk_hdr, SizeOf(TWAVE_subchunk_hdr)); + Inc(Pos, SizeOf(TWAVE_subchunk_hdr)); + if subchunk_hdr.subchunk_id = fmt_SIGN then + begin + LastPos := Pos; + if InSize - Pos < SizeOf(TWAVE_hdr) then + exit; + Move((InBuff + Pos)^, wave_hdr^, SizeOf(TWAVE_hdr)); + Inc(Pos, SizeOf(TWAVE_hdr)); + if wave_hdr^.audio_format = WAVE_FORMAT_EXTENSIBLE then + begin + if InSize - Pos < SizeOf(TWAVE_ext_hdr) then + exit; + Move((InBuff + Pos)^, wave_hdr_ex, SizeOf(TWAVE_ext_hdr)); + Inc(Pos, SizeOf(TWAVE_ext_hdr)); + wave_hdr^.audio_format := wave_hdr_ex.est.f1; + end + else if wave_hdr^.audio_format <> WAVE_FORMAT_PCM then + exit; + Pos := LastPos + subchunk_hdr.subchunk_size; + end + else if subchunk_hdr.subchunk_id = data_SIGN then + begin + Result := True; + chunk_size^ := subchunk_hdr.subchunk_size; + header_size^ := Pos; + exit; + end + else + Inc(Pos, subchunk_hdr.subchunk_size); + end; +end; + +function FlacEncoderWriteCB(encoder: Pointer; const Buffer; bytes: NativeUInt; + samples, current_frame: Cardinal; client_data: PFlacEncCD): Integer cdecl; +begin + try + client_data^.Output.WriteBuffer(Buffer, bytes); + except + client_data^.Error := True; + end; + Result := 0; +end; + +function FlacDecoderReadCB(decoder: Pointer; var Buffer; bytes: PNativeUInt; + client_data: PFlacDecCD): Integer cdecl; +begin + bytes^ := client_data^.Input.Read(Buffer, bytes^); + if bytes^ > 0 then + Result := 0 + else + Result := 1; +end; + +function FlacDecoderWriteCB(decoder: Pointer; frame: PFLAC__Frame; + Buffer: Pointer; client_data: PFlacDecCD): Integer cdecl; +var + I, J, X: Integer; +begin + for I := 0 to frame^.header.blocksize - 1 do + for J := 0 to frame^.header.channels - 1 do + begin + X := PInteger(PNativeInt(Buffer)[J])[I]; + client_data^.Output.WriteBuffer(X, frame^.header.bits_per_sample div 8); + end; + Result := 0; +end; + +procedure FlacDecoderErrorCB(decoder: Pointer; Status: Integer; + client_data: Pointer)cdecl; +begin + +end; + +function FlacEncode(ctx: Pointer; cd: PFlacEncCD; InBuff: PByte; + InSize: Integer; OutBuff: PByte; OutSize: Integer; + Level: Integer = 5): Integer; +const + READSIZE = 1000; +var + I, J, X: Integer; + Ptr: PByte; + wave_hdr: TWAVE_hdr; + byte_per_sample: Word; + data_size, hdr_size, smp_size: Cardinal; + ok: Boolean; + pcm: TArray; +begin + Result := 0; + ok := False; + if GetWAVEInfo(InBuff, InSize, @wave_hdr, @data_size, @hdr_size) then + begin + ok := True; + byte_per_sample := (wave_hdr.bits_per_sample + 7) div 8; + smp_size := wave_hdr.num_channels * byte_per_sample; + ok := ok and FLAC__stream_encoder_set_verify(ctx, True); + ok := ok and FLAC__stream_encoder_set_compression_level(ctx, Level); + ok := ok and FLAC__stream_encoder_set_channels(ctx, wave_hdr.num_channels); + ok := ok and FLAC__stream_encoder_set_bits_per_sample(ctx, + wave_hdr.bits_per_sample); + ok := ok and FLAC__stream_encoder_set_sample_rate(ctx, + wave_hdr.sample_rate); + end; + if ok then + try + cd^.Error := False; + cd^.Output.Update(OutBuff, OutSize); + cd^.Output.Size := 0; + if FLAC__stream_encoder_init_stream(ctx, @FlacEncoderWriteCB, nil, nil, + nil, cd) = 0 then + begin + Ptr := InBuff + hdr_size; + SetLength(pcm, READSIZE * wave_hdr.num_channels); + while True do + begin + X := 0; + for I := 0 to READSIZE - 1 do + begin + if NativeInt(Ptr + smp_size - InBuff) > hdr_size + data_size then + break; + Inc(X); + for J := 0 to wave_hdr.num_channels - 1 do + begin + if NativeInt(Ptr - InBuff) > hdr_size + data_size then + break; + pcm[I * wave_hdr.num_channels + J] := + GetBits(PInteger(Ptr)^, 0, Pred(wave_hdr.bits_per_sample)) - + IfThen(GetBits(PInteger(Ptr)^, Pred(wave_hdr.bits_per_sample), + 1) = 0, 0, 1 shl Pred(wave_hdr.bits_per_sample)); + Inc(Ptr, byte_per_sample); + end; + end; + ok := ok and FLAC__stream_encoder_process_interleaved(ctx, pcm[0], X); + if (NativeInt(Ptr + smp_size - InBuff) > hdr_size + data_size) or + (ok = False) then + break; + end; + end; + finally + FLAC__stream_encoder_finish(ctx); + if ok and (cd^.Error = False) then + Result := cd^.Output.Size; + end; +end; + +function FlacDecode(ctx: Pointer; cd: PFlacDecCD; InBuff: PByte; + InSize: Integer; OutBuff: PByte; OutSize: Integer): Integer; +var + ok: Boolean; +begin + Result := 0; + ok := False; + cd^.Input.Update(InBuff, InSize); + cd^.Input.Position := 0; + cd^.Input.Size := InSize; + cd^.Output.Update(OutBuff, OutSize); + cd^.Output.Size := 0; + try + if FLAC__stream_decoder_init_stream(ctx, @FlacDecoderReadCB, nil, nil, nil, + nil, @FlacDecoderWriteCB, nil, @FlacDecoderErrorCB, cd) = 0 then + begin + ok := True; + ok := ok and FLAC__stream_decoder_process_until_end_of_stream(ctx); + end; + finally + FLAC__stream_decoder_finish(ctx); + if ok then + Result := cd^.Output.Size; + end; +end; + +const + JPG_HEADER = $D8FF; + JPG_FOOTER = $D9FF; + +function GetJPEGInfo(InBuff: PByte; InSize: NativeInt; ImageSize: PCardinal; + IsProgressive: PBoolean): Boolean; +// https://github.com/schnaader/precomp-cpp/blob/master/precomp.cpp +var + Pos: NativeInt; + hasQuantTable: Boolean; + done, found: Boolean; + length: Integer; + isMarker: Boolean; + bytesRead: Cardinal; +begin + Result := False; + Pos := 0; + done := False; + found := False; + if (PWord(InBuff)^ = JPG_HEADER) and ((InBuff + 2)^ = $FF) then + begin + Pos := 3; + if (InBuff + Pos)^ in [$C0, $C2, $C4, $DB .. $FE] then + begin + hasQuantTable := (InBuff + Pos)^ = $DB; + IsProgressive^ := (InBuff + Pos)^ = $C2; + Pos := 2; + repeat + if (Pos + 5 > InSize) or ((InBuff + Pos)[0] <> $FF) then + break; + length := Integer((InBuff + Pos)[2]) * 256 + Integer((InBuff + Pos)[3]); + case (InBuff + Pos)[1] of + $DB: + begin + if (length <= 262) and (((length - 2) mod 65) = 0) and + ((InBuff + Pos)[4] <= 3) then + begin + hasQuantTable := True; + Inc(Pos, length + 2); + end + else + done := True; + end; + $C4: + begin + done := (((InBuff + Pos)[4] and $F) > 3) or + (((InBuff + Pos)[4] shr 4) > 1); + Inc(Pos, length + 2); + end; + $DA: + begin + found := hasQuantTable; + Inc(Pos, length + 2); + end; + $D9: + done := True; + $C2: + begin + IsProgressive^ := True; + Inc(Pos, length + 2); + end; + $C0: + begin + done := (InBuff + Pos)[4] <> $08; + Inc(Pos, length + 2); + end; + else + Inc(Pos, length + 2); + end; + until done; + if found then + while Pos < InSize - 1 do + begin + if PWord(InBuff + Pos)^ = JPG_FOOTER then + begin + ImageSize^ := Pos + 2; + Result := True; + break; + end; + Inc(Pos); + end; + end; + end; +end; + +function BrunsliWriter(cd: Pointer; data: Pointer; Size: NativeUInt) + : Integer cdecl; +begin + RestoreOut[PInteger(cd)^](PInteger(cd)^, data, Size); + Inc(RestoreSize[PInteger(cd)^], Size); + Result := Size; +end; + +function MediaInit(Command: PChar; Count: Integer; + Funcs: PPrecompFuncs): Boolean; +var + I: Integer; + Options: TArray; + S: String; + X, Y: Integer; +begin + Result := True; + SetLength(RestoreOut, Count); + SetLength(RestoreSize, Count); + for X := Low(CodecAvailable) to High(CodecAvailable) do + begin + CodecAvailable[X] := False; + CodecEnabled[X] := False; + end; + CodecAvailable[FLAC_CODEC] := FLACDLL.DLLLoaded; + CodecAvailable[PACKJPG_CODEC] := PackJPGDLL.DLLLoaded; + CodecAvailable[BRUNSLI_CODEC] := BrunsliDLL.DLLLoaded; + CodecAvailable[JOJPEG_CODEC] := JoJpegDLL.DLLLoaded; + X := 0; + while Funcs^.GetCodec(Command, X, False) <> '' do + begin + S := Funcs^.GetCodec(Command, X, False); + if (CompareText(S, MediaCodecs[FLAC_CODEC]) = 0) and FLACDLL.DLLLoaded then + begin + CodecEnabled[FLAC_CODEC] := True; + if Funcs^.GetParam(Command, X, 'l') <> '' then + FlacLevel := StrToInt(Funcs^.GetParam(Command, X, 'l')); + end + else if (CompareText(S, MediaCodecs[PACKJPG_CODEC]) = 0) and PackJPGDLL.DLLLoaded + then + CodecEnabled[PACKJPG_CODEC] := True + else if (CompareText(S, MediaCodecs[BRUNSLI_CODEC]) = 0) and BrunsliDLL.DLLLoaded + then + CodecEnabled[BRUNSLI_CODEC] := True + else if (CompareText(S, MediaCodecs[JOJPEG_CODEC]) = 0) and JoJpegDLL.DLLLoaded + then + CodecEnabled[JOJPEG_CODEC] := True;; + Inc(X); + end; + if CodecAvailable[FLAC_CODEC] then + begin + SetLength(cctx, Count); + SetLength(dctx, Count); + SetLength(ccd, Count); + SetLength(dcd, Count); + for X := Low(cctx) to High(cctx) do + begin + cctx[X] := FLAC__stream_encoder_new; + dctx[X] := FLAC__stream_decoder_new; + ccd[X].Output := TMemoryStreamEx.Create(False); + dcd[X].Input := TMemoryStreamEx.Create(False); + dcd[X].Output := TMemoryStreamEx.Create(False); + end; + end; + if CodecAvailable[JOJPEG_CODEC] then + begin + SetLength(JJInst, Count); + for X := Low(JJInst) to High(JJInst) do + JJInst[X] := GetMemory(jojpeg_Size); + end; +end; + +procedure MediaFree(Funcs: PPrecompFuncs); +var + X: Integer; +begin + if CodecAvailable[FLAC_CODEC] then + begin + for X := Low(cctx) to High(cctx) do + begin + FLAC__stream_encoder_delete(cctx[X]); + FLAC__stream_decoder_delete(dctx[X]); + ccd[X].Output.Free; + dcd[X].Input.Free; + dcd[X].Output.Free; + end; + end; + if CodecAvailable[JOJPEG_CODEC] then + for X := Low(JJInst) to High(JJInst) do + FreeMemory(JJInst[X]); +end; + +function MediaParse(Command: PChar; Option: PInteger; + Funcs: PPrecompFuncs): Boolean; +var + S: String; + I: Integer; +begin + Result := False; + Option^ := 0; + I := 0; + while Funcs^.GetCodec(Command, I, False) <> '' do + begin + S := Funcs^.GetCodec(Command, I, False); + if (CompareText(S, MediaCodecs[FLAC_CODEC]) = 0) and FLACDLL.DLLLoaded then + begin + SetBits(Option^, FLAC_CODEC, 0, 5); + Result := True; + end + else if (CompareText(S, MediaCodecs[PACKJPG_CODEC]) = 0) and PackJPGDLL.DLLLoaded + then + begin + SetBits(Option^, PACKJPG_CODEC, 0, 5); + Result := True; + end + else if (CompareText(S, MediaCodecs[BRUNSLI_CODEC]) = 0) and BrunsliDLL.DLLLoaded + then + begin + SetBits(Option^, BRUNSLI_CODEC, 0, 5); + Result := True; + end + else if (CompareText(S, MediaCodecs[JOJPEG_CODEC]) = 0) and JoJpegDLL.DLLLoaded + then + begin + SetBits(Option^, JOJPEG_CODEC, 0, 5); + Result := True; + end; + Inc(I); + end; +end; + +procedure MediaScan1(Instance, Depth: Integer; Input: PByte; + Size, SizeEx: NativeInt; Output: _PrecompOutput; Add: _PrecompAdd; + Funcs: PPrecompFuncs); +var + Buffer: PByte; + Pos: NativeInt; + X, Y, Z: Integer; + SI: _StrInfo1; + DI1, DI2: TDepthInfo; + DS: TPrecompStr; + wave_hdr: TWAVE_hdr; + data_size, hdr_size: Cardinal; + progressive: Boolean; +begin + DI1 := Funcs^.GetDepthInfo(Instance); + DS := Funcs^.GetCodec(DI1.Codec, 0, False); + if DS <> '' then + begin + X := IndexTextW(@DS[0], MediaCodecs); + if (X < 0) or (DI1.OldSize <> SizeEx) then + exit; + if not CodecAvailable[X] then + exit; + Y := DI1.OldSize; + case X of + FLAC_CODEC: + begin + if GetWAVEInfo(Input, Y, @wave_hdr, @data_size, @hdr_size) then + Y := data_size + hdr_size + else + Y := 0; + end; + PACKJPG_CODEC, BRUNSLI_CODEC, JOJPEG_CODEC: + begin + if GetJPEGInfo(Input, Y, @data_size, @progressive) then + Y := data_size + else + Y := 0; + end; + else + Y := 0; + end; + if Y > 0 then + begin + Z := data_size; + if X = FLAC_CODEC then + begin + Buffer := Funcs^.Allocator(Instance, hdr_size); + Move(Input^, Buffer^, hdr_size); + Inc(PInteger(Buffer)^); + Output(Instance, @hdr_size, hdr_size.Size); + Output(Instance, Buffer, hdr_size); + Output(Instance, Input + hdr_size, Z); + Inc(Z, hdr_size.Size); + Inc(Z, hdr_size); + end + else + Output(Instance, Input, Z); + SI.Position := 0; + SI.OldSize := Y; + SI.NewSize := Z; + SI.Option := 0; + SetBits(SI.Option, X, 0, 5); + if System.Pos(SPrecompSep2, DI1.Codec) > 0 then + SI.Status := TStreamStatus.Predicted + else + SI.Status := TStreamStatus.None; + DS := Funcs^.GetDepthCodec(DI1.Codec); + Move(DS[0], DI2.Codec, SizeOf(DI2.Codec)); + DI2.OldSize := SI.NewSize; + DI2.NewSize := SI.NewSize; + Funcs^.LogScan1(MediaCodecs[GetBits(SI.Option, 0, 5)], SI.Position, + SI.OldSize, -1); + Add(Instance, @SI, DI1.Codec, @DI2); + end; + exit; + end; + if BoolArray(CodecEnabled, False) then + exit; + Pos := 0; + while Pos < Size do + begin + if CodecEnabled[FLAC_CODEC] and (PCardinal(Input + Pos)^ = RIFF_SIGN) then + begin + Y := SizeEx - Pos; + if GetWAVEInfo(Input + Pos, Y, @wave_hdr, @data_size, @hdr_size) then + Y := data_size + hdr_size + else + Y := 0; + if Y > 0 then + begin + Z := data_size; + Buffer := Funcs^.Allocator(Instance, hdr_size); + Move((Input + Pos)^, Buffer^, hdr_size); + Inc(PInteger(Buffer)^); + Output(Instance, @hdr_size, hdr_size.Size); + Output(Instance, Buffer, hdr_size); + Output(Instance, Input + Pos + hdr_size, Z); + Inc(Z, hdr_size.Size); + Inc(Z, hdr_size); + SI.Position := Pos; + SI.OldSize := Y; + SI.NewSize := Z; + SI.Option := 0; + SetBits(SI.Option, FLAC_CODEC, 0, 5); + SI.Status := TStreamStatus.None; + Funcs^.LogScan1(MediaCodecs[GetBits(SI.Option, 0, 5)], SI.Position, + SI.OldSize, -1); + Add(Instance, @SI, nil, nil); + Inc(Pos, SI.OldSize); + continue; + end; + end; + if (CodecEnabled[PACKJPG_CODEC] or CodecEnabled[BRUNSLI_CODEC] or + CodecEnabled[JOJPEG_CODEC]) and (PWord(Input + Pos)^ = JPG_HEADER) then + begin + Y := SizeEx - Pos; + if GetJPEGInfo(Input + Pos, Y, @data_size, @progressive) then + Y := data_size + else + Y := 0; + if Y > 0 then + begin + Z := data_size; + Output(Instance, Input + Pos, Z); + SI.Position := Pos; + SI.OldSize := Y; + SI.NewSize := Z; + SI.Option := 0; + if CodecEnabled[BRUNSLI_CODEC] then + SetBits(SI.Option, BRUNSLI_CODEC, 0, 5) + else if CodecEnabled[JOJPEG_CODEC] then + SetBits(SI.Option, JOJPEG_CODEC, 0, 5) + else if CodecEnabled[PACKJPG_CODEC] then + SetBits(SI.Option, PACKJPG_CODEC, 0, 5); + SI.Status := TStreamStatus.None; + Funcs^.LogScan1(MediaCodecs[GetBits(SI.Option, 0, 5)], SI.Position, + SI.OldSize, -1); + Add(Instance, @SI, nil, nil); + Inc(Pos, SI.OldSize); + continue; + end; + end; + Inc(Pos); + end; +end; + +function MediaScan2(Instance, Depth: Integer; Input: Pointer; Size: NativeInt; + StreamInfo: PStrInfo2; Offset: PInteger; Output: _PrecompOutput; + Funcs: PPrecompFuncs): Boolean; +var + Buffer: PByte; + X, Y, Z: Integer; + wave_hdr: TWAVE_hdr; + data_size, hdr_size: Cardinal; + progressive: Boolean; +begin + Result := False; + X := GetBits(StreamInfo^.Option, 0, 5); + if StreamInfo^.OldSize <= 0 then + exit; + Y := StreamInfo^.OldSize; + case X of + FLAC_CODEC: + begin + if GetWAVEInfo(Input, Y, @wave_hdr, @data_size, @hdr_size) then + Y := data_size + hdr_size + else + Y := 0; + end; + PACKJPG_CODEC, BRUNSLI_CODEC, JOJPEG_CODEC: + begin + if GetJPEGInfo(Input, Y, @data_size, @progressive) then + Y := data_size + else + Y := 0; + end; + else + Y := 0; + end; + if Y > 0 then + begin + Z := data_size; + if X = FLAC_CODEC then + begin + Buffer := Funcs^.Allocator(Instance, hdr_size); + Move(Input^, Buffer^, hdr_size); + Inc(PInteger(Buffer)^); + Output(Instance, @hdr_size, hdr_size.Size); + Output(Instance, Buffer, hdr_size); + Output(Instance, PByte(Input) + hdr_size, Z); + Inc(Z, hdr_size.Size); + Inc(Z, hdr_size); + end + else + Output(Instance, Input, Z); + StreamInfo^.NewSize := Z; + Funcs^.LogScan2(MediaCodecs[GetBits(StreamInfo^.Option, 0, 5)], + StreamInfo^.OldSize, -1); + Result := True; + end; +end; + +function MediaProcess(Instance, Depth: Integer; OldInput, NewInput: Pointer; + StreamInfo: PStrInfo2; Output: _PrecompOutput; Funcs: PPrecompFuncs): Boolean; +var + Buffer, Ptr: PByte; + Params: String; + ctx: Pointer; + I, J: Integer; + X, Y: Integer; + Res: Cardinal; + Res1, Res2: Integer; +begin + Result := False; + X := GetBits(StreamInfo^.Option, 0, 5); + if BoolArray(CodecAvailable, False) or (CodecAvailable[X] = False) then + exit; + Params := ''; + case X of + FLAC_CODEC: + begin + Params := 'l' + FlacLevel.ToString; + Y := Integer.Size + PInteger(NewInput)^; + Res := StreamInfo.NewSize - Y; + Res := FlacEncode(cctx[Instance], @ccd[Instance], OldInput, + StreamInfo^.OldSize, PByte(NewInput) + Y, Res, FlacLevel); + if (Res > 0) and (Res + Y < StreamInfo^.NewSize) then + begin + StreamInfo^.NewSize := Res + Y; + Result := True; + end; + end; + PACKJPG_CODEC: + begin + Buffer := nil; + Res := StreamInfo.OldSize; + pjglib_init_streams(OldInput, pjglib_memory, StreamInfo^.OldSize, + Buffer, pjglib_memory); + if pjglib_convert_stream2mem(@Buffer, @Res, nil) and + (Res < StreamInfo^.NewSize) then + begin + Move(Buffer^, NewInput^, Res); + StreamInfo^.NewSize := Res; + Result := True; + end; + ShowMessage(''); + end; + BRUNSLI_CODEC: + begin + ctx := brunsli_alloc_JPEGData; + try + if brunsli_ReadJpeg(ctx, OldInput, StreamInfo^.OldSize) = Integer(True) + then + begin + Res := brunsli_GetMaximumEncodedSize(ctx); + Buffer := Funcs^.Allocator(Instance, Res); + Res := brunsli_EncodeJpeg(ctx, Buffer, Res); + if Res < StreamInfo^.NewSize then + begin + Move(Buffer^, NewInput^, Res); + StreamInfo^.NewSize := Res; + Result := True; + end; + end; + finally + brunsli_free_JPEGData(ctx); + end; + end; + JOJPEG_CODEC: + begin + ctx := JJInst[Instance]; + Buffer := Funcs^.Allocator(Instance, J_WORKMEM * 2); + I := 0; + J := 0; + Y := 0; + jojpeg_Init(ctx, jojpeg_Compress); + try + jojpeg_Addbuf(ctx, jojpeg_Compress, Buffer, J_WORKMEM, + jojpeg_enc_Output1); + jojpeg_Addbuf(ctx, jojpeg_Compress, Buffer + J_WORKMEM, J_WORKMEM, + jojpeg_enc_Output2); + while True do + begin + Res1 := jojpeg_Loop(ctx, jojpeg_Compress); + if (Res1 = jojpeg_enc_Input) then + begin + Res2 := Min(StreamInfo^.OldSize - I, J_WORKMEM); + jojpeg_Addbuf(ctx, jojpeg_Compress, PByte(OldInput) + I, Res2, + jojpeg_enc_Input); + Inc(I, Res2); + end; + if (Res1 in [0, jojpeg_enc_Output1]) then + begin + Res2 := jojpeg_Getvalue(ctx, jojpeg_Compress, jojpeg_enc_Output1); + Move(Buffer^, (PByte(NewInput) + J)^, Res2); + Inc(J, Res2); + jojpeg_Addbuf(ctx, jojpeg_Compress, Buffer, J_WORKMEM, + jojpeg_enc_Output1); + end; + if (Res1 = jojpeg_enc_Output2) or (Res1 = 0) then + begin + Res2 := jojpeg_Getvalue(ctx, jojpeg_Compress, jojpeg_enc_Output2); + Output(Instance, Buffer + J_WORKMEM, Res2); + Inc(Y, Res2); + jojpeg_Addbuf(ctx, jojpeg_Compress, Buffer + J_WORKMEM, J_WORKMEM, + jojpeg_enc_Output2); + if Res1 = 0 then + break; + end; + end; + if I = StreamInfo^.OldSize then + begin + StreamInfo^.NewSize := J; + Result := True; + end; + finally + jojpeg_Quit(ctx, jojpeg_Compress); + end; + end; + end; + Funcs^.LogProcess(MediaCodecs[GetBits(StreamInfo^.Option, 0, 5)], + PChar(Params), StreamInfo^.OldSize, StreamInfo^.NewSize, -1, Result); +end; + +function MediaRestore(Instance, Depth: Integer; Input, InputExt: Pointer; + StreamInfo: _StrInfo3; Output: _PrecompOutput; Funcs: PPrecompFuncs): Boolean; +var + Buffer: PByte; + Params: String; + ctx, writer: Pointer; + I, J: Integer; + X, Y: Integer; + Res: Cardinal; + Res1, Res2: Integer; +begin + Result := False; + X := GetBits(StreamInfo.Option, 0, 5); + if BoolArray(CodecAvailable, False) or (CodecAvailable[X] = False) then + exit; + Params := ''; + case X of + FLAC_CODEC: + begin + Buffer := Funcs^.Allocator(Instance, StreamInfo.OldSize); + Y := Integer.Size + PInteger(Input)^; + Res := StreamInfo.OldSize; + Res := FlacDecode(dctx[Instance], @dcd[Instance], PByte(Input) + Y, + StreamInfo.NewSize - Y, Buffer + PInteger(Input)^, Res); + if Res > 0 then + begin + Move((PByte(Input) + Integer.Size)^, Buffer^, PInteger(Input)^); + Inc(Res, PInteger(Input)^); + Dec(PInteger(Buffer)^); + Result := True; + end; + if Result then + Output(Instance, Buffer, Res); + end; + PACKJPG_CODEC: + begin + Buffer := nil; + pjglib_init_streams(Input, pjglib_memory, StreamInfo.NewSize, Buffer, + pjglib_memory); + Res := StreamInfo.OldSize; + Result := pjglib_convert_stream2mem(@Buffer, @Res, nil); + if Result then + Output(Instance, Buffer, Res); + end; + BRUNSLI_CODEC: + begin + RestoreOut[Instance] := Output; + RestoreSize[Instance] := 0; + Res := RestoreSize[Instance]; + ctx := brunsli_alloc_JPEGData; + try + if brunsli_DecodeJpeg(ctx, Input, StreamInfo.NewSize) = BRUNSLI_OK + then + begin + writer := brunsli_alloc_JPEGOutput(BrunsliWriter, @Instance); + try + Result := Boolean(brunsli_WriteJpeg(ctx, writer)); + finally + brunsli_free_JPEGOutput(writer); + end; + end; + finally + brunsli_free_JPEGData(ctx); + end; + Res := RestoreSize[Instance]; + end; + JOJPEG_CODEC: + begin + ctx := JJInst[Instance]; + Buffer := Funcs^.Allocator(Instance, J_WORKMEM); + I := 0; + J := 0; + Y := 0; + jojpeg_Init(ctx, jojpeg_Decompress); + try + jojpeg_Addbuf(ctx, jojpeg_Decompress, Buffer, J_WORKMEM, + jojpeg_dec_Output); + while True do + begin + Res1 := jojpeg_Loop(ctx, jojpeg_Decompress); + if (Res1 = jojpeg_dec_Input1) then + begin + Res2 := Min(StreamInfo.NewSize - I, J_WORKMEM); + jojpeg_Addbuf(ctx, jojpeg_Decompress, PByte(Input) + I, Res2, + jojpeg_dec_Input1); + Inc(I, Res2); + end; + if (Res1 = jojpeg_dec_Input2) then + begin + Res2 := Min(StreamInfo.ExtSize - J, J_WORKMEM); + jojpeg_Addbuf(ctx, jojpeg_Decompress, PByte(InputExt) + J, Res2, + jojpeg_dec_Input2); + Inc(J, Res2); + end; + if (Res1 in [0, jojpeg_dec_Output]) then + begin + Res2 := jojpeg_Getvalue(ctx, jojpeg_Decompress, + jojpeg_dec_Output); + Output(Instance, Buffer, Res2); + Inc(Y, Res2); + jojpeg_Addbuf(ctx, jojpeg_Decompress, Buffer, J_WORKMEM, + jojpeg_dec_Output); + if Res1 = 0 then + break; + end; + end; + { while True do + begin + Res1 := jojpeg_Loop(ctx, jojpeg_Decompress); + if Res1 = 0 then + break; + if (Res1 = jojpeg_dec_Input1) then + begin + Res2 := Min(StreamInfo.NewSize - I, J_WORKMEM); + jojpeg_Addbuf(ctx, jojpeg_Decompress, PByte(Input) + I, Res2, + jojpeg_dec_Input1); + Inc(I, Res2); + end; + if (Res1 = jojpeg_dec_Input2) then + begin + Res2 := Min(StreamInfo.ExtSize - J, J_WORKMEM); + jojpeg_Addbuf(ctx, jojpeg_Decompress, PByte(InputExt) + J, Res2, + jojpeg_dec_Input2); + Inc(J, Res2); + end; + if (Res1 = jojpeg_dec_Output) then + begin + Output(Instance, Buffer, J_WORKMEM); + Inc(Y, J_WORKMEM); + jojpeg_Addbuf(ctx, jojpeg_Decompress, Buffer, J_WORKMEM, + jojpeg_dec_Output); + end; + end; + if (Y <> StreamInfo.OldSize) and (StreamInfo.OldSize - Y < J_WORKMEM) + then + begin + Res2 := StreamInfo.OldSize - Y; + Output(Instance, Buffer, Res2); + Inc(Y, Res2); + end; } + Res := Y; + Result := Y = StreamInfo.OldSize; + finally + jojpeg_Quit(ctx, jojpeg_Decompress); + end; + end; + end; + Funcs^.LogRestore(MediaCodecs[GetBits(StreamInfo.Option, 0, 5)], + PChar(Params), StreamInfo.NewSize, Res, -1, Result); +end; + +var + I: Integer; + +initialization + +Codec.Names := []; +for I := Low(MediaCodecs) to High(MediaCodecs) do +begin + Codec.Names := Codec.Names + [MediaCodecs[I]]; + StockMethods.Add(MediaCodecs[I]); +end; +Codec.Initialised := False; +Codec.Init := @MediaInit; +Codec.Free := @MediaFree; +Codec.Parse := @MediaParse; +Codec.Scan1 := @MediaScan1; +Codec.Scan2 := @MediaScan2; +Codec.Process := @MediaProcess; +Codec.Restore := @MediaRestore; +SetLength(CodecAvailable, length(Codec.Names)); +SetLength(CodecEnabled, length(Codec.Names)); + +end. diff --git a/precompressor/PrecompOodle.pas b/precompressor/PrecompOodle.pas index 53710e3..7950c03 100644 --- a/precompressor/PrecompOodle.pas +++ b/precompressor/PrecompOodle.pas @@ -681,8 +681,8 @@ begin if CustomLZ_Decompress0(Input, Buffer, StreamInfo^.OldSize, OodleSI.DSize, Res) then begin - StreamInfo^.NewSize := Res; Output(Instance, Buffer, Res); + StreamInfo^.NewSize := Res; Funcs^.LogScan2(OodleCodecs[GetBits(StreamInfo^.Option, 0, 5)], StreamInfo^.OldSize, StreamInfo^.NewSize); Result := True; @@ -730,7 +730,7 @@ begin break; end; if (Result = False) and ((StreamInfo^.Status = TStreamStatus.Predicted) or - (SOList[Instance][X].Count = 1)) then + (SOList[Instance][X].Count = 1)) and (DIFF_TOLERANCE > 0) then begin Buffer := Funcs^.Allocator(Instance, Res1 + Max(StreamInfo^.OldSize, Res1)); Res2 := PrecompEncodePatch(OldInput, StreamInfo^.OldSize, Buffer, Res1, diff --git a/precompressor/PrecompUtils.pas b/precompressor/PrecompUtils.pas index 72a4e6e..377e612 100644 --- a/precompressor/PrecompUtils.pas +++ b/precompressor/PrecompUtils.pas @@ -18,7 +18,7 @@ resourcestring SPrecompSep4 = '/'; const - SuccessStatus = 3; + SuccessStatus = 4; DEFAULT_STREAM = 0; EXTENDED_STREAM = 1; @@ -32,7 +32,7 @@ type TPrecompStr = array [0 .. 255] of Char; - TStreamStatus = (None, Invalid, Predicted); + TStreamStatus = (None, Invalid, Predicted, Database); PDepthInfo = ^TDepthInfo; @@ -182,10 +182,10 @@ type LogScan1: procedure(Codec: PChar; Position: Int64; InSize, OutSize: Integer)cdecl; LogScan2: procedure(Codec: PChar; InSize, OutSize: Integer)cdecl; // 35 - LogProcess: procedure(Codec, Method: PChar; - OriginalSize, InSize, OutSize: Integer; Status: Boolean)cdecl; - LogRestore: procedure(Codec, Method: PChar; - OriginalSize, InSize, OutSize: Integer; Status: Boolean)cdecl; + LogProcess: procedure(Codec, Method: PChar; Size1, Size2, Size3: Integer; + Status: Boolean)cdecl; + LogRestore: procedure(Codec, Method: PChar; Size1, Size2, Size3: Integer; + Status: Boolean)cdecl; LogPatch1: procedure(OldSize, NewSize, PatchSize: Integer; Status: Boolean)cdecl; LogPatch2: procedure(OldSize, NewSize, PatchSize: Integer; @@ -243,22 +243,29 @@ type PDatabase = ^TDatabase; - TDatabase = record + TDatabase = packed record + Size: Integer; Codec: Byte; - Status: TStreamStatus; Option: Integer; + Checksum: Cardinal; + Status: TStreamStatus; end; - TDatabaseDynArray = TArray; + PDuplicate1 = ^TDuplicate1; - PDuplicate = ^TDuplicate; - - TDuplicate = record + TDuplicate1 = packed record + Size: Integer; + Checksum: Cardinal; Index: Integer; Count: Integer; end; - TDuplicateDynArray = TArray; + PDuplicate2 = ^TDuplicate2; + + TDuplicate2 = packed record + Index: Integer; + Count: Integer; + end; TPrecompVMStream = class(TStream) private const @@ -288,8 +295,6 @@ type Size: Integer; end; -function DuplicateSortCompare(const Left, Right): Integer; - procedure AddMethod(Method: String); procedure ClearMethods; @@ -358,7 +363,6 @@ function PrecompAcceptPatch(OldSize, NewSize, PatchSize: Integer) var PrecompFunctions: _PrecompFuncs; DIFF_TOLERANCE: Single = 0.05; - VERBOSE: Boolean = False; EncodeSICmp: TEncodeSIComparer; FutureSICmp: TFutureSIComparer; StockMethods, ExternalMethods: TStringList; @@ -380,11 +384,6 @@ begin Result := Integer(CompareValue(Left.Position, Right.Position)); end; -function DuplicateSortCompare(const Left, Right): Integer; -begin - Result := TDuplicate(Left).Index - TDuplicate(Right).Index; -end; - procedure AddMethod(Method: String); begin if (StockMethods.IndexOf(Method) < 0) and (ExternalMethods.IndexOf(Method) < 0) @@ -856,7 +855,7 @@ var begin Result := 0; if xd3_encode(OldBuff, OldSize, NewBuff, NewSize, PatchBuff, @Res, PatchSize, - 0) = 0 then + Integer(XD3_NOCOMPRESS)) = 0 then Result := Res; // MakeDiff(OldBuff, NewBuff, PatchBuff, OldSize, NewSize, Result); end; @@ -869,7 +868,7 @@ var begin Result := 0; if xd3_decode(PatchBuff, PatchSize, OldBuff, OldSize, NewBuff, @Res, NewSize, - 0) = 0 then + Integer(XD3_NOCOMPRESS)) = 0 then Result := Res; // MakePatch(OldBuff, PatchBuff, NewBuff, OldSize, PatchSize, Result); end; diff --git a/precompressor/PrecompZLib.pas b/precompressor/PrecompZLib.pas index 82b44ce..5ae50ee 100644 --- a/precompressor/PrecompZLib.pas +++ b/precompressor/PrecompZLib.pas @@ -255,7 +255,8 @@ begin CodecAvailable[ZLIB_CODEC] := ZLibDLL.DLLLoaded; CodecAvailable[REFLATE_CODEC] := ReflateDLL.DLLLoaded; CodecAvailable[PREFLATE_CODEC] := PreflateDLL.DLLLoaded; - CodecAvailable[PNG_CODEC] := True; + CodecAvailable[PNG_CODEC] := ZLibDLL.DLLLoaded or ReflateDLL.DLLLoaded or + PreflateDLL.DLLLoaded; X := 0; while Funcs^.GetCodec(Command, X, False) <> '' do begin @@ -433,12 +434,13 @@ var begin DI1 := Funcs^.GetDepthInfo(Instance); DS := Funcs^.GetCodec(DI1.Codec, 0, False); + X := -1; if DS <> '' then begin X := IndexTextW(@DS[0], ZlibCodecs); if (X < 0) or (DI1.OldSize <> SizeEx) then exit; - if CodecAvailable[X] then + if not CodecAvailable[X] then exit; end else if BoolArray(CodecEnabled, False) then @@ -567,7 +569,7 @@ begin if (I = ZLIB_CODEC) and (WinBits = 0) then SetBits(SI.Option, 1, 12, 3); SetBits(SI.Option, I, 0, 5); - if CodecEnabled[I] then + if CodecEnabled[I] or (I = X) then begin DS := Funcs^.GetDepthCodec(DI1.Codec); Move(DS[0], DI2.Codec, SizeOf(DI2.Codec)); @@ -632,18 +634,14 @@ function ZlibProcess(Instance, Depth: Integer; OldInput, NewInput: Pointer; function IsValidLevel(CLevel, ZLevel: Integer): Boolean; begin + Result := False; case CLevel of 1, 6: - if CLevel = ZLevel then - Result := True; + Result := CLevel = ZLevel; 2 .. 5: - if ZLevel = 5 then - Result := True; + Result := ZLevel = 5; 7 .. 9: - if ZLevel = 9 then - Result := True; - else - Result := False; + Result := ZLevel = 9; end; end; @@ -664,6 +662,7 @@ begin if not X in [PNG_CODEC] then if BoolArray(CodecAvailable, False) or (CodecAvailable[X] = False) then exit; + Params := ''; case X of ZLIB_CODEC: begin @@ -673,7 +672,7 @@ begin begin L := I div 10; M := I mod 10; - if StreamInfo^.Status = TStreamStatus.Predicted then + if StreamInfo^.Status >= TStreamStatus.Predicted then begin if InRange(GetBits(StreamInfo^.Option, 5, 7), 1, 9) then begin @@ -684,36 +683,40 @@ begin begin if GetBits(StreamInfo^.Option, 5, 7) <> I then continue; - { I := GetBits(StreamInfo^.Option, 5, 7); - SOList[Instance][ZLIB_CODEC].Add(I); + if StreamInfo^.Status = TStreamStatus.Database then Result := True; - break; } end; end; Params := 'l' + I.ToString + ':' + 'w' + (GetBits(StreamInfo^.Option, 12, 3) + 8).ToString; ZStream := @ZStream1[Instance, L, M, GetBits(StreamInfo^.Option, 12, 3)]; - ZStream^.next_in := NewInput; - ZStream^.avail_in := StreamInfo^.NewSize; - deflateReset(ZStream^); - repeat - ZStream^.next_out := Buffer; - ZStream^.avail_out := Z_BLKSIZE; - Res1 := deflate(ZStream^, Z_FINISH); - if Res1 < 0 then - raise EZCompressionError.Create(string(_z_errmsg[2 - Res1])) - at ReturnAddress; - Res2 := Z_BLKSIZE - ZStream^.avail_out; - Verified := CompareMem(PByte(OldInput) + ZStream^.total_out - Res2, - Buffer, Res2); - if not Verified then - break; - until (ZStream^.avail_in = 0) and (ZStream^.avail_out > 0); + if not Result then + begin + ZStream^.next_in := NewInput; + ZStream^.avail_in := StreamInfo^.NewSize; + deflateReset(ZStream^); + repeat + ZStream^.next_out := Buffer; + ZStream^.avail_out := Z_BLKSIZE; + Res1 := deflate(ZStream^, Z_FINISH); + if Res1 < 0 then + raise EZCompressionError.Create(string(_z_errmsg[2 - Res1])) + at ReturnAddress; + Res2 := Z_BLKSIZE - ZStream^.avail_out; + Verified := CompareMem(PByte(OldInput) + ZStream^.total_out - + Res2, Buffer, Res2); + if not Verified then + break; + until (ZStream^.avail_in = 0) and (ZStream^.avail_out > 0); + end + else + ZStream.total_out := StreamInfo^.OldSize; Funcs^.LogProcess(ZlibCodecs[GetBits(StreamInfo^.Option, 0, 5)], PChar(Params), StreamInfo^.OldSize, StreamInfo^.NewSize, - ZStream^.total_out, Verified and (Res1 = Z_STREAM_END)); - if Verified and (Res1 = Z_STREAM_END) then + ZStream^.total_out, (Result = True) or + (Verified and (Res1 = Z_STREAM_END))); + if (Result = True) or (Verified and (Res1 = Z_STREAM_END)) then begin SetBits(StreamInfo^.Option, I, 5, 7); SOList[Instance][ZLIB_CODEC].Add(I); @@ -739,7 +742,7 @@ begin Buffer := Funcs^.Allocator(Instance, R_WORKMEM * 2); J := 0; HR := RefInst1[Instance]; - if StreamInfo^.Status = TStreamStatus.Predicted then + if StreamInfo^.Status >= TStreamStatus.Predicted then L := GetBits(StreamInfo^.Option, 5, 7) else L := RLevel; @@ -804,7 +807,6 @@ begin PNG_CODEC: begin Buffer := Funcs^.Allocator(Instance, StreamInfo^.OldSize); - Params := ''; if DecodePNG(NewInput, Buffer, StreamInfo^.OldSize) then Result := CompareMem(OldInput, Buffer, StreamInfo^.OldSize); Funcs^.LogProcess(ZlibCodecs[GetBits(StreamInfo^.Option, 0, 5)], @@ -831,6 +833,7 @@ begin if not X in [PNG_CODEC] then if BoolArray(CodecAvailable, False) or (CodecAvailable[X] = False) then exit; + Params := ''; case X of ZLIB_CODEC: begin @@ -915,7 +918,6 @@ begin PNG_CODEC: begin Buffer := Funcs^.Allocator(Instance, StreamInfo.OldSize); - Params := ''; if DecodePNG(Input, Buffer, StreamInfo.OldSize) then begin Output(Instance, Buffer, StreamInfo.OldSize); diff --git a/precompressor/PrecompZSTD.pas b/precompressor/PrecompZSTD.pas index 8615493..c4bd9d1 100644 --- a/precompressor/PrecompZSTD.pas +++ b/precompressor/PrecompZSTD.pas @@ -233,8 +233,8 @@ begin end; if Res > StreamInfo^.OldSize then begin - StreamInfo^.NewSize := Res; Output(Instance, Buffer, Res); + StreamInfo^.NewSize := Res; Funcs^.LogScan2(ZSTDCodecs[GetBits(StreamInfo^.Option, 0, 5)], StreamInfo^.OldSize, StreamInfo^.NewSize); Result := True; @@ -265,6 +265,7 @@ begin if StreamInfo^.Status = TStreamStatus.Predicted then if GetBits(StreamInfo^.Option, 5, 7) <> I then continue; + Params := ''; case X of ZSTD_CODEC: begin @@ -307,7 +308,7 @@ begin if Res1 < 0 then exit; if (Result = False) and ((StreamInfo^.Status = TStreamStatus.Predicted) or - (SOList[Instance][X].Count = 1)) then + (SOList[Instance][X].Count = 1)) and (DIFF_TOLERANCE > 0) then begin Buffer := Funcs^.Allocator(Instance, Res1 + Max(StreamInfo^.OldSize, Res1)); Res2 := PrecompEncodePatch(OldInput, StreamInfo^.OldSize, Buffer, Res1, diff --git a/resources/Win32/fast-lzma2.dll b/resources/Win32/fast-lzma2.dll new file mode 100644 index 0000000000000000000000000000000000000000..c166eb44cf715260698700f36021f08b24aa90ec GIT binary patch literal 166400 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 dD`Q7HJtC} literal 0 HcmV?d00001 diff --git a/resources/Win64/fast-lzma2.dll b/resources/Win64/fast-lzma2.dll new file mode 100644 index 0000000000000000000000000000000000000000..397329f03a48edb953709c6909dc96030b045e14 GIT binary patch literal 196608 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 literal 0 HcmV?d00001 diff --git a/xtool.dpr b/xtool.dpr index bbe9006..9dc958e 100644 --- a/xtool.dpr +++ b/xtool.dpr @@ -52,9 +52,13 @@ uses oObjects in 'contrib\ParseExpression\oObjects.pas', ParseClass in 'contrib\ParseExpression\ParseClass.pas', ParseExpr in 'contrib\ParseExpression\ParseExpr.pas', + BrunsliDLL in 'imports\BrunsliDLL.pas', + FLACDLL in 'imports\FLACDLL.pas', + JoJpegDLL in 'imports\JoJpegDLL.pas', LZ4DLL in 'imports\LZ4DLL.pas', LZODLL in 'imports\LZODLL.pas', OodleDLL in 'imports\OodleDLL.pas', + PackJPGDLL in 'imports\PackJPGDLL.pas', PreflateDLL in 'imports\PreflateDLL.pas', ReflateDLL in 'imports\ReflateDLL.pas', XDeltaDLL in 'imports\XDeltaDLL.pas', @@ -68,6 +72,7 @@ uses PrecompLZ4 in 'precompressor\PrecompLZ4.pas', PrecompLZO in 'precompressor\PrecompLZO.pas', PrecompZSTD in 'precompressor\PrecompZSTD.pas', + PrecompMedia in 'precompressor\PrecompMedia.pas', PrecompOodle in 'precompressor\PrecompOodle.pas', PrecompINI in 'precompressor\PrecompINI.pas', PrecompSearch in 'precompressor\PrecompSearch.pas', @@ -82,7 +87,8 @@ uses IOPatch in 'io\IOPatch.pas', IOExecute in 'io\IOExecute.pas', IODecode in 'io\IODecode.pas', - IOUtils in 'io\IOUtils.pas'; + IOUtils in 'io\IOUtils.pas', + LZMADLL in 'imports\LZMADLL.pas'; {$SETPEFLAGS IMAGE_FILE_LARGE_ADDRESS_AWARE or IMAGE_FILE_RELOCS_STRIPPED} @@ -141,265 +147,24 @@ begin Result := THandleStream.Create(GetStdHandle(STD_INPUT_HANDLE)) else if Pos('://', Input) > 0 then Result := TDownloadStream.Create(Input) + else if FileExists(Input) then + Result := TFileStream.Create(Input, fmShareDenyNone) else - Result := TFileStream.Create(Input, fmShareDenyNone); + Result := TDirInputStream.Create(Input); end; -function GetOutStream(Output: string): TStream; +function GetOutStream(Output: string; MultiInput: Boolean = False): TStream; begin - if (Output = '-') or (Output = '') then + if (Output = '') then + Result := TNullStream.Create + else if DirectoryExists(Output) then + Result := TDirOutputStream.Create(Output) + else if (Output = '-') then Result := THandleStream.Create(GetStdHandle(STD_OUTPUT_HANDLE)) else Result := TFileStream.Create(Output, fmCreate); end; -{ changelog - ES_R34 (0.5.3) - - added png stream preprocessor - - removed grittibanzli codec (since nobody uses it) - - ES_R33 (0.5.2) - - added IO functions (archive, execute) - - fixed issue in patch io function - - removed compression on patch diff files - - ES_R32 (0.5.1) - - added IO functions (find, extract, patch) - - generate database feature and IO functions now can search for streams larger than chunk size - - ES_R31 (0.5.0) - - added IO functions (erase, replace) - - fixed external executable support bugs - - ES_R30 (0.4.8) - - fixed issue with storing incorrect recompression information when stream patching is performed - - ES_R29 (0.4.7) - - updated oodle scanner - - updated external executable support - - updated configuration based plugin support to add depth information - - updated verbose mode - - ES_R28 (0.4.6) - - generate database feature fixed - - fixed external executable support issues - - fixed lz4f level setting bug - - ES_R28 (0.4.5) - - removed leviathan codec restriction - - ES_R27 (0.4.4) - - fixed issue of lz4 codec loading incorrect library - - fixed issue with handling endianess via configuration based plugins - - updated framework of library based plugins - - ES_R26 (0.4.3) - - added verbose mode - - added feature that allows you to enforce a different library to be loaded - - fixed issues related to imperfect stream patching - - fixed issues with old libraries with missing functions that cause xtool to crash on startup - - updated oodle codec - - updated reflate codec - - updated zstd codec - - ES_R25 (0.4.2) - - removed debugging code from encryption and executable codec - - fixed issue with depth when using search codec - - fixed external executable support issues - - ES_R24 (0.4.1) - - fixed issue of status not reporting when encoding - - added depth method support for search support - - fixed zlib encoding issues for different window bits - - fixed zlib memory leak issue - - updated all internal codecs to support information relayed by external codecs - - updated lz4f codec and removed temporarily removed support for universal scanning - - added option to change recompression level to be used by reflate - - updated external executable support - - generate database feature currently bugged, wait for next update - - search database structure changed, older database files will no longer work with newer releases - - ES_R23 (0.4.0) - - project made open source - - added external executable support - - added generate database feature - - fixed search support bug - - ES_R22 (0.3.22) - - updated search support (speed improvements) - - updated command line parser - - added partial universal scanner for lzo1x streams - - added universal scanner for lz4f streams - - fixed issue with configuration files failing to execute without conditions - - ES_R21 (0.3.21) - - updated search support - - ES_R20 (0.3.20) - - fixed library support bug - - x86 build discontinued (has bugs from nowhere) - - ES_R19 (0.3.19) - - updated lzo codec - - ES_R18 (0.3.18) - - fixed depth bug - - fixed library plugin bugs - - ES_R17 (0.3.17) - - fixed multi-threading bug - - ES_R16 (0.3.16) - - minor bug fixes - - ES_R15 (0.3.15) - - converted library support to unicode (don't know why I used ansi in the first place) - - added library support functions - - added rc4 encryption support - - ES_R14 (0.3.14) - - fixed library support bug - - updated library structure - - ES_R13 (0.3.13) - - updated lz4 codec - - updated library structure - - updated depth info functions - - updated depth feature - - ES_R12 (0.3.12) - - added depth info functions - - added support for oodle 2.9.0+ functions - - fixed data patching bug - - updated oodle codec - - updated command line parser - - ES_R11 (0.3.11) - - fixed x86 build bugs - - fixed config multi-threading bug - - fixed resource management bug - - fixed deduplication bug - - ES_R10 (0.3.10) - - minor bug fixes - - added diff tolerance parameter (--diff=) - - fixed plugin database bug - - fixed lz4 codec bug - - updated oodle codec - - updated library structure - - added resource management - - added direct use encryption codecs - - added embedded deduplication feature (--dedup) [makes temps during encoding] - - ES_R9 (0.3.9) - - fixed future stream bug - - ES_R8 (0.3.8) - - fixed command line parser bug - - updated library support - - ES_R7 (0.3.7) - - updated library structure - - ES_R6 (0.3.6) - - updated oodle codec (fixed more lzna bugs) - - ES_R5 (0.3.5) - - updated oodle codec (fixed lzna bug) - - added custom method configuration - - ES_R4 (0.3.4) - - fixed bug depthing - - ES_R3 (0.3.3) - - updated lz4 codec - - updated library support - - ES_R2 (0.3.2) - - improved depthing - - updated library support - - fixed zstd codec issues - - removed fast memory - - ES_R1 (0.3.1) - - updated library support - - updated command line parser - - included x86 build - - fixed depthing issues - - 2012_R2 (0.2.14) - - added library support - - added compress, decompress, encrypt, decrypt, hash, delta functions (used by library) - - added lzo codec placeholders - - fixed oodle bug - - fixed lz4 bug - - removed libdunia codec - - 2012_R1 (0.2.13) - - added oo2ext* dll support - - updated search support - - 2011_R1 (0.2.12) - - added temporary libdunia codec - - 2010_R5 (0.2.11) - - fixed search/config support bug - - 2010_R4 (0.2.10) - - updated search/config support - - 2010_R3 (0.2.9) - - added database search - - updated zlib scanner - - fixed reflate bug - - fixed 2GB memory limit - - 2010_R2 (0.2.8) - - fixed zstd codec - - 2010_R1 (0.2.7) - - added zstd codec - - added lz4, lz4hc, lzna, mermaid, selkie, hydra, leviathan codec placeholders - - added configuration support - - added xdelta support to handle crc mismatch streams - - 2009_R3 (0.2.6) - - documentation added - - 2009_R2 (0.2.5) - - added kraken codec - - fixed depthing issues - - 2009_R1 (0.2.4) - - added reflate forced verification - - updated deflate scanner - - fixed depthing issues - - fixed low memory mode issues - - fixed hanging issues when encoding - - 2008_R3 (0.2.3) - - fixed deduplication memory calculation error - - added virtual memory support for deduplication - - added --mem=# parameter to control deduplication memory usage - - 2008_R2 (0.2.2) - - fixed command line parser - - updated deflate scanner - - added stream deduplication - - added stream database - - added decompression memory limiter - - added grittibanzli (also handles deflate stream but slow af) - - 2008_R1 (0.2.1) - - initial release - - changelog } - -procedure EncodePNG(Input: PByte; Output: PByte); -begin - -end; - const BufferSize = 1048576; @@ -460,12 +225,10 @@ begin PrecompMain.PrintHelp else begin - while Length(ParamArg[1]) < 2 do - Insert('-', ParamArg[1], Length(ParamArg[1])); - Input := TBufferedStream.Create(GetInStream(ParamArg[1, 0]), True, - BufferSize); - Output := TBufferedStream.Create(GetOutStream(ParamArg[1, 1]), False, + Input := TBufferedStream.Create(GetInStream(ParamArgSafe(1, 0)), True, BufferSize); + Output := TBufferedStream.Create(GetOutStream(ParamArgSafe(1, 1)), + False, BufferSize); try PrecompMain.Parse(ParamArg[0], PrecompEnc); PrecompMain.Encode(Input, Output, PrecompEnc); @@ -550,8 +313,6 @@ begin IOArchive.PrintHelp else begin - while Length(ParamArg[1]) < 2 do - Insert('-', ParamArg[1], Length(ParamArg[1])); SetLength(StrArray, 0); for I := 0 to High(ParamArg[1]) - 1 do Insert(ParamArg[1, I], StrArray, Length(StrArray)); @@ -569,8 +330,6 @@ begin IOExecute.PrintHelp else begin - while Length(ParamArg[1]) < 2 do - Insert('-', ParamArg[1], Length(ParamArg[1])); SetLength(StrArray, 0); for I := 2 to High(ParamArg[1]) do Insert(ParamArg[1, I], StrArray, Length(StrArray)); diff --git a/xtool.dproj b/xtool.dproj index 9936163..194f8e7 100644 --- a/xtool.dproj +++ b/xtool.dproj @@ -111,9 +111,13 @@ + + + + @@ -127,6 +131,7 @@ + @@ -142,6 +147,7 @@ + ResourceItem RCDATA @@ -152,6 +158,7 @@ RCDATA XDELTA64_DLL + Base @@ -189,6 +196,12 @@ true + + + .\ + true + + .\

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