REM Program: Hex Editor v5.5a, Module 3 of 5, PD 2003.
REM Author: Erik Jon Oredson AS. Csci
REM Release: 11/17/2003.
REM Status: Public Domain.
REM Email: eoredson@yahoo.com
REM Urls: www.simtel.net www.filegate.net
REM  www.winsite.com

REM Hexedit Clipboard & ViewFile module source file.

' get include file.
REM $INCLUDE: 'hexedit.inc'

REM Clipboard routines:

SUB Clipboard2(Var)
' declare error trap.
ON LOCAL ERROR GOTO Error.Routine

IF Var = 1 THEN
   GOSUB CopyToClipboard
   EXIT SUB
END IF
IF Var = 2 THEN
   GOSUB PasteFromClipboard
   EXIT SUB
END IF
IF Var = 3 THEN
   GOSUB ClipboardUndo
   EXIT SUB
END IF
IF Var = 4 THEN
   GOSUB ClipboardUndoAll
   EXIT SUB
END IF
TopProgram:
EXIT SUB

' copy hilighted area to clipboard file.
CopyToClipboard:
 IF CopyPositionStart = 0 THEN
    RETURN
 END IF
 ' store clipboard length.
 ByteLength# = CopyPositionEnd - CopyPositionStart + 1#
 IF ByteLength# + 4# > 2147483647# THEN
    StatusMessage = "Copy extends beyond clipboard file length."
    GOSUB DisplayStatus2Q
    RETURN
 END IF
 GOSUB ClearStatusQ
 PRINT "Editing file: "; RTRIM$(Filename); " :";
 PRINT " Copying bytes to clipboard..";
 GOSUB LocateCursor2Q
 Bytes$ = RIGHT$("00000000" + HEX$(ByteLength#), 8)
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 1, 2)))
 PUT #4, 1, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 3, 2)))
 PUT #4, 2, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 5, 2)))
 PUT #4, 3, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 7, 2)))
 PUT #4, 4, CopyByte
 ' write clipboard.
 Temp# = FilePosition
 Temp2# = 4#
 FOR NextByte = CopyPositionStart TO CopyPositionEnd
    SeekPosition = NextByte
    GOSUB LseekFile
    GOSUB ReadFile
    FileByte = Buffer
    Temp2# = Temp2# + 1#
    PUT #4, Temp2#, FileByte
 NEXT
 FilePosition = Temp#
 GOSUB ClearStatusQ
 PRINT "Editing file: "; RTRIM$(Filename); " :";
 UndoToggle = NOT UndoToggle
 IF UndoToggle THEN
    PRINT " (+)";
 ELSE
    PRINT " (X)";
 END IF
 PRINT STR$(ByteLength#) + " bytes copied to clipboard.";
 GOSUB LocateCursor2Q
 RETURN

' paste clipboard to current file position.
PasteFromClipboard:
 ' read clipboard length.
 IF LOF(4) = 0 THEN
    RETURN
 END IF
 ' get clipboard length.
 ByteLength# = 0#
 GET #4, 1, CopyByte
 ByteLength# = ByteLength# + ASC(CopyByte) * 16 ^ 6
 GET #4, 2, CopyByte
 ByteLength# = ByteLength# + ASC(CopyByte) * 16 ^ 4
 GET #4, 3, CopyByte
 ByteLength# = ByteLength# + ASC(CopyByte) * 16 ^ 2
 GET #4, 4, CopyByte
 ByteLength# = ByteLength# + ASC(CopyByte) 
 ' check end of file.
 IF FilePosition + ByteLength# - 1# > FileLength THEN
    StatusMessage = "Paste extends beyond file length."
    GOSUB DisplayStatus2Q
    RETURN
 END IF
 ' clear hilight area.
 IF CopyPositionStart > 0# THEN
    GOSUB ResetHilightBytesQ
 END IF
 ' store paste area to undo clipboard file.
 GOSUB StoreUndoPaste
 IF ValidPaste = 0 THEN
    RETURN
 END IF
 ' write paste bytes.
 Temp# = FilePosition
 LastPage = FilePage
 FOR NextByte = 1# TO ByteLength#
    GET #4, NextByte + 4#, FileByte
    SeekPosition = FilePosition
    GOSUB LseekFile
    GOSUB WriteFile
    GOSUB CalculatePosition1Q
    IF LastPage = FilePage THEN
       GOSUB ClearPageByteQ
    END IF
    IF NextByte < ByteLength# THEN
       FilePosition = FilePosition + 1
    END IF
 NEXT
 FilePosition = Temp#
 GOSUB CalculatePosition1Q
 GOSUB DisplayPageByteQ
 GOSUB ClearStatusQ
 PRINT "Editing file: "; RTRIM$(Filename); " :";
 PRINT STR$(ByteLength#); " bytes ";
 PRINT "(entry #" + MID$(STR$(ByteEntries), 2); ") pasted.";
 GOSUB LocateCursor2Q
 RETURN

' store area to be over-written into paste undo file.
'   see also: Struc.txt describes file format.
StoreUndoPaste:
 IF LEN(5)=0 THEN
    CopyByte = CHR$(0)
    PUT #5, 1, CopyByte
    PUT #5, 2, CopyByte
 END IF
 ValidPaste = -1
 ' get maximum entries.
 ByteEntries = 0
 GET #5, 1, CopyByte
 ByteEntries = ByteEntries + ASC(CopyByte) * 16 ^ 2
 GET #5, 2, CopyByte
 ByteEntries = ByteEntries + ASC(CopyByte) 
 IF ByteEntries >= 32767 THEN
    ValidPaste = 0
    StatusMessage = "Paste undo entries extend beyond file length."
    GOSUB DisplayStatus2Q
    RETURN
 END IF
 ' scan to last entry position in paste undo file.
 Temp2# = 3#
 Temp3 = 1
 DO
    ' check last entry.
    IF Temp3 > ByteEntries THEN
       EXIT DO
    END IF
    ' skip file start position.
    Temp2# = Temp2# + 4#
    LengthOfRecord# = 0#
    GET #5, Temp2#, CopyByte
    LengthOfRecord# = LengthOfRecord# + ASC(CopyByte) * 16 ^ 6
    Temp2# = Temp2# + 1#
    GET #5, Temp2#, CopyByte
    LengthOfRecord# = LengthOfRecord# + ASC(CopyByte) * 16 ^ 4
    Temp2# = Temp2# + 1#
    GET #5, Temp2#, CopyByte
    LengthOfRecord# = LengthOfRecord# + ASC(CopyByte) * 16 ^ 2
    Temp2# = Temp2# + 1#
    GET #5, Temp2#, CopyByte
    LengthOfRecord# = LengthOfRecord# + ASC(CopyByte) 
    Temp2# = Temp2# + LengthOfRecord# + 1#
    Temp3 = Temp3 + 1
 LOOP
 ' get maximum entries.
 TotalBytes# = Temp2# + ByteLength# + 8#
 IF TotalBytes# >= 2147483647# THEN
    ValidPaste = 0
    StatusMessage = "Paste undo extends beyond file length."
    GOSUB DisplayStatus2Q
    RETURN
 END IF
 GOSUB ClearStatusQ
 PRINT "Editing file: "; RTRIM$(Filename); " :";
 PRINT " Pasting bytes from clipboard..";
 GOSUB LocateCursor2Q
 ' store new entry.
 ByteEntries = ByteEntries + 1
 Bytes$ = RIGHT$("0000" + HEX$(ByteEntries), 4)
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 1, 2)))
 PUT #5, 1, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 3, 2)))
 PUT #5, 2, CopyByte
 ' store file position of undo paste area.
 Bytes$ = RIGHT$("00000000" + HEX$(FilePosition), 8)
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 1, 2)))
 PUT #5, Temp2#, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 3, 2)))
 Temp2# = Temp2# + 1#
 PUT #5, Temp2#, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 5, 2)))
 Temp2# = Temp2# + 1#
 PUT #5, Temp2#, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 7, 2)))
 Temp2# = Temp2# + 1#
 PUT #5, Temp2#, CopyByte
 ' store length of undo paste area.
 Bytes$ = RIGHT$("00000000" + HEX$(ByteLength#), 8)
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 1, 2)))
 Temp2# = Temp2# + 1#
 PUT #5, Temp2#, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 3, 2)))
 Temp2# = Temp2# + 1#
 PUT #5, Temp2#, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 5, 2)))
 Temp2# = Temp2# + 1#
 PUT #5, Temp2#, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 7, 2)))
 Temp2# = Temp2# + 1#
 PUT #5, Temp2#, CopyByte
 ' write undo clipboard.
 Temp# = FilePosition
 FOR NextByte = Temp# TO Temp# + ByteLength# - 1#
    SeekPosition = NextByte
    GOSUB LseekFile
    GOSUB ReadFile
    FileByte = Buffer
    Temp2# = Temp2# + 1#
    PUT #5, Temp2#, FileByte
 NEXT
 FilePosition = Temp#
 RETURN

