diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a06fe35 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "external/Selene"] + path = external/Selene + url = /home/oleg/Downloads/Programming/Languages/Lua/Selene +[submodule "external/sol"] + path = external/sol + url = /home/oleg/Downloads/Programming/Languages/Lua/sol +[submodule "external/Catch"] + path = external/Catch + url = /home/okhryptul/Downloads/Programming/Languages/Lua/Catch +[submodule "external/boost_endian"] + path = external/boost_endian + url = https://github.com/boostorg/endian.git diff --git a/CMakeLists.txt b/CMakeLists.txt index fab096b..28f5998 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,39 +14,53 @@ add_definitions(-std=c++11) set(CMAKE_BUILD_TYPE Debug) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}) -find_package(Poco REQUIRED) +cmake_policy(SET CMP0015 NEW) -if(NOT Poco_FOUND) - message(FATAL_ERROR "Cannot find Poco package") +# ---------------------------------------------------------------------------------------- +# Packages +# ---------------------------------------------------------------------------------------- + +# Boost +set(Boost_USE_STATIC_LIBS OFF) +set(Boost_USE_MULTITHREADED ON) +set(Boost_USE_STATIC_RUNTIME OFF) +find_package(Boost 1.56.0 REQUIRED COMPONENTS program_options) + +if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) endif() -find_package(Lua REQUIRED) +# Poco +find_package(Poco REQUIRED) -if(NOT LUA_FOUND) - message(FATAL_ERROR "Cannot find Lua package") +if(NOT Poco_FOUND) + message(FATAL_ERROR "Cannot find Poco package") endif() # ---------------------------------------------------------------------------------------- -set(BinaryDigger_PackageFolder "${BinaryDigger_BINARY_DIR}/package/") -set(BinaryDigger_PluginsFolder "${BinaryDigger_PackageFolder}/plugins/") +set(BD_PACKAGE_FOLDER "${BinaryDigger_BINARY_DIR}/package/") +set(BD_PLUGINS_FOLDER "${BD_PACKAGE_FOLDER}/plugins/") +set(BD_SCRIPTS_FOLDER "${BD_PACKAGE_FOLDER}/scripts/") -execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${BinaryDigger_PluginsFolder}) +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${BD_PLUGINS_FOLDER}) # ---------------------------------------------------------------------------------------- set(CMAKE_INCLUDE_CURRENT_DIR ON) -include_directories("${BinaryDigger_SOURCE_DIR}/include" - "${BinaryDigger_SOURCE_DIR}/utils" - ${LUA_INCLUDE_DIR} +include_directories("include" + "external/boost_endian/include" + "utils" + "default_plugin" ${Poco_INCLUDE_DIR}) -link_directories(${Poco_LIBRARY_DIRS}) +link_directories("default_plugin" ${Poco_LIBRARY_DIRS}) # Solving problem with linking unused static library. # Source: http://stackoverflow.com/questions/17470350/c-cmake-how-to-add-a-linker-flag-to-an-unused-library-when-the-library-is-sp SET(bd_default_plugin_FORCE_LINK -Wl,--whole-archive bd_default_plugin -Wl,--no-whole-archive) # Build subdirectories. +add_subdirectory(external) add_subdirectory(include) add_subdirectory(default_plugin) add_subdirectory(gui) @@ -55,7 +69,7 @@ add_subdirectory(scripters) add_subdirectory(tests) add_subdirectory(tools) add_subdirectory(utils) -add_subdirectory(server) # "${BinaryDigger_BINARY_DIR}/server" +add_subdirectory(server) # installation #install(FILES MathFunctions.h DESTINATION include) diff --git a/default_plugin/block_templ.h b/default_plugin/block_templ.h new file mode 100644 index 0000000..737eda5 --- /dev/null +++ b/default_plugin/block_templ.h @@ -0,0 +1,51 @@ +/* + * block_templ.h + * + * Created on: 25 жовт. 2014 + * Author: oleg + */ + +#ifndef BLOCK_TEMPL_H_ +#define BLOCK_TEMPL_H_ + +#include "block_templ_base.h" + +/** + * Base class for the template structures declared via BD_TEMPL_DECL or BD_TEMPL macros. + * Also it used directly for simple C datatypes (char, int, ... or plain structures). + * + * @param T Data type + * @param simple_type true if + */ +template +class BlockTempl : public BlockTemplBase +{ +public: + BlockTempl(bd_block_io* _blob, bd_cstring _var_name, bd_u32 _count, BlockTemplBase* _parent, const bd_property_records &props) + : BlockTemplBase(_blob, _var_name, (bd_cstring) get_type_name().c_str(), _type, sizeof(T), _count, _parent, props) + { + } + + BlockTempl(bd_block_io* _block_io, bd_cstring _var_name, bd_cstring _type_name, bd_u32 _count, BlockTemplBase* _parent, const bd_property_records &props) + : BlockTemplBase(_block_io, _var_name, _type_name, _type, sizeof(T), _count, _parent, props) + { + } + + inline T value() + { + return static_cast(*this); + } + + void apply() {} +}; + +// Simple type templates +#define BD_BLOCK_TYPE_DECL(name, tp) \ + class name : public BlockTempl { \ + public: name(bd_block_io* _block_io, bd_cstring _var_name, bd_u32 _count, BlockTemplBase* _parent, \ + const bd_property_records &props) : \ + BlockTempl(_block_io, _var_name, _count, _parent, props) {}}; + BD_BLOCK_TYPES +#undef BD_BLOCK_TYPE_DECL + +#endif /* BLOCK_TEMPL_H_ */ diff --git a/default_plugin/block_templ_base.cpp b/default_plugin/block_templ_base.cpp index fd6b4c2..1cc472f 100644 --- a/default_plugin/block_templ_base.cpp +++ b/default_plugin/block_templ_base.cpp @@ -8,13 +8,14 @@ #include "block_templ_base.h" BlockTemplBase::BlockTemplBase(bd_block_io *_block_io, bd_cstring _name, bd_cstring _type_name, bd_block_type _type, - bd_u64 _size, bd_u32 _count, BlockTemplBase *_parent) + bd_u64 _size, bd_u32 _count, BlockTemplBase *_parent, const bd_property_records &props) { - block_io = _block_io; - name = strdup(_name); - type_name = strdup(_type_name); - type = _type; - parent = _parent; + block_io = _block_io; + name = strdup(_name); + type_name = strdup(_type_name); + type = _type; + parent = _parent; + templ_properties = props; children.child = 0; children.count = 0; @@ -41,7 +42,7 @@ BlockTemplBase::BlockTemplBase(bd_block_io *_block_io, bd_cstring _name, bd_cstr for (auto i = 0; i < count; ++i) { // add single element - new BlockTemplBase(_block_io, _name, _type_name, _type, _size, 0, this); + new BlockTemplBase(_block_io, _name, _type_name, _type, _size, 0, this, props); } bd_u64 pos = getPosition(); @@ -122,6 +123,47 @@ std::string BlockTemplBase::getString() const return result; } +std::string BlockTemplBase::to_string() +{ + if (is_array == BD_TRUE) + { + if (type == BD_CHAR) + return getString(); + return ""; + } + + switch (getType()) + { + case BD_TEMPL: + return ""; +#define BD_BLOCK_TYPE_DECL(name, tp) \ + case BD_##name: { \ + auto val = (tp) *this; \ + return std::to_string(val); } + BD_BLOCK_TYPES +#undef BD_BLOCK_TYPE_DECL + } + + return ""; +} + +bd_property BlockTemplBase::get_property(const std::string &name) +{ + if (obj_properties.find(name) != obj_properties.end()) + return obj_properties[name]; + + if (templ_properties.find(name) != templ_properties.end()) + return templ_properties[name]; + + if (parent != nullptr) + return ((BlockTemplBase *) parent)->get_property(name); + + if (default_property.find(name) != default_property.end()) + return default_property[name]; + + return bd_property(); +} + // -------------------------------------------------------------------------------------------------------------------- bool operator ==(const BlockTemplBase& val1, const char* val2) diff --git a/default_plugin/block_templ_base.h b/default_plugin/block_templ_base.h index d2514a0..5acdacf 100644 --- a/default_plugin/block_templ_base.h +++ b/default_plugin/block_templ_base.h @@ -10,81 +10,22 @@ #include #include +#include #include #include #include +#include #include #include - -#include - -class BlockTemplException : public std::exception -{ -public: - BlockTemplException(bd_result result, const std::string& msg) : result(result), message(msg) - {} - - BlockTemplException(const std::string& msg) : result(BD_EUSER), message(msg) - {} - - ~BlockTemplException() throw() - {} - - bd_result getResult() const - { - return result; - } - - const char* getMessage() const - { - return message.c_str(); - } - - const char* what() const throw() - { - return message.c_str(); - } - -private: - bd_result result; - std::string message; -}; - -#define bd_throw(msg) throw BlockTemplException(msg) -#define bd_throw_f(msg, ...) bd_throw(Poco::format(msg, __VA_ARGS__)) - -#define bd_require_true(cond, msg) if (!(cond)) bd_throw(msg) -#define bd_require_false(cond, msg) if (cond) bd_throw(msg) - -#define bd_require_is_null(cond, msg) bd_require_true(cond == 0, msg) -#define bd_require_not_null(cond, msg) bd_require_true(cond != 0, msg) - -#define bd_require_eq(v1, v2, msg) bd_require_true(v1 == v2, msg) -#define bd_require_ne(v1, v2, msg) bd_require_true(v1 != v2, msg) - -#define bd_require_true_f(cond, msg, ...) if (!(cond)) bd_throw_f(msg, __VA_ARGS__) -#define bd_require_false_f(cond, msg, ...) if (cond) bd_throw_f(msg, __VA_ARGS__) - -#define bd_require_is_null_f(cond, msg, ...) bd_require_true_f(cond == 0, msg, __VA_ARGS__) -#define bd_require_not_null_f(cond, msg, ...) bd_require_true_f(cond != 0, msg, __VA_ARGS__) - -#define bd_require_eq_f(v1, v2, msg, ...) bd_require_true_f(v1 == v2, msg, __VA_ARGS__) -#define bd_require_ne_f(v1, v2, msg, ...) bd_require_true_f(v1 != v2, msg, __VA_ARGS__) - -#define bd_result_throw(result, msg) { \ - bd_result res = result; \ - bd_require_true(BD_SUCCEED(res), msg); } - -#define bd_result_throw_f(result, msg, ...) { \ - bd_result res = result; \ - bd_require_true_f(BD_SUCCEED(res), msg, ## __VA_ARGS__); } +#include "property.h" +#include "exception.h" class BlockTemplBase : public bd_block { public: BlockTemplBase(bd_block_io *_block_io, bd_cstring _name, bd_cstring _type_name, bd_block_type _type, bd_u64 _size, - bd_u32 _count, BlockTemplBase *_parent); + bd_u32 _count, BlockTemplBase *_parent, const bd_property_records &props); ~BlockTemplBase(); @@ -127,25 +68,15 @@ class BlockTemplBase : public bd_block BlockTemplBase* get(const char *block_name, bd_u32 index = 0) const; - const BlockTemplBase& getBlock(const char *block_name, bd_u32 index = 0) const + BlockTemplBase& getBlock(const char *block_name, bd_u32 index = 0) const { return *get(block_name, index); } -#pragma GCC diagnostic ignored "-Wreturn-local-addr" - template - inline T value() const - { - bd_require_true(type != BD_TEMPL, "Cannot get value of template object"); - - T val; - get_data(offset, sizeof(T), &val); - return val; - } -#pragma GCC diagnostic pop - std::string getString() const; + std::string to_string(); + bd_block_io* getBlockIo() { return block_io; } void getData(void* val) const @@ -155,16 +86,27 @@ class BlockTemplBase : public bd_block get_data(offset, size, val); } - // casting operators -#define BD_BLOCK_TYPE_DECL(name, tp) \ - operator tp() const { \ - bd_require_true_f(type == BD_##name, "Cannot cast value of template object of type %d to " #name, type); \ - tp val; get_data(offset, sizeof(tp), &val); return val; } + // casting operators for default types +#define BD_BLOCK_TYPE_DECL(name, tp) \ + operator tp() { \ + bd_require_true_f(type == BD_##name, \ + "Cannot cast value of template object of type %d to " #name, type); \ + tp val; get_data(offset, sizeof(tp), &val); \ + return correct_endian(val, (bd_endian) get_property("endian").value.i); } BD_BLOCK_TYPES #undef BD_BLOCK_TYPE_DECL + void set_properties(const bd_property_records &props) + { + obj_properties = props; + } + + bd_property get_property(const std::string &name); + protected: bd_block_io *block_io; + bd_property_records templ_properties; + bd_property_records obj_properties; typedef std::vector Children; Children children_vec; @@ -185,43 +127,6 @@ class BlockTemplBase : public bd_block } }; -/** - * Base class for the template structures declared via BD_TEMPL_DECL or BD_TEMPL macros. - * Also it used directly for simple C datatypes (char, int, ... or plain structures). - * - * @param T Data type - * @param simple_type true if - */ -template -class BlockTempl : public BlockTemplBase -{ -public: - BlockTempl(bd_block_io* _blob, bd_cstring _var_name, bd_u32 _count, BlockTemplBase* _parent) - : BlockTemplBase(_blob, _var_name, (bd_cstring) get_type_name().c_str(), _type, sizeof(T), _count, _parent) - { - } - - BlockTempl(bd_block_io* _block_io, bd_cstring _var_name, bd_cstring _type_name, bd_u32 _count, BlockTemplBase* _parent) - : BlockTemplBase(_block_io, _var_name, _type_name, _type, sizeof(T), _count, _parent) - { - } - - inline T value() const - { - return BlockTemplBase::value(); - } - - void apply() {} -}; - -// Simple type templates -#define BD_BLOCK_TYPE_DECL(name, tp) \ - class name : public BlockTempl { \ - public: name(bd_block_io* _block_io, bd_cstring _var_name, bd_u32 _count, BlockTemplBase* _parent) : \ - BlockTempl(_block_io, _var_name, _count, _parent) {}}; - BD_BLOCK_TYPES -#undef BD_BLOCK_TYPE_DECL - bool operator ==(const BlockTemplBase& val1, const char* val2); #endif /* DEFAULT_TEMPL_BASE_H_ */ diff --git a/default_plugin/default_plugin.cpp b/default_plugin/default_plugin.cpp index 06490f0..94e014d 100644 --- a/default_plugin/default_plugin.cpp +++ b/default_plugin/default_plugin.cpp @@ -7,6 +7,8 @@ #include "default_plugin.h" +#include + extern void register_plugin(std::string &name, bool &is_scripter); PluginContext::array_type PluginContext::registeredTemplates; @@ -50,6 +52,12 @@ bd_result bd_initialize_plugin(bd_string name, bd_u32 name_size, bd_u32 *templ_c return BD_SUCCESS; } +bd_result bd_finalize_plugin() +{ + PluginContext::freeTemplates(); + return BD_SUCCESS; +} + bd_result bd_template_name(bd_u32 index, bd_string name, bd_u32 name_size) { bd_check_not_null(name); @@ -64,6 +72,7 @@ bd_result bd_template_name(bd_u32 index, bd_string name, bd_u32 name_size) return BD_SUCCESS; } +// TODO: specify properties for the top level element bd_result bd_apply_template(bd_u32 index, bd_block_io *block_io, bd_block **block, bd_cstring script) { bd_check_not_null(block_io); @@ -105,8 +114,23 @@ bd_result bd_free_template(bd_u32 index, bd_block *block) return result; } -bd_result bd_finalize_plugin() +bd_result bd_get_string_value(bd_block *block, bd_string buf, bd_u32 size) { - PluginContext::freeTemplates(); + bd_check_not_null(block); + bd_check_not_null(buf); + + auto templ = (BlockTemplBase *) block; + auto value = templ->to_string(); + + if (value.size() + 1 > size) + { + PluginContext::setError((boost::format("String buffer for the value is too small. Expected at least %1% but was %2%") + % (value.size() + 1) % size).str()); + return BD_ERROR; + } + + strncpy(buf, value.c_str(), value.size()); + buf[value.size()] = 0; + return BD_SUCCESS; } diff --git a/default_plugin/default_plugin.h b/default_plugin/default_plugin.h index 575a4b6..1b28c43 100644 --- a/default_plugin/default_plugin.h +++ b/default_plugin/default_plugin.h @@ -8,11 +8,11 @@ #ifndef HIERARCHYDIGGERMACRO_H_ #define HIERARCHYDIGGERMACRO_H_ -#include "block_templ_base.h" +#include "block_templ.h" #include #define VAL(var) var->value() -#define GET(obj, type, field) const type##_T field = (type##_T) obj->getBlock(#field) +#define GET(obj, type, field) type##_T field = (type##_T) obj->getBlock(#field) // TODO: implement local variables //#define LOCAL(name, val) @@ -23,7 +23,7 @@ //#define REGISTER_STRUCT(name) const int BD_##name = BD_STRUCT; #define ARR(type, var, count, ...) \ - auto var = new type(block_io, (bd_cstring) #var, count, this); var->apply(__VA_ARGS__) + auto var = new type(block_io, (bd_cstring) #var, count, this, bd_property_records()); var->apply(__VA_ARGS__) #define VAR(type, var, ...) ARR(type, var, 0, __VA_ARGS__) // ----------------------------------------------------------------------------------------- @@ -32,12 +32,14 @@ #define TEMPL_DECL(templ_name, ...) \ class templ_name : public BlockTempl { public: \ - templ_name(bd_block_io* _block_io, bd_cstring _var_name, bd_u32 _count, BlockTemplBase* _parent); \ + templ_name(bd_block_io* _block_io, bd_cstring _var_name, bd_u32 _count, BlockTemplBase* _parent, \ + const bd_property_records &props); \ void apply(__VA_ARGS__); }; #define TEMPL_IMPL(templ_name, ...) \ - templ_name::templ_name(bd_block_io* _block_io, bd_cstring _var_name, bd_u32 _count, BlockTemplBase* _parent) \ - : BlockTempl(_block_io, _var_name, _count, _parent) {} \ + templ_name::templ_name(bd_block_io* _block_io, bd_cstring _var_name, bd_u32 _count, BlockTemplBase* _parent, \ + const bd_property_records &props) \ + : BlockTempl(_block_io, _var_name, _count, _parent, props) {} \ void templ_name::apply(__VA_ARGS__) { #define TEMPL(templ_name, ...) \ @@ -64,6 +66,7 @@ class RegisteredTemplWrapper std::string getName() { return type_name; } + // TODO: specify properties virtual bd_result applyTemplate(bd_block_io *block_io, bd_cstring script, bd_block **result, std::string &err) noexcept = 0; virtual bd_result freeTemplate(bd_block *block, std::string &err) noexcept = 0; @@ -127,7 +130,7 @@ class TemplWrapper: public RegisteredTemplWrapper try { - auto val = new T(block_io, (bd_cstring) "root", 0, 0); + auto val = new T(block_io, (bd_cstring) "root", 0, 0, bd_property_records()); val->apply(); *result = val; diff --git a/default_plugin/exception.h b/default_plugin/exception.h new file mode 100644 index 0000000..5a32959 --- /dev/null +++ b/default_plugin/exception.h @@ -0,0 +1,74 @@ +/* + * exception.h + * + * Created on: 25 жовт. 2014 + * Author: oleg + */ + +#ifndef EXCEPTION_H_ +#define EXCEPTION_H_ + +#include + +class BlockTemplException : public std::exception +{ +public: + BlockTemplException(bd_result result, const std::string& msg) : result(result), message(msg) + {} + + BlockTemplException(const std::string& msg) : result(BD_EUSER), message(msg) + {} + + ~BlockTemplException() throw() + {} + + bd_result getResult() const + { + return result; + } + + const char* getMessage() const + { + return message.c_str(); + } + + const char* what() const throw() + { + return message.c_str(); + } + +private: + bd_result result; + std::string message; +}; + +#define bd_throw(msg) throw BlockTemplException(msg) +#define bd_throw_f(msg, ...) bd_throw(Poco::format(msg, __VA_ARGS__)) + +#define bd_require_true(cond, msg) if (!(cond)) bd_throw(msg) +#define bd_require_false(cond, msg) if (cond) bd_throw(msg) + +#define bd_require_is_null(cond, msg) bd_require_true(cond == 0, msg) +#define bd_require_not_null(cond, msg) bd_require_true(cond != 0, msg) + +#define bd_require_eq(v1, v2, msg) bd_require_true(v1 == v2, msg) +#define bd_require_ne(v1, v2, msg) bd_require_true(v1 != v2, msg) + +#define bd_require_true_f(cond, msg, ...) if (!(cond)) bd_throw_f(msg, __VA_ARGS__) +#define bd_require_false_f(cond, msg, ...) if (cond) bd_throw_f(msg, __VA_ARGS__) + +#define bd_require_is_null_f(cond, msg, ...) bd_require_true_f(cond == 0, msg, __VA_ARGS__) +#define bd_require_not_null_f(cond, msg, ...) bd_require_true_f(cond != 0, msg, __VA_ARGS__) + +#define bd_require_eq_f(v1, v2, msg, ...) bd_require_true_f(v1 == v2, msg, __VA_ARGS__) +#define bd_require_ne_f(v1, v2, msg, ...) bd_require_true_f(v1 != v2, msg, __VA_ARGS__) + +#define bd_result_throw(result, msg) { \ + bd_result res = result; \ + bd_require_true(BD_SUCCEED(res), msg); } + +#define bd_result_throw_f(result, msg, ...) { \ + bd_result res = result; \ + bd_require_true_f(BD_SUCCEED(res), msg, ## __VA_ARGS__); } + +#endif /* EXCEPTION_H_ */ diff --git a/default_plugin/property.cpp b/default_plugin/property.cpp new file mode 100644 index 0000000..e919862 --- /dev/null +++ b/default_plugin/property.cpp @@ -0,0 +1,12 @@ +/* + * property.cpp + * + * Created on: 25 жовт. 2014 + * Author: oleg + */ + +#include "property.h" + +bd_property_records default_property = { + {"endian", bd_property((bd_i32) BD_ENDIAN_NONE)}, +}; diff --git a/default_plugin/property.h b/default_plugin/property.h new file mode 100644 index 0000000..3174c3c --- /dev/null +++ b/default_plugin/property.h @@ -0,0 +1,116 @@ +/* + * property.h + * + * Created on: 25 жовт. 2014 + * Author: oleg + */ + +#ifndef PROPERTY_H_ +#define PROPERTY_H_ + +typedef bd_result (*bd_to_string)(bd_block *block, char *buf, bd_u32 size); + +union bd_property_value +{ + bd_i32 i; + bd_f64 d; + bd_string s; + bd_to_string to_s; +}; + +struct bd_property +{ + bd_property_type type; + bd_property_value value; + + bd_property() : type(BD_PROP_UNDEF) + { + value.i = 0; + memset(&value, 0, sizeof(value)); + } + + bd_property(const bd_property &other) : type(BD_PROP_UNDEF) + { + switch (other.type) + { + case BD_PROP_INTEGER: + *this = bd_property(other.value.i); + break; + case BD_PROP_DOUBLE: + *this = bd_property(other.value.d); + break; + case BD_PROP_STRING: + *this = bd_property(other.value.s); + break; + case BD_PROP_TO_STRING: + *this = bd_property(other.value.to_s); + break; + default: + *this = bd_property(); + break; + } + } + + bd_property(bd_i32 val) : type(BD_PROP_INTEGER) + { + value.i = val; + } + + bd_property(bd_f64 val) : type(BD_PROP_DOUBLE) + { + value.d = val; + } + + bd_property(const bd_string val) : type(BD_PROP_STRING) + { + if (val != nullptr) + { + value.s = new bd_char[strlen(val) + 1]; + strncpy(value.s, val, strlen(val)); + value.s[strlen(val)] = 0; + } + else + { + value.s = nullptr; + } + } + + bd_property(bd_to_string val) : type(BD_PROP_TO_STRING) + { + value.to_s = val; + } + + ~bd_property() + { + if (type == BD_PROP_STRING && value.s != nullptr) + delete[] value.s; + } +}; + +typedef std::map bd_property_records; +extern bd_property_records default_property; + +enum bd_endian +{ + BD_ENDIAN_NONE, // use system specific default endian + BD_ENDIAN_LIT, // little endian + BD_ENDIAN_BIG, // big endian +}; + +template +T correct_endian(T val, bd_endian endian) +{ + if (endian == BD_ENDIAN_LIT) + { + T res = boost::endian::little_endian_value(val); + return res; + } + if (endian == BD_ENDIAN_BIG) + { + T res = boost::endian::big_endian_value(val); + return res; + } + return val; +} + +#endif /* PROPERTY_H_ */ diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt new file mode 100644 index 0000000..7ecf03f --- /dev/null +++ b/external/CMakeLists.txt @@ -0,0 +1,2 @@ + +#include_directories("boost_endian/include") diff --git a/external/boost_endian b/external/boost_endian new file mode 160000 index 0000000..49866e0 --- /dev/null +++ b/external/boost_endian @@ -0,0 +1 @@ +Subproject commit 49866e07842e4b2b9c2d8e76b2c7fb3a61a1bdd2 diff --git a/gui/BinaryDigger/CMakeLists.txt b/gui/BinaryDigger/CMakeLists.txt index 2b0b4dd..79953cd 100644 --- a/gui/BinaryDigger/CMakeLists.txt +++ b/gui/BinaryDigger/CMakeLists.txt @@ -34,8 +34,8 @@ target_link_libraries(BinaryDigger ${Qt5Core_QTMAIN_LIBRARIES}) add_custom_command(TARGET BinaryDigger POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ - "${BinaryDigger_PackageFolder}" - COMMENT "BinaryDigger copied to ${BinaryDigger_PackageFolder}" + "${BD_PACKAGE_FOLDER}" + COMMENT "BinaryDigger copied to ${BD_PACKAGE_FOLDER}" ) qt5_use_modules(BinaryDigger Core Gui Widgets) diff --git a/gui/BinaryDigger/mainwindow.cpp b/gui/BinaryDigger/mainwindow.cpp index a42a9de..ff68830 100644 --- a/gui/BinaryDigger/mainwindow.cpp +++ b/gui/BinaryDigger/mainwindow.cpp @@ -92,10 +92,11 @@ void MainWindow::loadPlugin(PluginLibrary &pl) { pl.plugin.result_message = (bd_result_message_t) loadPluginFunction(pl.library, "bd_result_message"); pl.plugin.initialize_plugin = (bd_initialize_plugin_t) loadPluginFunction(pl.library, "bd_initialize_plugin"); + pl.plugin.finalize_plugin = (bd_finalize_plugin_t) loadPluginFunction(pl.library, "bd_finalize_plugin"); pl.plugin.template_name = (bd_template_name_t) loadPluginFunction(pl.library, "bd_template_name"); pl.plugin.apply_template = (bd_apply_template_t) loadPluginFunction(pl.library, "bd_apply_template"); pl.plugin.free_template = (bd_free_template_t) loadPluginFunction(pl.library, "bd_free_template"); - pl.plugin.finalize_plugin = (bd_finalize_plugin_t) loadPluginFunction(pl.library, "bd_finalize_plugin"); + pl.plugin.get_string_value = (bd_get_string_value_t) loadPluginFunction(pl.library, "bd_get_string_value"); } catch (const BDException &/*ex*/) { @@ -208,7 +209,7 @@ void MainWindow::applyTemplate(bd_block *block) // QMessageBox::information(this, tr("Plugin: ") + dataFile, tr("Template applied successfully."), QMessageBox::Ok); - treeModel = new TreeModel(treeModelHeaders, &templBlob, block); + treeModel = new TreeModel(treeModelHeaders, &plugins[pluginIndex].plugin, &templBlob, block); treeView->setModel(treeModel); resizeTreeColumns(); diff --git a/gui/BinaryDigger/treemodel.cpp b/gui/BinaryDigger/treemodel.cpp index 9c20085..dd509c6 100644 --- a/gui/BinaryDigger/treemodel.cpp +++ b/gui/BinaryDigger/treemodel.cpp @@ -7,8 +7,8 @@ #include -TreeModel::TreeModel(const QStringList &headers, bd_block_io *block_io, const bd_block *root_block, QObject *parent) - : QAbstractItemModel(parent), block_io(block_io), hdRootBlock(root_block) +TreeModel::TreeModel(const QStringList &headers, bd_plugin *plugin, bd_block_io *block_io, const bd_block *root_block, QObject *parent) + : QAbstractItemModel(parent), plugin(plugin), block_io(block_io), hdRootBlock(root_block) { QVector rootData; foreach (QString header, headers) @@ -226,73 +226,94 @@ void TreeModel::generateModelData(const bd_block *block, bd_u32 index, TreeItem< } else // char array or array element { - bd_u64 offset = block->offset + (index == (bd_u32)-1 ? 0 : index * block->elem_size); - switch (block->type) - { - case BD_CHAR: + bd_u32 buf_size = 100; + + if (block->is_array == BD_TRUE && block->type == BD_CHAR) { - char *data = new char[block->count + 1]; - if (!BD_SUCCEED(block_io->get_datap(block_io, block->offset, block->size, data))) - { - throw BDException(tr("Could not read single CHAR or CHAR array")); - } - data[block->count] = '\0'; - str = QString("\"%1\"").arg(data); - break; + buf_size = block->size + 1; } - case BD_UCHAR: - { - unsigned char data; - if (!BD_SUCCEED(block_io->get_datap(block_io, offset, block->elem_size, &data))) - { - throw BDException(tr("Could not read UCHAR")); - } - str = QString("%1").arg(data); - } - break; - case BD_WORD: - { - WORD_T data; - if (!BD_SUCCEED(block_io->get_datap(block_io, offset, block->elem_size, &data))) - { - throw BDException(tr("Could not read WORD")); - } - str = QString("%1").arg(data); - } - break; - case BD_DWORD: - { - DWORD_T data; - if (!BD_SUCCEED(block_io->get_datap(block_io, offset, block->elem_size, &data))) - { - throw BDException(tr("Could not read DWORD")); - } - str = QString("%1").arg(data); - } - break; - case BD_QWORD: - { - QWORD_T data; - if (!BD_SUCCEED(block_io->get_datap(block_io, offset, block->elem_size, &data))) - { - throw BDException(tr("Could not read QWORD")); - } - str = QString("%1").arg(data); - } - break; - case BD_DOUBLE: - { - DOUBLE_T data; - if (!BD_SUCCEED(block_io->get_datap(block_io, offset, block->elem_size, &data))) - { - throw BDException(tr("Could not read DOUBLE")); - } - str = QString("%1").arg(data); - } - break; - default: - break; + + bd_string buf = new bd_char[buf_size]; + + bd_result res = plugin->get_string_value((bd_block *) block, buf, buf_size); + if (!(res == BD_SUCCESS)) + { + char buf[1024]; + plugin->result_message(res, (bd_string) buf, sizeof(buf)); + throw BDException(tr("%1\n%2").arg(tr("Cannot get string representation of block value")).arg(buf)); } + + str = buf; + + delete[] buf; + +// bd_u64 offset = block->offset + (index == (bd_u32)-1 ? 0 : index * block->elem_size); +// switch (block->type) +// { +// case BD_CHAR: +// { +// char *data = new char[block->count + 1]; +// if (!BD_SUCCEED(block_io->get_datap(block_io, block->offset, block->size, data))) +// { +// throw BDException(tr("Could not read single CHAR or CHAR array")); +// } +// data[block->count] = '\0'; +// str = QString("\"%1\"").arg(data); +// break; +// } +// case BD_UCHAR: +// { +// unsigned char data; +// if (!BD_SUCCEED(block_io->get_datap(block_io, offset, block->elem_size, &data))) +// { +// throw BDException(tr("Could not read UCHAR")); +// } +// str = QString("%1").arg(data); +// } +// break; +// case BD_WORD: +// { +// WORD_T data; +// if (!BD_SUCCEED(block_io->get_datap(block_io, offset, block->elem_size, &data))) +// { +// throw BDException(tr("Could not read WORD")); +// } +// str = QString("%1").arg(data); +// } +// break; +// case BD_DWORD: +// { +// DWORD_T data; +// if (!BD_SUCCEED(block_io->get_datap(block_io, offset, block->elem_size, &data))) +// { +// throw BDException(tr("Could not read DWORD")); +// } +// str = QString("%1").arg(data); +// } +// break; +// case BD_QWORD: +// { +// QWORD_T data; +// if (!BD_SUCCEED(block_io->get_datap(block_io, offset, block->elem_size, &data))) +// { +// throw BDException(tr("Could not read QWORD")); +// } +// str = QString("%1").arg(data); +// } +// break; +// case BD_DOUBLE: +// { +// DOUBLE_T data; +// if (!BD_SUCCEED(block_io->get_datap(block_io, offset, block->elem_size, &data))) +// { +// throw BDException(tr("Could not read DOUBLE")); +// } +// str = QString("%1").arg(data); +// } +// break; +// default: +// break; +// } } child->setData(1, str); // Value diff --git a/gui/BinaryDigger/treemodel.h b/gui/BinaryDigger/treemodel.h index 468c9d1..317b0b6 100644 --- a/gui/BinaryDigger/treemodel.h +++ b/gui/BinaryDigger/treemodel.h @@ -14,7 +14,7 @@ class TreeModel : public QAbstractItemModel Q_OBJECT public: - TreeModel(const QStringList &headers, bd_block_io *block_io, const bd_block *root_block, + TreeModel(const QStringList &headers, bd_plugin *plugin, bd_block_io *block_io, const bd_block *root_block, QObject *parent = 0); ~TreeModel(); @@ -57,6 +57,7 @@ class TreeModel : public QAbstractItemModel private: void setupModelData(const QStringList &lines, TreeItem *parent); + bd_plugin *plugin; bd_block_io *block_io; TreeItem *rootItem; diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 359994e..306d011 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -1,7 +1,7 @@ set(CMAKE_PREFIX_PATH "$ENV{QT_HOME}/5.3/gcc_64/") -find_package(Qt5Widgets) +find_package(Qt5Widgets REQUIRED) set(CMAKE_AUTOMOC ON) diff --git a/include/bd.h b/include/bd.h index bb27001..b26dc15 100644 --- a/include/bd.h +++ b/include/bd.h @@ -7,31 +7,6 @@ BD_C_EXTERN_BEGIN -#define BD_BLOCK_TYPES \ - BD_BLOCK_TYPE_DECL(CHAR, bd_i8) \ - BD_BLOCK_TYPE_DECL(UCHAR, bd_u8) \ - BD_BLOCK_TYPE_DECL(WORD, bd_i16) \ - BD_BLOCK_TYPE_DECL(DWORD, bd_i32) \ - BD_BLOCK_TYPE_DECL(QWORD, bd_i64) \ - BD_BLOCK_TYPE_DECL(DOUBLE, bd_f64) - -typedef enum -{ - BD_TEMPL = 0, // user defined template - - // simple types -#define BD_BLOCK_TYPE_DECL(name, type) BD_##name, - BD_BLOCK_TYPES -#undef BD_BLOCK_TYPE_DECL - - BD_STRUCT, // user defined plain structure -} bd_block_type; - -// Template types -#define BD_BLOCK_TYPE_DECL(name, type) typedef type name##_T; - BD_BLOCK_TYPES -#undef BD_BLOCK_TYPE_DECL - typedef struct bd_block_io { bd_result (*get_pos) (bd_block_io *self, bd_u64 *pos); @@ -64,11 +39,35 @@ typedef struct bd_block } children; } bd_block; +typedef struct bd_message +{ + bd_result code; // message result code + bd_cstring msg; // message text + bd_pointer block; // block to which this message relates + bd_u32 count; // number of occurrences of this message (to avoid duplications) +} bd_message; + +typedef struct bd_messages +{ + bd_message *message; // array of messages + bd_u16 count; // number of messages +} bd_messages; + +/** + * Get last operation messages. + * + * @param messages [OUT] Array of messages. + * @return Result code @see(bd_result) for details + */ +BD_EXPORT bd_result bd_get_messages(bd_messages **messages); +typedef bd_result (*bd_get_messages_t)(bd_messages **messages); + /** * Get text message by its result code. * - * @param result Result code. - * @param msg [OUT] Message string. + * @param result Result code. + * @param msg [OUT] Message buffer. + * @param msg_size Message buffer size. * @return Result code @see(bd_result) for details */ BD_EXPORT bd_result bd_result_message(bd_result result, bd_string msg, bd_u32 msg_size); @@ -77,17 +76,25 @@ typedef bd_result (*bd_result_message_t)(bd_result result, bd_string msg, bd_u32 /** * Initialize plugin. * - * @param name Plugin name. + * @param name Plugin name. * @param templ_count [OUT] Return registered plugin templetes count. * @return Result code @see(bd_result) for details */ BD_EXPORT bd_result bd_initialize_plugin(bd_string name, bd_u32 name_size, bd_u32 *templ_count); typedef bd_result (*bd_initialize_plugin_t)(bd_string name, bd_u32 name_size, bd_u32 *templ_count); +/** + * Finalize plugin. + * + * @return Result code @see(bd_result) for details + */ +BD_EXPORT bd_result bd_finalize_plugin(); +typedef bd_result (*bd_finalize_plugin_t)(); + /** * Retrieve template name by index. * - * @param index Template index + * @param index Template index * @param name [OUT] Template name * @return Result code @see(bd_result) for details */ @@ -116,21 +123,30 @@ BD_EXPORT bd_result bd_free_template(bd_u32 index, bd_block *block); typedef bd_result (*bd_free_template_t)(bd_u32 index, bd_block *block); /** - * Finalize plugin. + * Get string representation of block value. Can be applied only to the basic types: int, string, ... * + * @param block Blocks item + * @param buf [OUT] Result buffer + * @param size Result buffer size * @return Result code @see(bd_result) for details */ -BD_EXPORT bd_result bd_finalize_plugin(); -typedef bd_result (*bd_finalize_plugin_t)(); +BD_EXPORT bd_result bd_get_string_value(bd_block *block, bd_string buf, bd_u32 size); +typedef bd_result (*bd_get_string_value_t)(bd_block *block, bd_string buf, bd_u32 size); -typedef struct + +typedef struct bd_plugin { - bd_result_message_t result_message; + bd_get_messages_t get_messages; + bd_result_message_t result_message; // obsolete + bd_initialize_plugin_t initialize_plugin; + bd_finalize_plugin_t finalize_plugin; + bd_template_name_t template_name; bd_apply_template_t apply_template; bd_free_template_t free_template; - bd_finalize_plugin_t finalize_plugin; + + bd_get_string_value_t get_string_value; } bd_plugin; BD_C_EXTERN_END diff --git a/include/types.h b/include/types.h index 432a33d..903de73 100644 --- a/include/types.h +++ b/include/types.h @@ -46,5 +46,53 @@ enum bd_base_result_code }; #define BD_SUCCEED(result) (result == BD_SUCCESS) +#define BD_BLOCK_TYPES \ + BD_BLOCK_TYPE_DECL(CHAR, bd_i8) \ + BD_BLOCK_TYPE_DECL(UCHAR, bd_u8) \ + BD_BLOCK_TYPE_DECL(WORD, bd_i16) \ + BD_BLOCK_TYPE_DECL(DWORD, bd_i32) \ + BD_BLOCK_TYPE_DECL(QWORD, bd_i64) \ + BD_BLOCK_TYPE_DECL(DOUBLE, bd_f64) + +typedef enum /*bd_block_type*/ +{ + BD_TEMPL = 0, // user defined template + + // simple types +#define BD_BLOCK_TYPE_DECL(name, type) BD_##name, + BD_BLOCK_TYPES +#undef BD_BLOCK_TYPE_DECL + + BD_STRUCT, // user defined plain structure +} bd_block_type; + +// Template types +#define BD_BLOCK_TYPE_DECL(name, type) typedef type name##_T; + BD_BLOCK_TYPES +#undef BD_BLOCK_TYPE_DECL + +//const char *get_block_type_name(bd_block_type type) +//{ +// switch (type) +// { +// case BD_TEMPL: +// return "TEMPL"; +//#define BD_BLOCK_TYPE_DECL(name, tp) \ +// case BD_##name: { \ +// return #name; } +// BD_BLOCK_TYPES +//#undef BD_BLOCK_TYPE_DECL +// } +// return ""; +//} + +typedef enum /*bd_property_type*/ +{ + BD_PROP_UNDEF, // undefined + BD_PROP_INTEGER, // bd_i32 + BD_PROP_DOUBLE, // bd_f64 + BD_PROP_STRING, // bd_string + BD_PROP_TO_STRING, // bd_result (*func)(bd_block *block, char *buf, bd_u32 size); +} bd_property_type; #endif // BD_TYPES_H_ diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 2519e82..c13b06e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,7 +1,4 @@ -include_directories("${BinaryDigger_SOURCE_DIR}/default_plugin") -link_directories("${BinaryDigger_BINARY_DIR}/default_plugin") - file(GLOB plugins "*.cpp") foreach(plugin_file ${plugins}) get_filename_component(plugin_name ${plugin_file} NAME_WE) @@ -19,7 +16,7 @@ foreach(plugin_file ${plugins}) add_custom_command(TARGET ${plugin_name} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ - "${BinaryDigger_PluginsFolder}" - COMMENT "${plugin_name} copied to ${BinaryDigger_PluginsFolder}" + "${BD_PLUGINS_FOLDER}" + COMMENT "${plugin_name} copied to ${BD_PLUGINS_FOLDER}" ) endforeach() diff --git a/scripters/CMakeLists.txt b/scripters/CMakeLists.txt index cbc5383..8238bea 100644 --- a/scripters/CMakeLists.txt +++ b/scripters/CMakeLists.txt @@ -1,7 +1,15 @@ # ---------------------------------------------------------------------------------------- -find_package(Luabind) +# Lua +find_package(Lua REQUIRED) + +if(NOT LUA_FOUND) + message(FATAL_ERROR "Cannot find Lua package") +endif() + +# Luabind +find_package(Luabind REQUIRED) if(NOT LUABIND_FOUND) message(FATAL_ERROR "Cannot find Luabind package") @@ -11,13 +19,10 @@ endif() #message("LUABIND_INCLUDE_DIRS: ${LUABIND_INCLUDE_DIRS}") #message("LUABIND_LIBRARIES: ${LUABIND_LIBRARIES}") -include_directories(${LUABIND_INCLUDE_DIRS}) +include_directories(${LUA_INCLUDE_DIR} ${LUABIND_INCLUDE_DIRS}) # ---------------------------------------------------------------------------------------- -include_directories("${BinaryDigger_SOURCE_DIR}/default_plugin") -link_directories("${BinaryDigger_BINARY_DIR}/default_plugin") - file(GLOB plugins "*.cpp") foreach(plugin_file ${plugins}) get_filename_component(plugin_name ${plugin_file} NAME_WE) @@ -34,8 +39,8 @@ foreach(plugin_file ${plugins}) add_custom_command(TARGET ${plugin_name} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ - "${BinaryDigger_PluginsFolder}" - COMMENT "${plugin_name} copied to ${BinaryDigger_PluginsFolder}" + "${BD_PLUGINS_FOLDER}" + COMMENT "${plugin_name} copied to ${BD_PLUGINS_FOLDER}" ) endforeach() diff --git a/scripters/LuaScripter.cpp b/scripters/LuaScripter.cpp index 264ff68..177c97b 100644 --- a/scripters/LuaScripter.cpp +++ b/scripters/LuaScripter.cpp @@ -6,9 +6,11 @@ */ #include +#include #include -#include - +#include +#include +#include extern "C" { #include #include @@ -28,8 +30,9 @@ using namespace std; class LuaTempl: public BlockTempl { public: - LuaTempl(bd_block_io* _blob, bd_cstring _var_name, bd_cstring _templ_name, bd_u32 _count, BlockTemplBase* _parent) - : BlockTempl(_blob, _var_name, _templ_name, _count, _parent) + LuaTempl(bd_block_io* _blob, bd_cstring _var_name, bd_cstring _templ_name, bd_u32 _count, BlockTemplBase* _parent, + const bd_property_records &props) + : BlockTempl(_blob, _var_name, _templ_name, _count, _parent, props) {} }; @@ -129,35 +132,289 @@ void getLuaCurrentFunctionName(lua_State* L, string& func_name) } } -void parse_apply_templ_params(const luabind::object& data, string& var_name, int& arr_size, luabind::object& params) +auto lua_type_name = map{ + {LUA_TNONE, "NONE"}, + {LUA_TNIL, "NIL"}, + {LUA_TBOOLEAN, "BOOLEAN"}, + {LUA_TLIGHTUSERDATA, "LIGHTUSERDATA"}, + {LUA_TNUMBER, "NUMBER"}, + {LUA_TSTRING, "STRING"}, + {LUA_TTABLE, "TABLE"}, + {LUA_TFUNCTION, "FUNCTION"}, + {LUA_TUSERDATA, "USERDATA"}, + {LUA_TTHREAD, "THREAD"}, +// {LUA_NUMTAGS, "NUMTAGS"}, +}; + +bool check_lua_type(const luabind::object ¶m, int type, string &err) +{ + if (luabind::type(param) != type) + { + err = (boost::format("Property has wrong type. Expected: %1% but was %2%") % + type % lua_type_name[luabind::type(param)]).str(); + return false; + } + return true; +} + +bool parse_property(const luabind::object ¶m, bd_property &result, string &err) +{ + switch (result.type) + { + case BD_PROP_INTEGER: + if (!check_lua_type(param, LUA_TNUMBER, err)) + return false; + result.value.i = luabind::object_cast(param); + return true; + case BD_PROP_DOUBLE: + if (!check_lua_type(param, LUA_TNUMBER, err)) + return false; + result.value.d = luabind::object_cast(param); + break; + case BD_PROP_STRING: + { + if (!check_lua_type(param, LUA_TSTRING, err)) + return false; + auto val = luabind::object_cast(param); + result = bd_property((bd_string) val.c_str()); + break; + } + case BD_PROP_TO_STRING: + if (!check_lua_type(param, LUA_TFUNCTION, err)) + return false; + // TODO: implement + break; + default: + err = (boost::format("Unsupported type: %1%") % result.type).str(); + return false; + } + return true; +} + +/** + * Parse properties in a tail of a table that should be in a form: + * + * { ..., endian = BdTempl.BIG_ENDIAN, to_string = function(obj) return "test" end} + * + * @param iter Points to the first property. + * @param props [OUT] Properties + */ +void parse_properties(luabind::iterator &iter, bd_property_records &props) +{ + auto end = luabind::iterator(); + for (; iter != end; ++iter) + { + // property name should be a string + if (luabind::type(iter.key()) != LUA_TSTRING) + { + // TODO: add warning about wrong property + continue; + } + + auto name = luabind::object_cast(iter.key()); + + // check if property is registered one + if (default_property.find(name) == default_property.end()) + { + // TODO: add warning about unsupported property + continue; // TODO: just add user specified property for later usage + } + + // get property + auto prop = bd_property(); + prop.type = default_property[name].type; + string err; + if (!parse_property(*iter, prop, err)) + { + // TODO: add warning with err message + continue; + } + + props[name] = prop; + } +} + +/** + * Parse template block definition call. Examples: + * + * 1. Single block with optional properties: + * + * Templ{"name" [, endian = BdTempl.BIG_ENDIAN, ...]} + * + * 2. Array block with optional properties: + * + * Templ{"name", 3 [, endian = BdTempl.BIG_ENDIAN, ...]} + * + * 3. Template element that accept parameters (up to 10) with optional properties: + * + * Templ{"name", {par1, par2, ... par10} [, endian = BdTempl.BIG_ENDIAN, ...]} + * + * 4. Array of template elements with optional properties. Each element accept parameters (up to 10): + * + * Templ{"name", 3, {par1, par2, ... par10} [, endian = BdTempl.BIG_ENDIAN, ...]} + * + * @param data Table to parse + * @param var_name [OUT] Template name + * @param arr_size [OUT] Array size if set + * @param params [OUT] Template parameters if set + * @param props [OUT] Template properties if set + */ +void parse_apply_templ_params(const luabind::object &data, string &var_name, int &arr_size, luabind::object ¶ms, + bd_property_records &props) { bd_require_lua_type(LUA_TTABLE, data); - bd_require_lua_type(LUA_TSTRING, data[1]); + + auto iter = luabind::iterator(data); + auto end = luabind::iterator(); + + // variable name + bd_require_lua_type(LUA_TSTRING, *iter); + var_name = luabind::object_cast(*iter); + ++iter; + + // array size + if (iter != end && luabind::type(iter.key()) == LUA_TNUMBER && luabind::type(*iter) == LUA_TNUMBER) + { + arr_size = (int) luabind::object_cast(*iter); + ++iter; + } + + // template parameters + if (iter != end && luabind::type(iter.key()) == LUA_TNUMBER) + { + bd_require_lua_type(LUA_TTABLE, *iter); + params = *iter; + ++iter; + } + + // object properties + parse_properties(iter, props); DEBUG_OUTPUT("Data: " << tostring(data) << "\n"); +} + +/** + * Call lua function with up to 10 parameters + * + * @param L Lua state + * @param func_name Name of the lua function + * @param params Parameters table + */ +void call_lua_function(lua_State* L, const string &func_name, const luabind::object ¶ms) +{ + if (params.interpreter() == nullptr || luabind::type(params) != LUA_TTABLE) + { + luabind::call_function(L, func_name.c_str(), params); + return; + } + + auto iter = luabind::iterator(params); + auto end = luabind::iterator(); + + if (iter == end) + { + luabind::call_function(L, func_name.c_str()); + return; + } + + auto param0 = *iter; + ++iter; + if (iter == end) + { + luabind::call_function(L, func_name.c_str(), param0); + return; + } + + auto param1 = *iter; + ++iter; + if (iter == end) + { + luabind::call_function(L, func_name.c_str(), param0, param1); + return; + } + + auto param2 = *iter; + ++iter; + if (iter == end) + { + luabind::call_function(L, func_name.c_str(), param0, param1, param2); + return; + } + + auto param3 = *iter; + ++iter; + if (iter == end) + { + luabind::call_function(L, func_name.c_str(), param0, param1, param2, param3); + return; + } + + auto param4 = *iter; + ++iter; + if (iter == end) + { + luabind::call_function(L, func_name.c_str(), param0, param1, param2, param3, param4); + return; + } - var_name = luabind::object_cast(data[1]); + auto param5 = *iter; + ++iter; + if (iter == end) + { + luabind::call_function(L, func_name.c_str(), param0, param1, param2, param3, param4, param5); + return; + } + + auto param6 = *iter; + ++iter; + if (iter == end) + { + luabind::call_function(L, func_name.c_str(), param0, param1, param2, param3, param4, param5, param6); + return; + } + + auto param7 = *iter; + ++iter; + if (iter == end) + { + luabind::call_function(L, func_name.c_str(), param0, param1, param2, param3, param4, param5, param6, param7); + return; + } - auto i = 2; - if (luabind::type(data[i]) == LUA_TNUMBER) + auto param8 = *iter; + ++iter; + if (iter == end) { - arr_size = (int) luabind::object_cast(data[i]); - i++; + luabind::call_function(L, func_name.c_str(), param0, param1, param2, param3, param4, param5, param6, param7, param8); + return; } - if (luabind::type(data[i]) == LUA_TTABLE) + auto param9 = *iter; + ++iter; + if (iter == end) { - params = data[i]; + luabind::call_function(L, func_name.c_str(), param0, param1, param2, param3, param4, param5, param6, param7, param8, param9); + return; } + + throw BlockTemplException(boost::format("Luabind supports only up to 10 function parameters.").str()); } -void apply_templ_func(lua_State* L, LuaTempl* templ, const char* func_name, const luabind::object& params) +/** + * Call 'apply' lua function associated with the template. + * + * @param L Lua state + * @param templ Template object + * @param func_name Name of the lua function + * @param params Parameters table + */ +void apply_templ_func(lua_State *L, LuaTempl *templ, const string &func_name, const luabind::object ¶ms) { DEBUG_OUTPUT("Apply templ func " << templ->type_name << "::" << templ->name << "." << func_name << " ...\n"); auto prev = setCurrentTempl(L, templ); - luabind::call_function(L, func_name, params); + call_lua_function(L, func_name, params); luabind::globals(L)["self"] = prev; @@ -167,13 +424,20 @@ void apply_templ_func(lua_State* L, LuaTempl* templ, const char* func_name, cons DEBUG_OUTPUT(" applied " << templ->type_name << "::" << templ->name << "." << func_name << "\n"); } +/** + * Wrapper function that creates new template object. + * + * @param data Template creation data. See @see(parse_apply_templ_params) for details + * @return Pointer to the created object + */ BlockTemplBase *apply_templ(const luabind::object& data) { auto var_name = string(); auto arr_size = 0; auto params = luabind::object(); + auto props = bd_property_records(); - parse_apply_templ_params(data, var_name, arr_size, params); + parse_apply_templ_params(data, var_name, arr_size, params, props); auto L = data.interpreter(); @@ -186,20 +450,20 @@ BlockTemplBase *apply_templ(const luabind::object& data) auto cur_templ = getCurrentTempl(L); bd_require_not_null(cur_templ, "Current template is null"); - // TODO: apply global template settings - auto settings_name = templ_name + "_settings"; -// luabind::object global_templ_settings = luabind::globals(L)[settings_name]; - - // TODO: apply local template settings that are defined in 'data' table as named elements + auto templ_props_name = templ_name + "_props"; + auto templ_props = luabind::object_cast(luabind::globals(L)[templ_props_name]); auto templ = new LuaTempl(cur_templ->getBlockIo(), (bd_cstring) var_name.c_str(), - (bd_cstring) templ_name.c_str(), arr_size, cur_templ); + (bd_cstring) templ_name.c_str(), arr_size, cur_templ, *templ_props); + + // set object properties + templ->set_properties(props); registerTemplVariable(L, templ, var_name); auto templ_func = templ_name + "_func"; - apply_templ_func(L, templ, templ_func.c_str(), params); + apply_templ_func(L, templ, templ_func, params); DEBUG_OUTPUT(" applied " << templ_name << "::" << var_name << "\n"); @@ -207,21 +471,32 @@ BlockTemplBase *apply_templ(const luabind::object& data) } /** - * Expecting 3 parameters: - * 1. Template name. - * 2. Template apply function. - * 3. Other template parameters table. + * Backend for 'templ' lua function. Declares new template class. Example: * - * @param obj Lua table object + * templ{"name", function([params]) ... end [, endian = BdTempl.BIG_ENDIAN, ...]} + * + * @param data Table to parse */ void register_templ(const luabind::object& data) { - bd_require_lua_type(LUA_TTABLE, data); - bd_require_lua_type(LUA_TSTRING, data[1]); - bd_require_lua_type(LUA_TFUNCTION, data[2]); + bd_require_lua_type(LUA_TTABLE, data); + + auto iter = luabind::iterator(data); + auto end = luabind::iterator(); + + // template name + bd_require_lua_type(LUA_TSTRING, *iter); + auto templ_name = luabind::object_cast(*iter); + ++iter; + + // template 'apply' function + bd_require_lua_type(LUA_TFUNCTION, *iter); + auto func = *iter; + ++iter; - auto templ_name = luabind::object_cast(data[1]); - auto func = data[2]; + // template properties (optional) + auto props = new bd_property_records; + parse_properties(iter, *props); auto L = func.interpreter(); @@ -233,36 +508,31 @@ void register_templ(const luabind::object& data) luabind::def(templ_name.c_str(), &apply_templ) ]; - // register template function + // register template 'apply' function auto templ_func = templ_name + "_func"; luabind::globals(L)[templ_func] = func; - // register template parameters - auto templ_params = templ_name + "_settings"; - luabind::globals(L)[templ_params] = data; + // register template properties + auto templ_props = templ_name + "_props"; + luabind::globals(L)[templ_props] = props; DEBUG_OUTPUT(" created\n"); } -vector split(const string &s, char delim) -{ - auto result = vector(); - stringstream ss(s); - auto item = string(); - while (getline(ss, item, delim)) - { - result.push_back(item); - } - return result; -} - +/** + * Convert simple template value to the lua object. + * + * @param L Lua state + * @param templ Template object + * @return Lua object + */ luabind::object get_templ_value(lua_State *L, BlockTemplBase *templ) { switch (templ->getType()) { #define BD_BLOCK_TYPE_DECL(name, tp) \ case BD_##name: { \ - auto val = templ->value(); \ + auto val = (tp) *templ; \ DEBUG_OUTPUT("Value " << templ->getName() << " = " << val << "\n"); \ return luabind::object(L, val); } BD_BLOCK_TYPES @@ -271,14 +541,24 @@ luabind::object get_templ_value(lua_State *L, BlockTemplBase *templ) bd_throw_f("Unsupported type: %d", templ->getType()); } +/** + * Backend for 'val' lua function. Usage examples: + * + * val{"field1.field2[2].field3 ..."} + * val{obj, "field.field2[2].field3 ..."} + * + * @param data Table of function parameters. + * @return Simple template value wrapped in lua object. + */ luabind::object get_value(const luabind::object& data) { bd_require_lua_type(LUA_TTABLE, data); - BlockTemplBase *self; auto L = data.interpreter(); auto paramsStr = string(); + // get template object + BlockTemplBase *self; auto params_pos = 1; if (LUA_TSTRING == luabind::type(data[1])) { @@ -300,10 +580,12 @@ luabind::object get_value(const luabind::object& data) { paramsStr = luabind::object_cast(data[params_pos]); - auto params = split(paramsStr, '.'); + auto params = vector(); + boost::split(params, paramsStr, boost::is_any_of(".")); + for (auto param : params) { - // TODO: trim param + boost::trim(param); if (param.empty()) continue; @@ -331,37 +613,74 @@ luabind::object get_value(const luabind::object& data) return get_templ_value(L, templ); } +/** + * Wrapper function that creates new simple template object. + * + * @param data Template creation data. See @see(parse_apply_templ_params) for details + * @return Pointer to the created object + */ template -void apply_simple_templ(const luabind::object& data) +BlockTemplBase *apply_simple_templ(const luabind::object& data) { auto var_name = string(); auto arr_size = 0; auto params = luabind::object(); + auto props = bd_property_records(); - parse_apply_templ_params(data, var_name, arr_size, params); + parse_apply_templ_params(data, var_name, arr_size, params, props); + + DEBUG_OUTPUT("Apply simple templ " << get_type_name() << "." << var_name << " ..."); auto L = data.interpreter(); auto cur_templ = getCurrentTempl(L); bd_require_not_null(cur_templ, "Current template is null"); - DEBUG_OUTPUT("Apply simple templ " << get_type_name() << "." << var_name << " ..."); - - // TODO: apply local template settings that are defined in 'data' table as named elements + auto templ = new T(cur_templ->getBlockIo(), (bd_cstring) var_name.c_str(), arr_size, cur_templ, bd_property_records()); - auto templ = new T(cur_templ->getBlockIo(), (bd_cstring) var_name.c_str(), arr_size, cur_templ); + // set object properties + templ->set_properties(props); registerTemplVariable(L, templ, var_name); DEBUG_OUTPUT(" applied\n"); + + return templ; } +/** + * Registers lua function for simple template object creation. + * + * @param name Template name. + * @return Luabind scope + */ template luabind::scope register_simple_templ(const string& name) { return luabind::def(name.c_str(), &apply_simple_templ); } +/** + * Backend for 'check_bit' lua function. Checks bit of the value. + * + * @param value Value to check. + * @param pos Bit position in the value. + * @return true if bit is set otherwise false + */ +bool check_bit(int value, int pos) +{ + return (value & (1 << pos)) != 0; +} + +/** + * TemplWrapper::applyTemplate method specialization for LuaTempl object. + * + * @param block_io Block IO object. + * @param script Script text. + * @param result [OUT] Created blocks hierarchy. + * @param err [OUT] Error message. + * @return BD_SUCCESS if template was successfully applied otherwise error code. + */ template<> bd_result TemplWrapper::applyTemplate(bd_block_io* block_io, bd_cstring script, bd_block **result, std::string &err) noexcept { @@ -386,9 +705,17 @@ bd_result TemplWrapper::applyTemplate(bd_block_io* block_io, bd_cstrin luabind::module(L) [ luabind::class_("TemplBlob"), + luabind::class_("Properties"), luabind::class_("BdTempl") .def("getPosition", &BlockTemplBase::getPosition) - .def("setPosition", &BlockTemplBase::setPosition), + .def("setPosition", &BlockTemplBase::setPosition) + .enum_("endian") + [ + luabind::value("LIT_ENDIAN", BD_ENDIAN_LIT), + luabind::value("BIG_ENDIAN", BD_ENDIAN_BIG) + ], + + luabind::def("check_bit", &check_bit), luabind::def("templ", ®ister_templ), luabind::def("val", &get_value), @@ -400,7 +727,8 @@ bd_result TemplWrapper::applyTemplate(bd_block_io* block_io, bd_cstrin register_simple_templ("double") ]; - templ = new LuaTempl(block_io, (bd_cstring) "bd", (bd_cstring) "LuaScript", 0, 0); + // TODO: specify properties + templ = new LuaTempl(block_io, (bd_cstring) "bd", (bd_cstring) "LuaScript", 0, 0, bd_property_records()); *result = templ; @@ -447,6 +775,12 @@ bd_result TemplWrapper::applyTemplate(bd_block_io* block_io, bd_cstrin return result_code; } +/** + * Adds LuaTempl wrapper to the list of registered templates. + * + * @param name Template name (LuaScripter). + * @param is_scripter Shows that plugin accepts script (true for LuaScripter). + */ void register_plugin(string &name, bool &is_scripter) { is_scripter = true; diff --git a/test_data/NpkTemplate.lua b/test_data/NpkTemplate.lua index 975defb..f3037a6 100644 --- a/test_data/NpkTemplate.lua +++ b/test_data/NpkTemplate.lua @@ -1,3 +1,4 @@ + templ{"NpkHeader", function() char{"tag", 4} dword{"blockLen"} @@ -11,16 +12,14 @@ templ{"NpkFileEntry", function(dataOffset) word{"entryNameLen"} char{"entryName", val{"entryNameLen"}} --- local pos = bd:getPosition() --- bd:setPosition(dataOffset + val{"entryFileOffset"}) --- --- uchar{"data", val{"entryFileLength"}} --- --- bd:setPosition(pos) + local pos = bd:getPosition() + bd:setPosition(dataOffset + val{"entryFileOffset"}) + + uchar{"data", val{"entryFileLength"}} + + bd:setPosition(pos) end} ---function NpkEntry(dataOffset) end - templ{"NpkDirEntry", function(dataOffset) dword{"blockLen"} word{"entryNameLen"} diff --git a/test_data/data1.npk b/test_data/data.npk similarity index 100% rename from test_data/data1.npk rename to test_data/data.npk diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7b93d6e..7ce20bc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,4 @@ -include_directories("${BinaryDigger_SOURCE_DIR}/core") - enable_testing() file(GLOB tests "*.cpp") diff --git a/tests/LuaPluginTest.cpp b/tests/LuaPluginTest.cpp index c92ad96..dc0d71c 100644 --- a/tests/LuaPluginTest.cpp +++ b/tests/LuaPluginTest.cpp @@ -38,16 +38,18 @@ bd_result loadPlugin(const char* file, PluginLibrary *pl) pl->plugin.result_message = (bd_result_message_t) loadPluginFunction(pl->library, file, "bd_result_message"); pl->plugin.initialize_plugin = (bd_initialize_plugin_t) loadPluginFunction(pl->library, file, "bd_initialize_plugin"); + pl->plugin.finalize_plugin = (bd_finalize_plugin_t) loadPluginFunction(pl->library, file, "bd_finalize_plugin"); pl->plugin.template_name = (bd_template_name_t) loadPluginFunction(pl->library, file, "bd_template_name"); pl->plugin.apply_template = (bd_apply_template_t) loadPluginFunction(pl->library, file, "bd_apply_template"); pl->plugin.free_template = (bd_free_template_t) loadPluginFunction(pl->library, file, "bd_free_template"); - pl->plugin.finalize_plugin = (bd_finalize_plugin_t) loadPluginFunction(pl->library, file, "bd_finalize_plugin"); - - if (pl->plugin.initialize_plugin == 0 || - pl->plugin.template_name == 0 || - pl->plugin.apply_template == 0 || - pl->plugin.free_template == 0 || - pl->plugin.finalize_plugin == 0) + pl->plugin.get_string_value = (bd_get_string_value_t) loadPluginFunction(pl->library, file, "bd_get_string_value"); + + if (pl->plugin.initialize_plugin == nullptr || + pl->plugin.finalize_plugin == nullptr || + pl->plugin.template_name == nullptr || + pl->plugin.apply_template == nullptr || + pl->plugin.free_template == nullptr || + pl->plugin.get_string_value == nullptr) { pl->library.unload(); return BD_ERROR; diff --git a/tests/NpkPluginTest.cpp b/tests/NpkPluginTest.cpp index cfc9ff2..c6a0109 100644 --- a/tests/NpkPluginTest.cpp +++ b/tests/NpkPluginTest.cpp @@ -116,11 +116,12 @@ bd_result loadPlugin(const char* file, PluginLibrary *pl) pl->plugin.free_template = (bd_free_template_t) loadPluginFunction(pl->library, file, "bd_free_template"); pl->plugin.finalize_plugin = (bd_finalize_plugin_t) loadPluginFunction(pl->library, file, "bd_finalize_plugin"); - if (pl->plugin.initialize_plugin == 0 || - pl->plugin.template_name == 0 || - pl->plugin.apply_template == 0 || - pl->plugin.free_template == 0 || - pl->plugin.finalize_plugin == 0) + if (pl->plugin.initialize_plugin == nullptr || + pl->plugin.finalize_plugin == nullptr || + pl->plugin.template_name == nullptr || + pl->plugin.apply_template == nullptr || + pl->plugin.free_template == nullptr || + pl->plugin.get_string_value == nullptr) { pl->library.unload(); return BD_ERROR; diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 15a0c50..f1d9671 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,6 +1,4 @@ -include_directories("${BinaryDigger_SOURCE_DIR}/core") - file(GLOB tools "*.cpp") foreach(tool_file ${tools}) get_filename_component(tool_name ${tool_file} NAME_WE) diff --git a/tools/dumptree.cpp b/tools/dumptree.cpp index 9e425b6..4f3f1e7 100644 --- a/tools/dumptree.cpp +++ b/tools/dumptree.cpp @@ -14,11 +14,11 @@ #include #include -#include - #include #include "../utils/default_block_io.h" +#include +#include using Poco::Util::Application; using Poco::Util::Option; @@ -51,7 +51,6 @@ using Poco::AutoPtr; return res; \ } } - class DumpTreeApp: public Application { public: @@ -213,10 +212,11 @@ class DumpTreeApp: public Application { plugin.result_message = (bd_result_message_t) pluginLibrary.getSymbol("bd_result_message"); plugin.initialize_plugin = (bd_initialize_plugin_t) pluginLibrary.getSymbol("bd_initialize_plugin"); + plugin.finalize_plugin = (bd_finalize_plugin_t) pluginLibrary.getSymbol("bd_finalize_plugin"); plugin.template_name = (bd_template_name_t) pluginLibrary.getSymbol("bd_template_name"); plugin.apply_template = (bd_apply_template_t) pluginLibrary.getSymbol("bd_apply_template"); plugin.free_template = (bd_free_template_t) pluginLibrary.getSymbol("bd_free_template"); - plugin.finalize_plugin = (bd_finalize_plugin_t) pluginLibrary.getSymbol("bd_finalize_plugin"); + plugin.get_string_value = (bd_get_string_value_t) pluginLibrary.getSymbol("bd_get_string_value"); } bd_result initializePlugin(PluginInfo& pluginInfo) @@ -258,9 +258,9 @@ class DumpTreeApp: public Application return BD_SUCCESS; } - bd_result applyTemplate(PluginInfo& pluginInfo, bd_default_block_io& templBlob, bd_block **item) + bd_result applyTemplate(PluginInfo& pluginInfo, bd_default_block_io& templBlob, bd_block **block) { - freeTemplate(pluginInfo, *item); + freeTemplate(pluginInfo, *block); // templBlob.dataFile.seekg(0); @@ -268,56 +268,78 @@ class DumpTreeApp: public Application bd_u32 templIndex = 0; DT_EXEC_CHECK_F(pluginInfo.plugin, - apply_template(templIndex, &templBlob, item, _scriptFile.empty() ? 0 : (bd_cstring) _scriptFile.c_str()), + apply_template(templIndex, &templBlob, block, _scriptFile.empty() ? 0 : (bd_cstring) _scriptFile.c_str()), "Could not apply template %u", templIndex); return BD_SUCCESS; } - bd_result freeTemplate(PluginInfo& pluginInfo, bd_block *item) + bd_result freeTemplate(PluginInfo& pluginInfo, bd_block *block) { - if (item == 0) + if (block == 0) return BD_SUCCESS; // TODO: set correct template index bd_u32 templIndex = 0; - DT_EXEC_CHECK_F(pluginInfo.plugin, free_template(templIndex, item), "Could not free template %u", templIndex); + DT_EXEC_CHECK_F(pluginInfo.plugin, free_template(templIndex, block), "Could not free template %u", templIndex); return BD_SUCCESS; } - void dumpTree(std::ostream& output, bd_default_block_io& templBlob, const bd_block *item, const std::string& indent = "") + bd_result dumpTree(std::ostream& output, bd_plugin& plugin, bd_default_block_io& templBlob, const bd_block *block, const std::string& indent = "") { - if (item == 0) + if (block == 0) { output << indent << "NULL\n"; - return; + return BD_SUCCESS; } - output << indent << item->type_name << "(" << item->type << ") " << item->name; - if (item->is_array == BD_TRUE) + output << indent << block->type_name << "(" << block->type << ") " << block->name; + if (block->is_array == BD_TRUE) { - output << "[" << item->count << "] (" << item->elem_size << "/" << item->size << ")"; + output << "[" << block->count << "] (" << block->elem_size << "/" << block->size << ")"; + + if (block->type == BD_CHAR) + { + bd_u32 val_size = block->size + 1; + bd_string val = new bd_char[val_size]; + BOOST_SCOPE_EXIT(&val) + { + delete[] val; + } + BOOST_SCOPE_EXIT_END + + DT_EXEC_CHECK(plugin, get_string_value((bd_block *) block, val, val_size), "Could not get string value"); + + output << ": \"" << val << "\""; + } } else { - output << "(" << item->size << ")"; + output << "(" << block->size << "): "; + + bd_char val[100]; + DT_EXEC_CHECK(plugin, get_string_value((bd_block *) block, val, sizeof(val)), "Could not get value"); + + output << val; } - if (item->children.count > 0) + if (block->children.count > 0) { - output << " [" << item->children.count << "]:\n"; + output << " [" << block->children.count << "]:\n"; - for (bd_u32 i = 0; i < item->children.count; ++i) + for (bd_u32 i = 0; i < block->children.count; ++i) { - dumpTree(output, templBlob, item->children.child[i], indent + " "); + dumpTree(output, plugin, templBlob, block->children.child[i], indent + " "); } } else { output << "\n"; } + + return BD_SUCCESS; } int main(const std::vector& args) @@ -355,6 +377,7 @@ class DumpTreeApp: public Application showPluginInfo(pluginInfo); } + auto result = Application::EXIT_OK; if (args.size() > 0) { // 2. Dump each file @@ -367,11 +390,15 @@ class DumpTreeApp: public Application if (!BD_SUCCEED(applyTemplate(pluginInfo, templBlob, &item))) { poco_error(logger(), _errorMessage); - return Application::EXIT_DATAERR; + result = Application::EXIT_DATAERR; } // 2. Dump tree hierarchy - dumpTree(std::cout, templBlob, item); + if (!BD_SUCCEED(dumpTree(std::cout, pluginInfo.plugin, templBlob, item))) + { + poco_error(logger(), _errorMessage); + return Application::EXIT_DATAERR; + } // 3. Cleanup tree if (!BD_SUCCEED(freeTemplate(pluginInfo, item))) @@ -388,14 +415,7 @@ class DumpTreeApp: public Application return Application::EXIT_DATAERR; } -// logger().information("Arguments to main():"); -// for (std::vector::const_iterator it = args.begin(); it != args.end(); ++it) -// { -// logger().information(*it); -// } -// logger().information("Application properties:"); -// printProperties(""); - return Application::EXIT_OK; + return result; } void printProperties(const std::string& base)