Newer
Older
ubixfs-2 / graphics.pas
// 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