' undo last paste clipboard.
ClipboardUndo:
 IF LEN(5)=0 THEN
    CopyByte = CHR$(0)
    PUT #5, 1, CopyByte
    PUT #5, 2, CopyByte
    ValidPaste = 0
 ELSE
    GOSUB ClearStatusQ
    PRINT "Editing file: "; RTRIM$(Filename); " :";
    PRINT " Undoing bytes from pastefile..";
    GOSUB LocateCursor2Q
    GOSUB UndoClipboard
 END IF
 GOSUB ClearStatusQ
 PRINT "Editing file: "; RTRIM$(Filename); " :";
 IF ValidPaste = 0 THEN
    PRINT " 0 bytes paste undo.";
 ELSE
    PRINT STR$(ByteLength#); " bytes ";
    PRINT "(entry #" + MID$(STR$(ByteEntries + 1), 2); ") paste undo.";
 END IF
 GOSUB LocateCursor2Q
 RETURN

' undo all paste clipboard.
ClipboardUndoAll:
 TotalEntries = 0
 IF LEN(5)=0 THEN
    CopyByte = CHR$(0)
    PUT #5, 1, CopyByte
    PUT #5, 2, CopyByte
 ELSE
    ByteEntries = 0
    GET #5, 1, CopyByte
    ByteEntries = ByteEntries + ASC(CopyByte) * 16 ^ 2
    GET #5, 2, CopyByte
    ByteEntries = ByteEntries + ASC(CopyByte)
    IF ByteEntries > 0 THEN
       TotalEntries = ByteEntries
       GOSUB ClearStatusQ
       PRINT "Editing file: "; RTRIM$(Filename); " :";
       PRINT " Undoing all entries from paste file..";
       GOSUB LocateCursor2Q
       DO
          GOSUB UndoClipboard
          IF ValidPaste = 0 THEN
             EXIT DO
          END IF
       LOOP
    END IF
 END IF
 GOSUB ClearStatusQ
 PRINT "Editing file: "; RTRIM$(Filename); " :";
 PRINT " Total" + STR$(TotalEntries) + " entries in paste undos.";
 GOSUB LocateCursor2Q
 RETURN

' copy last undo paste entry.
UndoClipboard:
 ' get last entry.
 ByteEntries = 0
 GET #5, 1, CopyByte
 ByteEntries = ByteEntries + ASC(CopyByte) * 16 ^ 2
 GET #5, 2, CopyByte
 ByteEntries = ByteEntries + ASC(CopyByte) 
 ValidPaste = -1
 IF ByteEntries = 0 THEN
    ValidPaste = 0
    RETURN
 END IF
 ' scan to last entry position in paste undo file.
 Temp# = FilePosition
 Temp2# = 3#
 Temp3 = 1
 DO
    ' check last entry.
    IF Temp3 = ByteEntries THEN
       EXIT DO
    END IF
    ' skip file start position.
    Temp2# = Temp2# + 4#
    LengthOfRecord# = 0#
    GET #5, Temp2#, CopyByte
    LengthOfRecord# = LengthOfRecord# + ASC(CopyByte) * 16 ^ 6
    Temp2# = Temp2# + 1#
    GET #5, Temp2#, CopyByte
    LengthOfRecord# = LengthOfRecord# + ASC(CopyByte) * 16 ^ 4
    Temp2# = Temp2# + 1#
    GET #5, Temp2#, CopyByte
    LengthOfRecord# = LengthOfRecord# + ASC(CopyByte) * 16 ^ 2
    Temp2# = Temp2# + 1#
    GET #5, Temp2#, CopyByte
    LengthOfRecord# = LengthOfRecord# + ASC(CopyByte) 
    Temp2# = Temp2# + LengthOfRecord# + 1#
    Temp3 = Temp3 + 1
 LOOP
 ' restore last entry.
 ByteEntries = ByteEntries - 1
 Bytes$ = RIGHT$("0000" + HEX$(ByteEntries), 4)
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 1, 2)))
 PUT #5, 1, CopyByte
 CopyByte = CHR$(VAL("&H" + MID$(Bytes$, 3, 2)))
 PUT #5, 2, CopyByte
 ' restore file position of undo paste area.
 NextByte = 0#
 GET #5, Temp2#, CopyByte
 NextByte = NextByte + ASC(CopyByte) * 16 ^ 6
 Temp2# = Temp2# + 1#
 GET #5, Temp2#, CopyByte
 NextByte = NextByte + ASC(CopyByte) * 16 ^ 4
 Temp2# = Temp2# + 1#
 GET #5, Temp2#, CopyByte
 NextByte = NextByte + ASC(CopyByte) * 16 ^ 2
 Temp2# = Temp2# + 1#
 GET #5, Temp2#, CopyByte
 NextByte = NextByte + ASC(CopyByte) 
 ' restore length of undo paste area.
 ByteLength# = 0#
 Temp2# = Temp2# + 1#
 GET #5, Temp2#, CopyByte
 ByteLength# = ByteLength# + ASC(CopyByte) * 16 ^ 6
 Temp2# = Temp2# + 1#
 GET #5, Temp2#, CopyByte
 ByteLength# = ByteLength# + ASC(CopyByte) * 16 ^ 4
 Temp2# = Temp2# + 1#
 GET #5, Temp2#, CopyByte
 ByteLength# = ByteLength# + ASC(CopyByte) * 16 ^ 2
 Temp2# = Temp2# + 1#
 GET #5, Temp2#, CopyByte
 ByteLength# = ByteLength# + ASC(CopyByte) 
 ' undo paste area.
 Temp# = FilePosition
 Temp3# = NextByte
 LastPage = FilePage
 FOR FilePosition = Temp3# TO Temp3# + ByteLength# - 1#
    Temp2# = Temp2# + 1#
    GET #5, Temp2#, FileByte
    SeekPosition = FilePosition
    GOSUB LseekFile
    GOSUB WriteFile
    GOSUB CalculatePosition1Q
    IF LastPage = FilePage THEN
       GOSUB ClearPageByteQ
    END IF
 NEXT
 FilePosition = Temp#
 GOSUB CalculatePosition1Q
 GOSUB DisplayPageByteQ
 RETURN

