73
86
// the lower 24 bits contain the color used for display.
74
87
// The actual display color is blended together from the flags
75
88
// in the upper 8 bits by ColorizeBlockMask
76
MASK_NON_TRIED = $01000000;
77
MASK_NON_TRIMMED = $02000000;
78
MASK_NON_SCRAPED = $04000000;
79
MASK_BAD_SECT = $08000000;
80
MASK_FINISHED = $10000000;
81
MASK_ALL_STATUSES = $1F000000;
82
MASK_ACTIVE = $20000000;
83
MASKS : array[0..4] of Longint =
84
(MASK_NON_TRIED, MASK_FINISHED, MASK_NON_TRIMMED, MASK_NON_SCRAPED, MASK_BAD_SECT);
89
MASK_OUTSIDE_DOMAIN = $01000000;
90
MASK_NON_TRIED = $02000000;
91
MASK_NON_TRIMMED = $04000000;
92
MASK_NON_SCRAPED = $08000000;
93
MASK_BAD_SECT = $10000000;
94
MASK_FINISHED = $20000000;
95
MASK_ALL_STATUSES = $3F000000;
96
MASK_ACTIVE = $40000000;
97
MASKS : array[0..5] of Longint =
98
(MASK_OUTSIDE_DOMAIN, MASK_NON_TRIED, MASK_FINISHED, MASK_NON_TRIMMED,
99
MASK_NON_SCRAPED, MASK_BAD_SECT);
86
// color defaults for the block statuses
102
DEF_COLOR_OUTSIDE_DOMAIN = $343434;
87
103
DEF_COLOR_NON_TRIED = $909090;
88
104
DEF_COLOR_NON_TRIMMED = $00e0ff;
89
105
DEF_COLOR_NON_SCRAPED = $ff2020;
107
126
// default device block size
110
PROGRAM_TITLE = 'ddrescue log viewer';
129
PROGRAM_TITLE = 'ddrescueview';
111
130
VERSION_MAJOR = '0';
112
VERSION_MINOR = '4 alpha';
132
VERSION_SUFFIX = 'alpha 2';
113
133
emptyRescueStatus : TRescueStatus =
114
(devicesize : 0; suggestedBlockSize : DEF_BSIZE; pos : 0; rescued : 0; nontried : 0; bad : 0;
115
nonscraped : 0; nontrimmed : 0; errors : 0; curOperation : #0; strCurOperation : '');
134
(devicesize : 0; suggestedBlockSize : DEF_BSIZE; pos : 0; rescued : 0;
135
nontried : 0; bad : 0; nonscraped : 0; nontrimmed : 0; outsidedomain : 0;
136
errors : 0; curOperation : #0; strCurOperation : '');
118
139
useDecimalUnits : boolean = true;
119
// colors for the block statuses
120
COLOR_NON_TRIED : Longint = $909090;
121
COLOR_NON_TRIMMED : Longint = $00e0ff;
122
COLOR_NON_SCRAPED : Longint = $ff2020;
123
COLOR_BAD_SECT : Longint = $0000ff;
124
COLOR_FINISHED : Longint = $20e020;
125
COLOR_UNDEFINED : Longint = $606060;
126
COLOR_ACTIVE : Longint = $ffff00;
141
COLOR_OUTSIDE_DOMAIN : Longint = DEF_COLOR_OUTSIDE_DOMAIN;
142
COLOR_NON_TRIED : Longint = DEF_COLOR_NON_TRIED;
143
COLOR_NON_TRIMMED : Longint = DEF_COLOR_NON_TRIMMED;
144
COLOR_NON_SCRAPED : Longint = DEF_COLOR_NON_SCRAPED;
145
COLOR_BAD_SECT : Longint = DEF_COLOR_BAD_SECT;
146
COLOR_FINISHED : Longint = DEF_COLOR_FINISHED;
147
COLOR_UNDEFINED : Longint = DEF_COLOR_UNDEFINED;
148
COLOR_ACTIVE : Longint = DEF_COLOR_ACTIVE;
149
COLOR_SELECTED : Longint = DEF_COLOR_SELECTED;
127
150
// color weights to be used by ColorizeBlockMask for blending
128
WEIGHT_NON_TRIED : Longint = 1;
129
WEIGHT_FINISHED : Longint = 2;
130
WEIGHT_NON_TRIMMED : Longint = 4;
131
WEIGHT_NON_SCRAPED : Longint = 10;
132
WEIGHT_BAD_SECT : Longint = 40;
133
// arrays containing the above constants for easier looping
134
COLORS : array[0..4] of ^Longint =
135
(@COLOR_NON_TRIED, @COLOR_FINISHED, @COLOR_NON_TRIMMED, @COLOR_NON_SCRAPED, @COLOR_BAD_SECT);
136
WEIGHTS : array[0..4] of ^Longint =
137
(@WEIGHT_NON_TRIED, @WEIGHT_FINISHED, @WEIGHT_NON_TRIMMED, @WEIGHT_NON_SCRAPED, @WEIGHT_BAD_SECT);
151
WEIGHT_OUTSIDE_DOMAIN : Longint = DEF_WEIGHT_OUTSIDE_DOMAIN;
152
WEIGHT_NON_TRIED : Longint = DEF_WEIGHT_NON_TRIED;
153
WEIGHT_FINISHED : Longint = DEF_WEIGHT_FINISHED;
154
WEIGHT_NON_TRIMMED : Longint = DEF_WEIGHT_NON_TRIMMED;
155
WEIGHT_NON_SCRAPED : Longint = DEF_WEIGHT_NON_SCRAPED;
156
WEIGHT_BAD_SECT : Longint = DEF_WEIGHT_BAD_SECT;
157
// arrays pointing at the above values for easier looping
158
COLORS : array[0..5] of ^Longint =
159
(@COLOR_OUTSIDE_DOMAIN, @COLOR_NON_TRIED, @COLOR_FINISHED,
160
@COLOR_NON_TRIMMED, @COLOR_NON_SCRAPED, @COLOR_BAD_SECT);
161
WEIGHTS : array[0..5] of ^Longint =
162
(@WEIGHT_OUTSIDE_DOMAIN, @WEIGHT_NON_TRIED, @WEIGHT_FINISHED,
163
@WEIGHT_NON_TRIMMED, @WEIGHT_NON_SCRAPED, @WEIGHT_BAD_SECT);
164
// Default size formatters
165
SF_FLOAT : TSizeFormat = (Rounding : rShortFloat; Base : bDec; Prefix : pDec; SectSize : DEF_BSIZE);
166
SF_SECT : TSizeFormat = (Rounding : rSector; Base : bDec; Prefix : pDec; SectSize : DEF_BSIZE);
167
SF_HEX : TSizeFormat = (Rounding : rNone; Base : bHex; Prefix : pDec; SectSize : DEF_BSIZE);
168
SFORMATS : array[0..2] of ^TSizeFormat = (@SF_FLOAT, @SF_SECT, @SF_HEX);
139
170
function BlockStatusToString(status: char) : String;
140
171
function OperationToText(status: char) : string;
141
function SizeStr(sizeInBytes : int64): String;
142
172
function BlockOverlap(b1Start, b1End, b2Start, b2End : Int64): Int64;
173
function verLT(versionA, versionB : String) : Boolean;
174
function blendColors(color1, color2: TColor; intensity1 : integer): TColor; inline;
175
function InRangeEx(const AValue, A1, A2: Longint): Boolean; inline;
176
function IntersectEntries(entry1, entry2 : TLogEntry): TLogEntry;
177
function FilePart(path: String): String;
146
180
uses SysUtils, Math;
182
// status strings for each block status
149
183
function BlockStatusToString(status: char) : String;
152
186
'?': result := 'Non-tried';
187
'+': result := 'Rescued';
153
188
'*': result := 'Non-trimmed';
154
189
'/': result := 'Non-scraped';
155
190
'-': result := 'Bad sector(s)';
156
'+': result := 'Rescued';
191
'd': result := 'Not in domain';
158
result := 'Unknown status';
193
result := 'Unknown ('+status+')';
163
198
function OperationToText(status: char) : string;
166
'?': OperationToText := 'Copying non-tried';
167
'*': OperationToText := 'Trimming non-trimmed blocks';
168
'/': OperationToText := 'Scraping non-scraped blocks';
169
'-': OperationToText := 'Retrying bad sectors';
170
'F': OperationToText := 'Filling specified blocks';
171
'G': OperationToText := 'Generating approximate logfile';
172
'+': OperationToText := 'Finished';
201
'?': result := 'Copying non-tried blocks';
202
'*': result := 'Trimming non-trimmed blocks';
203
'/': result := 'Scraping non-scraped blocks';
204
'-': result := 'Retrying bad sectors';
205
'F': result := 'Filling specified blocks';
206
'G': result := 'Generating approximate logfile';
207
'+': result := 'Finished';
174
OperationToText := 'Unknown operation';
209
result := 'Unknown ('+status+')';
178
function SizeStr(sizeInBytes : int64): String;
213
function TSizeFormat.SizeStr(size: Int64): String;
215
decPrefixes : array[0..5] of String = ('Byte', 'KB', 'MB', 'GB', 'TB', 'PB');
216
binPrefixes : array[0..5] of String = ('Byte', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
219
iSectSize, thresh, divisor : Longint;
180
if useDecimalUnits then begin
181
if sizeInBytes < 0 then SizeStr := 'invalid size?'
182
else if sizeInBytes < 100000 then SizeStr := IntToStr(sizeInBytes)+ ' Byte'
183
else if sizeInBytes < 100000000 then SizeStr := IntToStr(sizeInBytes div 1000)+ ' KB'
184
else if sizeInBytes < 100000000000 then SizeStr := IntToStr(sizeInBytes div 1000000)+ ' MB'
185
else if sizeInBytes < 100000000000000 then SizeStr := IntToStr(sizeInBytes div 1000000000)+ ' GB'
186
else if sizeInBytes < 100000000000000000 then SizeStr := IntToStr(sizeInBytes div 1000000000000)+ ' TB'
187
else SizeStr := IntToStr(sizeInBytes div 1000000000000000)+ ' PB';
189
if sizeInBytes < 0 then SizeStr := 'invalid size?'
190
else if sizeInBytes < 102400 then SizeStr := IntToStr(sizeInBytes)+ ' Byte'
191
else if sizeInBytes < 104857600 then SizeStr := IntToStr(sizeInBytes div 1024)+ ' KiB'
192
else if sizeInBytes < 107374182400 then SizeStr := IntToStr(sizeInBytes div 1048576)+ ' MiB'
193
else if sizeInBytes < 109951162777600 then SizeStr := IntToStr(sizeInBytes div 1073741824)+ ' GiB'
194
else if sizeInBytes < 112589990684262400 then SizeStr := IntToStr(sizeInBytes div 1099511627776)+ ' TiB'
195
else SizeStr := IntToStr(sizeInBytes div 1125899906842624)+ ' PiB';
224
if Base = bHex then begin
225
result:='0x'+IntToHex(size, 8);
228
if Rounding = rSector then begin
229
if SectSize < 1 then iSectSize:=512 else iSectSize:=SectSize;
230
result:=IntToStr(size div iSectSize)+' s';
233
if Rounding = rNone then begin
234
result:=IntToStr(size)+' Byte';
237
if Prefix = pDec then divisor:=1000 else divisor:=1024;
238
if Rounding = rShortInt then thresh:=divisor*100 else thresh:=divisor;
242
while i < High(decPrefixes) do begin
243
if Rounding = rShortInt then begin
244
if iValue < thresh then break;
245
iValue := iValue div divisor;
248
if fValue < thresh then break;
249
fValue := fValue / divisor;
253
if Prefix = pDec then sPrefix:=decPrefixes[i] else sPrefix:=binPrefixes[i];
254
if (i = 0) or (Rounding = rShortInt) then result:=IntToStr(iValue)+' '+sPrefix
255
else result:=FloatToStrF(fValue, ffFixed, 12, 2)+' '+sPrefix;
258
// returns the number of bytes by which two blocks on a device overlap.
199
259
function BlockOverlap(b1Start, b1End, b2Start, b2End: Int64): Int64;
201
261
BlockOverlap:=Max(0, Min(Min(b1End-b1Start, b2End-b2Start),
202
262
Min(b2End-b1Start, b1End-b2Start)));
265
// return the intersection of two log entries. Status is copied from first.
266
function IntersectEntries(entry1, entry2 : TLogEntry): TLogEntry;
268
result.status:=entry1.status;
269
result.length:=BlockOverlap(entry1.offset, entry1.offset+entry1.length,
270
entry2.offset, entry2.offset+entry2.length);
271
result.offset:=max(entry1.offset, entry2.offset);
274
// returns the file part of a file path
275
function FilePart(path: String): String;
276
var delimPos : Integer;
278
delimPos := LastDelimiter('\/:', path);
279
result := Copy(path, delimPos+1, Length(path)-delimPos);
282
// compare two version strings and return true if versionA is less than versionB
283
// could be used with TParser.Version in the future to adapt to ddrescue versions
284
function verLT(versionA, versionB : String) : Boolean;
286
listA, listB : TStringList;
287
intsA, intsB : array of integer;
290
// set up the string lists
291
listA:=TStringList.Create;
292
listB:=TStringList.Create;
293
listA.StrictDelimiter:=true;
294
listB.StrictDelimiter:=true;
295
listA.Delimiter:='.';
296
listB.Delimiter:='.';
297
// read in the delimited text
298
listA.DelimitedText:=versionA;
299
listB.DelimitedText:=versionB;
300
// Extend both lists to the same length by appending zeroes
301
while listA.Count < listB.Count do listA.Add('0');
302
while listB.Count < listA.Count do listB.Add('0');
303
// strip non-numeric suffixes from the version parts
304
for i := 0 to listA.Count-1 do begin
305
for j := 1 to Length(listA[i]) do begin
306
if not (listA[i][j] in ['0'..'9']) then begin
307
listA[i]:=Copy(listA[i], 1, j-1);
311
for j := 1 to Length(listB[i]) do begin
312
if not (listB[i][j] in ['0'..'9']) then begin
313
listB[i]:=Copy(listB[i], 1, j-1);
318
// set up the integer lists.
319
SetLength(intsA, listA.Count);
320
SetLength(intsB, listB.Count);
321
// Convert the string parts of each version to an int list
322
for i := 0 to listA.Count-1 do begin
323
intsA[i]:=StrToIntDef(listA[i], 0);
324
intsB[i]:=StrToIntDef(listB[i], 0);
329
// Compare the list elements
330
for i := 0 to Length(intsA)-1 do begin
331
if intsA[i] < intsB[i] then exit(true)
332
else if intsB[i] < intsA[i] then exit(false);
334
// at this point the versions are equal
338
// Blends two colors. intensity1 [0..256] determines the strength of color1.
339
// intensity1 values over 256 will cause color corruption
340
// this is more optimized for speed than for clarity or rounding accuracy.
341
function blendColors(color1, color2: TColor; intensity1 : integer): TColor; inline;
342
const mask = $FF00FF00FF;
345
tmp:=(((Int64(color1)*$1000001 and mask)*intensity1 +
346
(Int64(color2)*$1000001 and mask)*(256-intensity1)) shr 8) and mask;
347
result:= TColor(tmp) or TColor(tmp shr 24);
350
// allows Min/Max args to be reversed
351
function InRangeEx(const AValue, A1, A2: Longint): Boolean; inline;
353
Result:=((AValue>=A1) and (AValue<=A2)) or ((AValue>=A2) and (AValue<=A1));
205
356
procedure TObservablePersistent.AttachObserver(AObserver : TObject);
207
358
FPOAttachObserver(AObserver);