784 lines
18 KiB
C
784 lines
18 KiB
C
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
hivestat.c
|
|
|
|
Abstract:
|
|
|
|
Dumps various statistics on hv (low) level structures in a hive. (See
|
|
regstat for higher level stuff.)
|
|
|
|
Statistics:
|
|
|
|
Short: # of bins
|
|
average bin size
|
|
max bin size
|
|
# of cells
|
|
# of free cells
|
|
# of allocated cells
|
|
average free cell size
|
|
total free size
|
|
max free cell size
|
|
average allocated size
|
|
total allocated size
|
|
max allocated cell size
|
|
overhead summary (header, bin headers, cell headers)
|
|
|
|
Long: bin#, offset, size
|
|
cell offset, size, allocated
|
|
cell offset, size, free
|
|
|
|
|
|
Usage: {[+|-][<option>]} <filename>
|
|
(+ = on by default, - = off by default)
|
|
+s = summary - all of the short statistics
|
|
-t[bafc] = trace, line per entry, bin, allocated, free, all cells
|
|
(+tbc == +tbaf)
|
|
-c = cell type summary
|
|
-a[kvs] = Access Export (key nodes, values, SDs)
|
|
|
|
Author:
|
|
|
|
Bryan M. Willman (bryanwi) 2-Sep-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
/*
|
|
|
|
NOTE: Unlike other hive/registry tools, this one will not read the
|
|
entire hive into memory, but will instead read it in via
|
|
file I/O. This makes it faster/easier to apply to very large
|
|
hives.
|
|
|
|
*/
|
|
#include "regutil.h"
|
|
#include "edithive.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <windows.h>
|
|
|
|
UCHAR *helptext[] = {
|
|
"hivestat: ",
|
|
"Statistics: ",
|
|
" Short: # of bins ",
|
|
" average bin size ",
|
|
" max bin size ",
|
|
" # of cells ",
|
|
" # of free cells ",
|
|
" # of allocated cells ",
|
|
" average free cell size ",
|
|
" total free size ",
|
|
" max free cell size ",
|
|
" average allocated size ",
|
|
" total allocated size ",
|
|
" max allocated cell size ",
|
|
" overhead summary (header, bin headers, cell headers) ",
|
|
" Long: bin#, offset, size ",
|
|
" cell offset, size, allocated ",
|
|
" cell offset, size, free ",
|
|
"Usage: {[+|-][<option>]} <filename> ",
|
|
" (+ = on by default, - = off by default) ",
|
|
" +s = summary - all of the short statistics ",
|
|
" -t[bafc] = trace, line per entry, bin, allocated, free, all cells",
|
|
" (+tbc == +tbaf) ",
|
|
" -c = cell type summary ",
|
|
" -a[kvs] = Access Export (key nodes, values, SDs) ",
|
|
NULL
|
|
};
|
|
|
|
|
|
VOID
|
|
ParseArgs(
|
|
int argc,
|
|
char *argv[]
|
|
);
|
|
|
|
VOID
|
|
ScanHive(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
ScanCell(
|
|
PHCELL Cell,
|
|
ULONG CellSize
|
|
);
|
|
|
|
VOID
|
|
ScanKeyNode(
|
|
IN PCM_KEY_NODE Node,
|
|
IN ULONG CellSize
|
|
);
|
|
|
|
VOID
|
|
ScanKeyValue(
|
|
IN PCM_KEY_VALUE Value,
|
|
IN ULONG CellSize
|
|
);
|
|
|
|
VOID
|
|
ScanKeySD(
|
|
IN PCM_KEY_SECURITY Security,
|
|
IN ULONG CellSize
|
|
);
|
|
|
|
VOID
|
|
ScanKeyIndex(
|
|
IN PCM_KEY_INDEX Index,
|
|
IN ULONG CellSize
|
|
);
|
|
|
|
VOID
|
|
ScanUnknown(
|
|
IN PCELL_DATA Data,
|
|
IN ULONG CellSize
|
|
);
|
|
|
|
|
|
|
|
// CONTROL ARGUMENTS
|
|
|
|
BOOLEAN DoCellType = FALSE;
|
|
BOOLEAN DoSummary = TRUE;
|
|
BOOLEAN DoTraceBin = FALSE;
|
|
BOOLEAN DoTraceFree = FALSE;
|
|
BOOLEAN DoTraceAlloc = FALSE;
|
|
|
|
BOOLEAN AccessKeys = FALSE;
|
|
BOOLEAN AccessValues = FALSE;
|
|
BOOLEAN AccessSD = FALSE;
|
|
LPCTSTR FileName = NULL;
|
|
|
|
ULONG HiveVersion;
|
|
|
|
|
|
// SUMMARY TOTALS
|
|
|
|
ULONG SizeKeyData=0;
|
|
ULONG SizeValueData=0;
|
|
ULONG SizeSDData=0;
|
|
ULONG SizeIndexData=0;
|
|
ULONG SizeUnknownData=0;
|
|
|
|
ULONG NumKeyData=0;
|
|
ULONG NumValueData=0;
|
|
ULONG NumSDData=0;
|
|
ULONG NumIndexData=0;
|
|
ULONG NumUnknownData=0;
|
|
|
|
void
|
|
main(
|
|
int argc,
|
|
char *argv[]
|
|
)
|
|
{
|
|
ParseArgs(argc, argv);
|
|
ScanHive();
|
|
exit(0);
|
|
}
|
|
|
|
VOID
|
|
ParseArgs(
|
|
int argc,
|
|
char *argv[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read arguments and set control arguments and file name from them.
|
|
|
|
Arguments:
|
|
|
|
argc, argv, standard meaning
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
char *p;
|
|
int i;
|
|
BOOLEAN command;
|
|
|
|
if (argc == 1) {
|
|
for (i = 0; helptext[i] != NULL; i++) {
|
|
fprintf(stderr, "%s\n", helptext[i]);
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
p = argv[i];
|
|
|
|
if (*p == '+') {
|
|
// switch something on
|
|
command = TRUE;
|
|
|
|
} else if (*p == '-') {
|
|
// switch something off
|
|
command = FALSE;
|
|
|
|
} else {
|
|
FileName = p;
|
|
continue;
|
|
}
|
|
|
|
p++;
|
|
if (*p == '\0')
|
|
continue;
|
|
|
|
switch (*p) {
|
|
case 's':
|
|
case 'S':
|
|
DoSummary = command;
|
|
break;
|
|
|
|
case 'c':
|
|
case 'C':
|
|
DoCellType = command;
|
|
break;
|
|
|
|
case 'a':
|
|
case 'A':
|
|
p++;
|
|
while (*p != '\0') {
|
|
switch (*p) {
|
|
case 'k':
|
|
case 'K':
|
|
AccessKeys = command;
|
|
break;
|
|
|
|
case 's':
|
|
case 'S':
|
|
AccessSD = command;
|
|
break;
|
|
|
|
case 'v':
|
|
case 'V':
|
|
AccessValues = command;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
case 'T':
|
|
p++;
|
|
while (*p != '\0') {
|
|
|
|
switch (*p) {
|
|
case 'b':
|
|
case 'B':
|
|
DoTraceBin = command;
|
|
break;
|
|
|
|
case 'a':
|
|
case 'A':
|
|
DoTraceAlloc = command;
|
|
break;
|
|
|
|
case 'f':
|
|
case 'F':
|
|
DoTraceFree = command;
|
|
break;
|
|
|
|
case 'c':
|
|
case 'C':
|
|
DoTraceAlloc = command;
|
|
DoTraceFree = command;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
p++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
ScanHive(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Scan the hive, report what we see, based on control arguments.
|
|
|
|
--*/
|
|
{
|
|
static char buffer[HBLOCK_SIZE];
|
|
PHBASE_BLOCK bbp;
|
|
HANDLE filehandle;
|
|
BOOL rf;
|
|
ULONG readcount;
|
|
ULONG hivelength;
|
|
ULONG hiveposition;
|
|
PHCELL cp;
|
|
PHCELL guard;
|
|
PHBIN hbp;
|
|
ULONG hoff;
|
|
ULONG StatBinCount = 0;
|
|
ULONG StatBinTotal = 0;
|
|
ULONG StatBinMax = 0;
|
|
ULONG StatFreeCount = 0;
|
|
ULONG StatFreeTotal = 0;
|
|
ULONG StatFreeMax = 0;
|
|
ULONG StatAllocCount = 0;
|
|
ULONG StatAllocTotal = 0;
|
|
ULONG StatAllocMax = 0;
|
|
ULONG binread;
|
|
ULONG binsize;
|
|
ULONG cellsize;
|
|
ULONG boff;
|
|
ULONG lboff;
|
|
ULONG SizeTotal;
|
|
|
|
|
|
// open the file
|
|
|
|
filehandle = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
|
|
if (filehandle == INVALID_HANDLE_VALUE) {
|
|
fprintf(stderr,
|
|
"hivestat: Could not open file '%s' error = %08lx\n",
|
|
FileName, GetLastError()
|
|
);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
|
|
// read the header
|
|
|
|
rf = ReadFile(filehandle, buffer, HBLOCK_SIZE, &readcount, NULL);
|
|
if ( ( ! rf ) || (readcount != HBLOCK_SIZE) ) {
|
|
fprintf(stderr, "hivestat: '%s' - cannot read base block!\n", FileName);
|
|
exit(1);
|
|
}
|
|
|
|
bbp = (PHBASE_BLOCK)(&(buffer[0]));
|
|
|
|
if ((bbp->Major != HSYS_MAJOR) ||
|
|
(bbp->Minor > HSYS_MINOR))
|
|
{
|
|
fprintf(stderr,
|
|
"hivestat: major/minor != %d/%d get newer hivestat\n",
|
|
HSYS_MAJOR, HSYS_MINOR
|
|
);
|
|
exit(1);
|
|
}
|
|
|
|
HiveVersion = bbp->Minor;
|
|
|
|
hivelength = bbp->Length + HBLOCK_SIZE;
|
|
hiveposition = HBLOCK_SIZE;
|
|
hoff = 0;
|
|
|
|
printf("hivestat: '%s'\n", FileName);
|
|
if (DoTraceBin || DoTraceFree || DoTraceAlloc) {
|
|
printf("\nTrace\n");
|
|
printf("bi=bin, fr=free, al=allocated\n");
|
|
printf("offset is file offset, sub HBLOCK to get HCELL\n");
|
|
printf("type,offset,size\n");
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
// scan the hive
|
|
|
|
guard = (PHCELL)(&(buffer[0]) + HBLOCK_SIZE);
|
|
|
|
|
|
// hiveposition is file relative offset of next block we will read
|
|
|
|
// hoff is the file relative offset of the last block we read
|
|
|
|
// hivelength is actual length of file (header's recorded length plus
|
|
// the size of the header.
|
|
|
|
// cp is pointer into memory, within range of buffer, it's a cell pointer
|
|
|
|
while (hiveposition < hivelength) {
|
|
|
|
|
|
// read in first block of bin, check signature, get bin stats
|
|
|
|
rf = ReadFile(filehandle, buffer, HBLOCK_SIZE, &readcount, NULL);
|
|
if ( ( ! rf ) || (readcount != HBLOCK_SIZE) ) {
|
|
fprintf(stderr, "hivestat: '%s' read error @%08lx\n", FileName, hiveposition);
|
|
exit(1);
|
|
}
|
|
hbp = (PHBIN)(&(buffer[0]));
|
|
|
|
if (hbp->Signature != HBIN_SIGNATURE) {
|
|
fprintf(stderr,
|
|
"hivestat: '%s' bad bin sign. @%08lx\n", FileName, hiveposition);
|
|
exit(1);
|
|
}
|
|
hiveposition += HBLOCK_SIZE;
|
|
hoff += HBLOCK_SIZE;
|
|
ASSERT(hoff+HBLOCK_SIZE == hiveposition);
|
|
|
|
StatBinCount++;
|
|
binsize = hbp->Size;
|
|
StatBinTotal += binsize;
|
|
if (binsize > StatBinMax) {
|
|
StatBinMax = binsize;
|
|
}
|
|
|
|
if (DoTraceBin) {
|
|
printf("bi,x%08lx,%ld\n", hoff, binsize);
|
|
}
|
|
|
|
|
|
// scan the bin
|
|
|
|
// cp = pointer to cell we are looking at
|
|
// boff = offset within bin
|
|
// lboff = last offset within bin, used only for consistency checks
|
|
// binread = number of bytes of bin we've read so far
|
|
|
|
cp = (PHCELL)((PUCHAR)hbp + sizeof(HBIN));
|
|
boff = sizeof(HBIN);
|
|
lboff = -1;
|
|
binread = HBLOCK_SIZE;
|
|
|
|
while (binread <= binsize) {
|
|
|
|
|
|
// if free, do free stuff
|
|
// else do alloc stuff
|
|
// do full stuff
|
|
|
|
if (cp->Size > 0) {
|
|
|
|
// free
|
|
|
|
cellsize = cp->Size;
|
|
StatFreeCount++;
|
|
StatFreeTotal += cellsize;
|
|
if (cellsize > StatFreeMax) {
|
|
StatFreeMax = cellsize;
|
|
}
|
|
|
|
if (DoTraceFree) {
|
|
printf("fr,x%08lx,%ld\n",
|
|
hoff+((PUCHAR)cp - &(buffer[0])), cellsize);
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
// alloc
|
|
|
|
cellsize = -1 * cp->Size;
|
|
StatAllocCount++;
|
|
StatAllocTotal += cellsize;
|
|
if (cellsize > StatAllocMax) {
|
|
StatAllocMax = cellsize;
|
|
}
|
|
|
|
if (DoTraceAlloc) {
|
|
printf("al,x%08lx,%ld\n",
|
|
hoff+((PUCHAR)cp - &(buffer[0])), cellsize);
|
|
}
|
|
|
|
ScanCell(cp,cellsize);
|
|
|
|
}
|
|
|
|
|
|
// do basic consistency check
|
|
|
|
#if 0
|
|
if (cp->Last != lboff) {
|
|
printf("e!,x%08lx bad LAST pointer %08lx\n",
|
|
hoff+((PUCHAR)cp - &(buffer[0])), cp->Last);
|
|
}
|
|
#endif
|
|
|
|
|
|
// advance to next cell
|
|
|
|
lboff = boff;
|
|
cp = (PHCELL)((PUCHAR)cp + cellsize);
|
|
boff += cellsize;
|
|
|
|
|
|
// scan ahead in bin, if cp has reached off end of block,
|
|
// AND there's bin left to read.
|
|
// do this BEFORE breaking out for boff at end.
|
|
|
|
while ( (cp >= guard) && (binread < binsize) ) {
|
|
|
|
rf = ReadFile(filehandle, buffer, HBLOCK_SIZE, &readcount, NULL);
|
|
if ( ( ! rf ) || (readcount != HBLOCK_SIZE) ) {
|
|
fprintf(stderr, "hivestat: '%s' read error @%08lx\n", FileName, hiveposition);
|
|
exit(1);
|
|
}
|
|
cp = (PHCELL)((PUCHAR)cp - HBLOCK_SIZE);
|
|
hiveposition += HBLOCK_SIZE;
|
|
hoff += HBLOCK_SIZE;
|
|
binread += HBLOCK_SIZE;
|
|
ASSERT(hoff+HBLOCK_SIZE == hiveposition);
|
|
}
|
|
|
|
if (boff >= binsize) {
|
|
break; // we are done with this bin
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Traces are done, stats gathered, print summary
|
|
|
|
if (DoSummary) {
|
|
|
|
printf("\nSummary:\n");
|
|
printf("type\tcount/max single/total space\n");
|
|
printf("%s\t%7ld\t%7ld\t%7ld\n",
|
|
"bin", StatBinCount, StatBinMax, StatBinTotal);
|
|
printf("%s\t%7ld\t%7ld\t%7ld\n",
|
|
"free", StatFreeCount, StatFreeMax, StatFreeTotal);
|
|
printf("%s\t%7ld\t%7ld\t%7ld\n",
|
|
"alloc", StatAllocCount, StatAllocMax, StatAllocTotal);
|
|
|
|
}
|
|
|
|
if (DoSummary && DoCellType) {
|
|
|
|
printf("\n");
|
|
|
|
SizeTotal = SizeKeyData +
|
|
SizeValueData +
|
|
SizeSDData +
|
|
SizeIndexData +
|
|
SizeUnknownData;
|
|
|
|
printf("Total Key Data %7d (%5.2f %%)\n", SizeKeyData,
|
|
(float)SizeKeyData*100/SizeTotal);
|
|
printf("Total Value Data %7d (%5.2f %%)\n", SizeValueData,
|
|
(float)SizeValueData*100/SizeTotal);
|
|
printf("Total SD Data %7d (%5.2f %%)\n", SizeSDData,
|
|
(float)SizeSDData*100/SizeTotal);
|
|
printf("Total Index Data %7d (%5.2f %%)\n", SizeIndexData,
|
|
(float)SizeIndexData*100/SizeTotal);
|
|
printf("Total Unknown Data %7d (%5.2f %%)\n", SizeUnknownData,
|
|
(float)SizeUnknownData*100/SizeTotal);
|
|
|
|
printf("\n");
|
|
printf("Average Key Data %8.2f (%d cells)\n",
|
|
(float)SizeKeyData/NumKeyData,
|
|
NumKeyData);
|
|
printf("Average Value Data %8.2f (%d cells)\n",
|
|
(float)SizeValueData/NumValueData,
|
|
NumValueData);
|
|
printf("Average SD Data %8.2f (%d cells)\n",
|
|
(float)SizeSDData/NumSDData,
|
|
NumSDData);
|
|
printf("Average Index Data %8.2f (%d cells)\n",
|
|
(float)SizeIndexData/NumIndexData,
|
|
NumIndexData);
|
|
printf("Average Unknown Data %8.2f (%d cells)\n",
|
|
(float)SizeUnknownData/NumUnknownData,
|
|
NumUnknownData);
|
|
}
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
ScanCell(
|
|
IN PHCELL Cell,
|
|
IN ULONG CellSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a pointer to an HCELL, this tries to figure out what type
|
|
of data is in it (key, value, SD, etc.) and gather interesting
|
|
statistics about it.
|
|
|
|
Arguments:
|
|
|
|
Cell - Supplies a pointer to the HCELL
|
|
|
|
CellSize - Supplies the size of the HCELL
|
|
|
|
Return Value:
|
|
|
|
None, sets some global statistics depending on content of the cell.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCELL_DATA Data;
|
|
|
|
if (!DoCellType) {
|
|
return;
|
|
}
|
|
|
|
if (HiveVersion==1) {
|
|
Data = (PCELL_DATA)&Cell->u.OldCell.u.UserData;
|
|
} else {
|
|
Data = (PCELL_DATA)&Cell->u.NewCell.u.UserData;
|
|
}
|
|
|
|
|
|
// grovel through the data, see if we can figure out what it looks like
|
|
|
|
if ((Data->u.KeyNode.Signature == CM_KEY_NODE_SIGNATURE) &&
|
|
(CellSize > sizeof(CM_KEY_NODE))) {
|
|
|
|
|
|
// probably a key node
|
|
|
|
ScanKeyNode(&Data->u.KeyNode, CellSize);
|
|
|
|
} else if ((Data->u.KeyValue.Signature == CM_KEY_VALUE_SIGNATURE) &&
|
|
(CellSize > sizeof(CM_KEY_VALUE))) {
|
|
|
|
|
|
// probably a key value
|
|
|
|
ScanKeyValue(&Data->u.KeyValue, CellSize);
|
|
|
|
} else if ((Data->u.KeySecurity.Signature == CM_KEY_SECURITY_SIGNATURE) &&
|
|
(CellSize > sizeof(CM_KEY_SECURITY))) {
|
|
|
|
|
|
// probably a security descriptor
|
|
|
|
ScanKeySD(&Data->u.KeySecurity, CellSize);
|
|
|
|
} else if ((Data->u.KeyIndex.Signature == CM_KEY_INDEX_ROOT) ||
|
|
(Data->u.KeyIndex.Signature == CM_KEY_INDEX_LEAF)) {
|
|
|
|
// probably a key index
|
|
|
|
ScanKeyIndex(&Data->u.KeyIndex, CellSize);
|
|
|
|
} else {
|
|
|
|
// Nothing with a signature, could be either
|
|
// name
|
|
// key list
|
|
// value data
|
|
|
|
ScanUnknown(Data, CellSize);
|
|
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ScanKeyNode(
|
|
IN PCM_KEY_NODE Node,
|
|
IN ULONG CellSize
|
|
)
|
|
{
|
|
int i;
|
|
|
|
SizeKeyData += CellSize;
|
|
NumKeyData++;
|
|
|
|
if (AccessKeys) {
|
|
printf("%d, %d, %d, %d, \"",
|
|
Node->SubKeyCounts[Stable],
|
|
Node->ValueList.Count,
|
|
Node->NameLength,
|
|
Node->ClassLength);
|
|
|
|
for (i=0; i < Node->NameLength/sizeof(WCHAR); i++) {
|
|
printf("%c",(CHAR)Node->Name[i]);
|
|
}
|
|
printf("\"\n");
|
|
}
|
|
|
|
}
|
|
VOID
|
|
ScanKeyValue(
|
|
IN PCM_KEY_VALUE Value,
|
|
IN ULONG CellSize
|
|
)
|
|
{
|
|
int i;
|
|
int DataLength;
|
|
|
|
SizeValueData += CellSize;
|
|
NumValueData++;
|
|
if (AccessValues) {
|
|
DataLength = Value->DataLength;
|
|
if (DataLength >= CM_KEY_VALUE_SPECIAL_SIZE) {
|
|
DataLength -= CM_KEY_VALUE_SPECIAL_SIZE;
|
|
}
|
|
printf("%d, %d, \"",
|
|
DataLength,
|
|
Value->NameLength);
|
|
|
|
for (i=0; i < Value->NameLength/sizeof(WCHAR); i++) {
|
|
printf("%c",(CHAR)Value->Name[i]);
|
|
}
|
|
printf("\"\n");
|
|
}
|
|
|
|
}
|
|
VOID
|
|
ScanKeySD(
|
|
IN PCM_KEY_SECURITY Security,
|
|
IN ULONG CellSize
|
|
)
|
|
{
|
|
SizeSDData += CellSize;
|
|
NumSDData++;
|
|
|
|
if (AccessSD) {
|
|
printf("%d,%d\n",
|
|
Security->ReferenceCount,
|
|
Security->DescriptorLength);
|
|
}
|
|
|
|
}
|
|
VOID
|
|
ScanKeyIndex(
|
|
IN PCM_KEY_INDEX Index,
|
|
IN ULONG CellSize
|
|
)
|
|
{
|
|
SizeIndexData += CellSize;
|
|
NumIndexData++;
|
|
|
|
}
|
|
VOID
|
|
ScanUnknown(
|
|
IN PCELL_DATA Data,
|
|
IN ULONG CellSize
|
|
)
|
|
{
|
|
SizeUnknownData += CellSize;
|
|
NumUnknownData++;
|
|
|
|
}
|