' read 1 byte from file.
ReadFile:
 InregsX.AX = &H3F00
 InregsX.BX = Handle
 InregsX.CX = 1
 InregsX.DS = VARSEG(Buffer)
 InregsX.DX = VARPTR(Buffer)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' write 1 byte to file.
WriteFile:
 Buffer = FileByte
 InregsX.AX = &H4000
 InregsX.BX = Handle
 InregsX.CX = 1
 InregsX.DS = VARSEG(Buffer)
 InregsX.DX = VARPTR(Buffer)
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' position pointer in file.
LseekFile:
 ' calculate high/low seek position.
 SeekPosition2 = SeekPosition - 1
 High = INT(SeekPosition2 / 65536)
 Low = SeekPosition2 AND 65535
 ' seek position in file.
 InregsX.AX = &H4200
 InregsX.BX = Handle
 InregsX.CX = INT(VAL("&H" + HEX$(High)))
 InregsX.DX = INT(VAL("&H" + HEX$(Low)))
 CALL InterruptX(&H21, InregsX, OutregsX)
 RETURN

' clear top status area
ClearStatusQ:
 COLOR Yellow
 LOCATE 2, 4
 PRINT SPACE$(74);
 LOCATE 2, 4
 RETURN

' display status line message and prompt type 2Q.
DisplayStatus2Q:
 StatusMessage = StatusMessage + " Press <esc> to continue:"
 GOSUB ClearStatusQ
 PRINT StatusMessage;
 GOSUB LocateCursor2Q
 DO
    ' check escape key pressed.
    IF INKEY$ = CHR$(27) THEN
       EXIT DO
    END IF
    ' call mouse subroutine.
    CALL MouseDriver
    ' check left mouse button release.
    IF Mouse.ButtonX THEN
       EXIT DO
    END IF
    Var = ReleaseTime
 LOOP
 GOSUB DisplayFilenameQ
 RETURN

