Merge pull request #116 from matthew-cline/TOPIC-items

Improved Items module and itemhacks plugin
develop
Petr Mrázek 2011-07-25 01:10:07 -07:00
commit a8ff3d9d48
5 changed files with 333 additions and 63 deletions

@ -567,6 +567,42 @@ int Core::SDL_Event(SDL::Event* ev, int orig_return)
// do stuff with the events... // do stuff with the events...
} }
////////////////
// ClassNamCheck
////////////////
// Since there is no Process.cpp, put ClassNamCheck stuff in Core.cpp
static std::set<std::string> known_class_names;
static std::map<std::string, void*> known_vptrs;
ClassNameCheck::ClassNameCheck(std::string _name) : name(_name), vptr(0)
{
known_class_names.insert(name);
}
ClassNameCheck &ClassNameCheck::operator= (const ClassNameCheck &b)
{
name = b.name; vptr = b.vptr; return *this;
}
bool ClassNameCheck::operator() (Process *p, void * ptr) const {
if (vptr == 0 && p->readClassName(ptr) == name)
{
vptr = ptr;
known_vptrs[name] = ptr;
}
return (vptr && vptr == ptr);
}
void ClassNameCheck::getKnownClassNames(std::vector<std::string> &names)
{
std::set<std::string>::iterator it = known_class_names.begin();
for (; it != known_class_names.end(); it++)
names.push_back(*it);
}
/******************************************************************************* /*******************************************************************************
M O D U L E S M O D U L E S
*******************************************************************************/ *******************************************************************************/

@ -282,18 +282,18 @@ namespace DFHack
{ {
std::string name; std::string name;
mutable void * vptr; mutable void * vptr;
public: public:
ClassNameCheck() : vptr(0) {}; ClassNameCheck() : vptr(0) {}
ClassNameCheck(std::string _name) : name(_name), vptr(0) {}; ClassNameCheck(std::string _name);
ClassNameCheck &operator= (const ClassNameCheck &b) ClassNameCheck &operator= (const ClassNameCheck &b);
{
name = b.name; vptr = b.vptr; return *this; // Is the class name of the given virtual table pointer the same as the
} // name for thei ClassNameCheck object?
bool operator() (Process *p, void * ptr) const { bool operator() (Process *p, void * ptr) const;
if (vptr == 0 && p->readClassName(ptr) == name)
vptr = ptr; // Get list of names given to ClassNameCheck constructors.
return (vptr && vptr == ptr); static void getKnownClassNames(std::vector<std::string> &names);
};
}; };
} }
#endif #endif

