develop
Petr Mrázek 2011-04-17 16:08:13 +02:00
commit bc855b296f
12 changed files with 395 additions and 3 deletions

12
.gitignore vendored

@ -17,6 +17,16 @@ build*/
#except for the real one
!build/
# in-place build
build/Makefile
build/CMakeCache.txt
build/cmake_install.cmake
build/CMakeFiles
build/doc
build/bin
build/library
build/tools
#ignore Kdevelop stuff
.kdev4
@ -30,4 +40,4 @@ dfhack/python/PyDFHack.egg-info
dfhack/python/build
dfhack/python/dist
/cmakeall.bat
/cmakeall.bat

@ -29,6 +29,9 @@
<Offset name="first" description="Lowercase stl string with the first name. For ex. 'urist'" />
<Offset name="nick" description="Stl string with the nickname. Set by the player." />
<Offset name="second_words" description="Array of 7 indexes into the language vectors."/>
<Offset name="parts_of_speech" description="Array of 7 16-bit indexes into the language vectors."/>
<Offset name="language" description="Language of the name."/>
<Offset name="has_name" description="Boolean flag."/>
</Group>
</Offsets>
</Base>
@ -784,6 +787,9 @@
<Offset name="first" description="Lowercase stl string with the first name. For ex. 'urist'" />
<Offset name="nick" description="Stl string with the nickname. Set by the player." />
<Offset name="second_words" description="Array of 7 indexes into the language vectors."/>
<Offset name="parts_of_speech" description="Array of 7 16-bit indexes into the language vectors."/>
<Offset name="language" description="Language of the name."/>
<Offset name="has_name" description="Boolean flag."/>
</Group>
<Group name="Position" description="Offsets used by the Position module.">
<Address name="window_x" description="X coordinate of the current view (DWORD)" />
@ -872,6 +878,7 @@
<Offset name="position" description="X,Y,Z." />
<Offset name="flags1" description="First set of flags" />
<Offset name="flags2" description="Second set of flags" />
<Offset name="flags3" description="Third set of flags" />
<Offset name="caste" description="Caste of the creature. Same as sex most of the time." />
<Offset name="sex" description="Sex of the creature." />
<Offset name="id" description="Unique ID of the creature, seems to be used for binary search in the creature vector." />
@ -893,6 +900,7 @@
<Offset name="current_soul" description="Currently active soul?" />
<Offset name="labors" description="Array of labors. Used by DT to enable/disable them." />
<Offset name="happiness" description="Number that says how happy the creature is." />
<Offset name="hist_figure_id" description="For a creature matching a historical figure, it's ID"/>
</Group>
</Group>
@ -1013,6 +1021,39 @@
<Address name="control_mode" description="Current control mode" />
<!--<Address name="control_mode_copy" description="Copy of the control mode in DF memory" />-->
</Group>
<Group name="Legends">
<Group name="figures">
<Address name="vector"/>
<Offset name="profession" description="Figure's profession"/>
<Offset name="race" description="Figure's race"/>
<Offset name="caste" description="Figure's caste; mostly equal to sex"/>
<Offset name="sex" description="Figure's sex (or maybe the other way round)"/>
<Offset name="appeared_year"/>
<Offset name="born_year"/>
<Offset name="born_seconds"/>
<Offset name="died_year"/>
<Offset name="died_seconds"/>
<Offset name="name" description="A standard name record"/>
<Offset name="unit_id" description="ID of the creature matching the figure, or -1"/>
<Offset name="figure_id" description="ID of the figure, ordered"/>
<Offset name="entity_link_vector"/>
<Offset name="histfig_link_vector"/>
</Group>
<Group name="events">
<Address name="vector"/>
<Offset name="year"/>
<Offset name="seconds"/>
<Offset name="event_id"/>
<Group name="hist_figure_died">
<Offset name="figure_id"/>
<Offset name="slayer_id"/>
<Offset name="slayer_race"/>
<Offset name="slayer_caste"/>
<Offset name="slayer_item_id"/>
<Offset name="region_id"/>
</Group>
</Group>
</Group>
</Offsets>
</Base>
@ -1074,6 +1115,9 @@
<Offset name="first" value="0x0" />
<Offset name="nick" value="0x1C" />
<Offset name="second_words" value="0x38" />
<Offset name="parts_of_speech" value="0x54" />
<Offset name="language" value="0x64" />
<Offset name="has_name" value="0x68" />
</Group>
<Group name="Position">
<Address name="window_x" value="0xe32798" />
@ -1614,6 +1658,9 @@
<Offset name="first" value="0x0" />
<Offset name="nick" value="0x1C" />
<Offset name="second_words" value="0x38" />
<Offset name="parts_of_speech" value="0x54" />
<Offset name="language" value="0x64" />
<Offset name="has_name" value="0x68" />
</Group>
<Group name="Position">
<Address name="cursor_xyz" value="0xac77f0" />
@ -2249,6 +2296,9 @@
<Offset name="first" value="0x0" />
<Offset name="nick" value="0x4" />
<Offset name="second_words" value="0x8" />
<Offset name="parts_of_speech" value="0x24" />
<Offset name="language" value="0x34" />
<Offset name="has_name" value="0x3a" />
</Group>
<Group name="Position">
<Address name="window_x" value="0x8cc9b38" />
@ -2921,6 +2971,12 @@
<Offsets>
<Group name="Creatures">
<Address name="vector" value="0x0940b174" /> VERIFY
<Group name="creature">
<Offset name="flags3" value="0x94"/>
<Group name="advanced">
<Offset name="hist_figure_id" value="0x640"/>
</Group>
</Group>
</Group>
<Group name="Position" valid="true">
<Address name="cursor_xyz" value="0x8c3de60"/>
@ -2940,6 +2996,39 @@
<Offset name="item_ref_vector" value="0x24" />
<Offset name="owner_ref_id_field" value="0x4" />
</Group>
<Group name="Legends">
<Group name="figures">
<Address name="vector" value="0x9451a18"/>
<Offset name="profession" value="0x00"/>
<Offset name="race" value="0x02"/>
<Offset name="caste" value="0x04"/>
<Offset name="sex" value="0x06"/>
<Offset name="appeared_year" value="0x08"/>
<Offset name="born_year" value="0x0C"/>
<Offset name="born_seconds" value="0x10"/>
<Offset name="died_year" value="0x1C"/>
<Offset name="died_seconds" value="0x20"/>
<Offset name="name" value="0x24"/>
<Offset name="unit_id" value="0x70"/>
<Offset name="figure_id" value="0x74"/>
<Offset name="entity_link_vector" value="0x7c"/>
<Offset name="histfig_link_vector" value="0x94"/>
</Group>
<Group name="events">
<Address name="vector" value="0x9451a00"/>
<Offset name="year" value="0x04"/>
<Offset name="seconds" value="0x08"/>
<Offset name="event_id" value="0x14"/>
<Group name="hist_figure_died">
<Offset name="figure_id" value="0x18"/>
<Offset name="slayer_id" value="0x1C"/>
<Offset name="slayer_race" value="0x20"/>
<Offset name="slayer_caste" value="0x24"/>
<Offset name="slayer_item_id" value="0x28"/>
<Offset name="region_id" value="0x48"/>
</Group>
</Group>
</Group>
</Offsets>
</Version>
</DFHack>