' display name of file being edited.
DisplayFilenameQ:
 GOSUB ClearStatusQ
 PRINT "Editing file: "; RTRIM$(Filename); " ";
 IF FileLength = False THEN
    GOSUB LockedFileQ
    RETURN
 END IF
 GOSUB DisplayPositionQ
 RETURN

' display info on file being edited.
DisplayPositionQ:
 COLOR Yellow
 StringLength = LEN("Editing file: " + RTRIM$(Filename) + " ") + 4
 LOCATE 2, StringLength, 0
 PRINT SPACE$(77 - StringLength);
 LOCATE 2, StringLength, 0
 IF FileLength = False THEN
    GOSUB LockedFileQ
    RETURN
 END IF
 IF CurrentWindow2 = False THEN
    PRINT "(Position:"; STR$(FilePosition - 1); ") ";
 ELSE
    PRINT "(Position: "; RIGHT$("00000000" + HEX$(FilePosition - 1), 8); "H) ";
 END IF
 PRINT "(Ascii:"; STR$(AsciiValue); ") ";
 PRINT "(Hex: "; RIGHT$("00" + HEX$(AsciiValue), 2); "H)";
 GOSUB LocateCursor2Q
 RETURN

' display message for locked file.
LockedFileQ:
 COLOR White
 PRINT "<locked file>";
 LOCATE 1, 1, 0
 RETURN