@ -59,40 +59,40 @@ union t_itemflags
{ {
unsigned int on_ground : 1; ///< 0000 0001 Item on ground unsigned int on_ground : 1; ///< 0000 0001 Item on ground
unsigned int in_job : 1; ///< 0000 0002 Item currently being used in a job unsigned int in_job : 1; ///< 0000 0002 Item currently being used in a job
unsigned int u_ngrd1 : 1; ///< 0000 0004 unknown, unseen unsigned int hostile : 1; ///< 0000 0004 Item owned by hostile
unsigned int in_inventory : 1; ///< 0000 0008 Item in a creature or workshop inventory unsigned int in_inventory : 1; ///< 0000 0008 Item in a creature or workshop inventory
unsigned int u_ngrd2 : 1; ///< 0000 0010 unknown, lost (artifact)?, unseen unsigned int unk1 : 1; ///< 0000 0010 unknown, lost (artifact)?, unusable, unseen
unsigned int in_building : 1; ///< 0000 0020 Part of a building (including mechanisms, bodies in coffins) unsigned int in_building : 1; ///< 0000 0020 Part of a building (including mechanisms, bodies in coffins)
unsigned int u_ngrd3 : 1; ///< 0000 0040 unknown, unseen unsigned int unk2 : 1; ///< 0000 0040 unknown, unseen
unsigned int dead_dwarf : 1; ///< 0000 0080 Dwarf's dead body or body part unsigned int dead_dwarf : 1; ///< 0000 0080 Dwarf's dead body or body part
unsigned int rotten : 1; ///< 0000 0100 Rotten food unsigned int rotten : 1; ///< 0000 0100 Rotten food
unsigned int spider_web : 1; ///< 0000 0200 Thread in spider web unsigned int spider_web : 1; ///< 0000 0200 Thread in spider web
unsigned int construction : 1; ///< 0000 0400 Material used in construction unsigned int construction : 1; ///< 0000 0400 Material used in construction
unsigned int u_ngrd5 : 1; ///< 0000 0800 unknown, unseen unsigned int unk3 : 1; ///< 0000 0800 unknown, unseen, unusable
unsigned int unk3 : 1; ///< 0000 1000 unknown, unseen unsigned int unk4 : 1; ///< 0000 1000 unknown, unseen
unsigned int u_ngrd6 : 1; ///< 0000 2000 unknown, unseen unsigned int unk5 : 1; ///< 0000 2000 unknown, unseen
unsigned int foreign : 1; ///< 0000 4000 Item is imported unsigned int foreign : 1; ///< 0000 4000 Item is imported
unsigned int u_ngrd7 : 1; ///< 0000 8000 unknown, unseen unsigned int trader : 1; ///< 0000 8000 Item ownwed by trader
unsigned int owned : 1; ///< 0001 0000 Item is owned by a dwarf unsigned int owned : 1; ///< 0001 0000 Item is owned by a dwarf
unsigned int garbage_colect : 1; ///< 0002 0000 Marked for deallocation by DF it seems unsigned int garbage_colect : 1; ///< 0002 0000 Marked for deallocation by DF it seems
unsigned int artifact1 : 1; ///< 0004 0000 Artifact ? unsigned int artifact1 : 1; ///< 0004 0000 Artifact ?
unsigned int forbid : 1; ///< 0008 0000 Forbidden item unsigned int forbid : 1; ///< 0008 0000 Forbidden item
unsigned int unk5 : 1; ///< 0010 0000 unknown, unseen unsigned int unk6 : 1; ///< 0010 0000 unknown, unseen
unsigned int dump : 1; ///< 0020 0000 Designated for dumping unsigned int dump : 1; ///< 0020 0000 Designated for dumping
unsigned int on_fire: 1; ///< 0040 0000 Indicates if item is on fire, Will Set Item On Fire if Set! unsigned int on_fire: 1; ///< 0040 0000 Indicates if item is on fire, Will Set Item On Fire if Set!
unsigned int melt : 1; ///< 0080 0000 Designated for melting, if applicable unsigned int melt : 1; ///< 0080 0000 Designated for melting, if applicable
unsigned int hidden : 1; ///< 0100 0000 Hidden item unsigned int hidden : 1; ///< 0100 0000 Hidden item
unsigned int in_chest : 1; ///< 0200 0000 Stored in chest/part of well? unsigned int in_chest : 1; ///< 0200 0000 Stored in chest/part of well?
unsigned int unk6 : 1; ///< 0400 0000 unknown, unseen unsigned int unk7 : 1; ///< 0400 0000 unknown, unseen
unsigned int artifact2 : 1; ///< 0800 0000 Artifact ? unsigned int artifact2 : 1; ///< 0800 0000 Artifact ?
unsigned int unk8 : 1; ///< 1000 0000 unknown, unseen unsigned int unk8 : 1; ///< 1000 0000 unknown, unseen, common
unsigned int unk9 : 1; ///< 2000 0000 unknown, set when viewing details unsigned int unk9 : 1; ///< 2000 0000 unknown, set when viewing details
unsigned int unk10 : 1; ///< 4000 0000 unknown, unseen unsigned int unk10 : 1; ///< 4000 0000 unknown, unseen
unsigned int unk11 : 1; ///< 8000 0000 unknown, unseen unsigned int unk11 : 1; ///< 8000 0000 unknown, unseen
@ -120,9 +120,9 @@ struct t_item : public t_virtual
int16_t z; // 0x8 + 2 int16_t z; // 0x8 + 2
// 2B padding 0xA + 2 // 2B padding 0xA + 2
t_itemflags flags; // 0xC + 4 t_itemflags flags; // 0xC + 4
uint32_t unk1; // 0x10 + 4 uint32_t age ; // 0x10 + 4
uint32_t id; // 0x14 + 4 uint32_t id; // 0x14 + 4
std::vector<void *> unk2;// usage is pretty rare std::vector<void *> unk1;// Used by tasked items.
std::vector<t_itemref *> itemrefs; std::vector<t_itemref *> itemrefs;
}; };
@ -187,7 +187,10 @@ public:
/// wipe out the owner records /// wipe out the owner records
bool removeItemOwner(dfh_item &item, Creatures *creatures); bool removeItemOwner(dfh_item &item, Creatures *creatures);
bool readItemRefs(const dfh_item &item, const ClassNameCheck &classname, std::vector<int32_t> &values); bool readItemRefs(const dfh_item &item, const ClassNameCheck &classname,
std::vector<int32_t> &values);
bool unknownRefs(const dfh_item &item, std::vector<std::string>& names,
std::vector<int32_t>& values);
private: private:
class Private; class Private;
Private* d; Private* d;

