develop
Petr Mrázek 2010-08-23 01:29:55 +02:00
parent 1bceac2343
commit 368de864a1
5 changed files with 361 additions and 121 deletions

@ -120,19 +120,19 @@ Process * BadProcesses::operator[](uint32_t index)
Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID) Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID)
{ {
Process *p1 = new SHMProcess(ID.pid,meminfo->meminfo); Process *p1 = new SHMProcess(ID.pid,meminfo->versions);
if(p1->isIdentified()) if(p1->isIdentified())
return p1; return p1;
else else
delete p1; delete p1;
Process *p2 = new NormalProcess(ID.pid,meminfo->meminfo); Process *p2 = new NormalProcess(ID.pid,meminfo->versions);
if(p2->isIdentified()) if(p2->isIdentified())
return p2; return p2;
else else
delete p2; delete p2;
#ifdef LINUX_BUILD #ifdef LINUX_BUILD
Process *p3 = new WineProcess(ID.pid,meminfo->meminfo); Process *p3 = new WineProcess(ID.pid,meminfo->versions);
if(p3->isIdentified()) if(p3->isIdentified())
return p3; return p3;
else else

@ -118,6 +118,8 @@ class VersionInfo::Private
string version; string version;
OSType OS; OSType OS;
std::string md5;
uint32_t PE_timestamp;
}; };
@ -130,6 +132,8 @@ VersionInfo::VersionInfo()
d->classindex = 0; d->classindex = 0;
d->levels.reserve(NUM_RESERVE_LVLS); d->levels.reserve(NUM_RESERVE_LVLS);
d->moods.reserve(NUM_RESERVE_MOODS); d->moods.reserve(NUM_RESERVE_MOODS);
d->md5 = "invalid";
d->PE_timestamp = 0;
} }
@ -137,31 +141,37 @@ VersionInfo::VersionInfo()
VersionInfo::VersionInfo(const VersionInfo &old) VersionInfo::VersionInfo(const VersionInfo &old)
:d(new Private) :d(new Private)
{ {
d->version = old.d->version; copy(&old);
d->OS = old.d->OS; }
d->addresses = old.d->addresses;
d->offsets = old.d->offsets; void VersionInfo::copy(const VersionInfo * old)
d->hexvals = old.d->hexvals; {
d->strings = old.d->strings; d->version = old->d->version;
d->base = old.d->base; d->OS = old->d->OS;
d->md5 = old->d->md5;
d->PE_timestamp = old->d->PE_timestamp;
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; //d->classes = old.d->classes;
for(uint32_t i = 0; i < old.d->classes.size(); i++) for(uint32_t i = 0; i < old->d->classes.size(); i++)
{ {
t_class * copy = new t_class(*old.d->classes[i]); t_class * copy = new t_class(*old->d->classes[i]);
d->classes.push_back(copy); d->classes.push_back(copy);
} }
d->classnames = old.d->classnames; d->classnames = old->d->classnames;
d->classindex = old.d->classindex; d->classindex = old->d->classindex;
d->professions = old.d->professions; d->professions = old->d->professions;
d->jobs = old.d->jobs; d->jobs = old->d->jobs;
d->skills = old.d->skills; d->skills = old->d->skills;
d->traits = old.d->traits; d->traits = old->d->traits;
d->labors = old.d->labors; d->labors = old->d->labors;
d->levels = old.d->levels; d->levels = old->d->levels;
d->moods = old.d->moods; d->moods = old->d->moods;
} }
void VersionInfo::setParentProcess(Process * _p) void VersionInfo::setParentProcess(Process * _p)
{ {
d->p = _p; d->p = _p;
@ -198,6 +208,25 @@ string VersionInfo::getVersion()
return d->version; return d->version;
} }
void VersionInfo::setMD5(const string &v)
{
d->md5 = v;
}
string VersionInfo::getMD5()
{
return d->md5;
}
void VersionInfo::setPE(uint32_t v)
{
d->PE_timestamp = v;
}
uint32_t VersionInfo::getPE()
{
return d->PE_timestamp;
}
void VersionInfo::setOS(const char *os) void VersionInfo::setOS(const char *os)
{ {
@ -206,6 +235,8 @@ void VersionInfo::setOS(const char *os)
d->OS = OS_WINDOWS; d->OS = OS_WINDOWS;
else if(oss == "linux") else if(oss == "linux")
d->OS = OS_LINUX; d->OS = OS_LINUX;
else if(oss == "apple")
d->OS = OS_APPLE;
else else
d->OS = OS_BAD; d->OS = OS_BAD;
} }
@ -217,6 +248,8 @@ void VersionInfo::setOS(const string &os)
d->OS = OS_WINDOWS; d->OS = OS_WINDOWS;
else if(os == "linux") else if(os == "linux")
d->OS = OS_LINUX; d->OS = OS_LINUX;
else if(os == "apple")
d->OS = OS_APPLE;
else else
d->OS = OS_BAD; d->OS = OS_BAD;
} }
@ -550,12 +583,8 @@ void VersionInfo::RebaseAddresses(const int32_t new_base)
// change base of all addresses *and* vtable entries // change base of all addresses *and* vtable entries
void VersionInfo::RebaseAll(int32_t new_base) void VersionInfo::RebaseAll(int32_t new_base)
{ {
map<string, uint32_t>::iterator iter; RebaseAddresses(new_base);
int32_t rebase = - (int32_t)d->base + new_base; 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); RebaseVTable(rebase);
} }

