unit PrecompMain; {$POINTERMATH ON} interface uses InitCode, Threading, Utils, SynCommons, ParseClass, ParseExpr, FLZMA2DLL, XXHASHLIB, PrecompUtils, PrecompCrypto, PrecompZLib, PrecompLZ4, PrecompLZO, PrecompZSTD, PrecompOodle, PrecompMedia, PrecompINI, PrecompINIEx, 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, System.Generics.Defaults, System.Generics.Collections, System.Character; const XTOOL_PRECOMP = $304C5458; XTOOL_BSIZE = 4194304; type PEncodeOptions = ^TEncodeOptions; TEncodeOptions = record Method: String; ChunkSize, Threads: Integer; Depth: Integer; LowMem: Boolean; DBaseFile, ExtractDir: String; CThreads, CLevel: Integer; CDict, COverlap: Integer; CHighCompress: Boolean; end; PDecodeOptions = ^TDecodeOptions; TDecodeOptions = record Method: String; Threads: Integer; CacheSize: Int64; Depth: Integer; DedupSysMem: Int64; end; procedure PrintHelp; procedure Parse(ParamArg: TArray; out Options: TEncodeOptions); overload; procedure Parse(ParamArg: TArray; out Options: TDecodeOptions); overload; procedure Encode(Input, Output: TStream; Options: TEncodeOptions); procedure Decode(Input, Output: TStream; Options: TDecodeOptions); function PrecompAllocator(Instance: Integer; Size: Integer): Pointer cdecl; function PrecompGetDepthInfo(Index: Integer): TDepthInfo cdecl; function PrecompReadFuture(Index: Integer; Position: NativeInt; Buffer: Pointer; Count: Integer): Integer cdecl; procedure PrecompLogScan1(Codec: PChar; Position: Int64; InSize, OutSize: Integer)cdecl; procedure PrecompLogScan2(Codec: PChar; InSize, OutSize: Integer)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; Status: Boolean)cdecl; procedure PrecompLogReprocess(Method: PChar; Size1, Size2, Size3: Integer; Status: Boolean)cdecl; procedure PrecompOutput1(Instance: Integer; const Buffer: Pointer; Size: Integer)cdecl; procedure PrecompOutput2(Instance: Integer; const Buffer: Pointer; Size: Integer)cdecl; procedure PrecompOutput3(Instance: Integer; const Buffer: Pointer; Size: Integer)cdecl; procedure PrecompAddStream(Instance: Integer; Info: PStrInfo1; Codec: PChar; DepthInfo: PDepthInfo)cdecl; procedure PrecompTransfer(Instance: Integer; Codec: PChar)cdecl; function PrecompStorage(Instance: Integer; Size: PInteger): Pointer cdecl; function PrecompAddResourceEx(Data: Pointer; Size: Integer): Integer cdecl; implementation procedure EncInit(Input, Output: TStream; Options: PEncodeOptions); forward; procedure EncFree; forward; function EncData(Input, Output: TStream; Index, Depth: Integer) : Boolean; forward; procedure DecInit(Input, Output: TStream; Options: PDecodeOptions); forward; procedure DecFree; forward; procedure DecChunk(Input, Output: TStream; Index, Depth: Integer); forward; type TEncInfo = record Processed, Count: Integer; DecMem0, DecMem1, DecMem2, DecMem3: Int64; DupSize1, DupSize2: Int64; DupCount: Integer; InSize, InflSize, SrepSize, CompSize: Int64; SrepMem: Integer; CachedComp, CachedUsed: Int64; end; var GlobalSync: TCriticalSection; ThreadSync: TArray; IntArray: array [0 .. 1] of Int64; Codecs: array of TPrecompressor; DBFile: String = ''; ExtDir: String = ''; SrepInSize, SrepMemCfg: String; ResCount: Integer; UseDB: Boolean = False; StoreDD: Integer = -2; VERBOSE: Boolean = False; EXTRACT: Boolean = False; NOVERIFY: Boolean = False; REPROCESS: String = ''; COMPRESS: Byte = 0; EXTCOMP: String = ''; GPUMEM: Int64 = 0; NULLOUT: Boolean = False; DupSysMem: Int64 = 0; DecodeMemBlock: Int64 = 512 * 1024 * 1024; EncInfo: TEncInfo; EncFreed: Boolean = False; ConTask: TTask; Stopwatch: TStopwatch; function ExtractExec(S: String): String; begin Result := S.Substring(0, Pos('.exe', S) + 3); end; function ExtractParams(S: String): String; begin Result := S.Substring(Pos('.exe', S) + 4); end; procedure PrintHelp; var I, J: Integer; S: string; begin WriteLine('precomp - data precompressor'); WriteLine(''); WriteLine('Usage:'); WriteLine(' xtool precomp [parameters] input output'); WriteLine(''); WriteLine(''); WriteLine('Parameters:'); WriteLine( ' -m# - codecs to use for precompression (separate with "+" if more than one)'); WriteLine(' -c# - scanning range of precompressor [16mb]'); WriteLine(' -t# - number of working threads [50p]'); WriteLine(' -d# - scan depth [0]'); WriteLine(' -dd - use stream deduplication'); WriteLine( ' -l# - compress data using fast lzma2 (separate params with ":")'); WriteLine(' d# - dictionary size'); WriteLine(' -lm - low memory mode'); WriteLine(' -s - skip stream verification'); WriteLine(' -v - enables verbose'); WriteLine(' -df# - set xdelta threshold to accept streams [5p]'); WriteLine(' -x# - extract streams to directory path'); WriteLine(' -dm# - deduplication memory usage limit (#=size) [75p]'); WriteLine(' -sm# - srep memory usage limit (#=size) [75p]'); WriteLine(''); end; procedure Parse(ParamArg: TArray; out Options: TEncodeOptions); var ArgParse: TArgParser; ExpParse: TExpressionParser; List: TStringDynArray; I, J: Integer; S: String; begin FillChar(Options, SizeOf(TEncodeOptions), 0); Options.Depth := 1; ArgParse := TArgParser.Create(ParamArg); ExpParse := TExpressionParser.Create; try I := 0; while True do begin S := ArgParse.AsString('-m', I); if S = '' then break; S := ReplaceStr(S, SPrecompSep3, SPrecompSep2); if Options.Method <> '' then Options.Method := Options.Method + '+' + S else Options.Method := S; Inc(I); end; S := ArgParse.AsString('-g', 0, '0'); S := ReplaceText(S, 'KB', '* 1024^1'); S := ReplaceText(S, 'MB', '* 1024^2'); S := ReplaceText(S, 'GB', '* 1024^3'); S := ReplaceText(S, 'K', '* 1024^1'); S := ReplaceText(S, 'M', '* 1024^2'); S := ReplaceText(S, 'G', '* 1024^3'); S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + GPUSize.ToString); GPUMEM := EnsureRange(Round(ExpParse.Evaluate(S)), 0, Max(0, GPUSize - 512 * 1024 * 1024)); S := ArgParse.AsString('-c', 0, '16mb'); S := ReplaceText(S, 'KB', '* 1024^1'); S := ReplaceText(S, 'MB', '* 1024^2'); S := ReplaceText(S, 'GB', '* 1024^3'); S := ReplaceText(S, 'K', '* 1024^1'); S := ReplaceText(S, 'M', '* 1024^2'); S := ReplaceText(S, 'G', '* 1024^3'); if GPUMEM > MEM_LIMIT then GPUMEM := MEM_LIMIT; Options.ChunkSize := EnsureRange(Round(ExpParse.Evaluate(S)), 4 * 1024 * 1024, 2047 * 1024 * 1024); S := ArgParse.AsString('-t', 0, '50p'); S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + CPUCount.ToString); Options.Threads := Max(1, Round(ExpParse.Evaluate(S))); { S := ArgParse.AsString('-b', 0, '512mb'); S := ReplaceText(S, 'KB', '* 1024^1'); S := ReplaceText(S, 'MB', '* 1024^2'); S := ReplaceText(S, 'GB', '* 1024^3'); S := ReplaceText(S, 'K', '* 1024^1'); S := ReplaceText(S, 'M', '* 1024^2'); S := ReplaceText(S, 'G', '* 1024^3'); DecodeMemBlock := EnsureRange(Round(ExpParse.Evaluate(S)), 32 * 1024 * 1024, 2047 * 1024 * 1024); } StoreDD := -2; I := 0; while True do begin S := ArgParse.AsString('-d', I); if S = '' then break; case S[1] of 'b': UseDB := True; 'd': begin StoreDD := -1; if FileExists(ExpandPath(PluginsPath + 'srep.exe', True)) then if S.Length > 1 then StoreDD := StrToInt(S[2]); end; 'f': begin S := ReplaceText(S.Substring(1), 'p', '%'); DIFF_TOLERANCE := Max(0.00, ExpParse.Evaluate(S)); end; else if S[1].IsDigit then Options.Depth := EnsureRange(Succ(S.ToInteger), 1, 10); end; Inc(I); end; UseDB := True; I := 0; while True do begin S := ArgParse.AsString('-l', I); if S = '' then break; case S[1] of 'm': Options.LowMem := True; else if S[1].IsDigit and FLZMA2DLL.DLLLoaded then begin S := ReplaceText(S, SPrecompSep3, SPrecompSep2); if (S <> '') then begin List := DecodeStr(S, ':'); Options.CThreads := Options.Threads; Options.CHighCompress := List[0].Contains('x'); if List[0].Contains('x') then Options.CLevel := StrToIntDef(Copy(List[0], 1, List[0].Length - 1), 0) else Options.CLevel := StrToIntDef(List[0], 0); Options.COverlap := 2; for J := Low(List) to High(List) do begin if List[J].StartsWith('d', False) then Options.CDict := EnsureRange(ConvertToBytes(List[J].Substring(1) ), FL2_DICTSIZE_MIN, FL2_DICTSIZE_MAX); if List[J].StartsWith('o', False) then Options.COverlap := EnsureRange(ConvertToBytes(List[J].Substring(1)), FL2_BLOCK_OVERLAP_MIN, FL2_BLOCK_OVERLAP_MAX); end; COMPRESS := Byte(InRange(Options.CLevel, 1, 10)); end; end; end; Inc(I); end; SrepInSize := ArgParse.AsString('-SI', 0, '100gb').ToLower; VERBOSE := ArgParse.AsBoolean('-v'); NOVERIFY := ArgParse.AsBoolean('-s'); OPTIMISE_DEC := ArgParse.AsBoolean('-o'); Options.ExtractDir := ArgParse.AsString('-x'); if Options.ExtractDir <> '' then EXTRACT := DirectoryExists(Options.ExtractDir); EXTCOMP := ArgParse.AsString('-e'); if FileExists(ExpandPath(PluginsPath + ExtractExec(EXTCOMP), True)) then COMPRESS := 2; REPROCESS := ReplaceStr(ArgParse.AsString('-r'), SPrecompSep3, SPrecompSep2); finally ArgParse.Free; ExpParse.Free; end; if VERBOSE or EXTRACT then Options.Threads := 1; end; procedure Parse(ParamArg: TArray; out Options: TDecodeOptions); var ArgParse: TArgParser; ExpParse: TExpressionParser; S: String; B: Boolean; begin ArgParse := TArgParser.Create(ParamArg); ExpParse := TExpressionParser.Create; try Options.Method := ArgParse.AsString('-m'); S := ArgParse.AsString('-g', 0, '0'); S := ReplaceText(S, 'KB', '* 1024^1'); S := ReplaceText(S, 'MB', '* 1024^2'); S := ReplaceText(S, 'GB', '* 1024^3'); S := ReplaceText(S, 'K', '* 1024^1'); S := ReplaceText(S, 'M', '* 1024^2'); S := ReplaceText(S, 'G', '* 1024^3'); S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + GPUSize.ToString); GPUMEM := EnsureRange(Round(ExpParse.Evaluate(S)), 0, Max(0, GPUSize - 512 * 1024 * 1024)); S := ArgParse.AsString('-c', 0, '25p'); S := ReplaceText(S, 'KB', '* 1024^1'); S := ReplaceText(S, 'MB', '* 1024^2'); S := ReplaceText(S, 'GB', '* 1024^3'); S := ReplaceText(S, 'K', '* 1024^1'); S := ReplaceText(S, 'M', '* 1024^2'); S := ReplaceText(S, 'G', '* 1024^3'); S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + GPUMEM.ToString); Options.CacheSize := EnsureRange(Round(ExpParse.Evaluate(S)), 0, GPUSize); S := ArgParse.AsString('-t', 0, '50p'); S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + CPUCount.ToString); Options.Threads := Max(1, Round(ExpParse.Evaluate(S))); S := ArgParse.AsString('-dm', 0, '75p'); S := ReplaceText(S, 'KB', '* 1024^1'); S := ReplaceText(S, 'MB', '* 1024^2'); S := ReplaceText(S, 'GB', '* 1024^3'); S := ReplaceText(S, 'K', '* 1024^1'); S := ReplaceText(S, 'M', '* 1024^2'); S := ReplaceText(S, 'G', '* 1024^3'); S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + GetTotalSystemMemory.ToString); B := Pos('%', S) = 0; Options.DedupSysMem := Max(0, Round(ExpParse.Evaluate(S))); if B then Options.DedupSysMem := -Options.DedupSysMem; SrepMemCfg := ArgParse.AsString('-sm', 0, '75p').ToLower; VERBOSE := ArgParse.AsBoolean('-v'); EXTCOMP := ArgParse.AsString('-e'); finally ArgParse.Free; ExpParse.Free; end; if VERBOSE then Options.Threads := 1; end; type TCommonVarsEnc = record MemStream: TArray; DataStore: TDataStore; MemOutput1, MemOutput2, MemOutput3: TArray; CurPos1, CurPos2: TArray; CurTransfer: TArray; InfoStore1: TArray>; InfoStore2: TArray>>; ISIndex: TArray; StrIdx: TArray; end; var DBInfo: TArray>; DBCount: TArray; DDInfo: TArray>; DDCount1: TArray; DDList1: TArray; DDIndex: Integer; ComVars1: TArray; Tasks: TArray; CurCodec: TArray; CurDepth: TArray; DepthInfo: TArray; ThrIdx: TArray; WorkStream: TArray; Scanned1, Scanned2, Processed, Helping: TArray; DoScan2: Boolean; LogInt: Integer; LogInt64: Int64; LogPtr: Pointer; procedure CodecInit(Count: Integer; Method: String); var I, X, Y: Integer; S: String; List: TStringDynArray; begin SetLength(Codecs, 0); Insert(PrecompINI.Codec, Codecs, Length(Codecs)); Insert(PrecompINIEx.Codec, Codecs, Length(Codecs)); Insert(PrecompSearch.Codec, Codecs, Length(Codecs)); Insert(PrecompDLL.Codec, Codecs, Length(Codecs)); Insert(PrecompEXE.Codec, Codecs, Length(Codecs)); Insert(PrecompCrypto.Codec, Codecs, Length(Codecs)); Insert(PrecompZLib.Codec, Codecs, Length(Codecs)); Insert(PrecompLZ4.Codec, Codecs, Length(Codecs)); 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)); I := 0; while PrecompGetCodec(PChar(Method), I, False) <> '' do begin S := PrecompGetCodec(PChar(Method), I, False); if IndexText(PrecompGetCodec(PChar(Method), I, False), List) < 0 then raise Exception.CreateFmt(SPrecompError1, [S]); Inc(I); end; for X := Low(Codecs) to High(Codecs) do begin S := ''; for Y := Low(Codecs[X].Names) to High(Codecs[X].Names) do begin I := 0; while PrecompGetCodec(PChar(Method), I, False) <> '' do begin if SameText(PrecompGetCodec(PChar(Method), I, False), Codecs[X].Names[Y]) then S := S + PrecompGetCodec(PChar(Method), I, True) + SPrecompSep1; Inc(I); end; end; SetLength(S, Length(S) - 1); for I := Low(CurCodec) to High(CurCodec) do begin CurCodec[I] := X; CurDepth[I] := 0; end; Codecs[X].Initialised := Codecs[X].Init(PChar(S), Count, @PrecompFunctions); end; end; procedure CodecFree(Count: Integer); var I, X: Integer; begin for I := Low(CurCodec) to High(CurCodec) do begin CurCodec[I] := 0; CurDepth[I] := 0; end; for I := Low(Codecs) to High(Codecs) do if Codecs[I].Initialised then begin Codecs[I].Free(@PrecompFunctions); Codecs[I].Initialised := False; end; end; function PrecompAllocator(Instance: Integer; Size: Integer): Pointer; begin if WorkStream[Instance].Size < Size then WorkStream[Instance].Size := Size; Result := WorkStream[Instance].Memory; end; function PrecompGetDepthInfo(Index: Integer): TDepthInfo; begin Result := DepthInfo[Index]; end; function PrecompReadFuture(Index: Integer; Position: NativeInt; Buffer: Pointer; Count: Integer): Integer; var X: NativeInt; begin Result := 0; with ComVars1[CurDepth[Index]] do begin if CurDepth[Index] > 0 then begin X := TDataStore2(ComVars1[CurDepth[Index]].DataStore).ActualSize(Index); if Position < X then begin X := Min(X - Position, Count); Move(TDataStore2(ComVars1[CurDepth[Index]].DataStore).Slot(Index) .Memory^, Buffer^, X); Result := X; end; end else Result := TDataStore1(ComVars1[CurDepth[Index]].DataStore). Read(Index, Position, Buffer^, Count); end; 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 WriteLine(Format('[%d] Actual %s stream found at %s ' + S, [CurDepth[0], Codec, (DataStore.Position(0) + Position).ToHexString, InSize, OutSize])) else WriteLine(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; if OutSize < 0 then S := '(%d)' else S := '(%d >> %d)'; WriteLine(Format('[%d] Confirmed %s stream at %s ' + S, [CurDepth[0], Codec, LogInt64.ToHexString, InSize, OutSize])); end; procedure PrecompLogProcess(Codec, Method: PChar; Size1, Size2, Size3: Integer; Status: Boolean); var S1, S2: String; begin 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'; WriteLine(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; Size1, Size2, Size3: Integer; Status: Boolean); var S1, S2: String; begin if not VERBOSE then exit; if Size2 < 0 then S1 := '(%d)' else if Size3 < 0 then S1 := '(%d >> %d)' else 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'; WriteLine(Format(S2, [CurDepth[0], Codec, LogInt64.ToHexString, Size1, Size2, Size3])); end; procedure PrecompLogPatch1(OldSize, NewSize, PatchSize: Integer; Status: Boolean); var S: String; begin if not VERBOSE then exit; if Status then S := '[%d] - Patched stream at %s (%d >> %d) [%d] successfully' else S := '[%d] - Patching stream at %s (%d >> %d) [%d] has failed'; WriteLine(Format(S, [CurDepth[0], LogInt64.ToHexString, OldSize, NewSize, PatchSize])); end; procedure PrecompLogPatch2(OldSize, NewSize, PatchSize: Integer; Status: Boolean); var S: String; begin if not VERBOSE then exit; if Status then S := '[%d] - Patched stream at %s (%d >> %d) [%d] successfully' else S := '[%d] - Patching stream at %s (%d >> %d) [%d] has failed'; WriteLine(Format(S, [CurDepth[0], LogInt64.ToHexString, OldSize, NewSize, PatchSize])); end; procedure PrecompLogReprocess(Method: PChar; Size1, Size2, Size3: Integer; Status: Boolean); var S1, S2: String; begin 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] Reprocessed stream at %s ' + S1 + IfThen(String(Method) <> '', ' using ' + String(Method), '') + ' successfully' else S2 := '[%d] Reprocessing stream at %s ' + S1 + IfThen(String(Method) <> '', ' using ' + String(Method), '') + ' has failed'; WriteLine(Format(S2, [CurDepth[0], 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]), fmCreate) do try WriteBuffer(LogPtr^, Size1); finally Free; end; end; end; procedure PrecompOutput1(Instance: Integer; const Buffer: Pointer; Size: Integer); begin with ComVars1[CurDepth[Instance]] do begin if Assigned(Buffer) and (Size >= 0) then MemOutput1[Instance].WriteBuffer(Buffer^, Size) else MemOutput1[Instance].Position := CurPos1[Instance]; end; end; function PrecompGetCodecIndex(Codec: PChar; Index: PByte; Option: PInteger): Boolean; var I, X, Y: Integer; S: String; begin Result := False; I := 0; while PrecompGetCodec(Codec, I, False) <> '' do begin for X := Low(Codecs) to High(Codecs) do begin for Y := Low(Codecs[X].Names) to High(Codecs[X].Names) do if SameText(PrecompGetCodec(Codec, I, False), Codecs[X].Names[Y]) then begin Index^ := X; S := PrecompGetCodec(Codec, I, True); if Codecs[X].Initialised then if Codecs[X].Parse(PChar(S), Option, @PrecompFunctions) then begin Result := True; break; end; end; if Result then break; end; Inc(I); end; end; procedure PrecompAddStream(Instance: Integer; Info: PStrInfo1; Codec: PChar; DepthInfo: PDepthInfo); var SI1: TEncodeSI; SI2: TFutureSI; LValid: Boolean; LCodec: Byte; LOption: Integer; I: Integer; begin if CurDepth[Instance] > 0 then Inc(Info^.Position, Integer.Size); with ComVars1[CurDepth[Instance]] do begin if (Info^.Position < 0) then begin MemOutput1[Instance].Position := CurPos1[Instance]; exit; end; if Codec <> '' then begin LValid := PrecompGetCodecIndex(Codec, @LCodec, @LOption); if not LValid then begin MemOutput1[Instance].Position := CurPos1[Instance]; exit; end else if LCodec = CurCodec[Instance] then LOption := Info^.Option; end else begin LCodec := CurCodec[Instance]; LOption := Info^.Option; end; if (Info^.NewSize > 0) and (Info^.Position < DataStore.Size(Instance)) and (MemOutput1[Instance].Position - CurPos1[Instance] = Info^.NewSize) then begin AtomicIncrement(EncInfo.Count); FillChar(SI1, SizeOf(TEncodeSI), 0); SI1.ActualPosition := Info^.Position; SI1.StorePosition := CurPos1[Instance]; SI1.OldSize := Info^.OldSize; SI1.NewSize := Info^.NewSize; SI1.Resource := Info^.Resource; SI1.Thread := Instance; SI1.Codec := LCodec; SI1.Scan2 := False; SI1.Option := LOption; PrecompHash('xxh3_128', PByte(DataStore.Slot(Instance).Memory) + SI1.ActualPosition, SI1.OldSize, @SI1.Checksum, SizeOf(SI1.Checksum)); SI1.Status := Info^.Status; if Assigned(DepthInfo) then SI1.DepthInfo := DepthInfo^; InfoStore1[Instance].Add(SI1); end else begin MemOutput1[Instance].Position := CurPos1[Instance]; FillChar(SI2, SizeOf(TFutureSI), 0); SI2.Position := DataStore.Position(Instance) + Info^.Position; SI2.OldSize := Info^.OldSize; SI2.NewSize := Info^.NewSize; SI2.Resource := Info^.Resource; SI2.Codec := LCodec; SI2.Scan2 := True; SI2.Option := LOption; SI2.Status := Info^.Status; if Assigned(DepthInfo) then SI2.DepthInfo := DepthInfo^; if CurDepth[Instance] = 0 then I := (SI2.Position div IntArray[0]) mod IntArray[1] else I := Instance; ThreadSync[I].Acquire; try InfoStore2[I, ISIndex[I].ToInteger].Add(SI2); finally ThreadSync[I].Release; end; end; CurPos1[Instance] := MemOutput1[Instance].Position; end; end; procedure PrecompTransfer(Instance: Integer; Codec: PChar); begin with ComVars1[CurDepth[Instance]] do CurTransfer[Instance] := String(Codec); end; function PrecompStorage(Instance: Integer; Size: PInteger): Pointer; begin with ComVars1[CurDepth[Instance]] do begin Size^ := MemOutput1[Instance].Position - CurPos1[Instance]; Result := PByte(MemOutput1[Instance].Memory) + CurPos1[Instance]; end; end; function PrecompAddResourceEx(Data: Pointer; Size: Integer): Integer; var I: Integer; begin Result := -1; GlobalSync.Acquire; try I := Length(Resources); SetLength(Resources, Succ(I)); Resources[I].Name := Utils.Hash32(0, Data, Size).ToHexString; Resources[I].Size := Size; GetMem(Resources[I].Data, Resources[I].Size); Move(Data^, Resources[I].Data^, Resources[I].Size); Result := I; finally GlobalSync.Release; end; end; function CheckDB(StreamInfo: TEncodeSI; Database: PDatabase): Boolean; var A: Word; I: Integer; LCount: Integer; DB: PDatabase; begin Result := False; Move(StreamInfo.Checksum, A, A.Size); AtomicExchange(LCount, DBCount[A]); for I := 0 to LCount - 1 do begin DB := @DBInfo[A, I]; if (DB^.Size = StreamInfo.OldSize) and CompareMem(@DB^.Checksum, @StreamInfo.Checksum, SizeOf(DB^.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 Move(StreamInfo.Checksum, A, A.Size); if not CheckDB(StreamInfo, nil) then begin GlobalSync.Acquire; try DB.Size := StreamInfo.OldSize; DB.Codec := StreamInfo.Codec; DB.Option := StreamInfo.Option; Move(StreamInfo.Checksum, DB.Checksum, SizeOf(DB.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; Move(StreamInfo.Checksum, A, A.Size); LCount := DDCount1[A]; for I := 0 to LCount - 1 do begin DD := @DDInfo[A, I]; if (DD^.Size = StreamInfo.OldSize) and CompareMem(@DD^.Checksum, @StreamInfo.Checksum, SizeOf(DD^.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 Move(StreamInfo.Checksum, A, A.Size); 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); Move(StreamInfo.Checksum, A, A.Size); if not CheckDD(StreamInfo, nil, @I) then begin DD.Size := StreamInfo.OldSize; Move(StreamInfo.Checksum, DD.Checksum, SizeOf(DD.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; end else 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); var I: Integer; LPtr: Pointer; LSize, LSizeEx: NativeInt; begin with ComVars1[Depth] do for I := Low(Codecs) to High(Codecs) do begin CurPos1[Index] := MemOutput1[Index].Position; CurCodec[Index] := I; CurDepth[Index] := Depth; if Depth = 0 then begin LPtr := DataStore.Slot(Index).Memory; LSize := DataStore.Size(Index); LSizeEx := DataStore.ActualSize(Index); end else begin LPtr := PByte(DataStore.Slot(Index).Memory) + Integer.Size; LSize := PInteger(DataStore.Slot(Index).Memory)^; LSizeEx := LSize; end; Codecs[I].Scan1(Index, Depth, LPtr, LSize, LSizeEx, @PrecompOutput1, @PrecompAddStream, @PrecompFunctions); CurTransfer[Index] := ''; end; end; procedure Scan2(Index, Depth: Integer); var I, J: Integer; X: NativeInt; SI1: _StrInfo2; SI2: TFutureSI; SI3: TEncodeSI; LValid: Boolean; LCodec: Byte; LOption: Integer; begin with ComVars1[Depth] do try InfoStore2[Index, (not ISIndex[Index]).ToInteger].Count := 0; InfoStore2[Index, ISIndex[Index].ToInteger].Sort; InfoStore2[Index, ISIndex[Index].ToInteger].Index := 0; I := InfoStore2[Index, ISIndex[Index].ToInteger].Get(SI2); while I >= 0 do begin if SI2.Scan2 and InRange(SI2.Position, DataStore.Position(Index), Pred(DataStore.Position(Index) + DataStore.Size(Index))) then begin CurPos1[Index] := MemOutput1[Index].Position; CurCodec[Index] := SI2.Codec; CurDepth[Index] := Depth; SI1.OldSize := SI2.OldSize; SI1.NewSize := SI2.NewSize; SI1.Resource := SI2.Resource; SI1.Option := SI2.Option; SI1.Status := SI2.Status; J := 0; 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, @PrecompOutput1, @PrecompFunctions) then begin LValid := True; if CurTransfer[Index] <> '' then begin LValid := PrecompGetCodecIndex(PChar(CurTransfer[Index]), @LCodec, @LOption); if LValid then begin SI2.Codec := LCodec; SI1.Option := LOption; if System.Pos(SPrecompSep2, CurTransfer[Index]) > 0 then SI1.Status := TStreamStatus.Predicted else SI1.Status := TStreamStatus.None; end; CurTransfer[Index] := ''; end; if LValid and InRange(SI2.Position + J, DataStore.Position(Index), DataStore.Position(Index) + DataStore.Size(Index)) and InRange(SI2.Position + J + SI1.OldSize, DataStore.Position(Index), DataStore.Position(Index) + DataStore.ActualSize(Index)) and (MemOutput1[Index].Position - CurPos1[Index] = SI1.NewSize) then begin AtomicIncrement(EncInfo.Count); FillChar(SI3, SizeOf(TEncodeSI), 0); SI3.ActualPosition := NativeInt(SI2.Position - DataStore.Position(Index)) + J; SI3.StorePosition := CurPos1[Index]; SI3.OldSize := SI1.OldSize; SI3.NewSize := SI1.NewSize; SI3.Resource := SI1.Resource; SI3.Thread := Index; SI3.Codec := SI2.Codec; SI3.Scan2 := False; SI3.Option := SI1.Option; SI3.Status := SI1.Status; PrecompHash('xxh3_128', PByte(DataStore.Slot(Index).Memory) + SI3.ActualPosition, SI3.OldSize, @SI3.Checksum, SizeOf(SI3.Checksum)); SI3.DepthInfo := SI2.DepthInfo; InfoStore1[Index].Add(SI3); end else MemOutput1[Index].Position := CurPos1[Index]; end else begin LValid := False; if CurTransfer[Index] <> '' then begin LValid := PrecompGetCodecIndex(PChar(CurTransfer[Index]), @LCodec, @LOption); if LValid then begin SI2.Codec := LCodec; SI2.Option := LOption; if System.Pos(SPrecompSep2, CurTransfer[Index]) > 0 then SI2.Status := TStreamStatus.Predicted else SI2.Status := TStreamStatus.None; end; CurTransfer[Index] := ''; end; MemOutput1[Index].Position := CurPos1[Index]; if LValid then continue; end; end else InfoStore2[Index, (not ISIndex[Index]).ToInteger].Add(SI2); I := InfoStore2[Index, ISIndex[Index].ToInteger].Get(SI2); end; finally ISIndex[Index] := not ISIndex[Index]; end; end; function Process(ThreadIndex, StreamIndex, Index, Depth: Integer): Boolean; var SI1: _StrInfo2; SI2: TEncodeSI; DBTyp: TDatabase; DBBool: Boolean; Errored: Boolean; LValid: Boolean; LCodec: Byte; LOption: Integer; function Reproc(Method: String): Boolean; var Buffer: Pointer; Res: Integer; begin Result := False; with ComVars1[Depth] do begin Buffer := PrecompAllocator(ThreadIndex, SI1.NewSize); Res := PrecompCompress(PChar(Method), PByte(MemOutput1[ThreadIndex].Memory) + SI2.StorePosition, SI1.NewSize, Buffer, SI1.NewSize, nil, 0); if (Res > 0) and (Res < SI1.OldSize) then begin ThreadSync[ThreadIndex].Acquire; try Move(Buffer^, (PByte(DataStore.Slot(ThreadIndex).Memory) + SI2.ActualPosition)^, Res); FillChar((PByte(DataStore.Slot(ThreadIndex).Memory) + SI2.ActualPosition + Res)^, SI1.OldSize - Res, 0); Result := True; finally ThreadSync[ThreadIndex].Release; end; end; PrecompLogReprocess(PChar(Method), SI1.OldSize, SI1.NewSize, Res, Result); end; end; begin Result := False; with ComVars1[Depth] do begin SI2 := InfoStore1[ThreadIndex][StreamIndex]; SI1.OldSize := SI2.OldSize; SI1.NewSize := SI2.NewSize; SI1.Resource := SI2.Resource; SI1.Option := SI2.Option; SI1.Status := SI2.Status; LogInt64 := DataStore.Position(ThreadIndex) + SI2.ActualPosition; LogPtr := PByte(DataStore.Slot(ThreadIndex).Memory) + SI2.ActualPosition; if UseDB and (SI2.Codec > 2) then begin DBBool := CheckDB(SI2, @DBTyp); if DBBool and (SI2.Codec = DBTyp.Codec) then begin if DBTyp.Status = TStreamStatus.Invalid then exit else begin SI1.Option := DBTyp.Option; SI1.Status := TStreamStatus.Database; end; end; end; CurPos1[Index] := MemOutput1[Index].Position; CurCodec[Index] := SI2.Codec; CurDepth[Index] := Depth; try if NOVERIFY and not(SI2.Codec in [5]) then Result := True else if (REPROCESS <> '') and not(SI2.Codec in [5]) then begin Result := False; if Reproc(REPROCESS) then AtomicIncrement(EncInfo.Processed); end else Result := Codecs[SI2.Codec].Process(Index, Depth, PByte(DataStore.Slot(ThreadIndex).Memory) + SI2.ActualPosition, PByte(MemOutput1[ThreadIndex].Memory) + SI2.StorePosition, @SI1, @PrecompOutput1, @PrecompFunctions); except Result := False; end; LValid := False; if (CurTransfer[Index] <> '') then begin LValid := PrecompGetCodecIndex(PChar(CurTransfer[Index]), @LCodec, @LOption); if LValid then begin SI2.Codec := LCodec; SI2.Option := LOption; if System.Pos(SPrecompSep2, CurTransfer[Index]) > 0 then SI2.Status := TStreamStatus.Predicted else SI2.Status := TStreamStatus.None; InfoStore1[ThreadIndex][StreamIndex] := SI2; end; end; CurTransfer[Index] := ''; if LValid then begin MemOutput1[Index].Position := CurPos1[Index]; Result := Process(ThreadIndex, StreamIndex, Index, Depth); exit; end; if UseDB then if not DBBool then begin if Result then begin SI2.Option := SI1.Option; SI2.Status := TStreamStatus.Predicted end else SI2.Status := TStreamStatus.Invalid; AddDB(SI2); end; if Result then begin AtomicIncrement(EncInfo.Processed); SI2.OldSize := SI1.OldSize; SI2.NewSize := SI1.NewSize; SI2.Resource := SI1.Resource; SI2.Option := SI1.Option; SI2.Status := TStreamStatus(SuccessStatus); SI2.ExtPosition := CurPos1[Index]; SI2.ExtSize := MemOutput1[Index].Position - CurPos1[Index]; SI2.ExtThread := Index; InfoStore1[ThreadIndex][StreamIndex] := SI2; CurPos2[Index] := MemOutput2[Index].Position; if Succ(Depth) < Length(ComVars1) then begin with ComVars1[Succ(Depth)].DataStore as TDataStore2 do begin Reset(Index); Load(Index, @SI2.NewSize, SI2.NewSize.Size); Load(Index, PByte(MemOutput1[ThreadIndex].Memory) + SI2.StorePosition, SI2.NewSize); Load(Index, @SI2.ExtSize, SI2.ExtSize.Size); Load(Index, PByte(MemOutput1[Index].Memory) + SI2.ExtPosition, SI2.ExtSize); end; MemOutput3[Index].Position := 0; DepthInfo[Index] := SI2.DepthInfo; try if EncData(nil, MemOutput3[Index], Index, Succ(Depth)) then begin ThreadSync[Index].Acquire; try MemOutput2[Index].WriteBuffer(MemOutput3[Index].Memory^, MemOutput3[Index].Position); finally ThreadSync[Index].Release; end; SI2.StorePosition := CurPos2[Index]; SI2.NewSize := MemOutput2[Index].Position - CurPos2[Index]; SI2.Thread := Index; SI2.ExtPosition := 0; SI2.ExtSize := -1; SI2.ExtThread := 0; InfoStore1[ThreadIndex][StreamIndex] := SI2; CurPos2[Index] := MemOutput2[Index].Position; end; finally FillChar(DepthInfo[Index], SizeOf(TDepthInfo), 0); end; end; end else MemOutput1[Index].Position := CurPos1[Index]; end; end; procedure EncThreadEx(ThreadIndex, Index, Depth: IntPtr); var X: Integer; begin with ComVars1[Depth] do begin try X := AtomicIncrement(StrIdx[ThreadIndex]); while X < InfoStore1[ThreadIndex].Count do begin Process(ThreadIndex, X, Index, Depth); X := AtomicIncrement(StrIdx[ThreadIndex]); end; finally ThreadSync[Index].Acquire; try Helping[Index] := False; finally ThreadSync[Index].Release; end; end; end; end; procedure EncCallThreads(ThreadIndex, Index, Depth: Integer); var I: Integer; begin for I := Low(Tasks) to High(Tasks) do if I <> Index then begin ThreadSync[I].Acquire; try if (Tasks[I].Status = TThreadStatus.tsReady) and (Processed[I] = True) and (Helping[I] = False) then begin Helping[I] := True; Tasks[I].Update(ThreadIndex, I, Depth); Tasks[I].Perform(EncThreadEx); Tasks[I].Start; end; finally ThreadSync[I].Release; end; end; end; procedure EncThread(Y, W: IntPtr); function GetIndex: Integer; var I: Integer; begin if BoolArray(Processed, True) then begin Result := -2; exit; end else Result := -1; for I := Low(Scanned2) to High(Scanned2) do begin if (Scanned2[I] = True) and (Processed[I] = False) then begin Result := I; break; end; end; end; var X, Z: Integer; begin with ComVars1[W] do begin if InRange(Y, Low(InfoStore1), High(InfoStore1)) then begin if VERBOSE then WriteLine(Format('[%d] Performing scan from block %s to %s (%d)', [W, DataStore.Position(0).ToHexString, (DataStore.Position(0) + Pred(DataStore.Size(0))).ToHexString, DataStore.Size(0)])); Scan1(Y, W); if VERBOSE then WriteLine(''); if W = 0 then begin Scanned1[Y] := True; if DoScan2 then while not BoolArray(Scanned1, True) do Sleep(10); end; if DoScan2 then Scan2(Y, W); InfoStore1[Y].Sort; if W = 0 then Scanned2[Y] := True; end; while True do begin if W = 0 then begin Z := GetIndex; while Z = -1 do begin Sleep(10); Z := GetIndex; end; ThrIdx[Y] := Z; if Z < -1 then break; end else Z := Y; if VERBOSE and (InfoStore1[Z].Count > 0) then WriteLine(Format('[%d] Processing streams on block %s to %s (%d)', [W, DataStore.Position(0).ToHexString, (DataStore.Position(0) + Pred(DataStore.Size(0))).ToHexString, DataStore.Size(0)])); X := AtomicIncrement(StrIdx[Z]); while X < InfoStore1[Z].Count do begin if (Succ(W) = Length(ComVars1)) and (Length(Tasks) > 1) then EncCallThreads(Z, Y, W); Process(Z, X, Y, W); if W = 0 then begin Z := GetIndex; while Z = -1 do begin Sleep(10); Z := GetIndex; end; ThrIdx[Y] := Z; if Z < -1 then break; end; X := AtomicIncrement(StrIdx[Z]); end; while not BoolArray(Helping, False) do Sleep(10); if VERBOSE and (InfoStore1[Z].Count > 0) then WriteLine(''); if W = 0 then begin if Z < -1 then break; if X >= InfoStore1[Z].Count then Processed[Z] := True; end else break; end; end; end; procedure EncInit(Input, Output: TStream; Options: PEncodeOptions); var UI32: UInt32; I, J, K: Integer; B: Byte; W: Word; Bytes: TBytes; NI: NativeInt; S: String; DupMethod: Boolean; begin GlobalSync := TCriticalSection.Create; SetLength(ThreadSync, Options^.Threads); for I := Low(ThreadSync) to High(ThreadSync) do ThreadSync[I] := TCriticalSection.Create; I := XTOOL_PRECOMP; if REPROCESS = '' then Output.WriteBuffer(I, I.Size); if UseDB then begin SetLength(DBInfo, $10000); SetLength(DBCount, $10000); for I := Low(DBInfo) to High(DBInfo) do DBCount[I] := 0; end; if StoreDD > -2 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); SetLength(CurDepth, Options^.Threads); SetLength(DepthInfo, Options^.Threads); SetLength(ThrIdx, Options^.Threads); SetLength(WorkStream, Options^.Threads); for I := Low(Tasks) to High(Tasks) do begin if Length(Tasks) > 1 then Tasks[I] := TTask.Create; FillChar(DepthInfo[I], SizeOf(TDepthInfo), 0); WorkStream[I] := TMemoryStream.Create; end; if Options^.LowMem then I := 1 else I := Options^.Threads; SetLength(Scanned1, I); SetLength(Scanned2, I); SetLength(Processed, I); SetLength(Helping, I); SetLength(ComVars1, Options^.Depth); for J := Low(ComVars1) to High(ComVars1) do with ComVars1[J] do begin SetLength(MemStream, Options^.Threads); SetLength(MemOutput1, Options^.Threads); SetLength(MemOutput2, Options^.Threads); SetLength(MemOutput3, Options^.Threads); SetLength(CurPos1, Options^.Threads); SetLength(CurPos2, Options^.Threads); SetLength(CurTransfer, Options^.Threads); if Options^.LowMem and (J = 0) then I := 1 else I := Options^.Threads; SetLength(InfoStore1, I); SetLength(InfoStore2, I, 2); SetLength(ISIndex, I); SetLength(StrIdx, I); for I := Low(Tasks) to High(Tasks) do begin if (J = 0) and (I > 0) then MemStream[I] := MemStream[0] else MemStream[I] := TMemoryStream.Create; MemOutput1[I] := TMemoryStreamEx.Create; MemOutput2[I] := TMemoryStreamEx.Create; MemOutput3[I] := TMemoryStreamEx.Create; end; for I := Low(InfoStore1) to High(InfoStore1) do begin InfoStore1[I] := TListEx.Create(EncodeSICmp); for K := Low(InfoStore2[I]) to High(InfoStore2[I]) do InfoStore2[I, K] := TListEx.Create(FutureSICmp); ISIndex[I] := False; end; if J = 0 then begin DataStore := TDataStore1.Create(Input, True, Length(InfoStore1), Options^.ChunkSize, LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName), '_' + Random($7FFFFFFF).ToHexString + '-storage.tmp'))); IntArray[0] := Options^.ChunkSize; IntArray[1] := I; end else DataStore := TDataStore2.Create(Length(InfoStore1)); end; CodecInit(Options^.Threads, Options^.Method); DBFile := ExpandPath(Options^.DBaseFile); if FileExists(DBFile) then begin with TFileStream.Create(DBFile, fmShareDenyNone) do begin Position := 0; if WorkStream[0].Size < Size then WorkStream[0].Size := Size; ReadBuffer(WorkStream[0].Memory^, Size); Free; end; with WorkStream[0] do begin Position := 0; while Position < Size do begin 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); if REPROCESS = '' then Output.WriteBuffer(Options^.Depth, Options^.Depth.Size); DoScan2 := True; for J := 0 to ExternalMethods.Count - 1 do begin I := 0; while PrecompGetCodec(PChar(Options^.Method), I, False) <> '' do begin if PrecompGetCodec(PChar(S), I, False) = ExternalMethods[J] then begin DoScan2 := True; break; end; Inc(I); end; if DoScan2 then break; end; S := ''; I := 0; while PrecompGetCodec(PChar(Options^.Method), I, False) <> '' do begin if (IndexText(PrecompGetCodec(PChar(Options^.Method), I, False), PrecompINI.Codec.Names) < 0) and (IndexText(PrecompGetCodec(PChar(Options^.Method), I, False), PrecompINIEx.Codec.Names) < 0) and (IndexText(PrecompGetCodec(PChar(Options^.Method), I, False), PrecompSearch.Codec.Names) < 0) then begin if S = '' then S := PrecompGetCodec(PChar(Options^.Method), I, True) else S := S + SPrecompSep1 + PrecompGetCodec(PChar(Options^.Method), I, True); end; Inc(I); end; for J := 0 to ExternalMethods.Count - 1 do begin DupMethod := False; I := 0; while PrecompGetCodec(PChar(S), I, False) <> '' do begin DupMethod := PrecompGetCodec(PChar(S), I, False) = ExternalMethods[J]; if DupMethod then break; Inc(I); end; if not DupMethod then if S = '' then S := ExternalMethods[J] else S := S + SPrecompSep1 + ExternalMethods[J]; end; if REPROCESS = '' then begin Bytes := BytesOf(S); B := Length(Bytes); Output.WriteBuffer(B, B.Size); Output.WriteBuffer(Bytes[0], B); I := Length(Resources); Output.WriteBuffer(I, I.Size); for J := Low(Resources) to High(Resources) do begin Bytes := BytesOf(Resources[J].Name); B := Length(Bytes); Output.WriteBuffer(B, B.Size); Output.WriteBuffer(Bytes[0], B); Output.WriteBuffer(Resources[J].Size, Resources[J].Size.Size); Output.WriteBuffer(Resources[J].Data^, Resources[J].Size); end; ResCount := Length(Resources); Output.WriteBuffer(StoreDD, StoreDD.Size); end; end; procedure EncFree; var UI32: UInt32; I, J, K: Integer; begin EncFreed := True; if Length(Tasks) > 1 then WaitForAll(Tasks); CodecFree(Length(Tasks)); for J := Low(ComVars1) to High(ComVars1) do with ComVars1[J] do begin for I := Low(Tasks) to High(Tasks) do begin MemOutput1[I].Free; MemOutput2[I].Free; MemOutput3[I].Free; if (J = 0) and (I > 0) then continue; MemStream[I].Free; end; for I := Low(InfoStore1) to High(InfoStore1) do begin InfoStore1[I].Free; for K := Low(InfoStore2[I]) to High(InfoStore2[I]) do InfoStore2[I, K].Free; end; DataStore.Free; end; for I := Low(Tasks) to High(Tasks) do begin if Length(Tasks) > 1 then Tasks[I].Free; WorkStream[I].Free; end; FreeResources; GlobalSync.Free; for I := Low(ThreadSync) to High(ThreadSync) do ThreadSync[I].Free; end; function EncData(Input, Output: TStream; Index, Depth: Integer): Boolean; function FSMode(OpenAndUse: Boolean): Word; begin if OpenAndUse then Result := fmOpenReadWrite or fmShareDenyNone else Result := fmCreate; end; var TempOutput: TStream; StreamInfo: TEncodeSI; StreamHeader: TStreamHeader; StreamCount: Integer; BlockSize: Int64; UI32: UInt32; I, J, K, X: Integer; S: String; W: Word; I64: Int64; LastStream, LastPos: Int64; LastIndex: Integer; CurrSize: Cardinal; DupBool: Boolean; DupIdx1, DupIdx2, DupCount: Integer; DupTyp: TDuplicate2; ErrStream: TStringStream; LOutput, LCache: TStream; procedure SaveResources; var C, D: Integer; B: Byte; Bytes: TBytes; begin if Depth = 0 then begin GlobalSync.Acquire; try C := Length(Resources) - ResCount; TempOutput.WriteBuffer(C, C.Size); for D := ResCount to High(Resources) do begin Bytes := BytesOf(Resources[D].Name); B := Length(Bytes); TempOutput.WriteBuffer(B, B.Size); TempOutput.WriteBuffer(Bytes[0], B); TempOutput.WriteBuffer(Resources[D].Size, Resources[D].Size.Size); TempOutput.WriteBuffer(Resources[D].Data^, Resources[D].Size); end; ResCount := Length(Resources); finally GlobalSync.Release; end; end; end; procedure UpdateInfo; begin if NULLOUT then if StoreDD > 0 then EncInfo.SrepSize := TProcessStream(TBufferedStream(LOutput) .Instance).OutSize; if COMPRESS = 1 then EncInfo.CompSize := TLZMACompressStream(Output).OutSize else if COMPRESS = 2 then EncInfo.CompSize := TProcessStream(TBufferedStream(Output) .Instance).OutSize; end; begin if (Depth = 0) then begin LCache := nil; if GPUMEM > 0 then try LCache := TGPUMemoryStream.Create(GPUMEM); except LCache := nil; end; if Assigned(LCache) then LCache.Size := GPUMEM; ErrStream := TStringStream.Create; if NULLOUT then begin if StoreDD > 0 then begin LOutput := TBufferedStream.Create (TProcessStream.Create(ExpandPath(PluginsPath + 'srep.exe', True), '-m' + StoreDD.ToString + ' -s' + SrepInSize + ' - -', GetCurrentDir, nil, Output, ErrStream), False, XTOOL_BSIZE); TProcessStream(TBufferedStream(LOutput).Instance).Execute; end else LOutput := Output; end else if StoreDD > -2 then LOutput := TBufferedStream.Create (TFileStream.Create (LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName), '-dd.tmp')), fmCreate or fmShareDenyNone), False, XTOOL_BSIZE) else LOutput := Output; TempOutput := TCacheWriteStream.Create(LOutput, LCache, True, ccZSTD); end else TempOutput := Output; Result := False; DupIdx1 := 0; with ComVars1[Depth] do begin LastStream := 0; if Depth = 0 then TDataStore1(DataStore).Load; while not DataStore.Done do begin if (Depth = 0) and (Length(Tasks) > 1) then if IsErrored(Tasks) then for I := Low(Tasks) to High(Tasks) do Tasks[I].RaiseLastError; for I := Low(InfoStore1) to High(InfoStore1) do begin if (Depth > 0) and (I <> Index) then continue; InfoStore1[I].Count := 0; StrIdx[I] := -1; if Depth = 0 then begin Scanned1[I] := False; Scanned2[I] := False; Processed[I] := False; Helping[I] := False; end; end; for I := Low(Tasks) to High(Tasks) do begin if (Depth > 0) and (I <> Index) then continue; if Depth = 0 then ThrIdx[I] := 0; MemOutput1[I].Position := 0; MemOutput2[I].Position := 0; CurPos1[I] := 0; CurPos2[I] := 0; CurTransfer[I] := ''; if (Depth = 0) and (Length(Tasks) > 1) then begin Tasks[I].Update(I, Depth); Tasks[I].Perform(EncThread); Tasks[I].Start; end else EncThread(Index, Depth); end; for I := Low(InfoStore1) to High(InfoStore1) do begin if Depth = 0 then begin Inc(EncInfo.InSize, TDataStore1(DataStore).Size(I)); while Processed[I] = False do begin if Length(Tasks) > 1 then if IsErrored(Tasks) then for X := Low(Tasks) to High(Tasks) do Tasks[X].RaiseLastError; Sleep(10); end; for J := Low(ThrIdx) to High(ThrIdx) do while ThrIdx[J] = I do begin if Length(Tasks) > 1 then if IsErrored(Tasks) then for X := Low(Tasks) to High(Tasks) do Tasks[X].RaiseLastError; Sleep(10); end; end else if I <> Index then continue; LastIndex := 0; repeat LastPos := LastStream; MemStream[I].Position := 0; StreamCount := 0; BlockSize := 0; CurrSize := 0; MemStream[I].WriteBuffer(StreamCount, StreamCount.Size); MemStream[I].WriteBuffer(BlockSize, BlockSize.Size); InfoStore1[I].Index := LastIndex; J := InfoStore1[I].Get(StreamInfo); while J >= 0 do begin if (Integer(StreamInfo.Status) <> SuccessStatus) or (LastStream > StreamInfo.ActualPosition) or (StreamInfo.ActualPosition >= DataStore.Size(I)) then begin if LastStream > StreamInfo.ActualPosition then begin if StreamInfo.Status = TStreamStatus(SuccessStatus) then AtomicDecrement(EncInfo.Processed); AtomicDecrement(EncInfo.Count); end; InfoStore1[I].Delete(J); end else begin Inc(StreamCount); DupBool := False; if (Depth = 0) and (StoreDD > -2) then DupBool := not FindOrAddDD(StreamInfo, @DupIdx2, @DupCount); if DupBool then begin Inc(EncInfo.DupCount); if DupCount = 1 then begin Inc(EncInfo.DecMem2, StreamInfo.OldSize); Inc(EncInfo.DecMem3, StreamInfo.NewSize + StreamInfo.ExtSize); end; Inc(EncInfo.DupSize1, StreamInfo.OldSize); Inc(EncInfo.DupSize2, StreamInfo.NewSize + StreamInfo.ExtSize); FillChar(StreamHeader, SizeOf(TStreamHeader), 0); StreamHeader.Kind := DUPLICATED_STREAM; StreamHeader.Option := DupIdx2; end else begin StreamHeader.Kind := DEFAULT_STREAM; if StreamInfo.ExtSize > 0 then StreamHeader.Kind := StreamHeader.Kind or EXTENDED_STREAM; if StreamInfo.ExtSize < 0 then StreamHeader.Kind := StreamHeader.Kind or NESTED_STREAM; StreamHeader.OldSize := StreamInfo.OldSize; StreamHeader.NewSize := StreamInfo.NewSize; StreamHeader.Resource := StreamInfo.Resource; if StreamInfo.ExtSize > 0 then begin Inc(StreamHeader.NewSize, StreamInfo.ExtSize); Inc(StreamHeader.NewSize, StreamInfo.ExtSize.Size); end; StreamHeader.Codec := StreamInfo.Codec; StreamHeader.Option := StreamInfo.Option; Inc(BlockSize, StreamHeader.NewSize); EncInfo.DecMem0 := Max(EncInfo.DecMem0, Max(StreamHeader.OldSize, StreamHeader.NewSize)); Inc(CurrSize, Max(StreamHeader.OldSize, StreamHeader.NewSize)); end; MemStream[I].WriteBuffer(StreamHeader, SizeOf(TStreamHeader)); LastStream := Int64(StreamInfo.ActualPosition) + StreamInfo.OldSize; end; if (Depth = 0) and (CurrSize >= DecodeMemBlock) then break; J := InfoStore1[I].Get(StreamInfo); end; EncInfo.DecMem1 := Max(EncInfo.DecMem1, CurrSize); if InfoStore1[I].Count > 0 then 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); if REPROCESS = '' then begin SaveResources; TempOutput.WriteBuffer(MemStream[I].Memory^, I64); end; if Depth = 0 then Inc(EncInfo.InflSize, I64); InfoStore1[I].Index := LastIndex; J := InfoStore1[I].Get(StreamInfo); while J >= 0 do begin DupBool := False; if (Depth = 0) and (StoreDD > -2) then DupBool := FindDD(StreamInfo, @DupIdx2, @DupCount); if (DupBool = False) or (DupIdx1 = DupIdx2) then begin if StreamInfo.ExtSize < 0 then begin ThreadSync[StreamInfo.Thread].Acquire; try TempOutput.WriteBuffer ((PByte(MemOutput2[StreamInfo.Thread].Memory) + StreamInfo.StorePosition)^, StreamInfo.NewSize); finally ThreadSync[StreamInfo.Thread].Release; end; end else TempOutput.WriteBuffer ((PByte(MemOutput1[StreamInfo.Thread].Memory) + StreamInfo.StorePosition)^, StreamInfo.NewSize); if Depth = 0 then Inc(EncInfo.InflSize, StreamInfo.NewSize); if StreamInfo.ExtSize > 0 then begin TempOutput.WriteBuffer ((PByte(MemOutput1[StreamInfo.ExtThread].Memory) + StreamInfo.ExtPosition)^, StreamInfo.ExtSize); TempOutput.WriteBuffer(StreamInfo.ExtSize, StreamInfo.ExtSize.Size); if Depth = 0 then Inc(EncInfo.InflSize, StreamInfo.ExtSize + StreamInfo.ExtSize.Size); end; end; Inc(DupIdx1); if Succ(J - LastIndex) = StreamCount then break; J := InfoStore1[I].Get(StreamInfo); end; InfoStore1[I].Index := LastIndex; J := InfoStore1[I].Get(StreamInfo); while J >= 0 do begin UI32 := StreamInfo.ActualPosition - LastPos; TempOutput.WriteBuffer(UI32, UI32.Size); if UI32 > 0 then TempOutput.WriteBuffer ((PByte(DataStore.Slot(I).Memory) + LastPos)^, UI32); if Depth = 0 then Inc(EncInfo.InflSize, UI32 + UI32.Size); LastPos := StreamInfo.ActualPosition + StreamInfo.OldSize; if Succ(J - LastIndex) = StreamCount then break; J := InfoStore1[I].Get(StreamInfo); end; Inc(LastIndex, StreamCount); if LastIndex = InfoStore1[I].Count then UI32 := Max(DataStore.Size(I) - LastPos, 0) else UI32 := 0; if REPROCESS = '' then TempOutput.WriteBuffer(UI32, UI32.Size); if UI32 > 0 then TempOutput.WriteBuffer ((PByte(DataStore.Slot(I).Memory) + LastPos)^, UI32); if Depth = 0 then Inc(EncInfo.InflSize, UI32 + UI32.Size); until LastIndex = InfoStore1[I].Count; LastStream := Max(LastStream - DataStore.Size(I), 0); if Depth = 0 then EncInfo.CachedUsed := TCacheWriteStream(TempOutput) .Cached(@EncInfo.CachedComp); if Depth = 0 then if I > 0 then TDataStore1(DataStore).LoadEx; end; if Depth = 0 then begin UpdateInfo; TDataStore1(DataStore).LoadEx; if Length(Tasks) > 1 then WaitForAll(Tasks); end else break; end; if REPROCESS = '' then begin SaveResources; StreamCount := StreamCount.MinValue; TempOutput.WriteBuffer(StreamCount, StreamCount.Size); end; if Depth = 0 then begin Inc(EncInfo.InflSize, StreamCount.Size); EncInfo.CachedUsed := TCacheWriteStream(TempOutput) .Cached(@EncInfo.CachedComp); end; end; if Depth = 0 then begin if DBFile <> '' then begin with WorkStream[0] do begin Position := 0; for W := Low(DBInfo) to High(DBInfo) do begin J := DBCount[W]; 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(DBFile, fmCreate) do begin WriteBuffer(WorkStream[0].Memory^, WorkStream[0].Position); Free; end; end; if StoreDD > -2 then begin with WorkStream[0] do begin Position := 0; UI32 := 0; for I := Low(DDList1) to High(DDList1) do begin 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; Output.WriteBuffer(UI32, UI32.Size); Output.WriteBuffer(WorkStream[0].Memory^, WorkStream[0].Position); try EncFree; finally end; if NULLOUT then begin if StoreDD > 0 then begin TempOutput.Free; TBufferedStream(LOutput).Flush; TProcessStream(TBufferedStream(LOutput).Instance) .WriteBuffer(StoreDD, 0); TProcessStream(TBufferedStream(LOutput).Instance).Wait; TProcessStream(TBufferedStream(LOutput).Instance).Done; UpdateInfo; LOutput.Free; end; end else begin S := TFileStream(TBufferedStream(LOutput).Instance).FileName; TempOutput.Free; TBufferedStream(LOutput).Flush; if StoreDD > 0 then begin with TProcessStream.Create(ExpandPath(PluginsPath + 'srep.exe', True), '-m' + StoreDD.ToString + 'f ' + S + ' -', GetCurrentDir, nil, Output, ErrStream) do try if Execute then begin while Running do begin EncInfo.SrepSize := OutSize; Sleep(100); end; Done; EncInfo.SrepSize := OutSize; end; finally Free; end; end else Output.CopyFrom(TBufferedStream(LOutput).Instance, 0); LOutput.Free; DeleteFile(S); end; end else begin UpdateInfo; TempOutput.Free; try EncFree; finally end; end; S := 'Decompression memory is '; I := ErrStream.DataString.IndexOf(S); J := 0; if I > 0 then begin while ErrStream.DataString.Substring(I + S.Length + J, 1) <> ' ' do Inc(J); EncInfo.SrepMem := ErrStream.DataString.Substring(I + S.Length, J) .ToInteger; end; ErrStream.Free; end; end; type PStreamInfo = ^TStreamInfo; TStreamInfo = record Count: Integer; Pos: TArray; Completed: TArray; procedure Init; procedure Free; procedure SetCount(ACount: Integer); end; TCommonVarsDec = record DecInput, DecOutput: TArray; MemStream1: TArray; MemStream2: TArray; MemInput: TArray; MemOutput1, MemOutput2: TArray; StreamCount: TArray; StreamInfo: TArray; StreamIdx: TArray; BlockPos: TArray; end; procedure TStreamInfo.Init; begin Count := 0; SetLength(Pos, 0); SetLength(Completed, 0); end; procedure TStreamInfo.Free; begin Init; end; procedure TStreamInfo.SetCount(ACount: Integer); begin if ACount > Count then begin SetLength(Pos, ACount); SetLength(Completed, ACount); end; Count := ACount; end; function CalcSysMem: Int64; begin if DupSysMem <= 0 then Result := Max(0, Abs(DupSysMem) - GetUsedProcessMemory(GetCurrentProcess)) else Result := Max(0, DupSysMem - GetUsedSystemMemory); if Result > MEM_LIMIT then Result := MEM_LIMIT; end; var NStream: TArrayStream; DataMgr: TDataManager; ComVars2: TArray; DDList2: TArray; DDCount2: Integer; DDIndex1, DDIndex2: Integer; CacheSize: Int64; procedure PrecompOutput2(Instance: Integer; const Buffer: Pointer; Size: Integer); begin with ComVars2[CurDepth[Instance]] do DecOutput[Instance].WriteBuffer(Buffer^, Size); if (StoreDD > -2) and (CurDepth[Instance] = 0) then if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) then begin NStream.Update(0, NStream.MaxSize(0) + CalcSysMem); DataMgr.Write(DDIndex1, Buffer, Size); end; end; procedure PrecompOutput3(Instance: Integer; const Buffer: Pointer; Size: Integer); begin with ComVars2[CurDepth[Instance]] do MemOutput1[Instance].WriteBuffer(Buffer^, Size); end; procedure DecThread(X, Y, Z: IntPtr); forward; procedure DecCallThreads(Index, Depth: Integer); var I: Integer; begin for I := Low(Tasks) to High(Tasks) do if I <> Index then begin ThreadSync[I].Acquire; try if Tasks[I].Status = TThreadStatus.tsReady then begin Tasks[I].Update(Index, I, Depth); Tasks[I].Perform(DecThread); Tasks[I].Start; end; finally ThreadSync[I].Release; end; end; end; procedure Restore(MT: Boolean; Index1, Index2, Depth: Integer); var X, Y: Integer; Pos: Int64; X64: Int64; SI: _StrInfo3; SH: PStreamHeader; UI32: UInt32; Ptr1, Ptr2: PByte; LOutput: _PrecompOutput; begin with ComVars2[Depth] do begin CurDepth[Index2] := Depth; Pos := 0; X := AtomicIncrement(StreamIdx[Index1]^); while X < StreamCount[Index1]^ do begin if (Succ(Depth) = Length(ComVars2)) and (Length(Tasks) > 1) then DecCallThreads(Index1, Depth); SH := PStreamHeader(MemStream1[Index1].Memory) + X; if MT then begin LOutput := @PrecompOutput3; Pos := StreamInfo[Index1]^.Pos[X]; X64 := Pos + Max(SH^.OldSize, SH^.NewSize); while (BlockPos[Index1]^ < X64) do begin if IsErrored(Tasks) or (BlockPos[Index1]^ < 0) then exit; Sleep(1); end; MemOutput1[Index2].Position := 0; end else begin if (StoreDD > -2) 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[Index1].ReadBuffer(UI32, UI32.Size); if UI32 > 0 then CopyStreamEx(DecInput[Index1], DecOutput[Index1], UI32); end; SI.OldSize := SH^.OldSize; SI.NewSize := SH^.NewSize; SI.Resource := SH^.Resource; SI.Option := SH^.Option; Ptr1 := PByte(MemInput[Index1].Memory) + Pos; if SH^.Kind and EXTENDED_STREAM = EXTENDED_STREAM then begin SI.ExtSize := PInteger(Ptr1 + SI.NewSize - SI.NewSize.Size)^; SI.NewSize := SI.NewSize - SI.ExtSize - SI.ExtSize.Size; Ptr2 := PByte(MemInput[Index1].Memory) + Pos + SI.NewSize; end else Ptr2 := nil; if SH^.Kind and NESTED_STREAM = NESTED_STREAM then begin MemStream2[Index2].Update(Ptr1, SI.NewSize); MemStream2[Index2].Size := SI.NewSize; MemStream2[Index2].Position := 0; MemOutput2[Index2].Position := 0; DecChunk(MemStream2[Index2], MemOutput2[Index2], Index2, Succ(Depth)); SI.NewSize := PInteger(MemOutput2[Index2].Memory)^; Ptr1 := PByte(MemOutput2[Index2].Memory) + SI.NewSize.Size; SI.ExtSize := PInteger(PByte(MemOutput2[Index2].Memory) + SI.NewSize.Size + SI.NewSize)^; Ptr2 := PByte(MemOutput2[Index1].Memory) + SI.NewSize.Size + SI.NewSize + SI.ExtSize.Size; end; if SH^.Kind and DUPLICATED_STREAM = DUPLICATED_STREAM then begin if MT then StreamInfo[Index1]^.Completed[X] := True else DataMgr.CopyData(SH^.Option, DecOutput[Index1]); X := AtomicIncrement(StreamIdx[Index1]^); continue; end; CurCodec[Index1] := SH^.Codec; CurDepth[Index1] := 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(Index2, Depth, Ptr1, Ptr2, SI, LOutput, @PrecompFunctions) = False) then raise Exception.CreateFmt(SPrecompError3, [Codecs[SH^.Codec].Names[Y]]); if MT then begin Ptr1 := PByte(MemInput[Index1].Memory) + Pos; Move(MemOutput1[Index2].Memory^, Ptr1^, SI.OldSize); StreamInfo[Index1]^.Completed[X] := True; end else begin if (StoreDD > -2) and (Depth = 0) then if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) then Inc(DDIndex2); Inc(Pos, SH^.NewSize); end; X := AtomicIncrement(StreamIdx[Index1]^); end; end; end; procedure DecThread(X, Y, Z: IntPtr); begin Restore(True, X, Y, Z); end; procedure DecInit(Input, Output: TStream; Options: PDecodeOptions); var I, J, K: Integer; I64: Int64; B: Byte; Bytes: TBytes; UI32: UInt32; DupTyp: TDuplicate1; LResData: TResData; LStream: TStream; begin GlobalSync := TCriticalSection.Create; SetLength(ThreadSync, Options^.Threads); for I := Low(ThreadSync) to High(ThreadSync) do ThreadSync[I] := TCriticalSection.Create; DupSysMem := Options^.DedupSysMem; I64 := GPUMEM - Options^.CacheSize; if I64 > 0 then begin try LStream := TGPUMemoryStream.Create(I64); except I64 := 0; end; if I64 > 0 then NStream.Add(LStream, I64); end; NStream.Add(TypeInfo(TMemoryStream), CalcSysMem); NStream.Add(TypeInfo(TPrecompVMStream)); Input.ReadBuffer(Options^.Depth, Options^.Depth.Size); Input.ReadBuffer(B, B.Size); SetLength(Bytes, B); Input.ReadBuffer(Bytes[0], B); Options^.Method := StringOf(Bytes); CacheSize := Options^.CacheSize; Input.ReadBuffer(I, I.Size); for J := 0 to I - 1 do begin Input.ReadBuffer(B, B.Size); SetLength(Bytes, B); Input.ReadBuffer(Bytes[0], B); 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; if Options^.Threads > 1 then SetLength(Tasks, Options^.Threads); SetLength(CurCodec, Options^.Threads); SetLength(CurDepth, Options^.Threads); SetLength(DepthInfo, Options^.Threads); SetLength(WorkStream, Options^.Threads); for I := Low(ThreadSync) to High(ThreadSync) do begin if Length(Tasks) > 1 then Tasks[I] := TTask.Create; FillChar(DepthInfo[I], SizeOf(TDepthInfo), 0); WorkStream[I] := TMemoryStream.Create; end; CodecInit(Options^.Threads, Options^.Method); SetLength(ComVars2, Options^.Depth); for J := Low(ComVars2) to High(ComVars2) do with ComVars2[J] do begin SetLength(DecInput, Options^.Threads); SetLength(DecOutput, Options^.Threads); SetLength(MemStream1, Options^.Threads); SetLength(MemStream2, Options^.Threads); SetLength(MemInput, Options^.Threads); SetLength(MemOutput1, Options^.Threads); SetLength(MemOutput2, Options^.Threads); SetLength(StreamCount, Options^.Threads); SetLength(StreamInfo, Options^.Threads); SetLength(StreamIdx, Options^.Threads); SetLength(BlockPos, Options^.Threads); for I := Low(ThreadSync) to High(ThreadSync) do begin if (J = 0) and (I > 0) then begin MemStream1[I] := MemStream1[0]; MemInput[I] := MemInput[0]; StreamCount[I] := StreamCount[0]; StreamInfo[I] := StreamInfo[0]; StreamIdx[I] := StreamIdx[0]; BlockPos[I] := BlockPos[0]; end else begin MemStream1[I] := TMemoryStream.Create; MemInput[I] := TMemoryStream.Create; New(StreamCount[I]); New(StreamInfo[I]); StreamInfo[I]^.Init; New(StreamIdx[I]); New(BlockPos[I]); end; MemStream2[I] := TMemoryStreamEx.Create(False); MemOutput1[I] := TMemoryStream.Create; MemOutput2[I] := TMemoryStream.Create; end; end; DataMgr := TDataManager.Create(NStream); Input.ReadBuffer(StoreDD, StoreDD.Size); end; procedure DecFree; var I, J: Integer; begin WaitForAll(Tasks); CodecFree(Length(ThreadSync)); for J := Low(ComVars2) to High(ComVars2) do with ComVars2[J] do begin for I := Low(ThreadSync) to High(ThreadSync) do begin MemStream2[I].Free; MemOutput1[I].Free; MemOutput2[I].Free; if (J = 0) and (I > 0) then continue; MemStream1[I].Free; MemInput[I].Free; Dispose(StreamCount[I]); StreamInfo[I]^.Free; Dispose(StreamInfo[I]); Dispose(StreamIdx[I]); Dispose(BlockPos[I]); end; end; for I := Low(ThreadSync) to High(ThreadSync) do begin if Length(Tasks) > 1 then Tasks[I].Free; WorkStream[I].Free; end; DataMgr.Free; FreeResources; GlobalSync.Free; for I := Low(ThreadSync) to High(ThreadSync) do ThreadSync[I].Free; end; procedure DecChunk(Input, Output: TStream; Index, Depth: Integer); var StreamHeader: PStreamHeader; BlockSize: Int64; CurrPos: Int64; UI32: UInt32; I, J: Integer; LStream: TProcessStream; LCache: TStream; procedure LoadResources; var C, D: Integer; B: Byte; Bytes: TBytes; LResData: TResData; begin with ComVars2[Depth] do begin if Depth = 0 then begin DecInput[Index].ReadBuffer(C, C.Size); for D := 0 to C - 1 do begin DecInput[Index].ReadBuffer(B, B.Size); SetLength(Bytes, B); DecInput[Index].ReadBuffer(Bytes[0], B); LResData.Name := StringOf(Bytes); DecInput[Index].ReadBuffer(LResData.Size, LResData.Size.Size); GetMem(LResData.Data, LResData.Size); DecInput[Index].ReadBuffer(LResData.Data^, LResData.Size); Insert(LResData, Resources, Length(Resources)); end; end; end; end; begin if Depth = 0 then begin UI32 := 0; if (StoreDD > -2) then begin 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; LogInt64 := 0; LCache := nil; if CacheSize > 0 then try LCache := TGPUMemoryStream.Create(CacheSize); except LCache := nil; end; if Assigned(LCache) then LCache.Size := CacheSize; end; with ComVars2[Depth] do begin if (Depth = 0) and (StoreDD > 0) then begin LStream := TProcessStream.Create(ExpandPath(PluginsPath + 'srep.exe', True), '-d -s -mem' + SrepMemCfg + ' - -', GetCurrentDir, Input, nil); if not LStream.Execute then raise EReadError.CreateRes(@SReadError); DecInput[Index] := TCacheReadStream.Create(LStream, LCache, True, ccZSTD); end else if Depth = 0 then DecInput[Index] := TCacheReadStream.Create(Input, LCache, True, ccZSTD) else DecInput[Index] := Input; DecOutput[Index] := Output; LoadResources; DecInput[Index].ReadBuffer(StreamCount[Index]^, StreamCount[Index]^.Size); while StreamCount[Index]^ >= 0 do begin if IsErrored(Tasks) then for I := Low(Tasks) to High(Tasks) do Tasks[I].RaiseLastError; DecInput[Index].ReadBuffer(BlockSize, BlockSize.Size); if StreamCount[Index]^ > 0 then begin MemStream1[Index].Position := 0; CopyStreamEx(DecInput[Index], MemStream1[Index], StreamCount[Index]^ * SizeOf(TStreamHeader)); CurrPos := 0; if ((Depth > 0) and (Length(Tasks) > 1)) or ((Length(Tasks) > 1) and (StreamCount[Index]^ > 1)) then begin BlockPos[Index]^ := 0; StreamInfo[Index]^.SetCount(StreamCount[Index]^); for J := 0 to StreamCount[Index]^ - 1 do begin StreamInfo[Index]^.Pos[J] := CurrPos; StreamInfo[Index]^.Completed[J] := False; StreamHeader := PStreamHeader(MemStream1[Index].Memory) + J; Inc(CurrPos, Max(StreamHeader^.OldSize, StreamHeader^.NewSize)); end; end; if ((Depth > 0) and (Length(Tasks) > 1)) or ((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)) or ((Length(Tasks) > 1) and (StreamCount[Index]^ > 1)) then begin if Depth = 0 then for I := Low(Tasks) to High(Tasks) do begin Tasks[I].Update(I, I, Depth); Tasks[I].Perform(DecThread); Tasks[I].Start; end; for J := 0 to StreamCount[Index]^ - 1 do begin StreamHeader := PStreamHeader(MemStream1[Index].Memory) + J; MemInput[Index].Position := StreamInfo[Index]^.Pos[J]; if CopyStream(DecInput[Index], MemInput[Index], StreamHeader^.NewSize) <> StreamHeader^.NewSize then begin BlockPos[Index]^ := -1; raise EReadError.CreateRes(@SReadError); end; Inc(BlockPos[Index]^, Max(StreamHeader^.OldSize, StreamHeader^.NewSize)); end; if Depth > 0 then DecThread(Index, Index, Depth); end else CopyStreamEx(DecInput[Index], MemInput[Index], BlockSize); if ((Depth > 0) and (Length(Tasks) > 1)) or ((Length(Tasks) > 1) and (StreamCount[Index]^ > 1)) then begin for J := 0 to StreamCount[Index]^ - 1 do begin StreamHeader := PStreamHeader(MemStream1[Index].Memory) + J; DecInput[Index].ReadBuffer(UI32, UI32.Size); if UI32 > 0 then CopyStreamEx(DecInput[Index], DecOutput[Index], UI32); while (StreamInfo[Index]^.Completed[J] = False) and (IsErrored(Tasks) = False) do Sleep(1); if IsErrored(Tasks) then for I := Low(Tasks) to High(Tasks) do Tasks[I].RaiseLastError; if (StoreDD > -2) and (Depth = 0) then begin Inc(DDIndex1); if ((DDIndex2 < DDCount2) and (DDIndex1 = DDList2[DDIndex2].Index)) then begin NStream.Update(0, NStream.MaxSize(0) + CalcSysMem); 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])^, StreamHeader^.OldSize); end; if Depth = 0 then WaitForAll(Tasks); end else Restore(False, Index, 0, Depth); end; DecInput[Index].ReadBuffer(UI32, UI32.Size); if UI32 > 0 then CopyStreamEx(DecInput[Index], DecOutput[Index], UI32); LoadResources; DecInput[Index].ReadBuffer(StreamCount[Index]^, StreamCount[Index]^.Size); end; if (Depth = 0) and (StoreDD > 0) then begin with LStream do begin Wait; Done; end; end; if Depth = 0 then DecInput[Index].Free; end; end; procedure EncodeStats; var FHandle: THandle; SBInfo: TConsoleScreenBufferInfo; CLine: Integer; SL: TStringList; Coords: TCoord; ulLength: Cardinal; procedure Update; var I, J: Integer; TS: TTimeSpan; CreationTime, ExitTime, KernelTime, UserTime: TFileTime; TT: TSystemTime; I64: Int64; begin GetProcessTimes(GetCurrentProcess, CreationTime, ExitTime, KernelTime, UserTime); FileTimeToSystemTime(TFileTime(Int64(UserTime) + Int64(KernelTime)), TT); if GPUMEM > 0 then begin SL[0] := GPUName + ' (' + ConvertKB2TB(EncInfo.CachedUsed div 1024) + ') ' + IfThen(EncInfo.CachedComp > 0, '[' + ConvertKB2TB(EncInfo.CachedComp div 1024) + ']', '') + ' '; I := 1; end else I := 0; SL[I] := 'Streams: ' + EncInfo.Processed.ToString + ' / ' + EncInfo.Count.ToString; TS := Stopwatch.Elapsed; SL[I + 1] := 'Time: ' + Format('%0:.2d:%1:.2d:%2:.2d', [TS.Hours + TS.Days * 24, TS.Minutes, TS.Seconds]) + ' (CPU ' + Format('%0:.2d:%1:.2d:%2:.2d', [TT.wHour + Pred(TT.wDay) * 24, TT.wMinute, TT.wSecond]) + ')'; I64 := EncInfo.DecMem0 + EncInfo.DecMem1; I64 := I64 div 1024; if StoreDD > -2 then begin J := I + 4; SL[I + 2] := 'Duplicates: ' + EncInfo.DupCount.ToString + ' (' + ConvertKB2TB(EncInfo.DecMem2 div 1024) + ') [' + ConvertKB2TB(EncInfo.DupSize1 div 1024) + ' >> ' + ConvertKB2TB(EncInfo.DupSize2 div 1024) + '] '; if StoreDD > 0 then begin J := I + 5; SL[I + 3] := 'Srep decompression memory: ' + ConvertKB2TB(EncInfo.SrepMem * 1024) + ' [' + ConvertKB2TB((EncInfo.SrepMem * 1024) + (EncInfo.DecMem3 div 1024)) + IfThen(EncInfo.DecMem3 > 0, '*', '') + '] '; end; end else J := I + 3; SL[J] := 'Size: ' + ConvertKB2TB(EncInfo.InSize div 1024) + IfThen(StoreDD > -2, ' >> ' + ConvertKB2TB((EncInfo.InflSize + EncInfo.DupSize2) div 1024), '') + ' >> ' + ConvertKB2TB(EncInfo.InflSize div 1024) + IfThen(StoreDD > 0, ' >> ' + ConvertKB2TB((EncInfo.SrepSize) div 1024), '') + IfThen(COMPRESS > 0, ' >> ' + ConvertKB2TB((EncInfo.CompSize) div 1024), '') + ' '; SetConsoleCursorPosition(FHandle, Coords); WriteConsole(FHandle, PChar(SL.Text), Length(SL.Text), ulLength, nil); end; begin FHandle := GetStdHandle(STD_ERROR_HANDLE); GetConsoleScreenBufferInfo(FHandle, SBInfo); Coords.X := 0; Coords.Y := SBInfo.dwCursorPosition.Y; SL := TStringList.Create; if GPUMEM > 0 then SL.Add('Streams: 0 / 0'); SL.Add('Streams: 0 / 0'); SL.Add('Time: 00:00:00'); if StoreDD > -2 then begin SL.Add('Duplicates: 0 (0.00 MB) [0.00 MB >> 0.00 MB]'); if StoreDD > 0 then SL.Add('Srep decompression memory: 0.00 MB [0.00MB]'); end; SL.Add(''); SL.Add('Size: '); SL.Add(''); while Stopwatch.IsRunning do begin Update; Sleep(500); end; Update; SL.Free; end; procedure DecodeStats; var FHandle: THandle; SBInfo: TConsoleScreenBufferInfo; CLine: Integer; SL: TStringList; Coords: TCoord; ulLength: Cardinal; procedure Update; var TS: TTimeSpan; CreationTime, ExitTime, KernelTime, UserTime: TFileTime; TT: TSystemTime; begin GetProcessTimes(GetCurrentProcess, CreationTime, ExitTime, KernelTime, UserTime); FileTimeToSystemTime(TFileTime(Int64(UserTime) + Int64(KernelTime)), TT); TS := Stopwatch.Elapsed; SL[0] := 'Time: ' + Format('%0:.2d:%1:.2d:%2:.2d', [TS.Hours + TS.Days * 24, TS.Minutes, TS.Seconds]) + ' (CPU ' + Format('%0:.2d:%1:.2d:%2:.2d', [TT.wHour + Pred(TT.wDay) * 24, TT.wMinute, TT.wSecond]) + ')'; SetConsoleCursorPosition(FHandle, Coords); WriteConsole(FHandle, PChar(SL.Text), Length(SL.Text), ulLength, nil); end; begin FHandle := GetStdHandle(STD_ERROR_HANDLE); GetConsoleScreenBufferInfo(FHandle, SBInfo); Coords.X := 0; Coords.Y := SBInfo.dwCursorPosition.Y; SL := TStringList.Create; SL.Add('Time: 00:00:00'); SL.Add(''); while Stopwatch.IsRunning do begin Update; Sleep(500); end; Update; SL.Free; end; procedure Encode(Input, Output: TStream; Options: TEncodeOptions); var Compressed: Byte; LInput, LOutput: TStream; LCache: TStream; begin LCache := nil; { if GPUMEM > 0 then try LCache := TGPUMemoryStream.Create(GPUMEM); except LCache := nil; end; } if Assigned(LCache) then LCache.Size := GPUMEM; LInput := TCacheReadStream.Create(Input, LCache); NULLOUT := TBufferedStream(Output).Instance is TNullStream; FillChar(EncInfo, SizeOf(EncInfo), 0); ConTask := TTask.Create; Stopwatch := TStopwatch.Create; Stopwatch.Start; ConTask.Perform(EncodeStats); if not VERBOSE then ConTask.Start; try EncInit(LInput, Output, @Options); Compressed := COMPRESS; if REPROCESS = '' then Output.WriteBuffer(Compressed, Compressed.Size); if COMPRESS > 0 then begin case COMPRESS of 1: begin LOutput := TLZMACompressStream.Create(Output); with LOutput as TLZMACompressStream do begin Level := Options.CLevel; Threads := Options.CThreads; Dictionary := Options.CDict; Overlap := Options.COverlap; HighCompress := Options.CHighCompress end; end; 2: begin LOutput := TBufferedStream.Create (TProcessStream.Create(ExpandPath(PluginsPath + ExtractExec(EXTCOMP), True), ExtractParams(EXTCOMP), GetCurrentDir, nil, Output), False, XTOOL_BSIZE); TProcessStream(TBufferedStream(LOutput).Instance).Execute; end; end; end else LOutput := Output; EncData(LInput, LOutput, 0, 0); finally if COMPRESS > 0 then begin case COMPRESS of 1: TLZMACompressStream(LOutput).Flush; 2: begin TBufferedStream(LOutput).Flush; TProcessStream(TBufferedStream(LOutput).Instance) .WriteBuffer(StoreDD, 0); TProcessStream(TBufferedStream(LOutput).Instance).Wait; TProcessStream(TBufferedStream(LOutput).Instance).Done; end; end; if COMPRESS = 1 then EncInfo.CompSize := TLZMACompressStream(LOutput).OutSize else if COMPRESS = 2 then EncInfo.CompSize := TProcessStream(TBufferedStream(LOutput) .Instance).OutSize; LOutput.Free; end; try if not EncFreed then EncFree; finally Stopwatch.Stop; end; end; if VERBOSE then EncodeStats; ConTask.Wait; ConTask.Free; LInput.Free; end; procedure Decode(Input, Output: TStream; Options: TDecodeOptions); var Compressed: Byte; LInput: TStream; begin FillChar(EncInfo, SizeOf(EncInfo), 0); ConTask := TTask.Create; if GPUMEM > 0 then WriteLine(GPUName + ' (' + ConvertKB2TB(GPUMEM div 1024) + ')'); Stopwatch := TStopwatch.Create; Stopwatch.Start; ConTask.Perform(DecodeStats); if not VERBOSE then ConTask.Start; NStream := TArrayStream.Create; try DecInit(Input, Output, @Options); Input.ReadBuffer(Compressed, Compressed.Size); if Compressed = 1 then LInput := TLZMADecompressStream.Create(Input) else if Compressed = 2 then begin LInput := TProcessStream.Create (ExpandPath(PluginsPath + ExtractExec(EXTCOMP), True), ExtractParams(EXTCOMP), GetCurrentDir, Input); TProcessStream(LInput).Execute; end else LInput := Input; DecChunk(LInput, Output, 0, 0); finally if Compressed > 0 then begin if Compressed = 2 then with LInput as TProcessStream do begin Wait; Done; end; LInput.Free; end; try NStream.Free; DecFree; finally Stopwatch.Stop; end; end; if VERBOSE then DecodeStats; ConTask.Wait; ConTask.Free; end; initialization PrecompFunctions.GetCodec := @PrecompGetCodec; PrecompFunctions.GetParam := @PrecompGetParam; PrecompFunctions.Allocator := @PrecompAllocator; PrecompFunctions.GetDepthInfo := @PrecompGetDepthInfo; PrecompFunctions.COMPRESS := @PrecompCompress; PrecompFunctions.Decompress := @PrecompDecompress; PrecompFunctions.Encrypt := @PrecompEncrypt; PrecompFunctions.Decrypt := @PrecompDecrypt; PrecompFunctions.Hash := @PrecompHash; PrecompFunctions.EncodePatch := @PrecompEncodePatch; PrecompFunctions.DecodePatch := @PrecompDecodePatch; PrecompFunctions.AddResource := @PrecompAddResource; PrecompFunctions.GetResource := @PrecompGetResource; PrecompFunctions.SearchBinary := @PrecompSearchBinary; PrecompFunctions.SwapBinary := @PrecompSwapBinary; PrecompFunctions.Swap16 := @PrecompSwap16; PrecompFunctions.Swap32 := @PrecompSwap32; PrecompFunctions.Swap64 := @PrecompSwap64; PrecompFunctions.FileOpen := @PrecompFileOpen; PrecompFunctions.FileClose := @PrecompFileClose; PrecompFunctions.FileSeek := @PrecompFileSeek; PrecompFunctions.FileSize := @PrecompFileSize; PrecompFunctions.FileRead := @PrecompFileRead; PrecompFunctions.FileWrite := @PrecompFileWrite; PrecompFunctions.IniRead := @PrecompIniRead; PrecompFunctions.IniWrite := @PrecompIniWrite; PrecompFunctions.Exec := @PrecompExec; PrecompFunctions.ExecStdin := @PrecompExecStdin; PrecompFunctions.ExecStdout := @PrecompExecStdout; PrecompFunctions.ExecStdio := @PrecompExecStdio; PrecompFunctions.ExecStdioSync := @PrecompExecStdioSync; PrecompFunctions.GetDepthCodec := @PrecompGetDepthCodec; PrecompFunctions.ReadFuture := @PrecompReadFuture; PrecompFunctions.LogScan1 := PrecompLogScan1; PrecompFunctions.LogScan2 := PrecompLogScan2; PrecompFunctions.LogProcess := PrecompLogProcess; PrecompFunctions.LogRestore := PrecompLogRestore; PrecompFunctions.LogPatch1 := PrecompLogPatch1; PrecompFunctions.LogPatch2 := PrecompLogPatch2; PrecompFunctions.AcceptPatch := PrecompAcceptPatch; PrecompFunctions.Transfer := PrecompTransfer; PrecompFunctions.Storage := PrecompStorage; PrecompFunctions.AddResourceEx := PrecompAddResourceEx; end.