local function imLoadImage(fname) local f = im.FileOpen(fname) local r = f:LoadImage() f:Close() return r end dalosp.struct = { templates = {}, images = { up = imLoadImage "12-em-up.tga", down = imLoadImage "12-em-down.tga", cross = imLoadImage "12-em-cross.tga", plus = imLoadImage "12-em-plus.tga", }, types = { "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", "float", "double", "nascii", "asciiz", }, rtypes = { int8 = 1, uint8 = 2, int16 = 3, uint16 = 4, int32 = 5, uint32 = 6, int64 = 7, uint64 = 8, float = 9, double = 10, nascii = 11, asciiz = 12, }, typessizes = { 1, 1, 2, 2, 4, 4, 8, 8, 4, 8, 1, -1, }, get_settings = function (self) return { entries = self.extra.entries } end, configure = function (self) self.cfg_dlg:show() end, activate = function (self) self.act_dlg:show() end, input_change = function (self, ind) if ind == 1 then self:update_values() end if ind == 2 then local h = self:get_linked_input(2) self:auto_template(dalosp.struct.templates, h:readstring()) end end, offset = function (self, lin) local cache = self.extra.cache return cache[lin] end, read_value = function (self, h, t, flags, size) local v if t == "int8" then return h:read8() elseif t == "uint8" then return h:readU8() elseif t == "int16" then return h:read16() elseif t == "uint16" then return h:readU16() elseif t == "int32" then return h:read32() elseif t == "uint32" then return h:readU32() elseif t == "int64" then return h:read64() elseif t == "uint64" then return h:readU64() elseif t == "float" then return h:readFloat() elseif t == "double" then return h:readDouble() elseif t == "asciiz" then v = "" local b repeat b = h:readU8() if b ~= 0 then v = v .. string.char(b) end until b == 0 return v elseif t == "nascii" then return h:readstring(size) end return 0 end, update_values = function (self) local h = self:get_linked_input(1) self.extra.values = {} self.extra.values_by_name = {} if h and h:getsize() >= self.extra.size then h:seek(0) local v, s, t for e, f in ipairs(self.extra.entries) do s = f.size if not tonumber(s) then s = self.extra.values_by_name[s] end t = dalosp.struct.types[f.type] if t == "asciiz" or t == "nascii" or s == 1 then v = self:read_value(h, t, f.flags, s) else v = {} for i = 1, f.size do v[i] = self:read_value(h, t, f.flags, s) end end self.extra.values[e] = v self.extra.values_by_name[f.name] = v end self:output_selected() self:output_struct() else self:set_houtput(nil, 1) self:set_houtput(nil, 2) end end, output_selected = function (self) local selected = self.extra.selected local cache = self.extra.cache local cursor = selected and cache[selected] or -1 local h = self:get_linked_input(1) if h and selected and selected > 0 and cursor >= 0 then local field = self.extra.entries[selected].name local maxsize = (cache[selected + 1] or self.extra.size) - cursor h:seek(cursor) local obj = { h = h, str = self, origin = cursor, field = field, size = maxsize - cursor, getname = function (self) return self.str.name .. ":" .. self.field end, getmodif = function (self) return self.h:getmodif() end, do_seek = function (self) self.h:seek(self.offset + self.origin) end, do_read = function (self, count, userdata) return self.h:read(count, userdata) end, } self:set_houtput(dalos.luahandle(obj), 1) local b = Buffer(true) b:write(field .. "\n") self:set_houtput(b, 3) else self:set_houtput(nil, 1) self:set_houtput(nil, 3) end end, output_struct = function (self) local b = Buffer(true) dumpvars(b, self.extra.values_by_name, "data") self:set_houtput(b, 2) end, cacheoffset = function (self) local entries = self.extra.entries self.extra.cache = {} local cache = self.extra.cache local offset = 0 local got_var = false local ssize local vsize for i, v in ipairs(entries) do ssize = dalosp.struct.typessizes[v.type] vsize = ssize == -1 or type(v.size) ~= "number" if vsize then got_var = true end if got_var then cache[i] = -1 else cache[i] = offset end if not vsize then offset = offset + ssize * v.size end end self.extra.size = offset if self.cfg_dlg then self.cfg_dlg.mx.redraw = "C10" self.cfg_dlg.lsize.title = offset self:update_values() self.act_dlg.mx.numlin = #entries self.act_dlg.mx.numlin_visible = #entries self.act_dlg.mx.redraw = "All" end end, isunique = function (self, name) for _, v in ipairs(self.extra.entries) do if name == v.name then return false end end return true end, getunique = function (self, lin) local newname = "Field" .. lin local base_newname = newname local i = 1 while not self:isunique(newname) do newname = base_newname .. "." .. i i = i + 1 end return newname end, insert_line = function (self, lin) if not lin then lin = (#self.extra.entries + 1) end local name = self:getunique(lin) table.insert(self.extra.entries, lin, { name = name, type = 1, size = 1 }) self:cacheoffset() local mx = self.cfg_dlg.mx mx.numlin = mx.numlin + 1 mx.numlin_visible = mx.numlin_visible + 1 end, remove_line = function (self, lin) table.remove(self.extra.entries, lin) self:cacheoffset() local mx = self.cfg_dlg.mx mx.numlin = mx.numlin - 1 mx.numlin_visible = mx.numlin_visible - 1 end, line_up = function (self, lin) if lin == 1 then return iup.DEFAULT end local entries = self.extra.entries local old_lin = entries[lin] table.remove(entries, lin) table.insert(entries, lin - 1, old_lin) self:cacheoffset() local mx = self.cfg_dlg.mx mx.redraw = "L" .. (lin - 1) mx.redraw = "L" .. lin end, line_down = function (self, lin) local entries = self.extra.entries if lin == #entries then return iup.DEFAULT end local old_lin = entries[lin] table.remove(entries, lin) table.insert(entries, lin + 1, old_lin) self:cacheoffset() local mx = self.cfg_dlg.mx mx.redraw = "L" .. lin mx.redraw = "L" .. (lin + 1) end, gen_template = function (self) return self:get_settings() end, apply_template = function (self, data) self.extra.entries = data.entries self:cacheoffset() end, get_field_value = function (self, lin) local v = self.extra.values[lin] if type(v) == "table" then local r = nil for k, sv in ipairs(v) do if not r then r = sv else r = r .. ", " .. sv end end return "[" .. (r or " ") .."]" else return v or "" end end, cfg_draw_cb = function (self, lin, col, x1, x2, y1, y2, cv) if col == 1 then dalosp.struct.images.plus:cdCanvasPutImageRect(cv, x1 + 3, y2 + 5, 12, 12, 0, 12, 0, 12) return iup.DEFAULT elseif col == 2 and lin ~= 0 then dalosp.struct.images.cross:cdCanvasPutImageRect(cv, x1 + 3, y2 + 5, 12, 12, 0, 12, 0, 12) return iup.DEFAULT elseif col == 3 and lin ~= 0 and lin ~= 1 then dalosp.struct.images.up:cdCanvasPutImageRect(cv, x1 + 3, y2 + 5, 12, 12, 0, 12, 0, 12) return iup.DEFAULT elseif col == 4 and lin ~= 0 and lin ~= #self.struct.extra.entries then dalosp.struct.images.down:cdCanvasPutImageRect(cv, x1 + 3, y2 + 5, 12, 12, 0, 12, 0, 12) return iup.DEFAULT end return iup.IGNORE end, cfg_click_cb = function (self, lin, col, status) if col == 1 and lin == 0 then return self.struct:insert_line() end if lin == 0 then return iup.DEFAULT end if col == 1 then self.struct:insert_line(lin) elseif col == 2 then self.struct:remove_line(lin) elseif col == 3 then self.struct:line_up(lin) elseif col == 4 then self.struct:line_down(lin) elseif col == 11 then -- edit flags end end, cfg_value_cb = function (self, lin, col) if lin == 0 then if col == 5 then return "Name" elseif col == 6 then return "Type" elseif col == 7 then return "Size" elseif col == 8 then return "Enum" elseif col == 9 then return "Value Check" elseif col == 10 then return "Offset" elseif col == 11 then return "Flags" end return "" end local entries = self.struct.extra.entries if col == 5 then return entries[lin].name elseif col == 6 then return dalosp.struct.types[entries[lin].type] elseif col == 7 then return entries[lin].size elseif col == 10 then local off = self.struct:offset(lin) if off == -1 then return "" end return off elseif col == 11 then -- flags end return "" end, cfg_value_edit_cb = function (self, lin, col, newval) local entries = self.struct.extra.entries if col == 5 then entries[lin].name = newval self.struct:output_selected() self.struct:output_struct() elseif col == 6 then entries[lin].type = dalosp.struct.rtypes[newval] self.struct:cacheoffset() elseif col == 7 then local nnewval = tonumber(newval) if nnewval then newval = nnewval end entries[lin].size = newval self.struct:cacheoffset() elseif col == 8 then -- enum elseif col == 9 then -- value check end self.struct.act_dlg.mx.redraw = "All" end, cfg_dropcheck_cb = function (self, lin, col) return col == 6 and iup.DEFAULT or iup.IGNORE end, cfg_edition_cb = function (self, lin, col, mode, update) if mode == 1 then return (col >= 5 and col <= 7) and iup.DEFAULT or iup.IGNORE else local newval = self.value if update == 0 then return iup.DEFAULT end if col == 5 then if not self.struct:isunique(newval) then return iup.IGNORE end elseif col == 6 then -- do a check on the dropdown ? I don't think so elseif col == 7 then if not tonumber(newval) and self.struct:isunique(newval) then return iup.IGNORE end elseif col == 8 then -- enum elseif col == 9 then -- value check end return iup.DEFAULT end end, cfg_drop_cb = function (self, drop, lin, col) if col ~= 6 then return iup.IGNORE end for i, v in ipairs(dalosp.struct.types) do drop[i] = v end drop.value = self.previousvalue drop.visible_items = 15 return iup.DEFAULT end, act_value_cb = function (self, lin, col) if lin == 0 then if col == 1 then return "Field" elseif col == 2 then return "Value" end end if col == 0 then return nil end local entry = self.struct.extra.entries[lin] if col == 1 then return entry.name else return self.struct:get_field_value(lin) end end, act_click_cb = function (self, lin, col, status) self.struct.extra.selected = lin self.struct:output_selected() end, create = function (d, tab, settings) tab.ninputs = 2 tab.noutputs = 3 tab.otype = dalos.objtype.LUA_VIEWER tab.configure = dalosp.struct.configure tab.activate = dalosp.struct.activate tab.input_change = dalosp.struct.input_change tab.get_settings = dalosp.struct.get_settings tab.gen_template = dalosp.struct.gen_template tab.apply_template = dalosp.struct.apply_template tab.draw = function (self, cv, x, y, w, h) dalosp.object.default_draw(self, cv, x, y, w, h) end tab.default_name = "Struct" tab.ntype = "Struct" local extra = { } if not settings then settings = {} end local entries = settings.entries or {} extra.entries = entries local obj = dalos.object(d, tab, extra) obj.insert_line = dalosp.struct.insert_line obj.remove_line = dalosp.struct.remove_line obj.line_up = dalosp.struct.line_up obj.line_down = dalosp.struct.line_down obj.offset = dalosp.struct.offset obj.cacheoffset = dalosp.struct.cacheoffset obj.isunique = dalosp.struct.isunique obj.getunique = dalosp.struct.getunique obj.get_field_value = dalosp.struct.get_field_value obj.update_values = dalosp.struct.update_values obj.read_value = dalosp.struct.read_value obj.output_selected = dalosp.struct.output_selected obj.output_struct = dalosp.struct.output_struct local cmx = iup.matrix { numcol = 11, numcol_visible = 11, usetitlesize = "Yes", rasterwidth1 = 12, rasterwidth2 = 12, rasterwidth3 = 12, rasterwidth4 = 12, rasterwidth5 = 120, rasterwidth6 = 80, rasterwidth7 = 80, rasterwidth8 = 80, rasterwidth9 = 120, rasterwidth10 = 40, rasterwidth11 = 60, resizematrix = "Yes", readonly = "No", struct = obj, draw_cb = dalosp.struct.cfg_draw_cb, value_cb = dalosp.struct.cfg_value_cb, value_edit_cb = dalosp.struct.cfg_value_edit_cb, dropcheck_cb = dalosp.struct.cfg_dropcheck_cb, drop_cb = dalosp.struct.cfg_drop_cb, edition_cb = dalosp.struct.cfg_edition_cb, click_cb = dalosp.struct.cfg_click_cb, } local amx = iup.matrix { numcol = 2, numcol_visible = 2, usetitlesize = "Yes", rasterwidth1 = 120, rasterwidth2 = 120, readonly = "Yes", resizematrix = "Yes", struct = obj, value_cb = dalosp.struct.act_value_cb, click_cb = dalosp.struct.act_click_cb, } local lsize = iup.label { title = "0", expand = "Horizontal", } obj.cfg_dlg = iup.dialog { iup.vbox { iup.hbox { iup.label { title = "Struct size: " }, lsize, }, cmx, iup.hbox { iup.fill {}, iup.button { title = "Ok", action = function (self) return iup.CLOSE end, }, iup.button { title = "Import", action = function() obj:load_template() return iup.CLOSE end, }, iup.button { title = "Export", action = function() obj:save_template(self:gen_template()) end, }, iup.button { title = "Use Template", action = function() obj:use_template(dalsop.struct.templates) end, }, iup.button { title = "Change name", action = function() local s, n = iup.GetParam("Change name", nil, "Name: %s\n", obj.name) if s then obj.name = n end end }, iup.fill {}, iup.button { title = "Close", action = function() return iup.CLOSE end, }, }, }, size = "500x220", mx = cmx, lsize = lsize, title = obj.name .. " configuration", } obj.act_dlg = iup.dialog { amx, size = "250x120", mx = amx, title = obj.name, } if entries then obj.cfg_dlg.mx.numlin = #entries obj.cfg_dlg.mx.numlin_visible = #entries obj.act_dlg.mx.numlin = #entries obj.act_dlg.mx.numlin_visible = #entries end obj:cacheoffset() return obj end, register_template = function (data, tname) dalosp.struct.templates[tname] = data end, } dalos.struct = dalosp.struct.create dalos:register_obj("Struct", dalos.struct, "Programmable", dalosp.struct.register_template)