Merge remote-tracking branch 'origin/master'

develop
Alexander Gavrilov 2011-04-15 10:58:09 +04:00
commit 56b29cf12e
10 changed files with 1163 additions and 55 deletions

@ -1816,6 +1816,7 @@
<Group name="job">
<Offset name="id" value="0x08" />
<Offset name="type" value="0x40" />
</Group>
</Group>
<Group name="Vegetation">
@ -2008,6 +2009,7 @@
</Group>
<Group name="job" valid="false">
<Offset name="id" valid="true" />
<Offset name="type" valid="true" />
</Group>
</Group>
<Group name="Maps" valid="true">

@ -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;

@ -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;

@ -25,6 +25,7 @@ distribution.
#include "Internal.h"
#include <string>
#include <sstream>
#include <vector>
#include <cstdio>
#include <map>
@ -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();
}

@ -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))
{

@ -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(color<Materials->color.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())
{

@ -10,7 +10,7 @@
#include <vector>
#include <cstring>
using namespace std;
#define DFHACK_WANT_MISCUTILS
#include <DFHack.h>
#include <dfhack/DFVector.h>
@ -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 <uint32_t> 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");

@ -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)

@ -0,0 +1,259 @@
#include <iostream>
#include <string.h> // for memset
#include <string>
#include <vector>
#include <stack>
#include <map>
#include <stdio.h>
#include <cstdlib>
using namespace std;
#include <DFHack.h>
#include <dfhack/extra/MapExtras.h>
using namespace MapExtras;
//#include <argstream.h>
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 <n> : Dig upwards <n> times (default 5)" << endl
<< "-d <n> : Dig downwards <n> 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;
}

@ -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 <n> adds labor number n
* - Switch -rl <n> removes labor number n
* - Switch -ral removes all labors
* - Switch -ll lists all available labors
*********************************************
*/
#include <iostream>
#include <climits>
#include <string.h>
#include <vector>
#include <stdio.h>
using namespace std;
#define DFHACK_WANT_MISCUTILS
#include <DFHack.h>
#include <dfhack/modules/Creatures.h>
/* 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 <n> : Add labor <n> to creature" << endl
<< "-rl <n> : Remove labor <n> 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 <n> : Set mood to n (-1 = no mood, max=4)" << endl
// Doesn't work, because hapiness is recalculated
//<< "--sethappiness <n> : 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; i<set_size; i++)
{
if (m == set[i])
return true;
}
return false;
}
void printCreature(DFHack::Context * DF, const DFHack::t_creature & creature, int index)
{
cout << "Creature[" << index << "]: " << toCaps(Materials->raceEx[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<DFHack::t_material> 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<uint32_t> 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 <num>
(creature_id.empty() || creature_idx == creature_id_int)
// Check for -c <type>
&& (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;
}