Embark and friendship working

Signed-off-by: Warmist <Warmist@gmail.com>
develop
Warmist 2011-08-05 00:22:25 +03:00
parent 4b9786f8af
commit 835581e5d3
21 changed files with 589 additions and 5 deletions

@ -15,6 +15,7 @@
#include "lua_Console.h" #include "lua_Console.h"
#include "lua_Process.h" #include "lua_Process.h"
#include "lua_Hexsearch.h" #include "lua_Hexsearch.h"
#include "lua_Misc.h"
#include "functioncall.h" #include "functioncall.h"
using std::vector; using std::vector;
@ -39,6 +40,7 @@ DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand>
lua::RegisterConsole(lua::glua::Get(),&c->con); lua::RegisterConsole(lua::glua::Get(),&c->con);
lua::RegisterProcess(lua::glua::Get(),c->p); lua::RegisterProcess(lua::glua::Get(),c->p);
lua::RegisterHexsearch(lua::glua::Get()); lua::RegisterHexsearch(lua::glua::Get());
lua::RegisterMisc(lua::glua::Get());
commands.push_back(PluginCommand("dfusion","Init dfusion system.",dfusion)); commands.push_back(PluginCommand("dfusion","Init dfusion system.",dfusion));
commands.push_back(PluginCommand("lua", "Run interactive interpreter.\ commands.push_back(PluginCommand("lua", "Run interactive interpreter.\
\n Options: <filename> = run <filename> instead",lua_run)); \n Options: <filename> = run <filename> instead",lua_run));
@ -73,6 +75,7 @@ DFhackCExport command_result plugin_onupdate ( Core * c )
catch(lua::exception &e) catch(lua::exception &e)
{ {
c->con.printerr("Error OnTick:%s\n",e.what()); c->con.printerr("Error OnTick:%s\n",e.what());
c->con.printerr("%s",lua::DebugDump(lua::glua::Get()));
c->con.msleep(1000); c->con.msleep(1000);
} }
} }
@ -99,6 +102,7 @@ void InterpreterLoop(Core* c)
catch(lua::exception &e) catch(lua::exception &e)
{ {
con.printerr("Error:%s\n",e.what()); con.printerr("Error:%s\n",e.what());
c->con.printerr("%s",lua::DebugDump(lua::glua::Get()));
s.settop(0); s.settop(0);
} }
con.lineedit(">>",curline); con.lineedit(">>",curline);
@ -119,6 +123,7 @@ DFhackCExport command_result lua_run (Core * c, vector <string> & parameters)
catch(lua::exception &e) catch(lua::exception &e)
{ {
con.printerr("Error:%s\n",e.what()); con.printerr("Error:%s\n",e.what());
c->con.printerr("%s",lua::DebugDump(lua::glua::Get()));
} }
} }
else else
@ -143,6 +148,7 @@ DFhackCExport command_result dfusion (Core * c, vector <string> & parameters)
catch(lua::exception &e) catch(lua::exception &e)
{ {
con.printerr("Error:%s\n",e.what()); con.printerr("Error:%s\n",e.what());
c->con.printerr("%s",lua::DebugDump(lua::glua::Get()));
} }
s.settop(0);// clean up s.settop(0);// clean up
mymutex->unlock(); mymutex->unlock();

@ -1,11 +1,43 @@
#ifndef LUA_MISC_H #ifndef LUA_MISC_H
#define LUA_MISC_H #define LUA_MISC_H
#include <map>
#include <dfhack/Core.h>
#include <dfhack/Process.h>
#include "luamain.h" #include "luamain.h"
#include "OutFile.h"
namespace lua namespace lua
{ {
typedef std::map<std::string,void *> mapPlugs;
class PlugManager
{
public:
mapPlugs GetList(){return plugs;};
uint32_t AddNewPlug(std::string name,uint32_t size,uint32_t loc=0);
uint32_t FindPlugin(std::string name);
static PlugManager &GetInst()
{
void *p;
p=DFHack::Core::getInstance().GetData("dfusion_manager");
if(p==0)
{
p=new PlugManager;
DFHack::Core::getInstance().RegisterData(p,"dfusion_manager");
}
return *static_cast<PlugManager*>(p);
};
protected:
private:
PlugManager(){};
mapPlugs plugs;
};
void RegisterMisc(lua::state &st); void RegisterMisc(lua::state &st);
} }

