Start of dfusion module. Fixed small error in memscan.lua and start of custom embark command.

develop
Warmist 2012-10-17 21:43:44 +03:00
parent 1b5a6616e2
commit da92fb9a1c
3 changed files with 189 additions and 1 deletions

@ -24,7 +24,7 @@ function CheckedArray:__len()
return self.count
end
function CheckedArray:__index(idx)
if type(idx) == number then
if type(idx) == "number" then
if idx >= self.count then
error('Index out of bounds: '..tostring(idx))
end

@ -1,3 +1,28 @@
local dfu=require("dfusion")
local ms=require("memscan")
CustomEmbark=defclass(CustomEmbark,dfu.BinaryPlugin)
CustomEmbark.ATTRS{filename="dfusion/embark/embark.o",name="CustomEmbark",race_caste_data=DEFAULT_NIL}
function CustomEmbark:install()
local stoff=dfhack.internal.getAddress('start_dwarf_count')
if stoff==nil then
error("address for start_dwarf_count not found!")
end
local _,race_id_offset=df.sizeof(df.global.ui:_field("race_id"))
local needle={0x0f,0xb7,0x0d} --movzx,...
add_dword(needle,race_id_offset) -- ...word ptr[]
local mem=ms.get_code_segment()
local trg_offset=mem.uint8_t.find(needle,stoff)--maybe endoff=stoff+bignumber
if trg_offset==nil then
error("address for race_load not found")
end
needle={0x83,0xc8,0xff} -- or eax, 0xFF
local caste_offset=mem.uint8_t.find(needle,trg_offset)
if caste_offset==nil or caste_offset-stoff>1000 then
error("Caste change code not found or found too far!")
end
end
function MakeTable(modpos,modsize,names)
count=0
castes={}

@ -0,0 +1,163 @@
-- Stuff used by dfusion
local _ENV = mkmodule('plugins.dfusion')
local ms=require("memscan")
local marker={0xDE,0xAD,0xBE,0xEF}
patches={}
-- A reversable binary patch
BinaryPatch=defclass(BinaryPatch)
BinaryPatch.ATTRS {pre_data=DEFAULT_NIL,data=DEFAULT_NIL,address=DEFAULT_NIL,name=DEFAULT_NIL}
function BinaryPatch:init(args)
self.is_applied=false
if args.pre_data==nil or args.data==nil or args.address==nil or args.name==nil then
error("Invalid parameters to binary patch")
end
if patches[self.name]~=nil then
error("Patch already exist")
end
self.max_val=0
for k,v in pairs(args.pre_data) do
if type(k)~="number" then
error("non number key in pre_data")
end
if self.max_val<k then
self.max_val=k
end
end
for k,v in pairs(args.data) do
if type(k)~="number" then
error("non number key in data")
end
if args.pre_data[k]==nil then
error("can't edit without revert data")
end
end
end
function BinaryPatch:postinit(args)
patches[args.name]=self
end
function BinaryPatch:apply()
local arr=ms.CheckedArray.new('uint8_t',self.address,self.address+self.max_val)
for k,v in pairs(self.pre_data) do
if arr[k-1]~=v then
error(string.format("pre-data does not match expected:%x got:%x",v,arr[k-1]))
end
end
local post_buf=df.new('uint8_t',self.max_val)
for k,v in pairs(self.pre_data) do
if self.data[k]==nil then
post_buf[k-1]=v
else
post_buf[k-1]=self.data[k]
end
end
local ret=dfhack.with_finalize(function() post_buf:delete() end,dfhack.internal.patchMemory,self.address,post_buf,self.max_val)
if not ret then
error("Patch application failed!")
end
self.is_applied=true
--[[
for k,v in pairs(self.data) do
arr[k]=v
end
]]--
end
function BinaryPatch:remove(delete)
if delete==nil then
delete=true
end
if not self.is_applied then
error("can't remove BinaryPatch, not applied.")
end
local arr=ms.CheckedArray.new('uint8_t',self.address,self.address+self.max_val)
local post_buf=df.new('uint8_t',self.max_val)
for k,v in pairs(self.pre_data) do
post_buf[k-1]=v
end
local ret=dfhack.with_finalize(function() post_buf:delete() end,dfhack.internal.patchMemory,self.address,post_buf,self.max_val)
if not ret then
error("Patch remove failed!")
end
self.is_applied=false
if delete then
patches[self.name]=nil
end
--[[
for k,v in pairs(self.data) do
arr[k]=self.pre_data[k]
end --]]
end
function BinaryPatch:__gc()
if self.is_applied then
self:remove()
end
end
-- A binary hack (obj file) loader/manager
-- has to have: a way to get offsets for marked areas (for post load modification) or some way to do that pre-load
-- page managing (including excecute/write flags for DEP and the like)
plugins=plugins or {}
BinaryPlugin=defclass(BinaryPlugin)
BinaryPlugin.ATTRS {filename=DEFAULT_NIL,reloc_table={},name=DEFAULT_NIL}
function BinaryPlugin:init(args)
if args.name==nil then error("Not a valid plugin name!") end
if plugins[args.name]==nil then
plugins[args.name]=true
else
error("Trying to create a same plugin")
end
self.allocated_object={}
end
function BinaryPlugin:postinit(args)
self:load()
end
function BinaryPlugin:allocate(name,typename,arrsize)
local trg
if df[typename]==nil then
trg=df.new(typename,arrsize)
self.allocated_object[name]=trg
else
trg=df[typename]:new(arrsize)
self.allocated_object[name]=trg
end
return trg
end
function BinaryPlugin:load()
local obj=loadObjectFile(self.filename)
self.data=df.reinterpret_cast("uint8_t",obj.data)
self.size=obj.data_size
for _,v in pairs(obj.symbols) do
if string.sub(v.name,1,5)=="mark_" then
local new_pos=self:find_marker(v.pos)
self.reloc_table[string.sub(v.name,6)]=new_pos
end
end
end
function BinaryPlugin:find_marker(start_pos)
local matched=0
for i=start_pos,self.size do
if self.data[i]==marker[4-matched] then
matched=matched+1
if matched == 4 then
return i-4
end
end
end
end
function BinaryPlugin:print_data()
local out=""
for i=0,self.size do
out=out..string.format(" %02x",self.data[i])
if math.modf(i,16)==15 then
print(out)
out=""
end
end
print(out)
end
return _ENV