From 79ac2a781a3bfd3395a5785098a16403c1a9d3ce Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 24 Dec 2011 14:51:58 +0400 Subject: [PATCH] Add infrastructure necessary to use the generated headers. As a usage example, allow toggling water level display and idlers, and implement a ui tweak for easily copying stockpiles. Also disable df2mc by default - default options shouldn't require anything not in the base package. --- .gitignore | 1 + Memory.xml | 21 ++++ library/CMakeLists.txt | 3 + library/Core.cpp | 5 + library/DataDefs.cpp | 136 ++++++++++++++++++++++++ library/DataStatics.cpp | 5 + library/include/dfhack/Core.h | 7 ++ library/include/dfhack/DataDefs.h | 151 +++++++++++++++++++++++++++ library/include/dfhack/df/.gitignore | 2 + plugins/CMakeLists.txt | 6 +- plugins/initflags.cpp | 60 +++++++++++ plugins/stockpiles.cpp | 87 +++++++++++++++ 12 files changed, 482 insertions(+), 2 deletions(-) create mode 100644 library/DataDefs.cpp create mode 100644 library/DataStatics.cpp create mode 100644 library/include/dfhack/DataDefs.h create mode 100644 library/include/dfhack/df/.gitignore create mode 100644 plugins/initflags.cpp create mode 100644 plugins/stockpiles.cpp diff --git a/.gitignore b/.gitignore index 702a6490c..a19302fe4 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ build/CMakeCache.txt build/cmake_install.cmake build/CMakeFiles build/doc +build/lua build/bin build/library build/tools diff --git a/Memory.xml b/Memory.xml index 366dc48ba..b4f7ce1f3 100644 --- a/Memory.xml +++ b/Memory.xml @@ -1078,6 +1078,13 @@
+ +
+
+
+
+
+ @@ -2309,6 +2316,13 @@
+ +
+
+
+
+
+ cmake item vector: @@ -3174,6 +3188,13 @@
+ +
+
+
+
+
+ diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 7e24f3659..9a87db9af 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -29,6 +29,7 @@ SET(PROJECT_HDRS include/DFHack.h include/dfhack/Console.h include/dfhack/Core.h +include/dfhack/DataDefs.h include/dfhack/Error.h include/dfhack/Export.h include/dfhack/FakeSDL.h @@ -64,6 +65,8 @@ include/dfhack/modules/Graphic.h SET(PROJECT_SRCS Core.cpp +DataDefs.cpp +DataStatics.cpp PluginManager.cpp TileTypes.cpp VersionInfo.cpp diff --git a/library/Core.cpp b/library/Core.cpp index f218f2756..5296a057e 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -37,6 +37,7 @@ using namespace std; #include "dfhack/Error.h" #include "dfhack/Process.h" #include "dfhack/Core.h" +#include "dfhack/DataDefs.h" #include "dfhack/Console.h" #include "dfhack/Module.h" #include "dfhack/VersionInfoFactory.h" @@ -494,6 +495,10 @@ bool Core::Init() dump << vinfo->PrintOffsets(); dump.close(); } + + // initialize data defs + virtual_identity::Init(); + InitDataDefGlobals(this); // create mutex for syncing with interactive tasks StackMutex = new mutex(); diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp new file mode 100644 index 000000000..2de8c156e --- /dev/null +++ b/library/DataDefs.cpp @@ -0,0 +1,136 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Internal.h" + +#include +#include +#include + +#include "dfhack/Process.h" +#include "dfhack/Core.h" +#include "dfhack/DataDefs.h" +#include "dfhack/VersionInfo.h" +#include "tinythread.h" + +using namespace DFHack; + +/* The order of global object constructor calls is + * undefined between compilation units. Therefore, + * this list has to be plain data, so that it gets + * initialized by the loader in the initial mmap. + */ +virtual_identity *virtual_identity::list = NULL; + +virtual_identity::virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent) + : dfhack_name(dfhack_name), original_name(original_name), parent(parent), + prev(NULL), vtable_ptr(NULL), has_children(true) +{ + // Link into the static list. Nothing else can be safely done at this point. + next = list; list = this; +} + +/* Vtable to identity lookup. */ +static tthread::mutex *known_mutex = NULL; +std::map virtual_identity::known; + +virtual_identity *virtual_identity::get(virtual_class *instance_ptr) +{ + if (!instance_ptr) return NULL; + + // Actually, a reader/writer lock would be sufficient, + // since the table is only written once per class. + tthread::lock_guard lock(*known_mutex); + + void *vtable = get_vtable(instance_ptr); + std::map::iterator it = known.find(vtable); + + if (it != known.end()) + return it->second; + + // If using a reader/writer lock, re-grab as write here, and recheck + Core &core = Core::getInstance(); + std::string name = core.p->doReadClassName(vtable); + + virtual_identity *actual = NULL; + + for (virtual_identity *p = list; p; p = p->next) { + if (strcmp(name.c_str(), p->getOriginalName()) != 0) continue; + known[vtable] = p; + p->vtable_ptr = vtable; + return p; + } + + known[vtable] = NULL; + return NULL; +} + +bool virtual_identity::check_instance(virtual_class *instance_ptr, bool allow_subclasses) +{ + virtual_identity *actual = get(instance_ptr); + + if (actual == this) return true; + if (!allow_subclasses || !actual) return false; + + do { + actual = actual->parent; + if (actual == this) return true; + } while (actual); + + return false; +} + +void virtual_identity::Init() +{ + if (!known_mutex) + known_mutex = new tthread::mutex(); + + // This cannot be done in the constructors, because + // they are called in an undefined order. + for (virtual_identity *p = list; p; p = p->next) { + p->has_children = false; + p->children.clear(); + } + for (virtual_identity *p = list; p; p = p->next) { + if (p->parent) { + p->parent->children.push_back(p); + p->parent->has_children = true; + } + } +} + +#define GLOBAL(name,tname) \ + df::tname *df::global::name = NULL; +DF_KNOWN_GLOBALS +#undef GLOBAL + +void DFHack::InitDataDefGlobals(Core *core) { + OffsetGroup *global_table = core->vinfo->getGroup("global"); + uint32_t tmp; + +#define GLOBAL(name,tname) \ + if (global_table->getSafeAddress(#name,tmp)) df::global::name = (df::tname*)tmp; +DF_KNOWN_GLOBALS +#undef GLOBAL +} diff --git a/library/DataStatics.cpp b/library/DataStatics.cpp new file mode 100644 index 000000000..57aeb779c --- /dev/null +++ b/library/DataStatics.cpp @@ -0,0 +1,5 @@ +#include "Internal.h" +#include "dfhack/DataDefs.h" + +// Instantiate all the static objects +#include "dfhack/df/static.inc" diff --git a/library/include/dfhack/Core.h b/library/include/dfhack/Core.h index 2d7941af2..64eae5d3e 100644 --- a/library/include/dfhack/Core.h +++ b/library/include/dfhack/Core.h @@ -190,4 +190,11 @@ namespace DFHack tthread::mutex * misc_data_mutex; std::map misc_data_map; }; + + class CoreSuspender { + Core *core; + public: + CoreSuspender(Core *core) : core(core) { core->Suspend(); } + ~CoreSuspender() { core->Resume(); } + }; } diff --git a/library/include/dfhack/DataDefs.h b/library/include/dfhack/DataDefs.h new file mode 100644 index 000000000..aa325958c --- /dev/null +++ b/library/include/dfhack/DataDefs.h @@ -0,0 +1,151 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#pragma once + +#include +#include +#include + +#include "dfhack/Core.h" +#include "dfhack/BitArray.h" + +namespace DFHack +{ + class virtual_class {}; + + class DFHACK_EXPORT virtual_identity { + static virtual_identity *list; + static std::map known; + + virtual_identity *prev, *next; + const char *dfhack_name; + const char *original_name; + virtual_identity *parent; + std::vector children; + + void *vtable_ptr; + bool has_children; + + protected: + virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent); + + bool check_instance(virtual_class *instance_ptr, bool allow_subclasses); + + static void *get_vtable(virtual_class *instance_ptr) { return *(void**)instance_ptr; } + + public: + const char *getName() { return dfhack_name; } + const char *getOriginalName() { return original_name ? original_name : dfhack_name; } + + virtual_identity *getParent() { return parent; } + const std::vector &getChildren() { return children; } + + static virtual_identity *get(virtual_class *instance_ptr); + + bool is_instance(virtual_class *instance_ptr) { + if (!instance_ptr) return false; + if (vtable_ptr) { + void *vtable = get_vtable(instance_ptr); + if (vtable == vtable_ptr) return true; + if (!has_children) return false; + } + return check_instance(instance_ptr, true); + } + + bool is_direct_instance(virtual_class *instance_ptr) { + if (!instance_ptr) return false; + return vtable_ptr ? (vtable_ptr == get_vtable(instance_ptr)) + : check_instance(instance_ptr, false); + } + + static void Init(); + }; + + template + inline T *virtual_cast(virtual_class *ptr) { + return T::_identity.is_instance(ptr) ? static_cast(ptr) : NULL; + } + + template + inline T *strict_virtual_cast(virtual_class *ptr) { + return T::_identity.is_direct_instance(ptr) ? static_cast(ptr) : NULL; + } + + void InitDataDefGlobals(Core *core); + + template + T *ifnull(T *a, T *b) { return a ? a : b; } +} + +namespace df +{ + using DFHack::virtual_class; + using DFHack::BitArray; + + template + class class_virtual_identity : public DFHack::virtual_identity { + public: + class_virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent) + : virtual_identity(dfhack_name, original_name, parent) {}; + }; + + template + struct enum_field { + IntType value; + + enum_field() {} + enum_field(EnumType ev) : value(IntType(ev)) {} + template + enum_field(enum_field ev) : value(IntType(ev.value)) {} + + operator EnumType () { return EnumType(value); } + enum_field &operator=(EnumType ev) { + value = IntType(ev); return *this; + } + }; + + namespace enums {} +} + +#define ENUM_ATTR(enum,attr,val) (df::enums::enum::get_##attr(val)) +#define ENUM_ATTR_STR(enum,attr,val) DFHack::ifnull(ENUM_ATTR(enum,attr,val),"?") +#define ENUM_KEY_STR(enum,val) ENUM_ATTR_STR(enum,key,val) +#define ENUM_FIRST_ITEM(enum) (df::enums::enum::_first_item_of_##enum) +#define ENUM_LAST_ITEM(enum) (df::enums::enum::_last_item_of_##enum) + +namespace df { +#define DF_KNOWN_GLOBALS \ + GLOBAL(world,world) \ + GLOBAL(ui,ui) \ + GLOBAL(gview,interface) \ + GLOBAL(init,init) \ + GLOBAL(d_init,d_init) + +#define GLOBAL(name,tname) \ + struct tname; \ + namespace global { extern DFHACK_EXPORT tname *name; } +DF_KNOWN_GLOBALS +#undef GLOBAL +} diff --git a/library/include/dfhack/df/.gitignore b/library/include/dfhack/df/.gitignore new file mode 100644 index 000000000..11c5ffbb1 --- /dev/null +++ b/library/include/dfhack/df/.gitignore @@ -0,0 +1,2 @@ +*.h +*.inc diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index c08bcd9e1..ab7e039a4 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -30,7 +30,7 @@ if(BUILD_SERVER) add_subdirectory (server) endif() -OPTION(BUILD_DF2MC "Build DF2MC (needs a checkout first)." ON) +OPTION(BUILD_DF2MC "Build DF2MC (needs a checkout first)." OFF) if(BUILD_DF2MC) add_subdirectory (df2mc) endif() @@ -57,7 +57,9 @@ DFHACK_PLUGIN(deramp deramp.cpp) DFHACK_PLUGIN(flows flows.cpp) DFHACK_PLUGIN(filltraffic filltraffic.cpp) DFHACK_PLUGIN(seedwatch seedwatch.cpp) -DFHACK_PLUGIN(versionosd versionosd.cpp) +DFHACK_PLUGIN(initflags initflags.cpp) +DFHACK_PLUGIN(stockpiles stockpiles.cpp) +#DFHACK_PLUGIN(versionosd versionosd.cpp) # this is the skeleton plugin. If you want to make your own, make a copy and then change it OPTION(BUILD_SKELETON "Build the skeleton plugin." OFF) diff --git a/plugins/initflags.cpp b/plugins/initflags.cpp new file mode 100644 index 000000000..f27a318ae --- /dev/null +++ b/plugins/initflags.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include +#include + +using std::vector; +using std::string; +using std::endl; +using namespace DFHack; +using namespace df::enums; + +using df::global::d_init; + +DFhackCExport command_result twaterlvl(Core * c, vector & parameters); +DFhackCExport command_result tidlers(Core * c, vector & parameters); + +DFhackCExport const char * plugin_name ( void ) +{ + return "initflags"; +} + +DFhackCExport command_result plugin_init (Core *c, std::vector &commands) +{ + commands.clear(); + if (d_init) { + commands.push_back(PluginCommand("twaterlvl", "Toggle display of water/magma depth.", twaterlvl)); + commands.push_back(PluginCommand("tidlers", "Toggle display of idlers.", tidlers)); + } + std::cerr << "d_init: " << sizeof(df::d_init) << endl; + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( Core * c ) +{ + return CR_OK; +} + +DFhackCExport command_result twaterlvl(Core * c, vector & parameters) +{ + c->Suspend(); + df::global::d_init->flags1.toggle(d_init_flags1::SHOW_FLOW_AMOUNTS); + c->con << "Toggled the display of water/magma depth." << endl; + c->Resume(); + return CR_OK; +} + +DFhackCExport command_result tidlers(Core * c, vector & parameters) +{ + c->Suspend(); + df::d_init_idlers iv = df::d_init_idlers(int(d_init->idlers) + 1); + if (!d_init_idlers::is_valid(iv)) + iv = ENUM_FIRST_ITEM(d_init_idlers); + d_init->idlers = iv; + c->con << "Toggled the display of idlers to " << ENUM_KEY_STR(d_init_idlers, iv) << endl; + c->Resume(); + return CR_OK; +} diff --git a/plugins/stockpiles.cpp b/plugins/stockpiles.cpp new file mode 100644 index 000000000..5cd153335 --- /dev/null +++ b/plugins/stockpiles.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +using std::vector; +using std::string; +using std::endl; +using namespace DFHack; +using namespace df::enums; + +using df::global::world; +using df::global::ui; + +using df::building_stockpilest; + +DFhackCExport command_result copystock(Core * c, vector & parameters); + +DFhackCExport const char * plugin_name ( void ) +{ + return "stockpiles"; +} + +DFhackCExport command_result plugin_init (Core *c, std::vector &commands) +{ + commands.clear(); + if (world && ui) { + commands.push_back(PluginCommand("copystock", "Copy stockpile under cursor.", copystock)); + } + std::cerr << "world: " << sizeof(df::world) << " ui: " << sizeof(df::ui) + << " b_stock: " << sizeof(building_stockpilest) << endl; + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( Core * c ) +{ + return CR_OK; +} + +bool inSelectMode() { + using namespace ui_sidebar_mode; + + switch (ui->main.mode) { + case BuildingItems: + case QueryBuilding: + return true; + default: + return false; + } +} + +DFhackCExport command_result copystock(Core * c, vector & parameters) +{ + CoreSuspender suspend(c); + + // For convenience: when used in the stockpiles mode, switch to 'q' + if (ui->main.mode == ui_sidebar_mode::Stockpiles) { + world->selected_building = NULL; // just in case it contains some kind of garbage + ui->main.mode = ui_sidebar_mode::QueryBuilding; + + c->con << "Switched back to query building." << endl; + return CR_OK; + } + + if (!inSelectMode()) { + c->con << "Cannot copy stockpile in mode " << ENUM_KEY_STR(ui_sidebar_mode, ui->main.mode) << endl; + return CR_OK; + } + + building_stockpilest *sp = virtual_cast(world->selected_building); + if (!sp) { + c->con << "Selected building isn't a stockpile." << endl; + return CR_OK; + } + + ui->stockpile.custom_settings = sp->settings; + ui->main.mode = ui_sidebar_mode::Stockpiles; + world->selected_stockpile_type = stockpile_category::Custom; + + c->con << "Stockpile options copied." << endl; + return CR_OK; +}