@ -34,7 +34,7 @@ function GetRegionIn(pos)
--if(v["read"])then num=num+1 end --if(v["read"])then num=num+1 end
--if(v["write"])then num=num+10 end --if(v["write"])then num=num+10 end
--if(v["execute"]) then num=num+100 end --if(v["execute"]) then num=num+100 end
print(string.format("%d %x->%x %s %x",k,v["start"],v["end"],v.name,pos)) --print(string.format("%d %x->%x %s %x",k,v["start"],v["end"],v.name,pos))
if pos>=v.start and pos<=v["end"] then if pos>=v.start and pos<=v["end"] then
return v return v
end end
@ -69,6 +69,11 @@ function lockDF()
reg["write"]=false reg["write"]=false
Process.setPermisions(reg,reg) Process.setPermisions(reg,reg)
end end
function SetExecute(pos)
local reg=GetRegionIn(pos)
reg.execute=true
Process.setPermisions(reg,reg) -- TODO maybe make a page with only execute permisions or sth
end
-- engine bindings -- engine bindings
engine=engine or {} engine=engine or {}
engine.peekd=Process.readDWord engine.peekd=Process.readDWord
@ -163,6 +168,31 @@ function engine.pokepattern(offset,pattern,val)
end end
end end
function engine.LoadModData(file)
local T2={}
T2.symbols={}
T2.data,T2.size=engine.loadobj(file)
data,modsize=engine.loadobj(file)
local T=engine.loadobjsymbols(file)
for k,v in pairs(T) do
if v.pos~=0 then
T2.symbols[v.name]=v.pos
end
end
return T2
end
function engine.FindMarker(moddata,name)
if moddata.symbols[name] ~=nil then
return engine.findmarker(0xDEADBEEF,moddata.data,moddata.size,moddata.symbols[name])
end
end
function engine.installMod(file,name,bonussize)
local T=engine.LoadModData(file)
local modpos,modsize=engine.loadmod(file,name,bonussize)
T.pos=modpos
return T
end
it_menu={} it_menu={}
it_menu.__index=it_menu it_menu.__index=it_menu
@ -290,8 +320,11 @@ end
function GetRaceToken(p) --actually gets token... function GetRaceToken(p) --actually gets token...
local vec=engine.peek(offsets.getEx('CreatureGloss'),ptr_vector) local vec=engine.peek(offsets.getEx('CreatureGloss'),ptr_vector)
--print("Vector ok")
local off=vec:getval(p) local off=vec:getval(p)
--print("Offset:"..off)
local crgloss=engine.peek(off,ptr_CrGloss) local crgloss=engine.peek(off,ptr_CrGloss)
--print("Peek ok")
return crgloss.token:getval() return crgloss.token:getval()
end end
function BuildNameTable() function BuildNameTable()

@ -0,0 +1 @@
as -a --32 -o embark.o embark.asm

@ -0,0 +1,9 @@
.intel_syntax
mov eax , [esp+0x1C]
caste:
movsx eax, word ptr[eax*2+0xdeadbeef]
mov [esp+0x04],eax
mov eax , [esp+0x1C]
race:
movzx eax,word ptr [eax*2+0xDEADBEEF]
ret

