summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPetr Mrázek2012-03-23 22:59:28 +0100
committerPetr Mrázek2012-03-23 22:59:28 +0100
commitf549f4e54511a840966c7d81ff9a83cd779ae6b1 (patch)
treea1cc59d7d27871911dc56771bb932b39c727fccd
parent1311f4fbf99ef66b6f2ad10e7eb43e7cbe6eb876 (diff)
parente10b1a50a352fe63ca674310673c8b19dcee48fa (diff)
downloaddfhack-f549f4e54511a840966c7d81ff9a83cd779ae6b1.tar.gz
dfhack-f549f4e54511a840966c7d81ff9a83cd779ae6b1.tar.bz2
dfhack-f549f4e54511a840966c7d81ff9a83cd779ae6b1.tar.xz
Merge https://github.com/warmist/dfhack
-rw-r--r--CMakeLists.txt1
-rw-r--r--depends/lua/CMakeLists.txt10
-rw-r--r--depends/lua/include/luaconf.h15
-rw-r--r--library/CMakeLists.txt12
-rw-r--r--library/DataDefs.cpp214
-rw-r--r--library/DataStatics.cpp4
-rw-r--r--library/DataStaticsFields.cpp49
-rw-r--r--library/LuaWrapper.cpp1722
-rw-r--r--library/include/DataDefs.h321
-rw-r--r--library/include/DataIdentity.h438
-rw-r--r--library/proto/BasicApi.proto8
-rw-r--r--library/proto/CoreProtocol.proto6
m---------library/xml0
-rw-r--r--plugins/Dfusion/luafiles/common.lua27
-rw-r--r--plugins/Dfusion/luafiles/init.lua3
-rw-r--r--plugins/Dfusion/luafiles/onfunction/locations.lua9
-rw-r--r--plugins/Dfusion/luafiles/tools/init.lua5
-rw-r--r--plugins/proto/rename.proto2
18 files changed, 2731 insertions, 115 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1199f289..60b4ab65 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -103,6 +103,7 @@ ENDIF()
# use shared libraries for protobuf
ADD_DEFINITIONS(-DPROTOBUF_USE_DLLS)
+ADD_DEFINITIONS(-DLUA_BUILD_AS_DLL)
if(UNIX)
add_definitions(-D_LINUX)
diff --git a/depends/lua/CMakeLists.txt b/depends/lua/CMakeLists.txt
index b135f221..23ea6a48 100644
--- a/depends/lua/CMakeLists.txt
+++ b/depends/lua/CMakeLists.txt
@@ -1,6 +1,9 @@
PROJECT ( lua C )
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+# TODO: make this RelWithDebInfo only
+ADD_DEFINITIONS(-DLUA_USE_APICHECK)
+
IF(WIN32)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE )
ELSE()
@@ -76,8 +79,13 @@ src/lzio.c
)
LIST(APPEND SRC_LIBLUA ${HDR_LIBLUA})
-ADD_LIBRARY ( lua STATIC EXCLUDE_FROM_ALL ${SRC_LIBLUA} )
+ADD_LIBRARY ( lua SHARED EXCLUDE_FROM_ALL ${SRC_LIBLUA} )
TARGET_LINK_LIBRARIES ( lua ${LIBS})
+
+install(TARGETS lua
+ LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION}
+ RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION})
+
IDE_FOLDER(lua "Depends")
#SET ( SRC_LUA src/lua.c )
diff --git a/depends/lua/include/luaconf.h b/depends/lua/include/luaconf.h
index 66079335..b202967b 100644
--- a/depends/lua/include/luaconf.h
+++ b/depends/lua/include/luaconf.h
@@ -151,11 +151,20 @@
** the libraries, you may want to use the following definition (define
** LUA_BUILD_AS_DLL to get it).
*/
+#ifdef __cplusplus
+ #define LUA_API_EXTERN extern "C"
+#else
+ #define LUA_API_EXTERN extern
+#endif
#if defined(LUA_BUILD_AS_DLL)
- #if defined(LUA_CORE) || defined(LUA_LIB)
- #define LUA_API __declspec(dllexport)
+ #if defined(_MSC_VER)
+ #if defined(LUA_CORE) || defined(LUA_LIB)
+ #define LUA_API __declspec(dllexport) LUA_API_EXTERN
+ #else
+ #define LUA_API __declspec(dllimport) LUA_API_EXTERN
+ #endif
#else
- #define LUA_API __declspec(dllimport)
+ #define LUA_API LUA_API_EXTERN __attribute__ ((visibility("default")))
#endif
#else
#ifdef __cplusplus
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index 63107456..72bf91e6 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -55,8 +55,10 @@ SET(MAIN_SOURCES
Core.cpp
ColorText.cpp
DataDefs.cpp
+LuaWrapper.cpp
DataStatics.cpp
DataStaticsCtor.cpp
+DataStaticsFields.cpp
MiscUtils.cpp
PluginManager.cpp
TileTypes.cpp
@@ -195,6 +197,14 @@ ADD_CUSTOM_COMMAND(
ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/static.inc)
+IF(UNIX)
+ # Don't produce debug info for generated stubs
+ SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp
+ PROPERTIES COMPILE_FLAGS "-g0 -O1")
+ELSE(WIN32)
+ENDIF()
+
+
# Compilation
ADD_DEFINITIONS(-DBUILD_DFHACK_LIB)
@@ -242,7 +252,7 @@ ENDIF()
#effectively disables debug builds...
SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" )
-TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket ${PROJECT_LIBS})
+TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua ${PROJECT_LIBS})
SET_TARGET_PROPERTIES(dfhack PROPERTIES LINK_INTERFACE_LIBRARIES "")
TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket)
diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp
index 30a4da27..6fcd1141 100644
--- a/library/DataDefs.cpp
+++ b/library/DataDefs.cpp
@@ -34,28 +34,186 @@ distribution.
#include "tinythread.h"
// must be last due to MS stupidity
#include "DataDefs.h"
+#include "DataIdentity.h"
#include "MiscUtils.h"
using namespace DFHack;
+
+void *type_identity::do_allocate_pod() {
+ void *p = malloc(size);
+ memset(p, 0, size);
+ return p;
+}
+
+void type_identity::do_copy_pod(void *tgt, const void *src) {
+ memmove(tgt, src, size);
+};
+
+void *type_identity::allocate() {
+ if (can_allocate())
+ return do_allocate();
+ else
+ return NULL;
+}
+
+bool type_identity::copy(void *tgt, const void *src) {
+ if (can_allocate() && tgt && src)
+ do_copy(tgt, src);
+ else
+ return false;
+}
+
+void *enum_identity::do_allocate() {
+ void *p = malloc(byte_size());
+ memcpy(p, &first_item_value, std::min(byte_size(), sizeof(int64_t)));
+ return p;
+}
+
/* 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;
+compound_identity *compound_identity::list = NULL;
+std::vector<compound_identity*> compound_identity::top_scope;
-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)
+compound_identity::compound_identity(size_t size, TAllocateFn alloc,
+ compound_identity *scope_parent, const char *dfhack_name)
+ : constructed_identity(size, alloc), scope_parent(scope_parent), dfhack_name(dfhack_name)
{
- // Link into the static list. Nothing else can be safely done at this point.
- next = list; list = this;
+ next = list; list = this;
+}
+
+void compound_identity::doInit(Core *)
+{
+ if (scope_parent)
+ scope_parent->scope_children.push_back(this);
+ else
+ top_scope.push_back(this);
+}
+
+std::string compound_identity::getFullName()
+{
+ if (scope_parent)
+ return scope_parent->getFullName() + "." + getName();
+ else
+ return getName();
}
-/* Vtable to identity lookup. */
static tthread::mutex *known_mutex = NULL;
+
+void compound_identity::Init(Core *core)
+{
+ if (!known_mutex)
+ known_mutex = new tthread::mutex();
+
+ // This cannot be done in the constructors, because
+ // they are called in an undefined order.
+ for (compound_identity *p = list; p; p = p->next)
+ p->doInit(core);
+
+ //FIXME: ... nuked. the group was empty...
+/*
+ // Read pre-filled vtable ptrs
+ OffsetGroup *ptr_table = core->vinfo->getGroup("vtable");
+ for (virtual_identity *p = list; p; p = p->next) {
+ void * tmp;
+ if (ptr_table->getSafeAddress(p->getName(),tmp))
+ p->vtable_ptr = tmp;
+ }
+ */
+}
+
+bitfield_identity::bitfield_identity(size_t size,
+ compound_identity *scope_parent, const char *dfhack_name,
+ int num_bits, const bitfield_item_info *bits)
+ : compound_identity(size, NULL, scope_parent, dfhack_name), bits(bits), num_bits(num_bits)
+{
+}
+
+enum_identity::enum_identity(size_t size,
+ compound_identity *scope_parent, const char *dfhack_name,
+ type_identity *base_type,
+ int64_t first_item_value, int64_t last_item_value,
+ const char *const *keys)
+ : compound_identity(size, NULL, scope_parent, dfhack_name),
+ first_item_value(first_item_value), last_item_value(last_item_value),
+ keys(keys), base_type(base_type)
+{
+}
+
+struct_identity::struct_identity(size_t size, TAllocateFn alloc,
+ compound_identity *scope_parent, const char *dfhack_name,
+ struct_identity *parent, const struct_field_info *fields)
+ : compound_identity(size, alloc, scope_parent, dfhack_name),
+ parent(parent), has_children(false), fields(fields)
+{
+}
+
+void struct_identity::doInit(Core *core)
+{
+ compound_identity::doInit(core);
+
+ if (parent) {
+ parent->children.push_back(this);
+ parent->has_children = true;
+ }
+}
+
+bool struct_identity::is_subclass(struct_identity *actual)
+{
+ for (; actual; actual = actual->getParent())
+ if (actual == this) return true;
+
+ return false;
+}
+
+std::string pointer_identity::getFullName()
+{
+ return (target ? target->getFullName() : std::string("void")) + "*";
+}
+
+std::string container_identity::getFullName(type_identity *item)
+{
+ return "<" + (item ? item->getFullName() : std::string("void")) + ">";
+}
+
+std::string ptr_container_identity::getFullName(type_identity *item)
+{
+ return "<" + (item ? item->getFullName() : std::string("void")) + "*>";
+}
+
+std::string bit_container_identity::getFullName(type_identity *)
+{
+ return "<bool>";
+}
+
+std::string df::buffer_container_identity::getFullName(type_identity *item)
+{
+ return (item ? item->getFullName() : std::string("void")) +
+ (size > 0 ? stl_sprintf("[%d]", size) : std::string("[]"));
+}
+
+virtual_identity::virtual_identity(size_t size, TAllocateFn alloc,
+ const char *dfhack_name, const char *original_name,
+ virtual_identity *parent, const struct_field_info *fields)
+ : struct_identity(size, alloc, NULL, dfhack_name, parent, fields), original_name(original_name),
+ vtable_ptr(NULL)
+{
+}
+
+static std::map<std::string, virtual_identity*> name_lookup;
+
+void virtual_identity::doInit(Core *core)
+{
+ struct_identity::doInit(core);
+
+ name_lookup[getOriginalName()] = this;
+}
+
+/* Vtable to identity lookup. */
std::map<void*, virtual_identity*> virtual_identity::known;
virtual_identity *virtual_identity::get(virtual_ptr instance_ptr)
@@ -78,8 +236,9 @@ virtual_identity *virtual_identity::get(virtual_ptr instance_ptr)
virtual_identity *actual = NULL;
- for (virtual_identity *p = list; p; p = p->next) {
- if (strcmp(name.c_str(), p->getOriginalName()) != 0) continue;
+ auto name_it = name_lookup.find(name);
+ if (name_it != name_lookup.end()) {
+ virtual_identity *p = name_it->second;
if (p->vtable_ptr && p->vtable_ptr != vtable) {
std::cerr << "Conflicting vtable ptr for class '" << p->getName()
@@ -103,14 +262,6 @@ virtual_identity *virtual_identity::get(virtual_ptr instance_ptr)
return NULL;
}
-bool virtual_identity::is_subclass(virtual_identity *actual)
-{
- for (; actual; actual = actual->parent)
- if (actual == this) return true;
-
- return false;
-}
-
void virtual_identity::adjust_vtable(virtual_ptr obj, virtual_identity *main)
{
if (vtable_ptr) {
@@ -135,35 +286,6 @@ virtual_ptr virtual_identity::clone(virtual_ptr obj)
return copy;
}
-void virtual_identity::Init(Core *core)
-{
- 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;
- }
- }
- //FIXME: ... nuked. the group was empty...
-/*
- // Read pre-filled vtable ptrs
- OffsetGroup *ptr_table = core->vinfo->getGroup("vtable");
- for (virtual_identity *p = list; p; p = p->next) {
- void * tmp;
- if (ptr_table->getSafeAddress(p->getName(),tmp))
- p->vtable_ptr = tmp;
- }
- */
-}
-
bool DFHack::findBitfieldField(unsigned *idx, const std::string &name,
unsigned size, const bitfield_item_info *items)
{
diff --git a/library/DataStatics.cpp b/library/DataStatics.cpp
index 5ccabb7e..1e4b21be 100644
--- a/library/DataStatics.cpp
+++ b/library/DataStatics.cpp
@@ -7,6 +7,8 @@
#include "df/world_data.h"
#include "df/ui.h"
+#include "DataIdentity.h"
+
namespace {
template<class T>
inline T &_toref(T &r) { return r; }
@@ -21,6 +23,8 @@ namespace {
#define INIT_GLOBAL_FUNCTION_ITEM(type,name) \
if (global_table_->getAddress(#name,tmp_)) name = (type*)tmp_;
+#define TID(type) (&identity_traits< type >::identity)
+
// Instantiate all the static objects
#include "df/static.inc"
#include "df/static.enums.inc"
diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp
new file mode 100644
index 00000000..89736369
--- /dev/null
+++ b/library/DataStaticsFields.cpp
@@ -0,0 +1,49 @@
+#include "Internal.h"
+#include "DataDefs.h"
+#include "MiscUtils.h"
+#include "VersionInfo.h"
+
+#include "df/world.h"
+#include "df/world_data.h"
+#include "df/ui.h"
+
+#include "DataIdentity.h"
+
+#include <stddef.h>
+
+#pragma GCC diagnostic ignored "-Winvalid-offsetof"
+
+namespace df {
+#define NUMBER_IDENTITY_TRAITS(type) \
+ number_identity<type> identity_traits<type>::identity(#type);
+
+ NUMBER_IDENTITY_TRAITS(char);
+ NUMBER_IDENTITY_TRAITS(int8_t);
+ NUMBER_IDENTITY_TRAITS(uint8_t);
+ NUMBER_IDENTITY_TRAITS(int16_t);
+ NUMBER_IDENTITY_TRAITS(uint16_t);
+ NUMBER_IDENTITY_TRAITS(int32_t);
+ NUMBER_IDENTITY_TRAITS(uint32_t);
+ NUMBER_IDENTITY_TRAITS(int64_t);
+ NUMBER_IDENTITY_TRAITS(uint64_t);
+ NUMBER_IDENTITY_TRAITS(float);
+
+ bool_identity identity_traits<bool>::identity;
+ stl_string_identity identity_traits<std::string>::identity;
+ pointer_identity identity_traits<void*>::identity;
+ stl_ptr_vector_identity identity_traits<std::vector<void*> >::identity;
+ stl_bit_vector_identity identity_traits<std::vector<bool> >::identity;
+
+ buffer_container_identity buffer_container_identity::base_instance;
+
+#undef NUMBER_IDENTITY_TRAITS
+}
+
+#define TID(type) (&identity_traits< type >::identity)
+
+#define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name)
+#define GFLD(mode, name) struct_field_info::mode, #name, (size_t)&df::global::name
+#define FLD_END struct_field_info::END
+
+// Field definitions
+#include "df/static.fields.inc"
diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp
new file mode 100644
index 00000000..d6edef4e
--- /dev/null
+++ b/library/LuaWrapper.cpp
@@ -0,0 +1,1722 @@
+/*
+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 <string>
+#include <vector>
+#include <map>
+
+#include "MemAccess.h"
+#include "Core.h"
+#include "VersionInfo.h"
+#include "tinythread.h"
+// must be last due to MS stupidity
+#include "DataDefs.h"
+#include "DataIdentity.h"
+
+#include "MiscUtils.h"
+
+#include <lua.h>
+#include <lauxlib.h>
+
+using namespace DFHack;
+
+static luaL_Reg no_functions[] = { { NULL, NULL } };
+
+inline void lua_dup(lua_State *state) { lua_pushvalue(state, -1); }
+inline void lua_swap(lua_State *state) { lua_insert(state, -2); }
+
+/*
+ * Registry name: hash of type metatables <-> type identities.
+ */
+#define DFHACK_TYPETABLE_NAME "DFHack::DFTypes"
+
+/*
+ * Registry name: hash of type identity -> node in df.etc...
+ */
+#define DFHACK_TYPEID_TABLE_NAME "DFHack::DFTypeIds"
+
+/*
+ * Registry name: hash of enum/bitfield identity -> index lookup table
+ */
+#define DFHACK_ENUM_TABLE_NAME "DFHack::DFEnums"
+
+/*
+ * Registry name: hash of pointer target identity <-> adhoc pointer identity userdata.
+ */
+#define DFHACK_PTR_IDTABLE_NAME "DFHack::PtrDFTypes"
+
+// Function registry names
+#define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError"
+#define DFHACK_COMPARE_NAME "DFHack::ComparePtrs"
+#define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString"
+#define DFHACK_SIZEOF_NAME "DFHack::Sizeof"
+#define DFHACK_DISPLACE_NAME "DFHack::Displace"
+
+/*
+ * Upvalue: contents of DFHACK_TYPETABLE_NAME
+ */
+#define UPVAL_TYPETABLE lua_upvalueindex(1)
+
+/*
+ * Expected metatable of the current object.
+ */
+#define UPVAL_METATABLE lua_upvalueindex(2)
+
+/*
+ * Table mapping field names to indices or data structure pointers.
+ * Enum index table is linked into here via getmetatable($).__index.
+ * Fields that are actually in UPVAL_METATABLE are marked with NULL light udata.
+ */
+#define UPVAL_FIELDTABLE lua_upvalueindex(3)
+
+/*
+ * Only for containers: light udata with container identity.
+ */
+#define UPVAL_CONTAINER_ID lua_upvalueindex(4)
+
+/*
+ * Only for containers: light udata with item identity.
+ */
+#define UPVAL_ITEM_ID lua_upvalueindex(5)
+
+/*
+ * Only for containers: if not nil, overrides the item count.
+ */
+#define UPVAL_ITEM_COUNT lua_upvalueindex(6)
+
+namespace {
+ /**
+ * Object references are represented as userdata instances
+ * with an appropriate metatable; the payload of userdata is
+ * this structure:
+ */
+ struct DFRefHeader {
+ void *ptr;
+ };
+
+ /*
+ * The system might be extended to carry some simple
+ * objects inline inside the reference buffer.
+ */
+ inline bool is_self_contained(DFRefHeader *ptr) {
+ void **pp = &ptr->ptr;
+ return **(void****)pp == (pp + 1);
+ }
+}
+
+/**
+ * Report an error while accessing a field (index = field name).
+ */
+static void field_error(lua_State *state, int index, const char *err, const char *mode)
+{
+ lua_getfield(state, UPVAL_METATABLE, "__metatable");
+ const char *cname = lua_tostring(state, -1);
+ const char *fname = index ? lua_tostring(state, index) : "*";
+ luaL_error(state, "Cannot %s field %s.%s: %s.",
+ mode, (cname ? cname : "?"), (fname ? fname : "?"), err);
+}
+
+/*
+ * If is_method is true, these use UPVAL_TYPETABLE to save a hash lookup.
+ */
+static void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method = true);
+static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method = true);
+
+void DFHack::PushDFObject(lua_State *state, type_identity *type, void *ptr)
+{
+ push_object_internal(state, type, ptr, false);
+}
+
+void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index)
+{
+ return get_object_internal(state, type, val_index, false);
+}
+
+static void push_adhoc_pointer(lua_State *state, void *ptr, type_identity *target);
+
+/**************************************
+ * Identity object read/write methods *
+ **************************************/
+
+void constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
+{
+ push_object_internal(state, this, ptr);
+}
+
+void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
+{
+ field_error(state, fname_idx, "complex object", "write");
+}
+
+void enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
+{
+ base_type->lua_read(state, fname_idx, ptr);
+}
+
+void enum_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
+{
+ base_type->lua_write(state, fname_idx, ptr, val_index);
+}
+
+void df::number_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr)
+{
+ lua_pushnumber(state, read(ptr));
+}
+
+void df::number_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
+{
+ if (!lua_isnumber(state, val_index))
+ field_error(state, fname_idx, "number expected", "write");
+
+ write(ptr, lua_tonumber(state, val_index));
+}
+
+void df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
+{
+ lua_pushboolean(state, *(bool*)ptr);
+}
+
+void df::bool_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
+{
+ char *pb = (char*)ptr;
+
+ if (lua_isboolean(state, val_index) || lua_isnil(state, val_index))
+ *pb = lua_toboolean(state, val_index);
+ else if (lua_isnumber(state, val_index))
+ *pb = lua_tointeger(state, val_index);
+ else
+ field_error(state, fname_idx, "boolean or number expected", "write");
+}
+
+void df::stl_string_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
+{
+ auto pstr = (std::string*)ptr;
+ lua_pushlstring(state, pstr->data(), pstr->size());
+}
+
+void df::stl_string_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
+{
+ size_t size;
+ const char *bytes = lua_tolstring(state, val_index, &size);
+ if (!bytes)
+ field_error(state, fname_idx, "string expected", "write");
+
+ *(std::string*)ptr = std::string(bytes, size);
+}
+
+void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target)
+{
+ push_object_internal(state, target, *(void**)ptr);
+}
+
+void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr)
+{
+ lua_read(state, fname_idx, ptr, target);
+}
+
+void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr,
+ type_identity *target, int val_index)
+{
+ auto pptr = (void**)ptr;
+
+ if (lua_isnil(state, val_index))
+ *pptr = NULL;
+ else
+ {
+ void *nval = get_object_internal(state, target, val_index);
+ if (nval)
+ *pptr = nval;
+ else
+ field_error(state, fname_idx, "incompatible pointer type", "write");
+ }
+}
+
+void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index)
+{
+ lua_write(state, fname_idx, ptr, target, val_index);
+}
+
+int container_identity::lua_item_count(lua_State *state, void *ptr)
+{
+ if (lua_isnumber(state, UPVAL_ITEM_COUNT))
+ return lua_tointeger(state, UPVAL_ITEM_COUNT);
+ else
+ return item_count(ptr);
+}
+
+void container_identity::lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx)
+{
+ auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
+ void *pitem = item_pointer(id, ptr, idx);
+ push_object_internal(state, id, pitem);
+}
+
+void container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx)
+{
+ auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
+ void *pitem = item_pointer(id, ptr, idx);
+ id->lua_read(state, fname_idx, pitem);
+}
+
+void container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index)
+{
+ auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
+ void *pitem = item_pointer(id, ptr, idx);
+ id->lua_write(state, fname_idx, pitem, val_index);
+}
+
+void ptr_container_identity::lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx)
+{
+ auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
+ void *pitem = item_pointer(id, ptr, idx);
+ push_adhoc_pointer(state, pitem, id);
+}
+
+void ptr_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx)
+{
+ auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
+ void *pitem = item_pointer(&df::identity_traits<void*>::identity, ptr, idx);
+ df::pointer_identity::lua_read(state, fname_idx, pitem, id);
+}
+
+void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index)
+{
+ auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID);
+ void *pitem = item_pointer(&df::identity_traits<void*>::identity, ptr, idx);
+ df::pointer_identity::lua_write(state, fname_idx, pitem, id, val_index);
+}
+
+void bit_container_identity::lua_item_reference(lua_State *state, int, void *, int)
+{
+ lua_pushnil(state);
+}
+
+void bit_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx)
+{
+ lua_pushboolean(state, get_item(ptr, idx));
+}
+
+void bit_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index)
+{
+ if (lua_isboolean(state, val_index) || lua_isnil(state, val_index))
+ set_item(ptr, idx, lua_toboolean(state, val_index));
+ else if (lua_isnumber(state, val_index))
+ set_item(ptr, idx, lua_tointeger(state, val_index) != 0);
+ else
+ field_error(state, fname_idx, "boolean or number expected", "write");
+}
+
+/* */
+
+static int change_error(lua_State *state)
+{
+ luaL_error(state, "Attempt to change a read-only table.\n");
+ return 0;
+}
+
+/**
+ * Wrap a table so that it can't be modified.
+ */
+static void freeze_table(lua_State *state, bool leave_metatable = false, const char *name = NULL)
+{
+ // rv = {}; setmetatable(rv, { __index = in, __newindex = change_error, __metatable = name })
+ int base = lua_gettop(state);
+ lua_newtable(state);
+ lua_swap(state);
+ lua_setfield(state, base, "__index");
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME);
+ lua_setfield(state, base, "__newindex");
+ lua_newtable(state);
+ lua_swap(state);
+ lua_dup(state);
+ lua_setmetatable(state, base);
+ if (name)
+ {
+ lua_pushstring(state, name);
+ lua_setfield(state, -2, "__metatable");
+ }
+ // result: [frozen table] [metatable]
+ if (!leave_metatable)
+ lua_pop(state, 1);
+}
+
+static void LookupInTable(lua_State *state, const char *tname)
+{
+ lua_getfield(state, LUA_REGISTRYINDEX, tname);
+ lua_swap(state);
+ lua_rawget(state, -2);
+ lua_remove(state, -2);
+}
+
+/**
+ * Look up the key on the stack in DFHACK_TYPETABLE;
+ * if found, put result on the stack and return true.
+ */
+static bool LookupTypeInfo(lua_State *state, bool in_method)
+{
+ // stack: [lookup key]
+
+ if (in_method)
+ {
+ lua_rawget(state, UPVAL_TYPETABLE);
+ }
+ else
+ {
+ LookupInTable(state, DFHACK_TYPETABLE_NAME);
+ }
+
+ // stack: [info]
+
+ if (lua_isnil(state, -1))
+ {
+ lua_pop(state, 1);
+ return false;
+ }
+ else
+ return true;
+}
+
+static void LookupInTable(lua_State *state, void *id, const char *tname)
+{
+ lua_getfield(state, LUA_REGISTRYINDEX, tname);
+ lua_pushlightuserdata(state, id);
+ lua_rawget(state, -2);
+ lua_remove(state, -2);
+}
+
+static void SaveInTable(lua_State *state, void *node, const char *tname)
+{
+ // stack: [info]
+ lua_getfield(state, LUA_REGISTRYINDEX, tname);
+
+ lua_pushlightuserdata(state, node);
+ lua_pushvalue(state, -3);
+ lua_rawset(state, -3);
+
+ lua_pushvalue(state, -2);
+ lua_pushlightuserdata(state, node);
+ lua_rawset(state, -3);
+
+ lua_pop(state, 1);
+ // stack: [info]
+}
+
+static void SaveTypeInfo(lua_State *state, void *node)
+{
+ SaveInTable(state, node, DFHACK_TYPETABLE_NAME);
+}
+
+static void BuildTypeMetatable(lua_State *state, type_identity *type);
+
+/**
+ * Push the pointer as DF object ref using metatable on the stack.
+ */
+static void push_object_ref(lua_State *state, void *ptr)
+{
+ // stack: [metatable]
+ auto ref = (DFRefHeader*)lua_newuserdata(state, sizeof(DFRefHeader));
+ ref->ptr = ptr;
+
+ lua_swap(state);
+ lua_setmetatable(state, -2);
+ // stack: [userdata]
+}
+
+static void *get_object_ref(lua_State *state, int val_index)
+{
+ assert(!lua_islightuserdata(state, val_index));
+
+ auto ref = (DFRefHeader*)lua_touserdata(state, val_index);
+ return ref->ptr;
+}
+
+/**
+ * Push the pointer using given identity.
+ */
+static void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method)
+{
+ /*
+ * If NULL pointer or no type, push something simple
+ */
+
+ if (!ptr || !type)
+ {
+ if (!ptr)
+ lua_pushnil(state);
+ else
+ lua_pushlightuserdata(state, ptr);
+
+ return;
+ }
+
+ /*
+ * Resolve actual class using vtable
+ */
+
+ if (type->type() == IDTYPE_CLASS)
+ {
+ virtual_identity *class_vid = virtual_identity::get(virtual_ptr(ptr));
+ if (class_vid)
+ type = class_vid;
+ }
+
+ /*
+ * Resolve metatable by identity, and push the object
+ */
+
+ lua_pushlightuserdata(state, type); // () -> type
+
+ if (!LookupTypeInfo(state, in_method)) // type -> metatable?
+ BuildTypeMetatable(state, type); // () -> metatable
+
+ push_object_ref(state, ptr); // metatable -> userdata
+}
+
+/**
+ * Verify that the value matches the identity, and return ptr if so.
+ */
+static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method)
+{
+ /*
+ * Non-userdata results in NULL; nil for NULL gets handled here too.
+ */
+ if (!lua_isuserdata(state, val_index))
+ return NULL;
+
+ /*
+ * Light user data is allowed with null type; otherwise bail out.
+ */
+ if (!lua_getmetatable(state, val_index)) // () -> metatable?
+ {
+ if (!type && lua_islightuserdata(state, val_index))
+ return lua_touserdata(state, val_index);
+
+ return NULL;
+ }
+
+ /*
+ * Verify that the metatable is known, and refers to the correct type.
+ * Here doing reverse lookup of identity by metatable.
+ */
+ if (!LookupTypeInfo(state, in_method)) // metatable -> type?
+ return NULL;
+
+ bool ok = lua_islightuserdata(state, -1) &&
+ (!type || lua_touserdata(state, -1) == type);
+
+ lua_pop(state, 1); // type -> ()
+
+ if (!ok)
+ return NULL;
+
+ /*
+ * Finally decode the reference.
+ */
+ return get_object_ref(state, val_index);
+}
+
+/**
+ * Given a DF object reference or type, safely retrieve its identity pointer.
+ */
+static type_identity *get_object_identity(lua_State *state, int objidx,
+ const char *ctx, bool allow_type = false)
+{
+ if (!lua_getmetatable(state, objidx))
+ luaL_error(state, "Invalid object in %s", ctx);
+
+ // Verify object type validity
+ if (lua_isuserdata(state, objidx))
+ {
+ lua_dup(state);
+ LookupInTable(state, DFHACK_TYPETABLE_NAME);
+ }
+ else
+ {
+ if (!allow_type)
+ luaL_error(state, "Object expected in %s", ctx);
+
+ lua_pushvalue(state, objidx);
+ LookupInTable(state, DFHACK_TYPEID_TABLE_NAME);
+ }
+
+ if (lua_isnil(state, -1))
+ luaL_error(state, "Invalid object metatable in %s", ctx);
+ lua_pop(state, 1);
+
+ // Extract identity from metatable
+ lua_getfield(state, -1, "_identity");
+
+ type_identity *id = (type_identity*)lua_touserdata(state, -1);
+ if (!id)
+ luaL_error(state, "Invalid object identity in %s", ctx);
+
+ lua_pop(state, 2);
+ return id;
+}
+
+/**
+ * Metamethod: compare two DF object references.
+ *
+ * Equal if same pointer and same metatable.
+ */
+static int meta_ptr_compare(lua_State *state)
+{
+ if (!lua_isuserdata(state, 1) || !lua_isuserdata(state, 2) ||
+ !lua_getmetatable(state, 1) || !lua_getmetatable(state, 2))
+ {
+ lua_pushboolean(state, false);
+ return 1;
+ }
+
+ if (get_object_ref(state, 1) != get_object_ref(state, 2))
+ {
+ lua_pushboolean(state, false);
+ return 1;
+ }
+
+ if (!lua_rawequal(state, -1, -2))
+ {
+ // todo: nonidentical type comparison
+ lua_pushboolean(state, false);
+ return 1;
+ }
+
+ lua_pushboolean(state, true);
+ return 1;
+}
+
+/**
+ * Method: sizeof for DF object references.
+ *
+ * Returns: size[, address]
+ */
+static int meta_sizeof(lua_State *state)
+{
+ int argc = lua_gettop(state);
+
+ if (argc != 1)
+ luaL_error(state, "Usage: object:sizeof() or df.sizeof(object)");
+
+ // Two special cases: nil and lightuserdata for NULL and void*
+ if (lua_isnil(state, 1))
+ {
+ lua_pushnil(state);
+ lua_pushinteger(state, 0);
+ return 2;
+ }
+
+ if (lua_islightuserdata(state, 1))
+ {
+ lua_pushnil(state);
+ lua_pushnumber(state, (size_t)lua_touserdata(state, 1));
+ return 2;
+ }
+
+ type_identity *id = get_object_identity(state, 1, "df.sizeof()", true);
+
+ lua_pushinteger(state, id->byte_size());
+
+ if (lua_isuserdata(state, 1))
+ {
+ lua_pushnumber(state, (size_t)get_object_ref(state, 1));
+ return 2;
+ }
+ else
+ return 1;
+}
+
+/**
+ * Method: displace for DF object references.
+ *
+ * Returns: a reference with the same type, but modified address
+ */
+static int meta_displace(lua_State *state)
+{
+ int argc = lua_gettop(state);
+
+ bool has_step = (argc >= 3);
+ if ((argc < 2 || argc > 3) ||
+ !lua_isnumber(state, 2) ||
+ (has_step && !lua_isnumber(state, 3)))
+ {
+ luaL_error(state, "Usage: object:_displace(index[,step]) or df._displace(object,...)");
+ }
+
+ int index = lua_tointeger(state, 2);
+ int step = has_step ? lua_tointeger(state, 3) : 1;
+
+ // Two special cases: nil and lightuserdata for NULL and void*
+ if (lua_isnil(state, 1))
+ {
+ lua_pushnil(state);
+ return 1;
+ }
+
+ if (lua_islightuserdata(state, 1))
+ {
+ if (!has_step)
+ luaL_error(state, "Step is mandatory in _displace of void*");
+
+ auto ptr = (uint8_t*)lua_touserdata(state, 1);
+ lua_pushlightuserdata(state, ptr + index*step);
+ return 1;
+ }
+
+ type_identity *id = get_object_identity(state, 1, "df._displace()");
+
+ if (!has_step)
+ step = id->byte_size();
+
+ if (index == 0 || step == 0)
+ {
+ lua_pushvalue(state, 1);
+ }
+ else
+ {
+ auto ptr = (uint8_t*)get_object_ref(state, 1);
+ lua_getmetatable(state, 1);
+ push_object_ref(state, ptr + index*step);
+ }
+
+ return 1;
+}
+
+/**
+ * Resolve the field name in UPVAL_FIELDTABLE, die if not found.
+ */
+static void lookup_field(lua_State *state, int index, const char *mode)
+{
+ lua_pushvalue(state, index);
+ lua_gettable(state, UPVAL_FIELDTABLE); // uses metatable with enum keys
+
+ if (lua_isnil(state, -1))
+ field_error(state, index, "not found", mode);
+}
+
+static void *find_field(lua_State *state, int index, const char *mode)
+{
+ lookup_field(state, index, mode);
+
+ void *p = lua_touserdata(state, -1);
+ lua_pop(state, 1);
+ return p;
+}
+
+/**
+ * Verify that the object is a DF ref with UPVAL_METATABLE.
+ * If everything ok, extract the address.
+ */
+static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode)
+{
+ if (!lua_isuserdata(state, obj) ||
+ !lua_getmetatable(state, obj))
+ field_error(state, field, "invalid object", mode);
+
+ if (!lua_rawequal(state, -1, UPVAL_METATABLE))
+ field_error(state, field, "invalid object metatable", mode);
+
+ lua_pop(state, 1);
+
+ return (uint8_t*)get_object_ref(state, obj);
+}
+
+static void GetAdHocMetatable(lua_State *state, const struct_field_info *field);
+
+static void read_field(lua_State *state, const struct_field_info *field, void *ptr)
+{
+ switch (field->mode)
+ {
+ case struct_field_info::STATIC_STRING:
+ {
+ int len = strnlen((char*)ptr, field->count);
+ lua_pushlstring(state, (char*)ptr, len);
+ return;
+ }
+
+ case struct_field_info::PRIMITIVE:
+ case struct_field_info::SUBSTRUCT:
+ field->type->lua_read(state, 2, ptr);
+ return;
+
+ case struct_field_info::POINTER:
+ df::pointer_identity::lua_read(state, 2, ptr, field->type);
+ return;
+
+ case struct_field_info::CONTAINER:
+ if (!field->eid || !field->type->isContainer() ||
+ field->eid == ((container_identity*)field->type)->getIndexEnumType())
+ {
+ field->type->lua_read(state, 2, ptr);
+ return;
+ }
+ // fallthrough
+
+ case struct_field_info::STATIC_ARRAY:
+ case struct_field_info::STL_VECTOR_PTR:
+ GetAdHocMetatable(state, field);
+ push_object_ref(state, ptr);
+ return;
+
+ case struct_field_info::END:
+ break;
+ }
+
+ lua_pushnil(state);
+}
+
+static void field_reference(lua_State *state, const struct_field_info *field, void *ptr)
+{
+ switch (field->mode)
+ {
+ case struct_field_info::PRIMITIVE:
+ case struct_field_info::SUBSTRUCT:
+ push_object_internal(state, field->type, ptr);
+ return;
+
+ case struct_field_info::POINTER:
+ push_adhoc_pointer(state, ptr, field->type);
+ return;
+
+ case struct_field_info::CONTAINER:
+ read_field(state, field, ptr);
+ return;
+
+ case struct_field_info::STATIC_STRING:
+ case struct_field_info::STATIC_ARRAY:
+ case struct_field_info::STL_VECTOR_PTR:
+ GetAdHocMetatable(state, field);
+ push_object_ref(state, ptr);
+ return;
+
+ case struct_field_info::END:
+ break;
+ }
+
+ lua_pushnil(state);
+}
+
+static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx)
+{
+ switch (field->mode)
+ {
+ case struct_field_info::STATIC_STRING:
+ {
+ size_t size;
+ const char *str = lua_tolstring(state, value_idx, &size);
+ if (!str)
+ field_error(state, 2, "string expected", "write");
+ memcpy(ptr, str, std::min(size+1, size_t(field->count)));
+ return;
+ }
+
+ case struct_field_info::PRIMITIVE:
+ case struct_field_info::SUBSTRUCT:
+ case struct_field_info::CONTAINER:
+ field->type->lua_write(state, 2, ptr, value_idx);
+ return;
+
+ case struct_field_info::POINTER:
+ df::pointer_identity::lua_write(state, 2, ptr, field->type, value_idx);
+
+ case struct_field_info::STATIC_ARRAY:
+ case struct_field_info::STL_VECTOR_PTR:
+ field_error(state, 2, "complex object", "write");
+
+ case struct_field_info::END:
+ return;
+ }
+}
+
+/**
+ * Metamethod: represent a type node as string.
+ */
+static int meta_type_tostring(lua_State *state)
+{
+ if (!lua_getmetatable(state, 1))
+ return 0;
+
+ lua_getfield(state, -1, "__metatable");
+ const char *cname = lua_tostring(state, -1);
+
+ lua_pushstring(state, stl_sprintf("<type: %s>", cname).c_str());
+ return 1;
+}
+
+/**
+ * Metamethod: represent a DF object reference as string.
+ */
+static int meta_ptr_tostring(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 0, "access");
+
+ lua_getfield(state, UPVAL_METATABLE, "__metatable");
+ const char *cname = lua_tostring(state, -1);
+
+ lua_pushstring(state, stl_sprintf("<%s: 0x%08x>", cname, (unsigned)ptr).c_str());
+ return 1;
+}
+
+// Resolve the field in the metatable and return
+static int get_metafield(lua_State *state)
+{
+ lua_rawget(state, UPVAL_METATABLE);
+ return 1;
+}
+
+/**
+ * Metamethod: __index for structures.
+ */
+static int meta_struct_index(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 2, "read");
+ auto field = (struct_field_info*)find_field(state, 2, "read");
+ if (!field)
+ return get_metafield(state);
+ read_field(state, field, ptr + field->offset);
+ return 1;
+}
+
+/**
+ * Method: _field for structures.
+ */
+static int meta_struct_field_reference(lua_State *state)
+{
+ if (lua_gettop(state) != 2)
+ luaL_error(state, "Usage: object._field(name)");
+ uint8_t *ptr = get_object_addr(state, 1, 2, "reference");
+ auto field = (struct_field_info*)find_field(state, 2, "reference");
+ if (!field)
+ field_error(state, 2, "builtin property", "reference");
+ field_reference(state, field, ptr + field->offset);
+ return 1;
+}
+
+/**
+ * Metamethod: __newindex for structures.
+ */
+static int meta_struct_newindex(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 2, "write");
+ auto field = (struct_field_info*)find_field(state, 2, "write");
+ if (!field)
+ field_error(state, 2, "builtin property", "write");
+ write_field(state, field, ptr + field->offset, 3);
+ return 0;
+}
+
+/**
+ * Metamethod: __index for primitives, i.e. simple object references.
+ * Fields point to identity, or NULL for metafields.
+ */
+static int meta_primitive_index(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 2, "read");
+ auto type = (type_identity*)find_field(state, 2, "read");
+ if (!type)
+ return get_metafield(state);
+ type->lua_read(state, 2, ptr);
+ return 1;
+}
+
+/**
+ * Metamethod: __newindex for primitives.
+ */
+static int meta_primitive_newindex(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 2, "write");
+ auto type = (type_identity*)find_field(state, 2, "write");
+ if (!type)
+ field_error(state, 2, "builtin property", "write");
+ type->lua_write(state, 2, ptr, 3);
+ return 0;
+}
+
+/**
+ * Metamethod: __len for containers.
+ */
+static int meta_container_len(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 0, "get length");
+ auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
+ int len = id->lua_item_count(state, ptr);
+ lua_pushinteger(state, len);
+ return 1;
+}
+
+/**
+ * Field lookup for containers:
+ *
+ * - Numbers are indices and handled directly.
+ * - NULL userdata are metafields; push and exit;
+ */
+static int lookup_container_field(lua_State *state, int field, const char *mode = NULL)
+{
+ if (lua_type(state, field) == LUA_TNUMBER)
+ return field;
+
+ lookup_field(state, field, mode ? mode : "read");
+
+ if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1))
+ {
+ if (mode)
+ field_error(state, field, "builtin property", mode);
+
+ lua_pop(state, 1);
+ get_metafield(state);
+ return 0;
+ }
+
+ return -1;
+}
+
+/**
+ * Index verification: number and in range.
+ */
+static int check_container_index(lua_State *state, int len,
+ int fidx, int iidx, const char *mode)
+{
+ if (!lua_isnumber(state, iidx))
+ field_error(state, fidx, "invalid index", mode);
+
+ int idx = lua_tointeger(state, iidx);
+ if (idx < 0 || idx >= len)
+ field_error(state, fidx, "index out of bounds", mode);
+
+ return idx;
+}
+
+/**
+ * Metamethod: __index for containers.
+ */
+static int meta_container_index(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 2, "read");
+ int iidx = lookup_container_field(state, 2);
+ if (!iidx)
+ return 1;
+
+ auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
+ int len = id->lua_item_count(state, ptr);
+ int idx = check_container_index(state, len, 2, iidx, "read");
+ id->lua_item_read(state, 2, ptr, idx);
+ return 1;
+}
+
+/**
+ * Method: _field for containers.
+ */
+static int meta_container_field_reference(lua_State *state)
+{
+ if (lua_gettop(state) != 2)
+ luaL_error(state, "Usage: object._field(index)");
+ uint8_t *ptr = get_object_addr(state, 1, 2, "reference");
+ int iidx = lookup_container_field(state, 2, "reference");
+
+ auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
+ int len = id->lua_item_count(state, ptr);
+ int idx = check_container_index(state, len, 2, iidx, "reference");
+ id->lua_item_reference(state, 2, ptr, idx);
+ return 1;
+}
+
+/**
+ * Metamethod: __index for containers.
+ */
+static int meta_container_newindex(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 2, "write");
+ int iidx = lookup_container_field(state, 2, "write");
+
+ auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
+ int len = id->lua_item_count(state, ptr);
+ int idx = check_container_index(state, len, 2, iidx, "write");
+ id->lua_item_write(state, 2, ptr, idx, 3);
+ return 0;
+}
+
+/**
+ * Metamethod: __len for bitfields.
+ */
+static int meta_bitfield_len(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 0, "get size");
+ auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
+ lua_pushinteger(state, id->getNumBits());
+ return 1;
+}
+
+/**
+ * Metamethod: __index for bitfields.
+ */
+static int meta_bitfield_index(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 2, "read");
+ int iidx = lookup_container_field(state, 2);
+ if (!iidx)
+ return 1;
+
+ auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
+
+ // whole
+ if (lua_isuserdata(state, iidx) && lua_touserdata(state, iidx) == id)
+ {
+ size_t intv = 0;
+ memcpy(&intv, ptr, std::min(sizeof(intv), size_t(id->byte_size())));
+ lua_pushnumber(state, intv);
+ return 1;
+ }
+
+ int idx = check_container_index(state, id->getNumBits(), 2, iidx, "read");
+ int size = id->getBits()[idx].size;
+
+ int value = getBitfieldField(ptr, idx, size);
+ if (size <= 1)
+ lua_pushboolean(state, value != 0);
+ else
+ lua_pushinteger(state, value);
+ return 1;
+}
+
+/**
+ * Metamethod: __newindex for bitfields.
+ */
+static int meta_bitfield_newindex(lua_State *state)
+{
+ uint8_t *ptr = get_object_addr(state, 1, 2, "write");
+ int iidx = lookup_container_field(state, 2, "write");
+
+ auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID);
+
+ // whole
+ if (lua_isuserdata(state, iidx) && lua_touserdata(state, iidx) == id)
+ {
+ if (!lua_isnumber(state, 3))
+ field_error(state, 2, "number expected", "write");
+
+ size_t intv = (size_t)lua_tonumber(state, 3);
+ memcpy(ptr, &intv, std::min(sizeof(intv), size_t(id->byte_size())));
+ return 0;
+ }
+
+ int idx = check_container_index(state, id->getNumBits(), 2, iidx, "write");
+ int size = id->getBits()[idx].size;
+
+ if (lua_isboolean(state, 3) || lua_isnil(state, 3))
+ setBitfieldField(ptr, idx, size, lua_toboolean(state, 3));
+ else if (lua_isnumber(state, 3))
+ setBitfieldField(ptr, idx, size, lua_tointeger(state, 3));
+ else
+ field_error(state, 2, "number or boolean expected", "write");
+ return 0;
+}
+
+/**
+ * Metamethod: __index for df.global
+ */
+static int meta_global_index(lua_State *state)
+{
+ auto field = (struct_field_info*)find_field(state, 2, "read");
+ if (!field)
+ return get_metafield(state);
+ void *ptr = *(void**)field->offset;
+ if (!ptr)
+ field_error(state, 2, "global address not known", "read");
+ read_field(state, field, ptr);
+ return 1;
+}
+
+/**
+ * Metamethod: __newindex for df.global
+ */
+static int meta_global_newindex(lua_State *state)
+{
+ auto field = (struct_field_info*)find_field(state, 2, "write");
+ if (!field)
+ field_error(state, 2, "builtin property", "write");
+ void *ptr = *(void**)field->offset;
+ if (!ptr)
+ field_error(state, 2, "global address not known", "write");
+ write_field(state, field, ptr, 3);
+ return 0;
+}
+
+/**
+ * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack.
+ */
+static void IndexFields(lua_State *state, struct_identity *pstruct)
+{
+ // stack: fieldtable
+
+ int base = lua_gettop(state);
+
+ for (struct_identity *p = pstruct; p; p = p->getParent())
+ {
+ auto fields = p->getFields();
+
+ for (; fields; ++fields)
+ {
+ if (fields->mode == struct_field_info::END)
+ break;
+
+ lua_pushstring(state,fields->name);
+ lua_pushlightuserdata(state,(void*)fields);
+ lua_rawset(state,base);
+ }
+ }
+}
+
+/**
+ * Enable a metafield by injecting an entry into a UPVAL_FIELDTABLE.
+ */
+static void EnableMetaField(lua_State *state, int ftable_idx, const char *name, void *id = NULL)
+{
+ lua_pushlightuserdata(state, id);
+ lua_setfield(state, ftable_idx, name);
+}
+
+/**
+ * Set metatable properties common to all actual DF object references.
+ */
+static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx)
+{
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME);
+ lua_setfield(state, meta_idx, "__eq");
+
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME);
+ lua_pushvalue(state, meta_idx);
+ lua_pushcclosure(state, meta_ptr_tostring, 2);
+ lua_setfield(state, meta_idx, "__tostring");
+
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
+ lua_setfield(state, meta_idx, "sizeof");
+
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME);
+ lua_setfield(state, meta_idx, "_displace");
+
+ EnableMetaField(state, read_idx, "_type");
+ EnableMetaField(state, read_idx, "_kind");
+
+ EnableMetaField(state, read_idx, "_field");
+
+ EnableMetaField(state, read_idx, "sizeof");
+ EnableMetaField(state, read_idx, "_displace");
+}
+
+/**
+ * Add a struct-style (3 upvalues) metamethod to the metatable.
+ */
+static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx,
+ lua_CFunction function, const char *name)
+{
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME);
+ lua_pushvalue(state, meta_idx);
+ lua_pushvalue(state, ftable_idx);
+ lua_pushcclosure(state, function, 3);
+ lua_setfield(state, meta_idx, name);
+}
+
+/**
+ * Make a metatable with most common fields, and an empty table for UPVAL_FIELDTABLE.
+ */
+static void MakeMetatable(lua_State *state, type_identity *type, const char *kind)
+{
+ int base = lua_gettop(state);
+ lua_newtable(state); // metatable
+
+ lua_pushstring(state, type->getFullName().c_str());
+ lua_setfield(state, base+1, "__metatable");
+
+ lua_pushlightuserdata(state, type);
+ lua_setfield(state, base+1, "_identity");
+
+ LookupInTable(state, type, DFHACK_TYPEID_TABLE_NAME);
+ if (lua_isnil(state, -1))
+ {
+ // Copy the string from __metatable if no real type
+ lua_pop(state, 1);
+ lua_getfield(state, base+1, "__metatable");
+ }
+ lua_setfield(state, base+1, "_type");
+
+ lua_pushstring(state, kind);
+ lua_setfield(state, base+1, "_kind");
+
+ lua_newtable(state); // fieldtable
+}
+
+/**
+ * Make a struct-style object metatable.
+ */
+static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct,
+ lua_CFunction reader, lua_CFunction writer)
+{
+ int base = lua_gettop(state);
+
+ MakeMetatable(state, pstruct, "struct"); // meta, fields
+
+ IndexFields(state, pstruct);
+
+ SetStructMethod(state, base+1, base+2, reader, "__index");
+ SetStructMethod(state, base+1, base+2, writer, "__newindex");
+
+ // returns: [metatable readfields writefields];
+}
+
+/**
+ * Make a primitive-style metatable
+ */
+static void MakePrimitiveMetatable(lua_State *state, type_identity *type)
+{
+ int base = lua_gettop(state);
+
+ MakeMetatable(state, type, "primitive");
+
+ SetPtrMethods(state, base+1, base+2);
+
+ EnableMetaField(state, base+2, "value", type);
+
+ SetStructMethod(state, base+1, base+2, meta_primitive_index, "__index");
+ SetStructMethod(state, base+1, base+2, meta_primitive_newindex, "__newindex");
+}
+
+/**
+ * Add a 6 upvalue metamethod to the metatable.
+ */
+static void SetContainerMethod(lua_State *state, int meta_idx, int ftable_idx,
+ lua_CFunction function, const char *name,
+ type_identity *container, type_identity *item, int count)
+{
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME);
+ lua_pushvalue(state, meta_idx);
+ lua_pushvalue(state, ftable_idx);
+
+ lua_pushlightuserdata(state, container);
+ lua_pushlightuserdata(state, item);
+ if (count < 0)
+ lua_pushnil(state);
+ else
+ lua_pushinteger(state, count);
+
+ lua_pushcclosure(state, function, 6);
+ lua_setfield(state, meta_idx, name);
+}
+
+/**
+ * If ienum refers to a valid enum, attach its keys to UPVAL_FIELDTABLE,
+ * and the enum itself to the _enum metafield.
+ */
+static void AttachEnumKeys(lua_State *state, int base, type_identity *ienum)
+{
+ LookupInTable(state, ienum, DFHACK_ENUM_TABLE_NAME);
+
+ if (!lua_isnil(state, -1))
+ {
+ lua_newtable(state);
+ lua_swap(state);
+ lua_setfield(state, -2, "__index");
+ lua_setmetatable(state, base+2);
+ }
+ else
+ lua_pop(state, 1);
+
+ LookupInTable(state, ienum, DFHACK_TYPEID_TABLE_NAME);
+ lua_setfield(state, base+1, "_enum");
+
+ EnableMetaField(state, base+2, "_enum");
+}
+
+/**
+ * Make a container-style object metatable.
+ */
+static void MakeContainerMetatable(lua_State *state, container_identity *type,
+ type_identity *item, int count, type_identity *ienum)
+{
+ int base = lua_gettop(state);
+
+ MakeMetatable(state, type, "container");
+ SetPtrMethods(state, base+1, base+2);
+
+ // Update the type name using full info
+ lua_pushstring(state, type->getFullName(item).c_str());
+ lua_dup(state);
+ lua_setfield(state, base+1, "__metatable");
+ lua_setfield(state, base+1, "_type");
+
+ lua_pushlightuserdata(state, item);
+ lua_setfield(state, base+1, "_field_identity");
+
+ if (count >= 0)
+ {
+ lua_pushinteger(state, count);
+ lua_setfield(state, base+1, "_count");
+ }
+
+ SetContainerMethod(state, base+1, base+2, meta_container_len, "__len", type, item, count);
+ SetContainerMethod(state, base+1, base+2, meta_container_index, "__index", type, item, count);
+ SetContainerMethod(state, base+1, base+2, meta_container_newindex, "__newindex", type, item, count);
+
+ SetContainerMethod(state, base+1, base+2, meta_container_field_reference, "_field", type, item, count);
+
+ AttachEnumKeys(state, base, ienum);
+}
+
+/*
+ * Metatable construction identity methods.
+ */
+void type_identity::build_metatable(lua_State *state)
+{
+ MakePrimitiveMetatable(state, this);
+}
+
+void container_identity::build_metatable(lua_State *state)
+{
+ MakeContainerMetatable(state, this, getItemType(), -1, getIndexEnumType());
+}
+
+void bitfield_identity::build_metatable(lua_State *state)
+{
+ int base = lua_gettop(state);
+
+ MakeMetatable(state, this, "bitfield");
+
+ SetPtrMethods(state, base+1, base+2);
+
+ SetContainerMethod(state, base+1, base+2, meta_bitfield_len, "__len", this, NULL, -1);
+ SetContainerMethod(state, base+1, base+2, meta_bitfield_index, "__index", this, NULL, -1);
+ SetContainerMethod(state, base+1, base+2, meta_bitfield_newindex, "__newindex", this, NULL, -1);
+
+ AttachEnumKeys(state, base, this);
+
+ EnableMetaField(state, base+2, "whole", this);
+}
+
+void struct_identity::build_metatable(lua_State *state)
+{
+ int base = lua_gettop(state);
+ MakeFieldMetatable(state, this, meta_struct_index, meta_struct_newindex);
+ SetStructMethod(state, base+1, base+2, meta_struct_field_reference, "_field");
+ SetPtrMethods(state, base+1, base+2);
+}
+
+void global_identity::build_metatable(lua_State *state)
+{
+ MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex);
+}
+
+static void BuildTypeMetatable(lua_State *state, type_identity *type)
+{
+ type->build_metatable(state);
+
+ lua_pop(state, 1);
+
+ SaveTypeInfo(state, type);
+}
+
+/**
+ * Construct a metatable for an object type folded into the field descriptor.
+ * This is done to reduce compile-time symbol table bloat due to templates.
+ */
+static void GetAdHocMetatable(lua_State *state, const struct_field_info *field)
+{
+ lua_pushlightuserdata(state, (void*)field);
+
+ if (!LookupTypeInfo(state, true))
+ {
+ switch (field->mode)
+ {
+ case struct_field_info::CONTAINER:
+ {
+ auto ctype = (container_identity*)field->type;
+ MakeContainerMetatable(state, ctype, ctype->getItemType(), -1, field->eid);
+ break;
+ }
+
+ case struct_field_info::STATIC_STRING:
+ MakeContainerMetatable(state, &df::buffer_container_identity::base_instance,
+ &df::identity_traits<char>::identity, field->count, NULL);
+ break;
+
+ case struct_field_info::STATIC_ARRAY:
+ MakeContainerMetatable(state, &df::buffer_container_identity::base_instance,
+ field->type, field->count, field->eid);
+ break;
+
+ case struct_field_info::STL_VECTOR_PTR:
+ MakeContainerMetatable(state, &df::identity_traits<std::vector<void*> >::identity,
+ field->type, -1, field->eid);
+ break;
+
+ default:
+ luaL_error(state, "Invalid ad-hoc field: %d", field->mode);
+ }
+
+ lua_pop(state, 1);
+
+ SaveTypeInfo(state, (void*)field);
+ }
+}
+
+static void push_adhoc_pointer(lua_State *state, void *ptr, type_identity *target)
+{
+ if (!target)
+ {
+ push_object_internal(state, &df::identity_traits<void*>::identity, ptr);
+ return;
+ }
+
+ LookupInTable(state, target, DFHACK_PTR_IDTABLE_NAME);
+
+ type_identity *id = (type_identity*)lua_touserdata(state, -1);
+ lua_pop(state, 1);
+
+ if (!id)
+ {
+ /*
+ * HACK: relies on
+ * 1) pointer_identity destructor being no-op
+ * 2) lua gc never moving objects in memory
+ */
+
+ void *newobj = lua_newuserdata(state, sizeof(pointer_identity));
+ id = new (newobj) pointer_identity(target);
+
+ SaveInTable(state, target, DFHACK_PTR_IDTABLE_NAME);
+ lua_pop(state, 1);
+ }
+
+ push_object_internal(state, id, ptr);
+}
+
+/*
+ * Recursive walk of scopes to construct the df... tree.
+ */
+
+static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children);
+
+static void AssociateId(lua_State *state, int table, int val, const char *name)
+{
+ lua_pushinteger(state, val);
+ lua_pushstring(state, name);
+ lua_dup(state);
+ lua_pushinteger(state, val);
+
+ lua_rawset(state, table);
+ lua_rawset(state, table);
+}
+
+static void RenderType(lua_State *state, compound_identity *node)
+{
+ assert(node->getName());
+ std::string name = node->getFullName();
+
+ lua_newtable(state);
+ if (!lua_checkstack(state, 20))
+ return;
+
+ int base = lua_gettop(state);
+
+ switch (node->type())
+ {
+ case IDTYPE_STRUCT:
+ lua_pushstring(state, "struct-type");
+ lua_setfield(state, base, "_kind");
+ break;
+
+ case IDTYPE_CLASS:
+ lua_pushstring(state, "class-type");
+ lua_setfield(state, base, "_kind");
+ break;
+
+ case IDTYPE_ENUM:
+ {
+ lua_pushstring(state, "enum-type");
+ lua_setfield(state, base, "_kind");
+
+ enum_identity *eid = (enum_identity*)node;
+ const char *const *keys = eid->getKeys();
+
+ // For enums, set mapping between keys and values
+ for (int64_t i = eid->getFirstItem(), j = 0; i <= eid->getLastItem(); i++, j++)
+ {
+ if (keys[j])
+ AssociateId(state, base, i, keys[j]);
+ }
+
+ if (eid->getFirstItem() <= eid->getLastItem())
+ {
+ lua_pushinteger(state, eid->getFirstItem());
+ lua_setfield(state, base, "_first_item");
+
+ lua_pushinteger(state, eid->getLastItem());
+ lua_setfield(state, base, "_last_item");
+ }
+
+ SaveInTable(state, node, DFHACK_ENUM_TABLE_NAME);
+ }
+ break;
+
+ case IDTYPE_BITFIELD:
+ {
+ lua_pushstring(state, "bitfield-type");
+ lua_setfield(state, base, "_kind");
+
+ bitfield_identity *eid = (bitfield_identity*)node;
+ auto bits = eid->getBits();
+
+ for (int i = 0; i < eid->getNumBits(); i++)
+ {
+ if (bits[i].name)
+ AssociateId(state, base, i, bits[i].name);
+ if (bits[i].size > 1)
+ i += bits[i].size-1;
+ }
+
+ lua_pushinteger(state, 0);
+ lua_setfield(state, base, "_first_item");
+
+ lua_pushinteger(state, eid->getNumBits()-1);
+ lua_setfield(state, base, "_last_item");
+
+ SaveInTable(state, node, DFHACK_ENUM_TABLE_NAME);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ RenderTypeChildren(state, node->getScopeChildren());
+
+ assert(base == lua_gettop(state));
+
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
+ lua_setfield(state, base, "sizeof");
+
+ if (node->type() == IDTYPE_GLOBAL)
+ {
+ BuildTypeMetatable(state, node);
+
+ // Set metatable for the inner table
+ lua_dup(state);
+ lua_setmetatable(state, base);
+ lua_swap(state); // -> meta curtable
+
+ freeze_table(state, true, "global");
+
+ // Copy __newindex to the outer metatable
+ lua_getfield(state, base, "__newindex");
+ lua_setfield(state, -2, "__newindex");
+
+ lua_remove(state, base);
+ }
+ else
+ {
+ freeze_table(state, true, name.c_str());
+ }
+
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME);
+ lua_setfield(state, -2, "__tostring");
+
+ lua_pushlightuserdata(state, node);
+ lua_setfield(state, -2, "_identity");
+
+ lua_pop(state, 1);
+
+ SaveInTable(state, node, DFHACK_TYPEID_TABLE_NAME);
+}
+
+static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children)
+{
+ for (size_t i = 0; i < children.size(); i++)
+ {
+ RenderType(state, children[i]);
+ lua_pushstring(state, children[i]->getName());
+ lua_swap(state);
+ lua_rawset(state, -3);
+ }
+}
+
+static void DoAttach(lua_State *state)
+{
+ int base = lua_gettop(state);
+
+ lua_newtable(state);
+ lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_PTR_IDTABLE_NAME);
+
+ lua_newtable(state);
+ lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME);
+
+ lua_newtable(state);
+ lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_ENUM_TABLE_NAME);
+
+ lua_pushcfunction(state, change_error);
+ lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME);
+
+ lua_pushcfunction(state, meta_ptr_compare);
+ lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME);
+
+ lua_pushcfunction(state, meta_type_tostring);
+ lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME);
+
+ lua_pushcfunction(state, meta_sizeof);
+ lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
+
+ lua_pushcfunction(state, meta_displace);
+ lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME);
+
+ luaL_register(state, "df", no_functions);
+
+ {
+ // Assign df a metatable with read-only contents
+ lua_newtable(state);
+
+ // Render the type structure
+ RenderTypeChildren(state, compound_identity::getTopScope());
+
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME);
+ lua_setfield(state, -2, "sizeof");
+ lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME);
+ lua_setfield(state, -2, "_displace");
+
+ freeze_table(state, true, "df");
+ lua_remove(state, -2);
+ lua_setmetatable(state, -2);
+ }
+
+ lua_pop(state, 1);
+}
+
+/**
+ * Initialize access to DF objects from the interpreter
+ * context, unless it has already been done.
+ */
+void DFHack::AttachDFGlobals(lua_State *state)
+{
+ if (luaL_newmetatable(state, DFHACK_TYPETABLE_NAME))
+ DoAttach(state);
+
+ lua_pop(state, 1);
+}
diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h
index 99694d94..17751d90 100644
--- a/library/include/DataDefs.h
+++ b/library/include/DataDefs.h
@@ -37,6 +37,8 @@ distribution.
#undef interface
#endif
+typedef struct lua_State lua_State;
+
/*
* Definitions of DFHack namespace structs used by generated headers.
*/
@@ -45,47 +47,252 @@ namespace DFHack
{
class virtual_class {};
+ enum identity_type {
+ IDTYPE_GLOBAL,
+ IDTYPE_PRIMITIVE,
+ IDTYPE_POINTER,
+ IDTYPE_CONTAINER,
+ IDTYPE_PTR_CONTAINER,
+ IDTYPE_BIT_CONTAINER,
+ IDTYPE_BITFIELD,
+ IDTYPE_ENUM,
+ IDTYPE_STRUCT,
+ IDTYPE_CLASS,
+ IDTYPE_STL_PTR_VECTOR
+ };
+
+ typedef void *(*TAllocateFn)(void*,const void*);
+
+ class DFHACK_EXPORT type_identity {
+ size_t size;
+
+ protected:
+ type_identity(size_t size) : size(size) {};
+
+ void *do_allocate_pod();
+ void do_copy_pod(void *tgt, const void *src);
+
+ virtual bool can_allocate() { return true; }
+ virtual void *do_allocate() { return do_allocate_pod(); }
+ virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
+
+ public:
+ virtual ~type_identity() {}
+
+ size_t byte_size() { return size; }
+
+ virtual identity_type type() = 0;
+
+ virtual std::string getFullName() = 0;
+
+ // For internal use in the lua wrapper
+ virtual void lua_read(lua_State *state, int fname_idx, void *ptr) = 0;
+ virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) = 0;
+ virtual void build_metatable(lua_State *state);
+
+ virtual bool isContainer() { return false; }
+
+ void *allocate();
+ bool copy(void *tgt, const void *src);
+ };
+
+ class DFHACK_EXPORT constructed_identity : public type_identity {
+ TAllocateFn allocator;
+
+ protected:
+ constructed_identity(size_t size, TAllocateFn alloc)
+ : type_identity(size), allocator(alloc) {};
+
+ virtual bool can_allocate() { return (allocator != NULL); }
+ virtual void *do_allocate() { return allocator(NULL,NULL); }
+ virtual void do_copy(void *tgt, const void *src) { allocator(tgt,src); }
+
+ virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
+ virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
+ };
+
+ class DFHACK_EXPORT compound_identity : public constructed_identity {
+ static compound_identity *list;
+ compound_identity *next;
+
+ const char *dfhack_name;
+ compound_identity *scope_parent;
+ std::vector<compound_identity*> scope_children;
+ static std::vector<compound_identity*> top_scope;
+
+ protected:
+ compound_identity(size_t size, TAllocateFn alloc,
+ compound_identity *scope_parent, const char *dfhack_name);
+
+ virtual void doInit(Core *core);
+
+ public:
+ const char *getName() { return dfhack_name; }
+
+ virtual std::string getFullName();
+
+ compound_identity *getScopeParent() { return scope_parent; }
+ const std::vector<compound_identity*> &getScopeChildren() { return scope_children; }
+ static const std::vector<compound_identity*> &getTopScope() { return top_scope; }
+
+ static void Init(Core *core);
+ };
+
+ // Bitfields
+ struct bitfield_item_info {
+ const char *name;
+ int size;
+ };
+
+ class DFHACK_EXPORT bitfield_identity : public compound_identity {
+ const bitfield_item_info *bits;
+ int num_bits;
+
+ protected:
+ virtual bool can_allocate() { return true; }
+ virtual void *do_allocate() { return do_allocate_pod(); }
+ virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
+
+ public:
+ bitfield_identity(size_t size,
+ compound_identity *scope_parent, const char *dfhack_name,
+ int num_bits, const bitfield_item_info *bits);
+
+ virtual identity_type type() { return IDTYPE_BITFIELD; }
+
+ int getNumBits() { return num_bits; }
+ const bitfield_item_info *getBits() { return bits; }
+
+ virtual void build_metatable(lua_State *state);
+ };
+
+ class DFHACK_EXPORT enum_identity : public compound_identity {
+ const char *const *keys;
+ int64_t first_item_value;
+ int64_t last_item_value;
+
+ type_identity *base_type;
+
+ protected:
+ virtual bool can_allocate() { return true; }
+ virtual void *do_allocate();
+ virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); }
+
+ public:
+ enum_identity(size_t size,
+ compound_identity *scope_parent, const char *dfhack_name,
+ type_identity *base_type,
+ int64_t first_item_value, int64_t last_item_value,
+ const char *const *keys);
+
+ virtual identity_type type() { return IDTYPE_ENUM; }
+
+ int64_t getFirstItem() { return first_item_value; }
+ int64_t getLastItem() { return last_item_value; }
+ int getCount() { return int(last_item_value-first_item_value+1); }
+ const char *const *getKeys() { return keys; }
+
+ type_identity *getBaseType() { return base_type; }
+
+ virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
+ virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
+ };
+
+ struct struct_field_info {
+ enum Mode {
+ END,
+ PRIMITIVE,
+ STATIC_STRING,
+ POINTER,
+ STATIC_ARRAY,
+ SUBSTRUCT,
+ CONTAINER,
+ STL_VECTOR_PTR
+ };
+ Mode mode;
+ const char *name;
+ size_t offset;
+ type_identity *type;
+ size_t count;
+ enum_identity *eid;
+ };
+
+ class DFHACK_EXPORT struct_identity : public compound_identity {
+ struct_identity *parent;
+ std::vector<struct_identity*> children;
+ bool has_children;
+
+ const struct_field_info *fields;
+
+ protected:
+ virtual void doInit(Core *core);
+
+ public:
+ struct_identity(size_t size, TAllocateFn alloc,
+ compound_identity *scope_parent, const char *dfhack_name,
+ struct_identity *parent, const struct_field_info *fields);
+
+ virtual identity_type type() { return IDTYPE_STRUCT; }
+
+ struct_identity *getParent() { return parent; }
+ const std::vector<struct_identity*> &getChildren() { return children; }
+ bool hasChildren() { return has_children; }
+
+ const struct_field_info *getFields() { return fields; }
+
+ bool is_subclass(struct_identity *subtype);
+
+ virtual void build_metatable(lua_State *state);
+ };
+
+ class DFHACK_EXPORT global_identity : public struct_identity {
+ public:
+ global_identity(const struct_field_info *fields)
+ : struct_identity(0,NULL,NULL,"global",NULL,fields) {}
+
+ virtual identity_type type() { return IDTYPE_GLOBAL; }
+
+ virtual void build_metatable(lua_State *state);
+ };
+
#ifdef _MSC_VER
typedef void *virtual_ptr;
#else
typedef virtual_class *virtual_ptr;
#endif
- class DFHACK_EXPORT virtual_identity {
- static virtual_identity *list;
+ class DFHACK_EXPORT virtual_identity : public struct_identity {
static std::map<void*, virtual_identity*> known;
-
- virtual_identity *prev, *next;
- const char *dfhack_name;
+
const char *original_name;
- virtual_identity *parent;
- std::vector<virtual_identity*> children;
-
+
void *vtable_ptr;
- bool has_children;
protected:
- virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent);
+ virtual void doInit(Core *core);
static void *get_vtable(virtual_ptr instance_ptr) { return *(void**)instance_ptr; }
+ bool can_allocate() { return struct_identity::can_allocate() && (vtable_ptr != NULL); }
+
public:
- const char *getName() { return dfhack_name; }
- const char *getOriginalName() { return original_name ? original_name : dfhack_name; }
+ virtual_identity(size_t size, TAllocateFn alloc,
+ const char *dfhack_name, const char *original_name,
+ virtual_identity *parent, const struct_field_info *fields);
- virtual_identity *getParent() { return parent; }
- const std::vector<virtual_identity*> &getChildren() { return children; }
+ virtual identity_type type() { return IDTYPE_CLASS; }
+
+ const char *getOriginalName() { return original_name ? original_name : getName(); }
public:
static virtual_identity *get(virtual_ptr instance_ptr);
-
- bool is_subclass(virtual_identity *subtype);
+
bool is_instance(virtual_ptr 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;
+ if (!hasChildren()) return false;
}
return is_subclass(get(instance_ptr));
}
@@ -97,16 +304,11 @@ namespace DFHack
}
public:
- bool can_instantiate() { return (vtable_ptr != NULL); }
- virtual_ptr instantiate() { return can_instantiate() ? do_instantiate() : NULL; }
+ bool can_instantiate() { return can_allocate(); }
+ virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_allocate() : NULL; }
static virtual_ptr clone(virtual_ptr obj);
- protected:
- virtual virtual_ptr do_instantiate() = 0;
- virtual void do_copy(virtual_ptr tgt, virtual_ptr src) = 0;
public:
- static void Init(Core *core);
-
// Strictly for use in virtual class constructors
void adjust_vtable(virtual_ptr obj, virtual_identity *main);
};
@@ -127,6 +329,23 @@ namespace DFHack
void InitDataDefGlobals(Core *core);
+ // LUA wrapper
+
+ /**
+ * Make DF objects available to the given interpreter.
+ */
+ DFHACK_EXPORT void AttachDFGlobals(lua_State *state);
+
+ /**
+ * Push the pointer onto the stack as a wrapped DF object of the given type.
+ */
+ DFHACK_EXPORT void PushDFObject(lua_State *state, type_identity *type, void *ptr);
+
+ /**
+ * Check that the value is a wrapped DF object of the given type, and if so return the pointer.
+ */
+ DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index);
+
template<class T>
T *ifnull(T *a, T *b) { return a ? a : b; }
@@ -135,12 +354,6 @@ namespace DFHack
size_t size;
const T *items;
};
-
- // Bitfields
- struct bitfield_item_info {
- const char *name;
- int size;
- };
}
template<class T>
@@ -164,33 +377,37 @@ inline int linear_index(const DFHack::enum_list_attr<const char*> &lst, const st
namespace df
{
+ using DFHack::type_identity;
+ using DFHack::compound_identity;
using DFHack::virtual_ptr;
using DFHack::virtual_identity;
using DFHack::virtual_class;
+ using DFHack::global_identity;
+ using DFHack::struct_identity;
+ using DFHack::struct_field_info;
using DFHack::bitfield_item_info;
+ using DFHack::bitfield_identity;
+ using DFHack::enum_identity;
using DFHack::enum_list_attr;
using DFHack::BitArray;
using DFHack::DfArray;
template<class T>
- struct enum_traits {};
+ void *allocator_fn(void *out, const void *in) {
+ if (out) { *(T*)out = *(const T*)in; return out; }
+ else return new T();
+ }
template<class T>
- struct bitfield_traits {};
+ struct identity_traits {
+ static compound_identity *get() { return &T::_identity; }
+ };
template<class T>
- class class_virtual_identity : public virtual_identity {
- public:
- class_virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent)
- : virtual_identity(dfhack_name, original_name, parent) {};
-
- T *instantiate() { return static_cast<T*>(virtual_identity::instantiate()); }
- T *clone(T* obj) { return static_cast<T*>(virtual_identity::clone(obj)); }
+ struct enum_traits {};
- protected:
- virtual virtual_ptr do_instantiate() { return new T(); }
- virtual void do_copy(virtual_ptr tgt, virtual_ptr src) { *static_cast<T*>(tgt) = *static_cast<T*>(src); }
- };
+ template<class T>
+ struct bitfield_traits {};
template<class EnumType, class IntType = int32_t>
struct enum_field {
@@ -427,6 +644,24 @@ namespace DFHack {
flagarray_to_string<T>(&tmp, val);
return join_strings(sep, tmp);
}
+
+ // LUA wrapper
+
+ /**
+ * Push the pointer onto the stack as a wrapped DF object of a specific type.
+ */
+ template<class T>
+ void PushDFObject(lua_State *state, T *ptr) {
+ PushDFObject(state, df::identity_traits<T>::get(), ptr);
+ }
+
+ /**
+ * Check that the value is a wrapped DF object of the correct type, and if so return the pointer.
+ */
+ template<class T>
+ T *GetDFObject(lua_State *state, int val_index) {
+ return GetDFObject(state, df::identity_traits<T>::get(), val_index);
+ }
}
#define ENUM_ATTR(enum,attr,val) (df::enum_traits<df::enum>::attrs(val).attr)
diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h
new file mode 100644
index 00000000..80863479
--- /dev/null
+++ b/library/include/DataIdentity.h
@@ -0,0 +1,438 @@
+/*
+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 <string>
+#include <sstream>
+#include <vector>
+#include <map>
+
+#include "DataDefs.h"
+
+/*
+ * Definitions of DFHack namespace structs used by generated headers.
+ */
+
+namespace DFHack
+{
+ class DFHACK_EXPORT primitive_identity : public type_identity {
+ public:
+ primitive_identity(size_t size) : type_identity(size) {};
+
+ virtual identity_type type() { return IDTYPE_PRIMITIVE; }
+ };
+
+ class DFHACK_EXPORT pointer_identity : public primitive_identity {
+ type_identity *target;
+
+ public:
+ pointer_identity(type_identity *target = NULL)
+ : primitive_identity(sizeof(void*)), target(target) {};
+
+ virtual identity_type type() { return IDTYPE_POINTER; }
+
+ type_identity *getTarget() { return target; }
+
+ std::string getFullName();
+
+ static void lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target);
+ static void lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index);
+
+ virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
+ virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
+ };
+
+ class DFHACK_EXPORT container_identity : public constructed_identity {
+ type_identity *item;
+ enum_identity *ienum;
+
+ public:
+ container_identity(size_t size, TAllocateFn alloc, type_identity *item, enum_identity *ienum = NULL)
+ : constructed_identity(size, alloc), item(item), ienum(ienum) {};
+
+ virtual identity_type type() { return IDTYPE_CONTAINER; }
+
+ std::string getFullName() { return getFullName(item); }
+
+ virtual void build_metatable(lua_State *state);
+ virtual bool isContainer() { return true; }
+
+ type_identity *getItemType() { return item; }
+ type_identity *getIndexEnumType() { return ienum; }
+
+ virtual std::string getFullName(type_identity *item);
+
+ int lua_item_count(lua_State *state, void *ptr);
+
+ virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx);
+ virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx);
+ virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index);
+
+ protected:
+ virtual int item_count(void *ptr) = 0;
+ virtual void *item_pointer(type_identity *item, void *ptr, int idx) = 0;
+ };
+
+ class DFHACK_EXPORT ptr_container_identity : public container_identity {
+ public:
+ ptr_container_identity(size_t size, TAllocateFn alloc,
+ type_identity *item, enum_identity *ienum = NULL)
+ : container_identity(size, alloc, item, ienum) {};
+
+ virtual identity_type type() { return IDTYPE_PTR_CONTAINER; }
+
+ std::string getFullName(type_identity *item);
+
+ virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx);
+ virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx);
+ virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index);
+ };
+
+ class DFHACK_EXPORT bit_container_identity : public container_identity {
+ public:
+ bit_container_identity(size_t size, TAllocateFn alloc, enum_identity *ienum = NULL)
+ : container_identity(size, alloc, NULL, ienum) {};
+
+ virtual identity_type type() { return IDTYPE_BIT_CONTAINER; }
+
+ std::string getFullName(type_identity *item);
+
+ virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx);
+ virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx);
+ virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index);
+
+ protected:
+ virtual void *item_pointer(type_identity *, void *, int) { return NULL; }
+
+ virtual bool get_item(void *ptr, int idx) = 0;
+ virtual void set_item(void *ptr, int idx, bool val) = 0;
+ };
+}
+
+namespace df
+{
+ using DFHack::primitive_identity;
+ using DFHack::pointer_identity;
+ using DFHack::container_identity;
+ using DFHack::ptr_container_identity;
+ using DFHack::bit_container_identity;
+
+ class number_identity_base : public primitive_identity {
+ const char *name;
+
+ public:
+ number_identity_base(size_t size, const char *name)
+ : primitive_identity(size), name(name) {};
+
+ std::string getFullName() { return name; }
+
+ virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
+ virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
+
+ protected:
+ virtual double read(void *ptr) = 0;
+ virtual void write(void *ptr, double val) = 0;
+ };
+
+ template<class T>
+ class number_identity : public number_identity_base {
+ public:
+ number_identity(const char *name) : number_identity_base(sizeof(T), name) {}
+ protected:
+ virtual double read(void *ptr) { return double(*(T*)ptr); }
+ virtual void write(void *ptr, double val) { *(T*)ptr = T(val); }
+ };
+
+ class bool_identity : public primitive_identity {
+ public:
+ bool_identity() : primitive_identity(sizeof(bool)) {};
+
+ std::string getFullName() { return "bool"; }
+
+ virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
+ virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
+ };
+
+ class stl_string_identity : public primitive_identity {
+ public:
+ stl_string_identity() : primitive_identity(sizeof(std::string)) {};
+
+ std::string getFullName() { return "string"; }
+
+ virtual void lua_read(lua_State *state, int fname_idx, void *ptr);
+ virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index);
+ };
+
+ class stl_ptr_vector_identity : public ptr_container_identity {
+ public:
+ /*
+ * This class assumes that std::vector<T*> is equivalent
+ * in layout and behavior to std::vector<void*> for any T.
+ */
+
+ stl_ptr_vector_identity(type_identity *item = NULL, enum_identity *ienum = NULL)
+ : ptr_container_identity(sizeof(std::vector<void*>),allocator_fn<std::vector<void*> >,item, ienum)
+ {};
+
+ std::string getFullName(type_identity *item) {
+ return "vector" + ptr_container_identity::getFullName(item);
+ }
+
+ virtual DFHack::identity_type type() { return DFHack::IDTYPE_STL_PTR_VECTOR; }
+
+ protected:
+ virtual int item_count(void *ptr) {
+ return ((std::vector<void*>*)ptr)->size();
+ };
+ virtual void *item_pointer(type_identity *, void *ptr, int idx) {
+ return &(*(std::vector<void*>*)ptr)[idx];
+ }
+ };
+
+ class buffer_container_identity : public container_identity {
+ int size;
+
+ public:
+ buffer_container_identity()
+ : container_identity(0, NULL, NULL, NULL), size(0)
+ {}
+
+ buffer_container_identity(int size, type_identity *item, enum_identity *ienum = NULL)
+ : container_identity(item->byte_size()*size, NULL, item, ienum), size(size)
+ {}
+
+ std::string getFullName(type_identity *item);
+
+ static buffer_container_identity base_instance;
+
+ protected:
+ virtual int item_count(void *ptr) { return size; }
+ virtual void *item_pointer(type_identity *item, void *ptr, int idx) {
+ return ((uint8_t*)ptr) + idx * item->byte_size();
+ }
+ };
+
+ template<class T>
+ class stl_container_identity : public container_identity {
+ const char *name;
+
+ public:
+ stl_container_identity(const char *name, type_identity *item, enum_identity *ienum = NULL)
+ : container_identity(sizeof(T), &allocator_fn<T>, item, ienum), name(name)
+ {}
+
+ std::string getFullName(type_identity *item) {
+ return name + container_identity::getFullName(item);
+ }
+
+ protected:
+ virtual int item_count(void *ptr) { return ((T*)ptr)->size(); }
+ virtual void *item_pointer(type_identity *item, void *ptr, int idx) {
+ return &(*(T*)ptr)[idx];
+ }
+ };
+
+ class bit_array_identity : public bit_container_identity {
+ public:
+ /*
+ * This class assumes that BitArray<T> is equivalent
+ * in layout and behavior to BitArray<int> for any T.
+ */
+
+ typedef BitArray<int> container;
+
+ bit_array_identity(enum_identity *ienum = NULL)
+ : bit_container_identity(sizeof(container), &allocator_fn<container>, ienum)
+ {}
+
+ std::string getFullName(type_identity *item) {
+ return "BitArray<>";
+ }
+
+ protected:
+ virtual int item_count(void *ptr) { return ((container*)ptr)->size * 8; }
+ virtual bool get_item(void *ptr, int idx) {
+ return ((container*)ptr)->is_set(idx);
+ }
+ virtual void set_item(void *ptr, int idx, bool val) {
+ ((container*)ptr)->set(idx, val);
+ }
+ };
+
+ class stl_bit_vector_identity : public bit_container_identity {
+ public:
+ typedef std::vector<bool> container;
+
+ stl_bit_vector_identity(enum_identity *ienum = NULL)
+ : bit_container_identity(sizeof(container), &allocator_fn<container>, ienum)
+ {}
+
+ std::string getFullName(type_identity *item) {
+ return "vector" + bit_container_identity::getFullName(item);
+ }
+
+ protected:
+ virtual int item_count(void *ptr) { return ((container*)ptr)->size(); }
+ virtual bool get_item(void *ptr, int idx) {
+ return (*(container*)ptr)[idx];
+ }
+ virtual void set_item(void *ptr, int idx, bool val) {
+ (*(container*)ptr)[idx] = val;
+ }
+ };
+
+#define NUMBER_IDENTITY_TRAITS(type) \
+ template<> struct identity_traits<type> { \
+ static number_identity<type> identity; \
+ static number_identity_base *get() { return &identity; } \
+ };
+
+ NUMBER_IDENTITY_TRAITS(char);
+ NUMBER_IDENTITY_TRAITS(int8_t);
+ NUMBER_IDENTITY_TRAITS(uint8_t);
+ NUMBER_IDENTITY_TRAITS(int16_t);
+ NUMBER_IDENTITY_TRAITS(uint16_t);
+ NUMBER_IDENTITY_TRAITS(int32_t);
+ NUMBER_IDENTITY_TRAITS(uint32_t);
+ NUMBER_IDENTITY_TRAITS(int64_t);
+ NUMBER_IDENTITY_TRAITS(uint64_t);
+ NUMBER_IDENTITY_TRAITS(float);
+
+ template<> struct identity_traits<bool> {
+ static bool_identity identity;
+ static bool_identity *get() { return &identity; }
+ };
+
+ template<> struct identity_traits<std::string> {
+ static stl_string_identity identity;
+ static stl_string_identity *get() { return &identity; }
+ };
+
+ template<> struct identity_traits<void*> {
+ static pointer_identity identity;
+ static pointer_identity *get() { return &identity; }
+ };
+
+ template<> struct identity_traits<std::vector<void*> > {
+ static stl_ptr_vector_identity identity;
+ static stl_ptr_vector_identity *get() { return &identity; }
+ };
+
+ template<> struct identity_traits<std::vector<bool> > {
+ static stl_bit_vector_identity identity;
+ static stl_bit_vector_identity *get() { return &identity; }
+ };
+
+#undef NUMBER_IDENTITY_TRAITS
+
+ // Container declarations
+
+ template<class Enum, class FT> struct identity_traits<enum_field<Enum,FT> > {
+ static primitive_identity *get();
+ };
+
+ template<class T> struct identity_traits<T *> {
+ static pointer_identity *get();
+ };
+
+ template<class T, int sz> struct identity_traits<T [sz]> {
+ static container_identity *get();
+ };
+
+ template<class T> struct identity_traits<std::vector<T> > {
+ static container_identity *get();
+ };
+
+ template<class T> struct identity_traits<std::vector<T*> > {
+ static stl_ptr_vector_identity *get();
+ };
+
+ template<class T> struct identity_traits<std::deque<T> > {
+ static container_identity *get();
+ };
+
+ template<class T> struct identity_traits<BitArray<T> > {
+ static bit_container_identity *get();
+ };
+
+ template<class T> struct identity_traits<DfArray<T> > {
+ static container_identity *get();
+ };
+
+ // Container definitions
+
+ template<class Enum, class FT>
+ primitive_identity *identity_traits<enum_field<Enum,FT> >::get() {
+ return identity_traits<FT>::get();
+ }
+
+ template<class T>
+ pointer_identity *identity_traits<T *>::get() {
+ static pointer_identity identity(identity_traits<T>::get());
+ return &identity;
+ }
+
+ template<class T, int sz>
+ container_identity *identity_traits<T [sz]>::get() {
+ static buffer_container_identity identity(sz, identity_traits<T>::get());
+ return &identity;
+ }
+
+ template<class T>
+ container_identity *identity_traits<std::vector<T> >::get() {
+ typedef std::vector<T> container;
+ static stl_container_identity<container> identity("vector", identity_traits<T>::get());
+ return &identity;
+ }
+
+ template<class T>
+ stl_ptr_vector_identity *identity_traits<std::vector<T*> >::get() {
+ static stl_ptr_vector_identity identity(identity_traits<T>::get());
+ return &identity;
+ }
+
+ template<class T>
+ container_identity *identity_traits<std::deque<T> >::get() {
+ typedef std::deque<T> container;
+ static stl_container_identity<container> identity("deque", identity_traits<T>::get());
+ return &identity;
+ }
+
+ template<class T>
+ bit_container_identity *identity_traits<BitArray<T> >::get() {
+ static type_identity *eid = identity_traits<T>::get();
+ static enum_identity *reid = eid->type() == DFHack::IDTYPE_ENUM ? (enum_identity*)eid : NULL;
+ static bit_array_identity identity(reid);
+ return &identity;
+ }
+
+ template<class T>
+ container_identity *identity_traits<DfArray<T> >::get() {
+ typedef DfArray<T> container;
+ static stl_container_identity<container> identity("DfArray", identity_traits<T>::get());
+ return &identity;
+ }
+}
+
diff --git a/library/proto/BasicApi.proto b/library/proto/BasicApi.proto
index 7b105ee8..137f2572 100644
--- a/library/proto/BasicApi.proto
+++ b/library/proto/BasicApi.proto
@@ -4,6 +4,10 @@ option optimize_for = LITE_RUNTIME;
import "Basic.proto";
+// RPC GetVersion : EmptyMessage -> StringMessage
+// RPC GetDFVersion : EmptyMessage -> StringMessage
+
+// RPC GetWorldInfo : EmptyMessage -> GetWorldInfoOut
message GetWorldInfoOut {
enum Mode {
MODE_DWARF = 1;
@@ -27,6 +31,7 @@ message GetWorldInfoOut {
repeated int32 companion_histfig_ids = 10;
};
+// RPC ListEnums : EmptyMessage -> ListEnumsOut
message ListEnumsOut {
repeated EnumItemName material_flags = 1;
repeated EnumItemName inorganic_flags = 2;
@@ -46,6 +51,7 @@ message ListEnumsOut {
repeated EnumItemName profession = 11;
};
+// RPC ListMaterials : ListMaterialsIn -> ListMaterialsOut
message ListMaterialsIn {
optional BasicMaterialInfoMask mask = 1;
@@ -62,6 +68,7 @@ message ListMaterialsOut {
repeated BasicMaterialInfo value = 1;
};
+// RPC ListUnits : ListUnitsIn -> ListUnitsOut
message ListUnitsIn {
optional BasicUnitInfoMask mask = 1;
@@ -81,6 +88,7 @@ message ListUnitsOut {
repeated BasicUnitInfo value = 1;
};
+// RPC ListSquads : ListSquadsIn -> ListSquadsOut
message ListSquadsIn {}
message ListSquadsOut {
repeated BasicSquadInfo value = 1;
diff --git a/library/proto/CoreProtocol.proto b/library/proto/CoreProtocol.proto
index 6f3b8451..92d7c48d 100644
--- a/library/proto/CoreProtocol.proto
+++ b/library/proto/CoreProtocol.proto
@@ -62,18 +62,22 @@ message StringListMessage {
repeated string value = 1;
}
+// RPC BindMethod : CoreBindRequest -> CoreBindReply
message CoreBindRequest {
required string method = 1;
required string input_msg = 2;
required string output_msg = 3;
optional string plugin = 4;
}
-
message CoreBindReply {
required int32 assigned_id = 1;
}
+// RPC RunCommand : CoreRunCommandRequest -> EmptyMessage
message CoreRunCommandRequest {
required string command = 1;
repeated string arguments = 2;
}
+
+// RPC CoreSuspend : EmptyMessage -> IntMessage
+// RPC CoreResume : EmptyMessage -> IntMessage
diff --git a/library/xml b/library/xml
-Subproject 08e1f71e89c1af6b3bef940914ed7f3d8fed89b
+Subproject b28296a8c0c2e5aaa522840598738109018f014
diff --git a/plugins/Dfusion/luafiles/common.lua b/plugins/Dfusion/luafiles/common.lua
index bf41d710..ecb5b502 100644
--- a/plugins/Dfusion/luafiles/common.lua
+++ b/plugins/Dfusion/luafiles/common.lua
@@ -476,28 +476,25 @@ function getSelectedUnit()
end
end
function getxyz() -- this will return pointers x,y and z coordinates.
- local off=VersionInfo.getGroup("Position"):getAddress("cursor_xyz") -- lets find where in memory its being held
- -- now lets read them (they are double words (or unsigned longs or 4 bits each) and go in sucesion
- local x=engine.peekd(off)
- local y=engine.peekd(off+4) --next is 4 from start
- local z=engine.peekd(off+8) --next is 8 from start
- --print("Pointer @:"..x..","..y..","..z)
+ local x=df.cursor.x
+ local y=df.cursor.y
+ local z=df.cursor.z
return x,y,z -- return the coords
end
-function GetCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
+function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
- local vector=engine.peek(VersionInfo.getGroup("Creatures"):getAddress("vector"),ptr_vector) -- load all creatures
- for i = 0, vector:size()-1 do -- look into all creatures offsets
- local curoff=vector:getval(i) -- get i-th creatures offset
- local cx=engine.peek(curoff,ptr_Creature.x) --get its coordinates
- local cy=engine.peek(curoff,ptr_Creature.y)
- local cz=engine.peek(curoff,ptr_Creature.z)
+ local vector=df.world.units.all -- load all creatures
+ for i = 0, vector.size-1 do -- look into all creatures offsets
+ local curpos=vector[i]:deref().pos --get its coordinates
+ local cx=curpos.x
+ local cy=curpos.y
+ local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
- return i --return index
+ return vector[i]:deref() --return index
end
end
print("Creature not found!")
- return -1
+ return nil
end
function Allocate(size)
diff --git a/plugins/Dfusion/luafiles/init.lua b/plugins/Dfusion/luafiles/init.lua
index ccb51f57..97766a97 100644
--- a/plugins/Dfusion/luafiles/init.lua
+++ b/plugins/Dfusion/luafiles/init.lua
@@ -72,7 +72,8 @@ table.insert(plugins,{"adv_tools","some tools for (mainly) advneturer hacking"})
table.insert(plugins,{"triggers","a function calling plug (discontinued...)"})
table.insert(plugins,{"migrants","multi race imigrations"})
-table.insert(plugins,{"onfunction","run lua on some df function"})--]=]
+--]=]
+table.insert(plugins,{"onfunction","run lua on some df function"})
table.insert(plugins,{"editor","edit internals of df",EditDF})
table.insert(plugins,{"saves","run current worlds's init.lua",RunSaved})
loadall(plugins)
diff --git a/plugins/Dfusion/luafiles/onfunction/locations.lua b/plugins/Dfusion/luafiles/onfunction/locations.lua
index 57043b7a..7849fc45 100644
--- a/plugins/Dfusion/luafiles/onfunction/locations.lua
+++ b/plugins/Dfusion/luafiles/onfunction/locations.lua
@@ -1,5 +1,5 @@
if WINDOWS then --windows function defintions
- onfunction.AddFunction(0x55499D+offsets.base(),"Move") --on creature move found with "watch mem=xcoord"
+ --[=[onfunction.AddFunction(0x55499D+offsets.base(),"Move") --on creature move found with "watch mem=xcoord"
onfunction.AddFunction(0x275933+offsets.base(),"Die",{creature="edi"}) --on creature death? found by watching dead flag then stepping until new function
onfunction.AddFunction(0x2c1834+offsets.base(),"CreateCreature",{protocreature="eax"}) --arena
onfunction.AddFunction(0x349640+offsets.base(),"AddItem",{item="esp"}) --or esp
@@ -7,8 +7,9 @@ if WINDOWS then --windows function defintions
onfunction.AddFunction(0x3d4301+offsets.base(),"Make_Item",{item_type="esp"})
onfunction.AddFunction(0x5af826+offsets.base(),"Hurt",{target="esi",attacker={off=0x74,rtype=DWORD,reg="esp"}})
onfunction.AddFunction(0x3D5886+offsets.base(),"Flip",{building="esi"})
- onfunction.AddFunction(0x35E340+offsets.base(),"ItemCreate")
+ onfunction.AddFunction(0x35E340+offsets.base(),"ItemCreate")--]=]
+ onfunction.AddFunction(4B34B6+offsets.base(),"ReactionFinish") --esp item. Ecx creature, edx?
else --linux
- onfunction.AddFunction(0x899befe+offsets.base(),"Move") -- found out by attaching watch...
- onfunction.AddFunction(0x850eecd+offsets.base(),"Die",{creature="ebx"}) -- same
+ --[=[onfunction.AddFunction(0x899befe+offsets.base(),"Move") -- found out by attaching watch...
+ onfunction.AddFunction(0x850eecd+offsets.base(),"Die",{creature="ebx"}) -- same--]=]
end
diff --git a/plugins/Dfusion/luafiles/tools/init.lua b/plugins/Dfusion/luafiles/tools/init.lua
index a12a0148..43747aba 100644
--- a/plugins/Dfusion/luafiles/tools/init.lua
+++ b/plugins/Dfusion/luafiles/tools/init.lua
@@ -264,6 +264,11 @@ function tools.empregnate(unit)
if unit==nil then
unit=getSelectedUnit()
end
+
+ if unit==nil then
+ unit=getCreatureAtPos(getxyz())
+ end
+
if unit==nil then
error("Failed to empregnate. Unit not selected/valide")
end
diff --git a/plugins/proto/rename.proto b/plugins/proto/rename.proto
index a6c3b055..aa1e95f4 100644
--- a/plugins/proto/rename.proto
+++ b/plugins/proto/rename.proto
@@ -2,6 +2,7 @@ package dfproto;
option optimize_for = LITE_RUNTIME;
+// RPC RenameSquad : RenameSquadIn -> EmptyMessage
message RenameSquadIn {
required int32 squad_id = 1;
@@ -9,6 +10,7 @@ message RenameSquadIn {
optional string alias = 3;
}
+// RPC RenameUnit : RenameUnitIn -> EmptyMessage
message RenameUnitIn {
required int32 unit_id = 1;