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;
+}