Split LuaWrapper.cpp into two files.
							parent
							
								
									053bfe345c
								
							
						
					
					
						commit
						d865d54a90
					
				| @ -0,0 +1,872 @@ | ||||
| /*
 | ||||
| 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 "LuaWrapper.h" | ||||
| 
 | ||||
| #include "MiscUtils.h" | ||||
| 
 | ||||
| #include <lua.h> | ||||
| #include <lauxlib.h> | ||||
| 
 | ||||
| using namespace DFHack; | ||||
| using namespace DFHack::LuaWrapper; | ||||
| 
 | ||||
| /**************************************
 | ||||
|  * 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, false); | ||||
|         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"); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * 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; | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * 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"); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * 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+1, base+2, 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+1, base+2, 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); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * 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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void LuaWrapper::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); | ||||
| } | ||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -0,0 +1,189 @@ | ||||
| /*
 | ||||
| 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" | ||||
| 
 | ||||
| #include <lua.h> | ||||
| #include <lauxlib.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * Internal header file of the lua wrapper. | ||||
|  */ | ||||
| 
 | ||||
| namespace DFHack { namespace LuaWrapper { | ||||
| 
 | ||||
| /*
 | ||||
|  * 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" | ||||
| #define DFHACK_NEW_NAME "DFHack::New" | ||||
| #define DFHACK_ASSIGN_NAME "DFHack::Assign" | ||||
| 
 | ||||
| /*
 | ||||
|  * 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) | ||||
| 
 | ||||
|     inline void lua_dup(lua_State *state) { lua_pushvalue(state, -1); } | ||||
|     inline void lua_swap(lua_State *state) { lua_insert(state, -2); } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Object references are represented as userdata instances | ||||
|      * with an appropriate metatable; the payload of userdata is | ||||
|      * this structure: | ||||
|      */ | ||||
|     struct DFRefHeader { | ||||
|         void *ptr; | ||||
|     }; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Push the pointer as DF object ref using metatable on the stack. | ||||
|      */ | ||||
|     void push_object_ref(lua_State *state, void *ptr); | ||||
|     void *get_object_ref(lua_State *state, int val_index); | ||||
| 
 | ||||
|     /*
 | ||||
|      * 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). | ||||
|     */ | ||||
|     void field_error(lua_State *state, int index, const char *err, const char *mode); | ||||
| 
 | ||||
|     /*
 | ||||
|      * If is_method is true, these use UPVAL_TYPETABLE to save a hash lookup. | ||||
|      */ | ||||
|     void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method = true); | ||||
|     void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool exact_type, bool in_method = true); | ||||
| 
 | ||||
|     void push_adhoc_pointer(lua_State *state, void *ptr, type_identity *target); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Verify that the object is a DF ref with UPVAL_METATABLE. | ||||
|      * If everything ok, extract the address. | ||||
|      */ | ||||
|     uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode); | ||||
| 
 | ||||
|     void LookupInTable(lua_State *state, void *id, const char *tname); | ||||
|     void SaveInTable(lua_State *state, void *node, const char *tname); | ||||
|     void SaveTypeInfo(lua_State *state, void *node); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Look up the key on the stack in DFHACK_TYPETABLE; | ||||
|      * if found, put result on the stack and return true. | ||||
|      */ | ||||
|     bool LookupTypeInfo(lua_State *state, bool in_method); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Make a metatable with most common fields, and an empty table for UPVAL_FIELDTABLE. | ||||
|      */ | ||||
|     void MakeMetatable(lua_State *state, type_identity *type, const char *kind); | ||||
|     /**
 | ||||
|      * Enable a metafield by injecting an entry into a UPVAL_FIELDTABLE. | ||||
|      */ | ||||
|     void EnableMetaField(lua_State *state, int ftable_idx, const char *name, void *id = NULL); | ||||
|     /**
 | ||||
|      * Set metatable properties common to all actual DF object references. | ||||
|      */ | ||||
|     void SetPtrMethods(lua_State *state, int meta_idx, int read_idx); | ||||
|     /**
 | ||||
|      * Add a struct-style (3 upvalues) metamethod to the metatable. | ||||
|      */ | ||||
|     void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, | ||||
|                          lua_CFunction function, const char *name); | ||||
|     /**
 | ||||
|      * Add a 6 upvalue metamethod to the metatable. | ||||
|      */ | ||||
|     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); | ||||
|     /**
 | ||||
|      * If ienum refers to a valid enum, attach its keys to UPVAL_FIELDTABLE, | ||||
|      * and the enum itself to the _enum metafield. | ||||
|      */ | ||||
|     void AttachEnumKeys(lua_State *state, int meta_idx, int ftable_idx, type_identity *ienum); | ||||
| }} | ||||
| 
 | ||||
		Loading…
	
		Reference in New Issue