diff --git a/Memory.xml b/Memory.xml index 086f9e285..2c45336a5 100644 --- a/Memory.xml +++ b/Memory.xml @@ -1816,6 +1816,7 @@ + @@ -2008,6 +2009,7 @@ + diff --git a/library/include/dfhack/modules/Items.h b/library/include/dfhack/modules/Items.h index d0f1c3e79..a69f657e1 100644 --- a/library/include/dfhack/modules/Items.h +++ b/library/include/dfhack/modules/Items.h @@ -125,6 +125,8 @@ public: 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); private: class Private; Private* d; diff --git a/library/include/dfhack/modules/Materials.h b/library/include/dfhack/modules/Materials.h index 07e8c40f4..3407aeb06 100644 --- a/library/include/dfhack/modules/Materials.h +++ b/library/include/dfhack/modules/Materials.h @@ -174,8 +174,8 @@ namespace DFHack void ReadAllMaterials(void); - std::string getType(t_material & mat); - std::string getDescription(t_material & mat); + std::string getType(const t_material & mat); + std::string getDescription(const t_material & mat); private: class Private; Private* d; diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index d62aadec9..d235fa3ba 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -25,6 +25,7 @@ distribution. #include "Internal.h" #include +#include #include #include #include @@ -64,9 +65,11 @@ private: int32_t offset2; Process * p; DataWidth dataWidth; + uint32_t method; 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); + std::string dump(); int32_t getValue(uint32_t objectPtr); bool isConstant(); }; @@ -96,6 +99,7 @@ private: public: ItemDesc(uint32_t VTable, Process * p); bool readItem(uint32_t itemptr, dfh_item & item); + std::string dumpAccessors(); std::string className; uint32_t vtable; uint32_t mainType; @@ -178,22 +182,23 @@ Accessor::Accessor(uint32_t function, Process *p) this->constant = 0; return; } - uint32_t ptr = function; + method = function; + uint32_t temp = function; int data_reg = -1; - uint64_t v = p->readQuad(ptr); + uint64_t v = p->readQuad(temp); - if (do_match(ptr, v, 2, 0xFFFF, 0xC033) || - do_match(ptr, v, 2, 0xFFFF, 0xC031)) // XOR EAX, EAX + if (do_match(temp, v, 2, 0xFFFF, 0xC033) || + do_match(temp, v, 2, 0xFFFF, 0xC031)) // XOR EAX, EAX { data_reg = 0; this->constant = 0; } - else if (do_match(ptr, v, 3, 0xFFFFFF, 0xFFC883)) // OR EAX, -1 + else if (do_match(temp, v, 3, 0xFFFFFF, 0xFFC883)) // OR EAX, -1 { data_reg = 0; this->constant = -1; } - else if (do_match(ptr, v, 5, 0xFF, 0xB8)) // MOV EAX,imm + else if (do_match(temp, v, 5, 0xFF, 0xB8)) // MOV EAX,imm { data_reg = 0; this->constant = (v>>8) & 0xFFFFFFFF; @@ -204,22 +209,22 @@ Accessor::Accessor(uint32_t function, Process *p) int ptr_reg = 1, tmp; // ECX // MOV REG,[ESP+4] - if (do_match(ptr, v, 4, 0xFFFFC7FFU, 0x0424448B)) + if (do_match(temp, v, 4, 0xFFFFC7FFU, 0x0424448B)) { ptr_reg = (v>>11)&7; - v = p->readQuad(ptr); + v = p->readQuad(temp); } - if (match_MOV_MEM(ptr, v, ptr_reg, tmp, this->offset1, xsize)) { + if (match_MOV_MEM(temp, v, ptr_reg, tmp, this->offset1, xsize)) { data_reg = tmp; this->type = ACCESSOR_INDIRECT; this->dataWidth = xsize; if (xsize == Data32) { - v = p->readQuad(ptr); + v = p->readQuad(temp); - if (match_MOV_MEM(ptr, v, data_reg, tmp, this->offset2, xsize)) { + if (match_MOV_MEM(temp, v, data_reg, tmp, this->offset2, xsize)) { data_reg = tmp; this->type = ACCESSOR_DOUBLE_INDIRECT; this->dataWidth = xsize; @@ -228,9 +233,9 @@ Accessor::Accessor(uint32_t function, Process *p) } } - v = p->readQuad(ptr); + v = p->readQuad(temp); - if (data_reg == 0 && do_match(ptr, v, 1, 0xFF, 0xC3)) // RET + if (data_reg == 0 && do_match(temp, v, 1, 0xFF, 0xC3)) // RET return; else { @@ -248,6 +253,55 @@ bool Accessor::isConstant() return false; } +string Accessor::dump() +{ + stringstream sstr; + sstr << hex << "method @0x" << method << dec << " "; + switch(type) + { + case ACCESSOR_CONSTANT: + sstr << "Constant: " << dec << constant; + break; + case ACCESSOR_INDIRECT: + switch(dataWidth) + { + case Data32: + sstr << "int32_t "; + break; + case DataSigned16: + sstr << "int16_t "; + break; + case DataUnsigned16: + sstr << "uint16_t "; + break; + default: + sstr << "unknown "; + break; + } + sstr << hex << "[obj + 0x" << offset1 << " ]"; + break; + case ACCESSOR_DOUBLE_INDIRECT: + switch(dataWidth) + { + case Data32: + sstr << "int32_t "; + break; + case DataSigned16: + sstr << "int16_t "; + break; + case DataUnsigned16: + sstr << "uint16_t "; + break; + default: + sstr << "unknown "; + break; + } + sstr << hex << "[ [obj + 0x" << offset1 << " ] + 0x" << offset2 << " ]"; + break; + } + return sstr.str(); +} + int32_t Accessor::getValue(uint32_t objectPtr) { int32_t offset = this->offset1; @@ -283,8 +337,10 @@ int32_t Accessor::getValue(uint32_t objectPtr) Accessor * buildAccessor (OffsetGroup * I, Process * p, const char * name, uint32_t vtable) { int32_t offset; - if(I->getSafeOffset("item_type_accessor",offset)) + if(I->getSafeOffset(name,offset)) + { return new Accessor( p->readDWord( vtable + offset ), p); + } else { fprintf(stderr,"Missing offset for item accessor \"%s\"\n", name); @@ -324,6 +380,19 @@ ItemDesc::ItemDesc(uint32_t VTable, Process *p) } } +string ItemDesc::dumpAccessors() +{ + std::stringstream outss; + outss << "MainType :" << AMainType->dump() << endl; + outss << "ASubType :" << ASubType->dump() << endl; + outss << "ASubIndex :" << ASubIndex->dump() << endl; + outss << "AIndex :" << AIndex->dump() << endl; + outss << "AQuality :" << AQuality->dump() << endl; + outss << "AWear :" << AWear->dump() << endl; + return outss.str(); +} + + bool ItemDesc::readItem(uint32_t itemptr, DFHack::dfh_item &item) { p->read(itemptr, sizeof(t_item), (uint8_t*)&item.base); @@ -485,26 +554,38 @@ std::string Items::getItemClass(int32_t index) std::string Items::getItemDescription(const dfh_item & item, Materials * Materials) { - /* - DFHack::t_item item; - std::string out; - - if(!this->getItemData(itemptr, item)) - return "??"; + std::stringstream outss; switch(item.quality) { - case 0: break; - case 1: out.append("Well crafted "); break; - case 2: out.append("Finely crafted "); break; - case 3: out.append("Superior quality "); break; - case 4: out.append("Exceptionnal "); break; - case 5: out.append("Masterful "); break; - default: out.append("Crazy quality "); break; + case 0: + outss << "Ordinary "; + break; + case 1: + outss << "Well crafted "; + break; + case 2: + outss << "Finely crafted "; + break; + case 3: + outss << "Superior quality "; + break; + case 4: + outss << "Exceptionnal "; + break; + case 5: + outss << "Masterful "; + break; + default: outss << "Crazy quality " << item.quality << " "; break; } - out.append(Materials->getDescription(item.matdesc)); - out.append(" "); - out.append(this->getItemClass(item.matdesc.itemType)); - */ - //return out; - return getItemClass(item.matdesc.itemType); + outss << Materials->getDescription(item.matdesc) << " " << getItemClass(item.matdesc.itemType); + return outss.str(); } + +/// dump offsets used by accessors of a valid item to a string +std::string Items::dumpAccessors(const dfh_item & item) +{ + uint32_t vtable = item.base.vtable; + std::map< uint32_t, ItemDesc* >::const_iterator it = d->descVTable.find(vtable); + ItemDesc * desc = it->second; + return desc->dumpAccessors(); +} \ No newline at end of file diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 2a7ecf917..6f7c7727e 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -479,7 +479,8 @@ void Materials::ReadAllMaterials(void) this->ReadOthers(); } -std::string Materials::getDescription(t_material & mat) +/// miserable pile of magic. The material system is insane. +std::string Materials::getDescription(const t_material & mat) { std::string out; int32_t typeC; @@ -542,7 +543,7 @@ std::string Materials::getDescription(t_material & mat) } //type of material only so we know which vector to retrieve -std::string Materials::getType(t_material & mat) +std::string Materials::getType(const t_material & mat) { if((mat.subIndex<419) || (mat.subIndex>618)) { diff --git a/tools/examples/creaturedump.cpp b/tools/examples/creaturedump.cpp index c8e660ed7..55505b152 100644 --- a/tools/examples/creaturedump.cpp +++ b/tools/examples/creaturedump.cpp @@ -197,12 +197,12 @@ void printCreature(DFHack::Context * DF, const DFHack::t_creature & creature) { cout << ", custom profession: " << creature.custom_profession; } - /* + if(creature.current_job.active) { cout << ", current job: " << mem->getJob(creature.current_job.jobId); } - */ + cout << endl; dayoflife = creature.birth_year*12*28 + creature.birth_time/1200; cout << "Born on the year " << creature.birth_year << ", month " << (creature.birth_time/1200/28) << ", day " << ((creature.birth_time/1200) % 28 + 1) << ", " << dayoflife << " days lived." << endl; @@ -212,12 +212,20 @@ void printCreature(DFHack::Context * DF, const DFHack::t_creature & creature) cout << Materials->raceEx[creature.race].castes[creature.caste].ColorModifier[i].part << " "; uint32_t color = Materials->raceEx[creature.race].castes[creature.caste].ColorModifier[i].colorlist[creature.color[i]]; if(colorcolor.size()) + { cout << Materials->color[color].name << "[" - << (unsigned int) (Materials->color[color].r*255) << ":" - << (unsigned int) (Materials->color[color].v*255) << ":" - << (unsigned int) (Materials->color[color].b*255) << "]"; - else + << (unsigned int) (Materials->color[color].r*255) << ":" + << (unsigned int) (Materials->color[color].v*255) << ":" + << (unsigned int) (Materials->color[color].b*255) << "]"; + } + else if (color < Materials->alldesc.size()) + { cout << Materials->alldesc[color].id; + } + else + { + cout << "Unknown color " << color << endl; + } if( Materials->raceEx[creature.race].castes[creature.caste].ColorModifier[i].startdate > 0 ) { if( (Materials->raceEx[creature.race].castes[creature.caste].ColorModifier[i].startdate <= dayoflife) && @@ -467,7 +475,7 @@ int main (int numargs, char ** args) Materials->ReadPlantMaterials(); Materials->ReadCreatureTypes(); Materials->ReadCreatureTypesEx(); - Materials->ReadDescriptorColors(); + //Materials->ReadDescriptorColors(); if(!Tran->Start()) { diff --git a/tools/examples/dfitemdump.cpp b/tools/examples/dfitemdump.cpp index 875f38b76..bbae94883 100644 --- a/tools/examples/dfitemdump.cpp +++ b/tools/examples/dfitemdump.cpp @@ -10,7 +10,7 @@ #include #include using namespace std; - +#define DFHACK_WANT_MISCUTILS #include #include @@ -35,13 +35,16 @@ int main () } DFHack::Materials * Materials = DF->getMaterials(); Materials->ReadAllMaterials(); + + DFHack::Gui * Gui = DF->getGui(); DFHack::Items * Items = DF->getItems(); Items->Start(); DFHack::VersionInfo * mem = DF->getMemoryInfo(); p = DF->getProcess(); - + int32_t x,y,z; + Gui->getCursorCoords(x,y,z); // FIXME: tools should never be exposed to DFHack internals! DFHack::OffsetGroup* itemGroup = mem->getGroup("Items"); DFHack::DfVector p_items (p, itemGroup->getAddress("items_vector")); @@ -52,15 +55,35 @@ int main () DFHack::dfh_item itm; memset(&itm, 0, sizeof(DFHack::dfh_item)); Items->readItem(p_items[i],itm); - printf( - "%5d: %08x %08x (%d,%d,%d) #%08x [%d] %s - %s\n", - i, itm.origin, 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() - ); + if(x != -30000) + { + if(itm.base.x == x && itm.base.y == y && itm.base.z == z) + { + printf( + "%5d: %08x %08x (%d,%d,%d) #%08x [%d] %s - %s\n", + i, itm.origin, 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() + ); + hexdump(DF,p_items[i],0x100); + cout << Items->dumpAccessors(itm) << endl; + } + } + else + { + printf( + "%5d: %08x %08x (%d,%d,%d) #%08x [%d] %s - %s\n", + i, itm.origin, 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() + ); + } } /* printf("type\tvtable\tname\tquality\tdecorate\n"); diff --git a/tools/playground/CMakeLists.txt b/tools/playground/CMakeLists.txt index 6f60d0c16..2ff356e06 100644 --- a/tools/playground/CMakeLists.txt +++ b/tools/playground/CMakeLists.txt @@ -80,6 +80,17 @@ DFHACK_TOOL(dfprinttiletypes printtiletypes.cpp) # Will have many options in the future. DFHACK_TOOL(dfhellhole hellhole.cpp) +# skillmodify +# Author: raoulxq +# Lets modify skills and labors of dwarfs and other creatures +# Allows mass-modify, esp. for all immigrants +DFHACK_TOOL(dfskillmodify skillmodify.cpp) + +# digpattern +# Author: raoulxq +# Dig a specific pattern (in this case 3x3 bedrooms, modify as you like) +DFHACK_TOOL(dfdigpattern digpattern.cpp) + DFHACK_TOOL(dfcleanowned cleanowned.cpp) DFHACK_TOOL(dffixbug-3708 fix-3708.cpp) diff --git a/tools/playground/digpattern.cpp b/tools/playground/digpattern.cpp new file mode 100644 index 000000000..f49df96c6 --- /dev/null +++ b/tools/playground/digpattern.cpp @@ -0,0 +1,259 @@ +#include +#include // for memset +#include +#include +#include +#include +#include +#include +using namespace std; + +#include +#include +using namespace MapExtras; +//#include + +void usage(int argc, const char * argv[]) +{ + cout + << "Usage:" << endl + << argv[0] << " [option 1] [option 2] [...]" << endl + << "-q : Suppress \"Press any key to continue\" at program termination" << endl + << "-u : Dig upwards times (default 5)" << endl + << "-d : Dig downwards times (default 5)" << endl + ; +} + +void digat(MapCache * MCache, DFHack::DFCoord xy) +{ + int16_t tt; + tt = MCache->tiletypeAt(xy); + if(!DFHack::isWallTerrain(tt)) + return; + + // found a good tile, dig+unset material + DFHack::t_designation des = MCache->designationAt(xy); + + if(MCache->testCoord(xy)) + { + MCache->clearMaterialAt(xy); + + if(des.bits.dig == DFHack::designation_no) + des.bits.dig = DFHack::designation_default; + MCache->setDesignationAt(xy,des); + } +} + +int strtoint(const string &str) +{ + stringstream ss(str); + int result; + return ss >> result ? result : -1; +} + +typedef struct +{ + int16_t x; + int16_t y; +} pos; + +int main (int argc, const char* argv[]) +{ + // Command line options + bool updown = false; + bool quiet = true; + // let's be more useful when double-clicked on windows + #ifndef LINUX_BUILD + quiet = false; + #endif + int dig_up_n = 5; + int dig_down_n = 5; + + for(int i = 1; i < argc; i++) + { + string arg_cur = argv[i]; + string arg_next = ""; + int arg_next_int = -99999; + /* Check if argv[i+1] is a number >= 0 */ + if (i < argc-1) { + arg_next = argv[i+1]; + arg_next_int = strtoint(arg_next); + if (arg_next != "0" && arg_next_int == 0) { + arg_next_int = -99999; + } + } + if (arg_cur == "-x") + { + updown = true; + } + else if (arg_cur == "-q") + { + quiet = true; + } + else if(arg_cur == "-u" && i < argc-1) + { + if (arg_next_int < 0 || arg_next_int >= 99999) { + usage(argc, argv); + return 1; + } + dig_up_n = arg_next_int; + i++; + } + else if(arg_cur == "-d" && i < argc-1) + { + if (arg_next_int < 0 || arg_next_int >= 99999) { + usage(argc, argv); + return 1; + } + dig_down_n = arg_next_int; + i++; + } + else + { + usage(argc, argv); + return 1; + } + } + + DFHack::ContextManager DFMgr("Memory.xml"); + DFHack::Context * DF; + try + { + DF = DFMgr.getSingleContext(); + DF->Attach(); + } + catch (exception& e) + { + cerr << "Error getting context: " << e.what() << endl; + if (!quiet) + cin.ignore(); + + return 1; + } + + uint32_t x_max,y_max,z_max; + DFHack::Maps * Maps = DF->getMaps(); + DFHack::Gui * Gui = DF->getGui(); + + // init the map + if(!Maps->Start()) + { + cerr << "Can't init map. Make sure you have a map loaded in DF." << endl; + DF->Detach(); + if (!quiet) + cin.ignore(); + + return 1; + } + + int32_t cx, cy, cz; + Maps->getSize(x_max,y_max,z_max); + uint32_t tx_max = x_max * 16; + uint32_t ty_max = y_max * 16; + + Gui->getCursorCoords(cx,cy,cz); + if (cx == -30000) + { + cerr << "Cursor is not active. Point the cursor at the position to dig at." << endl; + DF->Detach(); + if (!quiet) + { + cin.ignore(); + } + return 1; + } + + DFHack::DFCoord xy ((uint32_t)cx,(uint32_t)cy,cz); + if(xy.x == 0 || xy.x == tx_max - 1 || xy.y == 0 || xy.y == ty_max - 1) + { + cerr << "I won't dig the borders. That would be cheating!" << endl; + DF->Detach(); + if (!quiet) + { + cin.ignore(); + } + return 1; + } + MapCache * MCache = new MapCache(Maps); + + DFHack::t_designation des = MCache->designationAt(xy); + int16_t tt = MCache->tiletypeAt(xy); + int16_t veinmat = MCache->veinMaterialAt(xy); + + /* + if( veinmat == -1 ) + { + cerr << "This tile is non-vein. Bye :)" << endl; + delete MCache; + DF->Detach(); + if (!quiet) { + cin.ignore(); + } + return 1; + } + */ + printf("Digging at (%d/%d/%d), tiletype: %d, veinmat: %d, designation: 0x%x ... DIGGING!\n", cx,cy,cz, tt, veinmat, des.whole); + + // 1 < xy.x < tx_max - 1 + // 1 < xy.y < ty_max - 1 + // xy.z + + // X____ + // X_XXX + // XXXXX + // __XXX + // __XXX + // _____ + pos map[] = + { + { 0,0 } + , { 0,1 } + , { 0,2 } , { 2,2 }, { 3,2 }, { 4,2 } + , { 0,3 }, { 1,3 }, { 2,3 }, { 3,3 }, { 4,3 } + , { 2,4 }, { 3,4 }, { 4,4 } + // this is mirrored, goes left instead of right + , {-2,2 }, {-3,2 }, {-4,2 } + , {-1,3 }, {-2,3 }, {-3,3 }, {-4,3 } + , {-2,4 }, {-3,4 }, {-4,4 } + }; + + DFHack::DFCoord npos = xy; + + if (dig_up_n > 0) + { + for (int j = 0; j < dig_up_n; j++) + { + for (int i = 0; i < sizeof(map)/sizeof(map[0]); i++) + { + npos=xy; + npos.x += map[i].x; + npos.y -= 4*j + map[i].y; + printf("Digging at (%d/%d/%d)\n", npos.x, npos.y, npos.z); + digat(MCache, npos); + } + } + } + if (dig_down_n > 0) + { + for (int j = 0; j < dig_down_n; j++) + { + for (int i = 0; i < sizeof(map)/sizeof(map[0]); i++) + { + npos=xy; + npos.x += map[i].x; + npos.y += 4*j + map[i].y; + printf("Digging at (%d/%d/%d)\n", npos.x, npos.y, npos.z); + digat(MCache, npos); + } + } + } + + MCache->WriteAll(); + delete MCache; + DF->Detach(); + if (!quiet) { + cout << "Done. Press any key to continue" << endl; + cin.ignore(); + } + return 0; +} diff --git a/tools/playground/skillmodify.cpp b/tools/playground/skillmodify.cpp new file mode 100644 index 000000000..c53ead8c8 --- /dev/null +++ b/tools/playground/skillmodify.cpp @@ -0,0 +1,721 @@ +/********************************************* + * skillmodify.cpp + * + * Purpose: + * + * - Display creatures + * - Modify skills and labors of creatures + * + * Version: 0.1.1 + * Date: 2011-04-07 + + * Todo: + * - Option to add/remove single skills + * - Ghosts/Merchants/etc. should be tagged as not own creatures + * - Filter by nickname with -n + * - Filter by first name with -fn + * - Filter by last name with -ln + * - Add pattern matching (or at least matching) to -n/-fn/-ln + * - Set nickname with --setnick (only if -i is given) + * - Kill/revive creature(s) with --kill/--revive + * - Show skills/labors only when -ss/-sl/-v is given or a skill/labor is changed + * - Allow multiple -i switches + * - Display current job + + * Done: + * - Remove magic numbers + * - Show social skills only when -ss is given + * - Hide hauler labors when +sh is given + * - Add -v for verbose + * - Override forbidden mass-designation with -f + * - Option to add/remove single labors + * - Switches -ras and rl should only be possible with -nn or -i + * - Switch -rh removes hauler jobs + * - Dead creatures should not be displayed + * - Childs should not get labors assigned to + * - Babies should not get labors assigned to + * - Switch -al adds labor number n + * - Switch -rl removes labor number n + * - Switch -ral removes all labors + * - Switch -ll lists all available labors + ********************************************* +*/ + +#include +#include +#include +#include +#include +using namespace std; + +#define DFHACK_WANT_MISCUTILS +#include +#include + +/* Note about magic numbers: + * If you have an idea how to better solve this, tell me. Currently I'd be + * either dependent on Toady One's implementation (#defining numbers) or + * Memory.xml (#defining text). I voted for Toady One's numbers to be more + * stable, but could be wrong. + */ +#define SKILL_PERSUASION 72 +#define SKILL_NEGOTIATION 73 +#define SKILL_JUDGING_INTENT 74 +#define SKILL_INTIMIDATION 79 +#define SKILL_CONVERSATION 80 +#define SKILL_COMEDY 81 +#define SKILL_FLATTERY 82 +#define SKILL_CONSOLING 83 +#define SKILL_PACIFICATION 84 +#define LABOR_STONE_HAULING 1 +#define LABOR_WOOD_HAULING 2 +#define LABOR_BURIAL 3 +#define LABOR_FOOD_HAULING 4 +#define LABOR_REFUSE_HAULING 5 +#define LABOR_ITEM_HAULING 6 +#define LABOR_FURNITURE_HAULING 7 +#define LABOR_ANIMAL_HAULING 8 +#define LABOR_CLEANING 9 +#define LABOR_FEED_PATIENTS_PRISONERS 22 +#define LABOR_RECOVERING_WOUNDED 23 +#define NOT_SET INT_MIN +#define MAX_MOOD 4 +#define NO_MOOD -1 + +bool quiet; +bool verbose = false; +bool showhauler = true; +bool showsocial = false; +int hauler_labors[] = { + LABOR_STONE_HAULING + ,LABOR_WOOD_HAULING + ,LABOR_BURIAL + ,LABOR_FOOD_HAULING + ,LABOR_REFUSE_HAULING + ,LABOR_ITEM_HAULING + ,LABOR_FURNITURE_HAULING + ,LABOR_ANIMAL_HAULING + ,LABOR_CLEANING + ,LABOR_FEED_PATIENTS_PRISONERS + ,LABOR_RECOVERING_WOUNDED +}; +int social_skills[] = +{ + SKILL_PERSUASION + ,SKILL_NEGOTIATION + ,SKILL_JUDGING_INTENT + ,SKILL_INTIMIDATION + ,SKILL_CONVERSATION + ,SKILL_COMEDY + ,SKILL_FLATTERY + ,SKILL_CONSOLING + ,SKILL_PACIFICATION +}; + +void usage(int argc, const char * argv[]) +{ + cout + << "Usage:" << endl + << argv[0] << " [option 1] [option 2] [...]" << endl + << "-q : Suppress \"Press any key to continue\" at program termination" << endl + << "-v : Increase verbosity" << endl + << "-c creature : Only show/modify this creature type instead of dwarfes" << endl + << "-i id : Only show/modify creature with this id" << endl + << "-nn : Only show/modify creatures with no custom nickname (migrants)" << endl + << "--nicks : Only show/modify creatures with custom nickname" << endl + << "-ll : List available labors" << endl + << "-al : Add labor to creature" << endl + << "-rl : Remove labor from creature" << endl + << "-ras : Remove all skills from creature" << endl + << "-ral : Remove all labors from creature" << endl + << "-ah : Add hauler labors (stone hauling, etc.) to creature" << endl + << "-rh : Remove hauler labors (stone hauling, etc.) from creature" << endl + << "--setmood : Set mood to n (-1 = no mood, max=4)" << endl + // Doesn't work, because hapiness is recalculated + //<< "--sethappiness : Set happiness to n" << endl + << "-f : Force an action" << endl + << endl + << "Example 1: Show all dwarfs" << endl + << argv[0] << " -c Dwarf" << endl + << endl + << "Example 2: Show all Yaks" << endl + << argv[0] << " -c Yak" << endl + << endl + << "Example 3: Remove all skills from dwarf with id 32" << endl + << argv[0] << " -i 32 -ras" << endl + << endl + << "Example 4: Remove all skills and labors from dwarfs with no custom nickname" << endl + << argv[0] << " -c DWARF -nn -ras -ral" << endl + << endl + << "Example 5: Add hauling labors to all migrants" << endl + << argv[0] << " -c DWARF -nn -ah" << endl + << endl + << "Example 6: Show list of labor ids" << endl + << argv[0] << " -c DWARF -ll" << endl + << endl + << "Example 7: Add engraving labor to all migrants (get the id from the list of labor ids)" << endl + << argv[0] << " -c DWARF -nn -al 13" << endl + ; + if (quiet == false) { + cout << "Press any key to continue" << endl; + cin.ignore(); + } +} + +DFHack::Materials * Materials; +DFHack::VersionInfo *mem; +DFHack::Creatures * Creatures = NULL; + +// Note that toCaps() changes the string itself and I'm using it a few times in +// an unsafe way below. Didn't crash yet however. +std::string toCaps(std::string s) +{ + const int length = s.length(); + if (length == 0) { + return s; + } + s[0] = std::toupper(s[0]); + for(int i=1; i!=length ; ++i) + { + s[i] = std::tolower(s[i]); + } + return s; +} + +int strtoint(const string &str) +{ + stringstream ss(str); + int result; + return ss >> result ? result : -1; +} + + +// A C++ standard library function should be used instead +bool is_in(int m, int set[], int set_size) +{ + for (int i=0; iraceEx[creature.race].rawname); + + DFHack::Translation *Tran = DF->getTranslation(); + DFHack::VersionInfo *mem = DF->getMemoryInfo(); + + if(creature.name.nickname[0]) + { + cout << ", " << creature.name.nickname; + } + else + { + if(creature.name.first_name[0]) + { + cout << ", " << toCaps(creature.name.first_name); + } + + string transName = Tran->TranslateName(creature.name,false); + if(!transName.empty()) + { + cout << " " << toCaps(transName); + } + } + + string prof_string=""; + try { + prof_string = mem->getProfession(creature.profession); + } + catch (exception& e) + { + cout << "Error retrieving creature profession: " << e.what() << endl; + } + cout << ", " << toCaps(prof_string) << "(" << int(creature.profession) << ")"; + + if(creature.custom_profession[0]) + { + cout << "/" << creature.custom_profession; + } + + if(creature.current_job.active) + { + cout << ", current job: " << mem->getJob(creature.current_job.jobId); + } + + cout << ", Happy = " << creature.happiness; + cout << endl; + + if((creature.mood != NO_MOOD) && (creature.mood<=MAX_MOOD)) + { + cout << "Creature is in a strange mood (mood=" << creature.mood << "), skill: " << mem->getSkill(creature.mood_skill) << endl; + vector mymat; + if(Creatures->ReadJob(&creature, mymat)) + { + for(unsigned int i = 0; i < mymat.size(); i++) + { + printf("\t%s(%d)\t%d %d %d - %.8x\n", Materials->getDescription(mymat[i]).c_str(), mymat[i].itemType, mymat[i].subType, mymat[i].subIndex, mymat[i].index, mymat[i].flags); + } + } + } + + if(creature.has_default_soul) + { + // Print out skills + int skillid; + int skillrating; + int skillexperience; + string skillname; + + cout << setiosflags(ios::left); + + for(unsigned int i = 0; i < creature.defaultSoul.numSkills;i++) + { + skillid = creature.defaultSoul.skills[i].id; + bool is_social = is_in(skillid, social_skills, sizeof(social_skills)/sizeof(social_skills[0])); + if (!is_social || (is_social && showsocial)) + { + skillrating = creature.defaultSoul.skills[i].rating; + skillexperience = creature.defaultSoul.skills[i].experience; + try + { + skillname = mem->getSkill(skillid); + } + catch(DFHack::Error::AllMemdef &e) + { + skillname = "Unknown skill"; + cout << e.what() << endl; + } + cout << "(Skill " << int(skillid) << ") " << setw(16) << skillname << ": " + << skillrating << "/" << skillexperience << endl; + } + } + + for(unsigned int i = 0; i < NUM_CREATURE_LABORS;i++) + { + if(!creature.labors[i]) + continue; + string laborname; + try + { + laborname = mem->getLabor(i); + } + catch(exception &e) + { + laborname = "(Undefined)"; + } + bool is_labor = is_in(i, hauler_labors, sizeof(hauler_labors)/sizeof(hauler_labors[0])); + if (!is_labor || (is_labor && showhauler)) + cout << "(Labor " << i << ") " << setw(16) << laborname << endl; + } + } + /* FLAGS 1 */ + if(creature.flags1.bits.dead) { cout << "Flag: Dead" << endl; } + if(creature.flags1.bits.on_ground) { cout << "Flag: On the ground" << endl; } + if(creature.flags1.bits.skeleton) { cout << "Flag: Skeletal" << endl; } + if(creature.flags1.bits.zombie) { cout << "Flag: Zombie" << endl; } + if(creature.flags1.bits.tame) { cout << "Flag: Tame" << endl; } + if(creature.flags1.bits.royal_guard){ cout << "Flag: Royal_guard" << endl; } + if(creature.flags1.bits.fortress_guard){cout<<"Flag: Fortress_guard" << endl; } + /* FLAGS 2 */ + if(creature.flags2.bits.killed) { cout << "Flag: Killed by kill function" << endl; } + if(creature.flags2.bits.resident) { cout << "Flag: Resident" << endl; } + if(creature.flags2.bits.gutted) { cout << "Flag: Gutted" << endl; } + if(creature.flags2.bits.slaughter) { cout << "Flag: Marked for slaughter" << endl; } + if(creature.flags2.bits.underworld) { cout << "Flag: From the underworld" << endl; } + + if(creature.flags1.bits.had_mood && (creature.mood == -1 || creature.mood == 8 ) ) + { + string artifact_name = Tran->TranslateName(creature.artifact_name,false); + cout << "Artifact: " << artifact_name << endl; + } +} + +int main (int argc, const char* argv[]) +{ + // let's be more useful when double-clicked on windows +#ifndef LINUX_BUILD + quiet = false; +#endif + + string creature_type = "Dwarf"; + string creature_id = ""; + int creature_id_int = 0; + bool find_nonicks = false; + bool find_nicks = false; + bool remove_skills = false; + bool remove_labors = false; + bool make_hauler = false; + bool remove_hauler = false; + bool add_labor = false; + int add_labor_n = NOT_SET; + bool remove_labor = false; + int remove_labor_n = NOT_SET; + bool set_happiness = false; + int set_happiness_n = NOT_SET; + bool set_mood = false; + int set_mood_n = NOT_SET; + bool list_labors = false; + bool force_massdesignation = false; + + if (argc == 1) { + usage(argc, argv); + return 1; + } + + for(int i = 1; i < argc; i++) + { + string arg_cur = argv[i]; + string arg_next = ""; + int arg_next_int = NOT_SET; + /* Check if argv[i+1] is a number >= 0 */ + if (i < argc-1) { + arg_next = argv[i+1]; + arg_next_int = strtoint(arg_next); + if (arg_next != "0" && arg_next_int == 0) { + arg_next_int = NOT_SET; + } + } + + if(arg_cur == "-q") + { + quiet = true; + } + else if(arg_cur == "+q") + { + quiet = false; + } + else if(arg_cur == "-v") + { + verbose = true; + } + else if(arg_cur == "-ss" || arg_cur == "--showsocial") + { + showsocial = true; + } + else if(arg_cur == "+sh" || arg_cur == "-nosh" || arg_cur == "--noshowhauler") + { + showhauler = false; + } + else if(arg_cur == "-ras") + { + remove_skills = true; + } + else if(arg_cur == "-f") + { + force_massdesignation = true; + } + // list labors + else if(arg_cur == "-ll") + { + list_labors = true; + } + // add single labor + else if(arg_cur == "-al" && i < argc-1) + { + if (arg_next_int == NOT_SET || arg_next_int >= NUM_CREATURE_LABORS) { + usage(argc, argv); + return 1; + } + add_labor = true; + add_labor_n = arg_next_int; + i++; + } + // remove single labor + else if(arg_cur == "-rl" && i < argc-1) + { + if (arg_next_int == NOT_SET || arg_next_int >= NUM_CREATURE_LABORS) { + usage(argc, argv); + return 1; + } + remove_labor = true; + remove_labor_n = arg_next_int; + i++; + } + else if(arg_cur == "--setmood" && i < argc-1) + { + if (arg_next_int < NO_MOOD || arg_next_int > MAX_MOOD) { + usage(argc, argv); + return 1; + } + set_mood = true; + set_mood_n = arg_next_int; + i++; + } + else if(arg_cur == "--sethappiness" && i < argc-1) + { + if (arg_next_int < 1 || arg_next_int >= 2000) { + usage(argc, argv); + return 1; + } + set_happiness = true; + set_happiness_n = arg_next_int; + i++; + } + else if(arg_cur == "-ral") + { + remove_labors = true; + } + else if(arg_cur == "-ah") + { + make_hauler = true; + } + else if(arg_cur == "-rh") + { + remove_hauler = true; + } + else if(arg_cur == "-nn") + { + find_nonicks = true; + } + else if(arg_cur == "--nicks") + { + find_nicks = true; + } + else if(arg_cur == "-c" && i < argc-1) + { + creature_type = argv[i+1]; + i++; + } + else if(arg_cur == "-i" && i < argc-1) + { + creature_id = argv[i+1]; + sscanf(argv[i+1], "%d", &creature_id_int); + i++; + } + else + { + if (arg_cur != "-h") { + cout << "Unknown option '" << arg_cur << "'" << endl; + cout << endl; + } + usage(argc, argv); + return 1; + } + } + + DFHack::ContextManager DFMgr("Memory.xml"); + DFHack::Context* DF; + try + { + DF = DFMgr.getSingleContext(); + DF->Attach(); + } + catch (exception& e) + { + cerr << e.what() << endl; + if (quiet == false) + { + cin.ignore(); + } + return 1; + } + + Creatures = DF->getCreatures(); + Materials = DF->getMaterials(); + DFHack::Translation * Tran = DF->getTranslation(); + + uint32_t numCreatures; + if(!Creatures->Start(numCreatures)) + { + cerr << "Can't get creatures" << endl; + if (quiet == false) + { + cin.ignore(); + } + return 1; + } + if(!numCreatures) + { + cerr << "No creatures to print" << endl; + if (quiet == false) + { + cin.ignore(); + } + return 1; + } + + mem = DF->getMemoryInfo(); + Materials->ReadInorganicMaterials(); + Materials->ReadOrganicMaterials(); + Materials->ReadWoodMaterials(); + Materials->ReadPlantMaterials(); + Materials->ReadCreatureTypes(); + Materials->ReadCreatureTypesEx(); + Materials->ReadDescriptorColors(); + + if(!Tran->Start()) + { + cerr << "Can't get name tables" << endl; + return 1; + } + + // List all available labors (reproduces contents of Memory.xml) + if (list_labors == true) { + string laborname; + for (int i=0; i < NUM_CREATURE_LABORS; i++) { + try { + laborname = mem->getLabor(i); + cout << "Labor " << int(i) << ": " << laborname << endl; + } + catch (exception& e) { + if (verbose) + { + laborname = "Unknown"; + cout << "Labor " << int(i) << ": " << laborname << endl; + } + } + } + } + else + { + vector addrs; + for(uint32_t creature_idx = 0; creature_idx < numCreatures; creature_idx++) + { + DFHack::t_creature creature; + Creatures->ReadCreature(creature_idx,creature); + /* Check if we want to display/change this creature or skip it */ + bool hasnick = (creature.name.nickname[0] != '\0'); + + if ( + // Check for -i + (creature_id.empty() || creature_idx == creature_id_int) + // Check for -c + && (creature_type.empty() || toCaps(string(Materials->raceEx[creature.race].rawname)) == toCaps(creature_type)) + // Check for -nn + && ((find_nonicks == true && hasnick == false) + || (find_nicks == true && hasnick == true) + || (find_nicks == false && find_nonicks == false)) + && (find_nonicks == false || creature.name.nickname[0] == '\0') + && (!creature.flags1.bits.dead) + ) + { + printCreature(DF,creature,creature_idx); + addrs.push_back(creature.origin); + + bool dochange = ( + remove_skills + || remove_labors || add_labor || remove_labor + || make_hauler || remove_hauler + || set_happiness + || set_mood + ); + + // 96=Child, 97=Baby + if (creature.profession == 96 || creature.profession == 97) + { + dochange = false; + } + + bool allow_massdesignation = + !creature_id.empty() || toCaps(creature_type) != "Dwarf" || find_nonicks == true || force_massdesignation; + if (dochange == true && allow_massdesignation == false) + { + cout + << "Not changing creature because none of -c (other than dwarf), -i or -nn was" << endl + << "selected. Add -f to still do mass designation." << endl; + dochange = false; + } + + if (dochange) + { + if(creature.has_default_soul) + { + if (set_mood) + { + cout << "Setting mood to " << set_mood_n << "..." << endl; + Creatures->WriteMood(creature_idx, set_mood_n); + } + + if (set_happiness) + { + cout << "Setting happiness to " << set_happiness_n << "..." << endl; + Creatures->WriteHappiness(creature_idx, set_happiness_n); + } + + if (remove_skills) + { + DFHack::t_soul & soul = creature.defaultSoul; + + cout << "Removing skills..." << endl; + for(unsigned int sk = 0; sk < soul.numSkills;sk++) + { + soul.skills[sk].rating=0; + soul.skills[sk].experience=0; + } + // Doesn't work anyways, so better leave it alone + //soul.numSkills=0; + if (Creatures->WriteSkills(creature_idx, soul) == true) { + cout << "Success writing skills." << endl; + } else { + cout << "Error writing skills." << endl; + } + } + + if (add_labor || remove_labor || remove_labors || make_hauler || remove_hauler) + { + if (add_labor) { + cout << "Adding labor " << add_labor_n << "..." << endl; + creature.labors[add_labor_n] = 1; + } + + if (remove_labor) { + cout << "Removing labor " << remove_labor_n << "..." << endl; + creature.labors[remove_labor_n] = 0; + } + + if (remove_labors) { + cout << "Removing labors..." << endl; + for(unsigned int lab = 0; lab < NUM_CREATURE_LABORS; lab++) { + creature.labors[lab] = 0; + } + } + + if (remove_hauler) { + for (int labs=0; + labs < sizeof(hauler_labors)/sizeof(hauler_labors[0]); + labs++) + { + creature.labors[hauler_labors[labs]] = 0; + } + } + + if (make_hauler) { + cout << "Setting hauler labors..." << endl; + for (int labs=0; + labs < sizeof(hauler_labors)/sizeof(hauler_labors[0]); + labs++) + { + creature.labors[hauler_labors[labs]] = 1; + } + } + if (Creatures->WriteLabors(creature_idx, creature.labors) == true) { + cout << "Success writing labors." << endl; + } else { + cout << "Error writing labors." << endl; + } + } + } + else + { + cout << "Error removing skills: Creature has no default soul." << endl; + } + printCreature(DF,creature,creature_idx); + } /* End remove skills/labors */ + cout << endl; + } /* if (print creature) */ + } /* End for(all creatures) */ + } /* End if (we need to walk creatures) */ + + Creatures->Finish(); + DF->Detach(); + if (quiet == false) + { + cout << "Done. Press any key to continue" << endl; + cin.ignore(); + } + return 0; +}