@ -32,18 +32,19 @@ using namespace DFHack;
VersionInfoFactory::~VersionInfoFactory() VersionInfoFactory::~VersionInfoFactory()
{ {
// for each in std::vector<memory_info*> meminfo;, delete // for each stored version, delete
for(uint32_t i = 0; i < meminfo.size();i++) for(uint32_t i = 0; i < versions.size();i++)
{ {
delete meminfo[i]; delete versions[i];
} }
meminfo.clear(); versions.clear();
} }
void VersionInfoFactory::ParseVTable(TiXmlElement* vtable, VersionInfo* mem) void VersionInfoFactory::ParseVTable(TiXmlElement* vtable, VersionInfo* mem)
{ {
TiXmlElement* pClassEntry; TiXmlElement* pClassEntry;
TiXmlElement* pClassSubEntry; TiXmlElement* pClassSubEntry;
/*
// check for rebase, do rebase if check positive // check for rebase, do rebase if check positive
const char * rebase = vtable->Attribute("rebase"); const char * rebase = vtable->Attribute("rebase");
if(rebase) if(rebase)
@ -51,6 +52,7 @@ void VersionInfoFactory::ParseVTable(TiXmlElement* vtable, VersionInfo* mem)
int32_t rebase_offset = strtol(rebase, NULL, 16); int32_t rebase_offset = strtol(rebase, NULL, 16);
mem->RebaseVTable(rebase_offset); mem->RebaseVTable(rebase_offset);
} }
*/
// parse vtable entries // parse vtable entries
pClassEntry = vtable->FirstChildElement(); pClassEntry = vtable->FirstChildElement();
for(;pClassEntry;pClassEntry=pClassEntry->NextSiblingElement()) for(;pClassEntry;pClassEntry=pClassEntry->NextSiblingElement())
@ -92,26 +94,214 @@ void VersionInfoFactory::ParseVTable(TiXmlElement* vtable, VersionInfo* mem)
} }
} }
void VersionInfoFactory::ParseEntry (TiXmlElement* entry, VersionInfo* mem, map <string ,TiXmlElement *>& knownEntries) 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
//ParseBaseOffsets(pMemEntry, mem);
continue;
}
else if (type == "Professions")
{
pElement2nd = entry->FirstChildElement("Profession")->ToElement();
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 = entry->FirstChildElement("Job")->ToElement();
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 = entry->FirstChildElement("Skill")->ToElement();
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 = entry->FirstChildElement("Trait")->ToElement();
for(;pElement2nd;pElement2nd=pElement2nd->NextSiblingElement("Trait"))
{
const char * id = pElement2nd->Attribute("id");
const char * name = pElement2nd->Attribute("name");
const char * lvl0 = pElement->Attribute("level_0");
const char * lvl1 = pElement->Attribute("level_1");
const char * lvl2 = pElement->Attribute("level_2");
const char * lvl3 = pElement->Attribute("level_3");
const char * lvl4 = pElement->Attribute("level_4");
const char * lvl5 = pElement->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 = entry->FirstChildElement("Labor")->ToElement();
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 = entry->FirstChildElement("Level")->ToElement();
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 = entry->FirstChildElement("Mood")->ToElement();
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;
versions.push_back(newmem);
}
mem->copy(desc.second);
}
}
void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
{ {
TiXmlElement* pMemEntry; TiXmlElement* pMemEntry;
const char *cstr_version = entry->Attribute("version"); const char *cstr_name = entry->Attribute("name");
const char *cstr_os = entry->Attribute("os"); const char *cstr_os = entry->Attribute("os");
const char *cstr_base = entry->Attribute("base"); const char *cstr_base = entry->Attribute("base");
const char *cstr_rebase = entry->Attribute("rebase"); const char *cstr_rebase = entry->Attribute("rebase");
if(cstr_base) if(cstr_base)
{ {
string base = cstr_base; string base = cstr_base;
ParseEntry(knownEntries[base], mem, knownEntries); EvalVersion(base, mem);
} }
if (!cstr_version) if (!cstr_name)
throw Error::MemoryXmlBadAttribute("version"); throw Error::MemoryXmlBadAttribute("name");
if (!cstr_os) if (!cstr_os)
throw Error::MemoryXmlBadAttribute("os"); throw Error::MemoryXmlBadAttribute("os");
string os = cstr_os; string os = cstr_os;
mem->setVersion(cstr_version); mem->setVersion(cstr_name);
mem->setOS(cstr_os); mem->setOS(cstr_os);
// offset inherited addresses by 'rebase'. // offset inherited addresses by 'rebase'.
@ -134,10 +324,6 @@ void VersionInfoFactory::ParseEntry (TiXmlElement* entry, VersionInfo* mem, map
// users are free to use a sane kernel that doesn't do this kind of **** by default // users are free to use a sane kernel that doesn't do this kind of **** by default
mem->setBase(0x0); mem->setBase(0x0);
} }
else if ( os == "all")
{
// yay
}
else else
{ {
throw Error::MemoryXmlBadAttribute("os"); throw Error::MemoryXmlBadAttribute("os");
@ -148,75 +334,55 @@ void VersionInfoFactory::ParseEntry (TiXmlElement* entry, VersionInfo* mem, map
pMemEntry = entry->FirstChildElement()->ToElement(); pMemEntry = entry->FirstChildElement()->ToElement();
for(;pMemEntry;pMemEntry=pMemEntry->NextSiblingElement()) 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; string type, name, value;
const char *cstr_type = pMemEntry->Value();
type = cstr_type; type = cstr_type;
// check for missing parts
if(type == "VTable") if(type == "VTable")
{ {
ParseVTable(pMemEntry, mem); ParseVTable(pMemEntry, mem);
continue; continue;
} }
if(!(cstr_name && cstr_value)) else if(type == "Offsets")
{
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")); /*
} if (type == "HexValue")
else if (type == "Labor") {
{ mem->setHexValue(name, value);
mem->setLabor(value,name); }
} else if (type == "Address")
else if (type == "Level") {
{ mem->setAddress(name, value);
mem->setLevel(value, name, pMemEntry->Attribute("xpNxtLvl")); }
else if (type == "Offset")
{
mem->setOffset(name, value);
}
else if (type == "String")
{
mem->setString(name, value);
}
else
{
throw Error::MemoryXmlUnknownType(type.c_str());
}
*/
//ParseOffsets(pMemEntry, mem);
continue;
} }
else if (type == "Mood") else if (type == "MD5")
{ {
mem->setMood(value, name); const char *cstr_value = pMemEntry->Attribute("value");
if(!cstr_value)
throw Error::MemoryXmlUnderspecifiedEntry(cstr_name);
mem->setMD5(cstr_value);
} }
else else if (type == "PETimeStamp")
{ {
throw Error::MemoryXmlUnknownType(type.c_str()); const char *cstr_value = pMemEntry->Attribute("value");
if(!cstr_value)
throw Error::MemoryXmlUnderspecifiedEntry(cstr_name);
mem->setPE(atol(cstr_value));
} }
} // for } // for
} // method } // method
@ -240,7 +406,7 @@ bool VersionInfoFactory::loadFile(string path_to_xml)
TiXmlHandle hDoc(&doc); TiXmlHandle hDoc(&doc);
TiXmlElement* pElem; TiXmlElement* pElem;
TiXmlHandle hRoot(0); TiXmlHandle hRoot(0);
VersionInfo mem; VersionInfo *mem;
// block: name // block: name
{ {
@ -263,31 +429,54 @@ bool VersionInfoFactory::loadFile(string path_to_xml)
// transform elements // transform elements
{ {
// trash existing list // trash existing list
for(uint32_t i = 0; i < meminfo.size(); i++) for(uint32_t i = 0; i < versions.size(); i++)
{ {
delete meminfo[i]; delete versions[i];
} }
meminfo.clear(); versions.clear();
TiXmlElement* pMemInfo=hRoot.FirstChild( "MemoryDescriptors" ).FirstChild( "Entry" ).Element();
// For each base version
TiXmlElement* pMemInfo=hRoot.FirstChild( "Base" ).Element();
map <string ,TiXmlElement *> map_pNamedEntries; map <string ,TiXmlElement *> map_pNamedEntries;
vector <TiXmlElement *> v_pEntries; vector <string> v_sEntries;
for( ; pMemInfo; pMemInfo=pMemInfo->NextSiblingElement("Entry")) for( ; pMemInfo; pMemInfo=pMemInfo->NextSiblingElement("Base"))
{ {
v_pEntries.push_back(pMemInfo); const char *name = pMemInfo->Attribute("name");
const char *id = pMemInfo->Attribute("id"); if(name)
if(id)
{ {
string str_id = id; string str_name = name;
map_pNamedEntries[str_id] = pMemInfo; VersionInfo *base = new VersionInfo();
ParseBase( pMemInfo , mem );
knownVersions[str_name] = v_descr (pMemInfo, mem);
} }
} }
for(uint32_t i = 0; i< v_pEntries.size();i++)
// 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++)
{ {
VersionInfo *mem = new VersionInfo();
//FIXME: add a set of entries processed in a step of this cycle, use it to check for infinite loops //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); string & name = v_sEntries[i];
meminfo.push_back(mem); v_descr & desc = knownVersions[name];
if(!desc.second)
{
VersionInfo *version = new VersionInfo();
ParseVersion( desc.first , version );
versions.push_back(version);
}
} }
// process found things here // process found things here
} }
error = false; error = false;

@ -28,6 +28,7 @@ distribution.
#include "DFPragma.h" #include "DFPragma.h"
#include "DFExport.h" #include "DFExport.h"
#include "dfhack/DFTypes.h" #include "dfhack/DFTypes.h"
#include <sys/types.h>
namespace DFHack namespace DFHack
{ {
@ -36,21 +37,31 @@ namespace DFHack
*/ */
class Process; class Process;
struct t_class; struct t_class;
class VersionInfoPrivate;
class OffsetGroupPrivate;
class DFHACK_EXPORT OffsetGroup
{
private:
OffsetGroupPrivate * d;
public:
};
class DFHACK_EXPORT VersionInfo class DFHACK_EXPORT VersionInfo
{ {
private: private:
class Private; VersionInfoPrivate * d;
Private * d;
public: public:
enum OSType enum OSType
{ {
OS_WINDOWS, OS_WINDOWS,
OS_LINUX, OS_LINUX,
OS_APPLE,
OS_BAD OS_BAD
}; };
VersionInfo(); VersionInfo();
VersionInfo(const VersionInfo&); VersionInfo(const VersionInfo&);
void copy(const DFHack::VersionInfo* old);
~VersionInfo(); ~VersionInfo();
void RebaseAddresses(const int32_t new_base); void RebaseAddresses(const int32_t new_base);
@ -59,6 +70,12 @@ namespace DFHack
void setBase (const std::string&); void setBase (const std::string&);
void setBase (const uint32_t); void setBase (const uint32_t);
void setMD5 (const std::string & _md5);
std::string getMD5();
void setPE (uint32_t PE_);
uint getPE();
int32_t getOffset (const std::string&); int32_t getOffset (const std::string&);
uint32_t getAddress (const std::string&); uint32_t getAddress (const std::string&);
uint32_t getHexValue (const std::string&); uint32_t getHexValue (const std::string&);
@ -101,7 +118,7 @@ namespace DFHack
void setHexValue (const std::string &, const std::string &); void setHexValue (const std::string &, const std::string &);
void setString (const std::string &, const std::string &); void setString (const std::string &, const std::string &);
void setProfession(const std::string &, const std::string &); void setProfession(const std::string & id, const std::string & name);
void setJob(const std::string &, const std::string &); void setJob(const std::string &, const std::string &);
void setSkill(const std::string &, const std::string &); void setSkill(const std::string &, const std::string &);
void setTrait(const std::string &, const std::string &, const std::string &, void setTrait(const std::string &, const std::string &, const std::string &,

@ -35,16 +35,21 @@ namespace DFHack
{ {
friend class ProcessEnumerator; friend class ProcessEnumerator;
public: public:
VersionInfoFactory(string path_to_xml); VersionInfoFactory(std::string path_to_xml);
~VersionInfoFactory(); ~VersionInfoFactory();
// memory info entries loaded from a file // memory info entries loaded from a file
bool loadFile( string path_to_xml); bool loadFile( std::string path_to_xml);
bool isInErrorState() const {return error;}; bool isInErrorState() const {return error;};
std::vector<VersionInfo*> meminfo; std::vector<VersionInfo*> versions;
private: private:
void ParseVTable(TiXmlElement* vtable, VersionInfo* mem); void ParseVTable(TiXmlElement* vtable, VersionInfo* mem);
void ParseEntry (TiXmlElement* entry, VersionInfo* mem, map <string ,TiXmlElement *>& knownEntries); void ParseBase (TiXmlElement* base, VersionInfo* mem);
void ParseVersion (TiXmlElement* version, VersionInfo* mem);
// copy version 'base' to 'target' or throw
void EvalVersion(std::string base, VersionInfo* target);
bool error; bool error;
typedef std::pair < TiXmlElement *, VersionInfo *> v_descr;
std::map <std::string , v_descr > knownVersions;
}; };
} }