/*****************************************************************/
/**                  Microsoft LAN Manager                      **/
/**            Copyright(c) Microsoft Corp., 1988-1990          **/
/*****************************************************************/

/****   hexedit.c  - Generic sector based hex editor function call
 *
 *  Fill out a HexEditParm structure, and call HexEdit.  It provides
 *  a simple hex editor with a few misc features.
 *
 *  Is single threaded & non-reentrant, but can be called from any thread.
 *
 *  External uses:
 *      he          - allows editing of a file
 *
 *  Written: Ken Reneris    2/25/91
 *
 */


#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <stdio.h>
#include <windows.h>
#include "hexedit.h"
#include <stdarg.h>

#define LOCAL   static
// #define LOCAL

#define BUFSZ       512
#define SECTORMASK  0xFFFFFE00L
#define SECTORSHIFT 9

static UCHAR rghexc[] = "0123456789ABCDEF";

struct Buffer {
    struct  Buffer    *next;
    ULONG   offset;
    ULONG   physloc;
    USHORT  flag;
    USHORT  len;
    UCHAR   *data;
    UCHAR   orig [BUFSZ];
} ;
#define FB_DIRTY    0x0001              // Buffer may be dirty
#define FB_BAD      0x0002              // Buffer had an error when read in

#define LINESZ      16                  // 16 bytes per display line
#define LINESHIFT   4L
#define LINEMASK    0xFFFFFFF0L
#define CELLPERLINE 80

struct Global {
    struct      HexEditParm  *Parm;
    HANDLE      Console;                // Internal console handle
    HANDLE      StdIn;
    NTSTATUS    (*Read)();              // Copy of HeGbl.Parm->read
    ULONG       Flag;                   // Copy of HeGbl.Parm->flag
    ULONG       TotLen;                 // Sizeof item being editted
    ULONG       TotLen1;                // Sizeof item being editted - 1
    USHORT      Lines;                  // # of lines in edit screen
    USHORT      LineTot;                // # of lines totaly in use
    USHORT      PageSz;                 // sizeof page in bytes
    USHORT      TopLine;                // TopLine edit starts at
    ULONG       CurOffset;              // Relative offset of first line
    ULONG       CurEditLoc;             // Location with cursor
    UCHAR       *CurPT;                 // Pointer to data where cursor is
    UCHAR       CurAscIndent;
    UCHAR       DWidth;                 // width of dispalymode
    UCHAR       na;
    struct      Buffer *CurBuf;         // Buffer which cursor is in
    ULONG       CurFlag;                // Cursor info
    ULONG       DisplayMode;            // Mask of displaymode
    ULONG       ItemWrap;               // Mask of displaymode wrap
    UCHAR       rgCols[LINESZ];         // Location within lines
    ULONG       UpdatePos;              // Location waitin to be updated
    struct  Buffer  *Buf;               // List of buffer's read in
    PCHAR_INFO  pVioBuf;                // Virtual screen
    COORD       dwVioBufSize;           // Dimensions of HeGbl.pVioBuf
    COORD       CursorPos;              // Position of cursor
    WORD        AttrNorm;               // Attribute of plain text
    WORD        AttrHigh;               // Attribute of highlighted text
    WORD        AttrReverse;            // Attribute of reverse text
    WORD        na3;
    COORD       dwSize;                 // Original screen size
    ULONG       OrigMode;               // Original screen mode
    CONSOLE_CURSOR_INFO CursorInfo;     // Original cursor info
    PUCHAR      SearchPattern;
} HeGbl;

#define D_BYTE  0                       // DisplayMode
#define D_WORD  1
#define D_DWORD 3

#define FC_NIBBLE       0x0001          // Cursor on lower or upper nibble?
#define FC_TEXT         0x0002          // Cursor on Hex or Text
#define FC_INFLUSHBUF   0x1000          // So we don't recurse
#define FC_CURCENTER    0x2000          // if jumping to cursor, put in center

#define PUTCHAR(a,b,c)  { a->Char.AsciiChar=b; a->Attributes=c; a++; }


//
// Internal prototypes
//

int heUpdateStats(), hePositionCursor(), heRefresh(), heSetDisp();
int heInitConsole(), heUpdateAllLines(), heInitScr(), heSetCursorBuf(), heUpdateFncs();
VOID _CRTAPI1 heDisp (USHORT, USHORT, PUCHAR, ...);
USHORT heIOErr (UCHAR *str, ULONG loc, ULONG ploc, ULONG errcd);

int heFlushBuf (struct Buffer *pBuf);

VOID heEndConsole(), heGotoPosition(), heJumpToLink();
VOID heUpdateCurLine(), heUndo(), heCopyOut(), heCopyIn(), heSearch();
VOID heBox (USHORT x, USHORT y, USHORT len_x, USHORT len_y);
UCHAR heGetChar (PUCHAR keys);
VOID heFlushAllBufs (USHORT update);
VOID heFindMousePos (COORD);
VOID heShowBuf (ULONG, ULONG);
VOID heSetDisplayMode (ULONG mode);

#define RefreshDisp()    heShowBuf(0, HeGbl.LineTot)
#define SetCurPos(a,b)  { \
    HeGbl.CursorPos.X = b;   \
    HeGbl.CursorPos.Y = a + HeGbl.TopLine;  \
    SetConsoleCursorPosition (HeGbl.Console, HeGbl.CursorPos);    \
    }


int  (*vrgUpdateFnc[])() = {
        NULL,                           // 0 - No update
        heUpdateStats,                  // 1 - Update stats
        hePositionCursor,               // 2 - Cursor has new position
        heUpdateAllLines,               // 3 - Update all lines
        heUpdateFncs,                   // 4 -
        hePositionCursor,               // 5 - Calc cursor before AllLines
        heRefresh,                      // 6 - Clear lines
        heSetDisp,                      // 7 - Draws init screen
    // the following functions are only called once during init
        heInitScr,                      // 8 - get's video mode, etc.
        heInitConsole                   // 9 - setup console handle
} ;

#define U_NONE      0
#define U_NEWPOS    2
#define U_SCREEN    5
#define U_REDRAW    9


#define TOPLINE     4
#define LINEINDENT  1
#define HEXINDENT   (10 + LINEINDENT)
#define ASCINDENT_BYTE   (3*16 + HEXINDENT + 1)
#define ASCINDENT_WORD   (5*8  + HEXINDENT + 1)
#define ASCINDENT_DWORD  (9*4  + HEXINDENT + 1)

#define POS(l,c)    (HeGbl.pVioBuf+CELLPERLINE*(l)+c)

USHORT  vrgAscIndent[] = {
        ASCINDENT_BYTE, ASCINDENT_WORD, 0, ASCINDENT_DWORD
 };

UCHAR   vrgDWidth[] = { 2, 4, 0, 8 };

LOCAL struct  Buffer  *vBufFree;              // List of free buffers
LOCAL USHORT  vUpdate;
LOCAL USHORT  vRecurseLevel = 0;
LOCAL BOOL    vInSearch = FALSE;


/*
 *  Prototypes
 */

struct Buffer *heGetBuf (ULONG);
void   heSetUpdate (USHORT);
void   heHexLine   (struct Buffer *, USHORT, USHORT);
void   heHexDWord  (PCHAR_INFO, ULONG, WORD);
USHORT heLtoa      (PCHAR_INFO, ULONG);
ULONG  heHtou      (UCHAR *);
VOID   heCalcCursorPosition ();
VOID   heGetString (PUCHAR s, USHORT len);
VOID   heRightOne  ();
VOID   heLeftOne   ();
NTSTATUS heWriteFile (HANDLE h, PUCHAR buffer, ULONG len);
NTSTATUS heReadFile (HANDLE h, PUCHAR buffer, ULONG len, PULONG br);
NTSTATUS heOpenFile (PUCHAR Name, PHANDLE handle, ULONG access);



/***
 *
 *  HexEdit - Full screen HexEdit of data
 *
 *      ename   - pointer to name of what's being edited
 *      totlen  - length of item being edited
 *      pRead   - function which can read data from item
 *      pWrite  - function which can write data to item
 *      handle  - handle to pass to pRead & pWrite
 *      flag    -
 *
 *
 *  All IO is assumed to be done on in 512 bytes on 512 byte boundrys
 *
 *      pRead (handle, offset, data, &physloc)
 *      pWrite (handle, offset, data, &physloc)
 *
 */

void HexEdit (pParm)
struct HexEditParm *pParm;
{
    USHORT  rc;
    INPUT_RECORD    Kd;
    USHORT  SkipCnt;
    DWORD   cEvents;
    USHORT  RepeatCnt;
    BOOL    bSuccess;
    struct  Global  *PriorGlobal;

    // code is not multi-threaded capable, but it can resurse.
    vRecurseLevel++;
    if (vRecurseLevel > 1) {
        PriorGlobal = (struct Global *) GlobalAlloc (0, sizeof (HeGbl));
        memcpy ((PUCHAR) PriorGlobal, (PUCHAR) &HeGbl, sizeof (HeGbl));
    }

    memset (&HeGbl, 0, sizeof (HeGbl));

    HeGbl.Parm    = pParm;
    HeGbl.Flag    = pParm->flag;
    HeGbl.TotLen  = pParm->totlen;
    HeGbl.Read    = pParm->read;
    HeGbl.TotLen1 = HeGbl.TotLen ? HeGbl.TotLen - 1L : 0L;
    pParm->flag = 0;

    HeGbl.CurEditLoc = pParm->start;                    // Cursor starts here
    HeGbl.CurOffset    = HeGbl.CurEditLoc & LINEMASK;   // Start at valid offset
    HeGbl.CurFlag      = FC_NIBBLE;
    HeGbl.Console      = INVALID_HANDLE_VALUE;
    heSetDisplayMode ((HeGbl.Flag & FHE_DWORD) ? D_DWORD : D_BYTE);

    HeGbl.AttrNorm = pParm->AttrNorm ? pParm->AttrNorm :  3;
    HeGbl.AttrHigh = pParm->AttrHigh ? pParm->AttrHigh : 15;
    HeGbl.AttrReverse = pParm->AttrReverse ? pParm->AttrReverse : 112;

    HeGbl.SearchPattern = GlobalAlloc (0, 512);
    memset (HeGbl.SearchPattern, 0, 512);

    RepeatCnt = 0;
    vUpdate   = U_REDRAW;
    heSetUpdate (U_NONE);         // get screen to redraw