@ -30,6 +30,7 @@ distribution.
#include <vector> #include <vector>
#include <cstdio> #include <cstdio>
#include <map> #include <map>
#include <set>
using namespace std; using namespace std;
#include "dfhack/Types.h" #include "dfhack/Types.h"
@ -427,9 +428,48 @@ class Items::Private
uint32_t refVectorOffset; uint32_t refVectorOffset;
uint32_t idFieldOffset; uint32_t idFieldOffset;
uint32_t itemVectorAddress; uint32_t itemVectorAddress;
ClassNameCheck isOwnerRefClass; ClassNameCheck isOwnerRefClass;
ClassNameCheck isContainerRefClass; ClassNameCheck isContainerRefClass;
ClassNameCheck isContainsRefClass; ClassNameCheck isContainsRefClass;
// Similar to isOwnerRefClass. Value is unique to each creature, but
// different than the creature's id.
ClassNameCheck isUnitHolderRefClass;
// One of these is present for each creature contained in a cage.
// The value is similar to that for isUnitHolderRefClass, different
// than the creature's ID but unique for each creature.
ClassNameCheck isCagedUnitRefClass;
// ID of bulding containing/holding the item.
ClassNameCheck isBuildingHolderRefClass;
// Building ID of lever/etc which triggers bridge/etc holding
// this mechanism.
ClassNameCheck isTriggeredByRefClass;
// Building ID of bridge/etc which is triggered by lever/etc holding
// this mechanism.
ClassNameCheck isTriggerTargetRefClass;
// Civilization ID of owner of item, for items not owned by the
// fortress.
ClassNameCheck isEntityOwnerRefClass;
// Item has been offered to the caravan. The value is the
// civilization ID of
ClassNameCheck isOfferedRefClass;
// Item is in a depot for trade. Purpose of value is unknown, but is
// different for each item, even in the same depot at the same time.
ClassNameCheck isTradingRefClass;
// Item is flying or falling through the air. The value seems to
// be the ID for a "projectile information" object.
ClassNameCheck isProjectileRefClass;
std::set<std::string> knownItemRefTypes;
}; };
Items::Items() Items::Items()
@ -438,14 +478,34 @@ Items::Items()
d = new Private; d = new Private;
d->owner = c.p; d->owner = c.p;
d->isOwnerRefClass = ClassNameCheck("general_ref_unit_itemownerst");
d->isContainerRefClass = ClassNameCheck("general_ref_contained_in_itemst");
d->isContainsRefClass = ClassNameCheck("general_ref_contains_itemst");
DFHack::OffsetGroup* itemGroup = c.vinfo->getGroup("Items"); DFHack::OffsetGroup* itemGroup = c.vinfo->getGroup("Items");
d->itemVectorAddress = itemGroup->getAddress("items_vector"); d->itemVectorAddress = itemGroup->getAddress("items_vector");
d->idFieldOffset = itemGroup->getOffset("id"); d->idFieldOffset = itemGroup->getOffset("id");
d->refVectorOffset = itemGroup->getOffset("item_ref_vector"); d->refVectorOffset = itemGroup->getOffset("item_ref_vector");
d->isOwnerRefClass = ClassNameCheck("general_ref_unit_itemownerst");
d->isContainerRefClass = ClassNameCheck("general_ref_contained_in_itemst");
d->isContainsRefClass = ClassNameCheck("general_ref_contains_itemst");
d->isUnitHolderRefClass = ClassNameCheck("general_ref_unit_holderst");
d->isCagedUnitRefClass = ClassNameCheck("general_ref_contains_unitst");
d->isBuildingHolderRefClass
= ClassNameCheck("general_ref_building_holderst");
d->isTriggeredByRefClass = ClassNameCheck("general_ref_building_triggerst");
d->isTriggerTargetRefClass
= ClassNameCheck("general_ref_building_triggertargetst");
d->isEntityOwnerRefClass = ClassNameCheck("general_ref_entity_itemownerst");
d->isOfferedRefClass = ClassNameCheck("general_ref_entity_offeredst");
d->isTradingRefClass = ClassNameCheck("general_ref_unit_tradebringerst");
d->isProjectileRefClass = ClassNameCheck("general_ref_projectilest");
std::vector<std::string> known_names;
ClassNameCheck::getKnownClassNames(known_names);
for (size_t i = 0; i < known_names.size(); i++)
{
if (known_names[i].find("general_ref_") == 0)
d->knownItemRefTypes.insert(known_names[i]);
}
} }
bool Items::Start() bool Items::Start()
@ -562,6 +622,28 @@ bool Items::readItemRefs(const dfh_item &item, const ClassNameCheck &classname,
return !values.empty(); return !values.empty();
} }
bool Items::unknownRefs(const dfh_item &item, std::vector<std::string>& names,
std::vector<int32_t>& values)
{
names.clear();
values.clear();
std::vector <t_itemref *> &p_refs = item.base->itemrefs;
for (uint32_t i=0; i<p_refs.size(); i++)
{
std::string name = p_refs[i]->getClassName();
if (d->knownItemRefTypes.find(name) == d->knownItemRefTypes.end())
{
names.push_back(name);
values.push_back(p_refs[i]->value);
}
}
return (names.size() > 0);
}
bool Items::removeItemOwner(dfh_item &item, Creatures *creatures) bool Items::removeItemOwner(dfh_item &item, Creatures *creatures)
{ {
std::vector <t_itemref *> &p_refs = item.base->itemrefs; std::vector <t_itemref *> &p_refs = item.base->itemrefs;

@ -6,24 +6,165 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <string.h> #include <string.h>
#include <stdio.h> // sprintf()
using std::vector; using std::vector;
using std::string; using std::string;
using namespace DFHack; using namespace DFHack;
//////////////////////
// START item choosers
//////////////////////
class item_chooser
{
public:
item_chooser(Core* _c, DFHack::Items* _Items) : c(_c), Items(_Items)
{
}
virtual bool doPrint(DFHack::dfh_item *itm) = 0;
virtual void postPrint(DFHack::dfh_item *itm)
{
}
protected:
Core *c;
DFHack::Items *Items;
};
class choose_all : public item_chooser
{
public:
choose_all(Core* _c, ::Items* _Items) : item_chooser(_c, _Items)
{
}
virtual bool doPrint(DFHack::dfh_item *itm)
{
return (true);
}
};
class choose_unknown : public item_chooser
{
public:
choose_unknown(Core* _c, ::Items* _Items) : item_chooser(_c, _Items)
{
}
virtual bool doPrint(DFHack::dfh_item *itm)
{
if (itm->base->unk1.size() > 0)
return true;
std::vector<std::string> refs;
std::vector<int32_t> values;
if (Items->unknownRefs(*itm, refs, values))
return true;
t_itemflags &f = itm->base->flags;
return (f.unk1 || f.unk2 || f.unk3 || f.unk4 || f.unk5 ||
f.unk6 || f.unk7 ||
// f.unk8 || f.unk9 || /* Too common */
f.unk10 || f.unk11);
}
virtual void postPrint(DFHack::dfh_item *itm)
{
std::vector<std::string> flags;
t_itemflags &f = itm->base->flags;
if (itm->base->unk1.size() > 0)
c->con.print(" vec1: %p\n", itm->base->unk1[0]);
std::vector<std::string> refs;
std::vector<int32_t> values;
if (Items->unknownRefs(*itm, refs, values))
{
c->con.print(" refs: ");
for (size_t i = 0; i < refs.size(); i++)
{
c->con.print("%s: %d", refs[i].c_str(), values[i]);
if ( (i + 1) < refs.size() )
c->con.print(", ");
}
c->con.print("\n");
}
if (f.unk1) flags.push_back("unk1");
if (f.unk2) flags.push_back("unk2");
if (f.unk3) flags.push_back("unk3");
if (f.unk4) flags.push_back("unk4");
if (f.unk5) flags.push_back("unk5");
if (f.unk6) flags.push_back("unk6");
if (f.unk7) flags.push_back("unk7");
if (f.unk8) flags.push_back("unk8");
if (f.unk9) flags.push_back("unk9");
if (f.unk10) flags.push_back("unk10");
if (f.unk11) flags.push_back("unk11");
if (flags.size() > 0)
{
c->con.print(" flags: ");
for (size_t i = 0; i < flags.size(); i++)
{
c->con.print("%s", flags[i].c_str());
if ( (i + 1) < flags.size() )
c->con.print(", ");
}
c->con.print("\n");
}
}
};
class choose_cursor : public item_chooser
{
public:
choose_cursor(Core* _c, ::Items* _Items, int32_t _x, int32_t _y, int32_t _z)
: item_chooser(_c, _Items), x(_x), y(_y), z(_z)
{
}
virtual bool doPrint(DFHack::dfh_item *itm)
{
return (itm->base->x == x && itm->base->y == y && itm->base->z == z
&& itm->base->flags.on_ground
&& !itm->base->flags.in_chest
&& !itm->base->flags.in_inventory
&& !itm->base->flags.in_building);
}
protected:
int32_t x, y, z;
};
//////////////////////
// END item choosers
//////////////////////
DFhackCExport command_result df_dumpitems (Core * c, vector <string> & parameters); DFhackCExport command_result df_dumpitems (Core * c, vector <string> & parameters);
DFhackCExport command_result df_itscanvec1 (Core * c, vector <string> & parameters);
DFhackCExport const char * plugin_name ( void ) DFhackCExport const char * plugin_name ( void )
{ {
return "itemhacks"; return "itemhacks";
} }
DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand> &commands) DFhackCExport command_result plugin_init ( Core * c,
std::vector <PluginCommand> &commands)
{ {
commands.clear(); commands.clear();
commands.push_back(PluginCommand("dumpitems", "Dump items...", df_dumpitems)); commands.push_back(PluginCommand("dumpitems",
commands.push_back(PluginCommand("itscanvec1", "Dump items that have the first vector valid.", df_itscanvec1)); "Dump items\
\n Options:\
\n unkown: Dump items that have anything unknown set",
df_dumpitems));
return CR_OK; return CR_OK;
} }
@ -32,31 +173,9 @@ DFhackCExport command_result plugin_shutdown ( Core * c )
return CR_OK; return CR_OK;
} }
DFhackCExport command_result df_itscanvec1 (Core * c, vector <string> & parameters)
{
c->Suspend();
DFHack::Items * Items = c->getItems();
Items->Start();
std::vector<t_item *> p_items;
Items->readItemVector(p_items);
for(int i = 0; i < p_items.size();i++)
{
t_item * itm = p_items[i];
if(itm->unk2.size())
{
c->con.print("Found %x, size %d\n",itm,itm->unk2.size());
}
}
c->Resume();
return CR_OK;
}
DFhackCExport command_result df_dumpitems (Core * c, vector <string> & parameters) DFhackCExport command_result df_dumpitems (Core * c, vector <string> & parameters)
{ {
c->Suspend(); c->Suspend();
bool print_hex = false;
if(parameters.size() && parameters[0] == "hex")
print_hex = true;
DFHack::Materials * Materials = c->getMaterials(); DFHack::Materials * Materials = c->getMaterials();
Materials->ReadAllMaterials(); Materials->ReadAllMaterials();
@ -72,30 +191,56 @@ DFhackCExport command_result df_dumpitems (Core * c, vector <string> & parameter
Items->readItemVector(p_items); Items->readItemVector(p_items);
uint32_t size = p_items.size(); uint32_t size = p_items.size();
item_chooser *chooser = NULL;
if (x != -30000)
chooser = new choose_cursor(c, Items, x, y, z);
else if (parameters.size() == 0)
chooser = new choose_all(c, Items);
else if (parameters[0] == "unknown")
chooser = new choose_unknown(c, Items);
else
{
c->con.printerr("Invalid option: %s\n", parameters[0].c_str());
Items->Finish();
c->Resume();
return CR_FAILURE;
}
for(size_t i = 0; i < size; i++) for(size_t i = 0; i < size; i++)
{ {
DFHack::dfh_item itm; DFHack::dfh_item itm;
memset(&itm, 0, sizeof(DFHack::dfh_item)); memset(&itm, 0, sizeof(DFHack::dfh_item));
Items->readItem(p_items[i],itm); Items->readItem(p_items[i],itm);
if (x != -30000 if (!chooser->doPrint(&itm))
&& !(itm.base->x == x && itm.base->y == y && itm.base->z == z
&& itm.base->flags.on_ground
&& !itm.base->flags.in_chest
&& !itm.base->flags.in_inventory
&& !itm.base->flags.in_building))
continue; continue;
// Print something useful, instead of (-30000,-30000,-30000), if
// the item isn't on the ground.
char location[80];
if (itm.base->flags.in_chest)
sprintf(location, "chest");
else if (itm.base->flags.in_inventory)
sprintf(location, "inventory");
else if (itm.base->flags.in_building)
sprintf(location, "building");
else
sprintf(location, "%d,%d,%d", itm.base->x, itm.base->y,
itm.base->z);
c->con.print( c->con.print(
"%5d: addr:0x%08x %6d %08x (%d,%d,%d) vptr:0x%08x [%d] *%d %s - %s\n", "%5d: addr:0x%08x %6d %08x (%s) vptr:0x%08x [%d]\n"
" *%d %s - %s\n",
i, itm.base, itm.base->id, itm.base->flags.whole, i, itm.base, itm.base->id, itm.base->flags.whole,
itm.base->x, itm.base->y, itm.base->z, location,
itm.base->vptr, itm.base->vptr,
itm.wear_level, itm.wear_level,
itm.quantity, itm.quantity,
Items->getItemClass(itm.matdesc.itemType).c_str(), Items->getItemClass(itm.matdesc.itemType).c_str(),
Items->getItemDescription(itm, Materials).c_str() Items->getItemDescription(itm, Materials).c_str()
); );
chooser->postPrint(&itm);
/* /*
if (print_hex) if (print_hex)
hexdump(DF,p_items[i],0x300); hexdump(DF,p_items[i],0x300);
@ -116,5 +261,9 @@ DFhackCExport command_result df_dumpitems (Core * c, vector <string> & parameter
*/ */
} }
c->Resume(); c->Resume();
Items->Finish();
delete chooser;
return CR_OK; return CR_OK;
} }