@ -35,6 +35,9 @@ bool DFContextShared::InitReadNames()
name_firstname_offset = OG->getOffset("first");
name_nickname_offset = OG->getOffset("nick");
name_words_offset = OG->getOffset("second_words");
name_parts_offset = OG->getOffset("parts_of_speech");
name_language_offset = OG->getOffset("language");
name_set_offset = OG->getOffset("has_name");
return true;
}
@ -42,5 +45,25 @@ void DFContextShared::readName(t_name & name, uint32_t address)
{
p->readSTLString(address + name_firstname_offset , name.first_name, 128);
p->readSTLString(address + name_nickname_offset , name.nickname, 128);
p->read(address + name_words_offset ,28, (uint8_t *) name.words);
p->read(address + name_words_offset, 7*4, (uint8_t *)name.words);
p->read(address + name_parts_offset, 7*2, (uint8_t *)name.parts_of_speech);
name.language = p->readDWord(address + name_language_offset);
name.has_name = p->readByte(address + name_set_offset);
}
void DFContextShared::copyName(uint32_t address, uint32_t target)
{
uint8_t buf[28];
if (address == target)
return;
p->copySTLString(address + name_firstname_offset, target + name_firstname_offset);
p->copySTLString(address + name_nickname_offset, target + name_nickname_offset);
p->read(address + name_words_offset, 7*4, buf);
p->write(target + name_words_offset, 7*4, buf);
p->read(address + name_parts_offset, 7*2, buf);
p->write(target + name_parts_offset, 7*2, buf);
p->writeDWord(target + name_language_offset, p->readDWord(address + name_language_offset));
p->writeByte(target + name_set_offset, p->readByte(address + name_set_offset));
}

