Merge branch 'master', remote branch 'peterix/master'
commit
e9a7026342
File diff suppressed because it is too large
Load Diff
@ -1,778 +0,0 @@
|
|||||||
/*
|
|
||||||
www.sourceforge.net/projects/dfhack
|
|
||||||
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
|
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied
|
|
||||||
warranty. In no event will the authors be held liable for any
|
|
||||||
damages arising from the use of this software.
|
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any
|
|
||||||
purpose, including commercial applications, and to alter it and
|
|
||||||
redistribute it freely, subject to the following restrictions:
|
|
||||||
|
|
||||||
1. The origin of this software must not be misrepresented; you must
|
|
||||||
not claim that you wrote the original software. If you use this
|
|
||||||
software in a product, an acknowledgment in the product documentation
|
|
||||||
would be appreciated but is not required.
|
|
||||||
|
|
||||||
2. Altered source versions must be plainly marked as such, and
|
|
||||||
must not be misrepresented as being the original software.
|
|
||||||
|
|
||||||
3. This notice may not be removed or altered from any source
|
|
||||||
distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Internal.h"
|
|
||||||
#include "dfhack/DFMemInfo.h"
|
|
||||||
#include "dfhack/DFError.h"
|
|
||||||
#include "dfhack/DFProcess.h"
|
|
||||||
|
|
||||||
//Inital amount of space in levels vector (since we usually know the number, efficent!)
|
|
||||||
#define NUM_RESERVE_LVLS 20
|
|
||||||
#define NUM_RESERVE_MOODS 6
|
|
||||||
using namespace DFHack;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Common data types
|
|
||||||
*/
|
|
||||||
namespace DFHack
|
|
||||||
{
|
|
||||||
struct t_type
|
|
||||||
{
|
|
||||||
t_type(uint32_t assign, uint32_t type, std::string classname)
|
|
||||||
:classname(classname),assign(assign),type(type){};
|
|
||||||
std::string classname;
|
|
||||||
uint32_t assign;
|
|
||||||
uint32_t type;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct t_class
|
|
||||||
{
|
|
||||||
t_class(const t_class &old)
|
|
||||||
{
|
|
||||||
classname = old.classname;
|
|
||||||
vtable = old.vtable;
|
|
||||||
assign = old.assign;
|
|
||||||
type_offset = old.type_offset;
|
|
||||||
for(uint32_t i = 0; i < old.subs.size();i++)
|
|
||||||
{
|
|
||||||
t_type * t = new t_type (*old.subs[i]);
|
|
||||||
subs.push_back(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t_class ()
|
|
||||||
{
|
|
||||||
vtable = 0;
|
|
||||||
assign = 0;
|
|
||||||
type_offset = 0;
|
|
||||||
}
|
|
||||||
~t_class()
|
|
||||||
{
|
|
||||||
for(uint32_t i = 0; i < subs.size();i++)
|
|
||||||
{
|
|
||||||
delete subs[i];
|
|
||||||
}
|
|
||||||
subs.clear();
|
|
||||||
}
|
|
||||||
std::string classname;
|
|
||||||
uint32_t vtable;
|
|
||||||
uint32_t assign;// index to typeclass array if multiclass. return value if not.
|
|
||||||
uint32_t type_offset; // offset of type data for multiclass
|
|
||||||
std::vector<t_type *> subs;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Private data
|
|
||||||
*/
|
|
||||||
class memory_info::Private
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
map <string, uint32_t> addresses;
|
|
||||||
map <string, int32_t> offsets;
|
|
||||||
map <string, uint32_t> hexvals;
|
|
||||||
map <string, string> strings;
|
|
||||||
|
|
||||||
vector<string> professions;
|
|
||||||
vector<string> jobs;
|
|
||||||
vector<string> skills;
|
|
||||||
vector<DFHack::t_level> levels;
|
|
||||||
vector< vector<string> > traits;
|
|
||||||
vector<string> moods;
|
|
||||||
map <uint32_t, string> labors;
|
|
||||||
|
|
||||||
// storage for class and multiclass
|
|
||||||
vector<t_class *> classes;
|
|
||||||
|
|
||||||
// cache for faster name lookup, indexed by classID
|
|
||||||
vector<string> classnames;
|
|
||||||
// map between vptr and class id, needs further type id lookup for multi-classes, not inherited
|
|
||||||
map<uint32_t, t_class *> classIDs;
|
|
||||||
|
|
||||||
// index for the next added class
|
|
||||||
uint32_t classindex;
|
|
||||||
|
|
||||||
int32_t base;
|
|
||||||
Process * p; // the process this belongs to
|
|
||||||
|
|
||||||
string version;
|
|
||||||
OSType OS;
|
|
||||||
};
|
|
||||||
|
|
||||||
// normal constructor
|
|
||||||
memory_info::memory_info()
|
|
||||||
:d(new Private)
|
|
||||||
{
|
|
||||||
d->base = 0;
|
|
||||||
d->p = 0;
|
|
||||||
d->classindex = 0;
|
|
||||||
d->levels.reserve(NUM_RESERVE_LVLS);
|
|
||||||
d->moods.reserve(NUM_RESERVE_MOODS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy constructor
|
|
||||||
memory_info::memory_info(const memory_info &old)
|
|
||||||
:d(new Private)
|
|
||||||
{
|
|
||||||
d->version = old.d->version;
|
|
||||||
d->OS = old.d->OS;
|
|
||||||
d->addresses = old.d->addresses;
|
|
||||||
d->offsets = old.d->offsets;
|
|
||||||
d->hexvals = old.d->hexvals;
|
|
||||||
d->strings = old.d->strings;
|
|
||||||
d->base = old.d->base;
|
|
||||||
//d->classes = old.d->classes;
|
|
||||||
for(uint32_t i = 0; i < old.d->classes.size(); i++)
|
|
||||||
{
|
|
||||||
t_class * copy = new t_class(*old.d->classes[i]);
|
|
||||||
d->classes.push_back(copy);
|
|
||||||
}
|
|
||||||
d->classnames = old.d->classnames;
|
|
||||||
d->classindex = old.d->classindex;
|
|
||||||
d->professions = old.d->professions;
|
|
||||||
d->jobs = old.d->jobs;
|
|
||||||
d->skills = old.d->skills;
|
|
||||||
d->traits = old.d->traits;
|
|
||||||
d->labors = old.d->labors;
|
|
||||||
d->levels = old.d->levels;
|
|
||||||
d->moods = old.d->moods;
|
|
||||||
}
|
|
||||||
void memory_info::setParentProcess(Process * _p)
|
|
||||||
{
|
|
||||||
d->p = _p;
|
|
||||||
}
|
|
||||||
|
|
||||||
// destructor
|
|
||||||
memory_info::~memory_info()
|
|
||||||
{
|
|
||||||
// delete the vtables
|
|
||||||
for(uint32_t i = 0; i < d->classes.size();i++)
|
|
||||||
{
|
|
||||||
delete d->classes[i];
|
|
||||||
}
|
|
||||||
// delete our data
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
void memory_info::setVersion(const char * v)
|
|
||||||
{
|
|
||||||
d->version = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setVersion(const string &v)
|
|
||||||
{
|
|
||||||
d->version = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
string memory_info::getVersion()
|
|
||||||
{
|
|
||||||
return d->version;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setOS(const char *os)
|
|
||||||
{
|
|
||||||
string oss = os;
|
|
||||||
if(oss == "windows")
|
|
||||||
d->OS = OS_WINDOWS;
|
|
||||||
else if(oss == "linux")
|
|
||||||
d->OS = OS_LINUX;
|
|
||||||
else
|
|
||||||
d->OS = OS_BAD;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setOS(const string &os)
|
|
||||||
{
|
|
||||||
if(os == "windows")
|
|
||||||
d->OS = OS_WINDOWS;
|
|
||||||
else if(os == "linux")
|
|
||||||
d->OS = OS_LINUX;
|
|
||||||
else
|
|
||||||
d->OS = OS_BAD;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setOS(OSType os)
|
|
||||||
{
|
|
||||||
if(os >= OS_WINDOWS && os < OS_BAD)
|
|
||||||
{
|
|
||||||
d->OS = os;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
d->OS = OS_BAD;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
memory_info::OSType memory_info::getOS() const
|
|
||||||
{
|
|
||||||
return d->OS;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t memory_info::getBase () const
|
|
||||||
{
|
|
||||||
return d->base;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setBase (const string &s)
|
|
||||||
{
|
|
||||||
d->base = strtol(s.c_str(), NULL, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setBase (const uint32_t b)
|
|
||||||
{
|
|
||||||
d->base = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setOffset (const string & key, const string & value)
|
|
||||||
{
|
|
||||||
int32_t offset = strtol(value.c_str(), NULL, 16);
|
|
||||||
d->offsets[key] = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setAddress (const string & key, const string & value)
|
|
||||||
{
|
|
||||||
uint32_t address = strtol(value.c_str(), NULL, 16);
|
|
||||||
d->addresses[key] = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setHexValue (const string & key, const string & value)
|
|
||||||
{
|
|
||||||
uint32_t hexval = strtol(value.c_str(), NULL, 16);
|
|
||||||
d->hexvals[key] = hexval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setString (const string & key, const string & value)
|
|
||||||
{
|
|
||||||
d->strings[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void memory_info::setLabor(const string & key, const string & value)
|
|
||||||
{
|
|
||||||
uint32_t keyInt = strtol(key.c_str(), NULL, 10);
|
|
||||||
d->labors[keyInt] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void memory_info::setProfession (const string & key, const string & value)
|
|
||||||
{
|
|
||||||
uint32_t keyInt = strtol(key.c_str(), NULL, 10);
|
|
||||||
if(d->professions.size() <= keyInt)
|
|
||||||
{
|
|
||||||
d->professions.resize(keyInt+1,"");
|
|
||||||
}
|
|
||||||
d->professions[keyInt] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void memory_info::setJob (const string & key, const string & value)
|
|
||||||
{
|
|
||||||
uint32_t keyInt = strtol(key.c_str(), NULL, 10);
|
|
||||||
if(d->jobs.size() <= keyInt)
|
|
||||||
{
|
|
||||||
d->jobs.resize(keyInt+1);
|
|
||||||
}
|
|
||||||
d->jobs[keyInt] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void memory_info::setSkill (const string & key, const string & value)
|
|
||||||
{
|
|
||||||
uint32_t keyInt = strtol(key.c_str(), NULL, 10);
|
|
||||||
if(d->skills.size() <= keyInt){
|
|
||||||
d->skills.resize(keyInt+1);
|
|
||||||
}
|
|
||||||
d->skills[keyInt] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void memory_info::setLevel(const std::string &nLevel,
|
|
||||||
const std::string &nName,
|
|
||||||
const std::string &nXp)
|
|
||||||
{
|
|
||||||
uint32_t keyInt = strtol(nLevel.c_str(), NULL, 10);
|
|
||||||
|
|
||||||
if(d->levels.size() <= keyInt)
|
|
||||||
d->levels.resize(keyInt+1);
|
|
||||||
|
|
||||||
d->levels[keyInt].level = keyInt;
|
|
||||||
d->levels[keyInt].name = nName;
|
|
||||||
d->levels[keyInt].xpNxtLvl = strtol(nXp.c_str(), NULL, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
void memory_info::setMood(const std::string &id, const std::string &mood)
|
|
||||||
{
|
|
||||||
uint32_t keyInt = strtol(id.c_str(), NULL, 10);
|
|
||||||
|
|
||||||
if(d->moods.size() <= keyInt)
|
|
||||||
d->moods.resize(keyInt+1);
|
|
||||||
|
|
||||||
d->moods[keyInt] = mood;
|
|
||||||
}
|
|
||||||
|
|
||||||
void memory_info::setTrait(const string & key,
|
|
||||||
const string & value,
|
|
||||||
const string & zero,
|
|
||||||
const string & one,
|
|
||||||
const string & two,
|
|
||||||
const string & three,
|
|
||||||
const string & four,
|
|
||||||
const string & five)
|
|
||||||
{
|
|
||||||
uint32_t keyInt = strtol(key.c_str(), NULL, 10);
|
|
||||||
if(d->traits.size() <= keyInt)
|
|
||||||
{
|
|
||||||
d->traits.resize(keyInt+1);
|
|
||||||
}
|
|
||||||
d->traits[keyInt].push_back(zero);
|
|
||||||
d->traits[keyInt].push_back(one);
|
|
||||||
d->traits[keyInt].push_back(two);
|
|
||||||
d->traits[keyInt].push_back(three);
|
|
||||||
d->traits[keyInt].push_back(four);
|
|
||||||
d->traits[keyInt].push_back(five);
|
|
||||||
d->traits[keyInt].push_back(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: next three methods should use some kind of custom container so it doesn't have to search so much.
|
|
||||||
t_class * memory_info::setClass (const char * name, uint32_t vtable, uint32_t typeoffset)
|
|
||||||
{
|
|
||||||
if(name == 0)
|
|
||||||
return 0;
|
|
||||||
for (uint32_t i=0; i<d->classes.size(); i++)
|
|
||||||
{
|
|
||||||
if(d->classes[i]->classname == name)
|
|
||||||
{
|
|
||||||
if(vtable != 0)
|
|
||||||
d->classes[i]->vtable = vtable;
|
|
||||||
if(typeoffset != 0)
|
|
||||||
d->classes[i]->type_offset = typeoffset;
|
|
||||||
return d->classes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t_class *cls = new t_class();
|
|
||||||
// get an unique ID and add ourselves to the index
|
|
||||||
cls->assign = d->classindex;
|
|
||||||
cls->classname = name;
|
|
||||||
d->classnames.push_back(name);
|
|
||||||
|
|
||||||
// vtables no longer a requirement
|
|
||||||
cls->vtable = vtable;
|
|
||||||
|
|
||||||
// multi class yes/no
|
|
||||||
cls->type_offset = typeoffset;
|
|
||||||
|
|
||||||
d->classes.push_back(cls);
|
|
||||||
d->classindex++;
|
|
||||||
return cls;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void memory_info::setClassChild (t_class * parent, const char * name, const char * type)
|
|
||||||
{
|
|
||||||
vector <t_type *>& vec = parent->subs;
|
|
||||||
for (uint32_t i=0; i<vec.size(); i++)
|
|
||||||
{
|
|
||||||
if(vec[i]->classname == name)
|
|
||||||
{
|
|
||||||
vec[i]->type = strtol(type, NULL, 16);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// new multiclass child
|
|
||||||
t_type *mcc = new t_type(d->classindex,strtol(type, NULL, 16),name);
|
|
||||||
d->classnames.push_back(name);
|
|
||||||
vec.push_back(mcc);
|
|
||||||
d->classindex++;
|
|
||||||
|
|
||||||
//cout << " classtype " << name << ", assign " << mcc->assign << ", vtable " << mcc->type << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: stupid. we need a better container
|
|
||||||
bool memory_info::resolveObjectToClassID(const uint32_t address, int32_t & classid)
|
|
||||||
{
|
|
||||||
uint32_t vtable = d->p->readDWord(address);
|
|
||||||
// try to find the vtable in our cache
|
|
||||||
map<uint32_t, t_class *>::iterator it;
|
|
||||||
it = d->classIDs.find(vtable);
|
|
||||||
t_class * cl;
|
|
||||||
|
|
||||||
// class found in cache?
|
|
||||||
if(it != d->classIDs.end())
|
|
||||||
{
|
|
||||||
cl = (*it).second;
|
|
||||||
}
|
|
||||||
else// couldn't find?
|
|
||||||
{
|
|
||||||
// we set up the class for the first time
|
|
||||||
string classname = d->p->readClassName(vtable);
|
|
||||||
d->classIDs[vtable] = cl = setClass(classname.c_str(),vtable);
|
|
||||||
}
|
|
||||||
// and isn't a multi-class
|
|
||||||
if(!cl->type_offset)
|
|
||||||
{
|
|
||||||
// return
|
|
||||||
classid = cl->assign;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// and is a multiclass
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// find the type
|
|
||||||
vector <t_type*>& vec = cl->subs;
|
|
||||||
uint32_t type = d->p->readWord(address + cl->type_offset);
|
|
||||||
// return typed building if successful
|
|
||||||
//FIXME: the vector should map directly to the type IDs here, so we don't have to mess with O(n) search
|
|
||||||
for (uint32_t k = 0; k < vec.size();k++)
|
|
||||||
{
|
|
||||||
if(vec[k]->type == type)
|
|
||||||
{
|
|
||||||
//cout << " multi " << address + classes[i].type_offset << " " << vec[k].classname << endl;
|
|
||||||
classid = vec[k]->assign;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// couldn't find the type... now what do we do here? throw?
|
|
||||||
// this is a case where it might be a good idea, because it uncovers some deeper problems
|
|
||||||
// we return the parent class instead, it doesn't change the API semantics and sort-of makes sense
|
|
||||||
classid = cl->assign;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//ALERT: doesn't care about multiclasses
|
|
||||||
bool memory_info::resolveClassnameToVPtr(const string classname, uint32_t & vptr)
|
|
||||||
{
|
|
||||||
// FIXME: another stupid search.
|
|
||||||
for(uint32_t i = 0;i< d->classes.size();i++)
|
|
||||||
{
|
|
||||||
//if(classes[i].)
|
|
||||||
if(d->classes[i]->classname == classname) // got class
|
|
||||||
{
|
|
||||||
vptr = d->classes[i]->vtable;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// we failed to find anything that would match
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool memory_info::resolveClassnameToClassID (const string classname, int32_t & classID)
|
|
||||||
{
|
|
||||||
// FIXME: another stupid search.
|
|
||||||
classID = -1;
|
|
||||||
for(uint32_t i = 0;i< d->classnames.size();i++)
|
|
||||||
{
|
|
||||||
if(d->classnames[i] == classname)
|
|
||||||
{
|
|
||||||
classID = i;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// we failed to find anything that would match
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool memory_info::resolveClassIDToClassname (const int32_t classID, string & classname)
|
|
||||||
{
|
|
||||||
if (classID >=0 && (uint32_t)classID < d->classnames.size())
|
|
||||||
{
|
|
||||||
classname = d->classnames[classID];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// return pointer to our internal classID -> className mapping
|
|
||||||
const vector<string> * memory_info::getClassIDMapping()
|
|
||||||
{
|
|
||||||
return &d->classnames;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// change base of all addresses
|
|
||||||
void memory_info::RebaseAddresses(const int32_t new_base)
|
|
||||||
{
|
|
||||||
map<string, uint32_t>::iterator iter;
|
|
||||||
int32_t rebase = - (int32_t)d->base + new_base;
|
|
||||||
for(iter = d->addresses.begin(); iter != d->addresses.end(); iter++)
|
|
||||||
{
|
|
||||||
d->addresses[iter->first] = iter->second + rebase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// change base of all addresses *and* vtable entries
|
|
||||||
void memory_info::RebaseAll(int32_t new_base)
|
|
||||||
{
|
|
||||||
map<string, uint32_t>::iterator iter;
|
|
||||||
int32_t rebase = - (int32_t)d->base + new_base;
|
|
||||||
for(iter = d->addresses.begin(); iter != d->addresses.end(); iter++)
|
|
||||||
{
|
|
||||||
d->addresses[iter->first] = iter->second + rebase;
|
|
||||||
}
|
|
||||||
RebaseVTable(rebase);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// change all vtable entries by offset
|
|
||||||
void memory_info::RebaseVTable(int32_t offset)
|
|
||||||
{
|
|
||||||
vector<t_class *>::iterator iter;
|
|
||||||
for(iter = d->classes.begin(); iter != d->classes.end(); iter++)
|
|
||||||
{
|
|
||||||
(*iter)->vtable += offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get named address
|
|
||||||
uint32_t memory_info::getAddress (const char *key)
|
|
||||||
{
|
|
||||||
map <string, uint32_t>::iterator iter = d->addresses.find(key);
|
|
||||||
|
|
||||||
if(iter != d->addresses.end())
|
|
||||||
{
|
|
||||||
return (*iter).second;
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("address", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Get named offset
|
|
||||||
int32_t memory_info::getOffset (const char *key)
|
|
||||||
{
|
|
||||||
map <string, int32_t>::iterator iter = d->offsets.find(key);
|
|
||||||
if(iter != d->offsets.end())
|
|
||||||
{
|
|
||||||
return (*iter).second;
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("offset", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get named numerical value
|
|
||||||
uint32_t memory_info::getHexValue (const char *key)
|
|
||||||
{
|
|
||||||
map <string, uint32_t>::iterator iter = d->hexvals.find(key);
|
|
||||||
if(iter != d->hexvals.end())
|
|
||||||
{
|
|
||||||
return (*iter).second;
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("hexvalue", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Get named address
|
|
||||||
uint32_t memory_info::getAddress (const string &key)
|
|
||||||
{
|
|
||||||
map <string, uint32_t>::iterator iter = d->addresses.find(key);
|
|
||||||
|
|
||||||
if(iter != d->addresses.end())
|
|
||||||
{
|
|
||||||
return (*iter).second;
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("address", key.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Get named offset
|
|
||||||
int32_t memory_info::getOffset (const string &key)
|
|
||||||
{
|
|
||||||
map <string, int32_t>::iterator iter = d->offsets.find(key);
|
|
||||||
if(iter != d->offsets.end())
|
|
||||||
{
|
|
||||||
return (*iter).second;
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("offset", key.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get named numerical value
|
|
||||||
uint32_t memory_info::getHexValue (const string &key)
|
|
||||||
{
|
|
||||||
map <string, uint32_t>::iterator iter = d->hexvals.find(key);
|
|
||||||
if(iter != d->hexvals.end())
|
|
||||||
{
|
|
||||||
return (*iter).second;
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("hexvalue", key.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get named string
|
|
||||||
std::string memory_info::getString (const string &key)
|
|
||||||
{
|
|
||||||
map <string, string>::iterator iter = d->strings.find(key);
|
|
||||||
if(iter != d->strings.end())
|
|
||||||
{
|
|
||||||
return (*iter).second;
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("string", key.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Profession
|
|
||||||
string memory_info::getProfession (const uint32_t key) const
|
|
||||||
{
|
|
||||||
if(d->professions.size() > key)
|
|
||||||
{
|
|
||||||
return d->professions[key];
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("profession", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Job
|
|
||||||
string memory_info::getJob (const uint32_t key) const
|
|
||||||
{
|
|
||||||
if(d->jobs.size() > key)
|
|
||||||
|
|
||||||
{
|
|
||||||
return d->jobs[key];
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("job", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
string memory_info::getSkill (const uint32_t key) const
|
|
||||||
{
|
|
||||||
if(d->skills.size() > key)
|
|
||||||
{
|
|
||||||
return d->skills[key];
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("skill", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
DFHack::t_level memory_info::getLevelInfo(const uint32_t level) const
|
|
||||||
{
|
|
||||||
if(d->levels.size() > level)
|
|
||||||
{
|
|
||||||
return d->levels[level];
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("Level", level);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: ugly hack that needs to die
|
|
||||||
int absolute (int number)
|
|
||||||
{
|
|
||||||
if (number < 0)
|
|
||||||
return -number;
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
|
|
||||||
string memory_info::getTrait (const uint32_t traitIdx, const uint32_t traitValue) const
|
|
||||||
{
|
|
||||||
if(d->traits.size() > traitIdx)
|
|
||||||
{
|
|
||||||
int diff = absolute(traitValue-50);
|
|
||||||
if(diff < 10)
|
|
||||||
{
|
|
||||||
return string("");
|
|
||||||
}
|
|
||||||
if (traitValue >= 91)
|
|
||||||
return d->traits[traitIdx][5];
|
|
||||||
else if (traitValue >= 76)
|
|
||||||
return d->traits[traitIdx][4];
|
|
||||||
else if (traitValue >= 61)
|
|
||||||
return d->traits[traitIdx][3];
|
|
||||||
else if (traitValue >= 25)
|
|
||||||
return d->traits[traitIdx][2];
|
|
||||||
else if (traitValue >= 10)
|
|
||||||
return d->traits[traitIdx][1];
|
|
||||||
else
|
|
||||||
return d->traits[traitIdx][0];
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("trait", traitIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
string memory_info::getTraitName(const uint32_t traitIdx) const
|
|
||||||
{
|
|
||||||
if(d->traits.size() > traitIdx)
|
|
||||||
{
|
|
||||||
return d->traits[traitIdx][d->traits[traitIdx].size()-1];
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("traitname", traitIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector< std::vector<std::string> > const& memory_info::getAllTraits()
|
|
||||||
{
|
|
||||||
return d->traits;
|
|
||||||
}
|
|
||||||
|
|
||||||
string memory_info::getLabor (const uint32_t laborIdx)
|
|
||||||
{
|
|
||||||
if(d->labors.count(laborIdx))
|
|
||||||
{
|
|
||||||
return d->labors[laborIdx];
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("labor", laborIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string memory_info::getMood(const uint32_t moodID)
|
|
||||||
{
|
|
||||||
if(d->moods.size() > moodID)
|
|
||||||
{
|
|
||||||
return d->moods[moodID];
|
|
||||||
}
|
|
||||||
throw Error::MissingMemoryDefinition("Mood", moodID);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string memory_info::PrintOffsets()
|
|
||||||
{
|
|
||||||
ostringstream ss;
|
|
||||||
ss << "<Version name=\"" << getVersion() << "\">" << endl;
|
|
||||||
switch (getOS())
|
|
||||||
{
|
|
||||||
case OS_LINUX:
|
|
||||||
ss << "<MD5 value=\"" << getString("md5") << "\" />" << endl;
|
|
||||||
break;
|
|
||||||
case OS_WINDOWS:
|
|
||||||
ss << "<PETimeStamp value=\"" << hex << "0x" << getHexValue("pe_timestamp") << "\" />" << endl;
|
|
||||||
ss << "<MD5 value=\"" << getString("md5") << "\" />" << endl;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ss << " UNKNOWN" << endl;
|
|
||||||
}
|
|
||||||
ss << "<Offsets>" << endl;
|
|
||||||
map<string,uint32_t>::const_iterator iter;
|
|
||||||
for(iter = d->addresses.begin(); iter != d->addresses.end(); iter++)
|
|
||||||
{
|
|
||||||
ss << " <Address name=\"" << (*iter).first << "\" value=\"" << hex << "0x" << (*iter).second << "\" />" << endl;
|
|
||||||
}
|
|
||||||
map<string,int32_t>::const_iterator iter2;
|
|
||||||
for(iter2 = d->offsets.begin(); iter2 != d->offsets.end(); iter2++)
|
|
||||||
{
|
|
||||||
ss << " <Offset name=\"" << (*iter2).first << "\" value=\"" << hex << "0x" << (*iter2).second <<"\" />" << endl;
|
|
||||||
}
|
|
||||||
for(iter = d->hexvals.begin(); iter != d->hexvals.end(); iter++)
|
|
||||||
{
|
|
||||||
ss << " <HexValue name=\"" << (*iter).first << "\" value=\"" << hex << "0x" << (*iter).second <<"\" />" << endl;
|
|
||||||
}
|
|
||||||
map<string,string>::const_iterator iter3;
|
|
||||||
for(iter3 = d->strings.begin(); iter3 != d->strings.end(); iter3++)
|
|
||||||
{
|
|
||||||
ss << " <String name=\"" << (*iter3).first << "\" value=\"" << (*iter3).second <<"\" />" << endl;
|
|
||||||
}
|
|
||||||
ss << "</Offsets>" << endl;
|
|
||||||
ss << "</Version>" << endl;
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
@ -1,297 +0,0 @@
|
|||||||
/*
|
|
||||||
www.sourceforge.net/projects/dfhack
|
|
||||||
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
|
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied
|
|
||||||
warranty. In no event will the authors be held liable for any
|
|
||||||
damages arising from the use of this software.
|
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any
|
|
||||||
purpose, including commercial applications, and to alter it and
|
|
||||||
redistribute it freely, subject to the following restrictions:
|
|
||||||
|
|
||||||
1. The origin of this software must not be misrepresented; you must
|
|
||||||
not claim that you wrote the original software. If you use this
|
|
||||||
software in a product, an acknowledgment in the product documentation
|
|
||||||
would be appreciated but is not required.
|
|
||||||
|
|
||||||
2. Altered source versions must be plainly marked as such, and
|
|
||||||
must not be misrepresented as being the original software.
|
|
||||||
|
|
||||||
3. This notice may not be removed or altered from any source
|
|
||||||
distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Internal.h"
|
|
||||||
#include "DFMemInfoManager.h"
|
|
||||||
|
|
||||||
#include "dfhack/DFMemInfo.h"
|
|
||||||
#include "dfhack/DFError.h"
|
|
||||||
|
|
||||||
using namespace DFHack;
|
|
||||||
|
|
||||||
MemInfoManager::~MemInfoManager()
|
|
||||||
{
|
|
||||||
// for each in std::vector<memory_info*> meminfo;, delete
|
|
||||||
for(uint32_t i = 0; i < meminfo.size();i++)
|
|
||||||
{
|
|
||||||
delete meminfo[i];
|
|
||||||
}
|
|
||||||
meminfo.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemInfoManager::ParseVTable(TiXmlElement* vtable, memory_info* mem)
|
|
||||||
{
|
|
||||||
TiXmlElement* pClassEntry;
|
|
||||||
TiXmlElement* pClassSubEntry;
|
|
||||||
// check for rebase, do rebase if check positive
|
|
||||||
const char * rebase = vtable->Attribute("rebase");
|
|
||||||
if(rebase)
|
|
||||||
{
|
|
||||||
int32_t rebase_offset = strtol(rebase, NULL, 16);
|
|
||||||
mem->RebaseVTable(rebase_offset);
|
|
||||||
}
|
|
||||||
// parse vtable entries
|
|
||||||
pClassEntry = vtable->FirstChildElement();
|
|
||||||
for(;pClassEntry;pClassEntry=pClassEntry->NextSiblingElement())
|
|
||||||
{
|
|
||||||
string type = pClassEntry->Value();
|
|
||||||
const char *cstr_name = pClassEntry->Attribute("name");
|
|
||||||
const char *cstr_vtable = pClassEntry->Attribute("vtable");
|
|
||||||
uint32_t vtable = 0;
|
|
||||||
if(cstr_vtable)
|
|
||||||
vtable = strtol(cstr_vtable, NULL, 16);
|
|
||||||
// it's a simple class
|
|
||||||
if(type== "class")
|
|
||||||
{
|
|
||||||
mem->setClass(cstr_name, vtable);
|
|
||||||
}
|
|
||||||
// it's a multi-type class
|
|
||||||
else if (type == "multiclass")
|
|
||||||
{
|
|
||||||
// get offset of the type variable
|
|
||||||
const char *cstr_typeoffset = pClassEntry->Attribute("typeoffset");
|
|
||||||
uint32_t typeoffset = 0;
|
|
||||||
if(cstr_typeoffset)
|
|
||||||
typeoffset = strtol(cstr_typeoffset, NULL, 16);
|
|
||||||
t_class * mclass = mem->setClass(cstr_name, vtable, typeoffset);
|
|
||||||
// parse class sub-entries
|
|
||||||
pClassSubEntry = pClassEntry->FirstChildElement();
|
|
||||||
for(;pClassSubEntry;pClassSubEntry=pClassSubEntry->NextSiblingElement())
|
|
||||||
{
|
|
||||||
type = pClassSubEntry->Value();
|
|
||||||
if(type== "class")
|
|
||||||
{
|
|
||||||
// type is a value loaded from type offset
|
|
||||||
cstr_name = pClassSubEntry->Attribute("name");
|
|
||||||
const char *cstr_value = pClassSubEntry->Attribute("type");
|
|
||||||
mem->setClassChild(mclass,cstr_name,cstr_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void MemInfoManager::ParseEntry (TiXmlElement* entry, memory_info* mem, map <string ,TiXmlElement *>& knownEntries)
|
|
||||||
{
|
|
||||||
TiXmlElement* pMemEntry;
|
|
||||||
const char *cstr_version = entry->Attribute("version");
|
|
||||||
const char *cstr_os = entry->Attribute("os");
|
|
||||||
const char *cstr_base = entry->Attribute("base");
|
|
||||||
const char *cstr_rebase = entry->Attribute("rebase");
|
|
||||||
if(cstr_base)
|
|
||||||
{
|
|
||||||
string base = cstr_base;
|
|
||||||
ParseEntry(knownEntries[base], mem, knownEntries);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cstr_version)
|
|
||||||
throw Error::MemoryXmlBadAttribute("version");
|
|
||||||
if (!cstr_os)
|
|
||||||
throw Error::MemoryXmlBadAttribute("os");
|
|
||||||
|
|
||||||
string os = cstr_os;
|
|
||||||
mem->setVersion(cstr_version);
|
|
||||||
mem->setOS(cstr_os);
|
|
||||||
|
|
||||||
// offset inherited addresses by 'rebase'.
|
|
||||||
int32_t rebase = 0;
|
|
||||||
if(cstr_rebase)
|
|
||||||
{
|
|
||||||
rebase = mem->getBase() + strtol(cstr_rebase, NULL, 16);
|
|
||||||
mem->RebaseAddresses(rebase);
|
|
||||||
}
|
|
||||||
|
|
||||||
//set base to default, we're overwriting this because the previous rebase could cause havoc on Vista/7
|
|
||||||
if(os == "windows")
|
|
||||||
{
|
|
||||||
// set default image base. this is fixed for base relocation later
|
|
||||||
mem->setBase(0x400000);
|
|
||||||
}
|
|
||||||
else if(os == "linux")
|
|
||||||
{
|
|
||||||
// this is wrong... I'm not going to do base image relocation on linux though.
|
|
||||||
// users are free to use a sane kernel that doesn't do this kind of **** by default
|
|
||||||
mem->setBase(0x0);
|
|
||||||
}
|
|
||||||
else if ( os == "all")
|
|
||||||
{
|
|
||||||
// yay
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw Error::MemoryXmlBadAttribute("os");
|
|
||||||
}
|
|
||||||
|
|
||||||
// process additional entries
|
|
||||||
//cout << "Entry " << cstr_version << " " << cstr_os << endl;
|
|
||||||
pMemEntry = entry->FirstChildElement()->ToElement();
|
|
||||||
for(;pMemEntry;pMemEntry=pMemEntry->NextSiblingElement())
|
|
||||||
{
|
|
||||||
// only elements get processed
|
|
||||||
const char *cstr_type = pMemEntry->Value();
|
|
||||||
const char *cstr_name = pMemEntry->Attribute("name");
|
|
||||||
const char *cstr_value = pMemEntry->GetText();
|
|
||||||
|
|
||||||
if(!cstr_value)
|
|
||||||
cstr_value = pMemEntry->Attribute("id");
|
|
||||||
|
|
||||||
// check for missing parts
|
|
||||||
string type, name, value;
|
|
||||||
type = cstr_type;
|
|
||||||
if(type == "VTable")
|
|
||||||
{
|
|
||||||
ParseVTable(pMemEntry, mem);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(!(cstr_name && cstr_value))
|
|
||||||
{
|
|
||||||
throw Error::MemoryXmlUnderspecifiedEntry(cstr_version);
|
|
||||||
}
|
|
||||||
name = cstr_name;
|
|
||||||
value = cstr_value;
|
|
||||||
if (type == "HexValue")
|
|
||||||
{
|
|
||||||
mem->setHexValue(name, value);
|
|
||||||
}
|
|
||||||
else if (type == "Address")
|
|
||||||
{
|
|
||||||
mem->setAddress(name, value);
|
|
||||||
}
|
|
||||||
else if (type == "Offset")
|
|
||||||
{
|
|
||||||
mem->setOffset(name, value);
|
|
||||||
}
|
|
||||||
else if (type == "String")
|
|
||||||
{
|
|
||||||
mem->setString(name, value);
|
|
||||||
}
|
|
||||||
else if (type == "Profession")
|
|
||||||
{
|
|
||||||
mem->setProfession(value,name);
|
|
||||||
}
|
|
||||||
else if (type == "Job")
|
|
||||||
{
|
|
||||||
mem->setJob(value,name);
|
|
||||||
}
|
|
||||||
else if (type == "Skill")
|
|
||||||
{
|
|
||||||
mem->setSkill(value,name);
|
|
||||||
}
|
|
||||||
else if (type == "Trait")
|
|
||||||
{
|
|
||||||
mem->setTrait(value, name,pMemEntry->Attribute("level_0"),pMemEntry->Attribute("level_1"),pMemEntry->Attribute("level_2"),pMemEntry->Attribute("level_3"),pMemEntry->Attribute("level_4"),pMemEntry->Attribute("level_5"));
|
|
||||||
}
|
|
||||||
else if (type == "Labor")
|
|
||||||
{
|
|
||||||
mem->setLabor(value,name);
|
|
||||||
}
|
|
||||||
else if (type == "Level")
|
|
||||||
{
|
|
||||||
mem->setLevel(value, name, pMemEntry->Attribute("xpNxtLvl"));
|
|
||||||
}
|
|
||||||
else if (type == "Mood")
|
|
||||||
{
|
|
||||||
mem->setMood(value, name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw Error::MemoryXmlUnknownType(type.c_str());
|
|
||||||
}
|
|
||||||
} // for
|
|
||||||
} // method
|
|
||||||
|
|
||||||
MemInfoManager::MemInfoManager(string path_to_xml)
|
|
||||||
{
|
|
||||||
error = false;
|
|
||||||
loadFile(path_to_xml);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load the XML file with offsets
|
|
||||||
bool MemInfoManager::loadFile(string path_to_xml)
|
|
||||||
{
|
|
||||||
TiXmlDocument doc( path_to_xml.c_str() );
|
|
||||||
//bool loadOkay = doc.LoadFile();
|
|
||||||
if (!doc.LoadFile())
|
|
||||||
{
|
|
||||||
error = true;
|
|
||||||
throw Error::MemoryXmlParse(doc.ErrorDesc(), doc.ErrorId(), doc.ErrorRow(), doc.ErrorCol());
|
|
||||||
}
|
|
||||||
TiXmlHandle hDoc(&doc);
|
|
||||||
TiXmlElement* pElem;
|
|
||||||
TiXmlHandle hRoot(0);
|
|
||||||
memory_info mem;
|
|
||||||
|
|
||||||
// block: name
|
|
||||||
{
|
|
||||||
pElem=hDoc.FirstChildElement().Element();
|
|
||||||
// should always have a valid root but handle gracefully if it does
|
|
||||||
if (!pElem)
|
|
||||||
{
|
|
||||||
error = true;
|
|
||||||
throw Error::MemoryXmlNoRoot();
|
|
||||||
}
|
|
||||||
string m_name=pElem->Value();
|
|
||||||
if(m_name != "DFExtractor")
|
|
||||||
{
|
|
||||||
error = true;
|
|
||||||
throw Error::MemoryXmlNoDFExtractor(m_name.c_str());
|
|
||||||
}
|
|
||||||
// save this for later
|
|
||||||
hRoot=TiXmlHandle(pElem);
|
|
||||||
}
|
|
||||||
// transform elements
|
|
||||||
{
|
|
||||||
// trash existing list
|
|
||||||
for(uint32_t i = 0; i < meminfo.size(); i++)
|
|
||||||
{
|
|
||||||
delete meminfo[i];
|
|
||||||
}
|
|
||||||
meminfo.clear();
|
|
||||||
TiXmlElement* pMemInfo=hRoot.FirstChild( "MemoryDescriptors" ).FirstChild( "Entry" ).Element();
|
|
||||||
map <string ,TiXmlElement *> map_pNamedEntries;
|
|
||||||
vector <TiXmlElement *> v_pEntries;
|
|
||||||
for( ; pMemInfo; pMemInfo=pMemInfo->NextSiblingElement("Entry"))
|
|
||||||
{
|
|
||||||
v_pEntries.push_back(pMemInfo);
|
|
||||||
const char *id = pMemInfo->Attribute("id");
|
|
||||||
if(id)
|
|
||||||
{
|
|
||||||
string str_id = id;
|
|
||||||
map_pNamedEntries[str_id] = pMemInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(uint32_t i = 0; i< v_pEntries.size();i++)
|
|
||||||
{
|
|
||||||
memory_info *mem = new memory_info();
|
|
||||||
//FIXME: add a set of entries processed in a step of this cycle, use it to check for infinite loops
|
|
||||||
/* recursive */ParseEntry( v_pEntries[i] , mem , map_pNamedEntries);
|
|
||||||
meminfo.push_back(mem);
|
|
||||||
}
|
|
||||||
// process found things here
|
|
||||||
}
|
|
||||||
error = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,630 @@
|
|||||||
|
/*
|
||||||
|
www.sourceforge.net/projects/dfhack
|
||||||
|
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any
|
||||||
|
damages arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any
|
||||||
|
purpose, including commercial applications, and to alter it and
|
||||||
|
redistribute it freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must
|
||||||
|
not claim that you wrote the original software. If you use this
|
||||||
|
software in a product, an acknowledgment in the product documentation
|
||||||
|
would be appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and
|
||||||
|
must not be misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Internal.h"
|
||||||
|
|
||||||
|
#include "dfhack/VersionInfoFactory.h"
|
||||||
|
#include "dfhack/VersionInfo.h"
|
||||||
|
#include "dfhack/DFError.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
|
||||||
|
VersionInfoFactory::~VersionInfoFactory()
|
||||||
|
{
|
||||||
|
// for each stored version, delete
|
||||||
|
for(uint32_t i = 0; i < versions.size();i++)
|
||||||
|
{
|
||||||
|
delete versions[i];
|
||||||
|
}
|
||||||
|
versions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VersionInfoFactory::ParseVTable(TiXmlElement* vtable, VersionInfo* mem)
|
||||||
|
{
|
||||||
|
TiXmlElement* pClassEntry;
|
||||||
|
TiXmlElement* pClassSubEntry;
|
||||||
|
/*
|
||||||
|
// check for rebase, do rebase if check positive
|
||||||
|
const char * rebase = vtable->Attribute("rebase");
|
||||||
|
if(rebase)
|
||||||
|
{
|
||||||
|
int32_t rebase_offset = strtol(rebase, NULL, 16);
|
||||||
|
mem->RebaseVTable(rebase_offset);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// parse vtable entries
|
||||||
|
pClassEntry = vtable->FirstChildElement();
|
||||||
|
for(;pClassEntry;pClassEntry=pClassEntry->NextSiblingElement())
|
||||||
|
{
|
||||||
|
string type = pClassEntry->Value();
|
||||||
|
const char *cstr_name = pClassEntry->Attribute("name");
|
||||||
|
const char *cstr_vtable = pClassEntry->Attribute("vtable");
|
||||||
|
uint32_t vtable = 0;
|
||||||
|
if(cstr_vtable)
|
||||||
|
vtable = strtol(cstr_vtable, NULL, 16);
|
||||||
|
// it's a simple class
|
||||||
|
if(type== "class")
|
||||||
|
{
|
||||||
|
mem->setClass(cstr_name, vtable);
|
||||||
|
}
|
||||||
|
// it's a multi-type class
|
||||||
|
else if (type == "multiclass")
|
||||||
|
{
|
||||||
|
// get offset of the type variable
|
||||||
|
const char *cstr_typeoffset = pClassEntry->Attribute("typeoffset");
|
||||||
|
uint32_t typeoffset = 0;
|
||||||
|
if(cstr_typeoffset)
|
||||||
|
typeoffset = strtol(cstr_typeoffset, NULL, 16);
|
||||||
|
t_class * mclass = mem->setClass(cstr_name, vtable, typeoffset);
|
||||||
|
// parse class sub-entries
|
||||||
|
pClassSubEntry = pClassEntry->FirstChildElement();
|
||||||
|
for(;pClassSubEntry;pClassSubEntry=pClassSubEntry->NextSiblingElement())
|
||||||
|
{
|
||||||
|
type = pClassSubEntry->Value();
|
||||||
|
if(type== "class")
|
||||||
|
{
|
||||||
|
// type is a value loaded from type offset
|
||||||
|
cstr_name = pClassSubEntry->Attribute("name");
|
||||||
|
const char *cstr_value = pClassSubEntry->Attribute("type");
|
||||||
|
mem->setClassChild(mclass,cstr_name,cstr_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: this is ripe for replacement with a more generic approach
|
||||||
|
void VersionInfoFactory::ParseOffsets(TiXmlElement * parent, VersionInfo* target, bool initial)
|
||||||
|
{
|
||||||
|
// we parse the groups iteratively instead of recursively
|
||||||
|
// breadcrubs acts like a makeshift stack
|
||||||
|
// first pair entry stores the current element of that level
|
||||||
|
// second pair entry the group object from OffsetGroup
|
||||||
|
typedef pair < TiXmlElement *, OffsetGroup * > groupPair;
|
||||||
|
vector< groupPair > breadcrumbs;
|
||||||
|
{
|
||||||
|
TiXmlElement* pEntry;
|
||||||
|
// we get the <Offsets>, look at the children
|
||||||
|
pEntry = parent->FirstChildElement();
|
||||||
|
if(!pEntry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OffsetGroup * currentGroup = reinterpret_cast<OffsetGroup *> (target);
|
||||||
|
breadcrumbs.push_back(groupPair(pEntry,currentGroup));
|
||||||
|
}
|
||||||
|
|
||||||
|
// work variables
|
||||||
|
OffsetGroup * currentGroup = 0;
|
||||||
|
TiXmlElement * currentElem = 0;
|
||||||
|
//cerr << "<Offsets>"<< endl;
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
// get current work variables
|
||||||
|
currentElem = breadcrumbs.back().first;
|
||||||
|
currentGroup = breadcrumbs.back().second;
|
||||||
|
|
||||||
|
// we reached the end of the current group?
|
||||||
|
if(!currentElem)
|
||||||
|
{
|
||||||
|
// go one level up
|
||||||
|
breadcrumbs.pop_back();
|
||||||
|
// exit if no more work
|
||||||
|
if(breadcrumbs.empty())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//cerr << "</group>" << endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!currentGroup)
|
||||||
|
{
|
||||||
|
groupPair & gp = breadcrumbs.back();
|
||||||
|
gp.first = gp.first->NextSiblingElement();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip non-elements
|
||||||
|
if (currentElem->Type() != TiXmlNode::ELEMENT)
|
||||||
|
{
|
||||||
|
groupPair & gp = breadcrumbs.back();
|
||||||
|
gp.first = gp.first->NextSiblingElement();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have a valid current element and current group
|
||||||
|
// get properties
|
||||||
|
string type = currentElem->Value();
|
||||||
|
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
||||||
|
const char *cstr_name = currentElem->Attribute("name");
|
||||||
|
if(!cstr_name)
|
||||||
|
{
|
||||||
|
// ERROR, missing attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluate elements
|
||||||
|
const char *cstr_value = currentElem->Attribute("value");
|
||||||
|
if(type == "group")
|
||||||
|
{
|
||||||
|
// FIXME: possibly use setGroup always, with the initial flag as parameter?
|
||||||
|
// create or get group
|
||||||
|
OffsetGroup * og;
|
||||||
|
if(initial)
|
||||||
|
og = currentGroup->createGroup(cstr_name);
|
||||||
|
else
|
||||||
|
og = currentGroup->getGroup(cstr_name);
|
||||||
|
//cerr << "<group name=\"" << cstr_name << "\">" << endl;
|
||||||
|
// advance this level to the next element
|
||||||
|
groupPair & gp = breadcrumbs.back();
|
||||||
|
gp.first = currentElem->NextSiblingElement();
|
||||||
|
|
||||||
|
if(!og)
|
||||||
|
{
|
||||||
|
string fullname = currentGroup->getFullName() + cstr_name;
|
||||||
|
throw Error::MissingMemoryDefinition("group", fullname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a new level that will be processed next
|
||||||
|
breadcrumbs.push_back(groupPair(currentElem->FirstChildElement(), og));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(type == "address")
|
||||||
|
{
|
||||||
|
if(initial)
|
||||||
|
{
|
||||||
|
currentGroup->createAddress(cstr_name);
|
||||||
|
}
|
||||||
|
else if(cstr_value)
|
||||||
|
{
|
||||||
|
currentGroup->setAddress(cstr_name, cstr_value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ERROR, missing attribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(type == "offset")
|
||||||
|
{
|
||||||
|
if(initial)
|
||||||
|
{
|
||||||
|
currentGroup->createOffset(cstr_name);
|
||||||
|
}
|
||||||
|
else if(cstr_value)
|
||||||
|
{
|
||||||
|
currentGroup->setOffset(cstr_name, cstr_value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ERROR, missing attribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(type == "string")
|
||||||
|
{
|
||||||
|
if(initial)
|
||||||
|
{
|
||||||
|
currentGroup->createString(cstr_name);
|
||||||
|
}
|
||||||
|
else if(cstr_value)
|
||||||
|
{
|
||||||
|
currentGroup->setString(cstr_name, cstr_value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ERROR, missing attribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(type == "hexvalue")
|
||||||
|
{
|
||||||
|
if(initial)
|
||||||
|
{
|
||||||
|
currentGroup->createHexValue(cstr_name);
|
||||||
|
}
|
||||||
|
else if(cstr_value)
|
||||||
|
{
|
||||||
|
currentGroup->setHexValue(cstr_name, cstr_value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ERROR, missing attribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance to next element
|
||||||
|
groupPair & gp = breadcrumbs.back();
|
||||||
|
gp.first = currentElem->NextSiblingElement();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//cerr << "</Offsets>"<< endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VersionInfoFactory::ParseBase (TiXmlElement* entry, VersionInfo* mem)
|
||||||
|
{
|
||||||
|
TiXmlElement* pElement;
|
||||||
|
TiXmlElement* pElement2nd;
|
||||||
|
const char *cstr_version = entry->Attribute("name");
|
||||||
|
|
||||||
|
if (!cstr_version)
|
||||||
|
throw Error::MemoryXmlBadAttribute("name");
|
||||||
|
|
||||||
|
mem->setVersion(cstr_version);
|
||||||
|
mem->setOS(VersionInfo::OS_BAD);
|
||||||
|
|
||||||
|
// process additional entries
|
||||||
|
pElement = entry->FirstChildElement()->ToElement();
|
||||||
|
for(;pElement;pElement=pElement->NextSiblingElement())
|
||||||
|
{
|
||||||
|
// only elements get processed
|
||||||
|
const char *cstr_type = pElement->Value();
|
||||||
|
std::string type = cstr_type;
|
||||||
|
if(type == "VTable")
|
||||||
|
{
|
||||||
|
ParseVTable(pElement, mem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(type == "Offsets")
|
||||||
|
{
|
||||||
|
// we don't care about the descriptions here, do nothing
|
||||||
|
ParseOffsets(pElement, mem, true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (type == "Professions")
|
||||||
|
{
|
||||||
|
pElement2nd = pElement->FirstChildElement("Profession");
|
||||||
|
for(;pElement2nd;pElement2nd=pElement2nd->NextSiblingElement("Profession"))
|
||||||
|
{
|
||||||
|
const char * id = pElement2nd->Attribute("id");
|
||||||
|
const char * name = pElement2nd->Attribute("name");
|
||||||
|
// FIXME: missing some attributes here
|
||||||
|
if(id && name)
|
||||||
|
{
|
||||||
|
mem->setProfession(id,name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FIXME: this is crap, doesn't tell anything about the error
|
||||||
|
throw Error::MemoryXmlUnderspecifiedEntry(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == "Jobs")
|
||||||
|
{
|
||||||
|
pElement2nd = pElement->FirstChildElement("Job");
|
||||||
|
for(;pElement2nd;pElement2nd=pElement2nd->NextSiblingElement("Job"))
|
||||||
|
{
|
||||||
|
const char * id = pElement2nd->Attribute("id");
|
||||||
|
const char * name = pElement2nd->Attribute("name");
|
||||||
|
if(id && name)
|
||||||
|
{
|
||||||
|
mem->setJob(id,name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FIXME: this is crap, doesn't tell anything about the error
|
||||||
|
throw Error::MemoryXmlUnderspecifiedEntry(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == "Skills")
|
||||||
|
{
|
||||||
|
pElement2nd = pElement->FirstChildElement("Skill");
|
||||||
|
for(;pElement2nd;pElement2nd=pElement2nd->NextSiblingElement("Skill"))
|
||||||
|
{
|
||||||
|
const char * id = pElement2nd->Attribute("id");
|
||||||
|
const char * name = pElement2nd->Attribute("name");
|
||||||
|
if(id && name)
|
||||||
|
{
|
||||||
|
mem->setSkill(id,name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FIXME: this is crap, doesn't tell anything about the error
|
||||||
|
throw Error::MemoryXmlUnderspecifiedEntry(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == "Traits")
|
||||||
|
{
|
||||||
|
pElement2nd = pElement->FirstChildElement("Trait");
|
||||||
|
for(;pElement2nd;pElement2nd=pElement2nd->NextSiblingElement("Trait"))
|
||||||
|
{
|
||||||
|
const char * id = pElement2nd->Attribute("id");
|
||||||
|
const char * name = pElement2nd->Attribute("name");
|
||||||
|
const char * lvl0 = pElement2nd->Attribute("level_0");
|
||||||
|
const char * lvl1 = pElement2nd->Attribute("level_1");
|
||||||
|
const char * lvl2 = pElement2nd->Attribute("level_2");
|
||||||
|
const char * lvl3 = pElement2nd->Attribute("level_3");
|
||||||
|
const char * lvl4 = pElement2nd->Attribute("level_4");
|
||||||
|
const char * lvl5 = pElement2nd->Attribute("level_5");
|
||||||
|
if(id && name && lvl0 && lvl1 && lvl2 && lvl3 && lvl4 && lvl5)
|
||||||
|
{
|
||||||
|
mem->setTrait(id, name, lvl0, lvl1, lvl2, lvl3, lvl4, lvl5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FIXME: this is crap, doesn't tell anything about the error
|
||||||
|
throw Error::MemoryXmlUnderspecifiedEntry(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == "Labors")
|
||||||
|
{
|
||||||
|
pElement2nd = pElement->FirstChildElement("Labor");
|
||||||
|
for(;pElement2nd;pElement2nd=pElement2nd->NextSiblingElement("Labor"))
|
||||||
|
{
|
||||||
|
const char * id = pElement2nd->Attribute("id");
|
||||||
|
const char * name = pElement2nd->Attribute("name");
|
||||||
|
if(id && name)
|
||||||
|
{
|
||||||
|
mem->setLabor(id,name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FIXME: this is crap, doesn't tell anything about the error
|
||||||
|
throw Error::MemoryXmlUnderspecifiedEntry(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == "Levels")
|
||||||
|
{
|
||||||
|
pElement2nd = pElement->FirstChildElement("Level");
|
||||||
|
for(;pElement2nd;pElement2nd=pElement2nd->NextSiblingElement("Level"))
|
||||||
|
{
|
||||||
|
const char * id = pElement2nd->Attribute("id");
|
||||||
|
const char * name = pElement2nd->Attribute("name");
|
||||||
|
const char * nextlvl = pElement2nd->Attribute("xpNxtLvl");
|
||||||
|
if(id && name && nextlvl)
|
||||||
|
{
|
||||||
|
mem->setLevel(id, name, nextlvl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FIXME: this is crap, doesn't tell anything about the error
|
||||||
|
throw Error::MemoryXmlUnderspecifiedEntry(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == "Moods")
|
||||||
|
{
|
||||||
|
pElement2nd = pElement->FirstChildElement("Mood");
|
||||||
|
for(;pElement2nd;pElement2nd=pElement2nd->NextSiblingElement("Mood"))
|
||||||
|
{
|
||||||
|
const char * id = pElement2nd->Attribute("id");
|
||||||
|
const char * name = pElement2nd->Attribute("name");
|
||||||
|
if(id && name)
|
||||||
|
{
|
||||||
|
mem->setMood(id, name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FIXME: this is crap, doesn't tell anything about the error
|
||||||
|
throw Error::MemoryXmlUnderspecifiedEntry(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//FIXME: only log, not hard error
|
||||||
|
//throw Error::MemoryXmlUnknownType(type.c_str());
|
||||||
|
}
|
||||||
|
} // for
|
||||||
|
} // method
|
||||||
|
|
||||||
|
void VersionInfoFactory::EvalVersion(string base, VersionInfo * mem)
|
||||||
|
{
|
||||||
|
if(knownVersions.find(base) != knownVersions.end())
|
||||||
|
{
|
||||||
|
v_descr & desc = knownVersions[base];
|
||||||
|
if (!desc.second)
|
||||||
|
{
|
||||||
|
VersionInfo * newmem = new VersionInfo();
|
||||||
|
ParseVersion(desc.first, newmem);
|
||||||
|
desc.second = newmem;
|
||||||
|
}
|
||||||
|
mem->copy(desc.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
|
||||||
|
{
|
||||||
|
TiXmlElement* pMemEntry;
|
||||||
|
const char *cstr_name = entry->Attribute("name");
|
||||||
|
const char *cstr_os = entry->Attribute("os");
|
||||||
|
const char *cstr_base = entry->Attribute("base");
|
||||||
|
const char *cstr_rebase = entry->Attribute("rebase");
|
||||||
|
if(cstr_base)
|
||||||
|
{
|
||||||
|
string base = cstr_base;
|
||||||
|
EvalVersion(base, mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cstr_name)
|
||||||
|
throw Error::MemoryXmlBadAttribute("name");
|
||||||
|
if (!cstr_os)
|
||||||
|
throw Error::MemoryXmlBadAttribute("os");
|
||||||
|
|
||||||
|
string os = cstr_os;
|
||||||
|
mem->setVersion(cstr_name);
|
||||||
|
mem->setOS(cstr_os);
|
||||||
|
|
||||||
|
// offset inherited addresses by 'rebase'.
|
||||||
|
int32_t rebase = 0;
|
||||||
|
if(cstr_rebase)
|
||||||
|
{
|
||||||
|
rebase = mem->getBase() + strtol(cstr_rebase, NULL, 16);
|
||||||
|
mem->RebaseAddresses(rebase);
|
||||||
|
}
|
||||||
|
|
||||||
|
//set base to default, we're overwriting this because the previous rebase could cause havoc on Vista/7
|
||||||
|
if(os == "windows")
|
||||||
|
{
|
||||||
|
// set default image base. this is fixed for base relocation later
|
||||||
|
mem->setBase(0x400000);
|
||||||
|
}
|
||||||
|
else if(os == "linux")
|
||||||
|
{
|
||||||
|
// this is wrong... I'm not going to do base image relocation on linux though.
|
||||||
|
// users are free to use a sane kernel that doesn't do this kind of **** by default
|
||||||
|
mem->setBase(0x0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw Error::MemoryXmlBadAttribute("os");
|
||||||
|
}
|
||||||
|
|
||||||
|
// process additional entries
|
||||||
|
//cout << "Entry " << cstr_version << " " << cstr_os << endl;
|
||||||
|
pMemEntry = entry->FirstChildElement()->ToElement();
|
||||||
|
for(;pMemEntry;pMemEntry=pMemEntry->NextSiblingElement())
|
||||||
|
{
|
||||||
|
string type, name, value;
|
||||||
|
const char *cstr_type = pMemEntry->Value();
|
||||||
|
type = cstr_type;
|
||||||
|
// check for missing parts
|
||||||
|
if(type == "VTable")
|
||||||
|
{
|
||||||
|
ParseVTable(pMemEntry, mem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(type == "Offsets")
|
||||||
|
{
|
||||||
|
ParseOffsets(pMemEntry, mem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (type == "MD5")
|
||||||
|
{
|
||||||
|
const char *cstr_value = pMemEntry->Attribute("value");
|
||||||
|
if(!cstr_value)
|
||||||
|
throw Error::MemoryXmlUnderspecifiedEntry(cstr_name);
|
||||||
|
mem->setMD5(cstr_value);
|
||||||
|
}
|
||||||
|
else if (type == "PETimeStamp")
|
||||||
|
{
|
||||||
|
const char *cstr_value = pMemEntry->Attribute("value");
|
||||||
|
if(!cstr_value)
|
||||||
|
throw Error::MemoryXmlUnderspecifiedEntry(cstr_name);
|
||||||
|
mem->setPE(strtol(cstr_value, 0, 16));
|
||||||
|
}
|
||||||
|
} // for
|
||||||
|
} // method
|
||||||
|
|
||||||
|
VersionInfoFactory::VersionInfoFactory(string path_to_xml)
|
||||||
|
{
|
||||||
|
error = false;
|
||||||
|
loadFile(path_to_xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the XML file with offsets
|
||||||
|
bool VersionInfoFactory::loadFile(string path_to_xml)
|
||||||
|
{
|
||||||
|
TiXmlDocument doc( path_to_xml.c_str() );
|
||||||
|
//bool loadOkay = doc.LoadFile();
|
||||||
|
if (!doc.LoadFile())
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
throw Error::MemoryXmlParse(doc.ErrorDesc(), doc.ErrorId(), doc.ErrorRow(), doc.ErrorCol());
|
||||||
|
}
|
||||||
|
TiXmlHandle hDoc(&doc);
|
||||||
|
TiXmlElement* pElem;
|
||||||
|
TiXmlHandle hRoot(0);
|
||||||
|
VersionInfo *mem;
|
||||||
|
|
||||||
|
// block: name
|
||||||
|
{
|
||||||
|
pElem=hDoc.FirstChildElement().Element();
|
||||||
|
// should always have a valid root but handle gracefully if it does
|
||||||
|
if (!pElem)
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
throw Error::MemoryXmlNoRoot();
|
||||||
|
}
|
||||||
|
string m_name=pElem->Value();
|
||||||
|
if(m_name != "DFHack")
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
throw Error::MemoryXmlNoRoot();
|
||||||
|
}
|
||||||
|
// save this for later
|
||||||
|
hRoot=TiXmlHandle(pElem);
|
||||||
|
}
|
||||||
|
// transform elements
|
||||||
|
{
|
||||||
|
// trash existing list
|
||||||
|
for(uint32_t i = 0; i < versions.size(); i++)
|
||||||
|
{
|
||||||
|
delete versions[i];
|
||||||
|
}
|
||||||
|
versions.clear();
|
||||||
|
|
||||||
|
// For each base version
|
||||||
|
TiXmlElement* pMemInfo=hRoot.FirstChild( "Base" ).Element();
|
||||||
|
map <string ,TiXmlElement *> map_pNamedEntries;
|
||||||
|
vector <string> v_sEntries;
|
||||||
|
for( ; pMemInfo; pMemInfo=pMemInfo->NextSiblingElement("Base"))
|
||||||
|
{
|
||||||
|
const char *name = pMemInfo->Attribute("name");
|
||||||
|
if(name)
|
||||||
|
{
|
||||||
|
string str_name = name;
|
||||||
|
VersionInfo *base = new VersionInfo();
|
||||||
|
mem = new VersionInfo();
|
||||||
|
ParseBase( pMemInfo , mem );
|
||||||
|
knownVersions[str_name] = v_descr (pMemInfo, mem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each derivative version
|
||||||
|
pMemInfo=hRoot.FirstChild( "Version" ).Element();
|
||||||
|
for( ; pMemInfo; pMemInfo=pMemInfo->NextSiblingElement("Version"))
|
||||||
|
{
|
||||||
|
const char *name = pMemInfo->Attribute("name");
|
||||||
|
if(name)
|
||||||
|
{
|
||||||
|
string str_name = name;
|
||||||
|
knownVersions[str_name] = v_descr (pMemInfo, NULL);
|
||||||
|
v_sEntries.push_back(str_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Parse the versions
|
||||||
|
for(uint32_t i = 0; i< v_sEntries.size();i++)
|
||||||
|
{
|
||||||
|
//FIXME: add a set of entries processed in a step of this cycle, use it to check for infinite loops
|
||||||
|
string & name = v_sEntries[i];
|
||||||
|
v_descr & desc = knownVersions[name];
|
||||||
|
if(!desc.second)
|
||||||
|
{
|
||||||
|
VersionInfo *version = new VersionInfo();
|
||||||
|
ParseVersion( desc.first , version );
|
||||||
|
versions.push_back(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process found things here
|
||||||
|
}
|
||||||
|
error = false;
|
||||||
|
return true;
|
||||||
|
}
|
@ -0,0 +1,449 @@
|
|||||||
|
// Console version of DF copy paste, proof of concept
|
||||||
|
// By belal
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <climits>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <ctime>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#define DFHACK_WANT_MISCUTILS
|
||||||
|
#define DFHACK_WANT_TILETYPES
|
||||||
|
#include <DFHack.h>
|
||||||
|
#include "dfhack/modules/WindowIO.h"
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
//bool waitTillCursorState(DFHack::Context *DF, bool On);
|
||||||
|
//bool waitTillCursorPositionState(DFHack::Context *DF, int32_t x,int32_t y, int32_t z);
|
||||||
|
|
||||||
|
//change this if you are having problems getting correct results, lower if you would like to go faster
|
||||||
|
//const int WAIT_AMT = 25;
|
||||||
|
|
||||||
|
void sort(uint32_t &a,uint32_t &b)
|
||||||
|
{
|
||||||
|
if(a > b){
|
||||||
|
uint32_t c = b;
|
||||||
|
b = a;
|
||||||
|
a = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void sort(int32_t &a,int32_t &b)
|
||||||
|
{
|
||||||
|
if(a > b){
|
||||||
|
int16_t c = b;
|
||||||
|
b = a;
|
||||||
|
a = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void printVecOfVec(ostream &out, vector<vector<vector<string> > >vec,char sep){
|
||||||
|
for(int k=0;k<vec.size();k++){
|
||||||
|
for(int i =0;i<vec[k].size();i++){
|
||||||
|
for(int j=0;j<vec[k][i].size();j++){
|
||||||
|
out << vec[k][i][j];
|
||||||
|
if(j==vec[k][i].size()-1)
|
||||||
|
{
|
||||||
|
out << "\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out << sep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << "#<\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int main (int numargs, const char ** args)
|
||||||
|
{
|
||||||
|
map<string, string> buildCommands;
|
||||||
|
buildCommands["building_stockpilest"]="";
|
||||||
|
buildCommands["building_zonest"]="";
|
||||||
|
buildCommands["building_construction_blueprintst"]="";
|
||||||
|
buildCommands["building_wagonst"]="";
|
||||||
|
buildCommands["building_armor_standst"]="a";
|
||||||
|
buildCommands["building_bedst"]="b";
|
||||||
|
buildCommands["building_seatst"]="c";
|
||||||
|
buildCommands["building_burial_receptaclest"]="n";
|
||||||
|
buildCommands["building_doorst"]="d";
|
||||||
|
buildCommands["building_floodgatest"]="x";
|
||||||
|
buildCommands["building_floor_hatchst"]="H";
|
||||||
|
buildCommands["building_wall_gratest"]="W";
|
||||||
|
buildCommands["building_floor_gratest"]="G";
|
||||||
|
buildCommands["building_vertical_barsst"]="B";
|
||||||
|
buildCommands["building_floor_barsst"]="alt-b";
|
||||||
|
buildCommands["building_cabinetst"]="f";
|
||||||
|
buildCommands["building_containerst"]="h";
|
||||||
|
buildCommands["building_shopst"]="";
|
||||||
|
buildCommands["building_workshopst"]="";
|
||||||
|
buildCommands["building_alchemists_laboratoryst"]="wa";
|
||||||
|
buildCommands["building_carpenters_workshopst"]="wc";
|
||||||
|
buildCommands["building_farmers_workshopst"]="ww";
|
||||||
|
buildCommands["building_masons_workshopst"]="wm";
|
||||||
|
buildCommands["building_craftdwarfs_workshopst"]="wr";
|
||||||
|
buildCommands["building_jewelers_workshopst"]="wj";
|
||||||
|
buildCommands["building_metalsmiths_workshopst"]="wf";
|
||||||
|
buildCommands["building_magma_forgest"]="";
|
||||||
|
buildCommands["building_bowyers_workshopst"]="wb";
|
||||||
|
buildCommands["building_mechanics_workshopst"]="wt";
|
||||||
|
buildCommands["building_siege_workshopst"]="ws";
|
||||||
|
buildCommands["building_butchers_shopst"]="wU";
|
||||||
|
buildCommands["building_leather_worksst"]="we";
|
||||||
|
buildCommands["building_tanners_shopst"]="wn";
|
||||||
|
buildCommands["building_clothiers_shopst"]="wk";
|
||||||
|
buildCommands["building_fisheryst"]="wh";
|
||||||
|
buildCommands["building_stillst"]="wl";
|
||||||
|
buildCommands["building_loomst"]="wo";
|
||||||
|
buildCommands["building_quernst"]="wq";
|
||||||
|
buildCommands["building_kennelsst"]="k";
|
||||||
|
buildCommands["building_kitchenst"]="wz";
|
||||||
|
buildCommands["building_asheryst"]="wy";
|
||||||
|
buildCommands["building_dyers_shopst"]="wd";
|
||||||
|
buildCommands["building_millstonest"]="wM";
|
||||||
|
buildCommands["building_farm_plotst"]="p";
|
||||||
|
buildCommands["building_weapon_rackst"]="r";
|
||||||
|
buildCommands["building_statuest"]="s";
|
||||||
|
buildCommands["building_tablest"]="t";
|
||||||
|
buildCommands["building_paved_roadst"]="o";
|
||||||
|
buildCommands["building_bridgest"]="g";
|
||||||
|
buildCommands["building_wellst"]="l";
|
||||||
|
buildCommands["building_siege enginest"]="i";
|
||||||
|
buildCommands["building_catapultst"]="ic";
|
||||||
|
buildCommands["building_ballistast"]="ib";
|
||||||
|
buildCommands["building_furnacest"]="";
|
||||||
|
buildCommands["building_wood_furnacest"]="ew";
|
||||||
|
buildCommands["building_smelterst"]="es";
|
||||||
|
buildCommands["building_glass_furnacest"]="ek";
|
||||||
|
buildCommands["building_kilnst"]="ek";
|
||||||
|
buildCommands["building_magma_smelterst"]="es";
|
||||||
|
buildCommands["building_magma_glass_furnacest"]="ek";
|
||||||
|
buildCommands["building_magma_kilnst"]="ek";
|
||||||
|
buildCommands["building_glass_windowst"]="y";
|
||||||
|
buildCommands["building_gem_windowst"]="Y";
|
||||||
|
buildCommands["building_tradedepotst"]="D";
|
||||||
|
buildCommands["building_mechanismst"]="";
|
||||||
|
buildCommands["building_leverst"]="Tl";
|
||||||
|
buildCommands["building_pressure_platest"]="Tp";
|
||||||
|
buildCommands["building_cage_trapst"]="Tc";
|
||||||
|
buildCommands["building_stonefall_trapst"]="Ts";
|
||||||
|
buildCommands["building_weapon_trapst"]="Tw";
|
||||||
|
buildCommands["building_spikest"]="";
|
||||||
|
buildCommands["building_animal_trapst"]="m";
|
||||||
|
buildCommands["building_screw_pumpst"]="Ms";
|
||||||
|
buildCommands["building_water_wheelst"]="Mw";
|
||||||
|
buildCommands["building_windmillst"]="Mm";
|
||||||
|
buildCommands["building_gear_assemblyst"]="Mg";
|
||||||
|
buildCommands["building_horizontal_axlest"]="Mh";
|
||||||
|
buildCommands["building_vertical_axlest"]="Mv";
|
||||||
|
buildCommands["building_supportst"]="S";
|
||||||
|
buildCommands["building_cagest"]="j";
|
||||||
|
buildCommands["building_archery_targetst"]="A";
|
||||||
|
buildCommands["building_restraintst"]="v";
|
||||||
|
|
||||||
|
DFHack::ContextManager DFMgr("Memory.xml");
|
||||||
|
DFHack::Context *DF = DFMgr.getSingleContext();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DF->Attach();
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
#ifndef LINUX_BUILD
|
||||||
|
cin.ignore();
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DFHack::Position *Pos = DF->getPosition();
|
||||||
|
DFHack::VersionInfo* mem = DF->getMemoryInfo();
|
||||||
|
DFHack::Process * p = DF->getProcess();
|
||||||
|
OffsetGroup * OG_Maps = mem->getGroup("Maps");
|
||||||
|
OffsetGroup * OG_MapBlock = OG_Maps->getGroup("block");
|
||||||
|
OffsetGroup * OG_LocalFt = OG_Maps->getGroup("features")->getGroup("local");
|
||||||
|
uint32_t designations = OG_MapBlock->getOffset("designation");
|
||||||
|
uint32_t block_feature1 = OG_MapBlock->getOffset("feature_local");
|
||||||
|
uint32_t block_feature2 = OG_MapBlock->getOffset("feature_global");
|
||||||
|
uint32_t region_x_offset = OG_Maps->getAddress("region_x");
|
||||||
|
uint32_t region_y_offset = OG_Maps->getAddress("region_y");
|
||||||
|
uint32_t region_z_offset = OG_Maps->getAddress("region_z");
|
||||||
|
uint32_t feature1_start_ptr = OG_LocalFt->getAddress("start_ptr");
|
||||||
|
int32_t regionX, regionY, regionZ;
|
||||||
|
|
||||||
|
// read position of the region inside DF world
|
||||||
|
p->readDWord (region_x_offset, (uint32_t &)regionX);
|
||||||
|
p->readDWord (region_y_offset, (uint32_t &)regionY);
|
||||||
|
p->readDWord (region_z_offset, (uint32_t &)regionZ);
|
||||||
|
while(1){
|
||||||
|
int32_t cx1,cy1,cz1;
|
||||||
|
cx1 = -30000;
|
||||||
|
while(cx1 == -30000){
|
||||||
|
DF->ForceResume();
|
||||||
|
cout << "Set cursor at first position, then press any key";
|
||||||
|
cin.ignore();
|
||||||
|
DF->Suspend();
|
||||||
|
Pos->getCursorCoords(cx1,cy1,cz1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tx1,ty1,tz1;
|
||||||
|
tx1 = cx1/16;
|
||||||
|
ty1 = cy1/16;
|
||||||
|
tz1 = cz1;
|
||||||
|
|
||||||
|
int32_t cx2,cy2,cz2;
|
||||||
|
cx2 = -30000;
|
||||||
|
while(cx2 == -30000){
|
||||||
|
DF->Resume();
|
||||||
|
cout << "Set cursor at second position, then press any key";
|
||||||
|
cin.ignore();
|
||||||
|
DF->Suspend();
|
||||||
|
Pos->getCursorCoords(cx2,cy2,cz2);
|
||||||
|
}
|
||||||
|
uint32_t tx2,ty2,tz2;
|
||||||
|
tx2 = cx2/16;
|
||||||
|
ty2 = cy2/16;
|
||||||
|
tz2 = cz2;
|
||||||
|
sort(tx1,tx2);
|
||||||
|
sort(ty1,ty2);
|
||||||
|
sort(tz1,tz2);
|
||||||
|
sort(cx1,cx2);
|
||||||
|
sort(cy1,cy2);
|
||||||
|
sort(cz1,cz2);
|
||||||
|
|
||||||
|
vector <vector<vector<string> > >dig(cz2-cz1+1,vector<vector<string> >(cy2-cy1+1,vector<string>(cx2-cx1+1)));
|
||||||
|
vector <vector<vector<string> > >build(cz2-cz1+1,vector<vector<string> >(cy2-cy1+1,vector<string>(cx2-cx1+1)));
|
||||||
|
mapblock40d block;
|
||||||
|
DFHack::Maps *Maps = DF->getMaps();
|
||||||
|
Maps->Start();
|
||||||
|
for(uint32_t y = ty1;y<=ty2;y++)
|
||||||
|
{
|
||||||
|
for(uint32_t x = tx1;x<=tx2;x++)
|
||||||
|
{
|
||||||
|
for(uint32_t z = tz1;z<=tz2;z++)
|
||||||
|
{
|
||||||
|
if(Maps->isValidBlock(x,y,z))
|
||||||
|
{
|
||||||
|
if(Maps->ReadBlock40d(x,y,z,&block))
|
||||||
|
{
|
||||||
|
int ystart,yend,xstart,xend;
|
||||||
|
ystart=xstart=0;
|
||||||
|
yend=xend=15;
|
||||||
|
if(y == ty2)
|
||||||
|
{
|
||||||
|
yend = cy2 % 16;
|
||||||
|
}
|
||||||
|
if(y == ty1)
|
||||||
|
{
|
||||||
|
ystart = cy1 % 16;
|
||||||
|
}
|
||||||
|
if(x == tx2)
|
||||||
|
{
|
||||||
|
xend = cx2 % 16;
|
||||||
|
}
|
||||||
|
if(x == tx1)
|
||||||
|
{
|
||||||
|
xstart = cx1 % 16;
|
||||||
|
}
|
||||||
|
int zidx = z-tz1;
|
||||||
|
for(int yy = ystart; yy <= yend;yy++)
|
||||||
|
{
|
||||||
|
int yidx = yy+(16*(y-ty1)-(cy1%16));
|
||||||
|
for(int xx = xstart; xx <= xend;xx++)
|
||||||
|
{
|
||||||
|
int xidx = xx+(16*(x-tx1)-(cx1%16));
|
||||||
|
if(DFHack::isOpenTerrain(block.tiletypes[xx][yy]) || DFHack::isFloorTerrain(block.tiletypes[xx][yy])) {dig[zidx][yidx][xidx] = "d";}
|
||||||
|
else if(DFHack::STAIR_DOWN == DFHack::tileTypeTable[block.tiletypes[xx][yy]].c){ dig [zidx][yidx][xidx] = "j"; build [zidx][yidx][xidx] = "Cd";}
|
||||||
|
else if(DFHack::STAIR_UP == DFHack::tileTypeTable[block.tiletypes[xx][yy]].c){ dig [zidx][yidx][xidx] = "u"; build [zidx][yidx][xidx] = "Cu";}
|
||||||
|
else if(DFHack::STAIR_UPDOWN == DFHack::tileTypeTable[block.tiletypes[xx][yy]].c){ dig [zidx][yidx][xidx] = "i"; build [zidx][yidx][xidx] = "Cx";}
|
||||||
|
else if(DFHack::isRampTerrain(block.tiletypes[xx][yy])){dig [zidx][yidx][xidx] = "r";build [zidx][yidx][xidx] = "Cr";}
|
||||||
|
else if(DFHack::isWallTerrain(block.tiletypes[xx][yy])){build [zidx][yidx][xidx] = "Cw";}
|
||||||
|
}
|
||||||
|
yidx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DFHack::Buildings * Bld = DF->getBuildings();
|
||||||
|
std::map <uint32_t, std::string> custom_workshop_types;
|
||||||
|
uint32_t numBuildings;
|
||||||
|
if(Bld->Start(numBuildings))
|
||||||
|
{
|
||||||
|
Bld->ReadCustomWorkshopTypes(custom_workshop_types);
|
||||||
|
for(uint32_t i = 0; i < numBuildings; i++)
|
||||||
|
{
|
||||||
|
DFHack::t_building temp;
|
||||||
|
Bld->Read(i, temp);
|
||||||
|
if(temp.type != 0xFFFFFFFF) // check if type isn't invalid
|
||||||
|
{
|
||||||
|
std::string typestr;
|
||||||
|
mem->resolveClassIDToClassname(temp.type, typestr);
|
||||||
|
if(temp.z == cz1 && cx1 <= temp.x1 && cx2 >= temp.x2 && cy1 <= temp.y1 && cy2 >= temp.y2)
|
||||||
|
{
|
||||||
|
string currStr = build[temp.z-cz1][temp.y1-cy1][temp.x1-cx1];
|
||||||
|
stringstream stream;
|
||||||
|
string newStr = buildCommands[typestr];
|
||||||
|
if(temp.x1 != temp.x2)
|
||||||
|
{
|
||||||
|
stream << "(" << temp.x2-temp.x1+1 << "x" << temp.y2-temp.y1+1 << ")";
|
||||||
|
newStr += stream.str();
|
||||||
|
}
|
||||||
|
build[temp.z-cz1][temp.y1-cy1][temp.x1-cx1] = newStr + currStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// for testing purposes
|
||||||
|
//ofstream outfile("test.txt");
|
||||||
|
// printVecOfVec(outfile, dig,'\t');
|
||||||
|
// outfile << endl;
|
||||||
|
// printVecOfVec(outfile, build,'\t');
|
||||||
|
// outfile << endl;
|
||||||
|
// outfile.close();
|
||||||
|
|
||||||
|
int32_t cx3,cy3,cz3,cx4,cy4,cz4;
|
||||||
|
uint32_t tx3,ty3,tz3,tx4,ty4,tz4;
|
||||||
|
char result;
|
||||||
|
while(1){
|
||||||
|
cx3 = -30000;
|
||||||
|
while(cx3 == -30000){
|
||||||
|
DF->Resume();
|
||||||
|
cout << "Set cursor at new position, then press any key:";
|
||||||
|
result = cin.get();
|
||||||
|
DF->Suspend();
|
||||||
|
Pos->getCursorCoords(cx3,cy3,cz3);
|
||||||
|
}
|
||||||
|
if(result == 'q'){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cx4 = cx3+cx2-cx1;
|
||||||
|
cy4 = cy3+cy2-cy1;
|
||||||
|
cz4 = cz3+cz2-cz1;
|
||||||
|
tx3=cx3/16;
|
||||||
|
ty3=cy3/16;
|
||||||
|
tz3=cz3;
|
||||||
|
tx4=cx4/16;
|
||||||
|
ty4=cy4/16;
|
||||||
|
tz4=cz4;
|
||||||
|
DFHack::WindowIO * Win = DF->getWindowIO();
|
||||||
|
designations40d designationBlock;
|
||||||
|
for(uint32_t y = ty3;y<=ty4;y++)
|
||||||
|
{
|
||||||
|
for(uint32_t x = tx3;x<=tx4;x++)
|
||||||
|
{
|
||||||
|
for(uint32_t z = tz3;z<=tz4;z++)
|
||||||
|
{
|
||||||
|
Maps->Start();
|
||||||
|
Maps->ReadBlock40d(x,y,z,&block);
|
||||||
|
Maps->ReadDesignations(x,y,z,&designationBlock);
|
||||||
|
int ystart,yend,xstart,xend;
|
||||||
|
ystart=xstart=0;
|
||||||
|
yend=xend=15;
|
||||||
|
if(y == ty4){
|
||||||
|
yend = cy4 % 16;
|
||||||
|
}
|
||||||
|
if(y == ty3){
|
||||||
|
ystart = cy3 % 16;
|
||||||
|
}
|
||||||
|
if(x == tx4){
|
||||||
|
xend = cx4 % 16;
|
||||||
|
}
|
||||||
|
if(x == tx3){
|
||||||
|
xstart = cx3 % 16;
|
||||||
|
}
|
||||||
|
int zidx = z-tz3;
|
||||||
|
for(int yy = ystart; yy <= yend;yy++){
|
||||||
|
int yidx = yy+(16*(y-ty3)-(cy3%16));
|
||||||
|
for(int xx = xstart; xx <= xend;xx++){
|
||||||
|
int xidx = xx+(16*(x-tx3)-(cx3%16));
|
||||||
|
if(dig[zidx][yidx][xidx] != ""){
|
||||||
|
char test = dig[zidx][yidx][xidx].c_str()[0];
|
||||||
|
switch (test){
|
||||||
|
case 'd':
|
||||||
|
designationBlock[xx][yy].bits.dig = DFHack::designation_default;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
designationBlock[xx][yy].bits.dig = DFHack::designation_ud_stair;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
designationBlock[xx][yy].bits.dig = DFHack::designation_u_stair;
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
designationBlock[xx][yy].bits.dig = DFHack::designation_d_stair;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
designationBlock[xx][yy].bits.dig = DFHack::designation_ramp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yidx++;
|
||||||
|
}
|
||||||
|
Maps->Start();
|
||||||
|
Maps->WriteDesignations(x,y,z,&designationBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DF->Detach();
|
||||||
|
#ifndef LINUX_BUILD
|
||||||
|
std::cout << "Done. Press any key to continue" << std::endl;
|
||||||
|
cin.ignore();
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
bool waitTillCursorState(DFHack::Context *DF, bool On)
|
||||||
|
{
|
||||||
|
DFHack::WindowIO * w = DF->getWindowIO();
|
||||||
|
DFHack::Position * p = DF->getPosition();
|
||||||
|
int32_t x,y,z;
|
||||||
|
int tryCount = 0;
|
||||||
|
DF->Suspend();
|
||||||
|
bool cursorResult = p->getCursorCoords(x,y,z);
|
||||||
|
while(tryCount < 50 && On && !cursorResult || !On && cursorResult)
|
||||||
|
{
|
||||||
|
DF->Resume();
|
||||||
|
w->TypeSpecial(DFHack::WAIT,1,WAIT_AMT);
|
||||||
|
tryCount++;
|
||||||
|
DF->Suspend();
|
||||||
|
cursorResult = p->getCursorCoords(x,y,z);
|
||||||
|
}
|
||||||
|
if(tryCount >= 50)
|
||||||
|
{
|
||||||
|
cerr << "Something went wrong, cursor at x: " << x << " y: " << y << " z: " << z << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DF->Resume();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool waitTillCursorPositionState(DFHack::Context *DF, int32_t x,int32_t y, int32_t z)
|
||||||
|
{
|
||||||
|
DFHack::WindowIO * w = DF->getWindowIO();
|
||||||
|
DFHack::Position * p = DF->getPosition();
|
||||||
|
int32_t x2,y2,z2;
|
||||||
|
int tryCount = 0;
|
||||||
|
DF->Suspend();
|
||||||
|
bool cursorResult = p->getCursorCoords(x2,y2,z2);
|
||||||
|
while(tryCount < 50 && (x != x2 || y != y2 || z != z2))
|
||||||
|
{
|
||||||
|
DF->Resume();
|
||||||
|
w->TypeSpecial(DFHack::WAIT,1,WAIT_AMT);
|
||||||
|
tryCount++;
|
||||||
|
DF->Suspend();
|
||||||
|
cursorResult = p->getCursorCoords(x2,y2,z2);
|
||||||
|
}
|
||||||
|
if(tryCount >= 50)
|
||||||
|
{
|
||||||
|
cerr << "Something went wrong, cursor at x: " << x2 << " y: " << y2 << " z: " << z2 << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DF->Resume();
|
||||||
|
return true;
|
||||||
|
}*/
|
@ -0,0 +1,98 @@
|
|||||||
|
// Just show some position data
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <climits>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <ctime>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#define DFHACK_WANT_MISCUTILS
|
||||||
|
#define DFHACK_WANT_TILETYPES
|
||||||
|
#include <DFHack.h>
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
|
||||||
|
void printWeather(DFHack::WeatherType current)
|
||||||
|
{
|
||||||
|
switch (current)
|
||||||
|
{
|
||||||
|
case CLEAR:
|
||||||
|
cout << "The sky is clear." << endl;
|
||||||
|
cout << "Options:" << endl;
|
||||||
|
cout << "'r' to make it rain." << endl;
|
||||||
|
cout << "'s' to make it snow." << endl;
|
||||||
|
break;
|
||||||
|
case RAINING:
|
||||||
|
cout << "It is raining." << endl;
|
||||||
|
cout << "Options:" << endl;
|
||||||
|
cout << "'c' to clear the sky." << endl;
|
||||||
|
cout << "'s' to make it snow." << endl;
|
||||||
|
break;
|
||||||
|
case SNOWING:
|
||||||
|
cout << "It is snowing." << endl;
|
||||||
|
cout << "Options:" << endl;
|
||||||
|
cout << "'c' to clear the sky." << endl;
|
||||||
|
cout << "'r' to make it rain." << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cout << "'q' to quit." << endl;
|
||||||
|
cout << "anything else to refresh" << endl;
|
||||||
|
cout << ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
int main (int numargs, const char ** args)
|
||||||
|
{
|
||||||
|
DFHack::ContextManager DFMgr("Memory.xml");
|
||||||
|
DFHack::Context *DF = DFMgr.getSingleContext();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DF->Attach();
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
#ifndef LINUX_BUILD
|
||||||
|
cin.ignore();
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
World *W = DF->getWorld();
|
||||||
|
W->Start();
|
||||||
|
bool end = false;
|
||||||
|
while(!end)
|
||||||
|
{
|
||||||
|
WeatherType current = (WeatherType) W->ReadCurrentWeather();
|
||||||
|
DF->Resume();
|
||||||
|
string command = "";
|
||||||
|
printWeather(current);
|
||||||
|
getline(cin, command);
|
||||||
|
DF->Suspend();
|
||||||
|
if(command == "c")
|
||||||
|
{
|
||||||
|
W->SetCurrentWeather(CLEAR);
|
||||||
|
}
|
||||||
|
else if(command == "r")
|
||||||
|
{
|
||||||
|
W->SetCurrentWeather(RAINING);
|
||||||
|
}
|
||||||
|
else if(command == "s")
|
||||||
|
{
|
||||||
|
W->SetCurrentWeather(SNOWING);
|
||||||
|
}
|
||||||
|
else if(command == "q")
|
||||||
|
{
|
||||||
|
end = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifndef LINUX_BUILD
|
||||||
|
std::cout << "Done. Press any key to continue" << std::endl;
|
||||||
|
cin.ignore();
|
||||||
|
#endif
|
||||||
|
DF->Detach();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue