dfhack/plugins/Dfusion/luafiles/xml_types.lua

734 lines
18 KiB
Lua

--<angavrilov> otherwise you just maintain alignment granularity in addition to size for all fields,
-- round up current offset to field alignment,
-- assign structs the max alignment of any field, and round up struct size to its alignment
function type_read(valtype,address)
if valtype.issimple then
--print("Simple read:"..tostring(valtype.ctype))
return engine.peek(address,valtype.ctype)
else
return valtype:makewrap(address)
end
end
function type_write(valtype,address,val)
if valtype.issimple then
engine.poke(address,valtype.ctype,val)
else
engine.poke(address,DWORD,rawget(val,"ptr"))
end
end
function first_of_type(node,labelname)
for k,v in ipairs(node) do
if type(v)=="table" and v.label==labelname then
return v
end
end
end
xtypes={} -- list of all types prototypes (e.g. enum-type -> announcement_type)
-- type must have size, new and makewrap (that makes a wrapper around ptr)
if WINDOWS then
dofile("dfusion/xml_types_windows.lua")
elseif LINUX then
dofile("dfusion/xml_types_linux.lua")
end
function padAddress(curoff,typetoadd) --return new offset to place things... Maybe linux is different?
--windows -> sizeof(x)==alignof(x)
--[=[
if sizetoadd>8 then sizetoadd=8 end
if(math.mod(curoff,sizetoadd)==0) then
return curoff
else
return curoff+(sizetoadd-math.mod(curoff,sizetoadd))
end
--]=]
if typetoadd==nil or typetoadd.__align==nil then
return curoff
else
if(math.mod(curoff,typetoadd.__align)==0) then
return curoff
else
if PRINT_PADS then
print("padding off:"..curoff.." with align:"..typetoadd.__align.." pad="..(typetoadd.__align-math.mod(curoff,typetoadd.__align)))
end
return curoff+(typetoadd.__align-math.mod(curoff,typetoadd.__align))
end
end
end
local sarr={}
sarr.__index=sarr
function sarr.new(node,obj)
local o=obj or {}
setmetatable(o,sarr)
--print("Making array.")
o.count=tonumber(node.xarg.count)
--print("got count:"..o.count)
o.item_type=makeType(first_of_type(node,"ld:item"))
o.__align=o.item_type.__align or 4
o.size=o.count*o.item_type.size
--print("got subtypesize:"..o.item_type.size)
return o
end
function sarr:makewrap(address)
local o={}
o.mtype=self
o.ptr=address
setmetatable(o,self.wrap)
return o
end
sarr.wrap={}
function sarr.wrap:__index(key)
if key=="size" then
return rawget(self,"mtype").count
end
local num=tonumber(key)
local mtype=rawget(self,"mtype")
if num~=nil and num<mtype.count then
return type_read(mtype.item_type,num*mtype.item_type.size+rawget(self,"ptr"))
else
error("invalid key to static-array")
end
end
function sarr.wrap:__newindex(key,val)
local num=tonumber(key)
if num~=nil and num<rawget(self,"mtype").count then
return type_write(mtype.item_type,num*mtype.item_type.size+rawget(self,"ptr"),val)
else
error("invalid key to static-array")
end
end
--]=]
xtypes["static-array"]=sarr
simpletypes={}
simpletypes["s-float"]={FLOAT,4,4}
simpletypes.int64_t={QWORD,8,8}
simpletypes.uint32_t={DWORD,4,4}
simpletypes.uint16_t={WORD,2,2}
simpletypes.uint8_t={BYTE,1,1}
simpletypes.int32_t={DWORD,4,4}
simpletypes.int16_t={WORD,2,2}
simpletypes.int8_t={BYTE,1,1}
simpletypes.bool={BYTE,1,1}
simpletypes["stl-string"]={STD_STRING,28,4}
function getSimpleType(typename,obj)
if simpletypes[typename] == nil then return end
local o=obj or {}
o.name=typename
o.ctype=simpletypes[typename][1]
o.size=simpletypes[typename][2]
o.__align=simpletypes[typename][3]
o.issimple=true
return o
end
local type_enum={}
type_enum.__index=type_enum
function type_enum.new(node,obj)
local o=obj or {}
setmetatable(o,type_enum)
o.names={}
for k,v in pairs(node) do
if v.label=="enum-item" then
if v.xarg~=nil and v.xarg.name then
--print("\t"..k.." "..v.xarg.name)
o.names[k-1]=v.xarg.name
else
o.names[k-1]=string.format("unk_%d",k-1)
end
end
end
local btype=node.xarg["base-type"] or "uint32_t"
--print(btype.." t="..convertType(btype))
o:setbtype(btype)
return o
end
function type_enum:setbtype(btype)
self.etype=getSimpleType(btype) -- should be simple type
self.size=self.etype.size
self.__align=self.etype.__align
end
function type_enum:makewrap(address)
local o={}
o.mtype=self
o.ptr=address
setmetatable(o,self.wrap)
return o
end
type_enum.wrap={}
type_enum.wrap.__index=type_enum.wrap
type_enum.wrap.set=function (tbl,val)
local mtype=rawget(tbl,"mtype")
local ptr=rawget(tbl,"ptr")
type_write(mtype.etype,ptr,val)
end
type_enum.wrap.get=function (tbl)
local mtype=rawget(tbl,"mtype")
local ptr=rawget(tbl,"ptr")
return type_read(mtype.etype,ptr)
end
xtypes["enum-type"]=type_enum
local type_bitfield={} --bitfield can be accessed by number (bf[1]=true) or by name bf.DO_MEGA=true
type_bitfield.__index=type_bitfield
function type_bitfield.new(node,obj)
local o=obj or {}
setmetatable(o,type_bitfield)
o.size=0
o.fields_named={}
o.fields_numed={}
o.__align=1
for k,v in pairs(node) do
if type(v)=="table" and v.xarg~=nil then
local name=v.xarg.name
if name==nil then
name="anon_"..tostring(k)
end
--print("\t"..k.." "..name)
o.fields_numed[k]=name
o.fields_named[name]=k
o.size=o.size+1
end
end
--o.size=o.size/8 -- size in bytes, not bits.
o.size=4
o.size=math.ceil(o.size)
--[=[if math.mod(o.size,o.__align) ~= 0 then
o.size=o.size+ (o.__align-math.mod(o.size,o.__align))
end]=]
return o
end
function type_bitfield:bitread(addr,nbit)
local byte=engine.peekb(addr+nbit/8)
if bit.band(byte,bit.lshift(1,nbit%8))~=0 then
return true
else
return false
end
end
function type_bitfield:bitwrite(addr,nbit,value)
local byte=engine.peekb(addr+nbit/8)
if self:bitread(addr,nbit)~= value then
local byte=bit.bxor(byte,bit.lshift(1,nbit%8))
engine.pokeb(addr+nbit/8,byte)
end
end
type_bitfield.wrap={}
function type_bitfield:makewrap(address)
local o={}
o.mtype=self
o.ptr=address
setmetatable(o,self.wrap)
return o
end
function type_bitfield.wrap.__next(tbl,key)
local kk,vv=next(rawget(tbl,"mtype").fields_named,key)
if kk ~= nil then
return kk,tbl[kk]
else
return nil
end
end
function type_bitfield.wrap:__index(key)
local myptr=rawget(self,"ptr")
local mytype=rawget(self,"mtype")
local num=tonumber(key)
if num~=nil then
if mytype.fields_numed[num]~=nil then
return mytype:bitread(myptr,num-1)
else
error("No bit with index:"..tostring(num))
end
elseif mytype.fields_named[key]~= nil then
return mytype:bitread(myptr,mytype.fields_named[key]-1)
else
error("No such field exists")
end
end
function type_bitfield.wrap:__newindex(key,value)
local myptr=rawget(self,"ptr")
local mytype=rawget(self,"mtype")
local num=tonumber(key)
if num~=nil then
if mytype.fields_numed[num]~=nil then
return mytype:bitwrite(myptr,num-1,value)
else
error("No bit with index:"..tostring(num))
end
elseif mytype.fields_named[key]~= nil then
return mytype:bitwrite(myptr,mytype.fields_named[key]-1,value)
else
error("No such field exists")
end
end
xtypes["bitfield-type"]=type_bitfield
local type_class={}
type_class.__index=type_class
function type_class.new(node,obj)
local o=obj or {}
setmetatable(o,type_class)
o.types={}
o.base={}
o.size=0
local isunion=false
if node.xarg["is-union"]=="true" then
--error("unions not supported!")
isunion=true
end
local firsttype;
--o.baseoffset=0
if node.xarg["inherits-from"]~=nil then
table.insert(o.base,getGlobal(node.xarg["inherits-from"]))
--error("Base class:"..node.xarg["inherits-from"])
end
for k,v in ipairs(o.base) do
for kk,vv in pairs(v.types) do
o.types[kk]={vv[1],vv[2]+o.size}
end
o.size=o.size+v.size
end
--o.baseoffset=o.size;
--o.size=0
for k,v in pairs(node) do
if type(v)=="table" and v.label=="ld:field" and v.xarg~=nil then
local t_name=""
local name=v.xarg.name or v.xarg["anon-name"] or ("unk_"..k)
--print("\t"..k.." "..name.."->"..v.xarg.meta.." ttype:"..v.label)
local ttype=makeType(v)
if ttype.size==0 then error("Field with 0 size! type="..node.xarg["type-name"].."."..name) end
if ttype.size-math.ceil(ttype.size) ~= 0 then error("Field with real offset! type="..node.xarg["type-name"].."."..name) end
--for k,v in pairs(ttype) do
-- print(k..tostring(v))
--end
local off=padAddress(o.size,ttype)
--[=[if PRINT_PADS then
if ttype.__align then
print(name.." "..ttype.__align .. " off:"..off.." "..math.mod(off,ttype.__align))
end
if off~=o.size then
print("var:"..name.." size:"..ttype.size)
end
end]=]
--print("var:"..name.." ->"..tostring(off).. " :"..ttype.size)
if isunion then
if ttype.size > o.size then
o.size=ttype.size
end
o.types[name]={ttype,0}
else
o.size=off
o.types[name]={ttype,o.size}--+o.baseoffset
o.size=o.size+ttype.size
end
if firsttype== nil then
firsttype=o.types[name][1]
end
end
end
if isunion then
o.__align=0
for k,v in pairs(o.types) do
if v[1].__align~= nil and v[1].__align>o.__align then
o.__align=v[1].__align
end
end
else
if o.base[1]~= nil then
o.__align=o.base[1].__align
elseif firsttype~= nil then
o.__align=firsttype.__align
--if o.__align~=nil then
--print("\t\t setting align to:"..(o.__align or ""))
--else
--o.__align=4
--print("\t\t NIL ALIGNMENT!")
--end
end
end
return o
end
type_class.wrap={}
function type_class.wrap:__address(key)
local myptr=rawget(self,"ptr")
local mytype=rawget(self,"mtype")
if mytype.types[key] ~= nil then
return myptr+mytype.types[key][2]
else
error("No such field exists")
end
end
function type_class.wrap:__index(key)
local myptr=rawget(self,"ptr")
local mytype=rawget(self,"mtype")
if mytype.types[key] ~= nil then
return type_read(mytype.types[key][1],myptr+mytype.types[key][2])
else
error("No such field exists")
end
end
function type_class.wrap:__newindex(key,value)
local myptr=rawget(self,"ptr")
local mytype=rawget(self,"mtype")
if mytype.types[key] ~= nil then
return type_write(mytype.types[key][1],myptr+mytype.types[key][2],value)
else
error("No such field exists")
end
end
function type_class:makewrap(ptr)
local o={}
o.ptr=ptr
o.mtype=self
setmetatable(o,self.wrap)
return o
end
xtypes["struct-type"]=type_class
xtypes["class-type"]=type_class
local type_pointer={}
type_pointer.__index=type_pointer
function type_pointer.new(node,obj)
local o=obj or {}
setmetatable(o,type_pointer)
local subnode=first_of_type(node,"ld:item")
if subnode~=nil then
o.ptype=makeType(subnode,nil,true)
end
o.size=4
o.__align=4
return o
end
type_pointer.wrap={}
type_pointer.wrap.__index=type_pointer.wrap
function type_pointer.wrap:tonumber()
local myptr=rawget(self,"ptr")
return engine.peekd(myptr)--type_read(DWORD,myptr)
end
function type_pointer.wrap:__setup(trg)
if trg~= nil then
self:fromnumber(trg)
else
self:fromnumber(0)
end
end
function type_pointer.wrap:fromnumber(num)
local myptr=rawget(self,"ptr")
return engine.poked(myptr,num)--type_write(DWORD,myptr,num)
end
function type_pointer.wrap:deref()
local myptr=rawget(self,"ptr")
local mytype=rawget(self,"mtype")
return type_read(mytype.ptype,engine.peekd(myptr))
end
function type_pointer.wrap:setref(val)
local myptr=rawget(self,"ptr")
local mytype=rawget(self,"mtype")
return type_write(mytype.ptype,engine.peekd(myptr),val)
end
function type_pointer.wrap:newref(val)
local myptr=rawget(self,"ptr")
local mytype=rawget(self,"mtype")
local ptr=engine.alloc(mytype.ptype.size)
self:fromnumber(ptr)
return ptr
end
function type_pointer:makewrap(ptr)
local o={}
o.ptr=ptr
o.mtype=self
setmetatable(o,self.wrap)
return o
end
xtypes["pointer"]=type_pointer
--------------------------------------------
--stl-vector (beginptr,endptr,allocptr)
--df-flagarray (ptr,size)
xtypes.containers=xtypes.containers or {}
local dfarr={}
dfarr.__index=dfarr
function dfarr.new(node,obj)
local o=obj or {}
setmetatable(o,dfarr)
o.size=8
o.__align=4
o.item_type=makeType(first_of_type(node,"ld:item"))
return o
end
function dfarr:makewrap(address)
local o={}
o.mtype=self
o.ptr=address
setmetatable(o,self.wrap)
return o
end
dfarr.wrap={}
function dfarr.wrap:__setup(size)
local mtype=rawget(self,"mtype")
engine.pokew(rawget(self,"ptr")+4,size)
local newptr=engine.alloc(size*mtype.item_type.size)
engine.poked(rawget(self,"ptr"),newptr)
end
function dfarr.wrap:__index(key)
local num=tonumber(key)
local mtype=rawget(self,"mtype")
local size=engine.peekw(rawget(self,"ptr")+4)
if key=="size" then
return size
end
local item_start=engine.peekd(rawget(self,"ptr"))
if num~=nil and num<sizethen then
return type_read(mtype.item_type,num*mtype.item_type.size+item_start)
else
error("invalid key to df-array")
end
end
function dfarr.wrap:__newindex(key,val)
local num=tonumber(key)
local size=engine.peekw(rawget(self,"ptr")+4)
local item_start=engine.peekd(rawget(self,"ptr"))
if num~=nil and num<size then
return type_write(mtype.item_type,num*mtype.item_type.size+item_start,val)
else
error("invalid key to df-array")
end
end
xtypes.containers["df-array"]=dfarr
local farr={}
farr.__index=farr
function farr.new(node,obj)
local o=obj or {}
setmetatable(o,farr)
o.size=8
o.__align=4
if node.xarg["index-enum"]~= nil then
o.index=getGlobal(node.xarg["index-enum"],true)
end
return o
end
function farr:makewrap(address)
local o={}
o.mtype=self
o.ptr=address
setmetatable(o,self.wrap)
return o
end
function farr:bitread(addr,nbit)
local byte=engine.peekb(addr+nbit/8)
if bit.band(byte,bit.lshift(1,nbit%8))~=0 then
return true
else
return false
end
end
function farr:bitwrite(addr,nbit,value)
local byte=engine.peekb(addr+nbit/8)
if self:bitread(addr,nbit)~= value then
local byte=bit.bxor(byte,bit.lshift(1,nbit%8))
engine.pokeb(addr+nbit/8,byte)
end
end
type_bitfield.wrap={}
function type_bitfield:makewrap(address)
local o={}
o.mtype=self
o.ptr=address
setmetatable(o,self.wrap)
return o
end
farr.wrap={}
function farr.wrap.__next(tbl,key)
error("TODO")
end
function farr.wrap:__index(key)
local num=tonumber(key)
local mtype=rawget(self,"mtype")
local size=engine.peekd(rawget(self,"ptr")+4)*8
if key=="size" then
return size;
end
if mtype.index~=nil and num==nil then
--print("Requested:"..key)
for k,v in pairs(mtype.index.names) do
if v==key then
num=k
break
end
end
--print("is key:"..num)
end
if num~=nil and num<size then
return mtype:bitread(rawget(self,"ptr"),num)
else
error("invalid key to df-flagarray")
end
end
function farr.wrap:__newindex(key,val)
local num=tonumber(key)
local size=engine.peekd(rawget(self,"ptr")+4)*8
local mtype=rawget(self,"mtype")
if mtype.index~=nil and num==nil then
--print("Requested:"..key)
for k,v in pairs(mtype.index.names) do
if v==key then
num=k
break
end
end
--print("is key:"..num)
end
if num~=nil and num<size then
return mtype:bitwrite(rawget(self,"ptr"),num,val)
else
error("invalid key to df-flagarray")
end
end
xtypes.containers["df-flagarray"]=farr
--------------------------------------------
local bytes_pad={}
bytes_pad.__index=bytes_pad
function bytes_pad.new(node,obj)
local o=obj or {}
setmetatable(o,bytes_pad)
o.size=tonumber(node.xarg.size)
if node.xarg.alignment~=nil then
--print("Aligned bytes!")
o.__align=tonumber(node.xarg.alignment)
end
return o
end
xtypes["bytes"]=bytes_pad
--------------------------------------------
function getGlobal(name,canDelay)
if types[name]== nil then
if canDelay then
types[name]={}
return types[name]
end
findAndParse(name)
if types[name]== nil then
error("type:"..name.." failed find-and-parse")
end
--error("type:"..node.xarg["type-name"].." should already be ready")
end
if types[name].size==nil and canDelay==nil then --was delayed, now need real type
findAndParse(name)
if types[name]== nil then
error("type:"..name.." failed find-and-parse")
end
end
return types[name]
end
parser={}
parser["ld:global-type"]=function (node,obj)
return xtypes[node.xarg.meta].new(node,obj)
end
parser["ld:global-object"]=function (node)
end
parser["ld:field"]=function (node,obj,canDelay)
local meta=node.xarg.meta
if meta=="number" or (meta=="primitive" and node.xarg.subtype=="stl-string") then
return getSimpleType(node.xarg.subtype,obj)
elseif meta=="static-array" then
return xtypes["static-array"].new(node,obj)
elseif meta=="global" then
local ltype=getGlobal(node.xarg["type-name"],canDelay)
if node.xarg["base-type"]~=nil then
ltype:setbtype(node.xarg["base-type"])
end
return ltype
elseif meta=="compound" then
if node.xarg.subtype==nil then
return xtypes["struct-type"].new(node,obj)
else
return xtypes[node.xarg.subtype.."-type"].new(node,obj)
end
elseif meta=="container" then
local subtype=node.xarg.subtype
if xtypes.containers[subtype]==nil then
error(subtype.." not implemented... (container)")
else
return xtypes.containers[subtype].new(node,obj)
end
elseif meta=="pointer" then
return xtypes["pointer"].new(node,obj)
elseif meta=="bytes" then
return xtypes["bytes"].new(node,obj)
else
error("Unknown meta:"..meta)
end
end
parser["ld:item"]=parser["ld:field"]
function makeType(node,obj,canDelay)
local label=node.label
if parser[label] ~=nil then
--print("Make Type with:"..label)
local ret=parser[label](node,obj,canDelay)
if ret==nil then
error("Error parsing:"..label.." nil returned!")
else
return ret
end
else
for k,v in pairs(node) do
print(k.."->"..tostring(v))
end
error("Node parser not found: "..label)
end
--[=[
if getSimpleType(node)~=nil then
return getSimpleType(node)
end
print("Trying to make:"..node.xarg.meta)
if xtypes[node.xarg.meta]~=nil then
return xtypes[node.xarg.meta].new(node,obj)
end
if node.xarg.meta=="global" then
--print(node.xarg["type-name"])
if types[node.xarg["type-name"]]== nil then
error("type:"..node.xarg["type-name"].." should already be ready")
end
return types[node.xarg["type-name"]]
end
]=]
--[=[for k,v in pairs(node) do
print(k.."=>"..tostring(v))
if type(v)=="table" then
for kk,vv in pairs(v) do
print("\t"..kk.."=>"..tostring(vv))
end
end
end]=]
end