├── README.md ├── changelog.txt ├── hSplit-Help.txt └── src ├── consoleread.pas ├── hSplit.lpi ├── hSplit.lpr └── hsplitthread.pas /README.md: -------------------------------------------------------------------------------- 1 | # hSplit 2 | [hSplit](hSplit-Help.txt) is a small command-line tool that lets you split and join files bit by bit very quickly. 3 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | changelog hSplit 2 | 3 | added & 4 | fixed * 5 | improved # 6 | changed % 7 | 8 | 9 | rev0.02 01.10.2020 10 | * hSplit not working if the root parameter file (params.txt) is not present 11 | and no parameter file was set in the commandline 12 | 13 | 14 | rev0.01 15 | * small issue for joining if a source file is not present 16 | 17 | # size type can now be also the last char (1b or 2k or 3g) 18 | - only one type is allowed: first or last position -------------------------------------------------------------------------------- /hSplit-Help.txt: -------------------------------------------------------------------------------- 1 | hSplit rev(0.01) 2 | ----------------------------------------------------------------- 3 | Syntax 4 | hSplit.exe [Param1 [Value1 [Param2 Value2 ... ...]]] "Source.file" ["..."] 5 | ----------------------------------------------------------------- 6 | Parameter 7 | -h > Show this Help 8 | 9 | -S > Segments: split file into segments (default is split Chunks) 10 | an array of comma separated start and end bytes is used 11 | values for start and end can be an Integer or a Hex string 12 | to use hex values set a first char to "h" -> h0-1000,2abc-4def 13 | the end byte is NOT included in the target file 14 | if an end value is set to 0, it means end of file(last byte) 15 | a byte pair of 0-0 means copy the entire file and will be skipped 16 | the start byte must be smaller than the end byte 17 | 18 | -s > Chunk size: value is an Integer and default means Megabyte (def=20) 19 | a first or last char can be set to define the value type 20 | b=Byte; k=Kilobyte g=Gigabyte 21 | 22 | -c > Chunks count: How many chunks should be written 23 | if not set or set to 0 means infinity until the source file ends (def=0) 24 | 25 | -t > Target folder: if not set or empty the source folder is used 26 | 27 | -n > Target file name: if not set or empty the source filename is used 28 | 29 | -P > Progress interval: Control how often the progress is send to the output 30 | value is a Byte from 0 to 255 31 | value 0 sends no progress numbers; 1=max 255=min (def=100) 32 | 33 | -J > Join files: value is a path for a target file 34 | only the -P switch can be used additionally 35 | 36 | ".." > Source file: MUST be the last parameter! Path to source file 37 | 38 | @".." > Path to a parameter file: MUST be the first parameter! 39 | The parameter file should be a simple text file 40 | You can use any name and extension for the file name 41 | Only source file parameters can be used additionally 42 | ----------------------------------------------------------------- 43 | Parameter file 44 | Each line is used as a parameter 45 | If you don't set a source path in the parameter file, 46 | the source path(s) is(are) read from the command-line. 47 | You can use a root parameter file called "params.txt" 48 | Exists this file in the root folder of hSplit, it will be used automatically. 49 | If the "params.txt" is empty, it will be ignored. 50 | ----------------------------------------------------------------- 51 | Command 52 | You can send a command while hSplit is running. 53 | Command will be executed after pressing the ENTER key 54 | 55 | "e" > User Exit: stops hSplit 56 | ----------------------------------------------------------------- 57 | Exit codes 58 | hSplit sends at the end an exit code 59 | 60 | 0 - all OK 61 | 1 - no source file 62 | 2 - error while loading source file 63 | 3 - no target folder (not found or could not created) 64 | 4 - chunk size too big (bigger than the source file size) 65 | 5 - nothing to split (no data) 66 | 6 - nothing to join (no data) 67 | 7 - parameter file not found 68 | 8 - parameter file is empty (only if used with the cli parameter) 69 | 9 - wrong parameters 70 | 99 - internal Lazarus error 71 | -1 - User Exit 72 | ----------------------------------------------------------------- 73 | Examples 74 | Split chunks: 75 | hSplit.exe "Path\to\source.file" 76 | split entire source file in 20mb chunks 77 | 78 | hSplit.exe -s 100 "Path\to\source.file" 79 | split entire source file in 100mb chunks 80 | 81 | hSplit.exe -s g2 -c 10 "Path\to\source.file" 82 | hSplit.exe -s 2g -c 10 "Path\to\source.file" 83 | split 10 chunks with 2 gigabyte each 84 | 85 | hSplit.exe -t "trg dir" -c 10 -n "trg name" "Path\to\source.file" 86 | split 10 chunks with 20mb each to folder "tar dir" with name "trg name" 87 | the file extension is used from the source file 88 | 89 | Split segments: 90 | hSplit.exe -S 0-1000,5000-6000,500-1500 "Path\to\source.file" 91 | split three segments with 1000 bytes each 92 | 93 | hSplit.exe -S h0-1000,5000-6000,500-1500 "Path\to\source.file" 94 | split three segments with 4096 bytes each 95 | 96 | Join files: 97 | hSplit.exe -J "Path\to trg.file" -P 0 "Path\source.1" "Path\source.2" 98 | join 2 files without progress numbers 99 | 100 | Parameter file: 101 | hSplit.exe @"Path\to MyParam.file" 102 | all parameters are read from the parameter file 103 | 104 | hSplit.exe @"Path\to MyParam.file" "Path\to\source.file" 105 | the source parameter from command-line is used (no path in the parameter file) 106 | (if you want to join files, you can specify more command-line parameters) 107 | -------------------------------------------------------------------------------- /src/consoleread.pas: -------------------------------------------------------------------------------- 1 | unit ConsoleRead; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils 9 | {$IfDef UNIX} 10 | , BaseUnix, termio 11 | {$Else} 12 | , Windows 13 | {$EndIf} 14 | ; 15 | 16 | function ReadSequence(Blocking: boolean = True): string; 17 | 18 | implementation 19 | 20 | {$IfDef UNIX} 21 | 22 | function ReadChar(Blocking: boolean = True): char; 23 | var 24 | oTIO, nTIO: Termios; 25 | {$IfDef NonBlockingStdIn} 26 | flags, 27 | {$Else} 28 | fdsin: tfdSet; 29 | {$EndIf} 30 | res: integer; 31 | begin 32 | res := 1; 33 | Result := #0; 34 | TCGetAttr(1, oTIO); 35 | nTIO := oTIO; 36 | CFMakeRaw(nTIO); 37 | TCSetAttr(1, TCSANOW, nTIO); 38 | if not Blocking then 39 | begin 40 | {$ifDef NonBlockingStdIn} 41 | flags := FpFcntl(StdInputHandle, F_GetFl, 0); 42 | FpFcntl(StdInputHandle, F_SetFl, flags or O_NONBLOCK); 43 | {$Else} 44 | fpFD_ZERO(fdsin); 45 | fpFD_SET(StdInputHandle, fdsin); 46 | res := fpSelect(StdInputHandle + 1, @fdsin, nil, nil, 0); 47 | {$EndIf} 48 | end; 49 | if res > 0 then 50 | res := FpRead(StdInputHandle, Result, 1); 51 | 52 | {$ifDef NonBlockingStdIn} 53 | if res = 0 then 54 | Result := #0; 55 | {$EndIf} 56 | 57 | //restore settings 58 | TCSetAttr(1, TCSANOW, oTIO); 59 | {$ifDef NonBlockingStdIn} 60 | if not Blocking then 61 | FpFcntl(StdInputHandle, F_SetFl, flags); 62 | {$EndIf} 63 | end; 64 | 65 | {$Else} 66 | 67 | var 68 | NextChars: string; 69 | 70 | // found at http://www.cplusplus.com/forum/articles/19975/ 71 | function ReadChar(Blocking: boolean = True): char; 72 | var 73 | STDInHandle: HANDLE; 74 | ConsoleInput: INPUT_RECORD; 75 | RecordCount: cardinal=0; 76 | begin 77 | if NextChars.Length > 0 then 78 | begin 79 | Result := NextChars.Chars[0]; 80 | NextChars := NextChars.Substring(1); 81 | Exit; 82 | end; 83 | STDInHandle := GetStdHandle(STD_INPUT_HANDLE); 84 | GetNumberOfConsoleInputEvents(STDInHandle, RecordCount); 85 | if Blocking or (RecordCount > 0) then 86 | while ReadConsoleInputA(STDInHandle, ConsoleInput, 1, RecordCount) do 87 | if (ConsoleInput.EventType = KEY_EVENT) and 88 | (ConsoleInput.Event.KeyEvent.wVirtualKeyCode <> VK_SHIFT) and 89 | (ConsoleInput.Event.KeyEvent.wVirtualKeyCode <> VK_MENU) and 90 | (ConsoleInput.Event.KeyEvent.wVirtualKeyCode <> VK_CONTROL) and 91 | (ConsoleInput.Event.KeyEvent.bKeyDown) then 92 | begin 93 | if (ConsoleInput.Event.KeyEvent.wVirtualKeyCode >= VK_PRIOR) and 94 | (ConsoleInput.Event.KeyEvent.wVirtualKeyCode <= VK_HOME) then 95 | begin 96 | case ConsoleInput.Event.KeyEvent.wVirtualKeyCode of 97 | VK_HOME: NextChars := '[H'; 98 | VK_END: NextChars := '[F'; 99 | VK_PRIOR: NextChars := '[5~'; 100 | VK_NEXT: NextChars := '[6~'; 101 | end; 102 | Result := #27; 103 | end 104 | else if (ConsoleInput.Event.KeyEvent.wVirtualKeyCode >= VK_F1) and 105 | (ConsoleInput.Event.KeyEvent.wVirtualKeyCode <= VK_F12) then 106 | begin 107 | case ConsoleInput.Event.KeyEvent.wVirtualKeyCode of // F-Keys 108 | VK_F1: NextChars := #79'P'; 109 | VK_F2: NextChars := #79'Q'; 110 | VK_F3: NextChars := #79'R'; 111 | VK_F4: NextChars := #79'S'; 112 | VK_F5: NextChars := #91#49#53'~'; 113 | VK_F6: NextChars := #91#49#55'~'; 114 | VK_F7: NextChars := #91#49#56'~'; 115 | VK_F8: NextChars := #91#49#57'~'; 116 | VK_F9: NextChars := #91#50#48'~'; 117 | VK_F10: NextChars := #91#50#49'~'; 118 | VK_F11: NextChars := #91#50#50'~'; 119 | VK_F12: NextChars := #91#50#52'~'; 120 | end; 121 | Result := #27; 122 | end 123 | else if ConsoleInput.Event.KeyEvent.dwControlKeyState and ($8 or $4) <> 0 then 124 | begin 125 | if (ConsoleInput.Event.KeyEvent.wVirtualKeyCode >= Ord('A')) and 126 | (ConsoleInput.Event.KeyEvent.wVirtualKeyCode <= Ord('Z')) then 127 | Result := chr(ConsoleInput.Event.KeyEvent.wVirtualKeyCode - Ord('A') + 1) 128 | else if (ConsoleInput.Event.KeyEvent.wVirtualKeyCode >= Ord('3')) and 129 | (ConsoleInput.Event.KeyEvent.wVirtualKeyCode <= Ord('7')) then 130 | Result := chr(ConsoleInput.Event.KeyEvent.wVirtualKeyCode - Ord('3') + 27); 131 | end 132 | else 133 | case ConsoleInput.Event.KeyEvent.AsciiChar of 134 | #8, #27, #13, #32..#254: 135 | if ConsoleInput.Event.KeyEvent.dwControlKeyState and $2 = $2 then 136 | begin 137 | NextChars := ConsoleInput.Event.KeyEvent.AsciiChar; 138 | Result := #27; 139 | end 140 | else 141 | Result := ConsoleInput.Event.KeyEvent.AsciiChar; 142 | #9: 143 | begin 144 | if ConsoleInput.Event.KeyEvent.dwControlKeyState and $0010 = $0010 then 145 | begin 146 | NextChars := '[Z'; 147 | Result := #27; 148 | end 149 | else 150 | Result := #9; 151 | end; 152 | #0: 153 | begin 154 | // Arrows 155 | SetLength(NextChars, 2); 156 | NextChars[1] := '['; 157 | case ConsoleInput.Event.KeyEvent.wVirtualKeyCode of 158 | $25: NextChars[2] := 'D'; 159 | $26: NextChars[2] := 'A'; 160 | $27: NextChars[2] := 'C'; 161 | $28: NextChars[2] := 'B'; 162 | end; 163 | Result := #27; 164 | end; 165 | end; 166 | Exit; 167 | end 168 | else 169 | if not Blocking then 170 | break; 171 | Result := #0; 172 | end; 173 | 174 | {$EndIf} 175 | 176 | function ReadSequence(Blocking: boolean = True): string; 177 | var 178 | c: char; 179 | l: integer; 180 | begin 181 | SetLength(Result, 1); 182 | Result[1] := ReadChar(Blocking); 183 | if Result[1] = #0 then 184 | begin 185 | SetLength(Result, 0); 186 | Exit; 187 | end 188 | else if Result[1] <> #27 then 189 | exit; 190 | l := 1; 191 | repeat 192 | c := ReadChar(False); 193 | if c > #0 then 194 | begin 195 | Inc(l); 196 | if l > Result.Length then 197 | SetLength(Result, Result.Length * 2); 198 | Result[l] := c; 199 | end; 200 | until c = #0; 201 | SetLength(Result, l); 202 | end; 203 | 204 | end. 205 | 206 | -------------------------------------------------------------------------------- /src/hSplit.lpi: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | <UseAppBundle Value="False"/> 15 | <ResourceType Value="res"/> 16 | </General> 17 | <BuildModes Count="1"> 18 | <Item1 Name="Default" Default="True"/> 19 | </BuildModes> 20 | <PublishOptions> 21 | <Version Value="2"/> 22 | <DestinationDirectory Value="C:\Users\Hubblec4\Desktop\Neuer Ordner"/> 23 | <UseFileFilters Value="True"/> 24 | </PublishOptions> 25 | <RunParams> 26 | <FormatVersion Value="2"/> 27 | <Modes Count="0"/> 28 | </RunParams> 29 | <RequiredPackages Count="1"> 30 | <Item1> 31 | <PackageName Value="LCLBase"/> 32 | </Item1> 33 | </RequiredPackages> 34 | <Units Count="3"> 35 | <Unit0> 36 | <Filename Value="hSplit.lpr"/> 37 | <IsPartOfProject Value="True"/> 38 | </Unit0> 39 | <Unit1> 40 | <Filename Value="consoleread.pas"/> 41 | <IsPartOfProject Value="True"/> 42 | <UnitName Value="ConsoleRead"/> 43 | </Unit1> 44 | <Unit2> 45 | <Filename Value="hsplitthread.pas"/> 46 | <IsPartOfProject Value="True"/> 47 | <UnitName Value="hSplitThread"/> 48 | </Unit2> 49 | </Units> 50 | </ProjectOptions> 51 | <CompilerOptions> 52 | <Version Value="11"/> 53 | <PathDelim Value="\"/> 54 | <Target> 55 | <Filename Value="hSplit"/> 56 | </Target> 57 | <SearchPaths> 58 | <IncludeFiles Value="$(ProjOutDir)"/> 59 | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> 60 | </SearchPaths> 61 | <Linking> 62 | <Debugging> 63 | <GenerateDebugInfo Value="False"/> 64 | </Debugging> 65 | </Linking> 66 | </CompilerOptions> 67 | <Debugging> 68 | <Exceptions Count="3"> 69 | <Item1> 70 | <Name Value="EAbort"/> 71 | </Item1> 72 | <Item2> 73 | <Name Value="ECodetoolError"/> 74 | </Item2> 75 | <Item3> 76 | <Name Value="EFOpenError"/> 77 | </Item3> 78 | </Exceptions> 79 | </Debugging> 80 | </CONFIG> 81 | -------------------------------------------------------------------------------- /src/hSplit.lpr: -------------------------------------------------------------------------------- 1 | program hSplit; 2 | 3 | (* hSplit 4 | ** 5 | ** Syntax 6 | ** hSplit.exe [Param1 [Value1 [Param2 Value2 ... ...]]] "Source.file" ["..."] 7 | ** 8 | ** Switches 9 | ** -h > The Help 10 | ** 11 | ** -S > Segments: split file into segments (default is split Chunks) 12 | ** An array of comma separated start and end bytes is used 13 | ** values for start and end can be an Integer or a Hex string 14 | ** to use hex values set a first char to "h" -> h0-1000,2000-4000 15 | ** if an end value is set 0 means end of file(last byte) 16 | ** the end byte is NOT included in the target file 17 | ** a byte pair of 0-0 means copy the entire file and will ne skipped 18 | ** the start byte must be smaller than the end byte 19 | ** 20 | ** -s > Chunk size: value is an Integer and default means Megabyte (def=20) 21 | ** a first or last char can be set to define the value type 22 | ** b=Byte; k=Kilobyte g=Gigabyte 23 | ** 24 | ** -c > Chunks count: How many chunks should be written 25 | ** if not set or set to 0 means infinity until the source file ends (def=0) 26 | ** 27 | ** -t > Target folder: if not set or empty the source folder is used 28 | ** 29 | ** -n > Target file name: if not set or empty the source filename is used 30 | ** 31 | ** -P > Progress interval: Control how often the progress is send to the output 32 | ** Value is a Byte from 0 to 255 33 | ** Value 0 sends no progress numbers; 1=max 255=min (def=100) 34 | ** 35 | ** -J > Join files: value is a path for a target file 36 | ** only the -P switch can be used additionally 37 | ** 38 | ** ".." > Source file(s): MUST be the last parameter(s)! Path to source file(s) 39 | ** 40 | ** @".." > Path to a parameter file: MUST be the first parameter! 41 | ** The parameter file should be a simple text file 42 | ** You can use any name and extension for the file name 43 | ** Only source file parameters can be used additionally 44 | ** 45 | ** Parameter file 46 | ** Each line is used as a parameter 47 | ** If you don't set a source path in the parameter file, 48 | ** the source path(s) is(are) read from the commandline. 49 | ** You can use a root parameter file called "params.txt" 50 | ** Exists this file in the root folder of hSplit, it will be used automatically. 51 | ** If the "params.txt" is empty, it will be ignored. 52 | ** 53 | ** Command 54 | ** You can send a command while hSplit is running. 55 | ** Command will be executed after pressing the ENTER key 56 | ** 57 | ** "e" > User Exit: stops hSplit 58 | ** 59 | ** Exit codes 60 | ** hSplit sends at the end an exit code 61 | 62 | ** 0 - all OK 63 | ** 1 - no source file 64 | ** 2 - error while loading source file 65 | ** 3 - no target folder (not found or could not created) 66 | ** 4 - chunk size too big (bigger than the source file size) 67 | ** 5 - nothing to split (no data) 68 | ** 6 - nothing to join (no data) 69 | ** 7 - parameter file not found 70 | ** 8 - parameter file is empty (only if used with the cli parameter) 71 | ** 9 - wrong parameters 72 | ** 99 - internal Lazarus error 73 | ** -1 - User Exit 74 | 75 | 76 | *) 77 | 78 | {$mode objfpc}{$H+} 79 | 80 | uses 81 | // Original 82 | //{$IFDEF UNIX}{$IFDEF UseCThreads} 83 | //cthreads, 84 | //{$ENDIF}{$ENDIF} 85 | {$ifdef unix} 86 | cthreads, 87 | cmem,// the c memory manager is on some systems much faster for multi-threading 88 | {$endif} 89 | Classes, SysUtils, CustApp, RegExpr, 90 | ConsoleRead, hSplitThread; 91 | 92 | // Compiler switches 93 | //{$define debug} 94 | 95 | const 96 | hSplitTitle = 'hSplit rev(0.02)'; 97 | Interval_Factor = 1024; 98 | hsParams_txt = 'params.txt'; 99 | 100 | type 101 | 102 | 103 | { THSplit } 104 | 105 | THSplit = class(TCustomApplication) 106 | private // vars 107 | FhSplitThread : ThSplitThread; 108 | FProgress_Interval : Integer; 109 | FProgress_IntervalCounter: Integer; 110 | 111 | FErrorText : String; 112 | 113 | FPara_SplitSegments : Boolean; // Segmente splitten 114 | FPara_ChunkSize : String; 115 | FPara_ChunkCount : String; 116 | FPara_SegSplitArray : String; 117 | FPara_ProgressInterval: String; // 0=no 1=max 255=min def=100 118 | FPara_TrgFolder : String; 119 | FPara_TrgFilename : String; 120 | FPara_Source : String; 121 | FJoinFiles : TStringList; // Datei Pfade 122 | 123 | private // Prozeduren 124 | function Check_Params: Boolean; 125 | function Check_HelpParam: Boolean; 126 | 127 | function Parse_Params: Boolean; 128 | function Start_hSplit: Boolean; 129 | 130 | procedure Log_ProgressNums; 131 | procedure Log_End; 132 | procedure DoUserExit; 133 | 134 | protected 135 | procedure DoRun; override; 136 | public 137 | constructor Create(TheOwner: TComponent); override; 138 | destructor Destroy; override; 139 | procedure WriteHelp; virtual; 140 | end; 141 | 142 | { THSplit } 143 | 144 | 145 | (*------------ private -------------------------------------------------------*) 146 | 147 | 148 | // Parameter Check 149 | function THSplit.Check_Params: Boolean; 150 | var 151 | errMsg: String; 152 | begin 153 | Result:=false; 154 | (* Keine Params *) 155 | if ParamCount = 0 then // Keine Params 156 | begin 157 | WriteHelp; // Hilfe anzeigen 158 | Exit; // fertig 159 | end; 160 | 161 | errMsg:=CheckOptions('hSsctnPJ', '',true); 162 | if errMsg <> '' then 163 | begin 164 | ExitCode:=hsErr_WrongParams; // Exitcode setzen 165 | ShowException(Exception.Create(errMsg)); 166 | //ReadLn; 167 | Exit; 168 | end; 169 | Result:=true; 170 | end; 171 | 172 | 173 | // Hilfe Parameter Check 174 | function THSplit.Check_HelpParam: Boolean; 175 | begin 176 | Result:=HasOption('h', ''); 177 | if Result then WriteHelp; 178 | end; 179 | 180 | 181 | // Parameter parsen 182 | function THSplit.Parse_Params: Boolean; 183 | var 184 | p: Integer; 185 | param,paramValue: String; 186 | doContinue: Boolean=false; 187 | Params_txtIsUsed: Boolean=false; 188 | 189 | 190 | // Param Value prüfen 191 | function Check_ParamValue: Boolean; 192 | var 193 | regex :TRegExpr=nil; 194 | expression,msg: String; 195 | begin 196 | Result:=false; 197 | case param of 198 | (* Segments *) 199 | '-S': 200 | begin 201 | if pos (',',paramValue) > 0 then 202 | expression:='[h]?[\da-fA-F]+[\-][\da-fA-F]+([\,][\da-fA-F]+[\-][\da-fA-F]+)+' 203 | else 204 | expression:='[h]?[\da-fA-F]+[\-][\da-fA-F]+'; 205 | msg:='Error parameter '+inttostr(p+1) 206 | +': incorrect format of the split array' 207 | +#13#10'usage: (h)start-end,start-end,...'; 208 | end; 209 | (* Chunk size *) 210 | '-s': 211 | begin 212 | expression:='^\d+$|^[bkg]\d+$|^\d+[bkg]$'; 213 | msg:='Error parameter '+inttostr(p+1) 214 | +': incorrect format of the chuck size' 215 | +#13#10'usage: 123 or b456 or k789 or g1 or 456b or 789k or 1g'; 216 | end; 217 | (* Chunk count *) 218 | '-c': 219 | begin 220 | expression:='\d+'; 221 | msg:='Error parameter '+inttostr(p+1) 222 | +': incorrect format of the chuck count' 223 | +#13#10'usage: an Integer 123'; 224 | end; 225 | (* Progress Interval *) 226 | '-P': 227 | begin 228 | expression:='\d+'; 229 | msg:='Error parameter '+inttostr(p+1) 230 | +': incorrect format of the progress interval' 231 | +#13#10'usage: an Integer from 0 to 255'; 232 | end; 233 | 234 | end; 235 | 236 | (* Prüfen *) 237 | regex:=TRegExpr.Create(expression); 238 | Result:=regex.Exec(paramValue); // Value String prüfen OK 239 | FreeAndNil(regex); 240 | if not Result then // NICHT OK 241 | begin 242 | WriteLn(msg); // Message ausgeben 243 | ReadLn; 244 | end; 245 | end; 246 | 247 | 248 | // Param parsen 249 | function Parse_Param: Boolean; 250 | begin 251 | Result:=false; 252 | case param of // Param auswerten 253 | '-S': // Segments 254 | begin 255 | FPara_SplitSegments:=true; // Segmente splitten 256 | if not Check_ParamValue then Exit; // Param Value prüfen 257 | FPara_SegSplitArray:=paramValue; // paramValue sichern 258 | doContinue:=true; // nächsten Param überspringen 259 | end; 260 | 261 | '-s': // Chunk size 262 | begin 263 | if not Check_ParamValue then Exit; // Param Value prüfen 264 | FPara_ChunkSize:=paramValue; // paramValue sichern 265 | doContinue:=true; // nächsten Param überspringen 266 | end; 267 | 268 | '-c': // Chunk count 269 | begin 270 | if not Check_ParamValue then Exit; // Param Value prüfen 271 | FPara_ChunkCount:=paramValue; // paramValue sichern 272 | doContinue:=true; // nächsten Param überspringen 273 | end; 274 | 275 | '-t': // Target Folder 276 | begin 277 | FPara_TrgFolder:=paramValue; // paramValue sichern 278 | doContinue:=true; // nächsten Param überspringen 279 | end; 280 | 281 | '-n': // Target file name 282 | begin 283 | FPara_TrgFilename:=paramValue; // paramValue sichern 284 | doContinue:=true; // nächsten Param überspringen 285 | end; 286 | 287 | '-P': // Progress interval 288 | begin 289 | if not Check_ParamValue then Exit; // Param Value prüfen 290 | FPara_ProgressInterval:=paramValue; // paramValue sichern 291 | doContinue:=true; // nächsten Param überspringen 292 | end; 293 | 294 | '-J': // Join files 295 | begin 296 | (* Ziel Pfad *) 297 | FPara_TrgFilename:=paramValue; // paramValue sichern 298 | FPara_TrgFolder:=ExtractFileDir(paramValue);// Ziel Ordner sichern 299 | FJoinFiles:=TStringList.Create; // Join File List erzeugen 300 | doContinue:=true; // nächsten Param überspringen 301 | end; 302 | 303 | end; 304 | 305 | (* Join Pfade *) 306 | if FJoinFiles <> nil then // Join List vorhanden 307 | FJoinFiles.Add(param); // Datei pfad aufnehmen 308 | Result:=true; 309 | end; 310 | 311 | 312 | // Param.txt prüfen 313 | function Check_Params_txt: Boolean; 314 | var 315 | s,s_Count: integer; 316 | params_txt_by_param1: Boolean=false; 317 | FParams_txt : TStringList; 318 | FParams_txtPath : String; 319 | 320 | begin 321 | Result:=false; 322 | (* Params.txt ist erster cli Param *) 323 | param:=Params[1]; // 1.Param ermitteln 324 | if param[1] = '@' then // @ Zeichen gefunden 325 | begin 326 | params_txt_by_param1:=true; // params file im 1.param 327 | FParams_txtPath:=copy(param,2,Length(param));// Pfad ermitteln 328 | end 329 | else // Params.txt im Root suchen 330 | FParams_txtPath:=ExtractFileDir(ExeName) // Pfad im Root ordner 331 | +DirectorySeparator+hsParams_txt; 332 | 333 | if not FileExists(FParams_txtPath) then // Datei nicht da 334 | begin 335 | if params_txt_by_param1 then // erster Param wurde benutzt 336 | begin 337 | ExitCode:=hsErr_NoParams_txt; // Params Datei nicht gefunden 338 | WriteLn('The params file could not be found.'); 339 | Exit; 340 | end; 341 | // Param Datei check erfolgreich aber nicht benutzt 342 | Result:=true; // Result auf true 343 | Exit; 344 | end; 345 | 346 | (* Params Datei verarbeiten *) 347 | FParams_txt:=TStringList.Create; // Params Liste erzeugen 348 | FParams_txt.LoadFromFile(FParams_txtPath); // Liste laden 349 | 350 | s_Count:=FParams_txt.Count; // Anzahl strings ermitteln 351 | 352 | (* Leere Params Datei - nur bei 1.cli param *) 353 | if s_Count = 0 then // Datei ist leer 354 | begin 355 | FParams_txt.Free; // Liste freigeben 356 | if params_txt_by_param1 then // Params Datei bei param1 357 | begin 358 | ExitCode:=hsErr_Params_txtEmpty; // Params Datei leer 359 | WriteLn('The params file is empty.'); 360 | end 361 | else // Root Param Datei 362 | // Param Datei check erfolgreich aber nicht benutzt 363 | Result:=true; // Result auf true 364 | Exit; // Abbruch 365 | end; 366 | 367 | for s:=0 to s_Count -1 do // Liste durchgehen 368 | begin 369 | if doContinue then // Weiter wenn nötig 370 | begin 371 | doContinue:=false; // Continue beenden 372 | param:=''; // Param leeren 373 | Continue; // weiter 374 | end; 375 | 376 | param:=FParams_txt[s]; // Parmater String ermitteln 377 | if s < s_Count-1 then // maximal vorletzter param 378 | paramValue:=FParams_txt[s+1] // nächster param ist value 379 | else paramValue:=''; // ansonst paramValue leeren 380 | 381 | if not Parse_Param then // Param parsen erfolgreich?? 382 | begin 383 | FParams_txt.Free; // Liste freigeben 384 | Exit; // Abbruch 385 | end; 386 | end; 387 | 388 | (* Last Param = Source *) 389 | if FJoinFiles = nil then // nicht fürs Joinen 390 | begin 391 | FPara_Source:=param; // letzten param sichern 392 | if param = '' then // keine Source Datei 393 | // der letzte Param aus den cli commands wird genutzt 394 | if ParamCount > 0 then // mindestens ein Param 395 | FPara_Source:=Params[ParamCount]; // letzter cli Param 396 | end 397 | else 398 | // Join Liste prüfen 399 | begin 400 | if FJoinFiles.Count = 0 then // Keine Source Dateien 401 | if params_txt_by_param1 then // Param_txt param1 402 | s:=2 else s:=1; 403 | for s:=s to ParamCount do // Source Params durchgehen 404 | FJoinFiles.Add(Params[s]); // Pfade sichern 405 | end; 406 | 407 | Params_txtIsUsed:=true; // Param Datei wurde benutzt 408 | Result:=true; 409 | end; 410 | 411 | 412 | begin 413 | Result:=false; 414 | (* Params Datei *) 415 | if not Check_Params_txt then Exit; // Params Datei prüfen erfolg?? 416 | if Params_txtIsUsed then // Params Datei wurde verwendet 417 | begin 418 | Result:=true; // Result auf true 419 | Exit; // fertig 420 | end; 421 | if ExitCode > 0 then Exit; // Abbruch ExitCode größer 0 422 | 423 | for p:=1 to ParamCount do // cli Params durchgehen 424 | begin 425 | if doContinue then // Weiter wenn nötig 426 | begin 427 | doContinue:=false; // Continue beenden 428 | param:=''; // Param leeren 429 | Continue; // weiter 430 | end; 431 | 432 | param:=Params[p]; // Parmater String ermitteln 433 | 434 | if p < ParamCount then // maximal vorletzter param 435 | paramValue:=Params[p+1] // nächster param ist value 436 | else paramValue:=''; // ansonst paramValue leeren 437 | 438 | if not Parse_Param then Exit; // Param parsen 439 | end; 440 | 441 | (* Last Param = Source *) 442 | if FJoinFiles = nil then // nicht fürs Joinen 443 | FPara_Source:=param; // letzten param sichern 444 | Result:=true; 445 | end; 446 | 447 | 448 | // Start hSplit 449 | function THSplit.Start_hSplit: Boolean; 450 | var 451 | sBytes,eBytes: QWordArray; 452 | interval: Integer; 453 | info: String; 454 | 455 | 456 | // Segment split Bytes verarbeiten 457 | procedure Process_SegmentSplitBytes; 458 | var 459 | regex: TRegExpr; 460 | s,e: TStringList; 461 | b: Integer; 462 | begin 463 | s:=TStringList.Create; e:=TStringList.Create; 464 | regex:=TRegExpr.Create('([\da-fA-F]+)-([\da-fA-F]+)'); 465 | if regex.Exec(FPara_SegSplitArray) then // regex hat was gefunden 466 | repeat 467 | s.Add(regex.Match[1]); // 1.Match = Start 468 | e.Add(regex.Match[2]); // 2.Match = End 469 | until not regex.ExecNext; // Kein Next 470 | 471 | SetLength(sBytes,s.Count); // Start array einstellen 472 | SetLength(eBytes,e.Count); // End array einstellen 473 | 474 | if FPara_SegSplitArray[1] = 'h' then // hex Zahlen 475 | for b:=0 to s.Count -1 do // Anzahl Splits verarbeiten 476 | begin 477 | sBytes[b]:=StrToQWord('$'+s[b]); // Start String zu QWord 478 | eBytes[b]:=StrToQWord('$'+e[b]); // End String zu QWord 479 | end 480 | else // Normale Zahlen 481 | for b:=0 to s.Count -1 do // Anzahl Splits verarbeiten 482 | begin 483 | sBytes[b]:=StrToQWord(s[b]); // Start String zu QWord 484 | eBytes[b]:=StrToQWord(e[b]); // End String zu QWord 485 | end; 486 | s.Free; e.Free; regex.Free; 487 | end; 488 | 489 | // Chunk size verarbeiten 490 | procedure Process_ChunkSize; 491 | const kb = 1024; mb = 1024*1024; gb = 1024*1024*1024; 492 | var 493 | c_size,c: String; 494 | q_size: QWord; 495 | l: Integer; 496 | begin 497 | l:=Length(FPara_ChunkSize); // Länge ermitteln 498 | c:=FPara_ChunkSize[1]; // erstes Zeichen 499 | (* Type - 1.Pos *) 500 | if pos(c,'bkg') > 0 then // Typen Definition 1.Stelle 501 | begin 502 | c_size:=copy(FPara_ChunkSize,2,l); // Type-Zeichen abtrennen 503 | q_size:=strtoint(c_size); // String in Zahl umwandeln 504 | case c of // Erstes Zeichen auswerten 505 | 'b': FhSplitThread.ChunkSize:=q_size; // Byte Angabe 506 | 'k': FhSplitThread.ChunkSize:=q_size*kb; // Kilo Byte Angabe 507 | 'g': FhSplitThread.ChunkSize:=q_size*gb; // Giga Byte Angabe 508 | end; 509 | Exit; 510 | end; 511 | (* Type - Letzte Pos *) 512 | c:=FPara_ChunkSize[l]; // letztes Zeichen 513 | if pos(c,'bkg') > 0 then // Typen Definition 1.Stelle 514 | begin 515 | c_size:=copy(FPara_ChunkSize,1,l-1); // Type-Zeichen abtrennen 516 | q_size:=strtoint(c_size); // String in Zahl umwandeln 517 | case c of // Erstes Zeichen auswerten 518 | 'b': FhSplitThread.ChunkSize:=q_size; // Byte Angabe 519 | 'k': FhSplitThread.ChunkSize:=q_size*kb; // Kilo Byte Angabe 520 | 'g': FhSplitThread.ChunkSize:=q_size*gb; // Giga Byte Angabe 521 | end; 522 | end 523 | else 524 | if pos(FPara_ChunkSize[1],'bkg') = 0 then // Keine Typen Definition 525 | begin 526 | q_size:=strtoint(FPara_ChunkSize); // String in Zahl umwandeln 527 | FhSplitThread.ChunkSize:=q_size*mb; // Chunk Size setzen > MegaByte 528 | end; 529 | end; 530 | 531 | begin 532 | Result:=false; 533 | (* Progress Interval *) 534 | if FPara_ProgressInterval <> '' then // Interval vorhanden 535 | begin 536 | interval:=strtoint(FPara_ProgressInterval); // Integer erzeugen 537 | if interval > 255 then interval:=255; // größer 255 prüfen 538 | FProgress_Interval:=interval*Interval_Factor; // Interval Faktor anwenden 539 | FProgress_IntervalCounter:=FProgress_Interval;// Interval Counter einstellen 540 | end; 541 | 542 | (* Split Thread erzeugen *) 543 | FhSplitThread:=ThSplitThread.Create; // Split Thread erzeugen 544 | FhSplitThread.Source:=FPara_Source; // Source setzen 545 | FhSplitThread.TargetFolder:=FPara_TrgFolder; // Target Folder setzen 546 | FhSplitThread.TargetFilename:=FPara_TrgFilename;// Target file name setzen 547 | 548 | (* Join *) 549 | if FJoinFiles <> nil then // Join Liste vorhanden 550 | begin 551 | info:='join files'; // Info - Dateien joinen 552 | FhSplitThread.Insert_JoinFiles(FJoinFiles); // Join Dateien übertragen 553 | end 554 | else 555 | 556 | (* Segments *) 557 | if FPara_SplitSegments then // Segmente splitten 558 | begin 559 | info:='split segments'; // Info - split segments 560 | FhSplitThread.SegmentSplit:=true; // Segmente splitten true 561 | Process_SegmentSplitBytes; // Split Bytes verarbeiten 562 | FhSplitThread.Insert_SegmentSplits(sBytes,eBytes); // Split Bytes einfügen 563 | end 564 | else 565 | (* Chunks *) 566 | begin 567 | info:='split chunks'; // Info - split chunks 568 | if FPara_ChunkSize <> '' then // Chunk size vorhanden 569 | Process_ChunkSize; // Chunk Size verarbeiten 570 | if FPara_ChunkCount <> '' then // Chunk Count vorhanden 571 | FhSplitThread.ChunkCount:=strtoint(FPara_ChunkCount); // Chunk Count setzen 572 | end; 573 | (* Thread Start *) 574 | FhSplitThread.Start; // Thread starten 575 | WriteLn('hSplit Start - '+info); // start ausgeben 576 | Result:=true; 577 | end; 578 | 579 | 580 | // Log - Progress Nums 581 | procedure THSplit.Log_ProgressNums; 582 | var 583 | total,chunk: Single; 584 | t,c: String; 585 | begin 586 | if ExitCode > 0 then Exit; // Abbruch Fehler Code > 0 587 | if FhSplitThread.Finished then // Thread ist bereits fertig 588 | begin 589 | total:=100; chunk:=100; // auf 100 setzen 590 | end 591 | else 592 | begin 593 | total:=FhSplitThread.ProgressNumTotal; // Total Progress Nummer 594 | if total = 100 then // wenn 100 595 | chunk:=100 // dann chunk auch 100 596 | else 597 | chunk:=FhSplitThread.ProgressNumChunk; // Chunk Progress Nummer 598 | end; 599 | 600 | (* Progress Nummern *) 601 | if (FProgress_Interval = FProgress_IntervalCounter)// Interval ist erreicht 602 | or (total = 100) then // oder Total ist 100 603 | begin 604 | if total = 100 then // total ist 100 605 | t:='100' // nur 100 als Wert 606 | else t:=FormatFloat('0.0',total); // Zahl formatieren 607 | if chunk = 100 then // chunk ist 100 608 | c:='100' // nur 100 als Wert 609 | else c:=FormatFloat('0.0',chunk); // Zahl formatieren 610 | 611 | WriteLn('Total-Progress: '+t+'% <> ' // Total-Progress 612 | +'Chunk-Progress: '+c+'%'); // Chunk-Progress 613 | FProgress_IntervalCounter:=Interval_Factor; // Interval Zähler zurücksetzen 614 | end 615 | else Inc(FProgress_IntervalCounter); // Interval Zähler erhöhen 616 | end; 617 | 618 | 619 | // Log - Ende 620 | procedure THSplit.Log_End; 621 | var 622 | msg: String; 623 | begin 624 | msg:=#10#13'Exit Code: '+inttostr(ExitCode); // msg - Exit Code einfügen 625 | if FErrorText <> '' then // Fehler Text vorhanden 626 | msg:=msg+'Error Text:'#13#10+FErrorText; // Fehler Text anhängen 627 | msg:=msg+#10#13'hSplit done!'; // hSplit fertig anhängen 628 | WriteLn(msg); // msg ausgeben 629 | end; 630 | 631 | 632 | // User Exit 633 | procedure THSplit.DoUserExit; 634 | begin 635 | FhSplitThread.UserExit:=true; 636 | ExitCode:=-1; 637 | end; 638 | 639 | 640 | 641 | (*------------ protected -----------------------------------------------------*) 642 | 643 | 644 | // Do Run 645 | procedure THSplit.DoRun; 646 | var 647 | user_input: String=''; 648 | command: String=''; 649 | begin 650 | (* Parameter Check *) 651 | if Check_Params then // Parameter prüfen 652 | if not Check_HelpParam then // Kein Hilfe Para gefunden 653 | if Parse_Params then // Parameter geparst 654 | if Start_hSplit then // Split Thread gestarted 655 | begin 656 | while (not FhSplitThread.Running) 657 | and (not FhSplitThread.Finished) 658 | and (ExitCode=0) do 659 | Sleep(1); // ganz kurz auf Thread warten 660 | 661 | (* Loop *) 662 | while (not FhSplitThread.ProgressDone) // Progress nicht 100 663 | and (not FhSplitThread.Finished) do // Thread läuft noch 664 | begin 665 | (* Fehler Code prüfen - Fehler Text sichern *) 666 | if ExitCode > 0 then // Es gibt einen Fehler 667 | begin 668 | FErrorText:=FhSplitThread.ErrorText; // Fehler Text sichern 669 | Break; // Abbruch 670 | end; 671 | 672 | (* Progress Nummer loggen *) 673 | if FProgress_Interval > 0 then // Interval muss da sein 674 | Log_ProgressNums; // Progress Nummer loggen 675 | 676 | (* User Input *) 677 | user_input:=ReadSequence(false); // Eingabe lesen 678 | if user_input <> '' then // Eingabe nicht leer 679 | if user_input = #13 then // Eingabe war ENTER 680 | begin 681 | if command = 'e' then // Kommando ist "e" = Exit 682 | begin 683 | DoUserExit; // Exit ausgeben 684 | Break; 685 | end; 686 | command:=''; // Kommando leeren 687 | end 688 | else // user_input anhängen 689 | begin 690 | command:=command+user_input; // Kommando erweitern 691 | WriteLn(#13#10+command); // Kommando anzeigen 692 | end; 693 | user_input:=''; // User input leeren 694 | end; 695 | 696 | (* Log 100% *) 697 | if FProgress_Interval > 0 then // Interval muss da sein 698 | Log_ProgressNums; // Progress Nummer loggen 699 | end; 700 | Log_End; // Ende loggen 701 | 702 | {$ifdef debug} 703 | ReadLn; // ReadLn - Thread anhalten 704 | {$endif} 705 | Terminate(ExitCode); // Main Thread beenden 706 | end; 707 | 708 | 709 | (*------------ public --------------------------------------------------------*) 710 | 711 | 712 | // Constructor - Progress Interval default init 713 | constructor THSplit.Create(TheOwner: TComponent); 714 | begin 715 | inherited Create(TheOwner); 716 | StopOnException:=True; 717 | FJoinFiles:=nil; 718 | 719 | FProgress_Interval:=100*Interval_Factor; // Standard Interval 100 720 | FProgress_IntervalCounter:=FProgress_Interval; 721 | end; 722 | 723 | 724 | // Destructor 725 | destructor THSplit.Destroy; 726 | begin 727 | FProgress_Interval:=0; 728 | FProgress_IntervalCounter:=0; 729 | FErrorText:=''; 730 | FPara_ChunkSize:=''; 731 | FPara_ChunkCount:=''; 732 | FPara_SegSplitArray:=''; 733 | FPara_ProgressInterval:=''; 734 | FPara_TrgFolder:=''; 735 | FPara_TrgFilename:=''; 736 | FPara_Source:=''; 737 | if FJoinFiles <> nil then 738 | FreeAndNil(FJoinFiles); 739 | 740 | inherited Destroy; 741 | end; 742 | 743 | 744 | // Write Help 745 | procedure THSplit.WriteHelp; 746 | var 747 | help: String; 748 | begin 749 | help:=hSplitTitle+#13#10 750 | +'-----------------------------------------------------------------'#13#10 751 | +'Syntax'#13#10 752 | +'hSplit.exe [Param1 [Value1 [Param2 Value2 ... ...]]] "Source.file" ["..."]'#13#10 753 | +'-----------------------------------------------------------------'#13#10 754 | +'Parameter'#13#10 755 | +'-h > Show this Help'#13#10#13#10 756 | +'-S > Segments: split file into segments (default is split Chunks)'#13#10 757 | +' an array of comma separated start and end bytes is used'#13#10 758 | +' values for start and end can be an Integer or a Hex string'#13#10 759 | +' to use hex values set a first char to "h" -> h0-1000,2abc-4def'#13#10 760 | +' the end byte is NOT included in the target file'#13#10 761 | +' if an end value is set to 0, it means end of file(last byte)'#13#10 762 | +' a byte pair of 0-0 means copy the entire file and will be skipped'#13#10 763 | +' the start byte must be smaller than the end byte'#13#10#13#10 764 | +'-s > Chunk size: value is an Integer and default means Megabyte (def=20)'#13#10 765 | +' a first or last char can be set to define the value type'#13#10 766 | +' b=Byte; k=Kilobyte g=Gigabyte'#13#10#13#10 767 | +'-c > Chunks count: How many chunks should be written'#13#10 768 | +' if not set or set to 0 means infinity until the source file ends (def=0)'#13#10#13#10 769 | +'-t > Target folder: if not set or empty the source folder is used'#13#10#13#10 770 | +'-n > Target file name: if not set or empty the source filename is used'#13#10#13#10 771 | +'-P > Progress interval: Control how often the progress is send to the output'#13#10 772 | +' value is a Byte from 0 to 255'#13#10 773 | +' value 0 sends no progress numbers; 1=max 255=min (def=100)'#13#10#13#10 774 | +'-J > Join files: value is a path for a target file'#13#10 775 | +' only the -P switch can be used additionally'#13#10#13#10 776 | +'".." > Source file: MUST be the last parameter! Path to source file'#13#10#13#10 777 | +'@".." > Path to a parameter file: MUST be the first parameter!'#13#10 778 | +' The parameter file should be a simple text file'#13#10 779 | +' You can use any name and extension for the file name'#13#10 780 | +' Only source file parameters can be used additionally'#13#10 781 | +'-----------------------------------------------------------------'#13#10 782 | +'Parameter file'#13#10 783 | +'Each line is used as a parameter'#13#10 784 | +'If you don''t set a source path in the parameter file,'#13#10 785 | +'the source path(s) is(are) read from the command-line.'#13#10 786 | +'You can use a root parameter file called "params.txt"'#13#10 787 | +'Exists this file in the root folder of hSplit, it will be used automatically.'#13#10 788 | +'If the "params.txt" is empty, it will be ignored.'#13#10 789 | +'-----------------------------------------------------------------'#13#10 790 | +'Command'#13#10 791 | +'You can send a command while hSplit is running.'#13#10 792 | +'Command will be executed after pressing the ENTER key'#13#10#13#10 793 | +'"e" > User Exit: stops hSplit'#13#10 794 | +'-----------------------------------------------------------------'#13#10 795 | +'Exit codes'#13#10 796 | +'hSplit sends at the end an exit code'#13#10#13#10 797 | +'0 - all OK'#13#10 798 | +'1 - no source file'#13#10 799 | +'2 - error while loading source file'#13#10 800 | +'3 - no target folder (not found or could not created)'#13#10 801 | +'4 - chunk size too big (bigger than the source file size)'#13#10 802 | +'5 - nothing to split (no data)'#13#10 803 | +'6 - nothing to join (no data)'#13#10 804 | +'7 - parameter file not found'#13#10 805 | +'8 - parameter file is empty (only if used with the cli parameter)'#13#10 806 | +'9 - wrong parameters '#13#10 807 | +'99 - internal Lazarus error'#13#10 808 | +'-1 - User Exit'#13#10 809 | +'-----------------------------------------------------------------'#13#10 810 | +'Examples'#13#10 811 | +'Split chunks:'#13#10 812 | +'hSplit.exe "Path\to\source.file"'#13#10 813 | +'split entire source file in 20mb chunks'#13#10#13#10 814 | +'hSplit.exe -s 100 "Path\to\source.file"'#13#10 815 | +'split entire source file in 100mb chunks'#13#10#13#10 816 | +'hSplit.exe -s g2 -c 10 "Path\to\source.file"'#13#10 817 | +'hSplit.exe -s 2g -c 10 "Path\to\source.file"'#13#10 818 | +'split 10 chunks with 2 gigabyte each'#13#10#13#10 819 | +'hSplit.exe -t "trg dir" -c 10 -n "trg name" "Path\to\source.file"'#13#10 820 | +'split 10 chunks with 20mb each to folder "tar dir" with name "trg name"'#13#10 821 | +'the file extension is used from the source file'#13#10#13#10 822 | +'Split segments:'#13#10 823 | +'hSplit.exe -S 0-1000,5000-6000,500-1500 "Path\to\source.file"'#13#10 824 | +'split three segments with 1000 bytes each'#13#10#13#10 825 | +'hSplit.exe -S h0-1000,5000-6000,500-1500 "Path\to\source.file"'#13#10 826 | +'split three segments with 4096 bytes each'#13#10#13#10 827 | +'Join files:'#13#10 828 | +'hSplit.exe -J "Path\to trg.file" -P 0 "Path\source.1" "Path\source.2"'#13#10 829 | +'join 2 files without progress numbers'#13#10#13#10 830 | +'Parameter file:'#13#10 831 | +'hSplit.exe @"Path\to MyParam.file"'#13#10 832 | +'all parameters are read from the parameter file'#13#10#13#10 833 | +'hSplit.exe @"Path\to MyParam.file" "Path\to\source.file"'#13#10 834 | +'the source parameter from command-line is used (no path in the parameter file)'#13#10 835 | +'(if you want to join files, you can specify more command-line parameters)'; 836 | 837 | writeln(help); 838 | {$ifdef windows} 839 | ReadLn; 840 | {$endif} 841 | end; 842 | 843 | 844 | var 845 | Application: THSplit; 846 | 847 | 848 | 849 | 850 | begin 851 | Application:=THSplit.Create(nil); 852 | Application.Title:=hSplitTitle; 853 | Application.Run; 854 | Application.Free; 855 | end. 856 | 857 | 858 | 859 | 860 | 861 | -------------------------------------------------------------------------------- /src/hsplitthread.pas: -------------------------------------------------------------------------------- 1 | unit hSplitThread; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils, LazFileUtils; 9 | 10 | const // Fehler Codes 11 | hsErr_NoSourceFile = 1; // Source Datei nicht vorhanden 12 | hsErr_LoadSource = 2; // Source Datei Fehler Laden 13 | hsErr_NoTrgFolder = 3; // Ziel Ordner nicht vorhanden 14 | hsErr_ChunkSizeTooBig = 4; // Teil-Größe zu groß 15 | hsErr_NothingToSplit = 5; // keine Daten zum splitten 16 | hsErr_NothingToJoin = 6; // keine Daten zum joinen 17 | hsErr_NoParams_txt = 7; // Params.txt nicht gefunden 18 | hsErr_Params_txtEmpty = 8; // Params.txt leer 19 | hsErr_WrongParams = 9; // Falsche Parameter 20 | hsErr_LazException = 99; // Lazarus Prozess Exception 21 | 22 | 23 | const // Auslese Größe 24 | read_size = 2097152; 25 | 26 | type 27 | 28 | QWordArray = array of QWord; 29 | 30 | 31 | 32 | ThSplitThread = class(TThread) 33 | strict private 34 | (* Getter *) 35 | function Get_ProgressNumChunk: Single; 36 | function Get_ProgressNumTotal: Single; 37 | function Get_ProgressDone: Boolean; 38 | 39 | private // private vars 40 | FUserExit : Boolean; 41 | FErrorText : String; 42 | 43 | FSource : String; 44 | FSrcExtension : String; 45 | FSrcStream : TFileStream; 46 | FSrcSize : QWord; 47 | FTargetFolder : String; 48 | // für Join ist FTrgFilename der gesamte Datei Pfad 49 | FTrgFilename : String; // Dateiname OHNE Erweiterung 50 | FtrgStream : TFileStream; 51 | 52 | FChunkSize : QWord; // Size für die Bytes 53 | FChunkCount : Integer; // 0 unendlich, bis Ende Datei 54 | FChunkBytes_readed : QWord; 55 | 56 | FSegmentSplit : Boolean; // false=Chunk, true=Segment 57 | FSegmentStarts : QWordArray; 58 | FSegmentEnds : QWordArray; 59 | 60 | FBytes_Total : QWord; 61 | FBytes_TotalCounter : QWord; 62 | 63 | FProgressNumChunk : Single; 64 | FProgressNumTotal : Single; 65 | 66 | FJoinFiles : TStringList; // Datei Pfade 67 | 68 | FBuffer : array [0..read_size] of Byte; // Größe = read_size 69 | FRunning : Boolean; 70 | 71 | private // Proceduren 72 | function Load_SourceFile: Boolean; 73 | function Check_Target: Boolean; 74 | 75 | function SplitChunks: Boolean; 76 | function SplitSegments: Boolean; 77 | procedure ReadAndWriteBytes; 78 | 79 | function JoinFiles: Boolean; 80 | 81 | 82 | protected // Proceduren 83 | procedure Execute; override; 84 | 85 | public // Proceduren 86 | constructor Create; 87 | destructor Destroy; override; 88 | 89 | procedure Insert_SegmentSplits(const Starts,Ends: QWordArray); 90 | procedure Insert_JoinFiles(const Join_Files: TStringList); 91 | 92 | public // Properties 93 | property Source : String read FSource write FSource; 94 | property TargetFolder: String read FTargetFolder write FTargetFolder; 95 | property TargetFilename: String read FTrgFilename write FTrgFilename; 96 | 97 | property SegmentSplit: Boolean read FSegmentSplit write FSegmentSplit; 98 | property ChunkSize : QWord read FChunkSize write FChunkSize; 99 | property ChunkCount : Integer read FChunkCount write FChunkCount; 100 | 101 | property Running : Boolean read FRunning; 102 | property ProgressNumChunk: Single read Get_ProgressNumChunk; 103 | property ProgressNumTotal: Single read Get_ProgressNumTotal; 104 | property ProgressDone: Boolean read Get_ProgressDone; 105 | 106 | property UserExit : Boolean write FUserExit; 107 | property ErrorText : String read FErrorText; 108 | end; 109 | 110 | 111 | 112 | 113 | implementation 114 | 115 | 116 | 117 | (*------------ strict private ------------------------------------------------*) 118 | 119 | 120 | // Progress Nummer für den aktuellen Chunk 121 | function ThSplitThread.Get_ProgressNumChunk: Single; 122 | begin 123 | if (FChunkSize = 0) 124 | or (FChunkBytes_readed = 0) then 125 | FProgressNumChunk:=0 126 | else 127 | if FChunkBytes_readed = FChunkSize then // alle Bytes verarbeitet 128 | FProgressNumChunk:=100 // 100% 129 | else // Prozente errechnen 130 | FProgressNumChunk:=(FChunkBytes_readed // ausrechnen 131 | /FChunkSize)*100; 132 | Result:=FProgressNumChunk; // Result ausgeben 133 | end; 134 | 135 | 136 | // Progress Nummer Total 137 | function ThSplitThread.Get_ProgressNumTotal: Single; 138 | begin 139 | if (FBytes_Total = 0) 140 | or (FBytes_TotalCounter = 0) then 141 | FProgressNumTotal:=0 142 | else 143 | if FBytes_TotalCounter = FBytes_Total then // alle Bytes verarbeitet 144 | FProgressNumTotal:=100 // 100% 145 | else // Prozente errechnen 146 | FProgressNumTotal:=(FBytes_TotalCounter // ausrechnen 147 | /FBytes_Total)*100; 148 | Result:=FProgressNumTotal; // Result ausgeben 149 | end; 150 | 151 | 152 | // Progress fertig 153 | function ThSplitThread.Get_ProgressDone: Boolean; 154 | begin 155 | Result:=false; 156 | if FBytes_Total = 0 then Exit; 157 | Result:=FBytes_TotalCounter >= FBytes_Total; 158 | end; 159 | 160 | 161 | 162 | (*------------ private -------------------------------------------------------*) 163 | 164 | 165 | // Source Datei laden 166 | function ThSplitThread.Load_SourceFile: Boolean; 167 | begin 168 | Result:=false; // Result init 169 | if not FileExists(FSource) then // Quell Datei ist nicht da 170 | begin 171 | ExitCode:=hsErr_NoSourceFile; // Fehler Code zuweisen 172 | Exit; // Abbruch Datei nicht da 173 | end; 174 | 175 | try 176 | FSrcExtension:=ExtractFileExt(FSource); // Datei Erweiterung ermitteln 177 | FSrcStream:=TFileStream.Create(FSource, fmOpenRead or fmShareDenyWrite); 178 | FSrcSize:=FSrcStream.Size; // Datei Größe ermitteln 179 | Result:=true; // Result ausgeben OK 180 | Except 181 | on e: Exception do // Fehler abfangen 182 | begin 183 | FErrorText:=e.Message; // Fehler Text sichern 184 | ExitCode:=hsErr_LoadSource; // Fehler Code zuweisen 185 | end; 186 | end; 187 | end; 188 | 189 | 190 | // Target Ordner prüfen 191 | function ThSplitThread.Check_Target: Boolean; 192 | begin 193 | Result:=false; // Result init 194 | (* Ziel Ordner *) 195 | if FTargetFolder = '' then // kein Ziel Ordner angegeben 196 | FTargetFolder:=ExtractFileDir(FSource) // Quell = Ziel Ordner 197 | else 198 | if not DirectoryExists(FTargetFolder) then // Ziel Ordner nicht vorhanden 199 | if not CreateDir(FTargetFolder) then // Ordner nicht erstellbar 200 | begin 201 | ExitCode:=hsErr_NoTrgFolder; // Fehler Code zuweisen 202 | Exit; // Abbruch 203 | end; 204 | (* Ziel Datei *) 205 | if FtrgFilename = '' then // Kein Zielname 206 | FtrgFilename:=ExtractFileNameOnly(FSource); // Zielnamen aus Quelle ermitte 207 | 208 | Result:=true; // Result ausgeben OK 209 | end; 210 | 211 | 212 | // Split Chunks - Teile nach einer gewissen Größe und Anzahl 213 | function ThSplitThread.SplitChunks: Boolean; 214 | var 215 | chunk_counter: Integer=0; 216 | chunk_filepath,chunk_filepathFull: String; 217 | lastChunkSize: QWord; 218 | begin 219 | Result:=false; // Result init false 220 | (* Teil-Größe prüfen *) 221 | if FChunkSize >= FSrcSize then // Teil-Größe zu groß 222 | begin 223 | ExitCode:=hsErr_ChunkSizeTooBig; // Fehler Code setzen 224 | Exit; // Abbruch 225 | end; 226 | FSrcStream.Position:=0; // 1.Byte anwählen 227 | 228 | (* FBytes_Total - die tatsächlichen Bytes die verarbeitet werden sollen *) 229 | if FChunkCount = 0 then // keine Chunk Begrenzung 230 | FBytes_Total:=FSrcSize // geamte Quell Datei Bytes 231 | else 232 | begin 233 | FBytes_Total:=FChunkSize * FChunkCount; // Anzahl der Chunk * Größe 234 | if FBytes_Total > FSrcSize then // zu viele Bytes 235 | FBytes_Total:=FSrcSize; // Quell Datei größe nutzen 236 | end; 237 | 238 | (* Ziel Dateiname vorbereiten - 1.Teil *) 239 | chunk_filepath:=FTargetFolder // Datei name bilden 240 | +DirectorySeparator+FtrgFilename+'_'; 241 | 242 | try 243 | FChunkBytes_readed:=FChunkSize; // FChunkBytes_readed init 244 | while FBytes_TotalCounter < FBytes_Total do // alle Bytes verarbeiten 245 | begin 246 | (* neuen Chunk *) 247 | if FChunkBytes_readed = FChunkSize then // Chunk Größe erreicht 248 | begin 249 | Inc(chunk_counter); // Datei Zähler erhöhen 250 | chunk_filepathFull:=chunk_filepath // Datei name fertig bilden 251 | +inttostr(chunk_counter)+FsrcExtension; 252 | 253 | FtrgStream:=TFileStream.Create // Datei erzeugen 254 | (chunk_filepathFull,fmCreate); 255 | FtrgStream.Position:=0; // 1.Byte anwählen 256 | FChunkBytes_readed:=0; // FChunkBytes_readed zurückset 257 | 258 | (* Last Chunk Size *) 259 | lastChunkSize:=FBytes_Total-FBytes_TotalCounter;// restlichen Byte Anzahl 260 | if lastChunkSize < FChunkSize then // kleiner als die Chunck Size 261 | FChunkSize:=lastChunkSize; // Chunksize ändern 262 | // erst jetzt running wegen den progress nummern 263 | FRunning:=true; // Running auf true setzen 264 | end; 265 | 266 | (* Bytes lesen und schreiben *) 267 | ReadAndWriteBytes; 268 | 269 | (* Chunk fertig - alle Bytes geschrieben *) 270 | if FChunkBytes_readed = FChunkSize then // Chunk Größe erreicht 271 | FreeAndNil(FtrgStream); // Datei Stream freigeben 272 | 273 | (* Chunk Anzahl erreicht - Fertig *) 274 | if FChunkCount > 0 then 275 | if chunk_counter > FChunkCount then Break; // Fertig 276 | 277 | (* Benutzer Exit *) 278 | if FUserExit then // Abbruch durch benutzer 279 | begin 280 | if FtrgStream <> nil then // Datei Stream nicht nil 281 | FreeAndNil(FtrgStream); // Datei Stream freigeben 282 | Exit; // Feritg 283 | end; 284 | end; 285 | 286 | Result:=true; 287 | Except 288 | on e: Exception do // Fehler abfangen 289 | begin 290 | FErrorText:=e.Message; // Fehler Text sichern 291 | ExitCode:=hsErr_LazException; // Fehler Code zuweisen 292 | end; 293 | end; 294 | end; 295 | 296 | 297 | // Split Segment(s) 298 | function ThSplitThread.SplitSegments: Boolean; 299 | var 300 | s,maxSegs,starts_count,ends_count: Integer; 301 | chunk_filepath,chunk_filepathFull: String; 302 | ByteStart,ByteEnd: QWord; 303 | 304 | (* Bytes Total - Ersetzen der Ends=0 durch die Datei-Size 305 | Alle ungültigen Zeit-Paare -> End-Byte auf 0 setzen *) 306 | procedure Get_Bytes_Total; 307 | var 308 | a : Integer; 309 | begin 310 | FBytes_Total:=0; // FBytes_Total init = 0 311 | for a:=0 to maxSegs -1 do // Segment Arrays durchgehen 312 | begin 313 | ByteStart:=FSegmentStarts[a]; // Start Byyte ermitteln 314 | ByteEnd:=FSegmentEnds[a]; // End Byyte ermitteln 315 | (* gesamte Datei kopieren ausschliessen *) 316 | if (ByteStart + ByteEnd) = 0 then // Alle bytes von der Datei 317 | Continue; // weiter 318 | (* End Byte prüfen und neu setzen *) 319 | if (ByteEnd = 0) // 0 = bis Ende Datei 320 | or (ByteEnd > FSrcSize) then // End ist zu groß 321 | begin 322 | FSegmentEnds[a]:=FSrcSize; // Datei größe setzen 323 | ByteEnd:=FSrcSize; // neues Ende setzen 324 | end; 325 | 326 | (* Start ist zu groß - größer als Datei wird damit auch anbgefangen *) 327 | if ByteStart >= ByteEnd then // Start Byte zu groß/gleich 328 | begin 329 | FSegmentEnds[a]:=0; // EndByte löschen 330 | Continue; 331 | // weiter 332 | end; 333 | Inc(FBytes_Total,(ByteEnd - ByteStart)); // Total Byte mitzählen 334 | end; 335 | end; 336 | 337 | begin 338 | Result:=false; // Result init false 339 | (* Maximale Anzahl an Segmenten *) 340 | starts_count:=Length(FSegmentStarts); // Anzahl Starts 341 | ends_count:=Length(FSegmentEnds); // Anzahl Ends 342 | if starts_count = ends_count then // beide gleich lang 343 | maxSegs:=starts_count // max Segmente setzen 344 | else 345 | if starts_count > ends_count then // es gibt mehr starts als end 346 | maxSegs:=ends_count // Ende Anzahl nutzen 347 | else // es gibt weniger starts 348 | maxSegs:=starts_count; // Starts Anzahl nutzen 349 | 350 | (* Bytes Total *) 351 | Get_Bytes_Total; // Total Bytes ermitteln 352 | if FBytes_Total = 0 then // keine Segmente zum splitten 353 | begin 354 | ExitCode:=hsErr_NothingToSplit; // Fehler Code setzen 355 | Exit; // fertig - Abbruch 356 | end; 357 | 358 | (* Ziel Dateiname vorbereiten - 1.Teil *) 359 | chunk_filepath:=FTargetFolder // Datei name bilden 360 | +DirectorySeparator+FtrgFilename+'_'; 361 | 362 | FRunning:=true; // Running auf true setzen 363 | try 364 | for s:=0 to maxSegs -1 do // Segmente durchgehen 365 | begin 366 | ByteEnd:=FSegmentEnds[s]; // End Byte ermitteln 367 | // EndByte 0 ist nicht Ende Datei, das wurde vorher geprüft 368 | if ByteEnd = 0 then Continue; // End=0 überspringen 369 | FChunkBytes_readed:=0; // Chunk Bytes gelesen reset 370 | ByteStart:=FSegmentStarts[s]; // Start Byyte ermitteln 371 | FChunkSize:=ByteEnd - ByteStart; // Segemnt Größe 372 | (* Streams vorbereiten *) 373 | chunk_filepathFull:=chunk_filepath // Datei name fertig bilden 374 | +inttostr(s+1)+FsrcExtension; 375 | FtrgStream:=TFileStream.Create // Datei erzeugen 376 | (chunk_filepathFull,fmCreate); 377 | FtrgStream.Position:=0; // 1.Byte anwählen 378 | FSrcStream.Position:=ByteStart; // Start Byte setzen 379 | 380 | while FChunkBytes_readed < FChunkSize do // Chunk Bytes verarbeiten 381 | begin 382 | (* Bytes lesen und schreiben *) 383 | ReadAndWriteBytes; 384 | 385 | (* Benutzer Exit *) 386 | if FUserExit then // Abbruch durch benutzer 387 | begin 388 | if FtrgStream <> nil then // Datei Stream nicht nil 389 | FreeAndNil(FtrgStream); // Datei Stream freigeben 390 | Exit; // Feritg 391 | end; 392 | end; 393 | FreeAndNil(FtrgStream); // Datei Stream freigeben 394 | end; 395 | 396 | Result:=true; 397 | Except 398 | on e: Exception do // Fehler abfangen 399 | begin 400 | FErrorText:=e.Message; // Fehler Text sichern 401 | ExitCode:=hsErr_LazException; // Fehler Code zuweisen 402 | end; 403 | end; 404 | end; 405 | 406 | // Bytes lesen und schreiben 407 | procedure ThSplitThread.ReadAndWriteBytes; 408 | var 409 | Bytes_Readed,read_Bytes: QWord; 410 | begin 411 | (* zu lesende Bytes ermitteln *) 412 | read_Bytes:=FChunkSize - FChunkBytes_readed; // read_Bytes init 413 | if read_Bytes > read_size then // read_Bytes zu groß 414 | read_Bytes:=read_size; // read_Bytes max read_size 415 | 416 | (* Bytes auslesen und mitzählen *) 417 | Bytes_Readed:=FSrcStream.Read(FBuffer // Bytes lesen 418 | ,read_Bytes); 419 | Inc(FChunkBytes_readed,Bytes_Readed); // gelesen B. mitzählen Chunk 420 | Inc(FBytes_TotalCounter,Bytes_Readed); // gelesen B. mitzählen Total 421 | 422 | (* Bytes schreiben *) 423 | FtrgStream.Write(FBuffer, Bytes_Readed); // Bytes in Datei schreiben 424 | end; 425 | 426 | 427 | (* Join *) 428 | 429 | // Join Files 430 | function ThSplitThread.JoinFiles: Boolean; 431 | var 432 | f,f_Count: Integer; 433 | fPath,fPathOld: String; 434 | 435 | // Totel Bytes ermitteln - Datei Pfad prüfen, Anzahl Join Dateien 436 | procedure Get_Bytes_Total; 437 | var 438 | p: Integer; 439 | fSize: Int64; 440 | begin 441 | fPathOld:=''; // alten Path leeren 442 | (* Pfade prüfen *) 443 | for p:=FJoinFiles.Count -1 downto 0 do // Pfad durchgehen 444 | begin 445 | fPath:=FJoinFiles[p]; // Pfad ermitteln 446 | if fPath <> fPathOld then // neuer Datei Pfad 447 | begin 448 | if not FileExists(fPath) then // Datei nicht vorhanden 449 | begin 450 | FJoinFiles.Delete(p); // Pfad löschen 451 | Continue; // weiter 452 | end; 453 | fSize:=FileSizeUtf8(fPath); // Datei Größe ermitteln 454 | end; 455 | fPathOld:=fPath; // neuen "alten" Pfad sichern 456 | FBytes_Total:=FBytes_Total+fSize; // Datei größe mitzählen 457 | end; 458 | f_Count:=FJoinFiles.Count; // Anzahl Join Dateien sichern 459 | end; 460 | 461 | begin 462 | Result:=false; // Result init false 463 | (* Bytes Total *) 464 | Get_Bytes_Total; // Total Bytes ermitteln 465 | if (FBytes_Total = 0) // keine Daten zum joinen 466 | or (f_Count < 2) then // keine Dateien zum joinen 467 | begin 468 | ExitCode:=hsErr_NothingToJoin; // Fehler Code setzen 469 | Exit; // fertig - Abbruch 470 | end; 471 | FtrgStream:=TFileStream.Create // Ziel Datei erzeugen 472 | (FTrgFilename,fmCreate); 473 | 474 | FRunning:=true; // Running auf true setzen 475 | try 476 | fPathOld:=''; // alten Path leeren 477 | for f:=0 to f_Count -1 do // Dateien durchgehen 478 | begin 479 | fPath:=FJoinFiles[f]; // Pfad ermitteln 480 | if fPath <> fPathOld then // neuer Datei Pfad 481 | begin 482 | FSrcStream:=TFileStream.Create(fPath // Join Datei öffnen 483 | ,fmOpenRead or fmShareDenyWrite); 484 | FChunkSize:=FSrcStream.Size; // Datei Größe zuweisen 485 | end; 486 | FSrcStream.Position:=0; // 1.Byte anwählen 487 | FChunkBytes_readed:=0; // Chunk Bytes gelesen reset 488 | 489 | while FChunkBytes_readed < FChunkSize do // Chunk Bytes verarbeiten 490 | begin 491 | (* Bytes lesen und schreiben *) 492 | ReadAndWriteBytes; 493 | 494 | (* Benutzer Exit *) 495 | if FUserExit then // Abbruch durch benutzer 496 | begin 497 | FreeAndNil(FSrcStream); // Join Datei Stream freigeben 498 | FreeAndNil(FtrgStream); // Ziel Datei Stream freigeben 499 | Exit; // Feritg 500 | end; 501 | end; 502 | FreeAndNil(FSrcStream); // Datei Stream freigeben 503 | end; 504 | 505 | Result:=true; 506 | Except 507 | on e: Exception do // Fehler abfangen 508 | begin 509 | FErrorText:=e.Message; // Fehler Text sichern 510 | ExitCode:=hsErr_LazException; // Fehler Code zuweisen 511 | end; 512 | end; 513 | FreeAndNil(FtrgStream); // Ziel Datei Stream freigeben 514 | end; 515 | 516 | 517 | 518 | (*------------ protected -----------------------------------------------------*) 519 | 520 | // Execute - Ausführen 521 | procedure ThSplitThread.Execute; 522 | begin 523 | if not FUserExit then // Abbruchbedingung 524 | (* Ziel Ordner prüfen *) 525 | if Check_Target then // Ziel prüfen 526 | 527 | (* Join Files *) 528 | if FJoinFiles <> nil then // Datei Liste vorhanden 529 | JoinFiles // Dateien joinen 530 | else 531 | 532 | (* Source Datei laden *) 533 | if Load_SourceFile then // Datei laden 534 | if not FUserExit then // User Exit 535 | 536 | if FSegmentSplit then // Segment split aktiv 537 | SplitSegments // Segment Splitting 538 | else 539 | SplitChunks; // Chunk splitten 540 | 541 | Terminate; // Thread beenden 542 | end; 543 | 544 | 545 | 546 | (*------------ public --------------------------------------------------------*) 547 | 548 | // Constructor 549 | constructor ThSplitThread.Create; 550 | begin 551 | FErrorText:=''; 552 | FUserExit:=false; 553 | FSrcStream:=nil; 554 | FtrgStream:=nil; 555 | FProgressNumTotal:=0; 556 | FProgressNumChunk:=0; 557 | FChunkBytes_readed:=0; 558 | FChunkSize:=20*1024*1024; // Default 20 mb init 559 | FBytes_TotalCounter:=0; // FBytes_TotalCounter init = 0 560 | FJoinFiles:=nil; 561 | FreeOnTerminate := True; 562 | inherited Create(true); 563 | end; 564 | 565 | // Destructor 566 | destructor ThSplitThread.Destroy; 567 | begin 568 | FUserExit:=false; 569 | FErrorText:=''; 570 | FreeAndNil(FSrcStream); 571 | FSrcSize:=0; 572 | FSource:=''; 573 | FsrcExtension:=''; 574 | FTargetFolder:=''; 575 | FtrgFilename:=''; 576 | FProgressNumTotal:=0; 577 | FProgressNumChunk:=0; 578 | FChunkSize:=0; 579 | FChunkCount:=0; 580 | FChunkBytes_readed:=0; 581 | FBytes_Total:=0; 582 | FBytes_TotalCounter:=0; 583 | if FJoinFiles <> nil then 584 | FreeAndNil(FJoinFiles); 585 | SetLength(FSegmentStarts,0); 586 | SetLength(FSegmentEnds,0); 587 | inherited Destroy; 588 | end; 589 | 590 | 591 | // Segment Zeiten einfügen 592 | procedure ThSplitThread.Insert_SegmentSplits(const Starts,Ends: QWordArray); 593 | begin 594 | FSegmentStarts:=Starts; 595 | FSegmentEnds:=Ends; 596 | end; 597 | 598 | 599 | // Join Dateien einfügen 600 | procedure ThSplitThread.Insert_JoinFiles(const Join_Files: TStringList); 601 | begin 602 | if FJoinFiles = nil then // Join List nicht da 603 | FJoinFiles:=TStringList.Create; // Liste erzeugen 604 | FJoinFiles.Text:=Join_Files.Text; // Daten übertragen 605 | end; 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | end. 632 | 633 | --------------------------------------------------------------------------------