// Note: This is an example of how *not* to write code
procedure TUbixFS.visualize;
type
TViewKey = record
message:string;
colour:array[0..3] of ogRGBA8;
end;
blockTypes = (BT_Free, BT_Used, BT_UsedCurrent, BT_InodeDir, BT_InodeFile, BT_BAT, BT_SB);
blockTypeArray = array[0..0] of blockTypes;
const
X_BLOCKS = 32;
Y_BLOCKS = 32;
BLOCKS_PER_PAGE = X_BLOCKS*Y_BLOCKS;
X_SIZE = (768-(X_BLOCKS*2)) div X_BLOCKS;
Y_SIZE = (768-(Y_BLOCKS*2)) div Y_BLOCKS;
viewKey:array[blockTypes] of TViewKey =
((message:'Free Block'; colour:((red:127; green:127; blue:127; alpha:255),
(red:63; green:63; blue:63; alpha:255),
(red:31; green:31; blue:31; alpha:255),
(red:63; green:63; blue:63; alpha:255))),
(message:'Used Block'; colour:((red:63; green:223; blue:127; alpha:255),
(red:31; green:111; blue:63; alpha:255),
(red:15; green:55; blue:31; alpha:255),
(red:31; green:111; blue:63; alpha:255))),
(message:'Used Blocks (current)'; colour:((red:255; green:255; blue:255; alpha:255),
(red:127; green:127; blue:127; alpha:255),
(red:63; green:63; blue:63; alpha:255),
(red:127; green:127; blue:127; alpha:255))),
(message:'Inode (directory)'; colour:((red:255; green:255; blue:000; alpha:255),
(red:127; green:127; blue:000; alpha:255),
(red:63; green:63; blue:000; alpha:255),
(red:127; green:127; blue:000; alpha:255))),
(message:'Inode (file)'; colour:((red:000; green:255; blue:255; alpha:255),
(red:000; green:127; blue:127; alpha:255),
(red:000; green:063; blue:063; alpha:255),
(red:000; green:127; blue:127; alpha:255))),
(message:'Inode (BAT)'; colour:((red:063; green:127; blue:255; alpha:255),
(red:031; green:63; blue:127; alpha:255),
(red:015; green:31; blue:63; alpha:255),
(red:031; green:63; blue:127; alpha:255))),
(message:'SuperBlock'; colour:((red:240; green:000; blue:063; alpha:255),
(red:120; green:000; blue:031; alpha:255),
(red:55; green:000; blue:015; alpha:255),
(red:120; green:000; blue:031; alpha:255)))
); // viewKey;
var
font:^ogBitFont;
dataMap:^blockTypeArray;
xBlocks, yBlocks:uInt32;
curIndex:uInt32;
displayY:uInt32;
curX, curY:uInt32;
curPage:uInt32;
xx, yy:uInt32;
u:uPtr;
inode:PUbixFSInode;
b:byte;
ky:char;
map:^ogSurface;
display:^ogSurface;
colours:array[0..3] of ogRGBA8;
points:array[0..3] of ogPoint2D;
label
safeExit;
procedure displayKey;
var index:blockTypes;
begin
if (screen = NIL) or (font = NIL) then exit;
for index := high(viewKey) downto low(viewKey) do
with font^, display^, viewKey[index] do
begin
with points[3] do begin x:=0; y:=ogGetMaxY() - ord(index)*Y_SIZE; end;
with points[2] do begin x:=X_SIZE; y:=ogGetMaxY() - (ord(index))*Y_SIZE; end;
with points[1] do begin x:=X_SIZE; y:=ogGetMaxY() - (ord(index)+1)*Y_SIZE; end;
with points[0] do begin x:=0; y:=ogGetMaxY() - (ord(index)+1)*Y_SIZE; end;
ogFillGouraudPolygon(4, points, colour);
// ogFillRect(0, ogGetMaxY() - ord(index)*Y_SIZE,
// X_SIZE, ogGetMaxY() - (ord(index)+1)*Y_SIZE,
// ogRGB(red, green, blue));
putString(display^, (X_SIZE*3 div 2), ogGetMaxY() - ord(index)*Y_SIZE - ((getHeight()+Y_SIZE) div 2), message);
end;
end; // local function displayKey
procedure displayMini;
var
yy, xx, index:uInt32;
begin
with screen^ do
begin
index := 0;
ogVLine(X_SIZE*(X_BLOCKS+1)-(X_SIZE div 2), 0, ogGetMaxY(), ogRGB(0, 0, 0));
ogVLine(X_SIZE*(X_BLOCKS+1)+X_BLOCKS+(X_SIZE div 2), 0, ogGetMaxY(), ogRGB(0, 0, 0));
for yy := 0 to ogGetMaxY() do
for xx := 0 to X_BLOCKS-1 do
if (index < uInt32(superBlock^.numBlocks)) then
with viewKey[dataMap^[index]], colour[0] do
begin
ogSetPixel(X_SIZE*(X_BLOCKS+1)+xx, yy, ogRGB(red, green, blue));
inc(index);
end;
ogVLine(X_SIZE*(X_BLOCKS+1)-(X_SIZE div 2), curPage*Y_BLOCKS, (curPage+1)*Y_BLOCKS-1, ogRGB(255, 255, 255));
ogVLine(X_SIZE*(X_BLOCKS+1)+X_BLOCKS+(X_SIZE div 2), curPage*Y_BLOCKS, (curPage+1)*Y_BLOCKS-1, ogRGB(255, 255, 255));
end; // with
end; // local function displayMini
procedure displayBlock(xx, yy, index:integer; displayCursor:boolean);
var oldBlending:boolean;
begin
if (screen = NIL) then exit;
with map^ do
if (index < uInt32(superBlock^.numBlocks)) then
with viewKey[dataMap^[index]] do
begin
with points[0] do begin x := xx*X_SIZE; y := yy*Y_SIZE; end;
with points[1] do begin x := (xx+1)*X_SIZE-1; y := yy*Y_SIZE; end;
with points[2] do begin x := (xx+1)*X_SIZE-1; y := (yy+1)*Y_SIZE-1; end;
with points[3] do begin x := xx*X_SIZE; y := (yy+1)*Y_SIZE-1; end;
ogFillGouraudPolygon(4, points, colour);
if (displayCursor) then
begin
oldBlending := ogSetBlending(TRUE);
with colours[2] do begin red:=255; green:=255; blue:=255; alpha:=255; end;
with colours[1] do begin red:=127; green:=127; blue:=127; alpha:=127; end;
with colours[0] do begin red:=63; green:=63; blue:=63; alpha:=63; end;
colours[3] := colours[1];
(* with points[0] do begin x := xx*X_SIZE; y := yy*Y_SIZE; end;
with points[1] do begin x := (xx+1)*X_SIZE; y := yy*Y_SIZE; end;
with points[2] do begin x := (xx+1)*X_SIZE; y := (yy+1)*Y_SIZE; end;
with points[3] do begin x := xx*X_SIZE; y := (yy+1)*Y_SIZE; end; *)
ogFillGouraudBSpline(4, points, 16,colours);
ogSetBlending(oldBlending);
end;
end
else
ogFillRect(xx*X_SIZE, yy*Y_SIZE, (xx+1)*X_SIZE-1, (yy+1)*Y_SIZE-1, ogRGB(0, 0, 0));
end;
procedure displayMap;
var
xx, yy, zz:uInt32;
index:uInt32;
oldBlending:boolean;
begin
if (screen = NIL) then exit;
index := curPage * Y_BLOCKS * X_BLOCKS;
for yy := 0 to Y_BLOCKS-1 do
for xx := 0 to X_BLOCKS-1 do
begin
displayBlock(xx, yy, index, FALSE);
(*
with map^ do
if (index < uInt32(superBlock^.numBlocks)) then
with viewKey[dataMap^[index]], colour[0] do
begin
with points[0] do begin x := xx*X_SIZE; y := yy*Y_SIZE; end;
with points[1] do begin x := (xx+1)*X_SIZE; y := yy*Y_SIZE; end;
with points[2] do begin x := (xx+1)*X_SIZE; y := (yy+1)*Y_SIZE; end;
with points[3] do begin x := xx*X_SIZE; y := (yy+1)*Y_SIZE; end;
ogFillGouraudPolygon(4, points, colour);
// ogFillRect(xx*X_SIZE, yy*Y_SIZE, (xx+1)*X_SIZE-1, (yy+1)*Y_SIZE-1, ogRGB(red, green, blue));
if (index = curIndex) then
begin
oldBlending := ogSetBlending(TRUE);
// with colours[2] do begin red:=255; green:=255; blue:=255; alpha:=223; end;
// with colours[1] do begin red:=127; green:=127; blue:=127; alpha:=160; end;
// with colours[0] do begin red:=63; green:=63; blue:=63; alpha:=127; end;
with colours[2] do begin red:=255; green:=255; blue:=255; alpha:=255; end;
with colours[1] do begin red:=127; green:=127; blue:=127; alpha:=127; end;
with colours[0] do begin red:=63; green:=63; blue:=63; alpha:=63; end;
colours[3] := colours[1];
with points[0] do begin x := xx*X_SIZE; y := yy*Y_SIZE; end;
with points[1] do begin x := (xx+1)*X_SIZE; y := yy*Y_SIZE; end;
with points[2] do begin x := (xx+1)*X_SIZE; y := (yy+1)*Y_SIZE; end;
with points[3] do begin x := xx*X_SIZE; y := (yy+1)*Y_SIZE; end;
ogFillGouraudBSpline(4, points, 16,colours);
ogSetBlending(oldBlending);
end;
end
else
ogFillRect(xx*X_SIZE, yy*Y_SIZE, (xx+1)*X_SIZE-1, (yy+1)*Y_SIZE-1, ogRGB(0, 0, 0)); *)
inc(index);
end;
displayMini();
end; // local function displayMap
function iAddrToLogicalBlock(iAddr:inodeAddr):uInt32; with iAddr, superBlock^ do result := (AG shl AGShift) + start;
function logicalBlockToIAddr(logicalBlock:uInt32):inodeAddr;
begin
setIAddr(result, logicalBlock div superBlock^.blocksPerAG, logicalBlock mod superBlock^.blocksPerAG, 1);
end;
function incDisplayY:uInt32;
begin
result := displayY;
inc(displayY, font^.getHeight()+2);
end;
procedure displayRange;
begin
displayY := 0;
display^.ogFillRect(0, 0, display^.ogGetMaxX(), font^.getHeight()*4+4, display^.ogRGB(0, 0, 0));
font^.centerTextX(display^, incDisplayY, 'Range');
font^.centerTextX(display^, incDisplayY, '[0x' + hex(curPage*X_BLOCKS*Y_BLOCKS) + ' - 0x' + hex((curPage+1)*X_BLOCKS*Y_BLOCKS-1) + ']');
font^.centerTextX(display^, incDisplayY, 'Selected Block');
font^.centerTextX(display^, incDisplayY, '[0x'+hex(curIndex)+']');
if (blockCache <> NIL) then font^.putString(screen^, 0, screen^.ogGetMaxY()-Y_SIZE*3 div 2, 'Block cache has ' + intToStr(blockCache^.getKeyCount()) + ' entries');
end;
procedure displayInodeInfo;
var
inode:PUbixFSInode;
smallDataNode:PSmallDataNode;
begin
inode := loadBlock(logicalBlockToIAddr(curIndex));
if (inode = NIL) then exit;
with font^, inode^ do
begin
displayY := getHeight()*6;
with font^, viewKey[dataMap^[curIndex]], colour[0] do
setFGColor(red, green, blue, alpha);
smallDataNode := findAttr('name');
if smallDataNode <> NIL then
centerTextX(display^, incDisplayY, strPas(smallDataNode^.dataPtr))
else
centerTextX(display^, incDisplayY, 'Name attr not found!');
font^.setFGColor(255, 255, 255, 255);
putString(display^, 0, incDisplayY, 'magic........ ' + hex(magic));
with inodeNum do
putString(display^,0, incDisplayY, 'inodeNum..... ' + intToStr(AG) + ':' + intToStr(start) + ':' + intToStr(len));
putString(display^, 0, incDisplayY, 'uid.......... ' + intToStr(uid));
putString(display^, 0, incDisplayY, 'gid.......... ' + intToStr(gid));
putString(display^, 0, incDisplayY, 'mode......... ' + hex(mode));
putString(display^, 0, incDisplayY, 'flags........ ' + hex(flags));
putString(display^, 0, incDisplayY, 'createTime... ' + hex(createTime));
putString(display^, 0, incDisplayY, 'modifiedTime. ' + hex(modifiedTime));
with parent do
putString(display^,0, incDisplayY, 'parent....... ' + intToStr(AG) + ':' + intToStr(start) + ':' + intToStr(len));
with attributes do
putString(display^,0, incDisplayY, 'attributes... ' + intToStr(AG) + ':' + intToStr(start) + ':' + intToStr(len));
putString(display^, 0, incDisplayY, 'iType........ ' + hex(iType));
putString(display^, 0, incDisplayY, 'inodeSize.... ' + intToStr(inodeSize));
with blocks do
begin
putString(display^, 0, incDisplayY, 'size......... ' + intToStr(uInt32(size)));
putString(display^, 0, incDisplayY, 'blockCount... ' + intToStr(blockCount));
with indirect do
putString(display^, 0, incDisplayY, 'indirect..... ' + intToStr(AG) + ':' + intToStr(start) + ':' + intToStr(len));
with doubleIndirect do
putString(display^, 0, incDisplayY, 'dIndirect.... ' + intToStr(AG) + ':' + intToStr(start) + ':' + intToStr(len));
end;
end; // with
end;
procedure displayDirectoryInodeInfo;
var dirTree:PBTree;
begin
displayInodeInfo();
new(dirTree, open(IAddrToStr(logicalBlockToIAddr(curIndex)), new(PUbixBTreeVFS, init(@self))));
font^.putString(display^, 0, incDisplayY, 'tree entries. ' + intToStr(dirTree^.getKeyCount()));
dispose(dirTree, done);
end;
procedure displaySuperBlockInfo;
begin
with font^, superBlock^ do
begin
displayY := getHeight()*6;
with viewKey[BT_SB], colour[0] do
font^.setFGColor(red, green, blue, alpha);
centerTextX(display^, incDisplayY, 'SuperBlock');
font^.setFGColor(255, 255, 255, 255);
putString(display^, 0, incDisplayY, 'name......... ' + name);
putString(display^, 0, incDisplayY, 'magic1....... ' + hex(magic1));
putString(display^, 0, incDisplayY, 'fsByteOrder.. ' + intToStr(fsByteOrder));
putString(display^, 0, incDisplayY, 'blockSize.... ' + intToStr(blockSize));
putString(display^, 0, incDisplayY, 'blockShift... ' + intToStr(blockShift));
putString(display^, 0, incDisplayY, 'numBlocks.... ' + intToStr(uInt32(numBlocks)));
putString(display^, 0, incDisplayY, 'usedBlocks... ' + intToStr(uInt32(usedBlocks)));
putString(display^, 0, incDisplayY, 'magic2....... ' + hex(magic2));
putString(display^, 0, incDisplayY, 'blocksPerAG.. ' + intToStr(blocksPerAG));
putString(display^, 0, incDisplayY, 'AGShift...... ' + intToStr(AGShift));
putString(display^, 0, incDisplayY, 'numAGs....... ' + intToStr(numAGs));
putString(display^, 0, incDisplayY, 'flags........ ' + hex(flags));
putString(display^, 0, incDisplayY, 'magic3....... ' + hex(magic3));
with superBlock^.BAT do
putString(display^,0, incDisplayY, 'BAT.......... ' + intToStr(AG) + ':' + intToStr(start) + ':' + intToStr(len));
with superBlock^.rootDir do
putString(display^,0, incDisplayY, 'rootDir...... ' + intToStr(AG) + ':' + intToStr(start) + ':' + intToStr(len));
end; // with
end; // displaySuperBlockInfo
procedure markBlocks(iAddr:inodeAddr; value:blockTypes);
var
inode:PUbixfsInode;
index:uInt32;
blockRun:TBlockRun;
begin
inode := loadBlock(iAddr);
if (inode = NIL) then exit;
if (inode^.blocks.blockCount = 0) then exit;
blockRun := inode^.blocks.indirect;
if isValidIAddr(blockRun) then
for index := 0 to blockRun.len-1 do
begin
dataMap^[iAddrToLogicalBlock(blockRun)] := value;
inc(blockRun.start);
end;
blockRun := inode^.blocks.doubleIndirect;
if isValidIAddr(blockRun) then
for index := 0 to blockRun.len-1 do
begin
dataMap^[iAddrToLogicalBlock(blockRun)] := value;
inc(blockRun.start);
end;
for index := 0 to inode^.blocks.blockCount-1 do
dataMap^[iAddrToLogicalBlock(findBRun(@inode^.blocks, index))] := value;
displayMap();
end; // local function markBlocks
procedure clearSelection;
begin
displayBlock((curIndex-curPage*BLOCKS_PER_PAGE) mod X_BLOCKS, (curIndex-curPage*BLOCKS_PER_PAGE) div X_BLOCKS, curIndex, FALSE);
if (dataMap^[curIndex] in [BT_InodeDir, BT_InodeFile, BT_BAT]) then
markBlocks(logicalBlockToIAddr(curIndex), BT_Used);
with display^, font^ do
ogFillRect(0, getHeight()*5, ogGetMaxX(), ogGetMaxY() - Y_SIZE*(ord(high(viewKey))+2),display^.ogRGB(0, 0, 0));
end;
procedure setSelection;
begin
if (dataMap^[curIndex] in [BT_InodeDir, BT_InodeFile, BT_BAT]) then
markBlocks(logicalBlockToIAddr(curIndex), BT_UsedCurrent);
case dataMap^[curIndex] of
BT_SB:displaySuperBlockInfo();
BT_InodeFile:displayInodeInfo();
BT_InodeDir, BT_BAT:displayDirectoryInodeInfo();
end; // case
displayBlock((curIndex-curPage*BLOCKS_PER_PAGE) mod X_BLOCKS, (curIndex-curPage*BLOCKS_PER_PAGE) div X_BLOCKS, curIndex, TRUE);
displayRange();
end;
procedure moveLeft;
var
oldPage:uInt32;
begin
if (curIndex > 0) then
begin
clearSelection();
dec(curIndex);
oldPage := curPage;
curPage := curIndex div (X_BLOCKS*Y_BLOCKS);
if (oldPage <> curPage) then
begin
displayRange();
displayMap();
end;
setSelection();
end;
end;
procedure moveRight;
var oldPage:uInt32;
begin
if (curIndex < uInt32(superBlock^.numBlocks-1)) then
begin
clearSelection();
inc(curIndex);
oldPage := curPage;
curPage := curIndex div (X_BLOCKS*Y_BLOCKS);
if (oldPage <> curPage) then
begin
displayRange();
displayMap();
end;
setSelection();
end;
end;
procedure moveUp;
var oldPage:uInt32;
begin
if (curIndex >= X_BLOCKS) then
begin
clearSelection();
dec(curIndex, X_BLOCKS);
oldPage := curPage;
curPage := curIndex div (X_BLOCKS*Y_BLOCKS);
if (oldPage <> curPage) then
begin
displayRange();
displayMap();
end;
setSelection();
end;
end;
procedure moveDown;
var oldPage:uInt32;
begin
if (curIndex < uInt32(superBlock^.numBlocks-X_BLOCKS)) then
begin
clearSelection();
inc(curIndex, X_BLOCKS);
oldPage := curPage;
curPage := curIndex div (X_BLOCKS*Y_BLOCKS);
if (oldPage <> curPage) then
begin
displayRange();
displayMap();
end;
setSelection();
end;
end;
procedure pageDown;
begin
if (curPage < (uInt32((superBlock^.numBlocks)+(X_BLOCKS*Y_BLOCKS-1)) div (X_BLOCKS*Y_BLOCKS)-1)) then
begin
inc(curPage);
displayRange();
displayMap();
end;
end;
procedure pageUp;
begin
if (curPage > 0) then
begin
dec(curPage);
displayRange();
displayMap();
end;
end;
procedure markDirectory(dirAddr:inodeAddr);
var dirTree:PbTree;
inode:PUbixFSInode;
searchRec:BTreeSearchRec;
begin
dataMap^[iAddrToLogicalBlock(dirAddr)] := BT_InodeDir;
new(dirTree, open(IAddrToStr(dirAddr), new(PUbixBTreeVFS, init(@self))));
dirTree^.getFirstKey(searchRec);
repeat
inode := loadBlock(searchRec.value.iAddr);
if (inode = NIL) then exit;
if (int8ArrayPtr(searchRec.key)^[0] <> ord('.')) then
begin
if (inode^.mode and S_IFDIR <> 0) then
markDirectory(searchRec.value.iAddr)
else
dataMap^[iAddrToLogicalBlock(searchRec.value.iAddr)] := BT_InodeFile;
end;
until not dirTree^.findNext(searchRec);
dispose(dirTree, done);
end;
procedure markAll;
//var f:text;
begin
fillchar(dataMap^, uInt32(superBlock^.numBlocks), BT_Free);
(* inode := loadBlock(superBlock^.BAT);
for yy := 0 to uInt32(inode^.blocks.size)-1 do
if (yy >= uInt32(superBlock^.numBlocks div 8)) then
break
else
begin
readDataStream(superBlock^.BAT, yy, @b, 1);
for xx := 0 to 7 do
if (b and (1 shl (7-xx)) <> 0) then dataMap^[yy*8+xx] := BT_USED;
end; *)
dataMap^[0] := BT_SB; // mark the superblock
dataMap^[iAddrToLogicalBlock(superBlock^.BAT)] := BT_BAT; // mark the BAT
markDirectory(superBlock^.rootDir);
(* system.assign(f, 'bc2.txt');
system.rewrite(f);
for xx := 0 to uInt32(superBlock^.numblocks)-1 do
if dataMap^[xx] <> BT_Free then writeln(f, hex(xx));
system.close(f); *)
end;
var
savebuf:^ogSurface;
imageSaver:^ogImage;
dirTree:PbTree;
smallDataNode:PSmallDataNode;
begin
if (dev = NIL) or (superBlock = NIL) then exit;
map := NIL;
display := NIL;
dataMap := NIL;
font := NIL;
if (screen = NIL) then new(screen, ogInit);
new(font, init);
if (font = NIL) then exit;
if not (font^.load('ROM8X16.DPF')) then
begin
writeln('Error loading font');
goto safeExit;
end;
if not (screen^.ogCreate(1280, 1024, OG_PIXFMT_32BPP)) then
begin
writeln('Error setting graphics mode');
goto safeExit;
end;
screen^.ogClear(screen^.ogRGB(0, 0, 0));
new(map, ogInit);
new(display, ogInit);
if not (display^.ogAlias(screen^, 780, 0, screen^.ogGetMaxX(), screen^.ogGetMaxY())) then goto safeExit;
if not (map^.ogAlias(screen^, 0, 0, X_SIZE*X_BLOCKS-1, Y_SIZE*Y_BLOCKS-1)) then goto safeExit;
GetMem(dataMap, uInt32(superBlock^.numBlocks)); // Hope that there aren't more than 4 billion blocks
if (dataMap = NIL) then
begin
writeln('Error allocating memory for dataMap');
goto safeExit;
end;
markAll();
font^.setFGColor(255, 255, 255, 255);
font^.setBGColor(0, 0, 0, 0);
curPage := 0;
curIndex := 0;
curX := 0;
curY := 0;
displayKey();
displayMap();
displayRange();
setSelection();
repeat
ky := upcase(readkey);
case ky of
#0:case readkey of
#72:moveUp();
#73:pageUp();
#75:moveLeft();
#77:moveRight();
#80:moveDown();
#81:pageDown();
#83:if dataMap^[curIndex] in [BT_InodeFile] then
begin
clearSelection();
inode := loadBlock(logicalBlockToIAddr(curIndex));
if (inode = NIL) then continue;
inode^.blocks.size := 0;
shrinkFile(inode);
u.iAddr := inode^.inodeNum;
new(dirTree, open(IAddrToStr(inode^.parent), new(PUbixBTreeVFS, init(@self))));
smallDataNode := inode^.findAttr('name');
assert(smallDataNode <> NIL);
dirTree^.Delete(smallDataNode^.dataPtr(), u);
dispose(dirTree, done);
freeUsedBlocks(inode^.inodeNum);
markAll();
displayMap();
setSelection();
end;
end; // case
'0'..'9':begin
new(imageSaver,init);
new(saveBuf,ogInit);
if (saveBuf^.ogCreate(screen^.ogGetMaxX+1,screen^.ogGetMaxY+1,OG_PIXFMT_24BPP)) then
begin
saveBuf^.ogCopy(screen^);
imageSaver^.saveGfx(saveBuf^,ky+'.bmp',BMP);
end;
dispose(saveBuf,ogDone);
dispose(imageSaver,done);
end;
end; // case
until ky = #27;
safeExit:
if (dataMap <> NIL) then FreeMem(dataMap, uInt32(superBlock^.numBlocks));
if (font <> NIL) then dispose(font, done);
if (map <> NIL) then dispose(map, ogDone);
if (display <> NIL) then dispose(display, ogDone);
if (screen <> NIL) then dispose(screen, ogDone);
screen := NIL;
end; // TUbixFS.visualize