source: trunk/UBlockMap.pas

Last change on this file was 21, checked in by chronos, 8 years ago
  • Added: Suport for physical drive enumeration under Windows.
File size: 12.2 KB
Line 
1unit UBlockMap;
2
3{$mode delphi}
4
5interface
6
7uses
8 Classes, SysUtils, Graphics, Math, Contnrs, Dialogs, DOM, UXMLUtils;
9
10type
11 TSectorState = (bsNone, bsRead, bsDamaged, bsWrite);
12
13 TChange = class
14 Index: Integer;
15 Value: TSectorState;
16 end;
17
18 { TBlockMap }
19
20 TBlockMap = class
21 private
22 FBlockSize: TPoint;
23 FDrawSize: TPoint;
24 FSectorPerBlock: Integer;
25 FSectorCount: Integer;
26 FBlockCount: Integer;
27 FChanges: TObjectList; // TObjectList<TChange>
28 function GetBlockCount: Integer;
29 function GetSector(Index: Integer): TSectorState;
30 procedure SetBlockCount(AValue: Integer);
31 procedure SetBlockSize(AValue: TPoint);
32 procedure SetDrawSize(AValue: TPoint);
33 procedure SetSector(Index: Integer; AValue: TSectorState);
34 procedure SetSectorCount(AValue: Integer);
35 procedure SetSectorPerBlock(AValue: Integer);
36 procedure UpdateBlockSize;
37 function FindChange(Index: Integer): TChange;
38 function GetCombinedSectors(StartIndex, EndIndex: Integer): TSectorState;
39 public
40 ItemsCount: TPoint;
41 procedure Draw(Canvas: TCanvas);
42 procedure Clear;
43 constructor Create;
44 destructor Destroy; override;
45 procedure SaveToNode(Node: TDOMNode);
46 procedure LoadFromNode(Node: TDOMNode);
47 property BlockSize: TPoint read FBlockSize write SetBlockSize;
48 property DrawSize: TPoint read FDrawSize write SetDrawSize;
49 property SectorPerBlock: Integer read FSectorPerBlock write SetSectorPerBlock;
50 property SectorCount: Integer read FSectorCount write SetSectorCount;
51 property BlockCount: Integer read GetBlockCount;
52 property Sectors[Index: Integer]: TSectorState read GetSector write SetSector;
53 end;
54
55
56implementation
57
58resourcestring
59 SBlockSizeZeroNotAllowed = 'BlockSize can''t be set to 0';
60 SIndexOutOfRange = 'Index %d out of range';
61 SEmptyChangesList = 'Empty changes list';
62 SUnexpectedCombination = 'Unexpected combination';
63
64{ TBlockMap }
65
66procedure TBlockMap.SetBlockCount(AValue: Integer);
67begin
68 if FBlockCount = AValue then Exit;
69 FBlockCount := AValue;
70end;
71
72procedure TBlockMap.SetBlockSize(AValue: TPoint);
73begin
74 if (FBlockSize.X = AValue.X) and (FBlockSize.Y = AValue.Y) then Exit;
75 if (AValue.X = 0) or (AValue.Y = 0) then
76 raise Exception.Create(SBlockSizeZeroNotAllowed);
77 FBlockSize := AValue;
78end;
79
80procedure TBlockMap.SetDrawSize(AValue: TPoint);
81begin
82 if (FDrawSize.X = AValue.X) and (FDrawSize.Y = AValue.Y) then Exit;
83 FDrawSize := AValue;
84 UpdateBlockSize;
85end;
86
87function TBlockMap.GetSector(Index: Integer): TSectorState;
88var
89 Change: TChange;
90begin
91 if Index < FSectorCount then begin
92 Change := FindChange(Index);
93 if Assigned(Change) then Result := Change.Value
94 else raise Exception.Create(SIndexOutOfRange);
95 end else raise Exception.Create(SIndexOutOfRange);
96end;
97
98function TBlockMap.GetBlockCount: Integer;
99begin
100 Result := FBlockCount;
101end;
102
103procedure TBlockMap.SetSector(Index: Integer; AValue: TSectorState);
104var
105 LeftBeforeSame, RightBeforeSame: Boolean;
106 LeftAfterSame, RightAfterSame: Boolean;
107 Change: TChange;
108 ChangeIndex: Integer;
109begin
110 Change := FindChange(Index);
111 if Assigned(Change) then begin
112 if Change.Value = AValue then Exit;
113
114 ChangeIndex := FChanges.IndexOf(Change);
115 // Before
116 if ChangeIndex > 0 then begin
117 if (Index - TChange(FChanges[ChangeIndex]).Index) > 0 then LeftBeforeSame := True
118 else LeftBeforeSame := TChange(FChanges[ChangeIndex - 1]).Value = Change.Value;
119 end else begin
120 LeftBeforeSame := Change.Value = bsNone;
121 end;
122 if ChangeIndex < (FChanges.Count - 1) then begin
123 if (TChange(FChanges[ChangeIndex]).Index - Index) > 0 then RightBeforeSame := True
124 else RightBeforeSame := TChange(FChanges[ChangeIndex + 1]).Value = Change.Value;
125 end else begin
126 RightBeforeSame := Change.Value = bsNone;
127 end;
128 // After
129 if ChangeIndex > 0 then begin
130 if (Index - TChange(FChanges[ChangeIndex]).Index) > 0 then LeftAfterSame := True
131 else LeftAfterSame := TChange(FChanges[ChangeIndex - 1]).Value = AValue;
132 end else begin
133 LeftAfterSame := AValue = bsNone;
134 end;
135 if ChangeIndex < (FChanges.Count - 1) then begin
136 if (TChange(FChanges[ChangeIndex]).Index - Index) > 0 then RightAfterSame := True
137 else RightAfterSame := TChange(FChanges[ChangeIndex + 1]).Value = AValue;
138 end else begin
139 RightAfterSame := AValue = bsNone;
140 end;
141
142 // Update items
143 if not LeftBeforeSame and not RightBeforeSame and not LeftAfterSame and not RightAfterSame then begin
144 Change.Value := AValue
145 end else
146 if not LeftBeforeSame and not RightBeforeSame and not LeftAfterSame and RightAfterSame then begin
147 FChanges.Delete(ChangeIndex + 1);
148 Change.Value := AValue;
149 end else
150 if not LeftBeforeSame and not RightBeforeSame and LeftAfterSame and not RightAfterSame then begin
151 FChanges.Delete(ChangeIndex);
152 end else
153 if not LeftBeforeSame and not RightBeforeSame and LeftAfterSame and RightAfterSame then begin
154 FChanges.Delete(ChangeIndex + 1);
155 FChanges.Delete(ChangeIndex);
156 end else
157 if not LeftBeforeSame and RightBeforeSame and not LeftAfterSame and not RightAfterSame then begin
158 FChanges.Insert(ChangeIndex + 1, TChange.Create);
159 TChange(FChanges[ChangeIndex + 1]).Index := Index + 1;
160 TChange(FChanges[ChangeIndex + 1]).Value := Change.Value;
161 Change.Value := AValue;
162 end else
163 if not LeftBeforeSame and RightBeforeSame and LeftAfterSame and not RightAfterSame then begin
164 Change.Index := Index + 1;
165 end else
166 if not LeftBeforeSame and RightBeforeSame and not LeftAfterSame and not RightAfterSame then begin
167 FChanges.Insert(ChangeIndex, TChange.Create);
168 TChange(FChanges[ChangeIndex]).Index := Index;
169 TChange(FChanges[ChangeIndex]).Value := AValue;
170 end else
171 if LeftBeforeSame and not RightBeforeSame and not LeftAfterSame and RightAfterSame then begin
172 TChange(FChanges[ChangeIndex + 1]).Index := Index;
173 end else
174 if LeftBeforeSame and RightBeforeSame and not LeftAfterSame and not RightAfterSame then begin
175 if ChangeIndex > 0 then begin
176 FChanges.Insert(ChangeIndex, TChange.Create);
177 TChange(FChanges[ChangeIndex]).Index := Index;
178 TChange(FChanges[ChangeIndex]).Value := AValue;
179 end;
180 FChanges.Insert(ChangeIndex + 1, TChange.Create);
181 TChange(FChanges[ChangeIndex + 1]).Index := Index + 1;
182 TChange(FChanges[ChangeIndex + 1]).Value := Change.Value;
183 if ChangeIndex = 0 then begin
184 TChange(FChanges[ChangeIndex]).Index := Index;
185 TChange(FChanges[ChangeIndex]).Value := AValue;
186 FChanges.Insert(ChangeIndex, TChange.Create);
187 TChange(FChanges[ChangeIndex]).Index := 0;
188 TChange(FChanges[ChangeIndex]).Value := bsNone;
189 end;
190 end else raise Exception.Create(SUnexpectedCombination);
191 end else raise Exception.Create(Format(SIndexOutOfRange, [Index]));
192end;
193
194procedure TBlockMap.SetSectorCount(AValue: Integer);
195var
196 I: Integer;
197begin
198 if FSectorCount = AValue then Exit;
199 if (FChanges.Count = 0) and (AValue > 0) then begin
200 FChanges.Add(TChange.Create);
201 TChange(FChanges[0]).Index := 0;
202 TChange(FChanges[0]).Value := bsNone;
203 end;
204 FSectorCount := AValue;
205
206 // Cut changes outside of max sector count
207 I := FChanges.Count - 1;
208 while (I >= 0) and (TChange(FChanges[I]).Index >= FSectorCount) do Dec(I);
209 if (I >= 0) and (TChange(FChanges[I]).Index >= FSectorCount) then FChanges.Count := I;
210
211 UpdateBlockSize;
212end;
213
214procedure TBlockMap.SetSectorPerBlock(AValue: Integer);
215begin
216 if FSectorPerBlock = AValue then Exit;
217 FSectorPerBlock := AValue;
218 UpdateBlockSize;
219end;
220
221procedure TBlockMap.UpdateBlockSize;
222begin
223 ItemsCount := Point(Trunc(FDrawSize.X / BlockSize.X), Trunc(FDrawSize.Y / BlockSize.Y));
224 if (ItemsCount.X > 0) and (ItemsCount.Y > 0) then begin
225 FSectorPerBlock := Ceil(SectorCount / (ItemsCount.X * ItemsCount.Y));
226 end else FSectorPerBlock := 1;
227 if FSectorPerBlock < 1 then FSectorPerBlock := 1;
228 SetBlockCount(Ceil(SectorCount / FSectorPerBlock));
229end;
230
231function TBlockMap.FindChange(Index: Integer): TChange;
232var
233 LeftIndex, MiddleIndex, RightIndex: Integer;
234begin
235 if Fchanges.Count = 0 then raise Exception.Create(SEmptyChangesList);
236 if FChanges.Count = 1 then begin
237 Result := TChange(FChanges[0]);
238 Exit;
239 end;
240 Result := nil;
241 LeftIndex := 0;
242 RightIndex := FChanges.Count - 1;
243 while (LeftIndex <= RightIndex) do begin
244 MiddleIndex := LeftIndex + (RightIndex - LeftIndex) div 2;
245 if (TChange(FChanges[MiddleIndex]).Index <= Index) and
246 ((FChanges.Count = (MiddleIndex + 1)) or
247 (TChange(FChanges[MiddleIndex + 1]).Index > Index)) then begin
248 Result := TChange(FChanges[MiddleIndex]);
249 Break;
250 end;
251 // Cut interval in half and use nearest section
252 if TChange(FChanges[MiddleIndex]).Index < Index then begin
253 if MiddleIndex = LeftIndex then Inc(LeftIndex)
254 else LeftIndex := MiddleIndex
255 end else begin
256 if MiddleIndex = RightIndex then Dec(RightIndex)
257 else RightIndex := MiddleIndex
258 end;
259 end;
260end;
261
262function TBlockMap.GetCombinedSectors(StartIndex, EndIndex: Integer
263 ): TSectorState;
264var
265 Change: TChange;
266 ChangeIndex: Integer;
267begin
268 Result := bsNone;
269 Change := FindChange(StartIndex);
270 if Assigned(Change) then begin
271 ChangeIndex := FChanges.IndexOf(Change);
272 while (ChangeIndex < FChanges.Count) and
273 (TChange(FChanges[ChangeIndex]).Index <= EndIndex) do begin
274 if (Result <> bsDamaged) and (TChange(FChanges[ChangeIndex]).Value = bsRead) then Result := bsRead
275 else if (Result <> bsDamaged) and (TChange(FChanges[ChangeIndex]).Value = bsWrite) then Result := bsWrite
276 else if TChange(FChanges[ChangeIndex]).Value = bsDamaged then Result := bsDamaged;
277 Inc(ChangeIndex);
278 end;
279 end;
280end;
281
282procedure TBlockMap.Draw(Canvas: TCanvas);
283var
284 I: Integer;
285 Rect: TRect;
286 BlockState: TSectorState;
287begin
288 Canvas.Pen.Style := psSolid;
289 Canvas.Pen.Color := clBlack;
290 // Clean background
291 Canvas.Brush.Style := bsSolid;
292 Canvas.Brush.Color := clBlack;
293 Canvas.FillRect(0, 0, Canvas.Width, Canvas.Height);
294
295 DrawSize := Point(Canvas.Width, Canvas.Height);
296 if SectorPerBlock >= 1 then begin
297 for I := 0 to BlockCount - 1 do begin
298 Rect := Bounds((I mod ItemsCount.X) * BlockSize.X,
299 (I div ItemsCount.X) * BlockSize.Y, BlockSize.X - 1, BlockSize.Y - 1);
300 BlockState := GetCombinedSectors(I * SectorPerBlock, (I + 1) * SectorPerBlock);
301 if BlockState = bsRead then Canvas.Brush.Color := clGreen
302 else if BlockState = bsDamaged then Canvas.Brush.Color := clRed
303 else if BlockState = bsWrite then Canvas.Brush.Color := clBlue
304 else Canvas.Brush.Color := clSilver;
305 Canvas.FillRect(Rect);
306 //Canvas.TextOut(Rect.Left, Rect.Top, IntToStr(I));
307 end;
308 end;
309end;
310
311procedure TBlockMap.Clear;
312begin
313 FChanges.Clear;
314 if FSectorCount > 0 then begin
315 FChanges.Add(TChange.Create);
316 TChange(FChanges[0]).Index := 0;
317 TChange(FChanges[0]).Value := bsNone;
318 end;
319end;
320
321constructor TBlockMap.Create;
322begin
323 FChanges := TObjectList.Create;
324 FBlockSize := Point(12, 12);
325 SectorPerBlock := 1;
326 FSectorCount := 0;
327end;
328
329destructor TBlockMap.Destroy;
330begin
331 FreeAndNil(FChanges);
332 inherited Destroy;
333end;
334
335procedure TBlockMap.SaveToNode(Node: TDOMNode);
336var
337 NewNode: TDOMNode;
338 NewNode2: TDOMNode;
339 I: Integer;
340begin
341 NewNode := Node.OwnerDocument.CreateElement('Changes');
342 Node.AppendChild(NewNode);
343 for I := 0 to FChanges.Count - 1 do begin
344 NewNode2 := NewNode.OwnerDocument.CreateElement('Change');
345 NewNode.AppendChild(NewNode2);
346 WriteInteger(NewNode2, 'Index', TChange(FChanges[I]).Index);
347 WriteInteger(NewNode2, 'Value', Integer(TChange(FChanges[I]).Value));
348 end;
349end;
350
351procedure TBlockMap.LoadFromNode(Node: TDOMNode);
352var
353 NewNode: TDOMNode;
354 NewNode2: TDOMNode;
355 NewChange: TChange;
356begin
357 NewNode := Node.FindNode('Changes');
358 if Assigned(NewNode) then begin
359 FChanges.Count := 0;
360 NewNode2 := NewNode.FirstChild;
361 while Assigned(NewNode2) and (NewNode2.NodeName = 'Change') do begin
362 NewChange := TChange.Create;
363 NewChange.Index := ReadInteger(NewNode2, 'Index', 0);
364 NewChange.Value := TSectorState(ReadInteger(NewNode2, 'Value', 0));
365 FChanges.Add(NewChange);
366 NewNode2 := NewNode2.NextSibling;
367 end;
368 end;
369end;
370
371end.
372
Note: See TracBrowser for help on using the repository browser.