unit PrecompMain; {$POINTERMATH ON} interface uses Threading, Utils, SynCommons, ParseClass, ParseExpr, PrecompUtils, PrecompCrypto, PrecompZLib, PrecompLZ4, PrecompLZO, PrecompZSTD, 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, System.Generics.Defaults, System.Generics.Collections; const XTOOL_PRECOMP = $304C5458; type PEncodeOptions = ^TEncodeOptions; TEncodeOptions = record Method: String; ChunkSize, Threads: Integer; Depth: Integer; LowMem: Boolean; DBaseFile, ExtractDir: String; end; PDecodeOptions = ^TDecodeOptions; TDecodeOptions = record Method: String; ChunkCount, Threads: Integer; Depth: Integer; DedupSysMem, DedupGPUMem: 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 PrecompOutput1(Instance: Integer; const Buffer: Pointer; Size: Integer); procedure PrecompOutput2(Instance: Integer; const Buffer: Pointer; Size: Integer); procedure PrecompOutput3(Instance: Integer; const Buffer: Pointer; Size: Integer); procedure PrecompAddStream(Instance: Integer; Info: PStrInfo1; Codec: PChar; DepthInfo: PDepthInfo)cdecl; procedure PrecompTransfer(Instance: Integer; Codec: PChar)cdecl; implementation var InternalSync: TCriticalSection; 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: Int64; end; const InternalMem: Int64 = 128 * 1024 * 1024; var GlobalSync: TCriticalSection; ThreadSync: TArray; IntArray: array [0 .. 1] of Int64; Codecs: array of TPrecompressor; DBFile: String = ''; ExtDir: String = ''; UseDB: Boolean = False; StoreDD: Boolean = False; VERBOSE: Boolean = False; EXTRACT: Boolean = False; DupSysMem: Int64 = 0; EncInfo: TEncInfo; ConTask: TTask; Stopwatch: TStopwatch; procedure PrintHelp; var I, J: Integer; S: string; begin WriteLn(ErrOutput, 'precomp - data precompressor'); WriteLn(ErrOutput, ''); WriteLn(ErrOutput, 'Usage:'); WriteLn(ErrOutput, ' xtool precomp [parameters] input output'); WriteLn(ErrOutput, ''); WriteLn(ErrOutput, ''); WriteLn(ErrOutput, 'Parameters:'); WriteLn(ErrOutput, ' -m# - codecs to use for precompression (separate by "+" if more than one)'); WriteLn(ErrOutput, ' -c# - scanning range of precompressor [16mb]'); WriteLn(ErrOutput, ' -t# - number of working threads [50p]'); WriteLn(ErrOutput, ' -lm - low memory mode'); WriteLn(ErrOutput, ' -d# - scan depth [0]'); WriteLn(ErrOutput, ''); WriteLn(ErrOutput, 'Advanced parameters:'); WriteLn(ErrOutput, ' --dbase=# - use database (#=filename to save db, optional)'); WriteLn(ErrOutput, ' --dedup=# - use stream deduplication (#=filename to save db, optional)'); WriteLn(ErrOutput, ' --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; procedure Parse(ParamArg: TArray; out Options: TEncodeOptions); var ArgParse: TArgParser; ExpParse: TExpressionParser; I: Integer; S: String; begin ArgParse := TArgParser.Create(ParamArg); ExpParse := TExpressionParser.Create; try Options.Method := ''; 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('-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'); Options.ChunkSize := Max(4194304, Round(ExpParse.Evaluate(S))); S := ArgParse.AsString('-t', 0, '50p'); S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + CPUCount.ToString); Options.Threads := Max(1, Round(ExpParse.Evaluate(S))); Options.Depth := EnsureRange(Succ(ArgParse.AsInteger('-d')), 1, 10); Options.LowMem := ArgParse.AsBoolean('-lm'); UseDB := ArgParse.AsBoolean('--dbase'); Options.DBaseFile := ArgParse.AsString('--dbase='); if Options.DBaseFile <> '' then UseDB := True; StoreDD := ArgParse.AsBoolean('--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 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('-t', 0, '50p'); S := ReplaceText(S, 'p', '%'); S := ReplaceText(S, '%', '%*' + CPUCount.ToString); Options.Threads := Max(1, Round(ExpParse.Evaluate(S))); S := ArgParse.AsString('--mem=', 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; VERBOSE := ArgParse.AsBoolean('--verbose'); finally ArgParse.Free; ExpParse.Free; end; if VERBOSE then Options.Threads := 1; end; function GetIndex(Scanned, Processed: TArray): Integer; var I: Integer; begin if BoolArray(Processed, True) then begin Result := -2; exit; end else Result := -1; for I := Low(Scanned) to High(Scanned) do begin if (Scanned[I] = True) and (Processed[I] = False) then begin Result := I; break; end; end; 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: TArray; 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(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 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 ' + 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)'; WriteLn(ErrOutput, 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'; 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; 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'; WriteLn(ErrOutput, 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'; WriteLn(ErrOutput, 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'; WriteLn(ErrOutput, Format(S, [CurDepth[0], LogInt64.ToHexString, OldSize, NewSize, PatchSize])); 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; SI1.Checksum := Utils.Hash32(0, PByte(DataStore.Slot(Instance).Memory) + SI1.ActualPosition, SI1.OldSize); 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].Enter; try InfoStore2[I, ISIndex[I].ToInteger].Add(SI2); finally ThreadSync[I].Leave; 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 CheckDB(StreamInfo: TEncodeSI; Database: PDatabase): Boolean; var A: Word; I: Integer; LCount: Integer; DB: PDatabase; begin Result := False; 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; 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; SI3.Checksum := Utils.Hash32(0, PByte(DataStore.Slot(Index).Memory) + SI3.ActualPosition, SI3.OldSize); 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; 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 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].Enter; try MemOutput2[Index].WriteBuffer(MemOutput3[Index].Memory^, MemOutput3[Index].Position); finally ThreadSync[Index].Leave; 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 EncThread(Y, W: IntPtr); var X, Z: Integer; begin with ComVars1[W] do begin if InRange(Y, Low(InfoStore1), High(InfoStore1)) then begin if VERBOSE then WriteLn(ErrOutput, 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 WriteLn(ErrOutput, ''); if W = 0 then begin Scanned1[Y] := True; while not BoolArray(Scanned1, True) do Sleep(10); end; 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(Scanned2, Processed); while Z = -1 do begin Sleep(10); Z := GetIndex(Scanned2, Processed); end; ThrIdx[Y] := Z; if Z < -1 then break; end else Z := Y; if VERBOSE and (InfoStore1[Z].Count > 0) then WriteLn(ErrOutput, 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 Process(Z, X, Y, W); if W = 0 then begin Z := GetIndex(Scanned2, Processed); while Z = -1 do begin Sleep(10); Z := GetIndex(Scanned2, Processed); end; ThrIdx[Y] := Z; if Z < -1 then break; end; X := AtomicIncrement(StrIdx[Z]); end; if VERBOSE and (InfoStore1[Z].Count > 0) then WriteLn(ErrOutput, ''); 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; 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; 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 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(I, 0); 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(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 := Options^.DBaseFile; if FileExists(ExtractFilePath(Utils.GetModuleName) + DBFile) then begin with TFileStream.Create(ExtractFilePath(Utils.GetModuleName) + 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); Output.WriteBuffer(Options^.Depth, Options^.Depth.Size); 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), 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; Bytes := BytesOf(S); LongRec(I).Bytes[0] := Length(Bytes); Output.WriteBuffer(LongRec(I).Bytes[0], LongRec(I).Bytes[0].Size); Output.WriteBuffer(Bytes[0], LongRec(I).Bytes[0]); I := Length(Resources); Output.WriteBuffer(I, I.Size); for J := Low(Resources) to High(Resources) do begin Bytes := BytesOf(Resources[J].Name); LongRec(I).Bytes[0] := Length(Bytes); Output.WriteBuffer(LongRec(I).Bytes[0], LongRec(I).Bytes[0].Size); Output.WriteBuffer(Bytes[0], LongRec(I).Bytes[0]); Output.WriteBuffer(Resources[J].Size, Resources[J].Size.Size); Output.WriteBuffer(Resources[J].Data^, Resources[J].Size); end; Output.WriteBuffer(StoreDD, StoreDD.Size); end; procedure EncFree; var UI32: UInt32; I, J, K: Integer; begin 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; const DecMemLimit = 384 * 1024 * 1024; var TempOutput: TStream; StreamInfo: TEncodeSI; StreamHeader: TStreamHeader; StreamCount: Integer; BlockSize: Int64; UI32: UInt32; I, J, K, X: Integer; W: Word; I64: Int64; LastStream, LastPos: Int64; LastIndex: Integer; CurrSize: Cardinal; DupBool: Boolean; DupIdx1, DupIdx2, DupCount: Integer; DupTyp: TDuplicate2; begin if (Depth = 0) then begin if StoreDD then TempOutput := TPrecompVMStream.Create else TempOutput := Output; 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; 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].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 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 then DupBool := not FindOrAddDD(StreamInfo, @DupIdx2, @DupCount); if DupBool then begin if DupCount = 1 then Inc(EncInfo.DecMem2, StreamInfo.OldSize); 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 CurrSize >= DecMemLimit 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); 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 StoreDD then DupBool := FindDD(StreamInfo, @DupIdx2, @DupCount); if (DupBool = False) or (DupIdx1 = DupIdx2) then begin if StreamInfo.ExtSize < 0 then begin ThreadSync[StreamInfo.Thread].Enter; try TempOutput.WriteBuffer ((PByte(MemOutput2[StreamInfo.Thread].Memory) + StreamInfo.StorePosition)^, StreamInfo.NewSize); finally ThreadSync[StreamInfo.Thread].Leave; end; end else TempOutput.WriteBuffer ((PByte(MemOutput1[StreamInfo.Thread].Memory) + StreamInfo.StorePosition)^, 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); 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); 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; TempOutput.WriteBuffer(UI32, UI32.Size); if UI32 > 0 then TempOutput.WriteBuffer ((PByte(DataStore.Slot(I).Memory) + LastPos)^, UI32); until LastIndex = InfoStore1[I].Count; LastStream := Max(LastStream - DataStore.Size(I), 0); if Depth = 0 then begin if I > 0 then TDataStore1(DataStore).LoadEx; end; end; if Depth = 0 then begin TDataStore1(DataStore).LoadEx; if Length(Tasks) > 1 then WaitForAll(Tasks); end else break; end; StreamCount := StreamCount.MinValue; TempOutput.WriteBuffer(StreamCount, StreamCount.Size); end; if Depth = 0 then begin if DBFile <> '' then begin with WorkStream[0] do begin Position := 0; for W := 0 to $10000 - 1 do begin 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, fmCreate) do begin WriteBuffer(WorkStream[0].Memory^, WorkStream[0].Position); Free; end; end; if StoreDD 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); Output.CopyFrom(TempOutput, 0); TempOutput.Free; end; 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; 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); end; var NStream: TArrayStream; DataMgr: TDataManager; ComVars2: TArray; DDList2: TArray; DDCount2: Integer; DDIndex1, DDIndex2: Integer; BlockPos: Int64; procedure PrecompOutput2(Instance: Integer; const Buffer: Pointer; Size: Integer); begin with ComVars2[CurDepth[Instance]] do DecOutput[Instance].WriteBuffer(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; Size: Integer); begin with ComVars2[CurDepth[Instance]] do MemOutput1[Instance].WriteBuffer(Buffer^, Size); end; procedure Restore(MT: Boolean; Index, 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 Pos := 0; X := AtomicIncrement(StreamIdx[Index]^); while X < StreamCount[Index]^ do begin SH := PStreamHeader(MemStream1[Index].Memory) + X; if MT then begin LOutput := @PrecompOutput3; Pos := StreamInfo[Index]^.Pos[X]; X64 := Pos + Max(SH^.OldSize, SH^.NewSize); while (BlockPos < X64) do begin if IsErrored(Tasks) or (BlockPos < 0) then exit; Sleep(1); end; MemOutput1[Index].Position := 0; 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 CopyStreamEx(DecInput[Index], DecOutput[Index], UI32); end; SI.OldSize := SH^.OldSize; SI.NewSize := SH^.NewSize; SI.Resource := SH^.Resource; SI.Option := SH^.Option; Ptr1 := PByte(MemInput[Index].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[Index].Memory) + Pos + SI.NewSize; end else Ptr2 := nil; if SH^.Kind and NESTED_STREAM = NESTED_STREAM then begin MemStream2[Index].Update(Ptr1, SI.NewSize); MemStream2[Index].Size := SI.NewSize; MemStream2[Index].Position := 0; MemOutput2[Index].Position := 0; DecChunk(MemStream2[Index], MemOutput2[Index], Index, Succ(Depth)); SI.NewSize := PInteger(MemOutput2[Index].Memory)^; Ptr1 := PByte(MemOutput2[Index].Memory) + SI.NewSize.Size; SI.ExtSize := PInteger(PByte(MemOutput2[Index].Memory) + SI.NewSize.Size + SI.NewSize)^; Ptr2 := PByte(MemOutput2[Index].Memory) + SI.NewSize.Size + SI.NewSize + SI.ExtSize.Size; end; if SH^.Kind and DUPLICATED_STREAM = DUPLICATED_STREAM then begin if MT then StreamInfo[Index]^.Completed[X] := True else DataMgr.CopyData(SH^.Option, DecOutput[Index]); X := AtomicIncrement(StreamIdx[Index]^); continue; 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[Y]]); NStream.Update(0, CalcSysMem); if MT then begin Ptr1 := PByte(MemInput[Index].Memory) + Pos; Move(MemOutput1[Index].Memory^, Ptr1^, SI.OldSize); 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; end; procedure DecThread(Y, Z: IntPtr); begin Restore(True, Y, Z); end; procedure DecReadCB(Pos: Int64); begin BlockPos := Pos; end; procedure DecInit(Input, Output: TStream; Options: PDecodeOptions); var I, J: Integer; Bytes: TBytes; UI32: UInt32; 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)); Input.ReadBuffer(Options^.Depth, Options^.Depth.Size); 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]); Options^.Method := StringOf(Bytes); Input.ReadBuffer(I, I.Size); for J := 0 to I - 1 do begin 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)); end; SetLength(Tasks, Options^.Threads); SetLength(CurCodec, Options^.Threads); SetLength(CurDepth, Options^.Threads); SetLength(DepthInfo, Options^.Threads); SetLength(WorkStream, Options^.Threads); for I := Low(Tasks) to High(Tasks) do begin if Length(Tasks) > 1 then Tasks[I] := TTask.Create(I, 0); 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); for I := Low(Tasks) to High(Tasks) 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]; end else begin MemStream1[I] := TMemoryStream.Create; MemInput[I] := TMemoryStream.Create; New(StreamCount[I]); New(StreamInfo[I]); StreamInfo[I]^.Init; New(StreamIdx[I]); end; MemStream2[I] := TMemoryStreamEx.Create(False); MemOutput1[I] := TMemoryStream.Create; MemOutput2[I] := TMemoryStream.Create; end; end; Input.ReadBuffer(StoreDD, StoreDD.Size); UI32 := 0; if StoreDD 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; DataMgr := TDataManager.Create(NStream); end; procedure DecFree; var I, J: Integer; begin if Length(Tasks) > 1 then WaitForAll(Tasks); CodecFree(Length(Tasks)); for J := Low(ComVars2) to High(ComVars2) do with ComVars2[J] do begin for I := Low(Tasks) to High(Tasks) 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]); end; end; for I := Low(Tasks) to High(Tasks) 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; begin if Depth = 0 then LogInt64 := 0; with ComVars2[Depth] do begin DecInput[Index] := Input; DecOutput[Index] := Output; DecInput[Index].ReadBuffer(StreamCount[Index]^, StreamCount[Index]^.Size); while StreamCount[Index]^ >= 0 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; if StreamCount[Index]^ > 0 then begin DecInput[Index].ReadBuffer(BlockSize, BlockSize.Size); MemStream1[Index].Position := 0; CopyStreamEx(DecInput[Index], MemStream1[Index], StreamCount[Index]^ * SizeOf(TStreamHeader)); CurrPos := 0; if (Depth = 0) and (Length(Tasks) > 1) and (StreamCount[Index]^ > 1) then begin BlockPos := 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) 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) then begin for I := Low(Tasks) to High(Tasks) do begin 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 := -1; raise EReadError.CreateRes(@SReadError); end; Inc(BlockPos, Max(StreamHeader^.OldSize, StreamHeader^.NewSize)); end; end else CopyStreamEx(DecInput[Index], MemInput[Index], BlockSize); if (Depth = 0) and (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 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])^, StreamHeader^.OldSize); end; WaitForAll(Tasks); end else Restore(False, Index, Depth); end; DecInput[Index].ReadBuffer(UI32, UI32.Size); if UI32 > 0 then CopyStreamEx(DecInput[Index], DecOutput[Index], UI32); DecInput[Index].ReadBuffer(StreamCount[Index]^, StreamCount[Index]^.Size); end; end; end; procedure EncodeStats; 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; I64: Int64; begin GetProcessTimes(GetCurrentProcess, CreationTime, ExitTime, KernelTime, UserTime); FileTimeToSystemTime(TFileTime(Int64(UserTime) + Int64(KernelTime)), TT); SL[0] := 'Streams: ' + EncInfo.Processed.ToString + '/' + EncInfo.Count.ToString; TS := Stopwatch.Elapsed; SL[1] := 'Time: ' + Format('%0:.2d:%1:.2d:%2:.2d', [TS.Hours + TS.Days * 24, TS.Minutes, TS.Seconds]) + ' (' + Format('%0:.2d:%1:.2d:%2:.2d', [TT.wHour + Pred(TT.wDay) * 24, TT.wMinute, TT.wSecond]) + ')'; I64 := InternalMem + EncInfo.DecMem0 + EncInfo.DecMem1; I64 := I64 div 1024; SL[2] := 'Memory: ' + ConvertKB2TB(I64) + ' (' + ConvertKB2TB(I64 + EncInfo.DecMem2 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; SL.Add('Streams: 0/0'); SL.Add('Time: 00:00:00'); SL.Add('Memory: 0.00 MB (0.00 MB)'); 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]) + ' (' + 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); begin InternalSync.Enter; FillChar(EncInfo, SizeOf(EncInfo), 0); ConTask := TTask.Create; Stopwatch := TStopwatch.Create; Stopwatch.Start; ConTask.Perform(EncodeStats); if not VERBOSE then ConTask.Start; try EncInit(Input, Output, @Options); EncData(Input, Output, 0, 0); finally try EncFree; finally Stopwatch.Stop; end; end; if VERBOSE then EncodeStats; ConTask.Wait; ConTask.Free; InternalSync.Leave; end; procedure Decode(Input, Output: TStream; Options: TDecodeOptions); begin InternalSync.Enter; FillChar(EncInfo, SizeOf(EncInfo), 0); ConTask := TTask.Create; Stopwatch := TStopwatch.Create; Stopwatch.Start; ConTask.Perform(DecodeStats); if not VERBOSE then ConTask.Start; NStream := TArrayStream.Create; try DecInit(Input, Output, @Options); DecChunk(Input, Output, 0, 0); finally try NStream.Free; DecFree; finally Stopwatch.Stop; end; end; if VERBOSE then DecodeStats; ConTask.Wait; ConTask.Free; InternalSync.Leave; end; initialization InternalSync := TCriticalSection.Create; 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; finalization InternalSync.Free; end.