@ -66,6 +66,7 @@ namespace {
const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString){};
void copySTLString(const uint32_t address, const uint32_t target);
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
};
@ -161,6 +162,29 @@ const string NormalProcess::readSTLString (uint32_t offset)
return ret;
}
void NormalProcess::copySTLString (uint32_t offset, uint32_t target)
{
_Rep_base header;
offset = Process::readDWord(offset);
uint32_t old_target = Process::readDWord(target);
if (offset == old_target)
return;
read(offset - sizeof(_Rep_base),sizeof(_Rep_base),(uint8_t *)&header);
// destroying the leaked state
if (header._M_refcount == -1)
header._M_refcount = 1;
else
header._M_refcount++;
write(offset - sizeof(_Rep_base),sizeof(_Rep_base),(uint8_t *)&header);
writeDWord(target, offset);
}
string NormalProcess::readClassName (uint32_t vptr)
{
int typeinfo = Process::readDWord(vptr - 0x4);

@ -163,6 +163,10 @@ namespace DFHack
virtual size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) = 0;
/// write an STL string
virtual void writeSTLString(const uint32_t address, const std::string writeString) = 0;
/// share the string at source with target; may leak target
virtual void copySTLString(const uint32_t address, const uint32_t target) {
writeSTLString(target, readSTLString(address));
}
/// read a STL vector
virtual void readSTLVector(const uint32_t address, t_vecTriplet & triplet) = 0;
/// get class name of an object with rtti/type info

@ -341,6 +341,8 @@ namespace DFHack
bool WritePos(const uint32_t index, const t_creature &creature);
bool WriteCiv(const uint32_t index, const int32_t civ);
void CopyNameTo(t_creature &creature, uint32_t address);
private:
struct Private;
Private *d;

@ -686,3 +686,12 @@ bool Creatures::ReadInventoryPtr(const uint32_t temp, std::vector<uint32_t> & it
item[i] = p->readDWord(citem[i]);
return true;
}
void Creatures::CopyNameTo(t_creature &creature, uint32_t address)
{
Private::t_offsets &offs = d->creatures;
if(d->Ft_basic)
d->d->copyName(creature.origin + offs.name_offset, address);
}

@ -56,11 +56,15 @@ namespace DFHack
// names, used by a few other modules.
void readName(t_name & name, uint32_t address);
void copyName(uint32_t address, uint32_t target);
// get the name offsets
bool InitReadNames();
uint32_t name_firstname_offset;
uint32_t name_nickname_offset;
uint32_t name_words_offset;
uint32_t name_parts_offset;
uint32_t name_language_offset;
uint32_t name_set_offset;
bool namesInited;
ProcessEnumerator* pm;

