1 | unit UDriveScan;
|
---|
2 |
|
---|
3 | {$mode delphi}
|
---|
4 |
|
---|
5 | interface
|
---|
6 |
|
---|
7 | uses
|
---|
8 | Classes, SysUtils, Syncobjs, UBlockMap, Forms, DOM, UConfig, UPhysDrive,
|
---|
9 | UXMLUtils, Contnrs, ExtCtrls, DateUtils;
|
---|
10 |
|
---|
11 | type
|
---|
12 | TDriveScan = class;
|
---|
13 |
|
---|
14 | TRunMode = (rmRead, rmWrite, rmNone, rmSpeed);
|
---|
15 | TExceptionEvent = procedure (Sender: TObject; E: Exception) of object;
|
---|
16 |
|
---|
17 | { TScanThread }
|
---|
18 |
|
---|
19 | TScanThread = class(TThread)
|
---|
20 | private
|
---|
21 | FOnException: TExceptionEvent;
|
---|
22 | public
|
---|
23 | Scan: TDriveScan;
|
---|
24 | ExceptionText: string;
|
---|
25 | procedure Execute; override;
|
---|
26 | property OnException: TExceptionEvent read FOnException write FOnException;
|
---|
27 | end;
|
---|
28 |
|
---|
29 | { TDriveScanProfile }
|
---|
30 |
|
---|
31 | TDriveScanProfile = class
|
---|
32 | SectorCount: Integer;
|
---|
33 | SectorStart: Integer;
|
---|
34 | SectorEnd: Integer;
|
---|
35 | Mode: TRunMode;
|
---|
36 | WritePattern: Byte;
|
---|
37 | WritePatternRandom: Boolean;
|
---|
38 | procedure LoadFromDriveInfo(DriveInfo: TDriveInfo);
|
---|
39 | end;
|
---|
40 |
|
---|
41 | { TTestStream }
|
---|
42 |
|
---|
43 | TTestStream = class(TStream)
|
---|
44 | public
|
---|
45 | function Read(var Buffer; Count: Longint): Longint; override;
|
---|
46 | function Write(const Buffer; Count: Longint): Longint; override;
|
---|
47 | function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
|
---|
48 | end;
|
---|
49 |
|
---|
50 | { TSpeedValue }
|
---|
51 |
|
---|
52 | TSpeedValue = record
|
---|
53 | public
|
---|
54 | Null: Boolean;
|
---|
55 | Max: Int64;
|
---|
56 | Average: Int64;
|
---|
57 | Values: array of Int64;
|
---|
58 | Min: Int64;
|
---|
59 | procedure Reset;
|
---|
60 | procedure UpdateValue(Value: Int64);
|
---|
61 | end;
|
---|
62 |
|
---|
63 | { TDriveScan }
|
---|
64 |
|
---|
65 | TDriveScan = class
|
---|
66 | private
|
---|
67 | FDriveName: string;
|
---|
68 | FOnChange: TNotifyEvent;
|
---|
69 | FOnTerminate: TNotifyEvent;
|
---|
70 | FSectorCurrent: Integer;
|
---|
71 | ScanThread: TScanThread;
|
---|
72 | LastExceptionMessage: string;
|
---|
73 | SpeedTimer: TTimer;
|
---|
74 | SpeedTimeLast: TDateTime;
|
---|
75 | SectorLast: Integer;
|
---|
76 | procedure DoOnExceptionSync;
|
---|
77 | procedure DoOnException(Sender: TObject; E: Exception);
|
---|
78 | procedure DoChange;
|
---|
79 | procedure DoTerminate;
|
---|
80 | function GetSectorCount: Integer;
|
---|
81 | procedure Run;
|
---|
82 | procedure SetDriveName(AValue: string);
|
---|
83 | procedure SetSectorCount(AValue: Integer);
|
---|
84 | procedure SpeedTimerExecute(Sender: TObject);
|
---|
85 | public
|
---|
86 | Lock: TCriticalSection;
|
---|
87 | BlockMap: TBlockMap;
|
---|
88 | TimeStart: TDateTime;
|
---|
89 | TimeEnd: TDateTime;
|
---|
90 | Terminated: Boolean;
|
---|
91 | DamagedBlockCount: Integer;
|
---|
92 | Mode: TRunMode;
|
---|
93 | WritePattern: Byte;
|
---|
94 | WritePatternRandom: Boolean;
|
---|
95 | SectorSize: Integer;
|
---|
96 | SectorStart: Integer;
|
---|
97 | SectorEnd: Integer;
|
---|
98 | SpeedSteps: array of TSpeedValue;
|
---|
99 | SpeedStepsCount: Integer;
|
---|
100 | function GetName: string;
|
---|
101 | function GetElapsedTime: TDateTime;
|
---|
102 | procedure Reset;
|
---|
103 | procedure Start;
|
---|
104 | procedure Stop;
|
---|
105 | procedure LoadProfile(Profile: TDriveScanProfile);
|
---|
106 | constructor Create;
|
---|
107 | destructor Destroy; override;
|
---|
108 | procedure SaveToNode(Node: TDOMNode);
|
---|
109 | procedure LoadFromNode(Node: TDOMNode);
|
---|
110 | property SectorCurrent: Integer read FSectorCurrent;
|
---|
111 | property DriveName: string read FDriveName write SetDriveName;
|
---|
112 | property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;
|
---|
113 | property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
---|
114 | property SectorCount: Integer read GetSectorCount write SetSectorCount;
|
---|
115 | end;
|
---|
116 |
|
---|
117 | { TDriveScanList }
|
---|
118 |
|
---|
119 | TDriveScanList = class(TObjectList)
|
---|
120 | procedure SaveToNode(Node: TDOMNode);
|
---|
121 | procedure LoadFromNode(Node: TDOMNode);
|
---|
122 | end;
|
---|
123 |
|
---|
124 |
|
---|
125 | implementation
|
---|
126 |
|
---|
127 | resourcestring
|
---|
128 | SUnknownRunMode = 'Unknown run mode';
|
---|
129 |
|
---|
130 | { TSpeedValue }
|
---|
131 |
|
---|
132 | procedure TSpeedValue.Reset;
|
---|
133 | begin
|
---|
134 | Null := True;
|
---|
135 | end;
|
---|
136 |
|
---|
137 | procedure TSpeedValue.UpdateValue(Value: Int64);
|
---|
138 | var
|
---|
139 | Sum: Double;
|
---|
140 | I: Integer;
|
---|
141 | begin
|
---|
142 | if Null then begin
|
---|
143 | Min := High(Int64);
|
---|
144 | Max := Low(Int64);
|
---|
145 | SetLength(Values, 0);
|
---|
146 | end;
|
---|
147 | Null := False;
|
---|
148 | if Value > Max then Max := Value;
|
---|
149 | // TODO: Computer average
|
---|
150 | SetLength(Values, Length(Values) + 1);
|
---|
151 | Values[Length(Values) - 1] := Value;
|
---|
152 | Sum := 0;
|
---|
153 | for I := 0 to Length(Values) - 1 do
|
---|
154 | Sum := Sum + Values[I];
|
---|
155 | Average := Trunc(Sum / Length(Values));
|
---|
156 | if Value < Min then Min := Value;
|
---|
157 | end;
|
---|
158 |
|
---|
159 | { TDriveScanProfile }
|
---|
160 |
|
---|
161 | procedure TDriveScanProfile.LoadFromDriveInfo(DriveInfo: TDriveInfo);
|
---|
162 | begin
|
---|
163 | SectorCount := DriveInfo.SectorCount;
|
---|
164 | SectorStart := 0;
|
---|
165 | SectorEnd := DriveInfo.SectorCount - 1;
|
---|
166 | end;
|
---|
167 |
|
---|
168 | { TTestStream }
|
---|
169 |
|
---|
170 | function TTestStream.Read(var Buffer; Count: Longint): Longint;
|
---|
171 | begin
|
---|
172 | // if Random < 0.00001 then Result := 0
|
---|
173 | // else
|
---|
174 | Result := Count;
|
---|
175 | end;
|
---|
176 |
|
---|
177 | function TTestStream.Write(const Buffer; Count: Longint): Longint;
|
---|
178 | begin
|
---|
179 | Result := 0;
|
---|
180 | end;
|
---|
181 |
|
---|
182 | function TTestStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
|
---|
183 | begin
|
---|
184 | Result := 0;
|
---|
185 | end;
|
---|
186 |
|
---|
187 | { TDriveScanList }
|
---|
188 |
|
---|
189 | procedure TDriveScanList.SaveToNode(Node: TDOMNode);
|
---|
190 | var
|
---|
191 | I: Integer;
|
---|
192 | NewNode2: TDOMNode;
|
---|
193 | begin
|
---|
194 | for I := 0 to Count - 1 do
|
---|
195 | with TDriveScan(Items[I]) do begin
|
---|
196 | NewNode2 := Node.OwnerDocument.CreateElement('Scan');
|
---|
197 | Node.AppendChild(NewNode2);
|
---|
198 | SaveToNode(NewNode2);
|
---|
199 | end;
|
---|
200 | end;
|
---|
201 |
|
---|
202 | procedure TDriveScanList.LoadFromNode(Node: TDOMNode);
|
---|
203 | var
|
---|
204 | Node2: TDOMNode;
|
---|
205 | NewScan: TDriveScan;
|
---|
206 | begin
|
---|
207 | Count := 0;
|
---|
208 | Node2 := Node.FirstChild;
|
---|
209 | while Assigned(Node2) and (Node2.NodeName = 'Scan') do begin
|
---|
210 | NewScan := TDriveScan.Create;
|
---|
211 | NewScan.LoadFromNode(Node2);
|
---|
212 | Add(NewScan);
|
---|
213 | Node2 := Node2.NextSibling;
|
---|
214 | end;
|
---|
215 | end;
|
---|
216 |
|
---|
217 |
|
---|
218 | { TScanThread }
|
---|
219 |
|
---|
220 | procedure TScanThread.Execute;
|
---|
221 | begin
|
---|
222 | ExceptionText := '';
|
---|
223 | try
|
---|
224 | Scan.Run;
|
---|
225 | except
|
---|
226 | on E: Exception do
|
---|
227 | if Assigned(FOnException) then FOnException(Self, E);
|
---|
228 | end;
|
---|
229 | end;
|
---|
230 |
|
---|
231 | { TDriveScan }
|
---|
232 |
|
---|
233 | procedure TDriveScan.DoOnExceptionSync;
|
---|
234 | begin
|
---|
235 | raise Exception.Create(LastExceptionMessage);
|
---|
236 | end;
|
---|
237 |
|
---|
238 | procedure TDriveScan.DoOnException(Sender: TObject; E: Exception);
|
---|
239 | begin
|
---|
240 | LastExceptionMessage := E.Message;
|
---|
241 | TThread(Sender).Synchronize(TThread(Sender), DoOnExceptionSync);
|
---|
242 | end;
|
---|
243 |
|
---|
244 | procedure TDriveScan.DoChange;
|
---|
245 | begin
|
---|
246 | if Assigned(FOnChange) then FOnChange(Self);
|
---|
247 | end;
|
---|
248 |
|
---|
249 | procedure TDriveScan.DoTerminate;
|
---|
250 | begin
|
---|
251 |
|
---|
252 | if Assigned(FOnTerminate) then FOnTerminate(Self);
|
---|
253 | end;
|
---|
254 |
|
---|
255 | function TDriveScan.GetSectorCount: Integer;
|
---|
256 | begin
|
---|
257 | Result := BlockMap.SectorCount;
|
---|
258 | end;
|
---|
259 |
|
---|
260 | function TDriveScan.GetElapsedTime: TDateTime;
|
---|
261 | begin
|
---|
262 | if TimeEnd <> 0 then Result := TimeEnd - TimeStart
|
---|
263 | else Result := Now - TimeStart;
|
---|
264 | end;
|
---|
265 |
|
---|
266 | procedure TDriveScan.Reset;
|
---|
267 | var
|
---|
268 | I: Integer;
|
---|
269 | begin
|
---|
270 | TimeStart := Now;
|
---|
271 | DamagedBlockCount := 0;
|
---|
272 | BlockMap.Clear;
|
---|
273 | FSectorCurrent := SectorStart;
|
---|
274 | SetLength(SpeedSteps, SpeedStepsCount);
|
---|
275 | for I := 0 to Length(SpeedSteps) - 1 do
|
---|
276 | SpeedSteps[I].Reset;
|
---|
277 | end;
|
---|
278 |
|
---|
279 | procedure TDriveScan.Start;
|
---|
280 | begin
|
---|
281 | if Terminated then begin
|
---|
282 | Terminated := False;
|
---|
283 | ScanThread := TScanThread.Create(True);
|
---|
284 | ScanThread.OnException := DoOnException;
|
---|
285 | ScanThread.Scan := Self;
|
---|
286 | ScanThread.Start;
|
---|
287 | SpeedTimeLast := Now;
|
---|
288 | SectorLast := SectorCurrent;
|
---|
289 | SpeedTimer.Enabled := True;
|
---|
290 | end;
|
---|
291 | end;
|
---|
292 |
|
---|
293 | procedure TDriveScan.Run;
|
---|
294 | var
|
---|
295 | F: TStream;
|
---|
296 | RealSize: Integer;
|
---|
297 | Buffer: Pointer;
|
---|
298 | BufferAligned: Pointer;
|
---|
299 | I: Integer;
|
---|
300 | Alignment: Integer;
|
---|
301 | begin
|
---|
302 | try
|
---|
303 | Lock.Acquire;
|
---|
304 | Terminated := False;
|
---|
305 | if ConfigTest then F := TTestStream.Create
|
---|
306 | else begin
|
---|
307 | //if Mode = rmRead then F := TFileStreamEx.Create(DriveName, fmOpenRead, [ffDirect])
|
---|
308 | // else if Mode = rmWrite then F := TFileStreamEx.Create(DriveName, fmOpenReadWrite, [ffDirect]);
|
---|
309 | if Mode = rmRead then F := TFileStream.Create(DriveName, fmOpenRead)
|
---|
310 | else if Mode = rmWrite then F := TFileStream.Create(DriveName, fmOpenReadWrite);
|
---|
311 | end;
|
---|
312 | try
|
---|
313 | Alignment := SectorSize;
|
---|
314 | Buffer := GetMem(SectorSize + Alignment);
|
---|
315 | BufferAligned := Pointer(Int64(Buffer) - (Int64(Buffer) and (Alignment - 1)) + Alignment);
|
---|
316 | if Mode = rmWrite then begin
|
---|
317 | if not WritePatternRandom then
|
---|
318 | FillChar(BufferAligned^, SectorSize, WritePattern)
|
---|
319 | else for I := 0 to SectorSize - 1 do
|
---|
320 | PByte(Int64(BufferAligned) + I)^ := Random(256);
|
---|
321 | end;
|
---|
322 | while FSectorCurrent < SectorEnd do begin
|
---|
323 | try
|
---|
324 | Lock.Release;
|
---|
325 | DoChange;
|
---|
326 | F.Position := FSectorCurrent * SectorSize;
|
---|
327 | if Mode = rmRead then RealSize := F.Read(BufferAligned^, SectorSize)
|
---|
328 | else if Mode = rmWrite then RealSize := F.Write(BufferAligned^, SectorSize)
|
---|
329 | else raise Exception.Create(SUnknownRunMode);
|
---|
330 | finally
|
---|
331 | Lock.Acquire;
|
---|
332 | end;
|
---|
333 | if RealSize <> SectorSize then begin
|
---|
334 | BlockMap.Sectors[FSectorCurrent] := bsDamaged;
|
---|
335 | Inc(DamagedBlockCount);
|
---|
336 | end else begin
|
---|
337 | if Mode = rmRead then BlockMap.Sectors[FSectorCurrent] := bsRead
|
---|
338 | else if Mode = rmWrite then BlockMap.Sectors[FSectorCurrent] := bsWrite;
|
---|
339 | end;
|
---|
340 | Inc(FSectorCurrent);
|
---|
341 | if Terminated then Break;
|
---|
342 | end;
|
---|
343 | FreeMem(Buffer);
|
---|
344 | finally
|
---|
345 | F.Free;
|
---|
346 | end;
|
---|
347 | finally
|
---|
348 | Lock.Release;
|
---|
349 | end;
|
---|
350 | TimeEnd := Now;
|
---|
351 | DoChange;
|
---|
352 | Terminated := True;
|
---|
353 | ScanThread.Synchronize(DoTerminate);
|
---|
354 | end;
|
---|
355 |
|
---|
356 | procedure TDriveScan.SetDriveName(AValue: string);
|
---|
357 | begin
|
---|
358 | if FDriveName = AValue then Exit;
|
---|
359 | FDriveName := AValue;
|
---|
360 | end;
|
---|
361 |
|
---|
362 | procedure TDriveScan.SetSectorCount(AValue: Integer);
|
---|
363 | begin
|
---|
364 | if BlockMap.SectorCount = AValue then Exit;
|
---|
365 | BlockMap.SectorCount := AValue;
|
---|
366 | end;
|
---|
367 |
|
---|
368 | procedure TDriveScan.SpeedTimerExecute(Sender: TObject);
|
---|
369 | var
|
---|
370 | SpeedTimeCurrent: TDateTime;
|
---|
371 | Index: Integer;
|
---|
372 | Value: Int64;
|
---|
373 | begin
|
---|
374 | SpeedTimeCurrent := Now;
|
---|
375 | Index := Trunc(SectorCurrent / SectorCount * SpeedStepsCount);
|
---|
376 | if Index >= Length(SpeedSteps) then Index := Length(SpeedSteps) - 1;
|
---|
377 | Value := Trunc((SectorCurrent - SectorLast) * SectorSize / ((SpeedTimeCurrent - SpeedTimeLast) / OneSecond));
|
---|
378 | if Value < 0 then Value := 0;
|
---|
379 | SpeedSteps[Index].UpdateValue(Value);
|
---|
380 | SpeedTimeLast := SpeedTimeCurrent;
|
---|
381 | SectorLast := SectorCurrent;
|
---|
382 | end;
|
---|
383 |
|
---|
384 | function TDriveScan.GetName: string;
|
---|
385 | begin
|
---|
386 | if Mode = rmNone then Result := 'None'
|
---|
387 | else if Mode = rmRead then Result := 'Read'
|
---|
388 | else if Mode = rmWrite then begin
|
---|
389 | if WritePatternRandom then Result := 'Write pattern random'
|
---|
390 | else Result := 'Write pattern $' + IntToHex(WritePattern, 2);
|
---|
391 | end else Result := '';
|
---|
392 | end;
|
---|
393 |
|
---|
394 | procedure TDriveScan.Stop;
|
---|
395 | begin
|
---|
396 | if not Terminated then begin
|
---|
397 | SpeedTimer.Enabled := False;
|
---|
398 | Terminated := True;
|
---|
399 | ScanThread.Terminate;
|
---|
400 | ScanThread.WaitFor;
|
---|
401 | FreeAndNil(ScanThread);
|
---|
402 | end;
|
---|
403 | end;
|
---|
404 |
|
---|
405 | procedure TDriveScan.LoadProfile(Profile: TDriveScanProfile);
|
---|
406 | begin
|
---|
407 | SectorStart := Profile.SectorStart;
|
---|
408 | SectorEnd := Profile.SectorEnd;
|
---|
409 | Mode := Profile.Mode;
|
---|
410 | WritePattern := Profile.WritePattern;
|
---|
411 | WritePatternRandom := Profile.WritePatternRandom;
|
---|
412 | end;
|
---|
413 |
|
---|
414 | constructor TDriveScan.Create;
|
---|
415 | begin
|
---|
416 | Lock := TCriticalSection.Create;
|
---|
417 | BlockMap := TBlockMap.Create;
|
---|
418 | SpeedTimer := TTimer.Create(nil);
|
---|
419 | SpeedTimer.Interval := 500;
|
---|
420 | SpeedTimer.Enabled := False;
|
---|
421 | SpeedTimer.OnTimer := SpeedTimerExecute;
|
---|
422 | SpeedStepsCount := 500;
|
---|
423 | SectorSize := 4096;
|
---|
424 | Terminated := True;
|
---|
425 | Reset;
|
---|
426 | Mode := rmNone;
|
---|
427 | end;
|
---|
428 |
|
---|
429 | destructor TDriveScan.Destroy;
|
---|
430 | begin
|
---|
431 | Stop;
|
---|
432 | FreeAndNil(SpeedTimer);
|
---|
433 | FreeAndNil(BlockMap);
|
---|
434 | FreeAndNil(Lock);
|
---|
435 | inherited Destroy;
|
---|
436 | end;
|
---|
437 |
|
---|
438 | procedure TDriveScan.SaveToNode(Node: TDOMNode);
|
---|
439 | var
|
---|
440 | NewNode: TDOMNode;
|
---|
441 | NewNode2: TDOMNode;
|
---|
442 | NewNode3: TDOMNode;
|
---|
443 | I: Integer;
|
---|
444 | begin
|
---|
445 | with Node do begin
|
---|
446 | WriteInteger(Node, 'DamagedBlockCount', DamagedBlockCount);
|
---|
447 | WriteInteger(Node, 'RunMode', Integer(Mode));
|
---|
448 | WriteInteger(Node, 'WritePattern', WritePattern);
|
---|
449 | WriteBoolean(Node, 'WritePatternRandom', WritePatternRandom);
|
---|
450 | WriteString(Node, 'DriveName', DriveName);
|
---|
451 | WriteDateTime(Node, 'TimeStart', TimeStart);
|
---|
452 | WriteDateTime(Node, 'TimeEnd', TimeEnd);
|
---|
453 | WriteInteger(Node, 'SectorSize', SectorSize);
|
---|
454 | WriteInteger(Node, 'SectorCount', SectorCount);
|
---|
455 | WriteInteger(Node, 'SectorStart', SectorStart);
|
---|
456 | WriteInteger(Node, 'SectorEnd', SectorEnd);
|
---|
457 | WriteInteger(Node, 'SectorCurrent', FSectorCurrent);
|
---|
458 |
|
---|
459 | NewNode := OwnerDocument.CreateElement('SectorMap');
|
---|
460 | AppendChild(NewNode);
|
---|
461 | BlockMap.SaveToNode(NewNode);
|
---|
462 |
|
---|
463 | NewNode := OwnerDocument.CreateElement('Speed');
|
---|
464 | AppendChild(NewNode);
|
---|
465 | WriteInt64(NewNode, 'Count', Length(SpeedSteps));
|
---|
466 | NewNode2 := OwnerDocument.CreateElement('Steps');
|
---|
467 | NewNode.AppendChild(NewNode2);
|
---|
468 | for I := 0 to Length(SpeedSteps) - 1 do
|
---|
469 | if not SpeedSteps[I].Null then begin
|
---|
470 | NewNode3 := OwnerDocument.CreateElement('Step');
|
---|
471 | NewNode2.AppendChild(NewNode3);
|
---|
472 | WriteInteger(NewNode3, 'Index', I);
|
---|
473 | WriteInt64(NewNode3, 'Avg', SpeedSteps[I].Average);
|
---|
474 | WriteInt64(NewNode3, 'Min', SpeedSteps[I].Min);
|
---|
475 | WriteInt64(NewNode3, 'Max', SpeedSteps[I].Max);
|
---|
476 | end;
|
---|
477 | end;
|
---|
478 | end;
|
---|
479 |
|
---|
480 | procedure TDriveScan.LoadFromNode(Node: TDOMNode);
|
---|
481 | var
|
---|
482 | NewNode: TDOMNode;
|
---|
483 | NewNode2: TDOMNode;
|
---|
484 | NewNode3: TDOMNode;
|
---|
485 | I: Integer;
|
---|
486 | begin
|
---|
487 | with Node do begin
|
---|
488 | DamagedBlockCount := ReadInteger(Node, 'DamagedBlockCount', 0);
|
---|
489 | Mode := TRunMode(ReadInteger(Node, 'RunMode', 0));
|
---|
490 | WritePattern := ReadInteger(Node, 'WritePattern', 0);
|
---|
491 | WritePatternRandom := ReadBoolean(Node, 'WritePatternRandom', False);
|
---|
492 | DriveName := ReadString(Node, 'DriveName', '');
|
---|
493 | TimeStart := ReadDateTime(Node, 'TimeStart', 0);
|
---|
494 | TimeEnd := ReadDateTime(Node, 'TimeEnd', 0);
|
---|
495 | SectorSize := ReadInteger(Node, 'SectorSize', 512);
|
---|
496 | SectorCount := ReadInteger(Node, 'SectorCount', 0);
|
---|
497 | SectorStart := ReadInteger(Node, 'SectorStart', 0);
|
---|
498 | SectorEnd := ReadInteger(Node, 'SectorEnd', SectorCount - 1);
|
---|
499 | FSectorCurrent := ReadInteger(Node, 'SectorCurrent', 0);
|
---|
500 |
|
---|
501 | NewNode := FindNode('SectorMap');
|
---|
502 | if Assigned(NewNode) then
|
---|
503 | BlockMap.LoadFromNode(NewNode);
|
---|
504 |
|
---|
505 | NewNode := FindNode('Speed');
|
---|
506 | if Assigned(NewNode) then begin
|
---|
507 | SpeedStepsCount := ReadInt64(NewNode, 'Count', 0);
|
---|
508 | SetLength(SpeedSteps, SpeedStepsCount);
|
---|
509 | NewNode2 := NewNode.FindNode('Steps');
|
---|
510 | if Assigned(NewNode2) then begin
|
---|
511 | NewNode3 := NewNode2.FirstChild;
|
---|
512 | while Assigned(NewNode3) and (NewNode3.NodeName = 'Step') do begin
|
---|
513 | I := ReadInteger(NewNode3, 'Index', 0);
|
---|
514 | SpeedSteps[I].Average := ReadInt64(NewNode3, 'Avg', 0);
|
---|
515 | SpeedSteps[I].Min := ReadInt64(NewNode3, 'Min', 0);
|
---|
516 | SpeedSteps[I].Max := ReadInt64(NewNode3, 'Max', 0);
|
---|
517 | SpeedSteps[I].Null := False;
|
---|
518 | NewNode3 := NewNode3.NextSibling;
|
---|
519 | end;
|
---|
520 | end;
|
---|
521 | end;
|
---|
522 | end;
|
---|
523 | end;
|
---|
524 |
|
---|
525 | end.
|
---|
526 |
|
---|