diff --git a/Memory.xml b/Memory.xml index d40c4a737..d92156255 100644 --- a/Memory.xml +++ b/Memory.xml @@ -870,7 +870,7 @@ - + @@ -995,7 +995,9 @@ - (in the vtable) + + +
@@ -2948,6 +2950,17 @@
+ +
+ + + + + + + + + diff --git a/library/include/dfhack/DFTypes.h b/library/include/dfhack/DFTypes.h index 3983122f8..f53e5afa3 100644 --- a/library/include/dfhack/DFTypes.h +++ b/library/include/dfhack/DFTypes.h @@ -124,46 +124,46 @@ struct t_item_df40d //They all seem to be valid on 40d as well struct naked_itemflags { - unsigned int on_ground : 1; // Item on ground - unsigned int in_job : 1; // item currently being used in a job - unsigned int in_inventory : 1; // Item in a creatures inventory - unsigned int u_ngrd1 : 1; // only occurs when not on ground, unknown function + 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 u_ngrd1 : 1; // 0000 0004 unknown, unseen + 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 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 dead_dwarf : 1; // 0000 0080 Dwarf's dead body or body part + + unsigned int rotten : 1; // 0000 0100 Rotten food + unsigned int spider_web : 1; // 0000 0200 Thread in spider web + unsigned int construction : 1; // 0000 0400 Material used in construction + unsigned int u_ngrd5 : 1; // 0000 0800 unknown, unseen + + unsigned int unk3 : 1; // 0000 1000 unknown, unseen + unsigned int u_ngrd6 : 1; // 0000 2000 unknown, unseen + unsigned int foreign : 1; // 0000 4000 Item is imported + unsigned int u_ngrd7 : 1; // 0000 8000 unknown, unseen + + unsigned int owned : 1; // 0001 0000 Item is owned by a dwarf + unsigned int unk4 : 1; // 0002 0000 unknown, unseen + unsigned int artifact1 : 1; // 0004 0000 Artifact ? + unsigned int forbid : 1; // 0008 0000 Forbidden item - unsigned int in_workshop : 1; // Item is in a workshops inventory - unsigned int u_ngrd2 : 1; // only occurs when not on ground, unknown function - unsigned int u_ngrd3 : 1; // only occurs when not on ground, unknown function - unsigned int rotten : 1; // Item is rotten - - unsigned int unk1 : 1; // unknown function - unsigned int u_ngrd4 : 1; // only occurs when not on ground, unknown function - unsigned int unk2 : 1; // unknown function - unsigned int u_ngrd5 : 1; // only occurs when not on ground, unknown function - - unsigned int unk3 : 1; // unknown function - unsigned int u_ngrd6 : 1; // only occurs when not on ground, unknown function - unsigned int narrow : 1; // Item is narrow - unsigned int u_ngrd7 : 1; // only occurs when not on ground, unknown function - - unsigned int worn : 1; // item shows wear - unsigned int unk4 : 1; // unknown function - unsigned int u_ngrd8 : 1; // only occurs when not on ground, unknown function - unsigned int forbid : 1; // designate forbid item - - unsigned int unk5 : 1; // unknown function - unsigned int dump : 1; // designate dump item - unsigned int on_fire: 1; //indicates if item is on fire, Will Set Item On Fire if Set! - unsigned int melt : 1; // designate melt item, if item cannot be melted, does nothing it seems + unsigned int unk5 : 1; // 0010 0000 unknown, unseen + 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 melt : 1; // 0080 0000 Designated for melting, if applicable // 0100 0000 - 8000 0000 - unsigned int hidden : 1; // designate hide item - unsigned int u_ngrd9 : 1; // only occurs when not on ground, unknown function - unsigned int unk6 : 1; // unknown function - unsigned int unk7 : 1; // unknown function + unsigned int hidden : 1; // 0100 0000 Hidden item + unsigned int in_chest : 1; // 0200 0000 Stored in chest/part of well? + unsigned int unk6 : 1; // 0400 0000 unknown, unseen + unsigned int artifact2 : 1; // 0800 0000 Artifact ? - unsigned int unk8 : 1; // unknown function - unsigned int unk9 : 1; // unknown function - unsigned int unk10 : 1; // unknown function - unsigned int unk11 : 1; // unknown function + unsigned int unk8 : 1; // 1000 0000 unknown, unseen + unsigned int unk9 : 1; // 2000 0000 unknown, set when viewing details + unsigned int unk10 : 1; // 4000 0000 unknown, unseen + unsigned int unk11 : 1; // 8000 0000 unknown, unseen }; union t_itemflags diff --git a/library/include/dfhack/modules/Creatures.h b/library/include/dfhack/modules/Creatures.h index 1585692ba..6a1218dd8 100644 --- a/library/include/dfhack/modules/Creatures.h +++ b/library/include/dfhack/modules/Creatures.h @@ -321,6 +321,8 @@ namespace DFHack bool ReadInventoryIdx(const uint32_t index, std::vector & item); bool ReadInventoryPtr(const uint32_t index, std::vector & item); + int32_t FindIndexById(int32_t id); + /* Getters */ uint32_t GetDwarfRaceIndex ( void ); int32_t GetDwarfCivId ( void ); diff --git a/library/include/dfhack/modules/Items.h b/library/include/dfhack/modules/Items.h index 11e59f77f..4b6021eed 100644 --- a/library/include/dfhack/modules/Items.h +++ b/library/include/dfhack/modules/Items.h @@ -7,17 +7,29 @@ */ #include "dfhack/DFExport.h" #include "dfhack/DFModule.h" +#include "dfhack/DFTypes.h" + namespace DFHack { class Context; class DFContextShared; +struct t_item_header +{ + int16_t x; + int16_t y; + int16_t z; + t_itemflags flags; +}; + struct t_item { + t_item_header header; t_material matdesc; int32_t quantity; int32_t quality; + int16_t wear_level; }; struct t_improvement @@ -36,6 +48,8 @@ public: std::string getItemDescription(uint32_t itemptr, Materials * Materials); std::string getItemClass(int32_t index); bool getItemData(uint32_t itemptr, t_item & item); + int32_t getItemOwnerID(uint32_t itemptr); + void setItemFlags(uint32_t itemptr, t_itemflags new_flags); private: class Private; Private* d; diff --git a/library/modules/Creatures.cpp b/library/modules/Creatures.cpp index 2e589d967..9e91a1e76 100644 --- a/library/modules/Creatures.cpp +++ b/library/modules/Creatures.cpp @@ -106,6 +106,8 @@ struct Creatures::Private uint32_t creature_module; uint32_t dwarf_race_index_addr; uint32_t dwarf_civ_id_addr; + bool IdMapReady; + std::map IdMap; DfVector *p_cre; DFContextShared *d; Process *owner; @@ -123,6 +125,7 @@ Creatures::Creatures(DFContextShared* _d) d->owner = _d->p; d->Inited = false; d->Started = false; + d->IdMapReady = false; d->p_cre = NULL; d->d->InitReadNames(); // throws on error VersionInfo * minfo = d->d->offset_descriptor; @@ -228,6 +231,7 @@ bool Creatures::Start( uint32_t &numcreatures ) d->p_cre = new DfVector (d->owner, d->creatures.vector); d->Started = true; numcreatures = d->p_cre->size(); + d->IdMapReady = false; return true; } return false; @@ -407,6 +411,35 @@ int32_t Creatures::ReadCreatureInBox (int32_t index, t_creature & furball, return -1; } +int32_t Creatures::FindIndexById(int32_t creature_id) +{ + if (!d->Started || !d->Ft_basic) + return -1; + + if (!d->IdMapReady) + { + d->IdMap.clear(); + + Process * p = d->owner; + Private::t_offsets &offs = d->creatures; + + uint32_t size = d->p_cre->size(); + for (uint32_t index = 0; index < size; index++) + { + uint32_t temp = d->p_cre->at(index); + int32_t id = p->readDWord (temp + offs.id_offset); + d->IdMap[id] = index; + } + } + + std::map::iterator it; + it = d->IdMap.find(creature_id); + if(it == d->IdMap.end()) + return -1; + else + return it->second; +} + bool Creatures::WriteLabors(const uint32_t index, uint8_t labors[NUM_CREATURE_LABORS]) { if(!d->Started || !d->Ft_advanced) return false; diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index e398f8e16..d29753b7d 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -51,13 +51,19 @@ enum accessor_type {ACCESSOR_CONSTANT, ACCESSOR_INDIRECT, ACCESSOR_DOUBLE_INDIRE /* this is used to store data about the way accessors work */ class DFHACK_EXPORT Accessor { +public: + enum DataWidth { + Data32 = 0, + DataSigned16, + DataUnsigned16 + }; private: accessor_type type; int32_t constant; - uint32_t offset1; - uint32_t offset2; + int32_t offset1; + int32_t offset2; Process * p; - uint32_t dataWidth; + DataWidth dataWidth; public: Accessor(uint32_t function, Process * p); Accessor(accessor_type type, int32_t constant, uint32_t offset1, uint32_t offset2, uint32_t dataWidth, Process * p); @@ -84,6 +90,7 @@ private: Accessor * ASubIndex; Accessor * AIndex; Accessor * AQuality; + Accessor * AWear; Process * p; bool hasDecoration; public: @@ -95,89 +102,138 @@ public: std::vector improvement; }; +inline bool do_match(uint32_t &ptr, uint64_t val, int size, uint64_t mask, uint64_t check) +{ + if ((val & mask) == check) { + ptr += size; + return true; + } + return false; +} + +static bool match_MEM_ACCESS(uint32_t &ptr, uint64_t v, int isize, int in_reg, int &out_reg, int &offset) +{ + // ESP & EBP are hairy + if (in_reg == 4 || in_reg == 5) + return false; + + if ((v & 7) != in_reg) + return false; + + out_reg = (v>>3) & 7; + + switch ((v>>6)&3) { + case 0: // MOV REG2, [REG] + offset = 0; + ptr += isize+1; + return true; + case 1: // MOV REG2, [REG+offset8] + offset = (signed char)(v >> 8); + ptr += isize+2; + return true; + case 2: // MOV REG2, [REG+offset32] + offset = (signed int)(v >> 8); + ptr += isize+5; + return true; + default: + return false; + } +} + +static bool match_MOV_MEM(uint32_t &ptr, uint64_t v, int in_reg, int &out_reg, int &offset, Accessor::DataWidth &size) +{ + int prefix = 0; + size = Accessor::Data32; + if ((v & 0xFF) == 0x8B) { // MOV + v >>= 8; + prefix = 1; + } + else if ((v & 0xFFFF) == 0x8B66) { // MOV 16-bit + v >>= 16; + prefix = 2; + size = Accessor::DataUnsigned16; + } + else if ((v & 0xFFFF) == 0xBF0F) { // MOVSX + v >>= 16; + prefix = 2; + size = Accessor::DataSigned16; + } + else if ((v & 0xFFFF) == 0xB70F) { // MOVZ + v >>= 16; + prefix = 2; + size = Accessor::DataUnsigned16; + } + else + return false; + + return match_MEM_ACCESS(ptr, v, prefix, in_reg, out_reg, offset); +} // FIXME: this is crazy Accessor::Accessor(uint32_t function, Process *p) { this->p = p; - this->constant = 0; - this->offset1 = 0; - this->offset2 = 0; this->type = ACCESSOR_CONSTANT; - this->dataWidth = 2; - uint64_t funcText = p->readQuad(function); - if( funcText == 0xCCCCCCCCCCC3C033LL ) + uint32_t ptr = function; + uint64_t v = p->readQuad(ptr); + int data_reg = -1; + + if (do_match(ptr, v, 2, 0xFFFF, 0xC033) || + do_match(ptr, v, 2, 0xFFFF, 0xC031)) // XOR EAX, EAX { - return; + data_reg = 0; + this->constant = 0; } - if( funcText == 0xCCCCCCCCC3FFC883LL ) + else if (do_match(ptr, v, 3, 0xFFFFFF, 0xFFC883)) // OR EAX, -1 { - /* or eax,-1; ret; */ + data_reg = 0; this->constant = -1; - return; - } - if( (funcText&0xFFFFFFFFFF0000FFLL) == 0xCCCCC300000000B8LL ) - { - /* mov eax, xx; ret; */ - this->constant = (funcText>>8) & 0xffff; - return; } - if( (funcText&0xFFFFFF0000FFFFFFLL) == 0xC300000000818B66LL ) + else if (do_match(ptr, v, 5, 0xFF, 0xB8)) // MOV EAX,imm { - /* mov ax, [ecx+xx]; ret; */ - this->type = ACCESSOR_INDIRECT; - this->offset1 = (funcText>>24) & 0xffff; - return; - } - if( (funcText&0x000000FF00FFFFFFLL) == 0x000000C300418B66LL ) - { - /* mov ax, [ecx+xx]; ret; (shorter instruction)*/ - this->type = ACCESSOR_INDIRECT; - this->offset1 = (funcText>>24) & 0xff; - return; - } - if( (funcText&0x00000000FF00FFFFLL) == 0x00000000C300418BLL ) - { - /* mov eax, [ecx+xx]; ret; */ - this->type = ACCESSOR_INDIRECT; - this->offset1 = (funcText>>16) & 0xff; - this->dataWidth = 4; - return; + data_reg = 0; + this->constant = (v>>8) & 0xFFFFFFFF; } - if( (funcText&0xFFFFFFFF0000FFFFLL) == 0x8B6600000000818BLL ) + else { - uint64_t funcText2 = p->readQuad(function+8); - if( (funcText2&0xFFFFFFFFFFFF00FFLL) == 0xCCCCCCCCCCC30040LL ) + DataWidth xsize; + int ptr_reg = 1, tmp; // ECX + + // MOV REG,[ESP+4] + if (do_match(ptr, v, 4, 0xFFFFC7FFU, 0x0424448B)) { - this->type = ACCESSOR_DOUBLE_INDIRECT; - this->offset1 = (funcText>>16) & 0xffff; - this->offset2 = (funcText2>>8) & 0xff; - return; + ptr_reg = (v>>11)&7; + v = p->readQuad(ptr); + } + + if (match_MOV_MEM(ptr, v, ptr_reg, tmp, this->offset1, xsize)) { + data_reg = tmp; + this->type = ACCESSOR_INDIRECT; + this->dataWidth = xsize; + + if (xsize == Data32) + { + v = p->readQuad(ptr); + + if (match_MOV_MEM(ptr, v, data_reg, tmp, this->offset2, xsize)) { + data_reg = tmp; + this->type = ACCESSOR_DOUBLE_INDIRECT; + this->dataWidth = xsize; + } + } } } - if( (funcText&0xFFFFFF0000FFFFFFLL) == 0xC30000000081BF0FLL ) - { - /* movsx eax, word ptr [ecx+xx]; ret */ - this->type = ACCESSOR_INDIRECT; - this->offset1 = (funcText>>24) & 0xffff; - return; - } - if( (funcText&0x000000FF00FFFFFFLL) == 0x000000C30041BF0FLL ) - { - /* movsx eax, word ptr [ecx+xx]; ret (shorter opcode)*/ - this->type = ACCESSOR_INDIRECT; - this->offset1 = (funcText>>24) & 0xff; + + v = p->readQuad(ptr); + + if (data_reg == 0 && do_match(ptr, v, 1, 0xFF, 0xC3)) // RET return; - } - if( (funcText&0xFFFFFFFF0000FFFFLL) == 0xCCC300000000818BLL ) + else { - /* mov eax, [ecx+xx]; ret; */ - this->type = ACCESSOR_INDIRECT; - this->offset1 = (funcText>>16) & 0xffff; - this->dataWidth = 4; - return; + this->type = ACCESSOR_CONSTANT; + this->constant = 0; + printf("bad accessor @0x%x\n", function); } - printf("bad accessor @0x%x\n", function); } bool Accessor::isConstant() @@ -190,29 +246,26 @@ bool Accessor::isConstant() int32_t Accessor::getValue(uint32_t objectPtr) { + int32_t offset = this->offset1; + switch(this->type) { case ACCESSOR_CONSTANT: return this->constant; break; - case ACCESSOR_INDIRECT: - switch(this->dataWidth) - { - case 2: - return (int16_t) p->readWord(objectPtr + this->offset1); - case 4: - return p->readDWord(objectPtr + this->offset1); - default: - return -1; - } - break; case ACCESSOR_DOUBLE_INDIRECT: + objectPtr = p->readDWord(objectPtr + this->offset1); + offset = this->offset2; + // fallthrough + case ACCESSOR_INDIRECT: switch(this->dataWidth) { - case 2: - return (int16_t) p->readWord(p->readDWord(objectPtr + this->offset1) + this->offset2); - case 4: - return p->readDWord(p->readDWord(objectPtr + this->offset1) + this->offset2); + case Data32: + return p->readDWord(objectPtr + offset); + case DataSigned16: + return (int16_t) p->readWord(objectPtr + offset); + case DataUnsigned16: + return (uint16_t) p->readWord(objectPtr + offset); default: return -1; } @@ -230,6 +283,7 @@ ItemDesc::ItemDesc(uint32_t VTable, Process *p) uint32_t funcOffsetC = Items->getOffset("item_subindex_accessor"); uint32_t funcOffsetD = Items->getOffset("item_index_accessor"); uint32_t funcOffsetQuality = Items->getOffset("item_quality_accessor"); + uint32_t funcOffsetWear = Items->getOffset("item_wear_accessor"); this->vtable = VTable; this->p = p; this->className = p->readClassName(VTable).substr(5); @@ -239,6 +293,7 @@ ItemDesc::ItemDesc(uint32_t VTable, Process *p) this->ASubIndex = new Accessor( p->readDWord( VTable + funcOffsetC ), p); this->AIndex = new Accessor( p->readDWord( VTable + funcOffsetD ), p); this->AQuality = new Accessor( p->readDWord( VTable + funcOffsetQuality ), p); + this->AWear = new Accessor( p->readDWord( VTable + funcOffsetWear ), p); this->hasDecoration = false; if(this->AMainType->isConstant()) this->mainType = this->AMainType->getValue(0); @@ -251,12 +306,16 @@ ItemDesc::ItemDesc(uint32_t VTable, Process *p) bool ItemDesc::getItem(uint32_t itemptr, DFHack::t_item &item) { + this->p->read(itemptr+4, sizeof(t_item_header), (uint8_t*)&item.header); item.matdesc.itemType = this->AMainType->getValue(itemptr); item.matdesc.subType = this->ASubType->getValue(itemptr); item.matdesc.subIndex = this->ASubIndex->getValue(itemptr); item.matdesc.index = this->AIndex->getValue(itemptr); item.quality = this->AQuality->getValue(itemptr); item.quantity = 1; /* TODO */ + // Note: this accessor returns a 32-bit value with the higher + // half sometimes containing garbage, so the cast is essential: + item.wear_level = (int16_t)this->AWear->getValue(itemptr); return true; } @@ -267,6 +326,9 @@ class Items::Private Process * owner; std::map descType; std::map descVTable; + uint32_t refVectorOffset; + uint32_t refIDOffset; + uint32_t ownerRefVTable; }; Items::Items(DFContextShared * d_) @@ -274,6 +336,7 @@ Items::Items(DFContextShared * d_) d = new Private; d->d = d_; d->owner = d_->p; + d->ownerRefVTable = d->refVectorOffset = d->refIDOffset = 0; } bool Items::Start() @@ -321,6 +384,45 @@ bool Items::getItemData(uint32_t itemptr, DFHack::t_item &item) return desc->getItem(itemptr, item); } +void Items::setItemFlags(uint32_t itemptr, t_itemflags new_flags) +{ + d->owner->writeDWord(itemptr + 0x0C, new_flags.whole); +} + +int32_t Items::getItemOwnerID(uint32_t itemptr) +{ + if (!d->refVectorOffset) + { + OffsetGroup * Items = d->owner->getDescriptor()->getGroup("Items"); + d->refVectorOffset = Items->getOffset("item_ref_vector"); + d->refIDOffset = Items->getOffset("owner_ref_id_field"); + } + + DFHack::DfVector p_refs(d->owner, itemptr + d->refVectorOffset); + uint32_t size = p_refs.size(); + + for (uint32_t i=0;iowner->readDWord(curRef); + + if (!d->ownerRefVTable) + { + std::string className = d->owner->readClassName(vtbl); + if (className == "general_ref_unit_itemownerst") + d->ownerRefVTable = vtbl; + else + continue; + } + else if (d->ownerRefVTable != vtbl) + continue; + + return d->owner->readDWord(curRef + d->refIDOffset); + } + + return -1; +} + std::string Items::getItemClass(int32_t index) { std::map::iterator it; diff --git a/tools/playground/CMakeLists.txt b/tools/playground/CMakeLists.txt index 2a2054ac4..5ff77de1c 100644 --- a/tools/playground/CMakeLists.txt +++ b/tools/playground/CMakeLists.txt @@ -80,6 +80,8 @@ DFHACK_TOOL(dfprinttiletypes printtiletypes.cpp) # Will have many options in the future. DFHACK_TOOL(dfhellhole hellhole.cpp) +DFHACK_TOOL(dfcleanowned cleanowned.cpp) + # this needs the C bindings IF(BUILD_DFHACK_C_BINDINGS) # The C bindings won't be real C bindings until this compiles. diff --git a/tools/playground/cleanowned.cpp b/tools/playground/cleanowned.cpp new file mode 100644 index 000000000..116754dc8 --- /dev/null +++ b/tools/playground/cleanowned.cpp @@ -0,0 +1,175 @@ +/* + * Confiscates and dumps garbage owned by dwarfs. + */ + +#include +#include +#include +#include +#include +#include +using namespace std; + +#include +#include +#include + +int main (int argc, char *argv[]) +{ + bool dump_scattered = false; + bool confiscate_all = false; + bool dry_run = false; + int wear_dump_level = 65536; + + for(int i = 1; i < argc; i++) + { + char *arg = argv[i]; + if (arg[0] != '-') + continue; + + for (; *arg; arg++) { + switch (arg[0]) { + case 'd': + dry_run = true; + break; + case 'l': + dump_scattered = true; + break; + case 'a': + confiscate_all = true; + break; + case 'x': + wear_dump_level = 1; + break; + case 'X': + wear_dump_level = 2; + break; + } + } + } + + DFHack::Process * p; + unsigned int i,j; + DFHack::ContextManager DFMgr("Memory.xml"); + DFHack::Context * DF; + try + { + DF = DFMgr.getSingleContext(); + DF->Attach(); + } + catch (exception& e) + { + cerr << e.what() << endl; +#ifndef LINUX_BUILD + cin.ignore(); +#endif + return 1; + } + + DFHack::VersionInfo * mem = DF->getMemoryInfo(); + DFHack::Materials *Materials = DF->getMaterials(); + DFHack::Items *Items = DF->getItems(); + DFHack::Creatures *Creatures = DF->getCreatures(); + DFHack::Translation *Tran = DF->getTranslation(); + + Materials->ReadAllMaterials(); + uint32_t num_creatures; + Creatures->Start(num_creatures); + Tran->Start(); + + p = DF->getProcess(); + DFHack::OffsetGroup* itemGroup = mem->getGroup("Items"); + unsigned vector_addr = itemGroup->getAddress("items_vector"); + DFHack::DfVector p_items (p, vector_addr); + uint32_t size = p_items.size(); + + printf("Found total %d items.\n", size); + + for (i=0;igetItemData(curItem, itm); + + if (!itm.header.flags.bits.owned) + continue; + + bool confiscate = false; + bool dump = false; + + if (itm.header.flags.bits.rotten) + { + printf("Confiscating a rotten item: \t"); + confiscate = true; + } + else if (itm.wear_level >= wear_dump_level) + { + printf("Confiscating and dumping a worn item: \t"); + confiscate = true; + dump = true; + } + else if (dump_scattered && itm.header.flags.bits.on_ground) + { + printf("Confiscating and dumping litter: \t"); + confiscate = true; + dump = true; + } + else if (confiscate_all) + { + printf("Confiscating: \t"); + confiscate = true; + } + + if (confiscate) + { + itm.header.flags.bits.owned = 0; + if (dump) + itm.header.flags.bits.dump = 1; + + if (!dry_run) + Items->setItemFlags(curItem, itm.header.flags); + + printf( + "%s (wear %d)", + Items->getItemDescription(curItem, Materials).c_str(), + itm.wear_level + ); + + int32_t owner = Items->getItemOwnerID(curItem); + int32_t owner_index = Creatures->FindIndexById(owner); + std::string info; + + if (owner_index >= 0) + { + DFHack::t_creature temp; + Creatures->ReadCreature(owner_index,temp); + temp.name.first_name[0] = toupper(temp.name.first_name[0]); + info = temp.name.first_name; + if (temp.name.nickname[0]) + info += std::string(" '") + temp.name.nickname + "'"; + info += " "; + info += Tran->TranslateName(temp.name,false); + printf(", owner %s", info.c_str()); + } + + printf("\n"); + +/* printf( + "%5d: %08x %08x (%d,%d,%d) #%08x [%d] %s - %s %s\n", + i, curItem, itm.header.flags.whole, + itm.header.x, itm.header.y, itm.header.z, + p->readDWord(curItem), + itm.wear_level, + Items->getItemClass(itm.matdesc.itemType).c_str(), + Items->getItemDescription(curItem, Materials).c_str(), + info.c_str() + );*/ + } + } + +#ifndef LINUX_BUILD + cout << "Done. Press any key to continue" << endl; + cin.ignore(); +#endif + return 0; +}