' select cursor on window.
LocateCursor2Q:
 IF CopyPositionStart = 0 THEN
    GOSUB LocateCursorQ
 ELSE
    GOSUB LocateHilightCursorQ
 END IF
 RETURN

' locate cursor on window.
LocateCursorQ:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 IF CurrentWindow = False THEN
    LOCATE PageRow + 3, Column + 5, 1
 ELSE
    LOCATE PageRow + 3, PageColumn + 54, 1
 END IF
 RETURN

' locate cursor on window for hilighted area.
LocateHilightCursorQ:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 IF CurrentWindow = False THEN
    LOCATE PageRow + 3, Column + 7, 1
 ELSE
    LOCATE PageRow + 3, PageColumn + 55, 1
 END IF
 RETURN

' reset hilight bytes.
ResetHilightBytesQ:
 Temp# = FilePosition
 Temp2# = FilePage
 IF CopyPositionStart < (Temp2# - 1) * 320 + 1 THEN
    CopyPositionStart = (Temp2# - 1) * 320 + 1
 END IF
 IF CopyPositionEnd > (Temp2# - 1) * 320 + 320 THEN
    CopyPositionEnd = (Temp2# - 1) * 320 + 320
 END IF
 IF CopyPositionEnd > FileLength THEN
    CopyPositionEnd = FileLength
 END IF
 FOR FilePosition = CopyPositionStart TO CopyPositionEnd
    GOSUB CalculatePosition1Q
    COLOR White
    GOSUB BytePrintQ
 NEXT
 CopyPositionStart = 0
 CopyPositionEnd = 0
 FilePosition = Temp#
 GOSUB CalculatePosition1Q
 IF PageColumn + 1 <= 20 THEN
    IF FilePosition + 1 <= FileLength THEN
       GOSUB ClearPageByteQ
       PageColumn = PageColumn + 1
       FilePosition = FilePosition + 1
       GOSUB DisplayPageByteQ
    END IF
 END IF
 COLOR Yellow
 GOSUB BytePrintQ
 RETURN

' print the byte.
BytePrintQ:
 SeekPosition = FilePosition
 GOSUB LseekFile
 GOSUB ReadFile
 FileByte = Buffer
 ByteValue = ASC(FileByte)
 GOSUB CalculateColumnQ
 ' display hex byte.
 CALL HMouse
 LOCATE PageRow + 3, Column + 5, 0
 PRINT RIGHT$("00" + HEX$(ByteValue), 2);
 ' check right window toggled.
 IF CurrentWindow2 = False THEN
    ' display ascii byte.
    LOCATE PageRow + 3, PageColumn + 54, 0
    ' skip unprintable characters.
    SELECT CASE ByteValue
    CASE 0, 7, 9 TO 13, 28 TO 32
       PRINT ".";
    CASE ELSE
       PRINT FileByte;
    END SELECT
 END IF
 CALL SMouse
 RETURN

' clear current byte on screen.
ClearPageByteQ:
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 IF CopyPositionStart > 0# THEN
    GOSUB ResetHilightBytesQ
 END IF
 COLOR White
 GOSUB BytePrintQ
 RETURN

' calculate current screen page number, row, and column, given file position.
CalculatePosition1Q:
 FilePage = INT((FilePosition - 1) / 320) + 1
 PageRow = INT((FilePosition - (FilePage - 1) * 320 - 1) / 20) + 1
 PageColumn = INT((FilePosition - (FilePage - 1) * 320) - (PageRow - 1) * 20)
 RETURN

' display current byte being edited.
DisplayPageByteQ:
 ' check filelength.
 IF FileLength = False THEN
    LOCATE 1, 1, 0
    RETURN
 END IF
 ' check hilighted bytes.
 IF CopyPositionStart > 0# THEN
    GOSUB ResetHilightBytesQ
 END IF
 ' display byte.
 COLOR Yellow
 GOSUB BytePrintQ
 ' store the current byte value being edited.
 AsciiValue = ASC(FileByte)
 RETURN

' calculate the spaces between hex value groups.
CalculateColumnQ:
 SELECT CASE PageColumn
 CASE 1 TO 4
    Column = (PageColumn - 1) * 2 + 1
 CASE 5 TO 8
    Column = (PageColumn - 1) * 2 + 2
 CASE 9 TO 12
    Column = (PageColumn - 1) * 2 + 3
 CASE 13 TO 16
    Column = (PageColumn - 1) * 2 + 4
 CASE 17 TO 20
    Column = (PageColumn - 1) * 2 + 5
 END SELECT
 RETURN

' critical error trap.
Error.Routine:
 IF ScreenDrawn THEN
    CLS
 END IF
 ScreenDrawn = False
 COLOR White
 PRINT "Hex Editor Clipboard " + Version + " " + Release + " critical error trap:"
 COLOR Yellow
 ErrorTrap = ERR
 SELECT CASE ERR
 CASE 5
    PRINT "Syntax error." ' should not happen.
 CASE 6
    PRINT "Overflow."
 CASE 9
    PRINT "Subscript out of range."
 CASE 14
    PRINT "Out of string space."
 CASE 24
    PRINT "Device timeout."
 CASE 25
    PRINT "Device fault."
 CASE 27
    PRINT "Out of paper."
 CASE 52
    PRINT "Bad file name or number."
 CASE 53
    PRINT "File not found."
 CASE 54
    PRINT "Bad file mode."
 CASE 55
    PRINT "File already open."
 CASE 57
    PRINT "Device I/O error."
 CASE 58
    PRINT "File already exists."
 CASE 59
    PRINT "Bad record length."
 CASE 61
    PRINT "Disk full."
 CASE 62
    PRINT "Input past eof."
 CASE 63
    PRINT "Bad record length."
 CASE 64
    PRINT "Bad filename."
 CASE 67
    PRINT "Not enough file handles."
 CASE 68
    PRINT "Device unavailable."
 CASE 70
    PRINT "Permission denied."
 CASE 71
    PRINT "Disk not ready."
 CASE 72
    PRINT "Disk-media error."
 CASE 75
    PRINT "Path/File access error."
 CASE 76
    PRINT "Pathname not found."
 CASE 81
    PRINT "Invalid name."
 CASE ELSE
    PRINT "Untrapped error" + STR$(ERR) + "."
 END SELECT
 ' display error prompt.
 COLOR Green
 PRINT "Press R(etry), C(ontinue), Q(uit):";
 ' get keypress.
 DO
    ErrorRespond$ = Nul
    DO
       ErrorRespond$ = INKEY$
       IF LEN(ErrorRespond$) THEN
          EXIT DO
       END IF
    LOOP
    ' parse key.
    SELECT CASE LCASE$(ErrorRespond$)
    CASE "r"
       PRINT "r"
       RESUME TopProgram
    CASE "c"
       PRINT "c"
       RESUME TopProgram
    CASE "q"
       PRINT "q"
       RESUME TopProgram
    END SELECT
 LOOP
END SUB

REM Viewfile routine:

' returns Var=1 to 9 of file number.
SUB Viewfiles(Var)
' declare error trap.
ON LOCAL ERROR GOTO Error.Routine2
 GOSUB StoreArea
 GOSUB DrawMenu
 MenuSelect = 1
 GOSUB DrawCurrentMenuSelection
 CALL SMouse

 ' keyboard/mouse input loop.
 DO
    CharInput$ = ""

    ' call mouse subroutine.
    CALL MouseDriver

    ' check left mouse button.
    IF Mouse.ButtonX THEN
       Mouse.Row = Mouse.RowX
       Mouse.Column = Mouse.ColumnX
       GOSUB MouseButton1Y
    ELSE
       ' check right/middle mouse button.
       IF Mouse.Button2 OR Mouse.Button3 THEN
          GOSUB RestoreArea
          Var = False
          EXIT SUB
       ELSE
          ' check mouse position.
          IF Mouse.Row OR Mouse.Column THEN
             GOSUB MoveMouse
          END IF
       END IF
    END IF
    ' store keyboard buffer.
    CharInput$ = INKEY$
    IF LEN(CharInput$) THEN
       SELECT CASE LEN(CharInput$)
       CASE 1
          SELECT CASE ASC(CharInput$)
          CASE 13 ' Enter
             GOSUB RestoreArea
             IF MenuSelect > NumberFiles THEN
                Var = 0
             ELSE
                Var = MenuSelect
             END IF
             EXIT SUB
          CASE 27 ' Escape
             GOSUB RestoreArea
             Var = False
             EXIT SUB
          CASE 49 TO 57 ' 1 to 9
             GOSUB EraseCurrentMenuSelection
             MenuSelect = ASC(CharInput$) - 48
             GOSUB DrawCurrentMenuSelection
          END SELECT
       CASE 2
          SELECT CASE ASC(RIGHT$(CharInput$, 1))
          CASE 72 ' Up
             GOSUB MenuUp
          CASE 80 ' Down
             GOSUB MenuDown
          END SELECT
       END SELECT
    END IF
    ' release time slice,
    Var = ReleaseTime
 LOOP
 EXIT SUB

' draws menu.
DrawMenu:
 CALL HMouse
 BoxDrawX1 = 4
 BoxDrawX2 = 14
 BoxDrawY1 = 6
 BoxDrawY2 = 21
 BoxDrawLength = 16
 GOSUB DrawBox
 COLOR White, 0
 FOR Var2 = 1 TO 9
    LOCATE 4 + Var2, 7, 0
    PRINT MID$(STR$(Var2), 2); " ";
    IF Var2 > NumberFiles THEN
       PRINT "<none>      ";
    ELSE
       PRINT File(Var2).Filename;
    END IF
 NEXT
 CALL SMouse
 RETURN

' draws box from (BoxDrawX1,BoxDrawY1) To (BoxDrawX2,BoxDrawY2),
'  length of box from left to right being BoxDrawLength.
DrawBox:
 COLOR Yellow
 LOCATE BoxDrawX1, BoxDrawY1, 0
 PRINT CHR$(ULcorner);STRING$(BoxDrawLength - 2, Hline);CHR$(URcorner);
 FOR RowX1 = BoxDrawX1 + 1 TO BoxDrawX2 - 1
    LOCATE RowX1, BoxDrawY1, 0
    PRINT CHR$(Vline);
    LOCATE RowX1, BoxDRawY2, 0
    PRINT CHR$(Vline);
 NEXT
 LOCATE BoxDrawX2, BoxDrawY1, 0
 PRINT CHR$(LLcorner);STRING$(BoxDrawLength - 2, Hline);CHR$(LRcorner);
 COLOR 0, 7
 LOCATE 4, 9, 0
 PRINT "View Files";
 COLOR Yellow
 RETURN

' stores area under menu.
StoreArea:
 CALL HMouse
 RowX1 = 0
 ColumnY1 = 0
 FOR RowX2 = 4 TO 14
    RowX1 = RowX1 + 1
    ColumnY1 = 0
    FOR ColumnY2 = 6 TO 21
       ColumnY1 = ColumnY1 + 1
       ' store ascii character.
       Area1(RowX1, ColumnY1) = SCREEN(RowX2, ColumnY2)
       ' store color. (undocumented: also stores background color).
       Area2(RowX1, ColumnY1) = SCREEN(RowX2, ColumnY2, 1)
    NEXT
 NEXT
 CALL SMouse
 RETURN

' restores area under menu.
RestoreArea:
 CALL HMouse
 RowX1 = 0
 ColumnY1 = 0
 FOR RowX2 = 4 TO 14
    RowX1 = RowX1 + 1
    ColumnY1 = 0
    FOR ColumnY2 = 6 TO 21
       ColumnY1 = ColumnY1 + 1
       LOCATE RowX2, ColumnY2, 1
       ' restore color.
       TempZ = Area2(RowX1, ColumnY1)
       VarB = INT(TempZ / 16)
       VarF = TempZ MOD 16
       COLOR VarF, VarB
       ' restore ascii character.
       PRINT CHR$(Area1(RowX1, ColumnY1));
    NEXT
 NEXT
 CALL SMouse
 RETURN

' moves menu up, wrapping.
MenuUp:
 GOSUB EraseCurrentMenuSelection
 IF MenuSelect = 1 THEN
    MenuSelect = 9
 ELSE
    MenuSelect = MenuSelect - 1
 END IF
 GOSUB DrawCurrentMenuSelection
 RETURN

' moves menu down, wrapping.
MenuDown:
 GOSUB EraseCurrentMenuSelection
 IF MenuSelect = 9 THEN
    MenuSelect = 1
 ELSE
    MenuSelect = MenuSelect + 1
 END IF
 GOSUB DrawCurrentMenuSelection
 RETURN

' removes hilight from current menu selection.
EraseCurrentMenuSelection:
 CALL HMouse
 COLOR White, 0
 GOSUB DisplayCurrentMenuSelection
 COLOR White, 0
 CALL SMouse
 RETURN

' adds hilight to current menu selection.
DrawCurrentMenuSelection:
 CALL HMouse
 COLOR White, 1
 GOSUB DisplayCurrentMenuSelection
 COLOR White, 0
 CALL SMouse
 RETURN

' draws current menu selection.
DisplayCurrentMenuSelection:
 LOCATE 4 + MenuSelect, 7, 0
 PRINT MID$(STR$(MenuSelect), 2); " ";
 IF MenuSelect > NumberFiles THEN
    PRINT "<none>      ";
    GOSUB ClearStatus2Z
    PRINT "View file"; MenuSelect; ": <none>";
 ELSE
    PRINT File(MenuSelect).Filename;
    GOSUB DisplayPath
 END IF
 RETURN

' display path of file being edited.
DisplayPath:
 GOSUB ClearStatus2Z
 Z$ = RTRIM$(File(MenuSelect).ShortFilename)
 CALL Deconcatenate(Z$, 64)
 Z2$ = LEFT$(Z$, 2)
 Z$ = MID$(Z$, 3)
 Var = LEN(Z$)
 DO
    Var = Var - 1
    IF Var = 0 THEN
       Z$ = ""
       EXIT DO
    END IF
    IF MID$(Z$, Var, 1) = "\" THEN
       Z$ = LEFT$(Z$, Var)
       EXIT DO
    END IF
 LOOP
 IF Z$ = "" THEN
    Z$ = "\"
 END IF
 PRINT "Viewing path: "; Z2$ + Z$; " ";
 FileDisplay2 = -1
 RETURN

' clear top status area
ClearStatus2Z:
 COLOR Yellow, 0
 LOCATE 2, 4
 PRINT SPACE$(74);
 LOCATE 2, 4
 RETURN

' process left mouse button.
MouseButton1Y:
 IF Mouse.Row >= 5 AND Mouse.Row <= 13 THEN
    IF Mouse.Column >= 7 AND Mouse.Column <= 20 THEN
       Var = Mouse.Row - 4
       GOSUB RestoreArea
       EXIT SUB
    END IF
 END IF
 RETURN

' process mouse move.
MoveMouse:
 ' check mouse selection boundaries.
 IF Mouse.Row >=5 AND Mouse.Row <= 13 THEN
    IF Mouse.Column >= 7 AND Mouse.Column <= 20 THEN
       IF Mouse.Row - 4 <> MenuSelect THEN
          GOSUB EraseCurrentMenuSelection
          MenuSelect = Mouse.Row - 4
       END IF
       GOSUB DrawCurrentMenuSelection
    END IF
 END IF
 RETURN

TopProgram1:
 EXIT SUB

' critical error trap.
Error.Routine2:
 IF ScreenDrawn THEN
    CLS
 END IF
 ScreenDrawn = False
 COLOR White
 PRINT "Hex Editor Viewfile " + Version + " " + Release + " critical error trap:"
 COLOR Yellow
 ErrorTrap = ERR
 SELECT CASE ERR
 CASE 5
    PRINT "Syntax error." ' should not happen.
 CASE 6
    PRINT "Overflow."
 CASE 9
    PRINT "Subscript out of range."
 CASE 14
    PRINT "Out of string space."
 CASE 24
    PRINT "Device timeout."
 CASE 25
    PRINT "Device fault."
 CASE 27
    PRINT "Out of paper."
 CASE 52
    PRINT "Bad file name or number."
 CASE 53
    PRINT "File not found."
 CASE 54
    PRINT "Bad file mode."
 CASE 55
    PRINT "File already open."
 CASE 57
    PRINT "Device I/O error."
 CASE 58
    PRINT "File already exists."
 CASE 59
    PRINT "Bad record length."
 CASE 61
    PRINT "Disk full."
 CASE 62
    PRINT "Input past eof."
 CASE 63
    PRINT "Bad record length."
 CASE 64
    PRINT "Bad filename."
 CASE 67
    PRINT "Not enough file handles."
 CASE 68
    PRINT "Device unavailable."
 CASE 70
    PRINT "Permission denied."
 CASE 71
    PRINT "Disk not ready."
 CASE 72
    PRINT "Disk-media error."
 CASE 75
    PRINT "Path/File access error."
 CASE 76
    PRINT "Pathname not found."
 CASE 81
    PRINT "Invalid name."
 CASE ELSE
    PRINT "Untrapped error" + STR$(ERR) + "."
 END SELECT
 ' display error prompt.
 COLOR Green
 PRINT "Press R(etry), C(ontinue), Q(uit):";
 ' get keypress.
 DO
    ErrorRespond$ = Nul
    DO
       ErrorRespond$ = INKEY$
       IF LEN(ErrorRespond$) THEN
          EXIT DO
       END IF
    LOOP
    ' parse key.
    SELECT CASE LCASE$(ErrorRespond$)
    CASE "r"
       PRINT "r"
       RESUME TopProgram1
    CASE "c"
       PRINT "c"
       RESUME TopProgram1
    CASE "q"
       PRINT "q"
       RESUME TopProgram1
    END SELECT
 LOOP
END SUB
