diff --git a/library/DFProcess-linux-SHM.cpp b/library/DFProcess-linux-SHM.cpp index d87ebb8bc..3d13f0397 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -382,6 +382,11 @@ void SHMProcess::readSTLVector(const uint32_t address, t_vecTriplet & triplet) read(address + d->vector_start, sizeof(triplet), (uint8_t *) &triplet); } +void SHMProcess::writeSTLVector(const uint32_t address, t_vecTriplet & triplet) +{ + write(address + d->vector_start, sizeof(triplet), (uint8_t *) &triplet); +} + string SHMProcess::doReadClassName (uint32_t vptr) { diff --git a/library/DFProcess-linux-wine.cpp b/library/DFProcess-linux-wine.cpp index 58c309173..0fb56d88f 100644 --- a/library/DFProcess-linux-wine.cpp +++ b/library/DFProcess-linux-wine.cpp @@ -69,6 +69,7 @@ namespace { bool forceresume(); void readSTLVector(const uint32_t address, t_vecTriplet & triplet); + void writeSTLVector(const uint32_t address, t_vecTriplet & triplet); const std::string readSTLString (uint32_t offset); size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity); size_t writeSTLString(const uint32_t address, const std::string writeString); @@ -152,6 +153,11 @@ void WineProcess::readSTLVector(const uint32_t address, t_vecTriplet & triplet) read(address + vector_start, sizeof(triplet), (uint8_t *) &triplet); } +void WineProcess::writeSTLVector(const uint32_t address, t_vecTriplet & triplet) +{ + write(address + vector_start, sizeof(triplet), (uint8_t *) &triplet); +} + size_t WineProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) { return stl.readSTLString(offset, buffer, bufcapacity); diff --git a/library/DFProcess-linux.cpp b/library/DFProcess-linux.cpp index 6926d229e..f1f99ee64 100644 --- a/library/DFProcess-linux.cpp +++ b/library/DFProcess-linux.cpp @@ -68,6 +68,7 @@ namespace { bool forceresume(); void readSTLVector(const uint32_t address, t_vecTriplet & triplet); + void writeSTLVector(const uint32_t address, t_vecTriplet & triplet); const std::string readSTLString (uint32_t offset); size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity); size_t writeSTLString(const uint32_t address, const std::string writeString); @@ -178,6 +179,11 @@ void NormalProcess::readSTLVector(const uint32_t address, t_vecTriplet & triplet read(address + vector_start, sizeof(triplet), (uint8_t *) &triplet); } +void NormalProcess::writeSTLVector(const uint32_t address, t_vecTriplet & triplet) +{ + write(address + vector_start, sizeof(triplet), (uint8_t *) &triplet); +} + const string NormalProcess::readSTLString (uint32_t offset) { _Rep_base header; diff --git a/library/DFProcess-windows-SHM.cpp b/library/DFProcess-windows-SHM.cpp index 9ce4fa2cf..ae5b71e77 100644 --- a/library/DFProcess-windows-SHM.cpp +++ b/library/DFProcess-windows-SHM.cpp @@ -394,6 +394,11 @@ void SHMProcess::readSTLVector(const uint32_t address, t_vecTriplet & triplet) read(address + d->vector_start, sizeof(triplet), (uint8_t *) &triplet); } +void SHMProcess::writeSTLVector(const uint32_t address, t_vecTriplet & triplet) +{ + write(address + d->vector_start, sizeof(triplet), (uint8_t *) &triplet); +} + string SHMProcess::doReadClassName (uint32_t vptr) { int rtti = Process::readDWord(vptr - 0x4); diff --git a/library/DFProcess-windows.cpp b/library/DFProcess-windows.cpp index 10a5d1459..cb651e3af 100644 --- a/library/DFProcess-windows.cpp +++ b/library/DFProcess-windows.cpp @@ -85,6 +85,7 @@ namespace void write(uint32_t address, uint32_t length, uint8_t* buffer); void readSTLVector(const uint32_t address, t_vecTriplet & triplet); + void writeSTLVector(const uint32_t address, t_vecTriplet & triplet); const std::string readSTLString (uint32_t offset); size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity); size_t writeSTLString(const uint32_t address, const std::string writeString); @@ -575,6 +576,11 @@ void NormalProcess::readSTLVector(const uint32_t address, t_vecTriplet & triplet read(address + vector_start, sizeof(triplet), (uint8_t *) &triplet); } +void NormalProcess::writeSTLVector(const uint32_t address, t_vecTriplet & triplet) +{ + write(address + vector_start, sizeof(triplet), (uint8_t *) &triplet); +} + size_t NormalProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) { return stl.readSTLString(offset, buffer, bufcapacity); diff --git a/library/include/dfhack/DFProcess.h b/library/include/dfhack/DFProcess.h index 4105f866b..b0f4cc658 100644 --- a/library/include/dfhack/DFProcess.h +++ b/library/include/dfhack/DFProcess.h @@ -182,6 +182,7 @@ namespace DFHack /// read a STL vector virtual void readSTLVector(const uint32_t address, t_vecTriplet & triplet) = 0; + virtual void writeSTLVector(const uint32_t address, t_vecTriplet & triplet) = 0; /// get class name of an object with rtti/type info virtual std::string doReadClassName(uint32_t vptr) = 0; @@ -225,21 +226,19 @@ namespace DFHack }; class DFHACK_EXPORT ClassNameCheck - { + { std::string name; - uint32_t vptr; + mutable uint32_t vptr; public: ClassNameCheck() : vptr(0) {}; ClassNameCheck(std::string _name) : name(_name), vptr(0) {}; ClassNameCheck &operator= (const ClassNameCheck &b) - { + { name = b.name; vptr = b.vptr; return *this; - }; - bool operator() (Process *p, uint32_t ptr) - { - if(vptr == 0) - if (p->readClassName(ptr) == name) - vptr = ptr; + } + bool operator() (Process *p, uint32_t ptr) const { + if (vptr == 0 && p->readClassName(ptr) == name) + vptr = ptr; return (vptr && vptr == ptr); }; }; diff --git a/library/include/dfhack/DFVector.h b/library/include/dfhack/DFVector.h index cbe32d8c9..b9e4d698e 100644 --- a/library/include/dfhack/DFVector.h +++ b/library/include/dfhack/DFVector.h @@ -31,18 +31,30 @@ distribution. #include "DFExport.h" #include "VersionInfo.h" #include "DFProcess.h" + +#include + namespace DFHack { template class DFHACK_EXPORT DfVector { private: + Process *_p; + uint32_t _address; t_vecTriplet t; uint32_t _size;// vector size T * data; // cached data + + bool isMetadataInSync() + { + t_vecTriplet t2; + _p->readSTLVector(_address,t2); + return (t2.start == t.start || t2.end == t.end || t2.alloc_end == t.alloc_end); + } public: - DfVector(Process * p, uint32_t address) + DfVector(Process *p, uint32_t address) : _p(p), _address(address) { p->readSTLVector(address,t); uint32_t byte_size = t.end - t.start; @@ -60,18 +72,43 @@ namespace DFHack delete [] data; }; // get offset of the specified index - inline T& operator[] (uint32_t index) + inline const T& operator[] (uint32_t index) { // FIXME: vector out of bounds exception //assert(index < size); return data[index]; }; // get offset of the specified index - inline T& at (uint32_t index) + inline const T& at (uint32_t index) { //assert(index < size); return data[index]; }; + // update value at index + bool set(uint32_t index, T value) + { + if (index >= _size) + return false; + data[index] = value; + _p->write(t.start + sizeof(T)*index, sizeof(T), (uint8_t *)&data[index]); + return true; + } + // remove value + bool remove(uint32_t index) + { + if (index >= _size || !isMetadataInSync()) + return false; + // Remove the item + _size--; + t.end -= sizeof(T); + int tail = (_size-index)*sizeof(T); + memmove(&data[index], &data[index+1], tail); + // Write back the data + if (tail) + _p->write(t.start + sizeof(T)*index, tail, (uint8_t *)&data[index]); + _p->writeSTLVector(_address,t); + return true; + } // get vector size inline uint32_t size () { diff --git a/library/include/dfhack/modules/Creatures.h b/library/include/dfhack/modules/Creatures.h index f175aad3a..99f6fd9c3 100644 --- a/library/include/dfhack/modules/Creatures.h +++ b/library/include/dfhack/modules/Creatures.h @@ -347,6 +347,11 @@ namespace DFHack void CopyNameTo(t_creature &creature, uint32_t address); + protected: + friend class Items; + bool RemoveOwnedItemIdx(const uint32_t index, int32_t id); + bool RemoveOwnedItemPtr(const uint32_t index, int32_t id); + private: struct Private; Private *d; diff --git a/library/include/dfhack/modules/Items.h b/library/include/dfhack/modules/Items.h index 966bd1b99..1ea509c5c 100644 --- a/library/include/dfhack/modules/Items.h +++ b/library/include/dfhack/modules/Items.h @@ -14,6 +14,7 @@ namespace DFHack class Context; class DFContextShared; +class Creatures; //From http://dwarffortresswiki.net/index.php/User:Rick/Memory_research //They all seem to be valid on 40d as well @@ -129,10 +130,20 @@ public: bool readItem(uint32_t itemptr, dfh_item & item); /// write item base (position and flags only = t_item part of dfh_item) bool writeItem(const dfh_item & item); - /// who owns this item we already read? - int32_t getItemOwnerID(const dfh_item & item); /// dump offsets used by accessors to a string std::string dumpAccessors(const dfh_item & item); + + /// who owns this item we already read? + int32_t getItemOwnerID(const dfh_item & item); + /// which item is it contained in? + int32_t getItemContainerID(const dfh_item & item); + /// which items does it contain? + bool getContainedItems(const dfh_item & item, std::vector &items); + + /// wipe out the owner records + bool removeItemOwner(dfh_item &item, Creatures *creatures); + + bool readItemRefs(const dfh_item &item, const ClassNameCheck &classname, std::vector &values); private: class Private; Private* d; diff --git a/library/modules/Creatures.cpp b/library/modules/Creatures.cpp index d2eb757e3..fc6615616 100644 --- a/library/modules/Creatures.cpp +++ b/library/modules/Creatures.cpp @@ -722,6 +722,30 @@ bool Creatures::ReadOwnedItemsPtr(const uint32_t temp, std::vector & it return true; } +bool Creatures::RemoveOwnedItemIdx(const uint32_t index, int32_t id) +{ + if(!d->Started || !d->Ft_owned_items) return false; + uint32_t temp = d->p_cre->at (index); + return this->RemoveOwnedItemPtr(temp, id); +} + +bool Creatures::RemoveOwnedItemPtr(const uint32_t temp, int32_t id) +{ + if(!d->Started || !d->Ft_owned_items) return false; + Process * p = d->owner; + + DfVector citem(p, temp + d->creatures.owned_items_offset); + + for (unsigned i = 0; i < citem.size(); i++) { + if (citem[i] != id) + continue; + if (!citem.remove(i--)) + return false; + } + + return true; +} + void Creatures::CopyNameTo(t_creature &creature, uint32_t address) { Private::t_offsets &offs = d->creatures; diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 0dcb62677..e9affec1a 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -38,6 +38,7 @@ using namespace std; #include "dfhack/DFVector.h" #include "dfhack/modules/Materials.h" #include "dfhack/modules/Items.h" +#include "dfhack/modules/Creatures.h" #include "ModuleFactory.h" using namespace DFHack; @@ -426,10 +427,11 @@ class Items::Private std::map descVTable; std::map idLookupTable; uint32_t refVectorOffset; - uint32_t refIDOffset; uint32_t idFieldOffset; uint32_t itemVectorAddress; ClassNameCheck isOwnerRefClass; + ClassNameCheck isContainerRefClass; + ClassNameCheck isContainsRefClass; }; Items::Items(DFContextShared * d_) @@ -437,12 +439,15 @@ Items::Items(DFContextShared * d_) d = new Private; d->d = d_; d->owner = d_->p; - d->refVectorOffset = d->refIDOffset = 0; + 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 = d_->offset_descriptor->getGroup("Items"); d->itemVectorAddress = itemGroup->getAddress("items_vector"); d->idFieldOffset = itemGroup->getOffset("id"); + d->refVectorOffset = itemGroup->getOffset("item_ref_vector"); } bool Items::Start() @@ -538,27 +543,65 @@ void Items::setItemFlags(uint32_t itemptr, t_itemflags new_flags) */ int32_t Items::getItemOwnerID(const DFHack::dfh_item &item) { - if (!d->refVectorOffset) + std::vector vals; + if (readItemRefs(item, d->isOwnerRefClass, vals)) + return vals[0]; + else + return -1; +} + +int32_t Items::getItemContainerID(const DFHack::dfh_item &item) +{ + std::vector vals; + if (readItemRefs(item, d->isContainerRefClass, vals)) + return vals[0]; + else + return -1; +} + +bool Items::getContainedItems(const DFHack::dfh_item &item, std::vector &items) +{ + return readItemRefs(item, d->isContainsRefClass, items); +} + +bool Items::readItemRefs(const dfh_item &item, const ClassNameCheck &classname, std::vector &values) +{ + DFHack::DfVector p_refs(d->owner, item.origin + d->refVectorOffset); + + values.clear(); + + for (uint32_t i=0; iowner->getDescriptor()->getGroup("Items"); - d->refVectorOffset = Items->getOffset("item_ref_vector"); - d->refIDOffset = Items->getOffset("owner_ref_id_field"); + uint32_t vtbl = d->owner->readDWord(p_refs[i]); + if (classname(d->owner, vtbl)) + values.push_back(int32_t(d->owner->readDWord(p_refs[i] + 4))); } + return !values.empty(); +} + +bool Items::removeItemOwner(dfh_item &item, Creatures *creatures) +{ DFHack::DfVector p_refs(d->owner, item.origin + d->refVectorOffset); - uint32_t size = p_refs.size(); - for (uint32_t i=0;iowner->readDWord(curRef); - + uint32_t vtbl = d->owner->readDWord(p_refs[i]); if (!d->isOwnerRefClass(d->owner, vtbl)) continue; - return d->owner->readDWord(curRef + d->refIDOffset); + int32_t oid = d->owner->readDWord(p_refs[i]+4); + int32_t ix = creatures->FindIndexById(oid); + + if (ix < 0 || !creatures->RemoveOwnedItemIdx(ix, item.id)) + return false; + + if (!p_refs.remove(i--)) + return false; } - return -1; + item.base.flags.owned = 0; + + return true; } std::string Items::getItemClass(const dfh_item & item) diff --git a/library/private/SHMProcess.h b/library/private/SHMProcess.h index 560a259e5..7ff463189 100644 --- a/library/private/SHMProcess.h +++ b/library/private/SHMProcess.h @@ -73,6 +73,7 @@ namespace DFHack size_t writeSTLString(const uint32_t address, const std::string writeString); void readSTLVector(const uint32_t address, t_vecTriplet & triplet); + void writeSTLVector(const uint32_t address, t_vecTriplet & triplet); // get class name of an object with rtti/type info std::string doReadClassName(uint32_t vptr); diff --git a/tools/examples/dfitemdump.cpp b/tools/examples/dfitemdump.cpp index 5ebc7eb49..acad786c5 100644 --- a/tools/examples/dfitemdump.cpp +++ b/tools/examples/dfitemdump.cpp @@ -14,8 +14,28 @@ using namespace std; #include #include -int main () +int main (int argc, char *argv[]) { + bool print_refs = false; + bool print_hex = false; + bool print_acc = false; + + for(int i = 1; i < argc; i++) + { + char *arg = argv[i]; + if (arg[0] != '-') + continue; + + for (; *arg; arg++) { + switch (arg[0]) { + case 'r': print_refs = true; break; + case 'x': print_hex = true; break; + case 'a': print_acc = true; break; + } + } + } + + DFHack::Process * p; unsigned int i,j; DFHack::ContextManager DFMgr("Memory.xml"); @@ -45,54 +65,54 @@ int main () p = DF->getProcess(); int32_t x,y,z; Gui->getCursorCoords(x,y,z); + + std::vector p_items; + Items->readItemVector(p_items); + uint32_t size = p_items.size(); + // FIXME: tools should never be exposed to DFHack internals! DFHack::OffsetGroup* itemGroup = mem->getGroup("Items"); - DFHack::DfVector p_items (p, itemGroup->getAddress("items_vector")); - uint32_t size = p_items.size(); + uint32_t ref_vector = itemGroup->getOffset("item_ref_vector"); for(int i = 0; i < size; i++) { DFHack::dfh_item itm; memset(&itm, 0, sizeof(DFHack::dfh_item)); Items->readItem(p_items[i],itm); - if(x != -30000) - { - if(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 - ) - { - printf( - "%5d: %08x %6d %08x (%d,%d,%d) #%08x [%d] %s - %s. Stack: %d\n", - i, itm.origin, itm.id, itm.base.flags.whole, - itm.base.x, itm.base.y, itm.base.z, - itm.base.vtable, - itm.wear_level, - Items->getItemClass(itm.matdesc.itemType).c_str(), - Items->getItemDescription(itm, Materials).c_str(), - itm.quantity - ); - hexdump(DF,p_items[i],0x300); - cout << Items->dumpAccessors(itm) << endl; + + if (x != -30000 + && !(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; + + printf( + "%5d: %08x %6d %08x (%d,%d,%d) #%08x [%d] *%d %s - %s\n", + i, itm.origin, itm.id, itm.base.flags.whole, + itm.base.x, itm.base.y, itm.base.z, + itm.base.vtable, + itm.wear_level, + itm.quantity, + Items->getItemClass(itm.matdesc.itemType).c_str(), + Items->getItemDescription(itm, Materials).c_str() + ); + + if (print_hex) + hexdump(DF,p_items[i],0x300); + + if (print_acc) + cout << Items->dumpAccessors(itm) << endl; + + if (print_refs) { + DFHack::DfVector p_refs(p, itm.origin + ref_vector); + for (int j = 0; j < p_refs.size(); j++) { + uint32_t vptr = p->readDWord(p_refs[j]); + uint32_t val = p->readDWord(p_refs[j]+4); + printf("\t-> %d \t%s\n", int(val), p->readClassName(vptr).c_str()); } } - else - { - printf( - "%5d: %08x %6d %08x (%d,%d,%d) #%08x [%d] %s - %s. Stack: %d\n", - i, itm.origin, itm.id, itm.base.flags.whole, - itm.base.x, itm.base.y, itm.base.z, - itm.base.vtable, - itm.wear_level, - Items->getItemClass(itm.matdesc.itemType).c_str(), - Items->getItemDescription(itm, Materials).c_str(), - itm.quantity - ); - } } /* printf("type\tvtable\tname\tquality\tdecorate\n"); diff --git a/tools/supported/cleanowned.cpp b/tools/supported/cleanowned.cpp index 5740d7ed8..2ef6b1007 100644 --- a/tools/supported/cleanowned.cpp +++ b/tools/supported/cleanowned.cpp @@ -92,14 +92,21 @@ int main (int argc, char *argv[]) DFHack::dfh_item itm; Items->readItem(curItem, itm); - if (!itm.base.flags.owned) - continue; - - std::string name = Items->getItemClass(itm.matdesc.itemType); - bool confiscate = false; bool dump = false; + if (!itm.base.flags.owned) { + int32_t owner = Items->getItemOwnerID(itm); + if (owner >= 0) { + printf("Fixing a misflagged item: "); + confiscate = true; + } + else + continue; + } + + std::string name = Items->getItemClass(itm.matdesc.itemType); + if (itm.base.flags.rotten) { printf("Confiscating a rotten item: \t"); @@ -131,12 +138,14 @@ int main (int argc, char *argv[]) if (confiscate) { - itm.base.flags.owned = 0; - if (dump) - itm.base.flags.dump = 1; + if (!dry_run) { + if (!Items->removeItemOwner(itm, Creatures)) + printf("(unsuccessfully) "); + if (dump) + itm.base.flags.dump = 1; - if (!dry_run) Items->writeItem(itm); + } printf( "%s (wear %d)", diff --git a/tools/supported/grow.cpp b/tools/supported/grow.cpp index 12f02acf3..afa15a14d 100644 --- a/tools/supported/grow.cpp +++ b/tools/supported/grow.cpp @@ -96,6 +96,5 @@ int main(int argc, char *argv[]) std::cout << " Press any key to finish."; std::cin.ignore(); } - std::cout << std::endl; return 0; } diff --git a/tools/supported/immolate.cpp b/tools/supported/immolate.cpp index e57a8f764..87e0d990a 100644 --- a/tools/supported/immolate.cpp +++ b/tools/supported/immolate.cpp @@ -105,8 +105,6 @@ int main(int argc, char *argv[]) veg->Read(i,p); if(all_shrubs && p.sdata.is_shrub || all_trees && !p.sdata.is_shrub) { - //p.sdata.temperature_1 = 0; - //p.sdata.temperature_2 = 0; if (immolate) p.sdata.is_burning = true; p.sdata.hitpoints = 0;