Once in a while some people (including myself) feel a need to modify several bytes in some binary files. Traditionally, piece of software called binary editor is used to complete this task.
In most cases J itself can be used as binary editor. For example,
D=.1!:1 <'temp\data.bin' NB. read data from file
a. i. 0 1{ D NB. inspect data
D=.'BM' 0 1}D NB. modify data
D 1!:2 <'temp\data.bin' NB. write data back to diskHowever, sometimes the file either is too big to comfortably fit in memory or it is desirable to have better view of bytes.
NB. Hex editor for string noun «history» require 'jzgrid jmf' «hexview class» cocurrent 'base' «startup cover verbs»
hexview class
The functionality of an editor is implemented in hexview class. It consists of a form with a single grid control in it. Grid control displays contents of a desired noun.
coclass 'hexview'
Formatting verbs are used to format
- a) address b) data c) top row of offsets
Formatting verbs together with menuitem IDs and captions are stored in VIEWFORMATS noun. VIEWFORMATS is used to build a menu and performa actual string transformations.
hexformat=:'0123456789abcdef' {~ ] #:~ 16 #~ [
VIEWFORMATS=:i.0 0
VIEWFORMATS=:VIEWFORMATS,'hex';'&Hex';(8&hexformat)`(2&hexformat)`(1&hexformat)
VIEWFORMATS=:VIEWFORMATS,'dec';'&Decimal';":`":`":
addr_format=:3 : '(FMT{VIEWFORMATS)@.2 y'
data_format=:3 : '(FMT{VIEWFORMATS)@.3 y'
col_format=: 3 : '(FMT{VIEWFORMATS)@.4 y'
VIEWCPS=:i.0 0 VIEWCPS=:VIEWCPS, 'ascii';'&ASCII';16 16$'.' ((i.32),127+i.129)}2 u: a. VIEWCPS=:VIEWCPS, 'iso88591';'&ISO8859-1'; '.' (,0 128+/i.32)} 2 u: a.
Form definition noun is split in two so that View menu can be constructed from VIEWFORMATS and VIEWCPS.
HEXVIEW_MENU=: 0 : 0 pc hexview; menupop "Data"; menu file "&File" "Ctrl-O" "" ""; menu noun "&Noun" "Ctrl-N" "" ""; menusep; menu cancel "&Exit" "" "" ""; menupopz; menupop "Edit"; menu goto "&Go to..." "Ctrl-G" "" ""; menusep; menu apply "&Apply changes" "" "" ""; menupopz; menupop "View"; ) HEXVIEW_FORM=: 0 : 0 menupopz; xywh 5 5 420 120;cc grid isigraph rightmove bottommove; pas 5 5;pcenter; rem form end; )
(The width of grid control was determined by issuing wd 'qchildxywh grid;' after manually resizing form so that grid fits with a little extra room)
create=: 3 : 0
wd HEXVIEW_MENU
([: wd 'menu ', >@(0&{), ' "' , >@(1&{) , '" "" "" "";'"_)"1 VIEWFORMATS
wd 'menusep;'
([: wd 'menu ', >@(0&{), ' "' , >@(1&{) , '" "" "" "";'"_)"1 VIEWCPS
wd HEXVIEW_FORM
grid=:conew 'jzgrid'
WIDTH is number of bytes per row of display, Grid control displays 1+WIDTH columns, where last column is text representation of WIDTH bytes shown in this row.
WIDTH=:16
DATA is character noun holding the bytes to display. This noun can be mapped to a file on disk to view files.
DATA=:y
Table of views defines how data is displayed. It can be hex or decimal. FMT is index in the table of viewed. Another parameter affecting display is CP -- index in list of codepages to use in text representation of the data.
FMT=:0 CP=:0
In case DATA is a mapped to a file, the name of this file is stored in FILE.
FILE=:''
Grid operates in virtual mode. This enables use of mapped nouns to view large files.
CELLRANGE__grid=:1 0+<:WIDTH ([ , *@| + <.@%~) # DATA
HDRTOP__grid=:<'Address'
HDRCOL__grid=:<@(col_format)"0 i.WIDTH
HDRCOL__grid=:HDRCOL__grid,<'Text'
GRIDVIRTUALMODE__grid=:1
CELLFONT__grid=:1
CELLCOLORS__grid=:CELLCOLORS__grid,24$192 192 192
wd 'set ',(>0{FMT{VIEWFORMATS),' 1'
wd 'set ',(>0{CP{VIEWCPS),' 1'
show__grid ''
wd 'pshow;'
)
Destructor
When object is destroyed it first closes the form, unmaps mapped file, if any, and then destroys member grid.
destroy=:3 : 0 wd 'pclose' closefile '' destroy__grid'' codestroy '' )
Form's close and cancel events simply destroy parent object, which also takes care of GUI.
hexview_close=: 3 : 0 destroy '' )
hexview_cancel_button=: destroy
Event processing
grid_gridhandler=:3 : 0
select. y
case. 'get' do.
NB. smoutput Cls__grid;Rws__grid
i=.(WIDTH*Rws__grid) +/ }:Cls__grid
t=.<@(data_format)@(a.&i.)"0 DATA{~(<:#DATA)<.i
text=.(,>2{CP{VIEWCPS) <@:(7&u:)@:{"1 ~a.i.DATA{~(<:#DATA)<.i
CELLCOLOR__grid=:(2 1{~i<#DATA),.0
CELLEDIT__grid=:(i<#DATA),.0
CELLDATA__grid=:t,.((<"1 i<#DATA) (8 u: #)&.> text)
HDRROW__grid=:<@(addr_format)"0 Rws__grid*WIDTH
HDRCOL__grid=:<@(col_format)"0 i.WIDTH
HDRCOL__grid=:HDRCOL__grid,-.&'&'&.>1{CP{VIEWCPS
end.
1
)
Since grid control accepts data in utf8, 8&u: had to be issued before assigning formatted text to CELLDATA.
---
closefile=:3 : 0
if. -.''-:FILE do.
n=.'DATA_',(>coname''),'_'
smoutput 'closing ',FILE,' as ',n
unmap_jmf_ n
FILE=:''
wd 'pn *'
end.
)
hexview_file_button=: 3 : 0
FILE=:wd 'mbopen "Select file" "" "" "All files (*.*)|*.*" ofn_filemustexist'
JCHAR map_jmf_ ('DATA_',(>coname''),'_');FILE
CELLRANGE__grid=:1 0+<:WIDTH ([ , *@| + <.@%~) # DATA
wd 'pn *',FILE
)
Since items of View menu are generated from nouns, default driver provides processing events from these elements -- change of format or change of displayed codepage.
hexview_default=:3 : 0
NB. smoutput syschild,' ',sysevent
if. (<syschild) e. 0{"1 VIEWFORMATS do.
i=.(<syschild) i.~ 0{"1 VIEWFORMATS
FMT=:i
([: wd 'set ' , ] , ' 0'"_)&> 0{"1 VIEWFORMATS
wd 'set ',(>0{i{VIEWFORMATS),' 1'
show__grid''
elseif. (<syschild) e. 0&{"1 VIEWCPS do.
i=.(<syschild) i.~ 0&{"1 VIEWCPS
CP=:i
([: wd 'set ' , ] , ' 0'"_)&> 0{"1 VIEWCPS
wd 'set ',(>0{i{VIEWCPS),' 1'
show__grid''
end.
)
Cover verbs
newhexview=:3 : 0
a=.a:
a=.conew 'hexview'
try.
create__a y
catchd.
smoutput 13!:12 ''
destroy__a ''
a=.a:
end.
a
)
z=:newhexview a.
History
NB. 2007-10-10 12:51:11 NB. 2007-11-14 16:48:53
TODO and comments
- allow updates; store list of addresses and modifications and apply in a
single step (or save delta to a file)
- (?) noun browser
- Goto address
- Search
- Copy/paste