@ -94,6 +94,8 @@ DFHACK_TOOL(dfdigpattern digpattern.cpp)
DFHACK_TOOL(dfcleanowned cleanowned.cpp)
DFHACK_TOOL(dffixbug-3708 fix-3708.cpp)
# this needs the C bindings
IF(BUILD_DFHACK_C_BINDINGS)
# The C bindings won't be real C bindings until this compiles.

@ -94,6 +94,8 @@ int main (int argc, char *argv[])
if (!itm.base.flags.owned)
continue;
std::string name = Items->getItemClass(itm.matdesc.itemType);
bool confiscate = false;
bool dump = false;
@ -102,6 +104,12 @@ int main (int argc, char *argv[])
printf("Confiscating a rotten item: \t");
confiscate = true;
}
else if (itm.base.flags.on_ground &&
(name == "food" || name == "meat" || name == "plant"))
{
printf("Confiscating a dropped foodstuff: \t");
confiscate = true;
}
else if (itm.wear_level >= wear_dump_level)
{
printf("Confiscating and dumping a worn item: \t");

@ -0,0 +1,217 @@
/* Fixes bug 3708 (Ghosts that can't be engraved on a slab).
Cause of the bug:
In order to be engraved on a slab, the creature must be
a historical figure, i.e. be in the historical figure list
of the Legends mode. It seems that caravan guards are not
added to that list until they do something notable, e.g.
kill a goblin. Unfortunately, their own death doesn't
trigger this sometimes.
Solution:
Steal a historical figure entry from a dead goblin, by
replacing the IDs in the structures; also overwrite his
name, race and profession to make the menus make slightly
more sense.
Downsides:
- Obviously, this is an ugly hack.
- The Legends mode still lists the guard as belonging to
the goblin civilization, and killed by whoever killed the
original goblin. There might be other inconsistencies.
Positive sides:
- Avoids messing with tricky creature control code,
by allowing the ghost to be removed naturally.
*/
#include <iostream>
#include <climits>
#include <string.h>
#include <vector>
#include <list>
#include <stdio.h>
using namespace std;
#define DFHACK_WANT_MISCUTILS
#include <DFHack.h>
enum likeType
{
FAIL = 0,
MATERIAL = 1,
ITEM = 2,
FOOD = 3
};
DFHack::Materials * Materials;
DFHack::VersionInfo *mem;
DFHack::Creatures * Creatures = NULL;
void printCreature(DFHack::Context * DF, const DFHack::t_creature & creature)
{
cout << "Address: " << hex << creature.origin << dec << ", creature race: " << Materials->raceEx[creature.race].rawname
<< ", position: " << creature.x << "x " << creature.y << "y "<< creature.z << "z" << endl
<< "Name: " << creature.name.first_name;
if (creature.name.nickname[0])
cout << " `" << creature.name.nickname << "'";
DFHack::Translation * Tran = DF->getTranslation();
cout << " " << Tran->TranslateName(creature.name,false)
<< " (" << Tran->TranslateName(creature.name,true) << ")" << endl;
cout << "Profession: " << mem->getProfession(creature.profession);
if(creature.custom_profession[0])
cout << ", custom: " << creature.custom_profession;
uint32_t dayoflife = creature.birth_year*12*28 + creature.birth_time/1200;
cout << endl
<< "Born on the year " << creature.birth_year
<< ", month " << (creature.birth_time/1200/28)
<< ", day " << ((creature.birth_time/1200) % 28 + 1)
<< ", " << dayoflife << " days lived." << endl << endl;
}
int main (int numargs, char ** args)
{
DFHack::World * World;
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;
}
Creatures = DF->getCreatures();
Materials = DF->getMaterials();
World = DF->getWorld();
DFHack::Translation * Tran = DF->getTranslation();
uint32_t numCreatures;
if(!Creatures->Start(numCreatures))
{
cerr << "Can't get creatures" << endl;
#ifndef LINUX_BUILD
cin.ignore();
#endif
return 1;
}
Materials->ReadCreatureTypes();
Materials->ReadCreatureTypesEx();
mem = DF->getMemoryInfo();
DFHack::Process *p = DF->getProcess();
if(!Tran->Start())
{
cerr << "Can't get name tables" << endl;
return 1;
}
DFHack::OffsetGroup *ogc = mem->getGroup("Creatures")->getGroup("creature");
uint32_t o_flags3 = ogc->getOffset("flags3");
uint32_t o_c_hfid = ogc->getGroup("advanced")->getOffset("hist_figure_id");
std::list<uint32_t> goblins;
std::list<uint32_t> ghosts;
for(uint32_t i = 0; i < numCreatures; i++)
{
DFHack::t_creature temp;
Creatures->ReadCreature(i,temp);
int32_t hfid = p->readDWord(temp.origin + o_c_hfid);
if (hfid > 0) {
if (temp.flags1.bits.dead) {
std::string name = Materials->raceEx[temp.race].rawname;
if (name == "GOBLIN")
goblins.push_back(i);
}
} else {
uint32_t flags3 = p->readDWord(temp.origin + o_flags3);
if (!(flags3 & 0x1000))
continue;
ghosts.push_back(i);
}
}
if (goblins.size() >= ghosts.size() && ghosts.size() > 0)
{
DFHack::OffsetGroup *ogf = mem->getGroup("Legends")->getGroup("figures");
uint32_t f_vector = p->readDWord(ogf->getAddress("vector"));
uint32_t f_id = ogf->getOffset("figure_id");
uint32_t f_unit = ogf->getOffset("unit_id");
uint32_t f_name = ogf->getOffset("name");
uint32_t f_race = ogf->getOffset("race");
uint32_t f_profession = ogf->getOffset("profession");
for (std::list<uint32_t>::iterator it = ghosts.begin(); it != ghosts.end(); ++it)
{
int i = *it;
DFHack::t_creature ghost;
Creatures->ReadCreature(i,ghost);
printCreature(DF,ghost);
int igoblin = goblins.front();
goblins.pop_front();
DFHack::t_creature goblin;
Creatures->ReadCreature(igoblin,goblin);
printCreature(DF,goblin);
int32_t hfid = p->readDWord(goblin.origin + o_c_hfid);
uint32_t fptr = p->readDWord(f_vector + 4*hfid);
if (p->readDWord(fptr + f_id) != hfid ||
p->readDWord(fptr + f_unit) != goblin.id ||
p->readWord(fptr + f_race) != goblin.race)
{
cout << "Data structure inconsistency detected, aborting.";
break;
}
if (1) {
p->writeDWord(goblin.origin + o_c_hfid, -1);
p->writeDWord(ghost.origin + o_c_hfid, hfid);
p->writeDWord(fptr + f_unit, ghost.id);
p->writeWord(fptr + f_race, ghost.race);
p->writeWord(fptr + f_profession, ghost.profession);
Creatures->CopyNameTo(ghost, fptr + f_name);
cout << "Pair succesfully patched." << endl << endl;
}
}
}
else
{
cout << "No suitable ghosts, or not enough goblins." << endl;
}
Creatures->Finish();
DF->Detach();
#ifndef LINUX_BUILD
cout << "Done. Press any key to continue" << endl;
cin.ignore();
#endif
return 0;
}

@ -529,7 +529,7 @@ bool findString (SegmentedFinder* s, uint32_t *addr, const char * compare )
bool findStrBuffer (SegmentedFinder* s, uint32_t *addr, const char * compare )
{
if(strcmp((const char *)addr, compare) == 0)
if(memcmp((const char *)addr, compare, strlen(compare)) == 0)
return true;
return false;
}