@ -0,0 +1,75 @@
function MakeTable(modpos,modsize,names)
count=0
castes={}
--print("Making table")
for _,line in pairs(names) do
--print("Line:"..line)
tpos=string.find(line,":")
if tpos~=nil then
--print("Was line:"..line)
table.insert(castes,tonumber(string.sub(line,tpos+1)))
line=string.sub(line,1,tpos-1)
--print("IS line:"..line)
else
table.insert(castes,-1)
end
if RaceTable[line] == nil then
error("Failure, "..line.." not found!")
end
engine.pokew(modpos+modsize+count*2,RaceTable[line]) -- add race
count = count + 1
end
i=0
for _,caste in pairs(castes) do
engine.pokew(modpos+modsize+count*2+i*2,caste) -- add caste
i= i+1
end
engine.poked(stoff,count)
return count
end
function embark(names)
RaceTable=RaceTable or BuildNameTable()
mypos=engine.getmod('Embark')
stoff=offsets.getEx('StartDwarfs')
if mypos then --if mod already loaded
print("Mod already loaded @:"..mypos.." just updating")
modpos=mypos
_,modsize=engine.loadobj('dfusion/embark/embark.o')
count=MakeTable(modpos,modsize,names) --just remake tables
else
tofind=offsets.getEx('CurrentRace')
loc=offsets.find(stoff,0xa1,DWORD_,tofind)
print("found:"..loc)
if((loc~=0)and(loc-stoff<1000)) then
modpos,modsize=engine.loadmod('dfusion/embark/embark.o','Embark',256)
count=MakeTable(modpos,modsize,names)
engine.poked(modpos+0x18,modpos+modsize) --fix array start for race
engine.poked(modpos+0x08,modpos+modsize+count*2) --fix array start for caste
print("sucess loading mod @:"..modpos)
-- build race vector after module.
--finaly poke in the call!
engine.pokeb(loc,0x6a)
engine.pokeb(loc+1,0xFF)
engine.pokeb(loc+2,0xe8)
engine.poked(loc+3,modpos-loc-7)
--engine.pokeb(loc+5,0x90)
SetExecute(modpos)
else
error("did not find patch location, failing...")
end
end
end
if not(FILE)then
names=ParseNames("dfusion/embark/races.txt")--io.open("plugins/embark/races.txt"):lines()
embark(names)
end

@ -0,0 +1,7 @@
DWARF:1
ELF:0
ELF:0
DWARF:0
DWARF:0
HUMAN:0
HUMAN:0

@ -0,0 +1 @@
as -anl --32 -o friendship.o friendship.asm

@ -0,0 +1,102 @@
.intel_syntax
push eax
mov eax,[esp+0x04]
push ebx
pushfd
mov eax,[eax] # get a byte after the call this procedure to analyze what register holds cr ptr
jmptbl:
cmp al,0x81
jz regC
cmp al,0x82
jz regD
cmp al,0x83
jz regB
cmp al,0x85
jz regBP
cmp al,0x86
jz regESI
cmp al,0x87
jz regEDI
cmp al,0x88
jz regA
cmp al,0x8A
jz regD
cmp al,0x8B
jz regB
cmp al,0x8D
jz regBP
cmp al,0x8E
jz regESI
cmp al,0x8F
jz regEDI
cmp al,0x90
jz regA
cmp al,0x91
jz regC
cmp al,0x93
jz regB
cmp al,0x95
jz regBP
cmp al,0x96
jz regESI
cmp al,0x97
jz regEDI
jmp fail
regA:
mov eax, [esp+0x8]
mov eax, [eax+0x8c]
jmp compare
regC:
mov eax, [ecx+0x8c]
jmp compare
regB:
mov eax, [ebx+0x8c]
jmp compare
regD:
mov eax, [edx+0x8c]
jmp compare
regBP:
mov eax, [ebp+0x8c]
jmp compare
regESI:
mov eax, [esi+0x8c]
jmp compare
regEDI:
mov eax, [edi+0x8c]
#jmp compare
compare:
push ecx
mov ebx,0xDEADBEEF #write a pointer to the list of allowed races
mov ecx,2000 #write a number of allowed races
loop1:
cmp word[ebx+ecx*2],ax
jz endok
dec ecx
cmp ecx ,-1
jnz loop1
pop ecx
popfd
jmp fail
endok:
pop ecx
popfd
cmp eax,eax
jmp endfinal
fail:
xor ebx,ebx
xor eax,eax
inc eax
cmp eax,ebx
endfinal:
pop ebx
pop eax
mov [0xFEEDBEEF],eax #write a pointer to safe location (usually after this)
pop eax
pushfd
inc eax #skip one instruction
popfd
push eax
mov eax,[0xFEEDBEEF] #write a pointer to safe location (same as above)
ret

