diff --git a/Memory.xml b/Memory.xml index f651c07df..0d3129ec7 100644 --- a/Memory.xml +++ b/Memory.xml @@ -1028,6 +1028,7 @@
+
@@ -3058,6 +3059,7 @@
+
diff --git a/library/include/dfhack/modules/World.h b/library/include/dfhack/modules/World.h index c33ffed6b..7203d775f 100644 --- a/library/include/dfhack/modules/World.h +++ b/library/include/dfhack/modules/World.h @@ -107,6 +107,7 @@ namespace DFHack void SetCurrentWeather(uint8_t weather); bool ReadGameMode(t_gamemodes& rd); bool WriteGameMode(const t_gamemodes & wr); // this is very dangerous + std::string ReadWorldFolder(); private: struct Private; Private *d; diff --git a/library/modules/World.cpp b/library/modules/World.cpp index 042864261..f19f94a9d 100644 --- a/library/modules/World.cpp +++ b/library/modules/World.cpp @@ -67,6 +67,10 @@ struct World::Private uint32_t gamemode_offset; uint32_t controlmode_offset; uint32_t controlmodecopy_offset; + + bool StartedFolder; + uint32_t folder_name_offset; + Process * owner; }; @@ -106,6 +110,14 @@ World::World() d->StartedMode = true; } catch(Error::All &){}; + try + { + d->folder_name_offset = OG_World->getAddress( "save_folder" ); + d->StartedFolder = true; + } + catch(Error::All &){}; + + d->Inited = true; } @@ -220,3 +232,12 @@ void World::SetCurrentWeather(uint8_t weather) d->owner->write(d->weather_offset,sizeof(buf),buf); } } + +string World::ReadWorldFolder() +{ + if (d->Inited && d->StartedFolder) + { + return string( * ( (string*) d->folder_name_offset ) ); + } + return string(""); +} diff --git a/package/linux/dfhack b/package/linux/dfhack index d5d221879..46d32f529 100755 --- a/package/linux/dfhack +++ b/package/linux/dfhack @@ -4,28 +4,53 @@ # changed to properly set LD_PRELOAD so as to run DFHACK. # # You can run DF under gdb by passing -g or --gdb as the first argument. +# +# If the file ".dfhackrc" exists in the DF directory or your home directory +# it will be sourced by this script, to let you set environmental variables. +# If it exists in both places it will first source the one in your home +# directory, then the on in the game directory. +# +# Shell variables .dfhackrc can set to affect this script: +# DF_GDB_OPTS: Options to pass to gdb, if it's being run +# DF_VALGRIND_OPTS: Options to pass to valgrind, if it's being run +# DF_HELGRIND_OPTS: Options to pass to helgrind, if it's being run +# DF_RESET_OPTS: Options to pass the reset command at the end of +# this script +# DF_POST_CMD: Shell command to be run at very end of script DF_DIR=$(dirname "$0") cd "${DF_DIR}" export SDL_DISABLE_LOCK_KEYS=1 # Work around for bug in Debian/Ubuntu SDL patch. #export SDL_VIDEO_CENTERED=1 # Centre the screen. Messes up resizing. +# User config files +RC=".dfhackrc" + +if [ -r "$HOME/$RC" ]; then + . $HOME/$RC +fi +if [ -r "./$RC" ]; then + . "./$RC" +fi + +# Now run + case "$1" in -g | --gdb) shift echo "set environment LD_PRELOAD=./libdfhack.so" > gdbcmd.tmp - gdb -x gdbcmd.tmp ./libs/Dwarf_Fortress $* + gdb $DF_GDB_OPTS -x gdbcmd.tmp ./libs/Dwarf_Fortress $* rm gdbcmd.tmp ret=$? ;; -h | --helgrind) shift - LD_PRELOAD=./libdfhack.so valgrind --tool=helgrind --log-file=helgrind.log ./libs/Dwarf_Fortress $* + LD_PRELOAD=./libdfhack.so valgrind $DF_HELGRIND_OPTS --tool=helgrind --log-file=helgrind.log ./libs/Dwarf_Fortress $* ret=$? ;; -v | --valgrind) shift - LD_PRELOAD=./libdfhack.so valgrind --log-file=valgrind.log ./libs/Dwarf_Fortress $* + LD_PRELOAD=./libdfhack.so valgrind $DF_VALGRIND_OPTS --log-file=valgrind.log ./libs/Dwarf_Fortress $* ret=$? ;; *) @@ -35,6 +60,10 @@ case "$1" in esac # Reset terminal to sane state in case of a crash -reset +reset $DF_RESET_OPTS + +if [ -n "$DF_POST_CMD" ]; then + eval $DF_POST_CMD +fi exit $ret diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 2d956b35c..98c70950a 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -108,10 +108,15 @@ if(BUILD_KITTENS) endif() IF(UNIX) - OPTION(BUILD_KILL_GAME "Build the kill gmae plugin." OFF) + OPTION(BUILD_KILL_GAME "Build the kill game plugin." OFF) if(BUILD_KILL_GAME) DFHACK_PLUGIN(die die.cpp) endif() + + OPTION(BUILD_VECTORS "Build the vectors search plugin." OFF) + if(BUILD_VECTORS) + DFHACK_PLUGIN(vectors vectors.cpp) + endif() endif() DFHACK_PLUGIN(reveal reveal.cpp) diff --git a/plugins/vectors.cpp b/plugins/vectors.cpp new file mode 100644 index 000000000..6a447178a --- /dev/null +++ b/plugins/vectors.cpp @@ -0,0 +1,228 @@ +// Lists embeded STL vectors and pointers to STL vectors found in the given +// memory range. +// +// Linux only, enabled with BUILD_VECTORS cmake option. + +#include +#include +#include +#include +#include +#include +#include + +#include + +using std::vector; +using std::string; +using namespace DFHack; + +struct t_vecTriplet +{ + uint32_t start; + uint32_t end; + uint32_t alloc_end; +}; + +DFhackCExport command_result df_vectors (Core * c, vector & parameters); + +DFhackCExport const char * plugin_name ( void ) +{ + return "vectors"; +} + +DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) +{ + commands.clear(); + commands.push_back(PluginCommand("vectors", + "Scan memory for vectors.\ +\n 1st param: start of scan\ +\n 2nd param: number of bytes to scan", + df_vectors)); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( Core * c ) +{ + return CR_OK; +} + +static bool hexOrDec(string &str, uint32_t &value) +{ + if (str.find("0x") == 0 && sscanf(str.c_str(), "%x", &value) == 1) + return true; + else if (sscanf(str.c_str(), "%u", &value) == 1) + return true; + + return false; +} + +static void usage(Console &con) +{ + con << "Usage: vectors <# bytes to scan>" + << std::endl; +} + +static bool mightBeVec(vector &heap_ranges, + t_vecTriplet *vec) +{ + if ((vec->start > vec->end) || (vec->end > vec->alloc_end)) + return false; + + if ((vec->end - vec->start) % 4 != 0) + return false; + + for (size_t i = 0; i < heap_ranges.size(); i++) + { + t_memrange &range = heap_ranges[i]; + + if (range.isInRange(vec->start) && range.isInRange(vec->alloc_end)) + return true; + } + + return false; +} + +static bool inAnyRange(vector &ranges, uint32_t ptr) +{ + for (size_t i = 0; i < ranges.size(); i++) + { + if (ranges[i].isInRange(ptr)) + return true; + } + + return false; +} + +static void printVec(Console &con, const char* msg, t_vecTriplet *vec, + uint32_t start, uint32_t pos) +{ + uint32_t length = vec->end - vec->start; + uint32_t offset = pos - start; + + con.print("%8s offset %06p, addr %010p, start %010p, length %u\n", + msg, offset, pos, vec->start, length); +} + +DFhackCExport command_result df_vectors (Core * c, vector & parameters) +{ + Console & con = c->con; + + if (parameters.size() != 2) + { + usage(con); + return CR_FAILURE; + } + + uint32_t start = 0, bytes = 0; + + if (!hexOrDec(parameters[0], start)) + { + usage(con); + return CR_FAILURE; + } + + if (!hexOrDec(parameters[1], bytes)) + { + usage(con); + return CR_FAILURE; + } + + uint32_t end = start + bytes; + + // 4 byte alignment. + while (start % 4 != 0) + start++; + + c->Suspend(); + + std::vector ranges; + std::vector heap_ranges; + c->p->getMemRanges(ranges); + + bool startInRange = false; + + for (size_t i = 0; i < ranges.size(); i++) + { + t_memrange &range = ranges[i]; + + // Some kernels don't report [heap], and the heap can consist of + // more segments than just the one labeled with [heap], so include + // all segments which *might* be part of the heap. + if (range.read && range.write && !range.shared) + { + if (strlen(range.name) == 0 || strcmp(range.name, "[heap]") == 0) + heap_ranges.push_back(range); + } + + if (!range.isInRange(start)) + continue; + + // Found the range containing the start + if (!range.isInRange(end)) + { + con.print("Scanning %u bytes would read past end of memory " + "range.\n", bytes); + uint32_t diff = end - range.end; + con.print("Cutting bytes down by %u.\n", diff); + + end = (uint32_t) range.end; + } + startInRange = true; + } // for (size_t i = 0; i < ranges.size(); i++) + + if (!startInRange) + { + con << "Address not in any memory range." << std::endl; + c->Resume(); + return CR_FAILURE; + } + + if (heap_ranges.empty()) + { + con << "No possible heap segments." << std::endl; + c->Resume(); + return CR_FAILURE; + } + + uint32_t pos = start; + + const uint32_t ptr_size = sizeof(void*); + + for (uint32_t pos = start; pos < end; pos += ptr_size) + { + // Is it an embeded vector? + if (pos <= ( end - sizeof(t_vecTriplet) )) + { + t_vecTriplet* vec = (t_vecTriplet*) pos; + + if (mightBeVec(heap_ranges, vec)) + { + printVec(con, "VEC:", vec, start, pos); + // Skip over rest of vector. + pos += sizeof(t_vecTriplet) - ptr_size; + continue; + } + } + + // Is it a vector pointer? + if (pos <= (end - ptr_size)) + { + uint32_t ptr = * ( (uint32_t*) pos); + + if (inAnyRange(heap_ranges, ptr)) + { + t_vecTriplet* vec = (t_vecTriplet*) ptr; + + if (mightBeVec(heap_ranges, vec)) + { + printVec(con, "VEC PTR:", vec, start, pos); + continue; + } + } + } + } // for (uint32_t pos = start; pos < end; pos += ptr_size) + + c->Resume(); + return CR_OK; +}