'============
'XE.BAS v1.11
'============
'A simple File editor/viewer.
'Coded by Dav, FEB/2021 with QB64-GL v1.4.
'
'==========================================================================
'* * * * USE THIS PROGRAM AT YOUR OWN RISK ONLY!! * * * *
'==========================================================================
'
' New in XE v1.11:
' ~~~~~~~~~~~~~~~
'
' * FIXED: Changed DEF SEG/POKE to PRINT calls - using _CONTROLCHR OFF now.
' * FIXED: Removed Windows API calls to let XE compile on other Platforms
' * ADDED: Now uses a FileSelect box, instead of having to type filename.
' * FIXED: Shows long file name at top, instead of the short 8.3 filename.
' * FIXED: Cleaned up code vastly to make it seem like a human wrote it.
' (Still a lot more to do...)
'
' THINGS TO DO:
' Add HEX Searching too.
' Add TEXT view for reading text files?
' Add a Create File option?
' Add (I) Info - Display File Information
' Add a File Copy to location...
' ...or more file manager like stuff
' Highlight found text when searching
'
'==========================================================================
'
' ABOUT:
' ~~~~~
'
' XE is a simple Binary File Editor (also called a HEX editor) that lets
' you view and edit raw data bytes of a file. With XE you can peek inside
' EXE/DLL files and see what information they may contain. XE also has the
' capacity to change bytes by either typing in ASCII characters or entering
' the HEX value for each byte. XE was first coded in Qbasic - now in QB64.
'
' Since the very nature of XE is to alter file data you should always use
' EXTREME caution when editing any file - AND ALWAYS MAKE A BACKUP FIRST!
'
'==========================================================================
'
' HOW TO USE:
' ~~~~~~~~~~
'
' XE accepts command line arguments. You can drag/drop a file onto XE.
' If you don't specify a filename on startup, XE will ask you for one.
'
' There are TWO ways to View & Edit files - in HEX (default) or ASCII mode.
'
' Files are first opened in HEX mode displaying 2 windows of data. The
' right window shows the charaters while the larger left window shows HEX
' values for them. HEX mode is best for patching and is the only way to
' edit the HEX values of bytes.
'
'
' Pressing ENTER switches to ASCII (non-HEX) mode, showing a larger page
' of raw data bytes - the ASCII chracter data only. This mode is best for
' skimming through files faster. ENTER toggles view modes back and forth.
'
' While viewing a file you can browse through the file using the ARROWS,
' PAGEUP/DOWN, HOME and the END keys.
'
' The currently opened filename is shown with full path in the title bar.
' and just filename is displayed in the FILE: area just below title bar.
'
' While viewing a file, press E to enter into EDIT mode and begin editing
' bytes at the current position. If you're in HEX mode (2 windows), you can
' edit bytes either by typing characters on the right side or entering HEX
' values on the left window. Press TAB to switch windows to edit in.
' Press ESC to save or disgard changes and to exit editing mode.
'
' Press M for a complete MENU listing all of the Key COMMANDS.
'
'==========================================================================
'
' COMMAND:
' ~~~~~~~~
'
' E = Enters EDIT MODE. Only the displayed bytes can be edited.
'
' TAB = Switchs panes (the cursor) while editing in HEX mode.
'
' S = Searches file for a string starting at the current byte.
' A Match-Case option is available. A high beep alerts you
' when match is found. A Low beep sounds when EOF reached.
'
' N = Finds NEXT Match after a do a string search.
'
' F = Toggles FILTERING of all non-standard-text characters.
' A flashing "F" is at the top-left corner when FILTER ON.
'
' G = GOTO a certain byte position (number) in the file.
'
' L = GOTO a specified location (Hex value) of the file.
'
' ENTER = Toggles HEX and ASCII view modes. The ASCII mode lets
' you browse more data per page. You can EDIT in both
' modes but can only enter in HEX vaules in HEX mode.
'
' ESC = EXITS out of editing mode, and also EXITS the program.
'
' ALT+ENTER = Toggle FULLSCREEN/WINDOWED mode of the XE program.
'
'==========================================================================
'==========================================================================
'==========================================================================
'SETUP SCREEN MODE
'=================
SCREEN Pete:
WIDTH 80, 25 'Use Screen mode 0, aka the Pete... _CONTROLCHR OFF 'Printing all 255 characters on screen, so this is needed.
_TITLE "XE v1.11" 'Everything has a name
'==========================================================================
'LOAD FILE
'=========
File$ = FileSelect$(5, 10, 15, 55, "*.*")
PRINT "No file selected."
PRINT "XE v1.11 - Binary file editor." PRINT File$;
" not found!"
File$
= LTRIM$(RTRIM$(File$
)) 'trim off any spaces is any...FullFileName$ = File$ 'make a copy For TITLE/OPEN to use...
'If filename+path too long for display, strip off path
ts$ = ""
ts$ = t$ + ts$
File$ = ts$
'If filename too long, shorten it for display
File$
= MID$(File$
, 1, 67) + "..."
'==========================================================================
'OPEN FILE
'=========
_TITLE "XE v1.11 - " + FullFileName$
DisplayView% = 1 'Default to 2-PANE view
ByteLocation& = 1
BufferSize% = (16 * 23)
BufferSize% = (79 * 23)
'==========================================================================
'DISPLAY FILE
'============
PageOfData$
= INPUT$(BufferSize%
, 7)
'If dual pane mode....
PageFlag%
= 1: PageLimit%
= LEN(PageOfData$
) PageOfData$
= PageOfData$
+ STRING$(16 * 23 - LEN(PageOfData$
), CHR$(0)) 'show right side
y% = 3: x% = 63
CurrentByte%
= ASC(MID$(PageOfData$
, c%
, 1)) 'show a . instead of a null (looks better to me)
IF CurrentByte%
= 0 THEN CurrentByte%
= 46 CASE 0 TO 31, 123 TO 255: CurrentByte%
= 32 x%
= x%
+ 1:
IF x%
= 79 THEN x%
= 63: y%
= y%
+ 1 'show left side
y% = 3: x% = 15
CurrentByte%
= ASC(MID$(PageOfData$
, c%
, 1)) CurrentByte$
= HEX$(CurrentByte%
):
IF LEN(CurrentByte$
) = 1 THEN CurrentByte$
= "0" + CurrentByte$
x%
= x%
+ 3:
IF x%
>= 62 THEN x%
= 15: y%
= y%
+ 1 'One page display, Full view
'Adjust data size used
IF LEN(PageOfData$
) < (79 * 23) THEN 'Enough to fill screen? PageFlag%
= 1: PageLimit%
= LEN(PageOfData$
) 'No? Mark this and pad PageOfData$
= PageOfData$
+ SPACE$(79 * 23 - LEN(PageOfData$
)) 'data with spaces. y% = 3: x% = 1 'Screen location where data begins displaying
FOR c%
= 1 TO LEN(PageOfData$
) 'Show all the bytes. CurrentByte%
= ASC(MID$(PageOfData$
, c%
, 1)) 'Check the ASCII value. IF Filter%
= 1 THEN 'If Filter is turned on, SELECT CASE CurrentByte%
'changes these values to spaces CASE 0 TO 32, 123 TO 255: CurrentByte%
= 32 'This line calculates when to go to next row.
x%
= x%
+ 1:
IF x%
= 80 THEN x%
= 1: y%
= y%
+ 1
GOSUB DrawTopBar
'update viewing info at top
'Get user input
K$ = L$: L$ = ""
INPUT " GOTO BYTE# > ", GotoByte$
TMP$ = ""
G$
= MID$(GotoByte$
, m%
, 1) 'to numerical vales CASE 48 TO 57: TMP$
= TMP$
+ G$
IF GotoByte$
<> "" THEN ByteLocation&
= 0 + VAL(GotoByte$
) LOCATE 1, 3:
'PRINT "TOTAL BYTES>"; LOF(7) INPUT " GOTO HEX LOCATION-> ", GotoByte$
GotoByte$ = "&H" + GotoByte$
IF GotoByte$
<> "" THEN ByteLocation&
= 0 + VAL(GotoByte$
) PRINT " CASE sensitive (Y/N)? ";
DisplayView% = 0
BufferSize% = (79 * 23)
DisplayView% = 1
BufferSize% = (16 * 23)
IF ByteLocation&
> 15 THEN ByteLocation&
= ByteLocation&
- 16 IF ByteLocation&
> 78 THEN ByteLocation&
= ByteLocation&
- 79 IF ByteLocation&
< LOF(7) - 15 THEN ByteLocation&
= ByteLocation&
+ 16 IF ByteLocation&
< LOF(7) - 78 THEN ByteLocation&
= ByteLocation&
+ 79 CASE CHR$(0) + CHR$(73): ByteLocation&
= ByteLocation&
- BufferSize%:
IF ByteLocation&
< 1 THEN ByteLocation&
= 1 CASE CHR$(0) + CHR$(81):
IF ByteLocation&
< LOF(7) - BufferSize%
THEN ByteLocation&
= ByteLocation&
+ BufferSize%
'==========================================================================
' GOSUB ROUTINES
'==========================================================================
'==========================================================================
Search:
'======
B$
= INPUT$(BufferSize%
, 7): ByteLocation&
= ByteLocation&
+ BufferSize%
ByteLocation&
= ByteLocation&
- LEN(s$
)
'==========================================================================
EditRightSide: 'Editing Right side info in dual pane mode
'============
Pane% = 1
leftx% = 15
IF test%
= 15 OR test%
= 16 THEN x%
= 63: leftx%
= 15 IF test%
= 18 OR test%
= 19 THEN x%
= 64: leftx%
= 18 IF test%
= 21 OR test%
= 22 THEN x%
= 65: leftx%
= 21 IF test%
= 24 OR test%
= 25 THEN x%
= 66: leftx%
= 24 IF test%
= 27 OR test%
= 28 THEN x%
= 67: leftx%
= 27 IF test%
= 30 OR test%
= 31 THEN x%
= 68: leftx%
= 30 IF test%
= 33 OR test%
= 34 THEN x%
= 69: leftx%
= 33 IF test%
= 36 OR test%
= 37 THEN x%
= 70: leftx%
= 36 IF test%
= 39 OR test%
= 40 THEN x%
= 71: leftx%
= 39 IF test%
= 42 OR test%
= 43 THEN x%
= 72: leftx%
= 42 IF test%
= 45 OR test%
= 46 THEN x%
= 73: leftx%
= 45 IF test%
= 48 OR test%
= 49 THEN x%
= 74: leftx%
= 48 IF test%
= 51 OR test%
= 52 THEN x%
= 75: leftx%
= 51 IF test%
= 54 OR test%
= 55 THEN x%
= 76: leftx%
= 54 IF test%
= 57 OR test%
= 58 THEN x%
= 77: leftx%
= 57 IF test%
= 60 OR test%
= 61 THEN x%
= 78: leftx%
= 60
Pane%
= 2:
GOTO EditLeftSide
Pane%
= 1:
GOTO EditRightSide
IF (ByteLocation&
+ ((y%
- 3) * 16 + x%
- 1) - 62) <= LOF(7) AND E$
<> CHR$(8) THEN changes% = 1
'new color for changed bytes...
CurrentByte$
= HEX$(ASC(E$
)):
IF LEN(CurrentByte$
) = 1 THEN CurrentByte$
= "0" + CurrentByte$
MID$(PageOfData$
, ((y%
- 3) * 16 + x%
* 1) - 62) = E$
IF x%
< 78 THEN x%
= x%
+ 1: leftx%
= leftx%
+ 3 'skip space
'==========================================================================
SaveChanges:
'===========
IF PageFlag%
= 1 THEN PageOfData$
= LEFT$(PageOfData$
, PageLimit%
) PUT #7, ByteLocation&
, PageOfData$:
'==========================================================================
EditLeftSide: 'Editing Left side info in dual pane mode
'===========
x% = 15: 'y% = 3
rightx% = 63
IF test%
= 63 THEN x%
= 15: rightx%
= 63 IF test%
= 64 THEN x%
= 18: rightx%
= 64 IF test%
= 65 THEN x%
= 21: rightx%
= 65 IF test%
= 66 THEN x%
= 24: rightx%
= 66 IF test%
= 67 THEN x%
= 27: rightx%
= 67 IF test%
= 68 THEN x%
= 30: rightx%
= 68 IF test%
= 69 THEN x%
= 33: rightx%
= 69 IF test%
= 70 THEN x%
= 36: rightx%
= 70 IF test%
= 71 THEN x%
= 39: rightx%
= 71 IF test%
= 72 THEN x%
= 42: rightx%
= 72 IF test%
= 73 THEN x%
= 45: rightx%
= 73 IF test%
= 74 THEN x%
= 48: rightx%
= 74 IF test%
= 75 THEN x%
= 51: rightx%
= 75 IF test%
= 76 THEN x%
= 54: rightx%
= 76 IF test%
= 77 THEN x%
= 57: rightx%
= 77 IF test%
= 78 THEN x%
= 60: rightx%
= 78
Pane%
= 2:
GOTO EditLeftSide
Pane%
= 1:
GOTO EditRightSide
CASE 17, 18, 20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63 x% = x% - 2
rightx% = rightx% - 1
CASE 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62 x% = x% + 2
rightx% = rightx% + 1
IF (ByteLocation&
+ ((y%
- 3) * 16 + rightx%
- 1) - 62) <= LOF(7) AND E$
<> CHR$(8) THEN CASE "A", "B", "C", "D", "E", "F", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" changes% = 1
CASE 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62 'reflect changes on right panel
MID$(PageOfData$
, ((y%
- 3) * 16 + rightx%
* 1) - 62) = e2$
'dont advance cursor if at last place
rightx% = rightx% + 1
x% = x% + 2
'==========================================================================
EditFullView: 'Editing file in full display mode (one pane)
'===========
x% = 1: y% = 3
changes% = 0
changes% = 1
'new color for changed bytes
MID$(PageOfData$
, (y%
- 3) * 79 + x%
* 1) = E$
'==========================================================================
DrawEditBar:
'===========
PRINT " Press TAB to switch editing sides ";
CHR$(179);
" Arrows move cursor ";
CHR$(179);
" ESC=Exit ";
CurrentByte& = ByteLocation& + (y% - 3) * 79 + x% - 1
CurrentValue%
= ASC(MID$(PageOfData$
, (y%
- 3) * 79 + x%
* 1, 1))
'==========================================================================
DrawTopBar:
'============
EC&
= ByteLocation&
+ BufferSize%:
IF EC&
> LOF(7) THEN EC&
= LOF(7)'Draw bar on right side of screen
'Draw lines down screen
'add space around numbers...
'(full screen messes it...)
'Draw location
nm$
= HEX$(ByteLocation&
- 32 + (d%
* 16))
Marker%
= CINT(ByteLocation&
/ LOF(7) * 22)
'==========================================================================
Menu:
'========
LOCATE 6, 26:
PRINT "Use the arrow keys, page up/down";
LOCATE 9, 26:
PRINT "E = Enter into file editing mode";
LOCATE 10, 26:
PRINT "F = Toggles the filter ON or OFF";
LOCATE 11, 26:
PRINT "G = Goto a certain byte position";
LOCATE 12, 26:
PRINT "L = Goto a certain HEX location";
LOCATE 13, 26:
PRINT "S = Searches for string in file";
LOCATE 14, 26:
PRINT "N = Find next match after search";
LOCATE 20, 26:
PRINT "ALT+ENTER for full screen window";
'==========================================================================
' FUNCTIONS/SUBS
'==========================================================================
FUNCTION FileSelect$
(y
, x
, y2
, x2
, Filespec$
)
'=== save original place of cursor
'=== save colors
'=== Save whole screen
_MEMCOPY scr1
, scr1.OFFSET
, scr1.SIZE
TO scr2
, scr2.OFFSET
loadagain:
top = 0
selection = 0
'get list...
REDIM FileNames$
(5000) 'space for 5000 filenames
'only show the ".." if not at root dir
FileNames$(0) = ".."
LineCount = 1
LineCount = 0
FileNames$(LineCount) = "[" + rl$ + "]"
LineCount = LineCount + 1
'KILL "$_TeMP_FILE_LIST"
SHELL _HIDE "dir /b /A:-D " + Filespec$
+ " > $_TeMP_FILE_LIST"
'skip the temp file...
IF rl$
<> "$_TeMP_FILE_LIST" THEN FileNames$(LineCount) = rl$
LineCount = LineCount + 1
'KILL "$_TeMP_FILE_LIST"
'draw a box
'show current working dir
'Shorten it is too long, for display purposes
CurDir$
= MID$(CurDir$
, 1, x2
- x
- 3) + "..."
'directories get a different color...
IF selection
> 0 THEN selection
= selection
- 1 IF selection
< top
THEN top
= selection
IF selection
< (LineCount
- 1) THEN selection
= selection
+ 1 IF selection
> (top
+ (y2
- 2)) THEN top
= selection
- y2
+ 1 top = top - y2
selection = selection - y2
IF selection
< 0 THEN selection
= 0 top = top + y2
selection = selection + y2
IF top
>= LineCount
- y2
THEN top
= LineCount
- y2
IF selection
>= LineCount
THEN selection
= LineCount
- 1 top = 0: selection = 0
selection = LineCount - 1
top = selection - y2 + 1
FileSelect$ = ""
'go up one dir
'see if directory
test$
= RTRIM$(FileNames$
(selection
)) test$
= MID$(test$
, 2, LEN(test$
) - 2) FileSelect$
= C$
+ RTRIM$(FileNames$
(selection
))
'=== Restore the whole screen
_MEMCOPY scr2
, scr2.OFFSET
, scr2.SIZE
TO scr1
, scr1.OFFSET
'=== restore original y,x