@ -0,0 +1,35 @@
function friendship_in.install(names)
RaceTable=RaceTable or BuildNameTable()
mypos=engine.getmod("Friendship")
if mypos then
modpos=mypos
_,modsize=engine.loadobj("dfusion/friendship/friendship.o")
_=nil
else
modpos,modsize=engine.loadmod("dfusion/friendship/friendship.o","Friendship",1024)
print(string.format("Loaded module @:%x",modpos))
end
count=0
for _,v in pairs(names) do
if RaceTable[v] == nil then
--print("Failure, "..v.." not found!")
error("Failure, "..v.." not found!")
--break --maybe some indication of failure? and cleanup?
end
engine.pokew(modpos+modsize+count*2+4+2,RaceTable[v]) -- for some reason it compiled strangely
-- cmp word[ebx+ecx*2],ax -> cmp word[ebx+ecx*2+2],ax
count = count + 1
end
engine.poked(modpos+0x8f,modpos+modsize+4) -- set ptr to creatures
engine.poked(modpos+0x94,count) -- set count of creatures
engine.poked(modpos+0xb9,modpos+modsize) -- set safe location
engine.poked(modpos+0xc3,modpos+modsize) -- set safe location
SetExecute(modpos)
end
function pokeCall(off)
engine.pokeb(off,0xe8)
b=engine.peekb(off+1)
engine.poked(off+1,modpos-off-5)
engine.pokeb(off+5,b)
end

@ -0,0 +1,56 @@
function friendship_in.patch()
pos=GetTextRegion().start
local crace=offsets.getEx("CurrentRace")
hits={}
i=1
repeat
--todo make something better/smarter...
pos1=offsets.find(pos+7,0x0f,0xBF,ANYBYTE,DWORD_,crace) -- movsx
pos2=offsets.find(pos+7,0x66,0xa1,DWORD_,crace) -- mov ax,[ptr]
pos3=offsets.find(pos+7,0xa1,DWORD_,crace) -- mov eax,[ptr]
pos4=offsets.find(pos+7,0x66,0x8b,ANYBYTE,DWORD_,crace) -- mov ANYREG,[ptr]
--pos5=offsets.find(pos+7,0x66,0x8b,0x15,DWORD_,crace) -- mov dx,[ptr]
pos=minEx(pos1,pos2,pos3,pos4)
if pos ~=0 then
hits[i]=pos
i=i+1
print(string.format("Found at %x",pos))
end
until pos==0
print("=======================================")
for _,p in pairs(hits) do
myp=p
repeat
--print(string.format("Analyzing %x...",p))
pos1=offsets.find(myp,0x39,ANYBYTE,0x8c,00,00,00) -- compare [reg+08c] (creature race) with any reg
pos2=offsets.find(myp,0x3b,ANYBYTE,0x8c,00,00,00) -- compare any reg with [reg+08c] (creature race)
pos=minEx(pos1,pos2)
if pos ~=0 then
if(pos-p>250) then
--this here does not work yet...
--[[pos =offsets.find(p,CALL)
print(string.format("Distance to call:%x",pos-p))
print(string.format("Call: %x",signDword(engine.peekd(pos+1)+pos)))
pos=analyzeF(signDword(signDword(engine.peekd(pos+1)+pos)))
print(string.format("Cmp @:%x",pos))]]--
print(string.format("skipping %x... Cmp too far away (dist=%i)",p,pos-p))
else
--print(string.format("Found at %x, simple compare",pos))
--print(string.format("Distance =%x",pos-p))
--patch compares
pokeCall(pos)
end
else
break
end
myp=myp+pos+6
if myp-p >250 then break end
until false
end
end

