loadmodule "luaiup" loadmodule "luahandle" loadmodule "lualibs" load "iupe-dbuffer.lua" if not iupe then iupe = {} end if not iupep then iupep = {} end local cursorcolors = { GREEN = cd.EncodeColor(0, 224, 0), LIGHTGREEN = cd.EncodeColor(224, 255, 224), } local mousecolors = { YELLOW = cd.EncodeColor(255, 255, 0), LIGHTYELLOW = cd.EncodeColor(255, 255, 192), } markercolors = { [-2] = cursorcolors.GREEN, [-1] = mousecolors.YELLOW, [0] = cd.EncodeColor(0, 255, 178), -- 0.45 [1] = cd.EncodeColor(0, 255, 255), -- 0.50 [2] = cd.EncodeColor(0, 178, 255), -- 0.55 [3] = cd.EncodeColor(0, 102, 255), -- 0.60 [4] = cd.EncodeColor(0, 25, 255), -- 0.65 [5] = cd.EncodeColor(50, 0, 255), -- 0.70 [6] = cd.EncodeColor(127, 0, 255), -- 0.75 [7] = cd.EncodeColor(204, 0, 255), -- 0.80 [8] = cd.EncodeColor(255, 0, 229), -- 0.85 [9] = cd.EncodeColor(255, 0, 152), -- 0.90 [10] = cd.EncodeColor(255, 0, 76), -- 0.95 } iupep.hexview = { gridsize = { x = 8, y = 14 }, printgrid = function (self, msg, y, x) local cvdb = self.cvdb if not x then x = self.textcursor.x end if not y then y = self.textcursor.y end cvdb:Foreground(cd.BLACK) cvdb:Text(x * self.gridsize.x, self.height - self.gridsize.y * (y + 1), msg) self.textcursor.x = x + msg:len() self.textcursor.y = y end, colorgrid = function (self, color, len, y, x, height) local cvdb = self.cvdb if not len then len = 1 end if not height then height = 1 end if not x then x = self.textcursor.x end if not y then y = self.textcursor.y end cvdb:Foreground(color) local xmin, xmax, ymin, ymax = x * self.gridsize.x, (x + len) * self.gridsize.x - 1, self.height - self.gridsize.y * (y + 1 - height) - 2, self.height - self.gridsize.y * (y + 1) - 1 cvdb:Box(xmin, xmax, ymin, ymax) end, byte2char = function(self, b) if b >= 32 and b <= 127 then return string.char(b) else return "." end end, draw = function (self) local cvdb = self.cvdb if not cvdb then return iup.DEFAULT end cvdb:Clear() local handle = self.handle local filecursor = self.filecursor + 0 local nbbytes = self.nbbytes + 0 local filesize = self.filesize + 0 local oldfilecursor = self.oldfilecursor + 0 local buf = self.buf local max_bytes = math.max(math.min(filesize - filecursor, nbbytes), 0) local old_max_bytes = (self.old_max_bytes or -1) + 0 if handle then handle:seek(filecursor) if ((filecursor ~= oldfilecursor or max_bytes ~= old_max_bytes) and max_bytes ~= 0) or not buf then if buf then buf:destroy() end buf = Buffer(true) self.buf = buf handle:copyto(buf, max_bytes) self.oldfilecursor = filecursor self.old_max_bytes = max_bytes else buf:seek(0) end end local line = 0 local rcursor = 0 local b local kline, kcolumn local mline, mcolumn local krcursor = self.kcursor - filecursor local mrcursor = self.mcursor - filecursor local columns = self.columns + 0 local nblines = self.nblines + 0 kline = math.floor(krcursor / columns) kcolumn = krcursor % columns mline = math.floor(mrcursor / columns) mcolumn = mrcursor % columns if self.mcursor ~= "-1" then self:colorgrid(mousecolors.LIGHTYELLOW, 12 + columns * 4, mline, 0) self:colorgrid(mousecolors.LIGHTYELLOW, 2, nblines - 1, mcolumn * 3 + 10, nblines) self:colorgrid(mousecolors.LIGHTYELLOW, 1, nblines - 1, mcolumn + 12 + columns * 3, nblines) self:colorgrid(mousecolors.YELLOW, 2, mline, mcolumn * 3 + 10) self:colorgrid(mousecolors.YELLOW, 1, mline, mcolumn + 12 + columns * 3) end self:colorgrid(cursorcolors.LIGHTGREEN, 12 + columns * 4, kline, 0) self:colorgrid(cursorcolors.LIGHTGREEN, 2, nblines - 1, kcolumn * 3 + 10, nblines) self:colorgrid(cursorcolors.LIGHTGREEN, 1, nblines - 1, kcolumn + 12 + columns * 3, nblines) self:colorgrid(cursorcolors.GREEN, 2, kline, kcolumn * 3 + 10) self:colorgrid(cursorcolors.GREEN, 1, kline, kcolumn + 12 + columns * 3) local marker, len, mlen for i = 0, 10 do marker = self.markers[i] or -1 marker = marker - filecursor mlen = self.markerslengths[i] if marker >= 0 and self.showmarkerslengths and mlen and mlen > 0 then len = math.min(mlen, nbbytes - marker) elseif marker >= 0 and marker < nbbytes then len = 1 else len = 0 end for j = 1, len do mline = math.floor(marker / columns) mcolumn = marker % columns self:colorgrid(markercolors[i], 2, mline, mcolumn * 3 + 10) self:colorgrid(markercolors[i], 1, mline, mcolumn + 12 + columns * 3) marker = marker + 1 end end while rcursor < max_bytes do local dstring = "" self:printgrid(hex(buf:tell() + filecursor, "%08x "), line, 0) for c = 1, columns do b = buf:readU8() self:printgrid(hex(b, "%02x ")) dstring = dstring .. self:byte2char(b) rcursor = rcursor + 1 if rcursor >= max_bytes then break end end self:printgrid(dstring, line, 12 + columns * 3) line = line + 1 end cvdb:Flush() return iup.DEFAULT end, updatescrollbar = function (self) self.ymax = math.floor(self.filesize / self.columns) self.dy = 1 self.posy = math.floor(self.filecursor / self.columns) iup.Update(self) end, resize_cb = function (self, width, height) local displaylines = self.displaylines + 0 self.nblines = displaylines > 0 and displaylines or math.floor(height / self.gridsize.y) self.nbbytes = self.nblines * self.columns self:updatescrollbar() iupep.dbuffer.resize_cb(self, width, height) end, keypress_cb = function (self, c, press) if press == 0 then return iup.DEFAULT end local kaction = false local raction = false local kcursor = self.kcursor + 0 local columns = self.columns + 0 local filesize = self.filesize + 0 local filecursor = self.filecursor + 0 local nbbytes = self.nbbytes + 0 if c == iup.K_UP then kaction = true kcursor = math.max(kcursor - columns, 0) elseif c == iup.K_DOWN then kaction = true kcursor = math.min(kcursor + columns, filesize - 1) elseif c == iup.K_LEFT then kaction = true kcursor = math.max(kcursor - 1, 0) elseif c == iup.K_RIGHT then kaction = true kcursor = math.min(kcursor + 1, filesize - 1) elseif c == iup.K_PGUP then kaction = true kcursor = math.max(kcursor - nbbytes + columns, 0) elseif c == iup.K_PGDN then kaction = true kcursor = math.min(kcursor + nbbytes - columns, filesize - 1) elseif c == iup.K_HOME then kaction = true kcursor = kcursor - (kcursor % columns) elseif c == iup.K_END then kaction = true kcursor = math.min(kcursor + columns - (kcursor % columns) - 1, filesize - 1) elseif c == iup.K_cUP then kaction = true if filecursor - columns >= 0 then kcursor = kcursor - columns filecursor = filecursor - columns end elseif c == iup.K_cDOWN then kaction = true if filecursor + columns < filesize then kcursor = kcursor + columns filecursor = filecursor + columns end elseif c == iup.K_cLEFT then kaction = true kcursor = math.max(kcursor - 1, 0) filecursor = math.max(filecursor - 1, 0) elseif c == iup.K_cRIGHT then kaction = true kcursor = math.min(kcursor + 1, filesize - 1) filecursor = math.min(filecursor + 1, filesize) elseif c == iup.K_cPGUP then kaction = true if filecursor - nbbytes >= 0 then kcursor = math.max(kcursor - nbbytes, 0) filecursor = math.max(filecursor - nbbytes, 0) end elseif c == iup.K_cPGDN then kaction = true if filecursor + nbbytes < filesize then kcursor = math.min(kcursor + nbbytes, filesize - 1) filecursor = math.min(filecursor + nbbytes, filesize) end elseif c == iup.K_cHOME then kaction = true kcursor = 0 elseif c == iup.K_cEND then kaction = true kcursor = filesize - 1 elseif c == iup.K_mLEFT then raction = true columns = columns - 1 elseif c == iup.K_mRIGHT then raction = true columns = columns + 1 elseif c == iup.K_mUP then raction = true filecursor = math.max(filecursor - columns, 0) elseif c == iup.K_mDOWN then raction = true filecursor = math.min(filecursor + columns, filesize) elseif c == iup.K_mPGUP then raction = true filecursor = math.max(filecursor - nbbytes, 0) elseif c == iup.K_mPGDN then raction = true filecursor = math.min(filecursor + nbbytes, filesize) elseif c == iup.K_1 then kaction = true local m = self.markers[1] if m ~= -1 then kcursor = m end elseif c == iup.K_2 then kaction = true local m = self.markers[2] if m ~= -1 then kcursor = m end elseif c == iup.K_3 then kaction = true local m = self.markers[3] if m ~= -1 then kcursor = m end elseif c == iup.K_4 then kaction = true local m = self.markers[4] if m ~= -1 then kcursor = m end elseif c == iup.K_5 then kaction = true local m = self.markers[5] if m ~= -1 then kcursor = m end elseif c == iup.K_6 then kaction = true local m = self.markers[6] if m ~= -1 then kcursor = m end elseif c == iup.K_7 then kaction = true local m = self.markers[7] if m ~= -1 then kcursor = m end elseif c == iup.K_8 then kaction = true local m = self.markers[8] if m ~= -1 then kcursor = m end elseif c == iup.K_9 then kaction = true local m = self.markers[9] if m ~= -1 then kcursor = m end elseif c == iup.K_0 then kaction = true local m = self.markers[10] if m ~= -1 then kcursor = m end elseif c == iup.K_m1 then kaction = true self.markers[1] = kcursor elseif c == iup.K_m2 then kaction = true self.markers[2] = kcursor elseif c == iup.K_m3 then kaction = true self.markers[3] = kcursor elseif c == iup.K_m4 then kaction = true self.markers[4] = kcursor elseif c == iup.K_m5 then kaction = true self.markers[5] = kcursor elseif c == iup.K_m6 then kaction = true self.markers[6] = kcursor elseif c == iup.K_m7 then kaction = true self.markers[7] = kcursor elseif c == iup.K_m8 then kaction = true self.markers[8] = kcursor elseif c == iup.K_m9 then kaction = true self.markers[9] = kcursor elseif c == iup.K_m0 then kaction = true self.markers[10] = kcursor elseif c == iup.K_c1 then kaction = true self.markers[1] = nil elseif c == iup.K_c2 then kaction = true self.markers[2] = nil elseif c == iup.K_c3 then kaction = true self.markers[3] = nil elseif c == iup.K_c4 then kaction = true self.markers[4] = nil elseif c == iup.K_c5 then kaction = true self.markers[5] = nil elseif c == iup.K_c6 then kaction = true self.markers[6] = nil elseif c == iup.K_c7 then kaction = true self.markers[7] = nil elseif c == iup.K_c8 then kaction = true self.markers[8] = nil elseif c == iup.K_c9 then kaction = true self.markers[9] = nil elseif c == iup.K_c0 then kaction = true self.markers[10] = nil elseif c == iup.K_SP then kaction = true end if kaction then while kcursor < filecursor do filecursor = filecursor - columns end while (kcursor - filecursor) >= nbbytes do filecursor = filecursor + columns end filecursor = math.min(math.max(filecursor, 0), filesize) self.kcursor = kcursor self.filecursor = filecursor self.columns = columns self:setcursor() return iup.IGNORE end if raction then self.filecursor = filecursor self:updatescrollbar() self:updatecolumns(columns) self:draw() return iup.IGNORE end return iup.DEFAULT end, updatehandle = function (self, handle) self.handle = handle local filesize = handle and handle:getsize() or 0 if filesize < 0 then self.handle = nil filesize = 0 end self.filesize = filesize self.old_max_bytes = -1 self.old_filecursor = -1 self:updatescrollbar() self:draw() for _, v in ipairs(self.cbs) do v.cb(v.opaque, self, true) end end, map_cb = function (self) iupep.dbuffer.map_cb(self) local cvdb = self.cvdb if not cvdb then return iup.DEFAULT end cvdb:Font("Courier", cd.PLAIN, -self.gridsize.y) -- self.rastersize = (self.gridsize.x * (self.columns * 4 + 14)) .. "x" -- will not allow the control to be shrinked. -- self.gridsize.x = cvdb:GetFontDim() -- doesn't work as expected end, setcursor = function (self, markerid, value) if markerid then markerid = markerid + 0 if markerid >= 0 and markerid <= 10 then self.markers[markerid] = value + 0 elseif markerid == -2 then self.kcursor = value + 0 self:keypress_cb(iup.K_SP, 1) end end for _, v in ipairs(self.cbs) do v.cb(v.opaque, self) end self:updatescrollbar() self:draw() end, registercb = function (self, cb, opaque) table.insert(self.cbs, { cb = cb, opaque = opaque }) end, wheel_cb = function (self, delta, x, y, status) if delta > 0 then for i = 1, delta do self:keypress_cb(iup.K_cPGUP) end else for i = 1, -delta do self:keypress_cb(iup.K_cPGDN) end end end, scroll_cb = function (self, op, posx, posy) if op == iup.SBUP then self:keypress_cb(iup.K_mUP, 1) elseif op == iup.SBDN then self:keypress_cb(iup.K_mDOWN, 1) elseif op == iup.SBPGUP then self:keypress_cb(iup.K_mPGUP, 1) elseif op == iup.SBPGDN then self:keypress_cb(iup.K_mPGDN, 1) else local columns = self.columns + 0 local startoffset = self.filecursor % columns self.filecursor = math.min(math.max(math.floor(self.posy) * columns + startoffset, 0), self.filesize) self:draw() end return iup.DEFAULT end, motion_cb = function (self, x, y, status) local r = self:resolvepos(x, y) if r ~= -1 then self.mcursor = r end self:setcursor() return iup.DEFAULT end, resolvepos = function (self, x, y) local line = math.floor(y / self.gridsize.y) local column = math.floor(x / self.gridsize.x) local filecursor = self.filecursor + 0 local columns = self.columns + 0 -- offset display if column <= 7 then return filecursor + columns * line end -- gap 1 if column <= 9 then return -1 end -- hex display if column <= (columns * 3 + 10) then -- interhex gap if (column - 10) % 3 == 2 then return -1 end return math.floor((column - 10) / 3) + filecursor + columns * line end -- gap 2 if column <= (columns * 3 + 11) then return -1 end -- ascii display if column <= (columns * 4 + 11) then return column - columns * 3 - 12 + filecursor + columns * line end return -1 end, button_cb = function (self, button, pressed, x, y, status) if pressed == 1 then return iup.DEFAULT end if button == iup.BUTTON1 then local newpos = self:resolvepos(x, y) if newpos ~= -1 then self.kcursor = newpos self:setcursor() return iup.IGNORE end elseif button == iup.BUTTON3 then local newpos = self:resolvepos(x, y) if newpos ~= -1 then self.markers[0] = newpos self:setcursor() return iup.IGNORE end end return iup.DEFAULT end, updategridsize = function (self, gridsize) self.gridsize = gridsize or iupep.hexview.gridsize self:resize_cb(self.width, self.height) end, updatecolumns = function (self, columns) self.columns = columns or 16 self:resize_cb(self.width, self.height) end, create = function (tab) local handle = tab.handle local columns = tab.columns local gridsize = tab.gridsize tab.handle = nil tab.columns = nil tab.gridsize = nil tab.cursor = "TEXT" tab.expand = "Yes" tab.border = "No" tab.minsize = "0x0" tab.size = "0x0" tab.scrollbar = "vertical" local r = iup.canvas(tab) r.action = iupep.dbuffer.action r.draw = iupep.hexview.draw r.resize_cb = iupep.hexview.resize_cb r.keypress_cb = iupep.hexview.keypress_cb r.button_cb = iupep.hexview.button_cb r.map_cb = iupep.hexview.map_cb r.unmap_cb = iupep.dbuffer.unmap_cb r.scroll_cb = iupep.hexview.scroll_cb r.motion_cb = iupep.hexview.motion_cb r.wheel_cb = iupep.hexview.wheel_cb r.printgrid = iupep.hexview.printgrid r.colorgrid = iupep.hexview.colorgrid r.updatehandle = iupep.hexview.updatehandle r.updategridsize = iupep.hexview.updategridsize r.updatecolumns = iupep.hexview.updatecolumns r.updatescrollbar = iupep.hexview.updatescrollbar r.resolvepos = iupep.hexview.resolvepos r.setcursor = iupep.hexview.setcursor r.registercb = iupep.hexview.registercb r.byte2char = iupep.hexview.byte2char r.cbs = {} r.handle = handle r.columns = columns or 16 r.filecursor = 0 r.oldfilecursor = -1 r.kcursor = 0 r.filesize = handle and handle:getsize() or 0 r.mcursor = -1 r.markers = {} r.markerslengths = {} r.showmarkerslengths = false r.displaylines = 0 for i = 0, 10 do r.markers[i] = -1 end r.gridsize = gridsize or iupep.hexview.gridsize r.textcursor = { x = 0, y = 0 } return r end, } iupe.hexview = iupep.hexview.create