    for (; ;) {
        if (RepeatCnt <= 1) {
            if (vUpdate != U_NONE) {            // Something to update?

                if (SkipCnt++ > 10) {
                    SkipCnt = 0;
                    heSetUpdate (U_NONE);
                    continue;
                }

                cEvents = 0;
                bSuccess = PeekConsoleInput( HeGbl.StdIn,
                                  &Kd,
                                  1,
                                  &cEvents );

                if (!bSuccess || cEvents == 0) {
                    heSetUpdate ((USHORT)(vUpdate-1));
                    continue;
                }
            } else {
                SkipCnt = 0;
            }

            ReadConsoleInput (HeGbl.StdIn, &Kd, 1, &cEvents);

            if (Kd.EventType != KEY_EVENT) {

                if (Kd.EventType == MOUSE_EVENT  &&
                    (Kd.Event.MouseEvent.dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED)) {
                        heFindMousePos(Kd.Event.MouseEvent.dwMousePosition);
                    }

                continue;                           // Not a key
            }


            if (!Kd.Event.KeyEvent.bKeyDown)
                continue;                           // Not a down stroke

            if (Kd.Event.KeyEvent.wVirtualKeyCode == 0    ||    // ALT
                Kd.Event.KeyEvent.wVirtualKeyCode == 0x10 ||    // SHIFT
                Kd.Event.KeyEvent.wVirtualKeyCode == 0x11 ||    // CONTROL
                Kd.Event.KeyEvent.wVirtualKeyCode == 0x14)      // CAPITAL
                    continue;

            RepeatCnt = Kd.Event.KeyEvent.wRepeatCount;
            if (RepeatCnt > 20)
                RepeatCnt = 20;
        } else
            RepeatCnt--;

        switch (Kd.Event.KeyEvent.wVirtualKeyCode) {
            case 0x21:                                    /* PgUp */
                if (HeGbl.CurOffset < HeGbl.PageSz)
                     HeGbl.CurOffset  = 0L;
                else HeGbl.CurOffset -= HeGbl.PageSz;

                if (HeGbl.CurEditLoc < HeGbl.PageSz)
                     HeGbl.CurEditLoc  = 0L;
                else HeGbl.CurEditLoc -= HeGbl.PageSz;

                heSetUpdate (U_SCREEN);
                continue;

            case 0x26:                                    /* Up   */
                if (HeGbl.CurEditLoc >= LINESZ) {
                    HeGbl.CurEditLoc -= LINESZ;
                    heSetUpdate (U_NEWPOS);
                }
                continue;

            case 0x22:                                    /* PgDn */
                if (HeGbl.TotLen > HeGbl.PageSz) {
                    if (HeGbl.CurOffset+HeGbl.PageSz+HeGbl.PageSz > HeGbl.TotLen1)
                         HeGbl.CurOffset = ((HeGbl.TotLen1-HeGbl.PageSz) & LINEMASK)+LINESZ;
                    else HeGbl.CurOffset += HeGbl.PageSz;

                    if (HeGbl.CurEditLoc+HeGbl.PageSz > HeGbl.TotLen1) {
                        HeGbl.CurEditLoc = HeGbl.TotLen1;
                        HeGbl.CurFlag &= ~FC_NIBBLE;
                    } else
                        HeGbl.CurEditLoc += HeGbl.PageSz;

                    heSetUpdate (U_SCREEN);
                }
                continue;


            case 0x28:                                  /* Down */
                if (HeGbl.CurEditLoc+LINESZ <= HeGbl.TotLen1) {
                    HeGbl.CurEditLoc += LINESZ;
                    heSetUpdate (U_NEWPOS);
                }
                continue;

            case 0x08:                                  /* backspace */
            case 0x25:                                  /* Left */
                if (HeGbl.CurFlag & FC_TEXT) {
                    if (HeGbl.CurEditLoc == 0L)
                        continue;

                    HeGbl.CurEditLoc--;
                    heSetUpdate (U_NEWPOS);
                    continue;
                }

                if (!(HeGbl.CurFlag & FC_NIBBLE)) {
                    HeGbl.CurFlag |= FC_NIBBLE;
                    heSetUpdate (U_NEWPOS);
                    continue;
                }

                HeGbl.CurFlag &= ~FC_NIBBLE;
                heLeftOne ();
                heSetUpdate (U_NEWPOS);
                continue;


            case 0x27:                                    /* Right */
                if (HeGbl.CurFlag & FC_TEXT) {
                    if (HeGbl.CurEditLoc >= HeGbl.TotLen1)
                        continue;

                    HeGbl.CurEditLoc++;
                    heSetUpdate (U_NEWPOS);
                    continue;
                }

                if (HeGbl.CurFlag & FC_NIBBLE) {
                    HeGbl.CurFlag &= ~FC_NIBBLE;
                    heSetUpdate (U_NEWPOS);
                    continue;
                }

                HeGbl.CurFlag |= FC_NIBBLE;
                heRightOne ();
                heSetUpdate (U_NEWPOS);
                continue;

            case 0x24:                                    /* HOME */
                if (Kd.Event.KeyEvent.dwControlKeyState &
                    (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
                    HeGbl.CurEditLoc = 0L;
                } else {
                    HeGbl.CurEditLoc &= LINEMASK;
                }

                if ((HeGbl.CurFlag & FC_TEXT) == 0)
                    HeGbl.CurEditLoc += HeGbl.DisplayMode;

                if (HeGbl.CurEditLoc > HeGbl.TotLen1)
                    HeGbl.CurEditLoc = HeGbl.TotLen1;

                HeGbl.CurFlag    |= FC_NIBBLE;
                heSetUpdate (U_NEWPOS);
                continue;


            case 0x23:                                    /* END  */
                if (Kd.Event.KeyEvent.dwControlKeyState &
                    (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
                    HeGbl.CurEditLoc = HeGbl.TotLen1;
                } else {
                    HeGbl.CurEditLoc = (HeGbl.CurEditLoc & LINEMASK) + LINESZ - 1;
                }

                HeGbl.CurFlag   &= ~FC_NIBBLE;
                if ((HeGbl.CurFlag & FC_TEXT) == 0)
                    HeGbl.CurEditLoc -= HeGbl.DisplayMode;

                if (HeGbl.CurEditLoc > HeGbl.TotLen1)
                    HeGbl.CurEditLoc = HeGbl.TotLen1;

                heSetUpdate (U_NEWPOS);
                continue;

            case 0x70:                                  /* F1       */
                switch (HeGbl.DisplayMode) {
                    case D_BYTE:    heSetDisplayMode(D_WORD);   break;
                    case D_WORD:    heSetDisplayMode(D_DWORD);  break;
                    case D_DWORD:   heSetDisplayMode(D_BYTE);   break;
                }
                heSetDisp ();
                heSetUpdate (U_SCREEN);
                continue;

            case 0x71:                                  /* F2       */
                heGotoPosition ();
                continue;

            case 0x72:                                  /* F3       */
                heSearch ();
                break;      // BUGBUG: not done

            case 0x73:                                  /* F4       */
                heCopyOut ();
                heSetDisp ();
                heSetUpdate (U_SCREEN);
                continue;

            case 0x74:                                  /* F5       */
                heCopyIn ();
                heSetDisp ();
                heSetUpdate (U_SCREEN);
                continue;

            case 0x75:                                  /* F6       */
                heJumpToLink ();
                break;

            case 0x79:                                  /* F10      */
                heUndo ();
                continue;

            case 0x0d:
                if (HeGbl.Flag & FHE_ENTER) {
                    HeGbl.Parm->flag |= FHE_ENTER;
                    Kd.Event.KeyEvent.uChar.AsciiChar = 27;  // fake an exit
                }
                break;

            //case 0x75:                                    /* F6       */
            //    if (HeGbl.Flag & FHE_F6) {
            //        HeGbl.Parm->flag |= FHE_F6;
            //        Kd.Event.KeyEvent.uChar.AsciiChar = 27;  // fake an exit
            //    }
            //    break;

        }

        // Now check for a known char code...

        if (Kd.Event.KeyEvent.uChar.AsciiChar == 27)
            break;

        if (Kd.Event.KeyEvent.uChar.AsciiChar == 9) {
            HeGbl.CurFlag ^= FC_TEXT;
            HeGbl.CurFlag |= FC_NIBBLE;
            heSetUpdate (U_NEWPOS);
            continue;
        }

        if (HeGbl.CurFlag & FC_TEXT) {
            if (Kd.Event.KeyEvent.uChar.AsciiChar == 0)
                continue;

            heSetCursorBuf ();

            *HeGbl.CurPT = Kd.Event.KeyEvent.uChar.AsciiChar;
            heUpdateCurLine ();

            if (HeGbl.CurEditLoc < HeGbl.TotLen1)
                HeGbl.CurEditLoc++;
        } else {
            if (Kd.Event.KeyEvent.uChar.AsciiChar >= 'a'  &&
                Kd.Event.KeyEvent.uChar.AsciiChar <= 'z')
                Kd.Event.KeyEvent.uChar.AsciiChar -= ('a' - 'A');

            if (!((Kd.Event.KeyEvent.uChar.AsciiChar >= '0'  &&
                   Kd.Event.KeyEvent.uChar.AsciiChar <= '9') ||
                  (Kd.Event.KeyEvent.uChar.AsciiChar >= 'A'  &&
                   Kd.Event.KeyEvent.uChar.AsciiChar <= 'F')))
                    continue;

            heSetCursorBuf ();

            if (Kd.Event.KeyEvent.uChar.AsciiChar >= 'A')
                 Kd.Event.KeyEvent.uChar.AsciiChar -= 'A' - 10;
            else Kd.Event.KeyEvent.uChar.AsciiChar -= '0';


            if (HeGbl.CurFlag & FC_NIBBLE) {
                *HeGbl.CurPT = (*HeGbl.CurPT & 0x0F) |
                                (Kd.Event.KeyEvent.uChar.AsciiChar << 4);
                heUpdateCurLine ();
            } else {
                *HeGbl.CurPT = (*HeGbl.CurPT & 0xF0) |
                                Kd.Event.KeyEvent.uChar.AsciiChar;
                heUpdateCurLine ();
                heRightOne ();
            }

            HeGbl.CurFlag ^= FC_NIBBLE;
        }
    }

    /*
     *  Free buffer memory
     */

    for (; ;) {
        rc = 0;
        while (HeGbl.Buf) {
            rc |= heFlushBuf (HeGbl.Buf);

            HeGbl.CurBuf = HeGbl.Buf->next;
            GlobalFree (HeGbl.Buf);
            HeGbl.Buf = HeGbl.CurBuf;
        }

        if (!rc)                        // If something was flushed,
            break;                      // then update the screen

        heSetUpdate (U_SCREEN);
        heSetUpdate (U_NONE);
    }                                   // and loop to free buffers (again)

    vRecurseLevel--;
    GlobalFree (HeGbl.SearchPattern);
    heEndConsole ();

    if (vRecurseLevel == 0) {
        while (vBufFree) {
            HeGbl.CurBuf = vBufFree->next;
            GlobalFree (vBufFree);
            vBufFree = HeGbl.CurBuf;
        }
    } else {
        memcpy ((PUCHAR) &HeGbl, (PUCHAR) PriorGlobal, sizeof (HeGbl));
        GlobalFree (PriorGlobal);
    }
}

VOID heSetDisplayMode (ULONG mode)
{
    PUCHAR  p;
    UCHAR   d,i,j,h,len;

    HeGbl.DisplayMode  = mode;
    HeGbl.CurAscIndent = (UCHAR)vrgAscIndent[HeGbl.DisplayMode];
    HeGbl.DWidth       = vrgDWidth[HeGbl.DisplayMode];
    HeGbl.ItemWrap     = (HeGbl.DisplayMode << 1) | 1;

    i = HeGbl.DWidth;
    j = i >> 1;
    h = HEXINDENT;
    len = LINESZ;

    p = HeGbl.rgCols;
    while (len) {
        for (d=0; d < i; d += 2) {
            len--;
            *(p++) = i - (d+2) + h;
        }
        h += i + 1;
    }
}

VOID heRightOne ()
{
    if (HeGbl.CurEditLoc & HeGbl.DisplayMode) {
        HeGbl.CurEditLoc--;
    } else {
        HeGbl.CurEditLoc += HeGbl.ItemWrap;
    }

    if (HeGbl.CurEditLoc > HeGbl.TotLen1) {
        HeGbl.CurEditLoc = HeGbl.TotLen1 & ~HeGbl.DisplayMode;
    }
}


VOID heLeftOne ()
{
    if ((HeGbl.CurEditLoc & HeGbl.DisplayMode) != HeGbl.DisplayMode) {
        if (HeGbl.CurEditLoc < HeGbl.TotLen1) {
            HeGbl.CurEditLoc++;
            return ;
        }
        if (HeGbl.TotLen1 > HeGbl.DisplayMode) {
            HeGbl.CurEditLoc |= HeGbl.DisplayMode;
        }
    }

    if (HeGbl.CurEditLoc > HeGbl.ItemWrap) {
        HeGbl.CurEditLoc -= HeGbl.ItemWrap;
        return ;
    }

    HeGbl.CurEditLoc =
        HeGbl.TotLen1 > HeGbl.DisplayMode ? HeGbl.DisplayMode : HeGbl.TotLen1;
}




VOID heUpdateCurLine ()
{
    USHORT  line;


    for (; ;) {
        HeGbl.CurBuf->flag |= FB_DIRTY;
        line = (USHORT) ((HeGbl.CurEditLoc - HeGbl.CurOffset) >> LINESHIFT);
        if (line+TOPLINE < HeGbl.LineTot - 1)
            break;

        heSetUpdate (U_NEWPOS);
        heSetUpdate (U_NONE);
        HeGbl.CurBuf = heGetBuf (HeGbl.CurEditLoc);
    }

    heHexLine (HeGbl.CurBuf, (USHORT)((HeGbl.CurEditLoc & LINEMASK) - HeGbl.CurBuf->offset), line);
    heShowBuf (line+TOPLINE, 1);
    heSetUpdate (U_NEWPOS);
    if (HeGbl.Flag & FHE_KICKDIRTY) {
        HeGbl.Parm->flag |= FHE_DIRTY;
        SetEvent (HeGbl.Parm->Kick);
    }
}


void heFindMousePos (Pos)
COORD Pos;
{
    ULONG   HoldLocation;
    USHORT  HoldFlag;
    USHORT  i;

    if (Pos.Y < TOPLINE  ||  Pos.Y >= TOPLINE+HeGbl.Lines)
        return ;


    heSetUpdate (U_NONE);
    HoldLocation = HeGbl.CurEditLoc;
    HoldFlag     = (USHORT)HeGbl.CurFlag;

    //
    // Take the cheap way out - simply run all the possibilities for the
    // target line looking for a match
    //

    HeGbl.CurEditLoc = HeGbl.CurOffset + ((Pos.Y-TOPLINE) << LINESHIFT);
    for (i=0; i < LINESZ; i++, HeGbl.CurEditLoc++) {
        HeGbl.CurFlag &= ~(FC_NIBBLE | FC_TEXT);
        heCalcCursorPosition ();
        if (Pos.X == HeGbl.CursorPos.X)
            break;

        HeGbl.CurFlag |= FC_NIBBLE;
        heCalcCursorPosition ();
        if (Pos.X == HeGbl.CursorPos.X)
            break;

        HeGbl.CurFlag |= FC_TEXT;
        heCalcCursorPosition ();
        if (Pos.X == HeGbl.CursorPos.X)
            break;
    }

    if (Pos.X == HeGbl.CursorPos.X) {
        heSetUpdate (U_NEWPOS);
    } else {
        HeGbl.CurEditLoc = HoldLocation;
        HeGbl.CurFlag    = HoldFlag;
        heCalcCursorPosition ();
    }
}



VOID heSetUpdate (USHORT i)
{
    USHORT  u;

    if (vUpdate) {
        /*
         * There's already some outstanding update going on
         * Get updat level down to current one.
         */

        while (vUpdate > i) {
            vrgUpdateFnc [u=vUpdate] ();
            if (u == vUpdate)               // If vUpdate changed, then
                vUpdate--;                  // we might have recursed
        }
    }

    vUpdate = i;
}

int heSetCursorBuf ()
{
    //  Calc HeGbl.CurBuf, HeGbl.CurPT

    if (HeGbl.CurBuf) {
        if (HeGbl.CurEditLoc >= HeGbl.CurBuf->offset  &&
            HeGbl.CurEditLoc < HeGbl.CurBuf->offset+BUFSZ ) {
                HeGbl.CurPT = HeGbl.CurBuf->data + (HeGbl.CurEditLoc - HeGbl.CurBuf->offset);
                return 0;
            }
    }

    HeGbl.CurBuf = heGetBuf (HeGbl.CurEditLoc);
    HeGbl.CurPT  = HeGbl.CurBuf->data + (HeGbl.CurEditLoc - HeGbl.CurBuf->offset);
    return 0;
}


int hePositionCursor ()
{
    heCalcCursorPosition ();
    SetConsoleCursorPosition (HeGbl.Console, HeGbl.CursorPos);

    if ((HeGbl.Flag & FHE_KICKMOVE)  &&  (HeGbl.CurEditLoc != HeGbl.Parm->editloc)) {
        HeGbl.Parm->editloc = HeGbl.CurEditLoc;
        SetEvent (HeGbl.Parm->Kick);
    }

    return 0;
}


VOID heCalcCursorPosition ()
{
    USHORT  lin, col;


    //  Verify HeGbl.CurOffset
    if (HeGbl.CurEditLoc < HeGbl.CurOffset) {
        HeGbl.CurOffset = HeGbl.CurEditLoc & LINEMASK;
        if (HeGbl.CurFlag & FC_CURCENTER) {
            if (HeGbl.CurOffset > (ULONG) HeGbl.PageSz / 2) {
                HeGbl.CurOffset -= (HeGbl.PageSz / 2) & LINEMASK;
            } else {
                HeGbl.CurOffset = 0;
            }
        }
        heSetUpdate (U_SCREEN);
    }

    if (HeGbl.CurEditLoc >= HeGbl.CurOffset+HeGbl.PageSz) {
        HeGbl.CurOffset = ((HeGbl.CurEditLoc - HeGbl.PageSz) & LINEMASK) + LINESZ;
        if (HeGbl.CurFlag & FC_CURCENTER) {
            if (HeGbl.CurOffset+HeGbl.PageSz < HeGbl.TotLen) {
                HeGbl.CurOffset += (HeGbl.PageSz / 2) & LINEMASK;
            } else {
                if (HeGbl.TotLen > HeGbl.PageSz) {
                    HeGbl.CurOffset = ((HeGbl.TotLen - HeGbl.PageSz) & LINEMASK) + LINESZ;
                }
            }
        }
        heSetUpdate (U_SCREEN);
    }

    lin = (USHORT) ((ULONG) HeGbl.CurEditLoc - HeGbl.CurOffset) >> LINESHIFT;

    if (HeGbl.CurFlag & FC_TEXT) {
        col  = (USHORT) (HeGbl.CurEditLoc & ~LINEMASK) + HeGbl.CurAscIndent+1;
    } else {
        col = HeGbl.rgCols [HeGbl.CurEditLoc & ~LINEMASK] +
              (HeGbl.CurFlag & FC_NIBBLE ? 0 : 1);
    }

    HeGbl.CursorPos.Y = lin + TOPLINE + HeGbl.TopLine;
    HeGbl.CursorPos.X = col;
}



heUpdateAllLines ()
{
    struct  Buffer  *next, *pBuf;
    USHORT  line, u;
    ULONG   loc;


    HeGbl.CurBuf = pBuf = NULL;

    /*
     *  Free up any buffers which are before the HeGbl.CurOffset
     */

    if (!(HeGbl.CurFlag & FC_INFLUSHBUF)) {
        while (HeGbl.Buf) {
            if (HeGbl.Buf->offset+BUFSZ >= HeGbl.CurOffset)
                break;

            heFlushBuf (HeGbl.Buf);

            /*
             *  Unlink buffer & put it on the free list
             */
            next = HeGbl.Buf->next;

            HeGbl.Buf->next = vBufFree;
            vBufFree   = HeGbl.Buf;

            HeGbl.Buf = next;
        }
    }

    /*
     *  Display each hex line now
     */

    loc = HeGbl.CurOffset;                       // starting offset
    for (line=0; line<HeGbl.Lines; line++) {     // for each line

        if (pBuf == NULL) {                     // do we have the buffer?
            pBuf = heGetBuf (loc);              // no, go get it
            u = (USHORT) (loc - pBuf->offset);  // calc offset into this buffer
        }

        heHexLine (pBuf, u, line);          // dump this line

        loc += LINESZ;                      // move offsets foreward one line
        u   += LINESZ;

        if (u >= BUFSZ) {                   // did we exceed the current buf?
            pBuf = pBuf->next;              // yes, move to next one
            u = 0;

            if (pBuf && loc < pBuf->offset) // verify buffer is right offs
                pBuf = NULL;                // no, let heGetBuf find it
        }
    }

    // Cause screen to be refreshed
    heShowBuf (TOPLINE, HeGbl.Lines);

    /*
     *  All lines have been displayed, free up any extra buffers
     *  at the end of the chain
     */

    if (pBuf  &&  !(HeGbl.CurFlag & FC_INFLUSHBUF)) {
        next = pBuf->next;              // get extra buffers
        pBuf->next = NULL;              // terminate active list

        pBuf = next;
        while (pBuf) {
            heFlushBuf (pBuf);          // flush this buffer

            next = pBuf->next;          // move it to the free list
                                        // and get next buffer to flush
            pBuf->next = vBufFree;
            vBufFree   = pBuf;

            pBuf = next;
        }

    }

    HeGbl.CurFlag &= ~FC_CURCENTER;
    return 0;
}




int heFlushBuf (pBuf)
struct Buffer *pBuf;
{
    ULONG   loc, ploc;
    USHORT  c;
    NTSTATUS status;

    if ((pBuf->flag & FB_DIRTY) == 0  ||
        memcmp (pBuf->data, pBuf->orig, pBuf->len) == 0)
            return (0);             // buffer isn't dirty, return

    // We may need to call heSetUpdate - setting this bit will
    // stop FlushBuf from being recursed.

    HeGbl.CurFlag |= FC_INFLUSHBUF;

    loc  = pBuf->offset;
    ploc = pBuf->physloc;
    if (HeGbl.Flag & (FHE_VERIFYONCE | FHE_VERIFYALL)) {
        heSetUpdate (U_NONE);             // make sure screen current

        heBox (12, TOPLINE+1, 55, 8);
        heDisp (TOPLINE+3, 14, "%HWrite changes to %S?", HeGbl.Parm->ename);
        heDisp (TOPLINE+7, 14, "Press '%HY%N'es or '%HN%N'o");

        if (HeGbl.Flag & FHE_VERIFYALL) {
            if (HeGbl.Flag & FHE_PROMPTSEC) {
                heDisp (TOPLINE+4, 14, "Sector %H%D%N has been modifed",ploc/BUFSZ);
            } else {
                heDisp (TOPLINE+4, 14, "Location %H%X%N-%H%X%N has been modifed",ploc,ploc+BUFSZ);
            }
            heDisp (TOPLINE+8, 14, "Press '%HA%N' to save all updates");
        }
        RefreshDisp ();

        c = heGetChar ("YNA");          // wait for key stroke
        heSetDisp ();                   // Get heBox off of screen
        heSetUpdate (U_SCREEN);         // we will need to update display

        if (c == 'N') {
            memcpy (pBuf->data, pBuf->orig, pBuf->len);
            HeGbl.CurFlag &= ~FC_INFLUSHBUF;

            if (HeGbl.Flag & FHE_KICKDIRTY) {
                HeGbl.Parm->flag |= FHE_DIRTY;
                 SetEvent (HeGbl.Parm->Kick);
            }
            return (0);
        }

        if (c == 'A')
            HeGbl.Flag &= ~FHE_VERIFYALL;
    }


    if (HeGbl.Parm->write) {
        /*
         *  Write new buffer.
         */
        do {
            status = HeGbl.Parm->write(HeGbl.Parm->handle, loc, pBuf->data,pBuf->len,ploc);
            if (!status) {
                pBuf->flag &= ~FB_DIRTY;
                break;
            }
        } while (heIOErr ("WRITE ERROR!", loc, ploc, status) == 'R');
    }

    HeGbl.Flag    &= ~FHE_VERIFYONCE;
    HeGbl.CurFlag &= ~FC_INFLUSHBUF;
    return (1);
}


VOID heJumpToLink ()
{
    PULONG  p;
    ULONG   l;

    if (HeGbl.DisplayMode != D_DWORD  ||  (HeGbl.Flag & FHE_JUMP) == 0)
        return;

    if (((HeGbl.CurEditLoc & ~3) + 3) > HeGbl.TotLen1)
        return;

    heSetCursorBuf ();
    p = (PULONG) (((ULONG) HeGbl.CurPT) & ~3);   // Round to dword location

    l = *p;
    if ((l & 3) == 0)
        l += 3;

    if (l > HeGbl.TotLen1) {
        Beep (500, 100);
        return;
    }

    HeGbl.CurFlag |= FC_NIBBLE | FC_CURCENTER;
    HeGbl.CurEditLoc = l;

    heSetDisp ();           // clear & restore orig screen (does not draw)
    heSetUpdate (U_SCREEN);   // redraw hex area
}

VOID heSearch ()
{
    ULONG   i, j, sec, off, slen, len, upd;
    struct  HexEditParm     ei;
    PUCHAR  data, data2;

    if (vInSearch || HeGbl.Lines < 25) {
        return ;
    }

    vInSearch = TRUE;

    heFlushAllBufs (1);               // Before we start flush & free all buffers
    heSetUpdate (U_NONE);

    memset ((PUCHAR) &ei, 0, sizeof (ei));
    ei.ename       = "Entering Search";
    ei.flag        = FHE_EDITMEM | FHE_ENTER;
    ei.mem         = HeGbl.SearchPattern;
    ei.totlen      = 512;
    ei.ioalign     = 1;
    ei.Console     = HeGbl.Console;
    ei.AttrNorm    = HeGbl.AttrNorm;
    ei.AttrHigh    = HeGbl.AttrHigh;
    ei.AttrReverse = HeGbl.AttrReverse;
    ei.CursorSize  = HeGbl.Parm->CursorSize;

    i = 24;
    if (HeGbl.Lines < i) {
        if (HeGbl.Lines < 12) {
            goto abort;
        }
        i = HeGbl.Lines - 8;
    }

    ei.TopLine     = HeGbl.Lines + TOPLINE - i;
    ei.MaxLine     = i + 1;
    if (HeGbl.DisplayMode == D_DWORD) {
        ei.flag |= FHE_DWORD;
    }

    HexEdit (&ei);              // Get search parameters
    vInSearch = FALSE;

    if (!(ei.flag & FHE_ENTER))
        goto abort;

    for (i=0, slen=0; i < 512; i++) {       // find last non-zero byte
        if (HeGbl.SearchPattern[i]) {       // in search patter
            slen = i+1;
        }
    }

    if (slen == 0) {
        goto abort;
    }

    heBox (12, TOPLINE+1, 48, 6);
    heDisp (TOPLINE+3, 14, "Searching for pattern");
    SetCurPos (TOPLINE+5, 24);
    RefreshDisp ();

    i   = HeGbl.CurEditLoc + 1;
    sec = i & SECTORMASK;                   // current sector
    off = i - sec;                          // offset within sector checking
    upd = 0;

    while (sec < HeGbl.TotLen) {
        if (++upd >= 50) {
            upd = 0;
            heFlushAllBufs (0);             // free memory
            heDisp (TOPLINE+6, 14, "Searching offset %H%D ", sec);
            heShowBuf (TOPLINE+6, 1);
        }

        data = heGetBuf(sec)->data;

nextoff:
        while (off < BUFSZ  &&  data[off] != HeGbl.SearchPattern[0]) {
            off++;
        }

        if (off >= BUFSZ) {
            // next sector...
            sec += BUFSZ;
            off  = 0;
            continue;
        }

        len = (off + slen) > BUFSZ ? BUFSZ - off : slen;
        for (i=0; i < len; i++) {
            if (data[off+i] != HeGbl.SearchPattern[i]) {
                off += 1;
                goto nextoff;
            }
        }

        if (i < slen) {
            // data is continued in next buffer..
            if (sec+BUFSZ >= HeGbl.TotLen) {
                off += 1;
                goto nextoff;
            }

            data2 = heGetBuf (sec+BUFSZ)->data;
            j     = (BUFSZ-off);
            len   = slen - j;
            for (i=0; i < len; i++) {
                if (data2[i] != HeGbl.SearchPattern[j+i]) {
                    off += 1;
                    goto nextoff;
                }
            }
        }

        // found match
        if (sec + off + slen > HeGbl.TotLen1) {
            break;
        }

        HeGbl.CurEditLoc = sec + off;
        heSetDisp   ();             // clear & restore orig screen (does not draw)
        heSetUpdate (U_SCREEN);     // redraw hex area
        return ;
    }


    heBox (12, TOPLINE+1, 48, 6);
    heDisp (TOPLINE+3, 14, "Data was not found");
    heDisp (TOPLINE+5, 17, "Press %HEnter%N to continue");
    SetCurPos (TOPLINE+6, 17);
    RefreshDisp ();
    heGetChar ("\r");

abort:
    heSetDisp   ();             // clear & restore orig screen (does not draw)
    heSetUpdate (U_SCREEN);     // redraw hex area
    return ;
}


VOID heGotoPosition ()
{
    UCHAR   s[16];
    ULONG   l;

    heSetUpdate (U_NONE);
    heBox (12, TOPLINE+1, 41, 6);

    heDisp (TOPLINE+3, 14, "Enter offset from %H%X%N - %H%X", 0L, HeGbl.TotLen1);
    heDisp (TOPLINE+5, 14, "Offset:           ");
    SetCurPos (TOPLINE+5, 22);
    RefreshDisp ();

    heGetString (s, 10);

    if (s[0]) {
        l = heHtou (s);
        if (l <= HeGbl.TotLen1) {
            HeGbl.CurFlag |= FC_NIBBLE;
            HeGbl.CurEditLoc = l;
        }
    }

    if (!(HeGbl.CurFlag & FC_TEXT)  &&  !(HeGbl.CurEditLoc & HeGbl.DisplayMode)) {
        // On xword boundry and not in text mode, adjust so cursor
        // is on the first byte which is being displayed of this
        // xword

        HeGbl.CurEditLoc += HeGbl.DisplayMode;
        if (HeGbl.CurEditLoc > HeGbl.TotLen1)
            HeGbl.CurEditLoc = HeGbl.TotLen1;
    }



    HeGbl.CurFlag |= FC_CURCENTER;      // set cursor to center in window moves
    heSetDisp ();             // clear & restore orig screen (does not draw)
    heSetUpdate (U_SCREEN);   // redraw hex area
}


VOID heGetString (PUCHAR s, USHORT len)
{
    UCHAR   i[50];
    DWORD   cb;

    ReadFile( HeGbl.StdIn, i, 50, &cb, NULL );

    if(cb >= 2  &&  (i[cb - 2] == 0x0d || i[cb - 2] == 0x0a) ) {
        i[cb - 2] = 0;     // Get rid of CR LF
    }
    i[ cb - 1] = 0;

    memcpy (s, i, len);
    s[len] = 0;
}



/***
 *  heCopyOut - Copies data to output filename
 */
VOID heCopyOut ()
{
    UCHAR       s[24];
    ULONG       len, rem, upd, u;
    HANDLE      h;
    NTSTATUS    status;
    struct Buffer *pB;

    heFlushAllBufs (1);               // Before we start flush & free all buffers
    heSetUpdate (U_NONE);
    heBox (12, TOPLINE+1, 48, 6);

    heDisp (TOPLINE+3, 14, "Copy stream to filename (%H%D%N Bytes)", HeGbl.TotLen);
    heDisp (TOPLINE+5, 14, "Filename:                      ");
    SetCurPos (TOPLINE+5, 24);
    RefreshDisp ();

    heGetString (s, 19);
    if (s[0] == 0)
        return;

    status = heOpenFile (s, &h, GENERIC_WRITE);
    if (NT_SUCCESS(status)) {
        len = upd = 0;
        rem = HeGbl.TotLen;
        while (NT_SUCCESS(status) && rem){
            if (upd++ > 50) {
                upd = 0;
                heFlushAllBufs (0);         // free memory
                heDisp (TOPLINE+6, 14, "Bytes written %H%D ", len);
                heShowBuf (TOPLINE+6, 1);
                RefreshDisp ();
            }

            u      = rem > BUFSZ ? BUFSZ : rem;
            pB     = heGetBuf (len);
            status = heWriteFile (h, pB->data, u);
            rem   -= u;
            len   += BUFSZ;
        }
        CloseHandle(h);
    }

    if (!NT_SUCCESS(status)) {
        heBox (15, TOPLINE+1, 33, 6);
        heDisp (TOPLINE+3, 17, "%HCopy failed");
        heDisp (TOPLINE+4, 17, "Error code %X", status);
        heDisp (TOPLINE+5, 17, "Press %HA%N to abort");
        RefreshDisp ();
        heGetChar ("A");
    }
}



/***
 *  heCopyIn - Copies data to output filename
 */

VOID
heCopyIn ()
{
    UCHAR       s[24];
    ULONG       holdEditLoc, rem, u, br;
    struct      Buffer *pB;
    char        *pErr;
    HANDLE      h;
    NTSTATUS    status;

    heSetUpdate (U_NONE);
    heBox (12, TOPLINE+1, 48, 6);

    heDisp (TOPLINE+3, 14, "Input from filename (%H%D%N Bytes)", HeGbl.TotLen);
    heDisp (TOPLINE+5, 14, "Filename:                      ");
    SetCurPos (TOPLINE+5, 24);
    RefreshDisp ();

    heGetString (s, 19);
    heSetDisp ();                   // Get heBox off of screen
    if (s[0] == 0) {
        return;
    }

    status = heOpenFile (s, &h, GENERIC_READ);
    if (NT_SUCCESS(status)) {
        rem = HeGbl.TotLen;
        holdEditLoc = HeGbl.CurEditLoc;
        HeGbl.CurEditLoc = 0;
        while (NT_SUCCESS(status) && rem) {
            pB     = heGetBuf (HeGbl.CurEditLoc);
            u      = rem >  BUFSZ ? BUFSZ : rem;
            status = heReadFile (h, pB->data, u, &br);

            if (memcmp (pB->data, pB->orig, pB->len)) {
                pB->flag |= FB_DIRTY;         // it's changed
                HeGbl.CurFlag |= FC_CURCENTER;
                heSetUpdate (U_SCREEN);
                heSetUpdate (U_NONE);         // Update screen
                if (HeGbl.Flag & FHE_KICKDIRTY) {
                    HeGbl.Parm->flag |= FHE_DIRTY;
                //    DosSemClear (HeGbl.Parm->sem);        BUGBUG
                }
            }
            heFlushAllBufs (1);
            if (NT_SUCCESS(status)  &&  br != u) {
                pErr = "Smaller then data";
            }

            rem -= u;
            HeGbl.CurEditLoc += BUFSZ;
        }

        if (NT_SUCCESS(status)) {
            heReadFile (h, s, 1, &br);
            if (br)                     // then what we are editting
                pErr = "Larger then data";
        }

        CloseHandle(h);
    }

    if (!NT_SUCCESS(status)  ||  pErr) {
        heBox (15, TOPLINE+1, 33, 6);
        if (pErr) {
            heDisp (TOPLINE+3, 17, "Import file is:");
            heDisp (TOPLINE+4, 17, pErr);
            heDisp (TOPLINE+5, 17, "Press %HC%N to continue");
        } else {
            heDisp (TOPLINE+3, 17, "%HImport failed");
            heDisp (TOPLINE+4, 17, "Error code %X", status);
            heDisp (TOPLINE+5, 17, "Press %HA%N to abort");
        }
        RefreshDisp ();
        heGetChar ("CA");
    }

    HeGbl.CurEditLoc = holdEditLoc;
}


VOID
heFlushAllBufs (USHORT update)
{
    struct  Buffer  *next;
    USHORT  rc;

    for (; ;) {
        rc = 0;
        while (HeGbl.Buf) {
            rc |= heFlushBuf (HeGbl.Buf);

            next = HeGbl.Buf->next;
            HeGbl.Buf->next = vBufFree;
            vBufFree   = HeGbl.Buf;
            HeGbl.Buf = next;
        }

        if (!rc)                        // If something was flushed,
            break;                      // then update the screen

        if (update) {
            heSetUpdate (U_SCREEN);
            heSetUpdate (U_NONE);
        }
    }                                   // and loop to free buffers (again)
}




VOID heBox (x, y, len_x, len_y)
USHORT x, y, len_x, len_y;
{
    CHAR_INFO   blank[CELLPERLINE];
    PCHAR_INFO  pt, pt1;
    USHORT      c, lc;

    pt = blank;
    for (c=len_x; c; c--) {                     /* Construct blank line */
        PUTCHAR (pt, ' ', HeGbl.AttrNorm);           /* with background color*/

    }
    blank[0].Char.AsciiChar = blank[lc=len_x-1].Char.AsciiChar = '�';

    for (c=0; c <= len_y; c++)                  /* blank each line      */
      memcpy (POS(c+y,x), blank, (pt - blank) * sizeof (CHAR_INFO));

    pt  = POS(y,x);
    pt1 = POS(y+len_y, x);
    for (c=0; c < len_x; c++)                   /* Draw horz lines      */
        pt[c].Char.AsciiChar = pt1[c].Char.AsciiChar  = '�';

    pt  [ 0].Char.AsciiChar  = '�';             /* Put corners on       */
    pt  [lc].Char.AsciiChar  = '�';
    pt1 [ 0].Char.AsciiChar  = '�';
    pt1 [lc].Char.AsciiChar  = '�';
}



VOID heUndo ()
{
    struct  Buffer  *pBuf;
    USHORT  flag;

    flag = 0;
    for (pBuf=HeGbl.Buf; pBuf; pBuf = pBuf->next)
        if (pBuf->flag & FB_DIRTY) {
            flag = 1;
            pBuf->flag &= ~FB_DIRTY;
            memcpy (pBuf->data, pBuf->orig, pBuf->len);
        }

    if (flag) {
        heSetUpdate (U_SCREEN);
        if (HeGbl.Flag & FHE_KICKDIRTY) {
            HeGbl.Parm->flag |= FHE_DIRTY;
            SetEvent (HeGbl.Parm->Kick);
        }
    }
}



void heHexLine (pBuf, u, line)
struct Buffer *pBuf;
USHORT u, line;
{
    PCHAR_INFO pt, hex, asc;
    UCHAR  *data, *orig;
    UCHAR  len, mlen, c, d, i, j;
    WORD   a;
    ULONG  l;
    WORD   AttrNorm = HeGbl.AttrNorm;

    data = pBuf->data + u;
    orig = pBuf->orig + u;

    pt  = HeGbl.pVioBuf + (line+TOPLINE) * CELLPERLINE;
    hex = pt + HEXINDENT;
    asc = pt + HeGbl.CurAscIndent;

    l = pBuf->offset + u;
    if (l & ((ULONG) BUFSZ-1)) {
        heHexDWord (pt+LINEINDENT, pBuf->physloc + u,
                AttrNorm);
    } else {
        heHexDWord (pt+LINEINDENT, pBuf->physloc + u,
                HeGbl.AttrHigh);
    }

    if (pBuf->flag & FB_BAD) {                          // If read error on
        pt[LINEINDENT+8].Char.AsciiChar = 'E';          // this sector, then
        pt[LINEINDENT+8].Attributes = HeGbl.AttrHigh;   // flag it
    } else
        pt[LINEINDENT+8].Char.AsciiChar = ' ';

    if (l + LINESZ > HeGbl.TotLen) {                    // if EOF
        if (l >= HeGbl.TotLen) {                        // Totally blankline?
            PUTCHAR (asc, ' ', AttrNorm);
            PUTCHAR (asc, ' ', AttrNorm);
            mlen = 0;

            for (len=0; len < 9; len++)
                pt[len].Char.AsciiChar = ' ';

            goto blankline;
        }
        len = mlen = (UCHAR) (HeGbl.TotLen - l);        // Clip line
    } else
        len = mlen = (UCHAR) LINESZ;                    // Full line


    PUTCHAR (asc, '*', AttrNorm);

    switch (HeGbl.DisplayMode) {
        case D_BYTE:
            while (len--) {
                c = *(data++);
                a = c == *(orig++) ? AttrNorm : HeGbl.AttrReverse;
                PUTCHAR (hex, rghexc [c >> 4],   a);
                PUTCHAR (hex, rghexc [c & 0x0F], a);
                hex++;

                PUTCHAR (asc, (c < ' '  ||  c > '~') ? '.' : c, a);
            }
            pt[((LINESZ/2)*3+HEXINDENT)-1].Char.AsciiChar = '-';
            break;

        default:
            hex--;
            i = HeGbl.DWidth;
            j = i >> 1;
            while (len) {
                hex += i;
                for (d=0; d<j; d++) {
                    if (len) {
                        len--;
                        c = *(data++);
                        a = c == *(orig++) ? AttrNorm : HeGbl.AttrReverse;

                        hex->Attributes     = a;
                        hex->Char.AsciiChar = rghexc[c & 0x0F];
                        hex--;
                        hex->Attributes     = a;
                        hex->Char.AsciiChar = rghexc[c >> 4];
                        hex--;

                        PUTCHAR (asc, (c < ' '  ||  c > '~') ? '.' : c, a);
                    } else {
                        hex->Attributes     = AttrNorm;
                        hex->Char.AsciiChar = '?';
                        hex--;
                        hex->Attributes     = AttrNorm;
                        hex->Char.AsciiChar = '?';
                        hex--;
                    }
                }
                hex += i + 1;
            }
            break;
    }

    PUTCHAR (asc, '*', AttrNorm);

blankline:
    while (mlen++ < LINESZ)
        PUTCHAR (asc, ' ', AttrNorm);

    asc = pt + HeGbl.CurAscIndent;
    while (hex < asc)
        PUTCHAR (hex, ' ', AttrNorm);
}


heInitScr ()
{
    CONSOLE_SCREEN_BUFFER_INFO  Mode;
    CONSOLE_CURSOR_INFO CursorInfo;
    USHORT      li;

    GetConsoleScreenBufferInfo(HeGbl.Console, &Mode);
    if (HeGbl.Parm->MaxLine) {
        HeGbl.TopLine = (USHORT)HeGbl.Parm->TopLine;
        li = (USHORT)HeGbl.Parm->MaxLine;  // +1;    adjust for no fnc key line
    } else {
        li = Mode.srWindow.Bottom - Mode.srWindow.Top + 1;
        if (li < 10)
            li = 10;

        Mode.dwSize.Y = li;
    }

    if (Mode.dwSize.X < 80)
        Mode.dwSize.X = 80;

    if (!SetConsoleScreenBufferSize(HeGbl.Console, Mode.dwSize)) {

        Mode.srWindow.Bottom -= Mode.srWindow.Top;
        Mode.srWindow.Right -= Mode.srWindow.Left;
        Mode.srWindow.Top = 0;
        Mode.srWindow.Left = 0;

        SetConsoleWindowInfo(HeGbl.Console, TRUE, &Mode.srWindow);
        SetConsoleScreenBufferSize(HeGbl.Console, Mode.dwSize);
    }

    HeGbl.Lines   = li - TOPLINE - 1;
    HeGbl.PageSz  = HeGbl.Lines * LINESZ;
    HeGbl.LineTot = li;

    if (HeGbl.pVioBuf)
        GlobalFree (HeGbl.pVioBuf);

    HeGbl.pVioBuf = (PCHAR_INFO) GlobalAlloc (0,
                            (HeGbl.LineTot+1)*CELLPERLINE*sizeof(CHAR_INFO));

    HeGbl.dwVioBufSize.X = CELLPERLINE;
    HeGbl.dwVioBufSize.Y = HeGbl.LineTot +
                           (HeGbl.TopLine*CELLPERLINE*sizeof(CHAR_INFO));

    GetConsoleCursorInfo (HeGbl.Console, &CursorInfo);
    CursorInfo.bVisible = TRUE;
    CursorInfo.dwSize = (ULONG) HeGbl.Parm->CursorSize ? HeGbl.Parm->CursorSize : 100;
    SetConsoleCursorInfo (HeGbl.Console, &CursorInfo);

    return heSetDisp ();
}

int heInitConsole ()
{
    CONSOLE_SCREEN_BUFFER_INFO  screenMode;
    DWORD   mode;

    HeGbl.StdIn = GetStdHandle (STD_INPUT_HANDLE);
    GetConsoleMode (HeGbl.StdIn, &mode);
    HeGbl.OrigMode = mode;
    SetConsoleMode (HeGbl.StdIn, mode | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT |
                            ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT );


    if (HeGbl.Flag & FHE_SAVESCRN) {
        HeGbl.Console = CreateConsoleScreenBuffer(
                        GENERIC_WRITE | GENERIC_READ,
                        FILE_SHARE_WRITE,
                        NULL,
                        CONSOLE_TEXTMODE_BUFFER,
                        NULL );

        SetConsoleActiveScreenBuffer (HeGbl.Console);
    } else {
        HeGbl.Console = HeGbl.Parm->Console;
        if (HeGbl.Console == INVALID_HANDLE_VALUE)
            HeGbl.Console = GetStdHandle( STD_OUTPUT_HANDLE );

        GetConsoleScreenBufferInfo(HeGbl.Console, &screenMode);
        HeGbl.dwSize = screenMode.dwSize;
    }

    GetConsoleCursorInfo(HeGbl.Console, &HeGbl.CursorInfo);
    return 0;
}


VOID heEndConsole ()
{
    CONSOLE_SCREEN_BUFFER_INFO  Mode;
    PCHAR_INFO  pt;
    ULONG   u;

    SetConsoleMode (HeGbl.StdIn, HeGbl.OrigMode);

    if (HeGbl.Flag & FHE_SAVESCRN) {
        CloseHandle (HeGbl.Console);

        if (HeGbl.Parm->Console == INVALID_HANDLE_VALUE)
             SetConsoleActiveScreenBuffer (GetStdHandle(STD_OUTPUT_HANDLE));
        else SetConsoleActiveScreenBuffer (HeGbl.Parm->Console);

    } else {
        if (!SetConsoleScreenBufferSize(HeGbl.Console, HeGbl.dwSize)) {

            GetConsoleScreenBufferInfo(HeGbl.Console, &Mode);
            Mode.srWindow.Bottom -= Mode.srWindow.Top;
            Mode.srWindow.Right -= Mode.srWindow.Left;
            Mode.srWindow.Top = 0;
            Mode.srWindow.Left = 0;
            SetConsoleWindowInfo(HeGbl.Console, TRUE, &Mode.srWindow);

            SetConsoleScreenBufferSize(HeGbl.Console, HeGbl.dwSize);
        }

        if (HeGbl.LineTot <= HeGbl.dwSize.Y) {
            HeGbl.dwSize.Y--;
            pt = POS(HeGbl.LineTot - 1,0);
            for (u=HeGbl.dwSize.X; u; u--) {
                PUTCHAR (pt, ' ', HeGbl.AttrNorm);
            }

            heShowBuf (HeGbl.LineTot - 1, 1);
        }

        HeGbl.dwSize.X = 0;
        SetConsoleCursorPosition (HeGbl.Console, HeGbl.dwSize);
        SetConsoleCursorInfo (HeGbl.Console, &HeGbl.CursorInfo);
    }

    if (HeGbl.pVioBuf) {
        GlobalFree (HeGbl.pVioBuf);
        HeGbl.pVioBuf = NULL;
    }
}


heRefresh ()
{
    RefreshDisp ();

    if (HeGbl.Flag & FHE_KICKDIRTY) {
        HeGbl.Parm->flag |= FHE_DIRTY;
        SetEvent (HeGbl.Parm->Kick);
    }
    return 0;
}


int heSetDisp ()
{
    PCHAR_INFO  pt, pt1;
    USHORT      u;

    pt = POS(0,0);
    for (u=HeGbl.LineTot * CELLPERLINE; u; u--) {
        PUTCHAR (pt, ' ', HeGbl.AttrNorm);
    }

    heDisp (1,  5, "Edit: %H%S", HeGbl.Parm->ename);
    heDisp (2,  5, "Size: %X (%D)", HeGbl.TotLen, HeGbl.TotLen);

    heDisp (1, 40, "Position:");
    for (pt1=POS(1,50), u=0; u<30; u++, pt1++)
        pt1->Attributes = HeGbl.AttrHigh;

    //if (HeGbl.Parm->MaxLine == 0) {
        u = HeGbl.LineTot - 1;
        heDisp (u, 0, "%HF1%N:Toggle");
        heDisp (u,11, "%HF2%N:Goto");

        if (!vInSearch) {
            heDisp (u,20, "%HF3%N:Search");
        }

        heDisp (u,31, "%HF4%N:Export");
        heDisp (u,42, "%HF5%N:Import");

        if (HeGbl.DisplayMode == D_DWORD  &&  (HeGbl.Flag & FHE_JUMP) != 0)
             heDisp (u,53, "%HF6%N:Jump");
        else heDisp (u,53, "       ");

        heDisp (u,66, "%HF10%N:Undo");

        //if (HeGbl.Flag & FHE_F6)
        //    heDisp (u,51, "%HF6%N:PSec");
    //}
    return 0;
}

int heUpdateFncs ()
{
    heShowBuf (HeGbl.LineTot - 1, 1);
    return 0;
}


int heUpdateStats ()
{
    COORD dwBufferCoord;
    SMALL_RECT lpWriteRegion;

    heHexDWord (POS(1, 50), HeGbl.CurEditLoc, HeGbl.AttrHigh);
    heLtoa     (POS(1, 60), HeGbl.CurEditLoc);

    dwBufferCoord.X = 50;
    dwBufferCoord.Y = 1;

    lpWriteRegion.Left   = 50;
    lpWriteRegion.Top    = HeGbl.TopLine + 1;
    lpWriteRegion.Right  = 74;
    lpWriteRegion.Bottom = HeGbl.TopLine + 1;

    WriteConsoleOutputA (
        HeGbl.Console,
        HeGbl.pVioBuf,
        HeGbl.dwVioBufSize, // size of VioBuf
        dwBufferCoord,      // location in VioBuf to write
        &lpWriteRegion      // location to write on display
    );

    SetConsoleCursorPosition (HeGbl.Console, HeGbl.CursorPos); // redisplay cursor
    return 0;
}

VOID heShowBuf (ULONG StartingLine, ULONG NoLines)
{
    COORD dwBufferCoord;
    SMALL_RECT lpWriteRegion;

    dwBufferCoord.X = (SHORT)0;
    dwBufferCoord.Y = (SHORT)StartingLine;

    StartingLine += HeGbl.TopLine;
    lpWriteRegion.Left   = (SHORT)0;
    lpWriteRegion.Top    = (SHORT)StartingLine;
    lpWriteRegion.Right  = (SHORT)(CELLPERLINE-1);
    lpWriteRegion.Bottom = (SHORT)(StartingLine+NoLines-1);

    WriteConsoleOutputA (
        HeGbl.Console,
        HeGbl.pVioBuf,
        HeGbl.dwVioBufSize, // size of VioBuf
        dwBufferCoord,      // location in VioBuf to write
        &lpWriteRegion      // location to write on display
    );
}


struct Buffer *heGetBuf (loc)
ULONG  loc;
{
    struct  Buffer  **ppBuf, *pBuf;
    ULONG   ioalign;
    USHORT  len;
    NTSTATUS status;

    loc &= SECTORMASK;

    ppBuf = &HeGbl.Buf;
    while (pBuf = *ppBuf) {
        if (pBuf->offset >= loc) {
            if (pBuf->offset == loc)        // buffer the correct offset?
                return pBuf;                // yup - all done

            break;                          // it's not here
        }
        ppBuf = &pBuf->next;                // try the next one
    }


    /*
     *  buffer was not in list - it should be insterted before ppBuf
     */

    ioalign = HeGbl.Parm->ioalign;
    if (vBufFree) {
        pBuf = vBufFree;
        vBufFree = vBufFree->next;
    } else
        pBuf = (struct Buffer *)
                 GlobalAlloc (0, sizeof(struct Buffer)+BUFSZ+ioalign);

    pBuf->data   = (PUCHAR)(((ULONG)pBuf+sizeof(struct Buffer)+ioalign)&~ioalign);
    pBuf->offset = loc;
    pBuf->physloc= loc;                     // Assume physloc is logical offset
    pBuf->flag   = 0;

    // Link this buffer in now! In case we recurse (due to read-error)
    pBuf->next = *ppBuf;                    // link in this new buffer
    *ppBuf = pBuf;

    if (loc + BUFSZ > HeGbl.TotLen) {       // are we going to hit the EOF?
         if (loc >= HeGbl.TotLen) {         // is buffer completely passed EOF?
            pBuf->len = 0;
            goto nodata;                    // yes, then no data at all
         }
         len = (USHORT) (HeGbl.TotLen - loc);   // else, clip to EOF
    } else len = BUFSZ;                     // not pass eof, get a full buffer

    pBuf->len = len;

    if (HeGbl.Flag & FHE_EDITMEM)               // Direct memory edit?
        pBuf->data = HeGbl.Parm->mem + loc;     // memory location of buffer

    if (HeGbl.Read) {
        /*
         *  Read buffer from file
         */
        status = HeGbl.Read (HeGbl.Parm->handle, loc, pBuf->data, len, &pBuf->physloc);
        if (status) {
            // If read error, we will always retry once.  Also clear buffer
            // before retry in case read retreives some info
            for (; ;) {
                memset (pBuf->data,   0, len);      // Clear read area
                memset (pBuf->orig,0xff, len);      // good effect
                status = HeGbl.Read (HeGbl.Parm->handle, loc, pBuf->data, len, &pBuf->physloc);

                if (!status)
                    break;

                if (heIOErr ("READ ERROR!", loc, pBuf->physloc, status) == 'I') {
                    pBuf->flag |= FB_BAD;
                    break;
                }
            }
        }
    }

    memcpy (pBuf->orig, pBuf->data, len);       // make a copy of the data

nodata:
    return pBuf;
}


USHORT heIOErr (UCHAR *str, ULONG loc, ULONG ploc, ULONG errcd)
{
    USHORT      c;

    if (errcd == 0xFFFFFFFF)
        return 'I';

    heSetUpdate (U_NONE);
    heBox (12, TOPLINE+1, 55, 8);

    heDisp (TOPLINE+3, 14, str);
    heDisp (TOPLINE+4, 14, "Error code %H%D%N, Offset %Xh, Sector %D",
        errcd, loc, ploc / BUFSZ);
    heDisp (TOPLINE+7, 14, "Press '%HR%N' to retry IO, or '%HI%N' to ignore");
    RefreshDisp ();

    c = heGetChar ("RI");

    heSetDisp ();                   // Get heBox off of screen
    heSetUpdate (U_SCREEN);
    return c;
}


UCHAR heGetChar (keys)
PUCHAR keys;
{
    INPUT_RECORD    Kd;
    DWORD           cEvents;
    UCHAR           *pt;

    for (; ;) {
        Beep (500, 100);

        for (; ;) {
            ReadConsoleInput (HeGbl.StdIn, &Kd, 1, &cEvents);

            if (Kd.EventType != KEY_EVENT)
                continue;                           // Not a key

            if (!Kd.Event.KeyEvent.bKeyDown)
                continue;                           // Not a down stroke

            if (Kd.Event.KeyEvent.wVirtualKeyCode == 0    ||    // ALT
                Kd.Event.KeyEvent.wVirtualKeyCode == 0x10 ||    // SHIFT
                Kd.Event.KeyEvent.wVirtualKeyCode == 0x11 ||    // CONTROL
                Kd.Event.KeyEvent.wVirtualKeyCode == 0x14)      // CAPITAL
                    continue;

            break;
        }

        if (Kd.Event.KeyEvent.wVirtualKeyCode >= 'a'  &&
            Kd.Event.KeyEvent.wVirtualKeyCode <= 'z')
                Kd.Event.KeyEvent.wVirtualKeyCode -= ('a' - 'A');

        for (pt=keys; *pt; pt++)            // Is this a key we are
            if (Kd.Event.KeyEvent.wVirtualKeyCode == *pt)
                return *pt;                 // looking for?
    }
}


VOID _CRTAPI1
heDisp (line, col, pIn, ...)
USHORT  line, col;
UCHAR   *pIn;
{
    register char   c;
    PCHAR_INFO pOut;
    WORD    attr;
    USHORT  u;
    UCHAR   *pt;
    va_list args;

    attr = HeGbl.AttrNorm;
    pOut = POS(line,col);

    va_start(args,pIn);
    while (c = *(pIn++)) {
        if (c != '%') {
            PUTCHAR (pOut, c, attr);
            continue;
        }

        switch (*(pIn++)) {
            case 'S':
                pt = va_arg(args, CHAR *);
                while (*pt) {
                    PUTCHAR (pOut, *(pt++), attr);
                }
                break;

            case 'X':               /* Long HEX, fixed len      */
                heHexDWord (pOut, va_arg(args, ULONG), attr);
                pOut += 8;
                break;

            case 'D':               /* Long dec, varible len    */
                u = heLtoa (pOut, va_arg(args, ULONG));
                while (u--) {
                    pOut->Attributes = attr;
                    pOut++;
                }
                break;
            case 'H':
                attr = HeGbl.AttrHigh;
                break;
            case 'N':
                attr = HeGbl.AttrNorm;
                break;
        }
    }
}




void heHexDWord (s, l, attr)
PCHAR_INFO  s;
ULONG   l;
WORD    attr;
{
    UCHAR   d, c;
    UCHAR   *pt;

    s += 8-1;
    pt = (UCHAR *) &l;

    for (d=0; d<4; d++) {
        c = *(pt++);
        s->Attributes     = attr;
        s->Char.AsciiChar = rghexc[c & 0x0F];
        s--;
        s->Attributes     = attr;
        s->Char.AsciiChar = rghexc[c >> 4];
        s--;
    }
}


USHORT heLtoa (s, l)
PCHAR_INFO s;
ULONG  l;
{
    static ULONG mask[] = { 0L,
                 1L,
                10L,
               100L,
              1000L,
             10000L,
            100000L,
           1000000L,
          10000000L,
         100000000L,
        1000000000L,
    };

    static UCHAR comm[] = "xxxx,xx,xx,xx,xx";
    PCHAR_INFO  os;
    UCHAR      c;
    USHORT     i, j;
    ULONG      m;

    if (l < 1000000000L) {
        for (i=1; mask[i] <= l; i++)  ;

        if  (l == 0L)       // Make Zeros
            i++;
    } else
        i = 11;

    os = s;
    j  = i;
    while (m = mask[--i]) {
        c  = (UCHAR) ((ULONG) l / m);
        l -= m * c;
        s->Char.AsciiChar = c + '0';
        if (comm[i] == ',') {
            s++;
            s->Char.AsciiChar = ',';
        }
        s++;
    }

    i = (s - os);                       // remember how long the number was

    while (j++ < 11) {                  /* Clear off some blank space after */
        s->Char.AsciiChar = ' ';        /* the number.                      */
        s++;
    }

    return i;
}


ULONG heHtou (s)
UCHAR *s;
{
    UCHAR   c;
    ULONG   l;

    l = 0;
    for (; ;) {
        c = *(s++);

        if (c == 's'  ||  c == 'S') {       // Sector multiplier?
            l = l * 512l;
            break;
        }

        if (c >= 'a')       c -= 'a' - 10;
        else if (c >= 'A')  c -= 'A' - 10;
        else                c -= '0';

        if (c > 15)
            break;

        l = (l<<4) + c;
    }
    return l;
}


NTSTATUS
heOpenFile (PUCHAR name, PHANDLE fhan, ULONG access)
{
    *fhan = CreateFile (
            name,
            access,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING,
            0,
            NULL );

    if (*fhan == INVALID_HANDLE_VALUE  &&  (access & GENERIC_WRITE)) {
        *fhan = CreateFile (
                name,
                access,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                NULL,
                CREATE_NEW,
                0,
                NULL );
    }

    return *fhan == INVALID_HANDLE_VALUE ? GetLastError() : STATUS_SUCCESS;
}

NTSTATUS
heReadFile (HANDLE h, PUCHAR buffer, ULONG len, PULONG br)
{
    if (!ReadFile (h, buffer, len, br, NULL))
        return GetLastError();
    return STATUS_SUCCESS;
}

NTSTATUS
heWriteFile (HANDLE h, PUCHAR buffer, ULONG len)
{
    ULONG   bw;

    if (!WriteFile (h, buffer, len, &bw, NULL))
        return GetLastError();
    return (bw != len ? ERROR_WRITE_FAULT : STATUS_SUCCESS);
}