@ -0,0 +1,60 @@
--if(mypos~=0) then
--print("plugin already active")
--maybe set options for reinit?
--return
--end
function analyzeF(off)
pos=offsets.find(off,0x39,ANYBYTE,0x8c,00,00,00,EOL)
print(string.format("Compare at:%x",pos))
if pos ==0 then
return 0
end
if(pos-off>0x100) then
print(string.format("Distance to cmp:%x",pos-off))
pos =offsets.find(off,CALL,EOL)
print(string.format("Distance to call:%x",pos-off))
return 0
--return analyzeF(pos)
else
return pos
end
end
function minEx(...)
local imin=arg[1]
for _,v in ipairs(arg) do
if imin> v and v~=0 then
imin=v
end
end
return imin
end
function signDword(dw)
if(dw>0xFFFFFFFF) then
return dw-0xFFFFFFFF
end
return dw
end
--[[
Warning: not all mov's are acounted for. Found one: mov EAX,WORD PTR[EBP+1EF4] WTF??
Two more compares are missing. There are calls instead (same function)
]]--
if not(FILE) then
print("race num:"..engine.peekw(offsets.getEx("CurrentRace")))
print("Your current race is:"..GetRaceToken(engine.peekw(offsets.getEx('CurrentRace'))))
print("If this is wrong please quit now (by ctrl+c)")
io.stdin:read()
end
friendship_in={}
dofile("dfusion/friendship/install.lua")
dofile("dfusion/friendship/patch.lua")
if not(FILE) then
names=ParseNames("dfusion/friendship/races.txt")--io.open("plugins/friendship/races.txt"):lines()
friendship_in.install(names)
friendship_in.patch()
end
function friendship(names)
friendship_in.install(names)
friendship_in.patch()
end

@ -0,0 +1,8 @@
DWARF
GOBLIN
ELF
HUMAN
KOBOLD
GREMLIN
TIGERMAN
ANT_MAN

@ -35,5 +35,7 @@ plugins={}
table.insert(plugins,{"simple_embark","A simple embark dwarf count editor"}) table.insert(plugins,{"simple_embark","A simple embark dwarf count editor"})
table.insert(plugins,{"items","A collection of item hacking tools"}) table.insert(plugins,{"items","A collection of item hacking tools"})
table.insert(plugins,{"offsets","Find all offsets"}) table.insert(plugins,{"offsets","Find all offsets"})
table.insert(plugins,{"friendship","Multi race fort enabler"})
table.insert(plugins,{"embark","Multi race embark"})
mainmenu(plugins) mainmenu(plugins)

@ -70,6 +70,7 @@ function offsets.find(startoffset,...)
--print("searching in:"..reg.name) --print("searching in:"..reg.name)
endadr=reg["end"] endadr=reg["end"]
end end
--print(string.format("Searching (%x->%x)",startoffset,endadr))
local h=hexsearch(startoffset,endadr,...) local h=hexsearch(startoffset,endadr,...)
local pos=h:find() local pos=h:find()
h=nil h=nil

@ -68,7 +68,9 @@ function ptr_vector:size()
end end
ptr_vector.type=DWORD ptr_vector.type=DWORD
function ptr_vector:getval(num) function ptr_vector:getval(num)
if self.st==0 then return 0 end if self.st==0 then return error("Vector empty.") end
--print("Wants:"..num.." size:"..self:size())
if num>=self:size() then error("Index out of bounds in vector.") end
return engine.peek(self.st+engine.sizeof(self.type)*num,self.type) return engine.peek(self.st+engine.sizeof(self.type)*num,self.type)
end end
function ptr_vector:setval(num,val) function ptr_vector:setval(num,val)

@ -19,6 +19,11 @@ inline bool Hexsearch::Compare(int a,int b)
} }
void Hexsearch::ReparseArgs() void Hexsearch::ReparseArgs()
{ {
union
{
uint32_t val;
uint8_t bytes[4];
}B;
SearchArgType targ; SearchArgType targ;
targ=args_; targ=args_;
args_.clear(); args_.clear();
@ -27,10 +32,10 @@ void Hexsearch::ReparseArgs()
if(targ[i]==DWORD_) if(targ[i]==DWORD_)
{ {
i++; i++;
B.val=targ[i];
for(int j=0;j<4;j++) for(int j=0;j<4;j++)
{ {
args_.push_back((targ[i]&(0xff<<i))>>i);//TODO is this correct??? args_.push_back(B.bytes[j]);
} }
i++; i++;
} }

@ -1,3 +1,152 @@
#include "lua_Misc.h" #include "lua_Misc.h"
uint32_t lua::PlugManager::AddNewPlug(std::string name,uint32_t size,uint32_t loc)
{
void *p;
if(size!=0)
p=new unsigned char[size];
else
p=(void*)loc;
plugs[name]=p;
return (uint32_t)p;
}
uint32_t lua::PlugManager::FindPlugin(std::string name)
{
mapPlugs::iterator it=plugs.find(name);
if(it!=plugs.end())
return (uint32_t)it->second;
else
return 0;
}
void lua::RegisterMisc(lua::state &st);
static int LoadMod(lua_State *L)
{
lua::state st(L);
std::string modfile=st.as<std::string>(1);
std::string modname=st.as<std::string>(2);
uint32_t size_add=st.as<uint32_t>(0,3);
OutFile::File f(modfile);
uint32_t size=f.GetTextSize();
uint32_t pos=lua::PlugManager::GetInst().AddNewPlug(modname,size+size_add);
char *buf;
buf=new char[size];
f.GetText(buf);
//std::cout<<"poking @:"<<std::hex<<pos<<"size :"<<size<<std::endl;
DFHack::Core::getInstance().p->write(pos,size,(uint8_t*)buf);
delete [] buf;
st.push(pos);
st.push(size);
return 2;
}
static int LoadObj(lua_State *L)
{
lua::state st(L);
std::string modfile=st.as<std::string>(1);
OutFile::File f(modfile);
size_t s=f.GetTextSize();
void *p=st.newuserdata(s); //TODO does it leak memory??
f.GetText((char*)p);
st.push(s);
return 2;
}
static int FindMarker(lua_State *L) // marker, void ptr, size, start
{
lua::state st(L);
union
{
unsigned char bytes[4];
size_t mark;
}M;
M.mark=st.as<size_t>(1);
unsigned char *p=(unsigned char *)lua_touserdata(L, 2);//st.as<lua::userdata>(2);
size_t size=st.as<size_t>(3);
size_t start=st.as<size_t>(4);
for(size_t i=start;i<size;i++)
{
bool ok;
ok=true;
if(p[i]==M.bytes[0])
{
for(size_t j=0;j<4;j++)
{
if(p[i+j]!=M.bytes[j])
{
ok=false;
break;
}
}
if(ok)
{
st.push(i);
return 1;
}
}
}
return 0;
}
static int LoadObjSymbols(lua_State *L)
{
lua::state st(L);
std::string modfile=st.as<std::string>(1);
OutFile::File f(modfile);
OutFile::vSymbol vec=f.GetSymbols();
OutFile::Symbol S;
st.newtable();
for(size_t i=0;i<vec.size();i++)
{
st.push(i);
S=vec[i];
st.newtable();
st.push(S.name);
st.setfield("name");
st.push(S.pos);
st.setfield("pos");
st.settable();
}
return 1;
}
static int NewMod(lua_State *L)
{
lua::state st(L);
std::string modname=st.as<std::string>(1);
size_t size=st.as<size_t>(2);
size_t loc=st.as<size_t>(3,0);
uint32_t pos=lua::PlugManager::GetInst().AddNewPlug(modname,size,loc);
st.push(pos);
return 1;
}
static int GetMod(lua_State *L)
{
lua::state st(L);
std::string modname=st.as<std::string>(1);
uint32_t pos=lua::PlugManager::GetInst().FindPlugin(modname);
if(pos==0)
st.push();
else
st.push(pos);
return 1;
}
const luaL_Reg lua_misc_func[]=
{
{"loadmod",LoadMod},
{"getmod",GetMod},
{"loadobj",LoadObj},
{"loadobjsymbols",LoadObjSymbols},
{"findmarker",FindMarker},
{"newmod",NewMod},
{NULL,NULL}
};
void lua::RegisterMisc(lua::state &st)
{
st.getglobal("engine");
if(st.is<lua::nil>())
{
st.pop();
st.newtable();
}
lua::RegFunctionsLocal(st, lua_misc_func);
st.setglobal("engine");
}