From b2846492f44ef48803bcc8f7cefb37bb80fd74eb Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 21 Mar 2012 18:49:50 +0100 Subject: [PATCH 01/72] ruby: compiles --- plugins/CMakeLists.txt | 5 + plugins/ruby/CMakeLists.txt | 7 + plugins/ruby/ruby.cpp | 609 ++++++++++++++++++++++++++++++++++++ plugins/ruby/ruby.rb | 44 +++ 4 files changed, 665 insertions(+) create mode 100644 plugins/ruby/CMakeLists.txt create mode 100644 plugins/ruby/ruby.cpp create mode 100644 plugins/ruby/ruby.rb diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 6c2a537d1..39fdcba9e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -35,6 +35,11 @@ if (BUILD_DWARFEXPORT) add_subdirectory (dwarfexport) endif() +OPTION(BUILD_RUBY "Build ruby binding." OFF) +if (BUILD_RUBY) + add_subdirectory (ruby) +endif() + # Protobuf FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt new file mode 100644 index 000000000..dc5f0284a --- /dev/null +++ b/plugins/ruby/CMakeLists.txt @@ -0,0 +1,7 @@ +find_package(Ruby) +if(RUBY_FOUND) + include_directories("${dfhack_SOURCE_DIR}/depends/tthread" ${RUBY_INCLUDE_PATH}) + DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) +else(RUBY_FOUND) + MESSAGE(STATUS "Required library (ruby) not found - ruby plugin can't be built.") +endif(RUBY_FOUND) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp new file mode 100644 index 000000000..7ecda28ca --- /dev/null +++ b/plugins/ruby/ruby.cpp @@ -0,0 +1,609 @@ +// blindly copied imports from fastdwarf +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" + +#include "DataDefs.h" +#include "df/world.h" +#include "df/unit.h" + +#include "tinythread.h" + +#include + +using std::string; +using std::vector; +using namespace DFHack; + + +static void df_rubythread(void*); +static command_result df_rubyload(color_ostream &out, vector & parameters); +static command_result df_rubyeval(color_ostream &out, vector & parameters); +static void ruby_bind_dfhack(void); + +// inter-thread communication stuff +enum RB_command { + RB_IDLE, + RB_INIT, + RB_DIE, + RB_LOAD, + RB_EVAL, + RB_CUSTOM, +}; +tthread::mutex *m_irun; +tthread::mutex *m_mutex; +static RB_command r_type; +static const char *r_command; +static command_result r_result; +static tthread::thread *r_thread; +static int onupdate_active; + +// dfhack interface +DFHACK_PLUGIN("ruby") + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + m_irun = new tthread::mutex(); + m_mutex = new tthread::mutex(); + r_type = RB_INIT; + + r_thread = new tthread::thread(df_rubythread, 0); + + while (r_type != RB_IDLE) + tthread::this_thread::yield(); + + m_irun->lock(); + + if (r_result == CR_FAILURE) + return CR_FAILURE; + + onupdate_active = 0; + + commands.push_back(PluginCommand("rb_load", + "Ruby interpreter. Loads the given ruby script.", + df_rubyload)); + + commands.push_back(PluginCommand("rb_eval", + "Ruby interpreter. Eval() a ruby string.", + df_rubyeval)); + + commands.push_back(PluginCommand("r", + "Ruby interpreter dev. Eval() a ruby string.", + df_rubyeval)); + + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + m_mutex->lock(); + if (!r_thread) + return CR_OK; + + r_type = RB_DIE; + r_command = 0; + m_irun->unlock(); + + r_thread->join(); + + delete r_thread; + r_thread = 0; + delete m_irun; + m_mutex->unlock(); + delete m_mutex; + + return CR_OK; +} + +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +{ + if (!onupdate_active) + return CR_OK; + + command_result ret; + + m_mutex->lock(); + if (!r_thread) + return CR_OK; + + r_type = RB_EVAL; + r_command = "DFHack.onupdate"; + m_irun->unlock(); + + while (r_type != RB_IDLE) + tthread::this_thread::yield(); + + ret = r_result; + + m_irun->lock(); + m_mutex->unlock(); + + return ret; +} + +static command_result df_rubyload(color_ostream &out, vector & parameters) +{ + command_result ret; + + if (parameters.size() == 1 && (parameters[0] == "help" || parameters[0] == "?")) + { + out.print("This command loads the ruby script whose path is given as parameter, and run it.\n"); + return CR_OK; + } + + // serialize 'accesses' to the ruby thread + m_mutex->lock(); + if (!r_thread) + // raced with plugin_shutdown ? + return CR_OK; + + r_type = RB_LOAD; + r_command = parameters[0].c_str(); + m_irun->unlock(); + + // could use a condition_variable or something... + while (r_type != RB_IDLE) + tthread::this_thread::yield(); + // XXX non-atomic with previous r_type change check + ret = r_result; + + m_irun->lock(); + m_mutex->unlock(); + + return ret; +} + +static command_result df_rubyeval(color_ostream &out, vector & parameters) +{ + command_result ret; + + if (parameters.size() == 1 && (parameters[0] == "help" || parameters[0] == "?")) + { + out.print("This command executes an arbitrary ruby statement.\n"); + return CR_OK; + } + + std::string full = ""; + full += "DFHack.puts(("; + + for (unsigned i=0 ; ilock(); + if (!r_thread) + return CR_OK; + + r_type = RB_EVAL; + r_command = full.c_str(); + m_irun->unlock(); + + while (r_type != RB_IDLE) + tthread::this_thread::yield(); + + ret = r_result; + + m_irun->lock(); + m_mutex->unlock(); + + return ret; +} + + + +// ruby thread code +static void dump_rb_error(void) +{ + VALUE s, err; + + err = rb_gv_get("$!"); + + s = rb_funcall(err, rb_intern("class"), 0); + s = rb_funcall(s, rb_intern("name"), 0); + Core::printerr("E: %s: ", rb_string_value_ptr(&s)); + + s = rb_funcall(err, rb_intern("message"), 0); + Core::printerr("%s\n", rb_string_value_ptr(&s)); + + err = rb_funcall(err, rb_intern("backtrace"), 0); + for (int i=0 ; i<8 ; ++i) + if ((s = rb_ary_shift(err)) != Qnil) + Core::printerr(" %s\n", rb_string_value_ptr(&s)); +} + +// ruby thread main loop +static void df_rubythread(void *p) +{ + int state, running; + + // initialize the ruby interpreter + ruby_init(); + ruby_init_loadpath(); + // default value for the $0 "current script name" + ruby_script("dfhack"); + + // create the ruby objects to map DFHack to ruby methods + ruby_bind_dfhack(); + + r_result = CR_OK; + r_type = RB_IDLE; + + running = 1; + while (running) { + // wait for new command + m_irun->lock(); + + switch (r_type) { + case RB_IDLE: + case RB_INIT: + break; + + case RB_DIE: + running = 0; + ruby_finalize(); + break; + + case RB_LOAD: + state = 0; + rb_load_protect(rb_str_new2(r_command), Qfalse, &state); + if (state) + dump_rb_error(); + break; + + case RB_EVAL: + state = 0; + rb_eval_string_protect(r_command, &state); + if (state) + dump_rb_error(); + break; + + case RB_CUSTOM: + // TODO handle ruby custom commands + break; + } + + r_result = CR_OK; + r_type = RB_IDLE; + m_irun->unlock(); + tthread::this_thread::yield(); + } +} + + + +// ruby classes +static VALUE rb_cDFHack; +static VALUE rb_c_WrapData; + + +// DFHack methods +// enable/disable calls to DFHack.onupdate() +static VALUE rb_dfonupdateactive(VALUE self) +{ + if (onupdate_active) + return Qtrue; + else + return Qfalse; +} + +static VALUE rb_dfonupdateactiveset(VALUE self, VALUE val) +{ + onupdate_active = (val == Qtrue || val == INT2FIX(1)) ? 1 : 0; + return Qtrue; +} + +static VALUE rb_dfresume(VALUE self) +{ + Core::getInstance().Resume(); + return Qtrue; +} + +static VALUE rb_dfsuspend(VALUE self) +{ + Core::getInstance().Suspend(); + return Qtrue; +} + +/* +static VALUE rb_dfgetversion(VALUE self) +{ + return rb_str_new2(getcore().vinfo->getVersion().c_str()); +} +*/ + +// TODO color_ostream proxy yadda yadda +static VALUE rb_dfprint_str(VALUE self, VALUE s) +{ + //getcore().con.print("%s", rb_string_value_ptr(&s)); + Core::printerr("%s", rb_string_value_ptr(&s)); + return Qnil; +} + +static VALUE rb_dfprint_err(VALUE self, VALUE s) +{ + Core::printerr("%s", rb_string_value_ptr(&s)); + return Qnil; +} + +// raw memory access +// WARNING: may cause game crash ! double-check your addresses ! +static VALUE rb_dfmemread(VALUE self, VALUE addr, VALUE len) +{ + return rb_str_new((char*)rb_num2ulong(addr), rb_num2ulong(len)); +} + +static VALUE rb_dfmemwrite(VALUE self, VALUE addr, VALUE raw) +{ + // no stable api for raw.length between rb1.8/rb1.9 ... + int strlen = FIX2INT(rb_funcall(raw, rb_intern("length"), 0)); + + memcpy((void*)rb_num2ulong(addr), rb_string_value_ptr(&raw), strlen); + + return Qtrue; +} + +static VALUE rb_dfmalloc(VALUE self, VALUE len) +{ + return rb_uint2inum((long)malloc(FIX2INT(len))); +} + +static VALUE rb_dffree(VALUE self, VALUE ptr) +{ + free((void*)rb_num2ulong(ptr)); + return Qtrue; +} + +// raw c++ wrappers +// return the nth element of a vector +static VALUE rb_dfvectorat(VALUE self, VALUE vect_addr, VALUE idx) +{ + vector *v = (vector*)rb_num2ulong(vect_addr); + return rb_uint2inum(v->at(FIX2INT(idx))); +} + +// return a c++ string as a ruby string (nul-terminated) +static VALUE rb_dfreadstring(VALUE self, VALUE str_addr) +{ + string *s = (string*)rb_num2ulong(str_addr); + return rb_str_new2(s->c_str()); +} + + + + +/* XXX this needs a custom DFHack::Plugin subclass to pass the cmdname to invoke(), to match the ruby callback +// register a ruby method as dfhack console command +// usage: DFHack.register("moo", "this commands prints moo on the console") { DFHack.puts "moo !" } +static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) +{ + commands.push_back(PluginCommand(rb_string_value_ptr(&name), + rb_string_value_ptr(&descr), + df_rubycustom)); + + return Qtrue; +} +*/ +static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) +{ + rb_raise(rb_eRuntimeError, "not implemented"); +} + + +// return the address of the struct in DF memory (for raw memread/write) +static VALUE rb_memaddr(VALUE self) +{ + void *data; + Data_Get_Struct(self, void, data); + + return rb_uint2inum((uint32_t)data); +} + + + + +// BEGIN GENERATED SECTION + +// begin generated T_cursor binding +static VALUE rb_c_T_cursor; + +static VALUE rb_m_T_cursor_x(VALUE self) { + struct df::global::T_cursor *var; + Data_Get_Struct(self, struct df::global::T_cursor, var); + return rb_uint2inum(var->x); +} +static VALUE rb_m_T_cursor_x_set(VALUE self, VALUE val) { + struct df::global::T_cursor *var; + Data_Get_Struct(self, struct df::global::T_cursor, var); + var->x = rb_num2ulong(val); + return Qtrue; +} + +static VALUE rb_m_T_cursor_y(VALUE self) { + struct df::global::T_cursor *var; + Data_Get_Struct(self, struct df::global::T_cursor, var); + return rb_uint2inum(var->y); +} +static VALUE rb_m_T_cursor_y_set(VALUE self, VALUE val) { + struct df::global::T_cursor *var; + Data_Get_Struct(self, struct df::global::T_cursor, var); + var->y = rb_num2ulong(val); + return Qtrue; +} + +static VALUE rb_m_T_cursor_z(VALUE self) { + struct df::global::T_cursor *var; + Data_Get_Struct(self, struct df::global::T_cursor, var); + return rb_uint2inum(var->z); +} +static VALUE rb_m_T_cursor_z_set(VALUE self, VALUE val) { + struct df::global::T_cursor *var; + Data_Get_Struct(self, struct df::global::T_cursor, var); + var->z = rb_num2ulong(val); + return Qtrue; +} + +// link methods to the class +static void ruby_bind_T_cursor(void) { + // create a class, child of WrapData, in module DFHack + rb_c_T_cursor = rb_define_class_under(rb_cDFHack, "T_cursor", rb_c_WrapData); + + // reader for 'x' (0 = no arg) + rb_define_method(rb_c_T_cursor, "x", RUBY_METHOD_FUNC(rb_m_T_cursor_x), 0); + // writer for 'x' (1 arg) + rb_define_method(rb_c_T_cursor, "x=", RUBY_METHOD_FUNC(rb_m_T_cursor_x_set), 1); + rb_define_method(rb_c_T_cursor, "y", RUBY_METHOD_FUNC(rb_m_T_cursor_y), 0); + rb_define_method(rb_c_T_cursor, "y=", RUBY_METHOD_FUNC(rb_m_T_cursor_y_set), 1); + rb_define_method(rb_c_T_cursor, "z", RUBY_METHOD_FUNC(rb_m_T_cursor_z), 0); + rb_define_method(rb_c_T_cursor, "z=", RUBY_METHOD_FUNC(rb_m_T_cursor_z_set), 1); +} + + +// create an instance of T_cursor from global::cursor +// this method is linked to DFHack.cursor in ruby_bind_dfhack() +static VALUE rb_global_cursor(VALUE self) { + return Data_Wrap_Struct(rb_c_T_cursor, 0, 0, df::global::cursor); +} + + +// begin generated unit binding +static VALUE rb_c_unit; + +static VALUE rb_m_unit_id(VALUE self) { + struct df::unit *var; + Data_Get_Struct(self, struct df::unit, var); + + return rb_uint2inum(var->id); +} +static VALUE rb_m_unit_id_set(VALUE self, VALUE val) { + struct df::unit *var; + Data_Get_Struct(self, struct df::unit, var); + var->id = rb_num2ulong(val); + return Qtrue; +} + +static void ruby_bind_unit(void) { + // ruby class name must begin with uppercase + rb_c_unit = rb_define_class_under(rb_cDFHack, "Unit", rb_c_WrapData); + + rb_define_method(rb_c_unit, "id", RUBY_METHOD_FUNC(rb_m_unit_id), 0); + rb_define_method(rb_c_unit, "id=", RUBY_METHOD_FUNC(rb_m_unit_id_set), 1); +} + + +// begin generated world binding +static VALUE rb_c_world; +static VALUE rb_c_world_T_units; + +static VALUE rb_m_world_T_units_all(VALUE self) { + struct df::world::T_units *var; + Data_Get_Struct(self, struct df::world::T_units, var); + + // read a vector + VALUE ret = rb_ary_new(); + for (unsigned i=0 ; iall.size() ; ++i) + rb_ary_push(ret, Data_Wrap_Struct(rb_c_unit, 0, 0, var->all[i])); + + return ret; +} + +static VALUE rb_m_world_units(VALUE self) { + struct df::world *var; + Data_Get_Struct(self, struct df::world, var); + return Data_Wrap_Struct(rb_c_world_T_units, 0, 0, &var->units); +} + +static void ruby_bind_world(void) { + rb_c_world = rb_define_class_under(rb_cDFHack, "World", rb_c_WrapData); + rb_c_world_T_units = rb_define_class_under(rb_c_world, "T_units", rb_c_WrapData); + + rb_define_method(rb_c_world, "units", RUBY_METHOD_FUNC(rb_m_world_units), 0); +} + +static VALUE rb_global_world(VALUE self) { + return Data_Wrap_Struct(rb_c_world, 0, 0, df::global::world); +} + +/* +static VALUE rb_dfcreatures(VALUE self) +{ + OffsetGroup *ogc = getcore().vinfo->getGroup("Creatures"); + vector *v = (vector*)ogc->getAddress("vector"); + + VALUE ret = rb_ary_new(); + for (unsigned i=0 ; isize() ; ++i) + rb_ary_push(ret, Data_Wrap_Struct(rb_cCreature, 0, 0, v->at(i))); + + return ret; +} + +static VALUE rb_getlaborname(VALUE self, VALUE idx) +{ + return rb_str_new2(getcore().vinfo->getLabor(FIX2INT(idx)).c_str()); +} + +static VALUE rb_getskillname(VALUE self, VALUE idx) +{ + return rb_str_new2(getcore().vinfo->getSkill(FIX2INT(idx)).c_str()); +} + +static VALUE rb_mapblock(VALUE self, VALUE x, VALUE y, VALUE z) +{ + Maps *map; + Data_Get_Struct(self, Maps, map); + df_block *block; + + block = map->getBlock(FIX2INT(x)/16, FIX2INT(y)/16, FIX2INT(z)); + if (!block) + return Qnil; + + return Data_Wrap_Struct(rb_cMapBlock, 0, 0, block); +} +*/ + + + + +// define module DFHack and its methods +static void ruby_bind_dfhack(void) { + rb_cDFHack = rb_define_module("DFHack"); + + // global DFHack commands + rb_define_singleton_method(rb_cDFHack, "onupdate_active", RUBY_METHOD_FUNC(rb_dfonupdateactive), 0); + rb_define_singleton_method(rb_cDFHack, "onupdate_active=", RUBY_METHOD_FUNC(rb_dfonupdateactiveset), 1); + rb_define_singleton_method(rb_cDFHack, "resume", RUBY_METHOD_FUNC(rb_dfresume), 0); + rb_define_singleton_method(rb_cDFHack, "do_suspend", RUBY_METHOD_FUNC(rb_dfsuspend), 0); + rb_define_singleton_method(rb_cDFHack, "resume", RUBY_METHOD_FUNC(rb_dfresume), 0); + //rb_define_singleton_method(rb_cDFHack, "version", RUBY_METHOD_FUNC(rb_dfgetversion), 0); + rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1); + rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1); + rb_define_singleton_method(rb_cDFHack, "memread", RUBY_METHOD_FUNC(rb_dfmemread), 2); + rb_define_singleton_method(rb_cDFHack, "memwrite", RUBY_METHOD_FUNC(rb_dfmemwrite), 2); + rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1); + rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1); + rb_define_singleton_method(rb_cDFHack, "vectorat", RUBY_METHOD_FUNC(rb_dfvectorat), 2); + rb_define_singleton_method(rb_cDFHack, "readstring", RUBY_METHOD_FUNC(rb_dfreadstring), 1); + rb_define_singleton_method(rb_cDFHack, "register_dfcommand", RUBY_METHOD_FUNC(rb_dfregister), 2); + + // accessors for dfhack globals + rb_define_singleton_method(rb_cDFHack, "cursor", RUBY_METHOD_FUNC(rb_global_cursor), 0); + rb_define_singleton_method(rb_cDFHack, "world", RUBY_METHOD_FUNC(rb_global_world), 0); + + // parent class for all wrapped objects + rb_c_WrapData = rb_define_class_under(rb_cDFHack, "WrapData", rb_cObject); + rb_define_method(rb_c_WrapData, "memaddr", RUBY_METHOD_FUNC(rb_memaddr), 0); + + // call generated bindings + ruby_bind_T_cursor(); + ruby_bind_unit(); + ruby_bind_world(); + + // load the default ruby-level definitions + int state=0; + rb_load_protect(rb_str_new2("./hack/plugins/ruby.rb"), Qfalse, &state); + if (state) + dump_rb_error(); +} diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb new file mode 100644 index 000000000..1ad7dbab3 --- /dev/null +++ b/plugins/ruby/ruby.rb @@ -0,0 +1,44 @@ +module DFHack + def suspend + if block_given? + begin + do_suspend + yield + ensure + resume + end + else + do_suspend + end + end + + def puts(*a) + a.flatten.each { |l| + print_str(l.to_s.chomp + "\n") + } + end + + def puts_err(*a) + a.flatten.each { |l| + print_err(l.to_s.chomp + "\n") + } + end + + def test + puts "starting" + + suspend { + c = cursor + puts "cursor pos: #{c.x} #{c.y} #{c.z}" + + puts "unit[0] id: #{world.units.all[0].id}" + } + + puts "done" + end +end + +end + +# load user-specified startup file +load 'ruby_custom.rb' if File.exist?('ruby_custom.rb') From f46e1ee51832e9e542412f112ccf89cfa2f7ef34 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 21 Mar 2012 19:30:31 +0100 Subject: [PATCH 02/72] ruby: link libruby, fix stuff, it works --- plugins/ruby/CMakeLists.txt | 2 + plugins/ruby/ruby.cpp | 5 ++- plugins/ruby/ruby.rb | 78 ++++++++++++++++++------------------- 3 files changed, 45 insertions(+), 40 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index dc5f0284a..a44fb52cd 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -2,6 +2,8 @@ find_package(Ruby) if(RUBY_FOUND) include_directories("${dfhack_SOURCE_DIR}/depends/tthread" ${RUBY_INCLUDE_PATH}) DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) + target_link_libraries(ruby ${RUBY_LIBRARY}) + install(FILES ruby.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION}) else(RUBY_FOUND) MESSAGE(STATUS "Required library (ruby) not found - ruby plugin can't be built.") endif(RUBY_FOUND) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 7ecda28ca..592525332 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -44,6 +44,7 @@ DFHACK_PLUGIN("ruby") DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { +out.print("plugin_init\n"); m_irun = new tthread::mutex(); m_mutex = new tthread::mutex(); r_type = RB_INIT; @@ -72,6 +73,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Wed, 21 Mar 2012 19:37:39 +0100 Subject: [PATCH 03/72] ruby: rm silly debug statement --- plugins/ruby/ruby.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 592525332..74755424f 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -44,7 +44,6 @@ DFHACK_PLUGIN("ruby") DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { -out.print("plugin_init\n"); m_irun = new tthread::mutex(); m_mutex = new tthread::mutex(); r_type = RB_INIT; @@ -73,7 +72,6 @@ out.print("plugin_init\n"); "Ruby interpreter dev. Eval() a ruby string.", df_rubyeval)); -out.print("plugin_init done\n"); return CR_OK; } From ca528fff35fa2915088419f10fb804c41ef53124 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 26 Mar 2012 14:37:18 +0200 Subject: [PATCH 04/72] rb codegen: generate ruby --- plugins/ruby/codegen.pl | 175 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100755 plugins/ruby/codegen.pl diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl new file mode 100755 index 000000000..74350c255 --- /dev/null +++ b/plugins/ruby/codegen.pl @@ -0,0 +1,175 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use XML::LibXML; + +our @lines_rb; +our @lines_cpp; + +sub indent_rb(&) { + my ($sub) = @_; + my @lines; + { + local @lines_rb; + $sub->(); + @lines = map { " " . $_ } @lines_rb; + } + push @lines_rb, @lines +} + +sub rb_ucase { + my ($name) = @_; + return join("", map { ucfirst $_ } (split('_', $name))); +} + +my %render_struct_field = ( + 'global' => \&render_field_global, + 'number' => \&render_field_number, + 'container' => \&render_field_container, +); + +my %render_global_type = ( + 'enum-type' => \&render_global_enum, + 'struct-type' => \&render_global_class, + 'class-type' => \&render_global_class, + 'bitfield-type' => \&render_global_bitfield, +); + +sub render_global_enum { + my ($name, $type) = @_; + my $value = -1; + + my $rbname = rb_ucase($name); + push @lines_rb, "class $rbname"; + indent_rb { + for my $item ($type->findnodes('child::enum-item')) { + $value = $item->getAttribute('value') || ($value+1); + my $elemname = $item->getAttribute('name'); # || "unk_$value"; + + if ($elemname) { + my $rbelemname = rb_ucase($elemname); + push @lines_rb, "$rbelemname = $value"; + } + } + }; + push @lines_rb, "end"; +} + +sub render_global_bitfield { + my ($name, $type) = @_; + + my $rbname = rb_ucase($name); + push @lines_rb, "class $rbname < MemStruct"; + indent_rb { + my $shift = 0; + for my $field ($type->findnodes('child::ld:field')) { + my $count = $field->getAttribute('count') || 1; + print "bitfield $name !number\n" if (!($field->getAttribute('ld:meta') eq 'number')); + my $fname = $field->getAttribute('name'); + push @lines_rb, "bits :$fname, $shift, $count" if ($fname); + $shift += $count; + } + }; + push @lines_rb, "end"; +} + +sub render_global_class { + my ($name, $type) = @_; + + #my $cppvar = 'v'; # for offsetof + + my $rbname = rb_ucase($name); + my $parent = rb_ucase($type->getAttribute('inherits-from') || 'MemStruct'); + push @lines_rb, "class $rbname < $parent"; + indent_rb { + render_struct_fields($type); + }; + push @lines_rb, "end"; +} + +sub render_field_global { + my ($name, $field) = @_; + + my $typename = $field->getAttribute('type-name'); + my $rbname = rb_ucase($typename); + my $offset = "'offsetof_$name'"; + + push @lines_rb, "global :$name, :$rbname, $offset"; +} + +sub render_field_number { + my ($name, $field) = @_; + + my $subtype = $field->getAttribute('ld:subtype'); + my $offset = "'offsetof_$name'"; + + push @lines_rb, "number :$name, :$subtype, $offset" +} + +sub render_field_container { + my ($name, $field) = @_; + + my $subtype = $field->getAttribute('ld:subtype'); + my $offset = "'offsetof_$name'"; + + if ($subtype eq 'stl-vector') { + my $elem = $field->findnodes('child::ld:item')->[0] or return; + + push @lines_rb, "vector :$name, :$subtype, $offset" + } else { + print "no render field container $subtype\n"; + } +} + +sub render_struct_fields { + my ($type) = @_; + + for my $field ($type->findnodes('child::ld:field')) { + my $name = $field->getAttribute('name'); + #$name = $field->getAttribute('ld:anon-name') if (!$name); + next if (!$name); + my $meta = $field->getAttribute('ld:meta'); + + my $renderer = $render_struct_field{$meta}; + if ($renderer) { + $renderer->($name, $field); + } else { + print "no render field $meta\n"; + } + } +} + + + +my $input = $ARGV[0] || '../../library/include/df/codegen.out.xml'; +my $output_rb = $ARGV[1] || 'ruby-autogen.rb'; +my $output_cpp = $ARGV[2] || 'ruby-autogen.cpp'; + +my $doc = XML::LibXML->new()->parse_file($input); +my %global_types; +$global_types{$_->getAttribute('type-name')} = $_ foreach $doc->findnodes('/ld:data-definition/ld:global-type'); + +for my $name (sort { $a cmp $b } keys %global_types) { + my $type = $global_types{$name}; + my $meta = $type->getAttribute('ld:meta'); + my $renderer = $render_global_type{$meta}; + if ($renderer) { + $renderer->($name, $type); + } else { + print "no render global type $meta\n"; + } +} + +for my $obj ($doc->findnodes('/ld:data-definition/ld:global-object')) { + my $name = $obj->getAttribute('name'); +} + +open FH, ">$output_rb"; +print FH "$_\n" for @lines_rb; +close FH; + +open FH, ">$output_cpp"; +print FH "$_\n" for @lines_cpp; +close FH; From 780d7a8b18c8644dfdb6362188c752cafa8583d0 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 1 Apr 2012 01:33:30 +0200 Subject: [PATCH 05/72] codegen ++ --- plugins/ruby/codegen.pl | 250 +++++++++++++++++++++++++++++++++++----- 1 file changed, 220 insertions(+), 30 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 74350c255..c4d7cae18 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -21,60 +21,93 @@ sub indent_rb(&) { sub rb_ucase { my ($name) = @_; + return $name if ($name eq uc($name)); return join("", map { ucfirst $_ } (split('_', $name))); } +my %render_global_type = ( + 'enum-type' => \&render_global_enum, + 'struct-type' => \&render_global_class, + 'class-type' => \&render_global_class, + 'bitfield-type' => \&render_global_bitfield, +); + my %render_struct_field = ( 'global' => \&render_field_global, 'number' => \&render_field_number, 'container' => \&render_field_container, + 'compound' => \&render_field_compound, + 'pointer' => \&render_field_pointer, + 'static-array' => \&render_field_array, + 'primitive' => \&render_field_primitive, + 'bytes' => \&render_field_bytes, ); -my %render_global_type = ( - 'enum-type' => \&render_global_enum, - 'struct-type' => \&render_global_class, - 'class-type' => \&render_global_class, - 'bitfield-type' => \&render_global_bitfield, +my %render_item = ( + 'number' => \&render_item_number, + 'global' => \&render_item_global, + 'primitive' => \&render_item_primitive, + 'pointer' => \&render_item_pointer, + 'static-array' => \&render_item_array, + 'container' => \&render_item_container, + 'compound' => \&render_item_compound, ); sub render_global_enum { my ($name, $type) = @_; - my $value = -1; my $rbname = rb_ucase($name); push @lines_rb, "class $rbname"; indent_rb { - for my $item ($type->findnodes('child::enum-item')) { - $value = $item->getAttribute('value') || ($value+1); - my $elemname = $item->getAttribute('name'); # || "unk_$value"; - - if ($elemname) { - my $rbelemname = rb_ucase($elemname); - push @lines_rb, "$rbelemname = $value"; - } - } + render_enum_fields($type); }; push @lines_rb, "end"; } +sub render_enum_fields { + my ($type) = @_; + + my $value = -1; + for my $item ($type->findnodes('child::enum-item')) { + $value = $item->getAttribute('value') || ($value+1); + my $elemname = $item->getAttribute('name'); # || "unk_$value"; + + if ($elemname) { + my $rbelemname = rb_ucase($elemname); + push @lines_rb, "$rbelemname = $value"; + } + } +} + sub render_global_bitfield { my ($name, $type) = @_; my $rbname = rb_ucase($name); push @lines_rb, "class $rbname < MemStruct"; indent_rb { - my $shift = 0; - for my $field ($type->findnodes('child::ld:field')) { - my $count = $field->getAttribute('count') || 1; - print "bitfield $name !number\n" if (!($field->getAttribute('ld:meta') eq 'number')); - my $fname = $field->getAttribute('name'); - push @lines_rb, "bits :$fname, $shift, $count" if ($fname); - $shift += $count; - } + render_bitfield_fields($type); }; push @lines_rb, "end"; } +sub render_bitfield_fields { + my ($type) = @_; + + my $shift = 0; + for my $field ($type->findnodes('child::ld:field')) { + my $count = $field->getAttribute('count') || 1; + my $name = $field->getAttribute('name'); + $name = $field->getAttribute('ld:anon-name') if (!$name); + print "bitfield $name !number\n" if (!($field->getAttribute('ld:meta') eq 'number')); + if ($count == 1) { + push @lines_rb, "bit :$name, $shift" if ($name); + } else { + push @lines_rb, "bits :$name, $shift, $count" if ($name); + } + $shift += $count; + } +} + sub render_global_class { my ($name, $type) = @_; @@ -96,7 +129,7 @@ sub render_field_global { my $rbname = rb_ucase($typename); my $offset = "'offsetof_$name'"; - push @lines_rb, "global :$name, :$rbname, $offset"; + push @lines_rb, "global :$name, $offset, :$rbname"; } sub render_field_number { @@ -104,8 +137,31 @@ sub render_field_number { my $subtype = $field->getAttribute('ld:subtype'); my $offset = "'offsetof_$name'"; + $subtype = 'float' if ($subtype eq 's-float'); - push @lines_rb, "number :$name, :$subtype, $offset" + push @lines_rb, "$subtype :$name, $offset" +} + +sub render_field_compound { + my ($name, $field) = @_; + + my $offset = "'offsetof_$name'"; + my $subtype = $field->getAttribute('ld:subtype'); + if (!$subtype || $subtype eq 'bitfield') { + push @lines_rb, "compound(:$name, $offset) {"; + indent_rb { + if (!$subtype) { + render_struct_fields($field); + } else { + render_bitfield_fields($field); + } + }; + push @lines_rb, "}" + } elsif ($subtype eq 'enum') { + render_enum_fields($field); + } else { + print "no render compound $subtype\n"; + } } sub render_field_container { @@ -114,21 +170,81 @@ sub render_field_container { my $subtype = $field->getAttribute('ld:subtype'); my $offset = "'offsetof_$name'"; - if ($subtype eq 'stl-vector') { - my $elem = $field->findnodes('child::ld:item')->[0] or return; - - push @lines_rb, "vector :$name, :$subtype, $offset" + if ($subtype eq 'stl-deque') { + push @lines_rb, "stl_deque :$name, $offset"; + } elsif ($subtype eq 'stl-bit-vector') { + push @lines_rb, "stl_bitvector :$name, $offset"; + } elsif ($subtype eq 'stl-vector') { + my $item = $field->findnodes('child::ld:item')->[0] or return; + my $itemdesc = render_item($item); + push @lines_rb, "stl_vector(:$name, $offset, $itemdesc"; + } elsif ($subtype eq 'df-array') { + my $item = $field->findnodes('child::ld:item')->[0] or return; + my $itemdesc = render_item($item); + push @lines_rb, "df_array(:$name, $offset, $itemdesc"; + } elsif ($subtype eq 'df-linked-list') { + my $item = $field->findnodes('child::ld:item')->[0] or return; + my $itemdesc = render_item($item); + push @lines_rb, "df_linklist(:$name, $offset, $itemdesc"; + } elsif ($subtype eq 'df-flagarray') { + push @lines_rb, "df_flagarray :$name, $offset"; } else { print "no render field container $subtype\n"; } } + +sub render_field_pointer { + my ($name, $field) = @_; + + my $offset = "'offsetof_$name'"; + my $item = $field->findnodes('child::ld:item')->[0] or return; + my $itemdesc = render_item($item); + push @lines_rb, "pointer(:$name, $offset, $itemdesc"; +} + +sub render_field_array { + my ($name, $field) = @_; + + my $offset = "'offsetof_$name'"; + my $count = $field->getAttribute('count'); + my $item = $field->findnodes('child::ld:item')->[0] or return; + my $itemdesc = render_item($item); + push @lines_rb, "staticarray(:$name, $offset, $count, $itemdesc"; +} + +sub render_field_primitive { + my ($name, $field) = @_; + + my $offset = "'offsetof_$name'"; + my $subtype = $field->getAttribute('ld:subtype'); + if ($subtype eq 'stl-string') { + push @lines_rb, "string :$name, $offset"; + } else { + print "no render primitive $subtype $name\n"; + } +} + +sub render_field_bytes { + my ($name, $field) = @_; + + my $offset = "'offsetof_$name'"; + my $subtype = $field->getAttribute('ld:subtype'); + if ($subtype eq 'padding') { + } elsif ($subtype eq 'static-string') { + my $size = $field->getAttribute('ld:subtype'); + push @lines_rb, "static_string :$name, $offset, $size"; + } else { + print "no render field bytes $subtype $name\n"; + } +} + sub render_struct_fields { my ($type) = @_; for my $field ($type->findnodes('child::ld:field')) { my $name = $field->getAttribute('name'); - #$name = $field->getAttribute('ld:anon-name') if (!$name); + $name = $field->getAttribute('ld:anon-name') if (!$name); next if (!$name); my $meta = $field->getAttribute('ld:meta'); @@ -141,7 +257,81 @@ sub render_struct_fields { } } +sub render_item_number { + my ($item) = @_; + + my $sz = $item->getAttribute('ld:bits') / 8; + my $subtype = $item->getAttribute('ld:subtype'); + $subtype = 'float' if ($subtype eq 's-float'); + return "$sz) { a_$subtype }"; +} + +sub render_item_global { + my ($item) = @_; + + my $typename = $item->getAttribute('type-name'); + my $rbname = rb_ucase($typename); + return "nil) { a_global :$rbname }"; +} + +sub render_item_primitive { + my ($item) = @_; + + my $subtype = $item->getAttribute('ld:subtype'); + if ($subtype eq 'stl-string') { + return "nil) { a_string }"; + } else { + print "no render a_primitive $subtype\n"; + return "nil)"; + } +} + +sub render_item_pointer { + my ($item) = @_; + my $pitem = $item->findnodes('child::ld:item')->[0]; + my $desc; + if ($pitem) { + $desc = render_item($pitem); + } else { + $desc = 'nil)'; + } + return "4) { a_pointer($desc }"; +} + +sub render_item_array { + my ($item) = @_; + + print "no render item array\n"; + return "nil)"; +} + +sub render_item_container { + my ($item) = @_; + + print "no render item container\n"; + return "nil)"; +} + +sub render_item_compound { + my ($item) = @_; + + print "no render item compound\n"; + return "nil)"; +} + +sub render_item { + my ($item) = @_; + my $meta = $item->getAttribute('ld:meta'); + + my $renderer = $render_item{$meta}; + if ($renderer) { + return $renderer->($item); + } else { + print "no render field $meta\n"; + return "nil)"; + } +} my $input = $ARGV[0] || '../../library/include/df/codegen.out.xml'; my $output_rb = $ARGV[1] || 'ruby-autogen.rb'; From 872eebe7ef6d04f9a22d1356a6a2f6f525ec34c9 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 2 Apr 2012 19:00:38 +0200 Subject: [PATCH 06/72] rbcodegen: hop --- plugins/ruby/codegen.pl | 304 +++++++++++++++++----------------------- 1 file changed, 130 insertions(+), 174 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index c4d7cae18..e7052f472 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -25,34 +25,25 @@ sub rb_ucase { return join("", map { ucfirst $_ } (split('_', $name))); } -my %render_global_type = ( +my %global_type_renderer = ( 'enum-type' => \&render_global_enum, 'struct-type' => \&render_global_class, 'class-type' => \&render_global_class, 'bitfield-type' => \&render_global_bitfield, ); -my %render_struct_field = ( - 'global' => \&render_field_global, - 'number' => \&render_field_number, - 'container' => \&render_field_container, - 'compound' => \&render_field_compound, - 'pointer' => \&render_field_pointer, - 'static-array' => \&render_field_array, - 'primitive' => \&render_field_primitive, - 'bytes' => \&render_field_bytes, -); - -my %render_item = ( - 'number' => \&render_item_number, +my %item_renderer = ( 'global' => \&render_item_global, - 'primitive' => \&render_item_primitive, - 'pointer' => \&render_item_pointer, - 'static-array' => \&render_item_array, + 'number' => \&render_item_number, 'container' => \&render_item_container, 'compound' => \&render_item_compound, + 'pointer' => \&render_item_pointer, + 'static-array' => \&render_item_staticarray, + 'primitive' => \&render_item_primitive, + 'bytes' => \&render_item_bytes, ); + sub render_global_enum { my ($name, $type) = @_; @@ -63,7 +54,6 @@ sub render_global_enum { }; push @lines_rb, "end"; } - sub render_enum_fields { my ($type) = @_; @@ -79,6 +69,7 @@ sub render_enum_fields { } } + sub render_global_bitfield { my ($name, $type) = @_; @@ -89,7 +80,6 @@ sub render_global_bitfield { }; push @lines_rb, "end"; } - sub render_bitfield_fields { my ($type) = @_; @@ -108,6 +98,7 @@ sub render_bitfield_fields { } } + sub render_global_class { my ($name, $type) = @_; @@ -121,157 +112,141 @@ sub render_global_class { }; push @lines_rb, "end"; } +sub render_struct_fields { + my ($type) = @_; -sub render_field_global { - my ($name, $field) = @_; - - my $typename = $field->getAttribute('type-name'); - my $rbname = rb_ucase($typename); - my $offset = "'offsetof_$name'"; - - push @lines_rb, "global :$name, $offset, :$rbname"; -} - -sub render_field_number { - my ($name, $field) = @_; - - my $subtype = $field->getAttribute('ld:subtype'); - my $offset = "'offsetof_$name'"; - $subtype = 'float' if ($subtype eq 's-float'); - - push @lines_rb, "$subtype :$name, $offset" -} - -sub render_field_compound { - my ($name, $field) = @_; + for my $field ($type->findnodes('child::ld:field')) { + my $name = $field->getAttribute('name'); + $name = $field->getAttribute('ld:anon-name') if (!$name); + next if (!$name); + my $offset = "'TODOoffsetof_$name'"; - my $offset = "'offsetof_$name'"; - my $subtype = $field->getAttribute('ld:subtype'); - if (!$subtype || $subtype eq 'bitfield') { - push @lines_rb, "compound(:$name, $offset) {"; + push @lines_rb, "field(:$name, $offset) {"; indent_rb { - if (!$subtype) { - render_struct_fields($field); - } else { - render_bitfield_fields($field); - } + render_item($field); }; - push @lines_rb, "}" - } elsif ($subtype eq 'enum') { - render_enum_fields($field); - } else { - print "no render compound $subtype\n"; + push @lines_rb, "}"; } } -sub render_field_container { - my ($name, $field) = @_; - - my $subtype = $field->getAttribute('ld:subtype'); - my $offset = "'offsetof_$name'"; - - if ($subtype eq 'stl-deque') { - push @lines_rb, "stl_deque :$name, $offset"; - } elsif ($subtype eq 'stl-bit-vector') { - push @lines_rb, "stl_bitvector :$name, $offset"; - } elsif ($subtype eq 'stl-vector') { - my $item = $field->findnodes('child::ld:item')->[0] or return; - my $itemdesc = render_item($item); - push @lines_rb, "stl_vector(:$name, $offset, $itemdesc"; - } elsif ($subtype eq 'df-array') { - my $item = $field->findnodes('child::ld:item')->[0] or return; - my $itemdesc = render_item($item); - push @lines_rb, "df_array(:$name, $offset, $itemdesc"; - } elsif ($subtype eq 'df-linked-list') { - my $item = $field->findnodes('child::ld:item')->[0] or return; - my $itemdesc = render_item($item); - push @lines_rb, "df_linklist(:$name, $offset, $itemdesc"; - } elsif ($subtype eq 'df-flagarray') { - push @lines_rb, "df_flagarray :$name, $offset"; +sub render_item { + my ($item) = @_; + return if (!$item); + + my $meta = $item->getAttribute('ld:meta'); + + my $renderer = $item_renderer{$meta}; + if ($renderer) { + $renderer->($item); } else { - print "no render field container $subtype\n"; + print "no render item $meta\n"; } } +sub render_item_global { + my ($item) = @_; -sub render_field_pointer { - my ($name, $field) = @_; - - my $offset = "'offsetof_$name'"; - my $item = $field->findnodes('child::ld:item')->[0] or return; - my $itemdesc = render_item($item); - push @lines_rb, "pointer(:$name, $offset, $itemdesc"; -} - -sub render_field_array { - my ($name, $field) = @_; + my $typename = $item->getAttribute('type-name'); + my $rbname = rb_ucase($typename); - my $offset = "'offsetof_$name'"; - my $count = $field->getAttribute('count'); - my $item = $field->findnodes('child::ld:item')->[0] or return; - my $itemdesc = render_item($item); - push @lines_rb, "staticarray(:$name, $offset, $count, $itemdesc"; + push @lines_rb, "global :$rbname"; } -sub render_field_primitive { - my ($name, $field) = @_; +sub render_item_number { + my ($item) = @_; - my $offset = "'offsetof_$name'"; - my $subtype = $field->getAttribute('ld:subtype'); - if ($subtype eq 'stl-string') { - push @lines_rb, "string :$name, $offset"; + my $subtype = $item->getAttribute('ld:subtype'); + $subtype = $item->getAttribute('base-type') if ($subtype eq 'enum'); + $subtype = 'int32_t' if (!$subtype); + + if ($subtype eq 'int64_t') { + push @lines_rb, 'number 64, true'; + } elsif ($subtype eq 'uint32_t') { + push @lines_rb, 'number 32, false'; + } elsif ($subtype eq 'int32_t') { + push @lines_rb, 'number 32, true'; + } elsif ($subtype eq 'uint16_t') { + push @lines_rb, 'number 16, false'; + } elsif ($subtype eq 'int16_t') { + push @lines_rb, 'number 16, true'; + } elsif ($subtype eq 'uint8_t') { + push @lines_rb, 'number 8, false'; + } elsif ($subtype eq 'int8_t') { + push @lines_rb, 'number 8, false'; + } elsif ($subtype eq 'bool') { + push @lines_rb, 'number 8, true'; + } elsif ($subtype eq 's-float') { + push @lines_rb, 'float'; } else { - print "no render primitive $subtype $name\n"; + print "no render number $subtype\n"; } } -sub render_field_bytes { - my ($name, $field) = @_; +sub render_item_compound { + my ($item) = @_; - my $offset = "'offsetof_$name'"; - my $subtype = $field->getAttribute('ld:subtype'); - if ($subtype eq 'padding') { - } elsif ($subtype eq 'static-string') { - my $size = $field->getAttribute('ld:subtype'); - push @lines_rb, "static_string :$name, $offset, $size"; + my $subtype = $item->getAttribute('ld:subtype'); + if (!$subtype || $subtype eq 'bitfield') { + push @lines_rb, "compound {"; + indent_rb { + if (!$subtype) { + render_struct_fields($item); + } else { + render_bitfield_fields($item); + } + }; + push @lines_rb, "}" + } elsif ($subtype eq 'enum') { + # declare constants + render_enum_fields($item); + # actual field + render_item_number($item); } else { - print "no render field bytes $subtype $name\n"; + print "no render compound $subtype\n"; } } -sub render_struct_fields { - my ($type) = @_; - - for my $field ($type->findnodes('child::ld:field')) { - my $name = $field->getAttribute('name'); - $name = $field->getAttribute('ld:anon-name') if (!$name); - next if (!$name); - my $meta = $field->getAttribute('ld:meta'); +sub render_item_container { + my ($item) = @_; - my $renderer = $render_struct_field{$meta}; - if ($renderer) { - $renderer->($name, $field); - } else { - print "no render field $meta\n"; - } + my $subtype = $item->getAttribute('ld:subtype'); + my $rbmethod = join('_', split('-', $subtype)); + my $tg = $item->findnodes('child::ld:item')->[0]; + if ($tg) { + my $tglen = get_tglen($tg); + push @lines_rb, "$rbmethod($tglen) {"; + indent_rb { + render_item($tg); + }; + push @lines_rb, "}"; + } else { + push @lines_rb, "$rbmethod"; } } -sub render_item_number { +sub render_item_pointer { my ($item) = @_; - my $sz = $item->getAttribute('ld:bits') / 8; - my $subtype = $item->getAttribute('ld:subtype'); - $subtype = 'float' if ($subtype eq 's-float'); - return "$sz) { a_$subtype }"; + my $tg = $item->findnodes('child::ld:item')->[0]; + my $tglen = get_tglen($tg); + push @lines_rb, "pointer($tglen) {"; + indent_rb { + render_item($tg); + }; + push @lines_rb, "}"; } -sub render_item_global { +sub render_item_staticarray { my ($item) = @_; - my $typename = $item->getAttribute('type-name'); - my $rbname = rb_ucase($typename); - return "nil) { a_global :$rbname }"; + my $count = $item->getAttribute('count'); + my $tg = $item->findnodes('child::ld:item')->[0]; + my $tglen = get_tglen($tg); + push @lines_rb, "static_array($count, $tglen) {"; + indent_rb { + render_item($tg); + }; + push @lines_rb, "}"; } sub render_item_primitive { @@ -279,57 +254,38 @@ sub render_item_primitive { my $subtype = $item->getAttribute('ld:subtype'); if ($subtype eq 'stl-string') { - return "nil) { a_string }"; + push @lines_rb, "stl_string"; } else { - print "no render a_primitive $subtype\n"; - return "nil)"; + print "no render primitive $subtype\n"; } } -sub render_item_pointer { +sub render_item_bytes { my ($item) = @_; - my $pitem = $item->findnodes('child::ld:item')->[0]; - my $desc; - if ($pitem) { - $desc = render_item($pitem); + my $subtype = $item->getAttribute('ld:subtype'); + if ($subtype eq 'padding') { + } elsif ($subtype eq 'static-string') { + my $size = $item->getAttribute('size'); + push @lines_rb, "static_string($size)"; } else { - $desc = 'nil)'; + print "no render bytes $subtype\n"; } - return "4) { a_pointer($desc }"; -} - -sub render_item_array { - my ($item) = @_; - - print "no render item array\n"; - return "nil)"; } -sub render_item_container { - my ($item) = @_; - - print "no render item container\n"; - return "nil)"; -} - -sub render_item_compound { - my ($item) = @_; - - print "no render item compound\n"; - return "nil)"; -} -sub render_item { - my ($item) = @_; - my $meta = $item->getAttribute('ld:meta'); +sub get_tglen { + my ($tg) = @_; + if (!$tg) { return 'nil'; } - my $renderer = $render_item{$meta}; - if ($renderer) { - return $renderer->($item); + my $meta = $tg->getAttribute('ld:meta'); + if ($meta eq 'number') { + return $tg->getAttribute('ld:bits')/8; + } elsif ($meta eq 'pointer') { + return 4; } else { - print "no render field $meta\n"; - return "nil)"; + # TODO + return "'TODOsizeof($meta)'"; } } @@ -344,7 +300,7 @@ $global_types{$_->getAttribute('type-name')} = $_ foreach $doc->findnodes('/ld:d for my $name (sort { $a cmp $b } keys %global_types) { my $type = $global_types{$name}; my $meta = $type->getAttribute('ld:meta'); - my $renderer = $render_global_type{$meta}; + my $renderer = $global_type_renderer{$meta}; if ($renderer) { $renderer->($name, $type); } else { From 3f39e285d06dba8ff9a64aa4a0cd628a888dd786 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 3 Apr 2012 16:09:29 +0200 Subject: [PATCH 07/72] codegen: gen cpp helper --- plugins/ruby/codegen.pl | 117 +++++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 36 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index e7052f472..58d97342f 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -6,7 +6,9 @@ use warnings; use XML::LibXML; our @lines_rb; -our @lines_cpp; +my @lines_cpp; +my @include_cpp; +my %offsets; sub indent_rb(&) { my ($sub) = @_; @@ -99,52 +101,56 @@ sub render_bitfield_fields { } +my $cpp_var_count = 0; sub render_global_class { my ($name, $type) = @_; - #my $cppvar = 'v'; # for offsetof + my $cppvar = "v_$cpp_var_count"; + $cpp_var_count += 1; + push @lines_cpp, "df::$name $cppvar;"; + push @include_cpp, $name; my $rbname = rb_ucase($name); my $parent = rb_ucase($type->getAttribute('inherits-from') || 'MemStruct'); push @lines_rb, "class $rbname < $parent"; indent_rb { - render_struct_fields($type); + render_struct_fields($type, $cppvar); }; push @lines_rb, "end"; } sub render_struct_fields { - my ($type) = @_; + my ($type, $cppvar) = @_; for my $field ($type->findnodes('child::ld:field')) { my $name = $field->getAttribute('name'); $name = $field->getAttribute('ld:anon-name') if (!$name); next if (!$name); - my $offset = "'TODOoffsetof_$name'"; + my $offset = get_offset($cppvar, $name); push @lines_rb, "field(:$name, $offset) {"; indent_rb { - render_item($field); + render_item($field, "$cppvar.$name"); }; push @lines_rb, "}"; } } sub render_item { - my ($item) = @_; + my ($item, $cppvar) = @_; return if (!$item); my $meta = $item->getAttribute('ld:meta'); my $renderer = $item_renderer{$meta}; if ($renderer) { - $renderer->($item); + $renderer->($item, $cppvar); } else { print "no render item $meta\n"; } } sub render_item_global { - my ($item) = @_; + my ($item, $cppvar) = @_; my $typename = $item->getAttribute('type-name'); my $rbname = rb_ucase($typename); @@ -153,7 +159,7 @@ sub render_item_global { } sub render_item_number { - my ($item) = @_; + my ($item, $cppvar) = @_; my $subtype = $item->getAttribute('ld:subtype'); $subtype = $item->getAttribute('base-type') if ($subtype eq 'enum'); @@ -183,14 +189,14 @@ sub render_item_number { } sub render_item_compound { - my ($item) = @_; + my ($item, $cppvar) = @_; my $subtype = $item->getAttribute('ld:subtype'); if (!$subtype || $subtype eq 'bitfield') { push @lines_rb, "compound {"; indent_rb { if (!$subtype) { - render_struct_fields($item); + render_struct_fields($item, $cppvar); } else { render_bitfield_fields($item); } @@ -200,23 +206,23 @@ sub render_item_compound { # declare constants render_enum_fields($item); # actual field - render_item_number($item); + render_item_number($item, $cppvar); } else { print "no render compound $subtype\n"; } } sub render_item_container { - my ($item) = @_; + my ($item, $cppvar) = @_; my $subtype = $item->getAttribute('ld:subtype'); my $rbmethod = join('_', split('-', $subtype)); my $tg = $item->findnodes('child::ld:item')->[0]; if ($tg) { - my $tglen = get_tglen($tg); + my $tglen = get_tglen($tg, $cppvar); push @lines_rb, "$rbmethod($tglen) {"; indent_rb { - render_item($tg); + render_item($tg, "${cppvar}[0]"); }; push @lines_rb, "}"; } else { @@ -225,32 +231,32 @@ sub render_item_container { } sub render_item_pointer { - my ($item) = @_; + my ($item, $cppvar) = @_; my $tg = $item->findnodes('child::ld:item')->[0]; - my $tglen = get_tglen($tg); + my $tglen = get_tglen($tg, $cppvar); push @lines_rb, "pointer($tglen) {"; indent_rb { - render_item($tg); + render_item($tg, "${cppvar}[0]"); }; push @lines_rb, "}"; } sub render_item_staticarray { - my ($item) = @_; + my ($item, $cppvar) = @_; my $count = $item->getAttribute('count'); my $tg = $item->findnodes('child::ld:item')->[0]; - my $tglen = get_tglen($tg); + my $tglen = get_tglen($tg, $cppvar); push @lines_rb, "static_array($count, $tglen) {"; indent_rb { - render_item($tg); + render_item($tg, "${cppvar}[0]"); }; push @lines_rb, "}"; } sub render_item_primitive { - my ($item) = @_; + my ($item, $cppvar) = @_; my $subtype = $item->getAttribute('ld:subtype'); if ($subtype eq 'stl-string') { @@ -261,7 +267,7 @@ sub render_item_primitive { } sub render_item_bytes { - my ($item) = @_; + my ($item, $cppvar) = @_; my $subtype = $item->getAttribute('ld:subtype'); if ($subtype eq 'padding') { @@ -274,9 +280,18 @@ sub render_item_bytes { } +sub get_offset { + my ($cppvar, $fname) = @_; + + return query_cpp("offsetof(typeof($cppvar), $fname)"); +} + sub get_tglen { - my ($tg) = @_; - if (!$tg) { return 'nil'; } + my ($tg, $cppvar) = @_; + + if (!$tg) { + return query_cpp("sizeof(${cppvar}[0])"); + } my $meta = $tg->getAttribute('ld:meta'); if ($meta eq 'number') { @@ -284,14 +299,40 @@ sub get_tglen { } elsif ($meta eq 'pointer') { return 4; } else { - # TODO - return "'TODOsizeof($meta)'"; + return query_cpp("sizeof(${cppvar}[0])"); } } +sub query_cpp { + my ($query) = @_; + + my $ans = $offsets{$query}; + return $ans if ($ans); + + push @lines_cpp, "cout << \"$query = \" << $query << endl;"; + return "'$query'"; +} + + + my $input = $ARGV[0] || '../../library/include/df/codegen.out.xml'; -my $output_rb = $ARGV[1] || 'ruby-autogen.rb'; -my $output_cpp = $ARGV[2] || 'ruby-autogen.cpp'; + +# run once with output = 'ruby-autogen.cpp' +# compile +# execute, save output to 'ruby-autogen.offsets' +# re-run this script with output = 'ruby-autogen.rb' and offsetfile = 'ruby-autogen.offsets' +# delete binary +# delete offsets +my $output = $ARGV[1] or die "need output file"; +my $offsetfile = $ARGV[2]; + +if ($offsetfile) { + while (<$offsetfile>) { + my @val = split(' = '); + $offsets{$val[0]} = $val[1]; + } +} + my $doc = XML::LibXML->new()->parse_file($input); my %global_types; @@ -310,12 +351,16 @@ for my $name (sort { $a cmp $b } keys %global_types) { for my $obj ($doc->findnodes('/ld:data-definition/ld:global-object')) { my $name = $obj->getAttribute('name'); + # TODO } -open FH, ">$output_rb"; -print FH "$_\n" for @lines_rb; -close FH; - -open FH, ">$output_cpp"; -print FH "$_\n" for @lines_cpp; +open FH, ">$output"; +if ($output =~ /\.cpp$/) { + print FH "#include \"$_\"\n" for @include_cpp; + print FH "void main(void) {\n"; + print FH " $_\n" for @lines_cpp; + print FH "}\n"; +} else { + print FH "$_\n" for @lines_rb; +} close FH; From 689cc50409c4f374da1695f6afb1f25e91849230 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 5 Apr 2012 16:46:07 +0200 Subject: [PATCH 08/72] multistage autogen en route --- plugins/ruby/CMakeLists.txt | 19 ++++++++++++++++++- plugins/ruby/codegen.pl | 32 ++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index a44fb52cd..4a69c09ea 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -1,9 +1,26 @@ find_package(Ruby) if(RUBY_FOUND) + ADD_CUSTOM_COMMAND( + OUTPUT ruby-autogen.cpp + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.cpp + DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl + ) + ADD_EXECUTABLE(ruby-autogen ruby-autogen.cpp) + ADD_CUSTOM_COMMAND( + OUTPUT ruby-autogen.offsets + COMMAND ruby-autogen ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets + DEPENDS ruby-autogen + ) + ADD_CUSTOM_COMMAND( + OUTPUT ruby-autogen.rb + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets + DEPENDS ruby-autogen.offsets + ) + ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb) include_directories("${dfhack_SOURCE_DIR}/depends/tthread" ${RUBY_INCLUDE_PATH}) DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) target_link_libraries(ruby ${RUBY_LIBRARY}) - install(FILES ruby.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION}) + install(FILES ruby.rb ruby-autogen.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION}) else(RUBY_FOUND) MESSAGE(STATUS "Required library (ruby) not found - ruby plugin can't be built.") endif(RUBY_FOUND) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 58d97342f..517f6576a 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -105,7 +105,7 @@ my $cpp_var_count = 0; sub render_global_class { my ($name, $type) = @_; - my $cppvar = "v_$cpp_var_count"; + my $cppvar = "*v_$cpp_var_count"; $cpp_var_count += 1; push @lines_cpp, "df::$name $cppvar;"; push @include_cpp, $name; @@ -114,7 +114,7 @@ sub render_global_class { my $parent = rb_ucase($type->getAttribute('inherits-from') || 'MemStruct'); push @lines_rb, "class $rbname < $parent"; indent_rb { - render_struct_fields($type, $cppvar); + render_struct_fields($type, "($cppvar)"); }; push @lines_rb, "end"; } @@ -283,7 +283,9 @@ sub render_item_bytes { sub get_offset { my ($cppvar, $fname) = @_; - return query_cpp("offsetof(typeof($cppvar), $fname)"); + # GCC fails with this + #return query_cpp("offsetof(typeof($cppvar), $fname)"); + return query_cpp("((char*)&$cppvar.$fname - (char*)&$cppvar)"); } sub get_tglen { @@ -309,7 +311,7 @@ sub query_cpp { my $ans = $offsets{$query}; return $ans if ($ans); - push @lines_cpp, "cout << \"$query = \" << $query << endl;"; + push @lines_cpp, "fout << \"$query = \" << $query << std::endl;"; return "'$query'"; } @@ -327,10 +329,12 @@ my $output = $ARGV[1] or die "need output file"; my $offsetfile = $ARGV[2]; if ($offsetfile) { - while (<$offsetfile>) { - my @val = split(' = '); + open OF, "<$offsetfile"; + while () { + my @val = split(' = ', chomp); $offsets{$val[0]} = $val[1]; } + close OF; } @@ -356,9 +360,21 @@ for my $obj ($doc->findnodes('/ld:data-definition/ld:global-object')) { open FH, ">$output"; if ($output =~ /\.cpp$/) { - print FH "#include \"$_\"\n" for @include_cpp; - print FH "void main(void) {\n"; + print FH "#include \"Core.h\"\n"; + print FH "#include \"Console.h\"\n"; + print FH "#include \"Export.h\"\n"; + print FH "#include \"PluginManager.h\"\n"; + print FH "#include \"DataDefs.h\"\n"; + print FH "#include \"df/$_.h\"\n" for @include_cpp; + print FH "#include \n"; + print FH "#include \n"; + print FH "int main(int argc, char **argv) {\n"; + print FH " std::ofstream fout;\n"; + print FH " if (argc < 2) return 1;\n"; + print FH " fout.open(argv[1]);\n"; print FH " $_\n" for @lines_cpp; + print FH " fout.close();\n"; + print FH " return 0;\n"; print FH "}\n"; } else { print FH "$_\n" for @lines_rb; From fb1b424cbf652fba42c34af5a1baa40420f7363c Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 5 Apr 2012 18:23:10 +0200 Subject: [PATCH 09/72] fix various crash with gcc autogen.cpp. all seems well --- plugins/ruby/codegen.pl | 44 ++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 517f6576a..110dcccbd 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -101,20 +101,22 @@ sub render_bitfield_fields { } -my $cpp_var_count = 0; +my $cpp_var_counter = 0; sub render_global_class { my ($name, $type) = @_; - my $cppvar = "*v_$cpp_var_count"; - $cpp_var_count += 1; - push @lines_cpp, "df::$name $cppvar;"; + my $cppvar = "v_$cpp_var_counter"; + $cpp_var_counter++; + push @lines_cpp, "}" if @include_cpp; + push @lines_cpp, "void cpp_$name(FILE *fout) {"; + push @lines_cpp, " df::$name *$cppvar = (df::$name*)moo;"; push @include_cpp, $name; my $rbname = rb_ucase($name); my $parent = rb_ucase($type->getAttribute('inherits-from') || 'MemStruct'); push @lines_rb, "class $rbname < $parent"; indent_rb { - render_struct_fields($type, "($cppvar)"); + render_struct_fields($type, "(*$cppvar)"); }; push @lines_rb, "end"; } @@ -218,7 +220,8 @@ sub render_item_container { my $subtype = $item->getAttribute('ld:subtype'); my $rbmethod = join('_', split('-', $subtype)); my $tg = $item->findnodes('child::ld:item')->[0]; - if ($tg) { + # df-linked-list has no list[0] + if ($tg and $rbmethod ne 'df_linked_list') { my $tglen = get_tglen($tg, $cppvar); push @lines_rb, "$rbmethod($tglen) {"; indent_rb { @@ -292,7 +295,7 @@ sub get_tglen { my ($tg, $cppvar) = @_; if (!$tg) { - return query_cpp("sizeof(${cppvar}[0])"); + return 'nil'; } my $meta = $tg->getAttribute('ld:meta'); @@ -309,9 +312,9 @@ sub query_cpp { my ($query) = @_; my $ans = $offsets{$query}; - return $ans if ($ans); + return $ans if (defined($ans)); - push @lines_cpp, "fout << \"$query = \" << $query << std::endl;"; + push @lines_cpp, " fprintf(fout, \"%s = %d\\n\", \"$query\", $query);"; return "'$query'"; } @@ -330,9 +333,10 @@ my $offsetfile = $ARGV[2]; if ($offsetfile) { open OF, "<$offsetfile"; - while () { - my @val = split(' = ', chomp); - $offsets{$val[0]} = $val[1]; + while (my $line = ) { + chomp($line); + my ($key, $val) = split(' = ', $line); + $offsets{$key} = $val; } close OF; } @@ -366,14 +370,18 @@ if ($output =~ /\.cpp$/) { print FH "#include \"PluginManager.h\"\n"; print FH "#include \"DataDefs.h\"\n"; print FH "#include \"df/$_.h\"\n" for @include_cpp; - print FH "#include \n"; - print FH "#include \n"; + print FH "#include \n"; + print FH "static void *moo[1024];\n"; + print FH "$_\n" for @lines_cpp; + print FH "}\n"; print FH "int main(int argc, char **argv) {\n"; - print FH " std::ofstream fout;\n"; + print FH " FILE *fout;\n"; print FH " if (argc < 2) return 1;\n"; - print FH " fout.open(argv[1]);\n"; - print FH " $_\n" for @lines_cpp; - print FH " fout.close();\n"; + # sometimes gcc will generate accesses to the structures at 0 just for a sizeof/offsetof, this works around the segfaults... + print FH " for (int i=0 ; i<1024 ; i++) moo[i] = &moo;\n"; + print FH " fout = fopen(argv[1], \"w\");\n"; + print FH " cpp_$_(fout);\n" for @include_cpp; + print FH " fclose(fout);\n"; print FH " return 0;\n"; print FH "}\n"; } else { From 52007e0d4f6750d9010ca6f8ff3181e69cf677ea Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 6 Apr 2012 19:30:58 +0200 Subject: [PATCH 10/72] ruby: patron for memory primitives --- plugins/ruby/codegen.pl | 33 ++++-- plugins/ruby/ruby-memstruct.rb | 200 +++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 plugins/ruby/ruby-memstruct.rb diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 110dcccbd..29259fdc3 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -46,11 +46,13 @@ my %item_renderer = ( ); +my %seen_enum_name; sub render_global_enum { my ($name, $type) = @_; my $rbname = rb_ucase($name); push @lines_rb, "class $rbname"; + %seen_enum_name = (); indent_rb { render_enum_fields($type); }; @@ -66,6 +68,8 @@ sub render_enum_fields { if ($elemname) { my $rbelemname = rb_ucase($elemname); + $rbelemname .= '_' while ($seen_enum_name{$rbelemname}); + $seen_enum_name{$rbelemname}++; push @lines_rb, "$rbelemname = $value"; } } @@ -76,7 +80,7 @@ sub render_global_bitfield { my ($name, $type) = @_; my $rbname = rb_ucase($name); - push @lines_rb, "class $rbname < MemStruct"; + push @lines_rb, "class $rbname < Compound"; indent_rb { render_bitfield_fields($type); }; @@ -92,19 +96,33 @@ sub render_bitfield_fields { $name = $field->getAttribute('ld:anon-name') if (!$name); print "bitfield $name !number\n" if (!($field->getAttribute('ld:meta') eq 'number')); if ($count == 1) { - push @lines_rb, "bit :$name, $shift" if ($name); + push @lines_rb, "field(:$name, 0) { bit $shift }" if ($name); } else { - push @lines_rb, "bits :$name, $shift, $count" if ($name); + push @lines_rb, "field(:$name, 0) { bits $shift, $count }" if ($name); } $shift += $count; } } +my %global_types; my $cpp_var_counter = 0; +my %seen_class; sub render_global_class { my ($name, $type) = @_; + my $rbname = rb_ucase($name); + + # ensure pre-definition of ancestors + my $parent = $type->getAttribute('inherits-from'); + render_global_class($parent, $global_types{$parent}) if ($parent and !$seen_class{$parent}); + + return if $seen_class{$name}; + $seen_class{$name}++; + %seen_enum_name = (); + + my $rbparent = ($parent ? rb_ucase($parent) : 'Compound'); + my $cppvar = "v_$cpp_var_counter"; $cpp_var_counter++; push @lines_cpp, "}" if @include_cpp; @@ -112,9 +130,7 @@ sub render_global_class { push @lines_cpp, " df::$name *$cppvar = (df::$name*)moo;"; push @include_cpp, $name; - my $rbname = rb_ucase($name); - my $parent = rb_ucase($type->getAttribute('inherits-from') || 'MemStruct'); - push @lines_rb, "class $rbname < $parent"; + push @lines_rb, "class $rbname < $rbparent"; indent_rb { render_struct_fields($type, "(*$cppvar)"); }; @@ -343,7 +359,6 @@ if ($offsetfile) { my $doc = XML::LibXML->new()->parse_file($input); -my %global_types; $global_types{$_->getAttribute('type-name')} = $_ foreach $doc->findnodes('/ld:data-definition/ld:global-type'); for my $name (sort { $a cmp $b } keys %global_types) { @@ -385,6 +400,10 @@ if ($output =~ /\.cpp$/) { print FH " return 0;\n"; print FH "}\n"; } else { + print FH "module DFHack\n"; + print FH "module MemHack\n"; print FH "$_\n" for @lines_rb; + print FH "end\n"; + print FH "end\n"; } close FH; diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb new file mode 100644 index 000000000..9b78a3086 --- /dev/null +++ b/plugins/ruby/ruby-memstruct.rb @@ -0,0 +1,200 @@ +module DFHack +module MemHack + +class MemStruct + attr_accessor :_memaddr + def _at(addr) ; @_memaddr = addr ; self ; end + def _get ; self ; end +end + +class Compound < MemStruct + class << self + attr_accessor :_fields + def field(name, offset) + struct = yield + @_fields ||= [] + @_fields << [name, offset, struct] + define_method(name) { struct._at(@_memaddr+offset)._get } + define_method("#{name}=") { |v| struct._at(@_memaddr+offset)._set(v) } + end + + def number(bits, signed) + Number.new(bits, signed) + end + def float + Float.new + end + def bit(shift) + BitField.new(shift, 1) + end + def bits(shift, len) + BitField.new(shift, len) + end + def pointer(tglen=nil) + Pointer.new(tglen, (yield if tglen)) + end + def static_array(len, tglen) + StaticArray.new(tglen, len, yield) + end + def static_string(len) + StaticString.new(len) + end + + def stl_vector(tglen=nil) + StlVector.new(tglen, (yield if tglen)) + end + def stl_string + StlString.new + end + def stl_bit_vector + StlBitVector.new + end + def stl_deque(tglen=nil) + StlDeque.new(tglen, (yield if tglen)) + end + + def df_flagarray + DfFlagarray.new + end + def df_array(tglen=nil) + DfArray.new(tglen, (yield if tglen)) + end + def df_linked_list + DfLinkedList.new((yield if block_given?)) + end + + def global(glob) + Global.new(glob) + end + def compound(&b) + m = Class.new(Compound) + m.instance_eval(&b) + m + end + end + def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end +end +class Number < MemStruct + attr_accessor :_bits, :_signed + def initialize(bits, signed) + @_bits = bits + @_signed = signed + end +end +class Float < MemStruct +end +class BitField < Number + attr_accessor :_shift, :_len + def initialize(shift, len) + @_shift = shift + @_len = len + super(32, false) + end + + def _get(whole=false) + v = super() + return v if whole + v = (v >> @_shift) % (1 << @_len) + if @_len == 1 + v == 0 ? false : true + else + v + end + end + + def _set(v) + if @_len == 1 + v = (v && v != 0 ? 1 : 0) + end + v = ((v % (1 << @_len)) << @_shift) + + ori = _get(true) + super(ori - (ori & (-1 % (1 << @_len)) << @_shift) + v) + end +end + +class Pointer < MemStruct + attr_accessor :_tglen, :_tg + def initialize(tglen, tg) + @_tglen = tglen + @_tg = tg + end +end +class StaticArray < MemStruct + attr_accessor :_tglen, :_length, :_tg + def initialize(tglen, length, tg) + @_tglen = tglen + @_length = length + @_tg = tg + end + def _set(a) ; a.each_with_index { |v, i| self[i] = v } ; end + alias length _length + alias size _length + def _tgat(i) + tg._at(@_memaddr + i*@_tglen) + end + def [](i) + if (i > 0) or (@_length and i < @_length) + tgat(i)._get + end + end + def []=(i, v) + if (i > 0) or (@_length and i < @_length) + tgat(i)._set + end + end +end +class StaticString < MemStruct + attr_accessor :_length + def initialize(length) + @_length = length + end +end + +class StlVector < MemStruct + attr_accessor :_tglen, :_tg + def initialize(tglen, tg) + @_tglen = tglen + @_tg = tg + end +end +class StlString < MemStruct +end +class StlBitVector < MemStruct +end +class StlDeque < MemStruct + attr_accessor :_tglen, :_tg + def initialize(tglen, tg) + @_tglen = tglen + @_tg = tg + end +end + +class DfFlagarray < MemStruct +end +class DfArray < MemStruct + attr_accessor :_tglen, :_tg + def initialize(tglen, tg) + @_tglen = tglen + @_tg = tg + end +end +class DfLinkedList < MemStruct + attr_accessor :_tg + def initialize(tg) + @_tg = tg + end +end + +class Global < MemStruct + attr_accessor :_glob + def initialize(glob) + @_glob = glob + end + def _at(addr) ; g = const_get(@_glob) ; g._at(addr) ; end +end + +end +end + +require 'ruby-autogen' From ac0d878b691bb73227f0b281a7cfbe6566813c38 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 6 Apr 2012 19:59:11 +0200 Subject: [PATCH 11/72] ruby: include memstruct in autogen --- plugins/ruby/CMakeLists.txt | 4 ++-- plugins/ruby/codegen.pl | 10 ++++++---- plugins/ruby/ruby-memstruct.rb | 7 +------ 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index 4a69c09ea..6927dbc8a 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -13,8 +13,8 @@ if(RUBY_FOUND) ) ADD_CUSTOM_COMMAND( OUTPUT ruby-autogen.rb - COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets - DEPENDS ruby-autogen.offsets + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets ${CMAKE_CURRENT_SOURCE_DIR}/ruby-memstruct.rb + DEPENDS ruby-autogen.offsets ruby-memstruct.rb ) ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb) include_directories("${dfhack_SOURCE_DIR}/depends/tthread" ${RUBY_INCLUDE_PATH}) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 29259fdc3..7afc63da7 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -346,6 +346,7 @@ my $input = $ARGV[0] || '../../library/include/df/codegen.out.xml'; # delete offsets my $output = $ARGV[1] or die "need output file"; my $offsetfile = $ARGV[2]; +my $memstruct = $ARGV[3]; if ($offsetfile) { open OF, "<$offsetfile"; @@ -379,10 +380,6 @@ for my $obj ($doc->findnodes('/ld:data-definition/ld:global-object')) { open FH, ">$output"; if ($output =~ /\.cpp$/) { - print FH "#include \"Core.h\"\n"; - print FH "#include \"Console.h\"\n"; - print FH "#include \"Export.h\"\n"; - print FH "#include \"PluginManager.h\"\n"; print FH "#include \"DataDefs.h\"\n"; print FH "#include \"df/$_.h\"\n" for @include_cpp; print FH "#include \n"; @@ -402,6 +399,11 @@ if ($output =~ /\.cpp$/) { } else { print FH "module DFHack\n"; print FH "module MemHack\n"; + if ($memstruct) { + open MH, "<$memstruct"; + print FH "$_" while(); + close MH; + } print FH "$_\n" for @lines_rb; print FH "end\n"; print FH "end\n"; diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 9b78a3086..f4afe8761 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -1,6 +1,3 @@ -module DFHack -module MemHack - class MemStruct attr_accessor :_memaddr def _at(addr) ; @_memaddr = addr ; self ; end @@ -194,7 +191,5 @@ class Global < MemStruct def _at(addr) ; g = const_get(@_glob) ; g._at(addr) ; end end -end -end -require 'ruby-autogen' + From f503bf36f4c49dab43f3dda37fc9d6f9348a89c3 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 12 Apr 2012 19:12:46 +0200 Subject: [PATCH 12/72] ruby: set everything up --- plugins/ruby/ruby-memstruct.rb | 177 ++++++++++++-- plugins/ruby/ruby.cpp | 408 ++++++++++++++++----------------- 2 files changed, 360 insertions(+), 225 deletions(-) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index f4afe8761..6ef3873cf 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -77,36 +77,65 @@ class Number < MemStruct @_bits = bits @_signed = signed end + + def _get + v = case @_bits + when 32; DFHack.memory_read_int32(@_memaddr) + when 16; DFHack.memory_read_int16(@_memaddr) + when 8; DFHack.memory_read_int8( @_memaddr) + when 64;(DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + (DFHack.memory_read_int32(@_memaddr+4) << 32) + end + v &= (1 << @_bits) - 1 if not @_signed + v + end + + def _set(v) + case @_bits + when 32; DFHack.memory_write_int32(@_memaddr, v) + when 16; DFHack.memory_write_int16(@_memaddr, v) + when 8; DFHack.memory_write_int8( @_memaddr, v) + when 64; DFHack.memory_write_int32(@_memaddr, v & 0xffffffff) ; DFHack.memory_write_int32(@memaddr+4, v>>32) + end + end end class Float < MemStruct + # _get/_set defined in ruby.cpp + def _get + DFHack.memory_read_float(@_memaddr) + end + + def _set(v) + DFHack.memory_write_float(@_memaddr, v) + end end -class BitField < Number +class BitField < MemStruct attr_accessor :_shift, :_len def initialize(shift, len) @_shift = shift @_len = len - super(32, false) + end + def _mask + (1 << @_len) - 1 end - def _get(whole=false) - v = super() - return v if whole - v = (v >> @_shift) % (1 << @_len) + def _get + v = DFHack.memory_read_int32(@_memaddr) >> @_shift if @_len == 1 - v == 0 ? false : true + ((v & 1) == 0) ? false : true else - v + v & _mask end end def _set(v) if @_len == 1 + # allow 'bit = 0' v = (v && v != 0 ? 1 : 0) end - v = ((v % (1 << @_len)) << @_shift) + v = (v & _mask) << @_shift - ori = _get(true) - super(ori - (ori & (-1 % (1 << @_len)) << @_shift) + v) + ori = DFHack.memory_read_int32(@_memaddr) & 0xffffffff + DFHack.memory_write_int32(@_memaddr, ori - (ori & ((-1 & _mask) << @_shift)) + v) end end @@ -116,6 +145,30 @@ class Pointer < MemStruct @_tglen = tglen @_tg = tg end + + def _getp + DFHack.memory_read_int32(@_memaddr) & 0xffffffff + end + def _setp(v) + DFHack.memory_write_int32(@_memaddr, v) + end + + # _getp/_setp defined in ruby.cpp, access the pointer value + def _get + addr = _getp + return if addr == 0 # XXX are there pointers with -1 as 'empty' value ? + @_tg._at(addr)._get + end + def _set(v) + addr = _getp + raise 'null pointer' if addr == 0 # TODO malloc ? + @_tg._at(addr)._set(v) + end + + # the pointer is invisible, forward all methods to the pointed object + def method_missing(*a) + _get.send(*a) + end end class StaticArray < MemStruct attr_accessor :_tglen, :_length, :_tg @@ -124,21 +177,21 @@ class StaticArray < MemStruct @_length = length @_tg = tg end - def _set(a) ; a.each_with_index { |v, i| self[i] = v } ; end + def _set(a) + a.each_with_index { |v, i| self[i] = v } + end alias length _length alias size _length def _tgat(i) - tg._at(@_memaddr + i*@_tglen) + @_tg._at(@_memaddr + i*@_tglen) if i >= 0 and i < @_length end def [](i) - if (i > 0) or (@_length and i < @_length) - tgat(i)._get - end + i += @_length if i < 0 + tgat(i)._get end def []=(i, v) - if (i > 0) or (@_length and i < @_length) - tgat(i)._set - end + i += @_length if i < 0 + tgat(i)._set(v) end end class StaticString < MemStruct @@ -146,6 +199,12 @@ class StaticString < MemStruct def initialize(length) @_length = length end + def _get + DFHack.memory_read(@_memaddr, @_length) + end + def _set(v) + DFHack.memory_write(@_memaddr, v[0, @_length]) + end end class StlVector < MemStruct @@ -154,10 +213,87 @@ class StlVector < MemStruct @_tglen = tglen @_tg = tg end + + def length + case @_tglen + when 1; DFHack.memory_vector8_length(@_memaddr) + when 2; DFHack.memory_vector16_length(@_memaddr) + else DFHack.memory_vector32_length(@_memaddr) + end + end + alias size length + + def value_at(idx) + case @_tglen + when 1; DFHack.memory_vector8_at(@_memaddr, idx) + when 2; DFHack.memory_vector16_at(@_memaddr, idx) + else DFHack.memory_vector32_at(@_memaddr, idx) + end + end + def insert_at(idx, val) + case @_tglen + when 1; DFHack.memory_vector8_insert(@_memaddr, idx, val) + when 2; DFHack.memory_vector16_insert(@_memaddr, idx, val) + else DFHack.memory_vector32_insert(@_memaddr, idx, val) + end + end + def delete_at(idx) + case @_tglen + when 1; DFHack.memory_vector8_delete(@_memaddr, idx) + when 2; DFHack.memory_vector16_delete(@_memaddr, idx) + else DFHack.memory_vector32_delete(@_memaddr, idx) + end + end + + def _set(v) + delete_at(length-1) while length > v.length # match lengthes + v.each_with_index { |e, i| self[i] = e } # patch entries + end + + def clear + delete_at(length-1) while length > 0 + end + def [](idx) + idx += length if idx < 0 + @_tg._at(value_at(idx)) if idx >= 0 and idx < length + end + def []=(idx, v) + idx += length if idx < 0 + if idx >= length + insert_at(idx, v) + elsif idx < 0 + raise 'invalid idx' + else + set_value_at(idx, v) + end + end + def <<(v) + insert_at(length, v) + self + end + def pop + l = length + if l > 0 + v = self[l-1] + delete_at(l-1) + end + v + end + def to_a + (0...length).map { |i| self[i] } + end end class StlString < MemStruct + def _get + DFHack.memory_read_stlstring(@_memaddr) + end + + def _set(v) + DFHack.memory_write_stlstring(@_memaddr, v) + end end class StlBitVector < MemStruct + # TODO end class StlDeque < MemStruct attr_accessor :_tglen, :_tg @@ -168,6 +304,7 @@ class StlDeque < MemStruct end class DfFlagarray < MemStruct + # TODO end class DfArray < MemStruct attr_accessor :_tglen, :_tg @@ -175,6 +312,8 @@ class DfArray < MemStruct @_tglen = tglen @_tg = tg end + + # TODO end class DfLinkedList < MemStruct attr_accessor :_tg diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 74755424f..7230b0d91 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -12,14 +12,16 @@ #include -using std::string; -using std::vector; using namespace DFHack; + +// DFHack stuff + + static void df_rubythread(void*); -static command_result df_rubyload(color_ostream &out, vector & parameters); -static command_result df_rubyeval(color_ostream &out, vector & parameters); +static command_result df_rubyload(color_ostream &out, std::vector & parameters); +static command_result df_rubyeval(color_ostream &out, std::vector & parameters); static void ruby_bind_dfhack(void); // inter-thread communication stuff @@ -39,7 +41,6 @@ static command_result r_result; static tthread::thread *r_thread; static int onupdate_active; -// dfhack interface DFHACK_PLUGIN("ruby") DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) @@ -122,7 +123,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) return ret; } -static command_result df_rubyload(color_ostream &out, vector & parameters) +static command_result df_rubyload(color_ostream &out, std::vector & parameters) { command_result ret; @@ -154,7 +155,7 @@ static command_result df_rubyload(color_ostream &out, vector & paramete return ret; } -static command_result df_rubyeval(color_ostream &out, vector & parameters) +static command_result df_rubyeval(color_ostream &out, std::vector & parameters) { command_result ret; @@ -195,6 +196,8 @@ static command_result df_rubyeval(color_ostream &out, vector & paramete +// ruby stuff + // ruby thread code static void dump_rb_error(void) { @@ -275,12 +278,12 @@ static void df_rubythread(void *p) -// ruby classes +// main DFHack ruby module static VALUE rb_cDFHack; -static VALUE rb_c_WrapData; -// DFHack methods +// DFHack module ruby methods, binds specific dfhack methods + // enable/disable calls to DFHack.onupdate() static VALUE rb_dfonupdateactive(VALUE self) { @@ -308,16 +311,33 @@ static VALUE rb_dfsuspend(VALUE self) return Qtrue; } -/* -static VALUE rb_dfgetversion(VALUE self) +// returns the delta to apply to dfhack xml addrs wrt actual memory addresses +// usage: real_addr = addr_from_xml + this_delta; +static VALUE rb_dfrebase_delta(void) { - return rb_str_new2(getcore().vinfo->getVersion().c_str()); + uint32_t expected_base_address; + uint32_t actual_base_address = 0; +#ifdef WIN32 + expected_base_address = 0x00400000; + actual_base_address = (uint32_t)GetModuleHandle(0); +#else + expected_base_address = 0x08048000; + FILE *f = fopen("/proc/self/maps", "r"); + char line[256]; + while (fgets(line, sizeof(line), f)) { + if (strstr(line, "libs/Dwarf_Fortress")) { + actual_base_address = strtoul(line, 0, 16); + break; + } + } +#endif + + return rb_int2inum(actual_base_address - expected_base_address); } -*/ -// TODO color_ostream proxy yadda yadda static VALUE rb_dfprint_str(VALUE self, VALUE s) { + // TODO color_ostream proxy yadda yadda //getcore().con.print("%s", rb_string_value_ptr(&s)); Core::printerr("%s", rb_string_value_ptr(&s)); return Qnil; @@ -329,22 +349,30 @@ static VALUE rb_dfprint_err(VALUE self, VALUE s) return Qnil; } -// raw memory access -// WARNING: may cause game crash ! double-check your addresses ! -static VALUE rb_dfmemread(VALUE self, VALUE addr, VALUE len) +/* TODO needs main dfhack support + this needs a custom DFHack::Plugin subclass to pass the cmdname to invoke(), to match the ruby callback +// register a ruby method as dfhack console command +// usage: DFHack.register("moo", "this commands prints moo on the console") { DFHack.puts "moo !" } +static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) { - return rb_str_new((char*)rb_num2ulong(addr), rb_num2ulong(len)); -} + commands.push_back(PluginCommand(rb_string_value_ptr(&name), + rb_string_value_ptr(&descr), + df_rubycustom)); -static VALUE rb_dfmemwrite(VALUE self, VALUE addr, VALUE raw) + return Qtrue; +} +*/ +static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) { - // no stable api for raw.length between rb1.8/rb1.9 ... - int strlen = FIX2INT(rb_funcall(raw, rb_intern("length"), 0)); + rb_raise(rb_eRuntimeError, "not implemented"); +} - memcpy((void*)rb_num2ulong(addr), rb_string_value_ptr(&raw), strlen); - return Qtrue; -} + + +// raw memory access +// used by the ruby class definitions +// WARNING: may cause game crash ! double-check your addresses ! static VALUE rb_dfmalloc(VALUE self, VALUE len) { @@ -357,214 +385,170 @@ static VALUE rb_dffree(VALUE self, VALUE ptr) return Qtrue; } -// raw c++ wrappers -// return the nth element of a vector -static VALUE rb_dfvectorat(VALUE self, VALUE vect_addr, VALUE idx) + +// memory reading (buffer) +static VALUE rb_dfmemory_read(VALUE self, VALUE addr, VALUE len) { - vector *v = (vector*)rb_num2ulong(vect_addr); - return rb_uint2inum(v->at(FIX2INT(idx))); + return rb_str_new((char*)rb_num2ulong(addr), rb_num2ulong(len)); } - -// return a c++ string as a ruby string (nul-terminated) -static VALUE rb_dfreadstring(VALUE self, VALUE str_addr) +static VALUE rb_dfmemory_read_stlstring(VALUE self, VALUE addr) { - string *s = (string*)rb_num2ulong(str_addr); - return rb_str_new2(s->c_str()); + std::string *s = (std::string*)rb_num2ulong(addr); + return rb_str_new(s->c_str(), s->length()); } - - - -/* XXX this needs a custom DFHack::Plugin subclass to pass the cmdname to invoke(), to match the ruby callback -// register a ruby method as dfhack console command -// usage: DFHack.register("moo", "this commands prints moo on the console") { DFHack.puts "moo !" } -static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) +// memory reading (integers/floats) +static VALUE rb_dfmemory_read_int8(VALUE self, VALUE addr) { - commands.push_back(PluginCommand(rb_string_value_ptr(&name), - rb_string_value_ptr(&descr), - df_rubycustom)); - - return Qtrue; + return rb_int2inum(*(char*)rb_num2ulong(addr)); } -*/ -static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) +static VALUE rb_dfmemory_read_int16(VALUE self, VALUE addr) { - rb_raise(rb_eRuntimeError, "not implemented"); + return rb_int2inum(*(short*)rb_num2ulong(addr)); } - - -// return the address of the struct in DF memory (for raw memread/write) -static VALUE rb_memaddr(VALUE self) +static VALUE rb_dfmemory_read_int32(VALUE self, VALUE addr) { - void *data; - Data_Get_Struct(self, void, data); - - return rb_uint2inum((uint32_t)data); + return rb_int2inum(*(int*)rb_num2ulong(addr)); } +static VALUE rb_dfmemory_read_float(VALUE self, VALUE addr) +{ + return rb_float_new(*(float*)rb_num2ulong(addr)); +} +// memory writing (buffer) +static VALUE rb_dfmemory_write(VALUE self, VALUE addr, VALUE raw) +{ + // no stable api for raw.length between rb1.8/rb1.9 ... + int strlen = FIX2INT(rb_funcall(raw, rb_intern("length"), 0)); -// BEGIN GENERATED SECTION - -// begin generated T_cursor binding -static VALUE rb_c_T_cursor; + memcpy((void*)rb_num2ulong(addr), rb_string_value_ptr(&raw), strlen); -static VALUE rb_m_T_cursor_x(VALUE self) { - struct df::global::T_cursor *var; - Data_Get_Struct(self, struct df::global::T_cursor, var); - return rb_uint2inum(var->x); + return Qtrue; } -static VALUE rb_m_T_cursor_x_set(VALUE self, VALUE val) { - struct df::global::T_cursor *var; - Data_Get_Struct(self, struct df::global::T_cursor, var); - var->x = rb_num2ulong(val); +static VALUE rb_dfmemory_write_stlstring(VALUE self, VALUE addr, VALUE val) +{ + std::string *s = (std::string*)rb_num2ulong(addr); + int strlen = FIX2INT(rb_funcall(val, rb_intern("length"), 0)); + s->assign(rb_string_value_ptr(&val), strlen); return Qtrue; } -static VALUE rb_m_T_cursor_y(VALUE self) { - struct df::global::T_cursor *var; - Data_Get_Struct(self, struct df::global::T_cursor, var); - return rb_uint2inum(var->y); -} -static VALUE rb_m_T_cursor_y_set(VALUE self, VALUE val) { - struct df::global::T_cursor *var; - Data_Get_Struct(self, struct df::global::T_cursor, var); - var->y = rb_num2ulong(val); +// memory writing (integers/floats) +static VALUE rb_dfmemory_write_int8(VALUE self, VALUE addr, VALUE val) +{ + *(char*)rb_num2ulong(addr) = rb_num2ulong(val); return Qtrue; } - -static VALUE rb_m_T_cursor_z(VALUE self) { - struct df::global::T_cursor *var; - Data_Get_Struct(self, struct df::global::T_cursor, var); - return rb_uint2inum(var->z); +static VALUE rb_dfmemory_write_int16(VALUE self, VALUE addr, VALUE val) +{ + *(short*)rb_num2ulong(addr) = rb_num2ulong(val); + return Qtrue; } -static VALUE rb_m_T_cursor_z_set(VALUE self, VALUE val) { - struct df::global::T_cursor *var; - Data_Get_Struct(self, struct df::global::T_cursor, var); - var->z = rb_num2ulong(val); +static VALUE rb_dfmemory_write_int32(VALUE self, VALUE addr, VALUE val) +{ + *(int*)rb_num2ulong(addr) = rb_num2ulong(val); return Qtrue; } -// link methods to the class -static void ruby_bind_T_cursor(void) { - // create a class, child of WrapData, in module DFHack - rb_c_T_cursor = rb_define_class_under(rb_cDFHack, "T_cursor", rb_c_WrapData); - - // reader for 'x' (0 = no arg) - rb_define_method(rb_c_T_cursor, "x", RUBY_METHOD_FUNC(rb_m_T_cursor_x), 0); - // writer for 'x' (1 arg) - rb_define_method(rb_c_T_cursor, "x=", RUBY_METHOD_FUNC(rb_m_T_cursor_x_set), 1); - rb_define_method(rb_c_T_cursor, "y", RUBY_METHOD_FUNC(rb_m_T_cursor_y), 0); - rb_define_method(rb_c_T_cursor, "y=", RUBY_METHOD_FUNC(rb_m_T_cursor_y_set), 1); - rb_define_method(rb_c_T_cursor, "z", RUBY_METHOD_FUNC(rb_m_T_cursor_z), 0); - rb_define_method(rb_c_T_cursor, "z=", RUBY_METHOD_FUNC(rb_m_T_cursor_z_set), 1); +static VALUE rb_dfmemory_write_float(VALUE self, VALUE addr, VALUE val) +{ + *(float*)rb_num2ulong(addr) = rb_num2dbl(val); + return Qtrue; } -// create an instance of T_cursor from global::cursor -// this method is linked to DFHack.cursor in ruby_bind_dfhack() -static VALUE rb_global_cursor(VALUE self) { - return Data_Wrap_Struct(rb_c_T_cursor, 0, 0, df::global::cursor); +// vector access +// vector +static VALUE rb_dfmemory_vec8_length(VALUE self, VALUE addr) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + return rb_uint2inum(v->size()); } - - -// begin generated unit binding -static VALUE rb_c_unit; - -static VALUE rb_m_unit_id(VALUE self) { - struct df::unit *var; - Data_Get_Struct(self, struct df::unit, var); - - return rb_uint2inum(var->id); +static VALUE rb_dfmemory_vec8_at(VALUE self, VALUE addr, VALUE idx) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + return rb_uint2inum(v->at(FIX2INT(idx))); } -static VALUE rb_m_unit_id_set(VALUE self, VALUE val) { - struct df::unit *var; - Data_Get_Struct(self, struct df::unit, var); - var->id = rb_num2ulong(val); +static VALUE rb_dfmemory_vec8_set(VALUE self, VALUE addr, VALUE idx, VALUE val) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + v->at(FIX2INT(idx)) = rb_num2ulong(val); return Qtrue; } - -static void ruby_bind_unit(void) { - // ruby class name must begin with uppercase - rb_c_unit = rb_define_class_under(rb_cDFHack, "Unit", rb_c_WrapData); - - rb_define_method(rb_c_unit, "id", RUBY_METHOD_FUNC(rb_m_unit_id), 0); - rb_define_method(rb_c_unit, "id=", RUBY_METHOD_FUNC(rb_m_unit_id_set), 1); +static VALUE rb_dfmemory_vec8_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); + return Qtrue; } - - -// begin generated world binding -static VALUE rb_c_world; -static VALUE rb_c_world_T_units; - -static VALUE rb_m_world_T_units_all(VALUE self) { - struct df::world::T_units *var; - Data_Get_Struct(self, struct df::world::T_units, var); - - // read a vector - VALUE ret = rb_ary_new(); - for (unsigned i=0 ; iall.size() ; ++i) - rb_ary_push(ret, Data_Wrap_Struct(rb_c_unit, 0, 0, var->all[i])); - - return ret; +static VALUE rb_dfmemory_vec8_delete(VALUE self, VALUE addr, VALUE idx) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + v->erase(v->begin()+FIX2INT(idx)); + return Qtrue; } -static VALUE rb_m_world_units(VALUE self) { - struct df::world *var; - Data_Get_Struct(self, struct df::world, var); - return Data_Wrap_Struct(rb_c_world_T_units, 0, 0, &var->units); +// vector +static VALUE rb_dfmemory_vec16_length(VALUE self, VALUE addr) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + return rb_uint2inum(v->size()); } - -static void ruby_bind_world(void) { - rb_c_world = rb_define_class_under(rb_cDFHack, "World", rb_c_WrapData); - rb_c_world_T_units = rb_define_class_under(rb_c_world, "T_units", rb_c_WrapData); - - rb_define_method(rb_c_world, "units", RUBY_METHOD_FUNC(rb_m_world_units), 0); - rb_define_method(rb_c_world_T_units, "all", RUBY_METHOD_FUNC(rb_m_world_T_units_all), 0); +static VALUE rb_dfmemory_vec16_at(VALUE self, VALUE addr, VALUE idx) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + return rb_uint2inum(v->at(FIX2INT(idx))); } - -static VALUE rb_global_world(VALUE self) { - return Data_Wrap_Struct(rb_c_world, 0, 0, df::global::world); +static VALUE rb_dfmemory_vec16_set(VALUE self, VALUE addr, VALUE idx, VALUE val) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + v->at(FIX2INT(idx)) = rb_num2ulong(val); + return Qtrue; } - -/* -static VALUE rb_dfcreatures(VALUE self) +static VALUE rb_dfmemory_vec16_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) { - OffsetGroup *ogc = getcore().vinfo->getGroup("Creatures"); - vector *v = (vector*)ogc->getAddress("vector"); - - VALUE ret = rb_ary_new(); - for (unsigned i=0 ; isize() ; ++i) - rb_ary_push(ret, Data_Wrap_Struct(rb_cCreature, 0, 0, v->at(i))); - - return ret; + std::vector *v = (std::vector*)rb_num2ulong(addr); + v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); + return Qtrue; } - -static VALUE rb_getlaborname(VALUE self, VALUE idx) +static VALUE rb_dfmemory_vec16_delete(VALUE self, VALUE addr, VALUE idx) { - return rb_str_new2(getcore().vinfo->getLabor(FIX2INT(idx)).c_str()); + std::vector *v = (std::vector*)rb_num2ulong(addr); + v->erase(v->begin()+FIX2INT(idx)); + return Qtrue; } -static VALUE rb_getskillname(VALUE self, VALUE idx) +// vector +static VALUE rb_dfmemory_vec32_length(VALUE self, VALUE addr) { - return rb_str_new2(getcore().vinfo->getSkill(FIX2INT(idx)).c_str()); + std::vector *v = (std::vector*)rb_num2ulong(addr); + return rb_uint2inum(v->size()); } - -static VALUE rb_mapblock(VALUE self, VALUE x, VALUE y, VALUE z) +static VALUE rb_dfmemory_vec32_at(VALUE self, VALUE addr, VALUE idx) { - Maps *map; - Data_Get_Struct(self, Maps, map); - df_block *block; - - block = map->getBlock(FIX2INT(x)/16, FIX2INT(y)/16, FIX2INT(z)); - if (!block) - return Qnil; - - return Data_Wrap_Struct(rb_cMapBlock, 0, 0, block); + std::vector *v = (std::vector*)rb_num2ulong(addr); + return rb_uint2inum(v->at(FIX2INT(idx))); +} +static VALUE rb_dfmemory_vec32_set(VALUE self, VALUE addr, VALUE idx, VALUE val) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + v->at(FIX2INT(idx)) = rb_num2ulong(val); + return Qtrue; +} +static VALUE rb_dfmemory_vec32_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); + return Qtrue; +} +static VALUE rb_dfmemory_vec32_delete(VALUE self, VALUE addr, VALUE idx) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + v->erase(v->begin()+FIX2INT(idx)); + return Qtrue; } -*/ - @@ -577,30 +561,42 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "onupdate_active=", RUBY_METHOD_FUNC(rb_dfonupdateactiveset), 1); rb_define_singleton_method(rb_cDFHack, "resume", RUBY_METHOD_FUNC(rb_dfresume), 0); rb_define_singleton_method(rb_cDFHack, "do_suspend", RUBY_METHOD_FUNC(rb_dfsuspend), 0); - rb_define_singleton_method(rb_cDFHack, "resume", RUBY_METHOD_FUNC(rb_dfresume), 0); - //rb_define_singleton_method(rb_cDFHack, "version", RUBY_METHOD_FUNC(rb_dfgetversion), 0); + rb_define_singleton_method(rb_cDFHack, "register_dfcommand", RUBY_METHOD_FUNC(rb_dfregister), 2); rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1); rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1); - rb_define_singleton_method(rb_cDFHack, "memread", RUBY_METHOD_FUNC(rb_dfmemread), 2); - rb_define_singleton_method(rb_cDFHack, "memwrite", RUBY_METHOD_FUNC(rb_dfmemwrite), 2); rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1); rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1); - rb_define_singleton_method(rb_cDFHack, "vectorat", RUBY_METHOD_FUNC(rb_dfvectorat), 2); - rb_define_singleton_method(rb_cDFHack, "readstring", RUBY_METHOD_FUNC(rb_dfreadstring), 1); - rb_define_singleton_method(rb_cDFHack, "register_dfcommand", RUBY_METHOD_FUNC(rb_dfregister), 2); - - // accessors for dfhack globals - rb_define_singleton_method(rb_cDFHack, "cursor", RUBY_METHOD_FUNC(rb_global_cursor), 0); - rb_define_singleton_method(rb_cDFHack, "world", RUBY_METHOD_FUNC(rb_global_world), 0); - - // parent class for all wrapped objects - rb_c_WrapData = rb_define_class_under(rb_cDFHack, "WrapData", rb_cObject); - rb_define_method(rb_c_WrapData, "memaddr", RUBY_METHOD_FUNC(rb_memaddr), 0); - - // call generated bindings - ruby_bind_T_cursor(); - ruby_bind_unit(); - ruby_bind_world(); + rb_define_const(rb_cDFHack, "REBASE_DELTA", rb_dfrebase_delta()); + + rb_define_singleton_method(rb_cDFHack, "memory_read", RUBY_METHOD_FUNC(rb_dfmemory_read), 2); + rb_define_singleton_method(rb_cDFHack, "memory_read_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_read_stlstring), 1); + rb_define_singleton_method(rb_cDFHack, "memory_read_int8", RUBY_METHOD_FUNC(rb_dfmemory_read_int8), 1); + rb_define_singleton_method(rb_cDFHack, "memory_read_int16", RUBY_METHOD_FUNC(rb_dfmemory_read_int16), 1); + rb_define_singleton_method(rb_cDFHack, "memory_read_int32", RUBY_METHOD_FUNC(rb_dfmemory_read_int32), 1); + rb_define_singleton_method(rb_cDFHack, "memory_read_float", RUBY_METHOD_FUNC(rb_dfmemory_read_float), 1); + + rb_define_singleton_method(rb_cDFHack, "memory_write", RUBY_METHOD_FUNC(rb_dfmemory_write), 2); + rb_define_singleton_method(rb_cDFHack, "memory_write_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_write_stlstring), 2); + rb_define_singleton_method(rb_cDFHack, "memory_write_int8", RUBY_METHOD_FUNC(rb_dfmemory_write_int8), 2); + rb_define_singleton_method(rb_cDFHack, "memory_write_int16", RUBY_METHOD_FUNC(rb_dfmemory_write_int16), 2); + rb_define_singleton_method(rb_cDFHack, "memory_write_int32", RUBY_METHOD_FUNC(rb_dfmemory_write_int32), 2); + rb_define_singleton_method(rb_cDFHack, "memory_write_float", RUBY_METHOD_FUNC(rb_dfmemory_write_float), 2); + + rb_define_singleton_method(rb_cDFHack, "memory_vector8_length", RUBY_METHOD_FUNC(rb_dfmemory_vec8_length), 1); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_at", RUBY_METHOD_FUNC(rb_dfmemory_vec8_at), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_set", RUBY_METHOD_FUNC(rb_dfmemory_vec8_set), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insert), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec8_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_length", RUBY_METHOD_FUNC(rb_dfmemory_vec16_length), 1); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_at", RUBY_METHOD_FUNC(rb_dfmemory_vec16_at), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_set", RUBY_METHOD_FUNC(rb_dfmemory_vec16_set), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insert), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec16_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_length", RUBY_METHOD_FUNC(rb_dfmemory_vec32_length), 1); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_at", RUBY_METHOD_FUNC(rb_dfmemory_vec32_at), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_set", RUBY_METHOD_FUNC(rb_dfmemory_vec32_set), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insert), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec32_delete), 2); // load the default ruby-level definitions int state=0; From 3044da58871c753783657266ad6580ec2739303a Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 13 Apr 2012 16:17:56 +0200 Subject: [PATCH 13/72] ruby: add global-objects, add bitfield._whole, make StlVector enumerable --- plugins/ruby/codegen.pl | 65 ++++++++++++++++++++++++---- plugins/ruby/ruby-memstruct.rb | 79 +++++++++++++++++++++------------- plugins/ruby/ruby.cpp | 8 ++++ plugins/ruby/ruby.rb | 5 ++- 4 files changed, 116 insertions(+), 41 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 7afc63da7..1894e883d 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -56,7 +56,7 @@ sub render_global_enum { indent_rb { render_enum_fields($type); }; - push @lines_rb, "end"; + push @lines_rb, "end\n"; } sub render_enum_fields { my ($type) = @_; @@ -84,11 +84,17 @@ sub render_global_bitfield { indent_rb { render_bitfield_fields($type); }; - push @lines_rb, "end"; + push @lines_rb, "end\n"; } sub render_bitfield_fields { my ($type) = @_; + push @lines_rb, "field(:_whole, 0) {"; + indent_rb { + render_item_number($type, ''); + }; + push @lines_rb, "}"; + my $shift = 0; for my $field ($type->findnodes('child::ld:field')) { my $count = $field->getAttribute('count') || 1; @@ -134,7 +140,7 @@ sub render_global_class { indent_rb { render_struct_fields($type, "(*$cppvar)"); }; - push @lines_rb, "end"; + push @lines_rb, "end\n"; } sub render_struct_fields { my ($type, $cppvar) = @_; @@ -153,6 +159,48 @@ sub render_struct_fields { } } +sub render_global_objects { + my (@objects) = @_; + my @global_objects; + + my $sname = 'global_objects'; + my $rbname = rb_ucase($sname); + + %seen_enum_name = (); + + push @lines_cpp, "}" if @include_cpp; + push @lines_cpp, "void cpp_$sname(FILE *fout) {"; + push @include_cpp, $sname; + + push @lines_rb, "class $rbname < Compound"; + indent_rb { + for my $obj (@objects) { + my $oname = $obj->getAttribute('name'); + my $addr = "DFHack.get_global_address('$oname')"; + push @lines_rb, "field(:$oname, $addr) {"; + my $item = $obj->findnodes('child::ld:item')->[0]; + indent_rb { + render_item($item, "(*df::global::$oname)"); + }; + push @lines_rb, "}"; + + push @global_objects, $oname; + } + }; + push @lines_rb, "end"; + + push @lines_rb, "module ::DFHack"; + indent_rb { + push @lines_rb, "Global = GlobalObjects.new._at(0)"; + for my $obj (@global_objects) { + push @lines_rb, "def self.$obj ; Global.$obj ; end"; + push @lines_rb, "def self.$obj=(v) ; Global.$obj = v ; end"; + } + }; + push @lines_rb, "end"; +} + + sub render_item { my ($item, $cppvar) = @_; return if (!$item); @@ -180,7 +228,7 @@ sub render_item_number { my ($item, $cppvar) = @_; my $subtype = $item->getAttribute('ld:subtype'); - $subtype = $item->getAttribute('base-type') if ($subtype eq 'enum'); + $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'enum' or $subtype eq 'bitfield'); $subtype = 'int32_t' if (!$subtype); if ($subtype eq 'int64_t') { @@ -298,7 +346,6 @@ sub render_item_bytes { } } - sub get_offset { my ($cppvar, $fname) = @_; @@ -373,10 +420,9 @@ for my $name (sort { $a cmp $b } keys %global_types) { } } -for my $obj ($doc->findnodes('/ld:data-definition/ld:global-object')) { - my $name = $obj->getAttribute('name'); - # TODO -} + +render_global_objects($doc->findnodes('/ld:data-definition/ld:global-object')); + open FH, ">$output"; if ($output =~ /\.cpp$/) { @@ -396,6 +442,7 @@ if ($output =~ /\.cpp$/) { print FH " fclose(fout);\n"; print FH " return 0;\n"; print FH "}\n"; + } else { print FH "module DFHack\n"; print FH "module MemHack\n"; diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 6ef3873cf..6f5702832 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -38,7 +38,12 @@ class Compound < MemStruct end def stl_vector(tglen=nil) - StlVector.new(tglen, (yield if tglen)) + tg = yield if tglen + case tglen + when 1; StlVector8.new(tg) + when 2; StlVector16.new(tg) + else StlVector32.new(tg) + end end def stl_string StlString.new @@ -66,7 +71,7 @@ class Compound < MemStruct def compound(&b) m = Class.new(Compound) m.instance_eval(&b) - m + m.new end end def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end @@ -193,6 +198,9 @@ class StaticArray < MemStruct i += @_length if i < 0 tgat(i)._set(v) end + + include Enumerable + def each; (0...length).each { |i| yield self[i] }; end end class StaticString < MemStruct attr_accessor :_length @@ -207,42 +215,24 @@ class StaticString < MemStruct end end -class StlVector < MemStruct - attr_accessor :_tglen, :_tg - def initialize(tglen, tg) - @_tglen = tglen +class StlVector32 < MemStruct + attr_accessor :_tg + def initialize(tg) @_tg = tg end def length - case @_tglen - when 1; DFHack.memory_vector8_length(@_memaddr) - when 2; DFHack.memory_vector16_length(@_memaddr) - else DFHack.memory_vector32_length(@_memaddr) - end + DFHack.memory_vector32_length(@_memaddr) end alias size length - def value_at(idx) - case @_tglen - when 1; DFHack.memory_vector8_at(@_memaddr, idx) - when 2; DFHack.memory_vector16_at(@_memaddr, idx) - else DFHack.memory_vector32_at(@_memaddr, idx) - end + DFHack.memory_vector32_at(@_memaddr, idx) end def insert_at(idx, val) - case @_tglen - when 1; DFHack.memory_vector8_insert(@_memaddr, idx, val) - when 2; DFHack.memory_vector16_insert(@_memaddr, idx, val) - else DFHack.memory_vector32_insert(@_memaddr, idx, val) - end + DFHack.memory_vector32_insert(@_memaddr, idx, val) end def delete_at(idx) - case @_tglen - when 1; DFHack.memory_vector8_delete(@_memaddr, idx) - when 2; DFHack.memory_vector16_delete(@_memaddr, idx) - else DFHack.memory_vector32_delete(@_memaddr, idx) - end + DFHack.memory_vector32_delete(@_memaddr, idx) end def _set(v) @@ -279,8 +269,37 @@ class StlVector < MemStruct end v end - def to_a - (0...length).map { |i| self[i] } + include Enumerable + def each; (0...length).each { |i| yield self[i] }; end +end +class StlVector16 < StlVector32 + def length + DFHack.memory_vector16_length(@_memaddr) + end + alias size length + def value_at(idx) + DFHack.memory_vector16_at(@_memaddr, idx) + end + def insert_at(idx, val) + DFHack.memory_vector16_insert(@_memaddr, idx, val) + end + def delete_at(idx) + DFHack.memory_vector16_delete(@_memaddr, idx) + end +end +class StlVector8 < StlVector32 + def length + DFHack.memory_vector8_length(@_memaddr) + end + alias size length + def value_at(idx) + DFHack.memory_vector8_at(@_memaddr, idx) + end + def insert_at(idx, val) + DFHack.memory_vector8_insert(@_memaddr, idx, val) + end + def delete_at(idx) + DFHack.memory_vector8_delete(@_memaddr, idx) end end class StlString < MemStruct @@ -327,7 +346,7 @@ class Global < MemStruct def initialize(glob) @_glob = glob end - def _at(addr) ; g = const_get(@_glob) ; g._at(addr) ; end + def _at(addr) ; g = DFHack::MemHack.const_get(@_glob) ; g.new._at(addr) ; end end diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 7230b0d91..8bf42be04 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -3,6 +3,7 @@ #include "Console.h" #include "Export.h" #include "PluginManager.h" +#include "VersionInfo.h" #include "DataDefs.h" #include "df/world.h" @@ -367,6 +368,12 @@ static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) rb_raise(rb_eRuntimeError, "not implemented"); } +static VALUE rb_dfget_global_address(VALUE self, VALUE name) +{ + uint32_t addr = Core::getInstance().vinfo->getAddress(rb_string_value_ptr(&name)); + return rb_uint2inum(addr); +} + @@ -561,6 +568,7 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "onupdate_active=", RUBY_METHOD_FUNC(rb_dfonupdateactiveset), 1); rb_define_singleton_method(rb_cDFHack, "resume", RUBY_METHOD_FUNC(rb_dfresume), 0); rb_define_singleton_method(rb_cDFHack, "do_suspend", RUBY_METHOD_FUNC(rb_dfsuspend), 0); + rb_define_singleton_method(rb_cDFHack, "get_global_address", RUBY_METHOD_FUNC(rb_dfget_global_address), 1); rb_define_singleton_method(rb_cDFHack, "register_dfcommand", RUBY_METHOD_FUNC(rb_dfregister), 2); rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1); rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1); diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index dcd6ff8b6..3decef958 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -1,3 +1,5 @@ +require 'hack/ruby-autogen' + module DFHack class << self def suspend @@ -29,8 +31,7 @@ module DFHack puts "starting" suspend { - c = cursor - puts "cursor pos: #{c.x} #{c.y} #{c.z}" + puts "cursor pos: #{cursor.x} #{cursor.y} #{cursor.z}" puts "unit[0] id: #{world.units.all[0].id}" } From bf4bd1f034d9d3f1a621d299d819278091338541 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 13 Apr 2012 17:19:53 +0200 Subject: [PATCH 14/72] ruby: fix vectors of pointers, fix global enum fields --- plugins/ruby/codegen.pl | 9 +++++++-- plugins/ruby/ruby-memstruct.rb | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 1894e883d..408121cf5 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -219,9 +219,14 @@ sub render_item_global { my ($item, $cppvar) = @_; my $typename = $item->getAttribute('type-name'); - my $rbname = rb_ucase($typename); + my $subtype = $item->getAttribute('ld:subtype'); - push @lines_rb, "global :$rbname"; + if ($subtype and $subtype eq 'enum') { + render_item_number($item, $cppvar); + } else { + my $rbname = rb_ucase($typename); + push @lines_rb, "global :$rbname"; + } } sub render_item_number { diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 6f5702832..5a759add8 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -39,6 +39,7 @@ class Compound < MemStruct def stl_vector(tglen=nil) tg = yield if tglen + tg = tg._tg if tg.kind_of?(Pointer) # Vector.at(4) already dereferences case tglen when 1; StlVector8.new(tg) when 2; StlVector16.new(tg) From f85810c1e22d8b7773ec027384b9a2f3795e48c6 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 13 Apr 2012 19:31:12 +0200 Subject: [PATCH 15/72] ruby: move enum constants up one level, allow pointer arithmetic (world.map.block_index), fix StaticArray, add map test --- plugins/ruby/codegen.pl | 3 ++- plugins/ruby/ruby-memstruct.rb | 25 +++++++++++++------------ plugins/ruby/ruby.rb | 7 +++++++ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 408121cf5..effcb9327 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -51,7 +51,8 @@ sub render_global_enum { my ($name, $type) = @_; my $rbname = rb_ucase($name); - push @lines_rb, "class $rbname"; + # store constants in DFHack::EnumName and not in DFHack::MemHack::EnumName + push @lines_rb, "class ::DFHack::$rbname"; %seen_enum_name = (); indent_rb { render_enum_fields($type); diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 5a759add8..e87731bf8 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -152,28 +152,29 @@ class Pointer < MemStruct @_tg = tg end - def _getp - DFHack.memory_read_int32(@_memaddr) & 0xffffffff + def _getp(i=0) + delta = (i != 0 ? i*@_tglen : 0) + (DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + delta end def _setp(v) DFHack.memory_write_int32(@_memaddr, v) end + def _get ; self ; end - # _getp/_setp defined in ruby.cpp, access the pointer value - def _get - addr = _getp - return if addr == 0 # XXX are there pointers with -1 as 'empty' value ? + def [](i) + addr = _getp(i) + return if addr == 0 @_tg._at(addr)._get end - def _set(v) - addr = _getp - raise 'null pointer' if addr == 0 # TODO malloc ? + def []=(i, v) + addr = _getp(i) + raise 'null pointer' if addr == 0 @_tg._at(addr)._set(v) end # the pointer is invisible, forward all methods to the pointed object def method_missing(*a) - _get.send(*a) + self[0].send(*a) end end class StaticArray < MemStruct @@ -193,11 +194,11 @@ class StaticArray < MemStruct end def [](i) i += @_length if i < 0 - tgat(i)._get + _tgat(i)._get end def []=(i, v) i += @_length if i < 0 - tgat(i)._set(v) + _tgat(i)._set(v) end include Enumerable diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 3decef958..13567c9ec 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -19,12 +19,14 @@ module DFHack a.flatten.each { |l| print_str(l.to_s.chomp + "\n") } + nil end def puts_err(*a) a.flatten.each { |l| print_err(l.to_s.chomp + "\n") } + nil end def test @@ -34,6 +36,11 @@ module DFHack puts "cursor pos: #{cursor.x} #{cursor.y} #{cursor.z}" puts "unit[0] id: #{world.units.all[0].id}" + + if cursor.x >= 0 + world.map.block_index[cursor.x/16][cursor.y/16][cursor.z].designation[cursor.x%16][cursor.y%16].dig = TileDigDesignation::Default + puts "dug cursor tile" + end } puts "done" From 3339fdfea85e6e0af05bef02330183573fcd3a62 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 17 Apr 2012 22:42:23 +0200 Subject: [PATCH 16/72] ruby: fix anon unions, add some find_* helpers in ruby.rb --- plugins/ruby/codegen.pl | 3 ++ plugins/ruby/ruby.cpp | 16 +++++++++- plugins/ruby/ruby.rb | 65 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index effcb9327..a13f0948c 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -149,6 +149,9 @@ sub render_struct_fields { for my $field ($type->findnodes('child::ld:field')) { my $name = $field->getAttribute('name'); $name = $field->getAttribute('ld:anon-name') if (!$name); + if (!$name and $field->getAttribute('ld:anon-compound')) { + render_struct_fields($field, $cppvar); + } next if (!$name); my $offset = get_offset($cppvar, $name); diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 8bf42be04..06f5654ad 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -273,7 +273,7 @@ static void df_rubythread(void *p) r_result = CR_OK; r_type = RB_IDLE; m_irun->unlock(); - tthread::this_thread::yield(); + tthread::this_thread::yield(); } } @@ -374,6 +374,18 @@ static VALUE rb_dfget_global_address(VALUE self, VALUE name) return rb_uint2inum(addr); } +static VALUE rb_dfget_vtable(VALUE self, VALUE name) +{ + uint32_t addr = (uint32_t)Core::getInstance().vinfo->getVTable(rb_string_value_ptr(&name)); + return rb_uint2inum(addr); +} + +static VALUE rb_dfget_rtti_classname(VALUE self, VALUE objptr) +{ + void **ptr = (void**)rb_num2ulong(objptr); + return rb_str_new2(typeid(*ptr).name()); +} + @@ -569,6 +581,8 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "resume", RUBY_METHOD_FUNC(rb_dfresume), 0); rb_define_singleton_method(rb_cDFHack, "do_suspend", RUBY_METHOD_FUNC(rb_dfsuspend), 0); rb_define_singleton_method(rb_cDFHack, "get_global_address", RUBY_METHOD_FUNC(rb_dfget_global_address), 1); + rb_define_singleton_method(rb_cDFHack, "get_vtable", RUBY_METHOD_FUNC(rb_dfget_vtable), 1); + rb_define_singleton_method(rb_cDFHack, "get_rtti_classname", RUBY_METHOD_FUNC(rb_dfget_rtti_classname), 1); rb_define_singleton_method(rb_cDFHack, "register_dfcommand", RUBY_METHOD_FUNC(rb_dfregister), 2); rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1); rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1); diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 13567c9ec..a75404365 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -29,16 +29,75 @@ module DFHack nil end + # return an Unit + # with no arg, return currently selected unit in df UI (v or k menu) + # with numeric arg, search unit by unit.id + # with an argument that respond to x/y/z (eg cursor), find first unit at this position + def find_unit(what=nil) + if what == nil + case ui.main.mode + when UiSidebarMode::ViewUnits + # nobody selected => idx == 0 + v = world.units.other[0][ui_selected_unit] + v if v and v.z == cursor.z + when UiSidebarMode::LookAround + k = ui_look_list.items[ui_look_cursor] + k.unit if k.type == MemHack::UiLookList::Unit + end + elsif what.kind_of?(Integer) + world.units.all.find { |u| u.id == what } + elsif what.respond_to?(:x) or what.respond_to?(:pos) + what = what.pos if what.respond_to?(:pos) + x, y, z = what.x, what.y, what.z + world.units.all.find { |u| u.pos.x == x and u.pos.y == y and u.pos.z == z } + else + raise "what what?" + end + end + + # return an Item + # arg similar to find_unit + def find_item(what=nil) + if what == nil + case ui.main.mode + when UiSidebarMode::LookAround + k = ui_look_list.items[ui_look_cursor] + k.item if k.type == MemHack::UiLookList::Item + end + elsif what.kind_of?(Integer) + world.items.all.find { |i| i.id == what } + elsif what.respond_to?(:x) or what.respond_to?(:pos) + what = what.pos if what.respond_to?(:pos) + x, y, z = what.x, what.y, what.z + world.items.all.find { |i| i.pos.x == x and i.pos.y == y and i.pos.z == z } + else + raise "what what?" + end + end + + # return a map block + # can use find_map_block(cursor) or anything that respond to x/y/z + def find_map_block(x=cursor, y=nil, z=nil) + x = x.pos if x.respond_to?(:pos) + x, y, z = x.x, x.y, x.z if x.respond_to?(:x) + if x >= 0 and x < world.map.x_count and y >= 0 and y < world.map.y_count and z >= 0 and z < world.map.z_count + world.map.block_index[x/16][y/16][z] + end + end + def test puts "starting" suspend { puts "cursor pos: #{cursor.x} #{cursor.y} #{cursor.z}" - puts "unit[0] id: #{world.units.all[0].id}" + if u = find_unit + puts "selected unit id: #{u.id}" + end - if cursor.x >= 0 - world.map.block_index[cursor.x/16][cursor.y/16][cursor.z].designation[cursor.x%16][cursor.y%16].dig = TileDigDesignation::Default + if b = find_map_block + b.designation[cursor.x%16][cursor.y%16].dig = TileDigDesignation::Default + b.flags.designated = true puts "dug cursor tile" end } From 3ad3d1af46d4cb0ff53c385c4ece4c166c631c25 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 18 Apr 2012 18:46:33 +0200 Subject: [PATCH 17/72] ruby: use rtti info to cast Item/etc subclasses --- plugins/ruby/codegen.pl | 15 +++++++++++++++ plugins/ruby/ruby-memstruct.rb | 32 +++++++++++++++++++++++++++++++- plugins/ruby/ruby.cpp | 28 ++++++++++++++++++++++++---- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index a13f0948c..a406cb758 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -128,6 +128,20 @@ sub render_global_class { $seen_class{$name}++; %seen_enum_name = (); + my $rtti_name = $type->getAttribute('original-name') || + $type->getAttribute('type-name'); + + my $has_rtti = $parent; + if (!$parent and $type->getAttribute('ld:meta') eq 'class-type') { + for my $anytypename (keys %global_types) { + my $anytype = $global_types{$anytypename}; + if ($anytype->getAttribute('ld:meta') eq 'class-type') { + my $anyparent = $anytype->getAttribute('inherits-from'); + $has_rtti = 1 if ($anyparent and $anyparent eq $name); + } + } + } + my $rbparent = ($parent ? rb_ucase($parent) : 'Compound'); my $cppvar = "v_$cpp_var_counter"; @@ -139,6 +153,7 @@ sub render_global_class { push @lines_rb, "class $rbname < $rbparent"; indent_rb { + push @lines_rb, "rtti_classname '$rtti_name'" if $has_rtti; render_struct_fields($type, "(*$cppvar)"); }; push @lines_rb, "end\n"; diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index e87731bf8..71d9f333e 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -74,6 +74,10 @@ class Compound < MemStruct m.instance_eval(&b) m.new end + def rtti_classname(n) + # TODO store total size for allocate() ? what about non-virtual ones ? + DFHack.rtti_register(n, self) + end end def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end end @@ -348,8 +352,34 @@ class Global < MemStruct def initialize(glob) @_glob = glob end - def _at(addr) ; g = DFHack::MemHack.const_get(@_glob) ; g.new._at(addr) ; end + def _at(addr) + g = DFHack::MemHack.const_get(@_glob) + g = DFHack.rtti_getclass(g, addr) + g.new._at(addr) + end end +module ::DFHack + def self.rtti_register(cname, cls) + @rtti_n2c ||= {} + @rtti_class ||= {} + @rtti_n2c[cname] = cls + @rtti_class[cls] = true + end + # return the ruby class to use for a given address if rtti info is available + def self.rtti_getclass(cls, addr) + @rtti_n2c ||= {} + @rtti_class ||= {} + if addr != 0 and @rtti_class[cls] + @rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls + else + cls + end + end + def self.rtti_readclassname(vptr) + @rtti_v2n ||= {} + @rtti_v2n[vptr] ||= get_rtti_classname(vptr) + end +end diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 06f5654ad..1e51c1102 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -380,10 +380,29 @@ static VALUE rb_dfget_vtable(VALUE self, VALUE name) return rb_uint2inum(addr); } -static VALUE rb_dfget_rtti_classname(VALUE self, VALUE objptr) +// read the c++ class name from a vtable pointer, inspired from doReadClassName +// XXX virtual classes only! dark pointer arithmetic, use with caution ! +static VALUE rb_dfget_rtti_classname(VALUE self, VALUE vptr) { - void **ptr = (void**)rb_num2ulong(objptr); - return rb_str_new2(typeid(*ptr).name()); + char *ptr = (char*)rb_num2ulong(vptr); +#ifdef WIN32 + char *rtti = *(char**)(ptr - 0x4); + char *typeinfo = *(char**)(rtti + 0xC); + // skip the .?AV, trim @@ from end + return rb_str_new(typeinfo+0xc, strlen(typeinfo+0xc)-2); +#else + char *typeinfo = *(char**)(ptr - 0x4); + char *typestring = *(char**)(typeinfo + 0x4); + while (*typestring >= '0' && *typestring <= '9') + typestring++; + return rb_str_new2(typestring); +#endif +} + +static VALUE rb_dfget_vtable_ptr(VALUE self, VALUE objptr) +{ + // actually, rb_dfmemory_read_int32 + return rb_uint2inum(*(uint32_t*)rb_num2ulong(objptr)); } @@ -391,7 +410,7 @@ static VALUE rb_dfget_rtti_classname(VALUE self, VALUE objptr) // raw memory access // used by the ruby class definitions -// WARNING: may cause game crash ! double-check your addresses ! +// XXX may cause game crash ! double-check your addresses ! static VALUE rb_dfmalloc(VALUE self, VALUE len) { @@ -583,6 +602,7 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "get_global_address", RUBY_METHOD_FUNC(rb_dfget_global_address), 1); rb_define_singleton_method(rb_cDFHack, "get_vtable", RUBY_METHOD_FUNC(rb_dfget_vtable), 1); rb_define_singleton_method(rb_cDFHack, "get_rtti_classname", RUBY_METHOD_FUNC(rb_dfget_rtti_classname), 1); + rb_define_singleton_method(rb_cDFHack, "get_vtable_ptr", RUBY_METHOD_FUNC(rb_dfget_vtable_ptr), 1); rb_define_singleton_method(rb_cDFHack, "register_dfcommand", RUBY_METHOD_FUNC(rb_dfregister), 2); rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1); rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1); From a450e9ddeb9d3458f37fad7c3d4243da582299de Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 19 Apr 2012 19:35:55 +0200 Subject: [PATCH 18/72] ruby: fix vectors, add bitvector --- plugins/ruby/ruby-memstruct.rb | 58 ++++++++++++++++-------- plugins/ruby/ruby.cpp | 80 +++++++++++++++++++++------------- plugins/ruby/ruby.rb | 5 +++ 3 files changed, 94 insertions(+), 49 deletions(-) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 71d9f333e..465e9975e 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -39,7 +39,6 @@ class Compound < MemStruct def stl_vector(tglen=nil) tg = yield if tglen - tg = tg._tg if tg.kind_of?(Pointer) # Vector.at(4) already dereferences case tglen when 1; StlVector8.new(tg) when 2; StlVector16.new(tg) @@ -230,9 +229,9 @@ class StlVector32 < MemStruct def length DFHack.memory_vector32_length(@_memaddr) end - alias size length - def value_at(idx) - DFHack.memory_vector32_at(@_memaddr, idx) + def size ; length ; end # alias wouldnt work for subclasses + def valueptr_at(idx) + DFHack.memory_vector32_ptrat(@_memaddr, idx) end def insert_at(idx, val) DFHack.memory_vector32_insert(@_memaddr, idx, val) @@ -251,22 +250,22 @@ class StlVector32 < MemStruct end def [](idx) idx += length if idx < 0 - @_tg._at(value_at(idx)) if idx >= 0 and idx < length + @_tg._at(valueptr_at(idx))._get if idx >= 0 and idx < length end def []=(idx, v) idx += length if idx < 0 if idx >= length - insert_at(idx, v) + insert_at(idx, 0) elsif idx < 0 raise 'invalid idx' - else - set_value_at(idx, v) end + @_tg._at(valueptr_at(idx))._set(v) end - def <<(v) - insert_at(length, v) + def push(v) + self[length] = v self end + def <<(v) ; push(v) ; end def pop l = length if l > 0 @@ -282,9 +281,8 @@ class StlVector16 < StlVector32 def length DFHack.memory_vector16_length(@_memaddr) end - alias size length - def value_at(idx) - DFHack.memory_vector16_at(@_memaddr, idx) + def valueptr_at(idx) + DFHack.memory_vector16_ptrat(@_memaddr, idx) end def insert_at(idx, val) DFHack.memory_vector16_insert(@_memaddr, idx, val) @@ -297,9 +295,8 @@ class StlVector8 < StlVector32 def length DFHack.memory_vector8_length(@_memaddr) end - alias size length - def value_at(idx) - DFHack.memory_vector8_at(@_memaddr, idx) + def valueptr_at(idx) + DFHack.memory_vector8_ptrat(@_memaddr, idx) end def insert_at(idx, val) DFHack.memory_vector8_insert(@_memaddr, idx, val) @@ -308,6 +305,32 @@ class StlVector8 < StlVector32 DFHack.memory_vector8_delete(@_memaddr, idx) end end +class StlBitVector < StlVector32 + def initialize ; end + def length + DFHack.memory_vectorbool_length(@_memaddr) + end + def insert_at(idx, val) + DFHack.memory_vectorbool_insert(@_memaddr, idx, val) + end + def delete_at(idx) + DFHack.memory_vectorbool_delete(@_memaddr, idx) + end + def [](idx) + idx += length if idx < 0 + DFHack.memory_vectorbool_at(@_memaddr, idx) if idx >= 0 and idx < length + end + def []=(idx, v) + idx += length if idx < 0 + if idx >= length + insert_at(idx, v) + elsif idx < 0 + raise 'invalid idx' + else + DFHack.memory_vectorbool_setat(@_memaddr, idx, v) + end + end +end class StlString < MemStruct def _get DFHack.memory_read_stlstring(@_memaddr) @@ -317,9 +340,6 @@ class StlString < MemStruct DFHack.memory_write_stlstring(@_memaddr, v) end end -class StlBitVector < MemStruct - # TODO -end class StlDeque < MemStruct attr_accessor :_tglen, :_tg def initialize(tglen, tg) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 1e51c1102..0e28087de 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -504,16 +504,10 @@ static VALUE rb_dfmemory_vec8_length(VALUE self, VALUE addr) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum(v->size()); } -static VALUE rb_dfmemory_vec8_at(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec8_ptrat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum(v->at(FIX2INT(idx))); -} -static VALUE rb_dfmemory_vec8_set(VALUE self, VALUE addr, VALUE idx, VALUE val) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->at(FIX2INT(idx)) = rb_num2ulong(val); - return Qtrue; + return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } static VALUE rb_dfmemory_vec8_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) { @@ -534,16 +528,10 @@ static VALUE rb_dfmemory_vec16_length(VALUE self, VALUE addr) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum(v->size()); } -static VALUE rb_dfmemory_vec16_at(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec16_ptrat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum(v->at(FIX2INT(idx))); -} -static VALUE rb_dfmemory_vec16_set(VALUE self, VALUE addr, VALUE idx, VALUE val) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->at(FIX2INT(idx)) = rb_num2ulong(val); - return Qtrue; + return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } static VALUE rb_dfmemory_vec16_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) { @@ -564,16 +552,10 @@ static VALUE rb_dfmemory_vec32_length(VALUE self, VALUE addr) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum(v->size()); } -static VALUE rb_dfmemory_vec32_at(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec32_ptrat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); - return rb_uint2inum(v->at(FIX2INT(idx))); -} -static VALUE rb_dfmemory_vec32_set(VALUE self, VALUE addr, VALUE idx, VALUE val) -{ - std::vector *v = (std::vector*)rb_num2ulong(addr); - v->at(FIX2INT(idx)) = rb_num2ulong(val); - return Qtrue; + return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } static VALUE rb_dfmemory_vec32_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) { @@ -588,6 +570,42 @@ static VALUE rb_dfmemory_vec32_delete(VALUE self, VALUE addr, VALUE idx) return Qtrue; } +// vector +static VALUE rb_dfmemory_vecbool_length(VALUE self, VALUE addr) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + return rb_uint2inum(v->size()); +} +static VALUE rb_dfmemory_vecbool_at(VALUE self, VALUE addr, VALUE idx) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + return v->at(FIX2INT(idx)) ? Qtrue : Qfalse; +} +static VALUE rb_dfmemory_vecbool_setat(VALUE self, VALUE addr, VALUE idx, VALUE val) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + if (val == Qnil || val == Qfalse || val == FIX2INT(0)) + v->at(FIX2INT(idx)) = 0; + else + v->at(FIX2INT(idx)) = 1; + return Qtrue; +} +static VALUE rb_dfmemory_vecbool_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + if (val == Qnil || val == Qfalse || val == FIX2INT(0)) + v->insert(v->begin()+FIX2INT(idx), 0); + else + v->insert(v->begin()+FIX2INT(idx), 1); + return Qtrue; +} +static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr, VALUE idx) +{ + std::vector *v = (std::vector*)rb_num2ulong(addr); + v->erase(v->begin()+FIX2INT(idx)); + return Qtrue; +} + // define module DFHack and its methods @@ -625,20 +643,22 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "memory_write_float", RUBY_METHOD_FUNC(rb_dfmemory_write_float), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector8_length", RUBY_METHOD_FUNC(rb_dfmemory_vec8_length), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_at", RUBY_METHOD_FUNC(rb_dfmemory_vec8_at), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_set", RUBY_METHOD_FUNC(rb_dfmemory_vec8_set), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_ptrat), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector8_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insert), 3); rb_define_singleton_method(rb_cDFHack, "memory_vector8_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec8_delete), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector16_length", RUBY_METHOD_FUNC(rb_dfmemory_vec16_length), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_at", RUBY_METHOD_FUNC(rb_dfmemory_vec16_at), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_set", RUBY_METHOD_FUNC(rb_dfmemory_vec16_set), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_ptrat), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector16_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insert), 3); rb_define_singleton_method(rb_cDFHack, "memory_vector16_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec16_delete), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector32_length", RUBY_METHOD_FUNC(rb_dfmemory_vec32_length), 1); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_at", RUBY_METHOD_FUNC(rb_dfmemory_vec32_at), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_set", RUBY_METHOD_FUNC(rb_dfmemory_vec32_set), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_ptrat), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector32_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insert), 3); rb_define_singleton_method(rb_cDFHack, "memory_vector32_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec32_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_length", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_length), 1); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_at", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_at), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_setat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_setat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insert", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insert), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 2); // load the default ruby-level definitions int state=0; diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index a75404365..380158a42 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -107,5 +107,10 @@ module DFHack end end +# alias, so we can write 'df.world.units.all[0]' +def df + DFHack +end + # load user-specified startup file load 'ruby_custom.rb' if File.exist?('ruby_custom.rb') From 0952b761730af7991b5715aa4c891299c7c735bb Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 20 Apr 2012 01:29:52 +0200 Subject: [PATCH 19/72] ruby: add df_array, pointer_ary, move generated classes in DFHack --- plugins/ruby/codegen.pl | 24 +++--- plugins/ruby/ruby-memstruct.rb | 138 ++++++++++++++++++++++++--------- plugins/ruby/ruby.cpp | 6 +- 3 files changed, 115 insertions(+), 53 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index a406cb758..1baa15365 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -51,8 +51,7 @@ sub render_global_enum { my ($name, $type) = @_; my $rbname = rb_ucase($name); - # store constants in DFHack::EnumName and not in DFHack::MemHack::EnumName - push @lines_rb, "class ::DFHack::$rbname"; + push @lines_rb, "class $rbname"; %seen_enum_name = (); indent_rb { render_enum_fields($type); @@ -81,7 +80,7 @@ sub render_global_bitfield { my ($name, $type) = @_; my $rbname = rb_ucase($name); - push @lines_rb, "class $rbname < Compound"; + push @lines_rb, "class $rbname < MemHack::Compound"; indent_rb { render_bitfield_fields($type); }; @@ -142,7 +141,7 @@ sub render_global_class { } } - my $rbparent = ($parent ? rb_ucase($parent) : 'Compound'); + my $rbparent = ($parent ? rb_ucase($parent) : 'MemHack::Compound'); my $cppvar = "v_$cpp_var_counter"; $cpp_var_counter++; @@ -191,7 +190,7 @@ sub render_global_objects { push @lines_cpp, "void cpp_$sname(FILE *fout) {"; push @include_cpp, $sname; - push @lines_rb, "class $rbname < Compound"; + push @lines_rb, "class $rbname < MemHack::Compound"; indent_rb { for my $obj (@objects) { my $oname = $obj->getAttribute('name'); @@ -208,7 +207,6 @@ sub render_global_objects { }; push @lines_rb, "end"; - push @lines_rb, "module ::DFHack"; indent_rb { push @lines_rb, "Global = GlobalObjects.new._at(0)"; for my $obj (@global_objects) { @@ -216,7 +214,6 @@ sub render_global_objects { push @lines_rb, "def self.$obj=(v) ; Global.$obj = v ; end"; } }; - push @lines_rb, "end"; } @@ -325,8 +322,13 @@ sub render_item_pointer { my ($item, $cppvar) = @_; my $tg = $item->findnodes('child::ld:item')->[0]; - my $tglen = get_tglen($tg, $cppvar); - push @lines_rb, "pointer($tglen) {"; + my $ary = $item->getAttribute('is-array'); + if ($ary and $ary eq 'true') { + my $tglen = get_tglen($tg, $cppvar); + push @lines_rb, "pointer_ary($tglen) {"; + } else { + push @lines_rb, "pointer {"; + } indent_rb { render_item($tg, "${cppvar}[0]"); }; @@ -468,15 +470,13 @@ if ($output =~ /\.cpp$/) { print FH "}\n"; } else { - print FH "module DFHack\n"; - print FH "module MemHack\n"; if ($memstruct) { open MH, "<$memstruct"; print FH "$_" while(); close MH; } + print FH "module DFHack\n"; print FH "$_\n" for @lines_rb; print FH "end\n"; - print FH "end\n"; } close FH; diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 465e9975e..f4796edef 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -1,6 +1,8 @@ +module DFHack +module MemHack class MemStruct attr_accessor :_memaddr - def _at(addr) ; @_memaddr = addr ; self ; end + def _at(addr) ; @_memaddr = addr ; dup ; end def _get ; self ; end end @@ -27,8 +29,11 @@ class Compound < MemStruct def bits(shift, len) BitField.new(shift, len) end - def pointer(tglen=nil) - Pointer.new(tglen, (yield if tglen)) + def pointer + Pointer.new((yield if block_given?)) + end + def pointer_ary(tglen) + PointerAry.new(tglen, yield) end def static_array(len, tglen) StaticArray.new(tglen, len, yield) @@ -80,6 +85,7 @@ class Compound < MemStruct end def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end end + class Number < MemStruct attr_accessor :_bits, :_signed def initialize(bits, signed) @@ -149,6 +155,23 @@ class BitField < MemStruct end class Pointer < MemStruct + attr_accessor :_tg + def initialize(tg) + @_tg = tg + end + + def _getp + DFHack.memory_read_int32(@_memaddr) & 0xffffffff + end + + # the pointer is invisible, forward all methods to the pointed object + def method_missing(*a) + addr = _getp + tg = (addr == 0 ? nil : @_tg._at(addr)._get) + tg.send(*a) + end +end +class PointerAry < MemStruct attr_accessor :_tglen, :_tg def initialize(tglen, tg) @_tglen = tglen @@ -159,10 +182,6 @@ class Pointer < MemStruct delta = (i != 0 ? i*@_tglen : 0) (DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + delta end - def _setp(v) - DFHack.memory_write_int32(@_memaddr, v) - end - def _get ; self ; end def [](i) addr = _getp(i) @@ -174,11 +193,6 @@ class Pointer < MemStruct raise 'null pointer' if addr == 0 @_tg._at(addr)._set(v) end - - # the pointer is invisible, forward all methods to the pointed object - def method_missing(*a) - self[0].send(*a) - end end class StaticArray < MemStruct attr_accessor :_tglen, :_length, :_tg @@ -205,7 +219,7 @@ class StaticArray < MemStruct end include Enumerable - def each; (0...length).each { |i| yield self[i] }; end + def each ; (0...length).each { |i| yield self[i] } ; end end class StaticString < MemStruct attr_accessor :_length @@ -274,8 +288,9 @@ class StlVector32 < MemStruct end v end + include Enumerable - def each; (0...length).each { |i| yield self[i] }; end + def each ; (0...length).each { |i| yield self[i] } ; end end class StlVector16 < StlVector32 def length @@ -346,19 +361,41 @@ class StlDeque < MemStruct @_tglen = tglen @_tg = tg end + # TODO end class DfFlagarray < MemStruct # TODO end -class DfArray < MemStruct +class DfArray < Compound attr_accessor :_tglen, :_tg def initialize(tglen, tg) @_tglen = tglen @_tg = tg end - # TODO + field(:_length, 0) { number 32, false } + field(:_ptr, 4) { number 32, false } + + def length ; _length ; end + def size ; _length ; end + def _tgat(i) + @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length + end + def [](i) + i += _length if i < 0 + _tgat(i)._get + end + def []=(i, v) + i += _length if i < 0 + _tgat(i)._set(v) + end + def _set(a) + a.each_with_index { |v, i| self[i] = v } + end + + include Enumerable + def each ; (0...length).each { |i| yield self[i] } ; end end class DfLinkedList < MemStruct attr_accessor :_tg @@ -373,33 +410,60 @@ class Global < MemStruct @_glob = glob end def _at(addr) - g = DFHack::MemHack.const_get(@_glob) - g = DFHack.rtti_getclass(g, addr) + g = DFHack.const_get(@_glob) + g = DFHack.rtti_getclassat(g, addr) g.new._at(addr) end end +end # module MemHack -module ::DFHack - def self.rtti_register(cname, cls) - @rtti_n2c ||= {} - @rtti_class ||= {} - @rtti_n2c[cname] = cls - @rtti_class[cls] = true - end - # return the ruby class to use for a given address if rtti info is available - def self.rtti_getclass(cls, addr) - @rtti_n2c ||= {} - @rtti_class ||= {} - if addr != 0 and @rtti_class[cls] - @rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls - else - cls - end +# cpp rtti name -> rb class +@rtti_n2c = {} +@rtti_c2n = {} + +# vtableptr -> cpp rtti name (cache) +@rtti_n2v = {} +@rtti_v2n = {} + +def self.rtti_n2c ; @rtti_n2c ; end +def self.rtti_c2n ; @rtti_c2n ; end + +# register a ruby class with a cpp rtti class name +def self.rtti_register(cname, cls) + @rtti_n2c[cname] = cls + @rtti_c2n[cls] = cname +end + +# return the ruby class to use for the cpp object at address if rtti info is available +def self.rtti_getclassat(cls, addr) + if addr != 0 and @rtti_c2n[cls] + # rtti info exist for class => cpp object has a vtable + @rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls + else + cls end +end - def self.rtti_readclassname(vptr) - @rtti_v2n ||= {} - @rtti_v2n[vptr] ||= get_rtti_classname(vptr) +# try to read the rtti classname from an object vtable pointer +def self.rtti_readclassname(vptr) + unless n = @rtti_v2n[vptr] + n = @rtti_v2n[vptr] = get_rtti_classname(vptr) + @rtti_n2v[n] = vptr end + n end + +# return the vtable pointer from the cpp rtti name +def self.rtti_getvtable(cname) + unless v = @rtti_n2v[cname] + v = get_vtable(cname) + @rtti_n2v[cname] = v + @rtti_v2n[v] = cname if v != 0 + end + v if v != 0 +end + +end + + diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 0e28087de..0b0df486f 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -370,14 +370,12 @@ static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) static VALUE rb_dfget_global_address(VALUE self, VALUE name) { - uint32_t addr = Core::getInstance().vinfo->getAddress(rb_string_value_ptr(&name)); - return rb_uint2inum(addr); + return rb_uint2inum(Core::getInstance().vinfo->getAddress(rb_string_value_ptr(&name))); } static VALUE rb_dfget_vtable(VALUE self, VALUE name) { - uint32_t addr = (uint32_t)Core::getInstance().vinfo->getVTable(rb_string_value_ptr(&name)); - return rb_uint2inum(addr); + return rb_uint2inum((uint32_t)Core::getInstance().vinfo->getVTable(rb_string_value_ptr(&name))); } // read the c++ class name from a vtable pointer, inspired from doReadClassName From fb2dcf55ca104bb60570446cb98ba61ee891054e Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 20 Apr 2012 15:30:15 +0200 Subject: [PATCH 20/72] ruby: fix DfArray, add #inspect, fix ruby.rb --- plugins/ruby/ruby-memstruct.rb | 64 ++++++++++++++++++++++++++++++---- plugins/ruby/ruby.rb | 6 ++-- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index f4796edef..d20c22c9d 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -4,6 +4,7 @@ class MemStruct attr_accessor :_memaddr def _at(addr) ; @_memaddr = addr ; dup ; end def _get ; self ; end + def inspect ; _get.inspect ; end end class Compound < MemStruct @@ -84,6 +85,33 @@ class Compound < MemStruct end end def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end + def _fields ; self.class._fields.to_a ; end + def inspect + cn = self.class.name.sub(/^DFHack::/, '') + cn << ' @' << ('0x%X' % _memaddr) if cn != '' + out = "#<#{cn}" + _fields.each { |n, o, s| + out << ' ' if out.length != 0 and out[-1, 1] != ' ' + if out.length > 1024 + out << '...' + break + end + out << inspect_field(n, o, s) + } + out << '>' + end + def inspect_field(n, o, s) + if s.kind_of?(BitField) and s._len == 1 + send(n) ? n.to_s : '' + elsif n == :_whole + "_whole=0x#{_whole.to_s(16)}" + else + v = send(n).inspect + "#{n}=#{v}" + end + rescue + "#{n}=ERR(#{$!})" + end end class Number < MemStruct @@ -164,11 +192,23 @@ class Pointer < MemStruct DFHack.memory_read_int32(@_memaddr) & 0xffffffff end - # the pointer is invisible, forward all methods to the pointed object - def method_missing(*a) + def _getv addr = _getp - tg = (addr == 0 ? nil : @_tg._at(addr)._get) - tg.send(*a) + return if addr == 0 + @_tg._at(addr)._get + end + + # these ruby Object methods should be forwarded to the ptr + undef id + undef type + def method_missing(*a) + _getv.send(*a) + end + + def inspect + cn = (@_tg ? @_tg.class.name.sub(/^DFHack::/, '') : '') + cn = @_tg._glob if cn == 'MemHack::Global' + "#" end end class PointerAry < MemStruct @@ -193,6 +233,8 @@ class PointerAry < MemStruct raise 'null pointer' if addr == 0 @_tg._at(addr)._set(v) end + + def inspect ; "#" ; end end class StaticArray < MemStruct attr_accessor :_tglen, :_length, :_tg @@ -220,6 +262,7 @@ class StaticArray < MemStruct include Enumerable def each ; (0...length).each { |i| yield self[i] } ; end + def inspect ; to_a.inspect ; end end class StaticString < MemStruct attr_accessor :_length @@ -291,6 +334,13 @@ class StlVector32 < MemStruct include Enumerable def each ; (0...length).each { |i| yield self[i] } ; end + def inspect + if _tg and _tg.kind_of?(Pointer) + length > 0 ? "[#{length}*#{self[0].inspect}]" : '[]' + else + to_a.inspect + end + end end class StlVector16 < StlVector32 def length @@ -374,8 +424,8 @@ class DfArray < Compound @_tg = tg end - field(:_length, 0) { number 32, false } - field(:_ptr, 4) { number 32, false } + field(:_ptr, 0) { number 32, false } + field(:_length, 4) { number 16, false } def length ; _length ; end def size ; _length ; end @@ -396,6 +446,7 @@ class DfArray < Compound include Enumerable def each ; (0...length).each { |i| yield self[i] } ; end + def inspect ; to_a.inspect ; end end class DfLinkedList < MemStruct attr_accessor :_tg @@ -414,6 +465,7 @@ class Global < MemStruct g = DFHack.rtti_getclassat(g, addr) g.new._at(addr) end + def inspect ; "#<#{@_glob}>" ; end end end # module MemHack diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 380158a42..976b3e1ac 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -39,10 +39,10 @@ module DFHack when UiSidebarMode::ViewUnits # nobody selected => idx == 0 v = world.units.other[0][ui_selected_unit] - v if v and v.z == cursor.z + v if v and v.pos.z == cursor.z when UiSidebarMode::LookAround k = ui_look_list.items[ui_look_cursor] - k.unit if k.type == MemHack::UiLookList::Unit + k.unit if k.type == UiLookList::Unit end elsif what.kind_of?(Integer) world.units.all.find { |u| u.id == what } @@ -62,7 +62,7 @@ module DFHack case ui.main.mode when UiSidebarMode::LookAround k = ui_look_list.items[ui_look_cursor] - k.item if k.type == MemHack::UiLookList::Item + k.item if k.type == UiLookList::Item end elsif what.kind_of?(Integer) world.items.all.find { |i| i.id == what } From 66891fd23ce64e76c2bcb9aff487cb0a9c30e4a0 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 20 Apr 2012 17:33:48 +0200 Subject: [PATCH 21/72] ruby: tweak onupdate, fix some inspects, add inverse ENUM --- plugins/ruby/codegen.pl | 6 +- plugins/ruby/ruby-memstruct.rb | 7 ++ plugins/ruby/ruby.cpp | 2 +- plugins/ruby/ruby.rb | 120 +++++++++++++++++++++++++++------ 4 files changed, 114 insertions(+), 21 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 1baa15365..72ef06156 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -62,6 +62,10 @@ sub render_enum_fields { my ($type) = @_; my $value = -1; + my $idxname = 'ENUM'; + $idxname .= '_' while ($seen_enum_name{$idxname}); + $seen_enum_name{$idxname}++; + push @lines_rb, "$idxname = {}"; for my $item ($type->findnodes('child::enum-item')) { $value = $item->getAttribute('value') || ($value+1); my $elemname = $item->getAttribute('name'); # || "unk_$value"; @@ -70,7 +74,7 @@ sub render_enum_fields { my $rbelemname = rb_ucase($elemname); $rbelemname .= '_' while ($seen_enum_name{$rbelemname}); $seen_enum_name{$rbelemname}++; - push @lines_rb, "$rbelemname = $value"; + push @lines_rb, "$rbelemname = $value ; ${idxname}[$value] = :$rbelemname"; } } } diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index d20c22c9d..c18a52bed 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -204,6 +204,9 @@ class Pointer < MemStruct def method_missing(*a) _getv.send(*a) end + def respond_to?(q) + _getv.respond_to?(q) + end def inspect cn = (@_tg ? @_tg.class.name.sub(/^DFHack::/, '') : '') @@ -412,10 +415,12 @@ class StlDeque < MemStruct @_tg = tg end # TODO + def inspect ; "#" ; end end class DfFlagarray < MemStruct # TODO + def inspect ; "#" ; end end class DfArray < Compound attr_accessor :_tglen, :_tg @@ -453,6 +458,8 @@ class DfLinkedList < MemStruct def initialize(tg) @_tg = tg end + # TODO + def inspect ; "#" ; end end class Global < MemStruct diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 0b0df486f..b60b5007b 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -296,7 +296,7 @@ static VALUE rb_dfonupdateactive(VALUE self) static VALUE rb_dfonupdateactiveset(VALUE self, VALUE val) { - onupdate_active = (val == Qtrue || val == INT2FIX(1)) ? 1 : 0; + onupdate_active = (val == Qfalse || val == Qnil || val == INT2FIX(0)) ? 0 : 1; return Qtrue; } diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 976b3e1ac..bb402404b 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -2,6 +2,7 @@ require 'hack/ruby-autogen' module DFHack class << self + # update the ruby.cpp version to accept a block def suspend if block_given? begin @@ -15,26 +16,48 @@ module DFHack end end - def puts(*a) - a.flatten.each { |l| - print_str(l.to_s.chomp + "\n") - } - nil + module ::Kernel + def puts(*a) + a.flatten.each { |l| + DFHack.print_str(l.to_s.chomp + "\n") + } + nil + end + + def puts_err(*a) + a.flatten.each { |l| + DFHack.print_err(l.to_s.chomp + "\n") + } + nil + end end - def puts_err(*a) - a.flatten.each { |l| - print_err(l.to_s.chomp + "\n") - } - nil + # register a callback to be called every gframe or more + # ex: DFHack.onupdate_register { DFHack.world.units[0].counters.job_counter = 0 } + def onupdate_register(&b) + @onupdate_list ||= [] + @onupdate_list << b + DFHack.onupdate_active = true + @onupdate_list.last + end + + # delete the callback for onupdate ; use the value returned by onupdate_register + def onupdate_unregister(b) + @onupdate_list.delete b + DFHack.onupdate_active = false if @onupdate_list.empty? + end + + # this method is called by dfhack every 'onupdate' if onupdate_active is true + def onupdate + @onupdate_list.each { |cb| cb.call } end # return an Unit - # with no arg, return currently selected unit in df UI (v or k menu) + # with no arg, return currently selected unit in df UI ('v' or 'k' menu) # with numeric arg, search unit by unit.id # with an argument that respond to x/y/z (eg cursor), find first unit at this position - def find_unit(what=nil) - if what == nil + def find_unit(what=:selected) + if what == :selected case ui.main.mode when UiSidebarMode::ViewUnits # nobody selected => idx == 0 @@ -56,9 +79,9 @@ module DFHack end # return an Item - # arg similar to find_unit - def find_item(what=nil) - if what == nil + # arg similar to find_unit; no arg = 'k' menu + def find_item(what=:selected) + if what == :selected case ui.main.mode when UiSidebarMode::LookAround k = ui_look_list.items[ui_look_cursor] @@ -75,8 +98,8 @@ module DFHack end end - # return a map block - # can use find_map_block(cursor) or anything that respond to x/y/z + # return a map block by tile coordinates + # you can also use find_map_block(cursor) or anything that respond to x/y/z def find_map_block(x=cursor, y=nil, z=nil) x = x.pos if x.respond_to?(:pos) x, y, z = x.x, x.y, x.z if x.respond_to?(:x) @@ -85,6 +108,65 @@ module DFHack end end + def center_viewscreen(x, y=nil, z=nil) + x = x.pos if x.respond_to?(:pos) + x, y, z = x.x, x.y, x.z if x.respond_to?(:x) + + # compute screen 'map' size (tiles) + menuwidth = ui_menu_width + # ui_menu_width shows only the 'tab' status + menuwidth = 1 if menuwidth == 2 and ui_area_map_width == 2 and cursor.x != -30000 + menuwidth = 2 if menuwidth == 3 and cursor.x != -30000 + w_w = gps.dimx - 2 + w_h = gps.dimy - 2 + case menuwidth + when 1; w_w -= 55 + when 2; w_w -= (ui_area_map_width == 2 ? 24 : 31) + end + + # center view + w_x = x - w_w/2 + w_y = y - w_h/2 + w_z = z + # round view coordinates (optional) + #w_x -= w_x % 10 + #w_y -= w_y % 10 + # crop to map limits + w_x = [[w_x, world.map.x_count - w_w].min, 0].max + w_y = [[w_y, world.map.y_count - w_h].min, 0].max + + self.window_x = w_x + self.window_y = w_y + self.window_z = w_z + + if cursor.x != -30000 + cursor.x, cursor.y, cursor.z = x, y, z + end + end + + # add an announcement + # color = integer, bright = bool + def add_announcement(str, color=0, bright=false) + cont = false + while str.length > 0 + rep = Report.cpp_alloc + rep.color = color + rep.bright = ((bright && bright != 0) ? 1 : 0) + rep.year = cur_year + rep.time = cur_year_tick + rep.flags.continuation = cont + cont = true + rep.flags.announcement = true + rep.text = str[0, 73] + str = str[73..-1].to_s + rep.id = world.status.next_report_id + world.status.next_report_id += 1 + world.status.reports << rep + world.status.announcements << rep + world.status.display_timer = 2000 + end + end + def test puts "starting" @@ -107,7 +189,7 @@ module DFHack end end -# alias, so we can write 'df.world.units.all[0]' +# global alias so we can write 'df.world.units.all[0]' def df DFHack end From f57d63ed95365be6f9ff6f6e4c540a10fdde7e47 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 20 Apr 2012 17:57:41 +0200 Subject: [PATCH 22/72] ruby: df_flagarray support --- plugins/ruby/ruby-memstruct.rb | 25 +++++++++++++++++++-- plugins/ruby/ruby.cpp | 41 ++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index c18a52bed..54bf18669 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -419,8 +419,29 @@ class StlDeque < MemStruct end class DfFlagarray < MemStruct - # TODO - def inspect ; "#" ; end + def length + DFHack.memory_bitarray_length(@_memaddr) + end + def size ; length ; end + def resize(len) + DFHack.memory_bitarray_resize(@_memaddr, len) + end + def [](idx) + idx += length if idx < 0 + DFHack.memory_bitarray_isset(@_memaddr, idx) if idx >= 0 and idx < length + end + def []=(idx, v) + idx += length if idx < 0 + if idx >= length or idx < 0 + raise 'invalid idx' + else + DFHack.memory_bitarray_set(@_memaddr, idx, v) + end + end + + include Enumerable + def each ; (0...length).each { |i| yield self[i] } ; end + def inspect ; to_a.inspect ; end end class DfArray < Compound attr_accessor :_tglen, :_tg diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index b60b5007b..7d94f4acd 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -278,6 +278,7 @@ static void df_rubythread(void *p) } +#define BOOL_ISFALSE(v) ((v) == Qfalse || (v) == Qnil || (v) == FIX2INT(0)) // main DFHack ruby module static VALUE rb_cDFHack; @@ -296,7 +297,7 @@ static VALUE rb_dfonupdateactive(VALUE self) static VALUE rb_dfonupdateactiveset(VALUE self, VALUE val) { - onupdate_active = (val == Qfalse || val == Qnil || val == INT2FIX(0)) ? 0 : 1; + onupdate_active = (BOOL_ISFALSE(val) ? 0 : 1); return Qtrue; } @@ -582,19 +583,13 @@ static VALUE rb_dfmemory_vecbool_at(VALUE self, VALUE addr, VALUE idx) static VALUE rb_dfmemory_vecbool_setat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); - if (val == Qnil || val == Qfalse || val == FIX2INT(0)) - v->at(FIX2INT(idx)) = 0; - else - v->at(FIX2INT(idx)) = 1; + v->at(FIX2INT(idx)) = (BOOL_ISFALSE(val) ? 0 : 1); return Qtrue; } static VALUE rb_dfmemory_vecbool_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); - if (val == Qnil || val == Qfalse || val == FIX2INT(0)) - v->insert(v->begin()+FIX2INT(idx), 0); - else - v->insert(v->begin()+FIX2INT(idx), 1); + v->insert(v->begin()+FIX2INT(idx), (BOOL_ISFALSE(val) ? 0 : 1)); return Qtrue; } static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr, VALUE idx) @@ -604,6 +599,30 @@ static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr, VALUE idx) return Qtrue; } +// BitArray +static VALUE rb_dfmemory_bitarray_length(VALUE self, VALUE addr) +{ + DFHack::BitArray *b = (DFHack::BitArray*)rb_num2ulong(addr); + return rb_uint2inum(b->size); +} +static VALUE rb_dfmemory_bitarray_resize(VALUE self, VALUE addr, VALUE sz) +{ + DFHack::BitArray *b = (DFHack::BitArray*)rb_num2ulong(addr); + b->resize(rb_num2ulong(sz)); + return Qtrue; +} +static VALUE rb_dfmemory_bitarray_isset(VALUE self, VALUE addr, VALUE idx) +{ + DFHack::BitArray *b = (DFHack::BitArray*)rb_num2ulong(addr); + return b->is_set(rb_num2ulong(idx)) ? Qtrue : Qfalse; +} +static VALUE rb_dfmemory_bitarray_set(VALUE self, VALUE addr, VALUE idx, VALUE val) +{ + DFHack::BitArray *b = (DFHack::BitArray*)rb_num2ulong(addr); + b->set(rb_num2ulong(idx), (BOOL_ISFALSE(val) ? 0 : 1)); + return Qtrue; +} + // define module DFHack and its methods @@ -657,6 +676,10 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_setat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_setat), 3); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insert", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insert), 3); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_bitarray_length", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_length), 1); + rb_define_singleton_method(rb_cDFHack, "memory_bitarray_resize", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_resize), 2); + rb_define_singleton_method(rb_cDFHack, "memory_bitarray_isset", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_isset), 2); + rb_define_singleton_method(rb_cDFHack, "memory_bitarray_set", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_set), 3); // load the default ruby-level definitions int state=0; From 417356c3cb1be379806f0754b6eb98b0eff32d3d Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 20 Apr 2012 18:20:24 +0200 Subject: [PATCH 23/72] ruby: add df-linked-list support (also, xml could be simplified) --- plugins/ruby/codegen.pl | 11 ++++++---- plugins/ruby/ruby-memstruct.rb | 38 +++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 72ef06156..82bff1137 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -309,10 +309,13 @@ sub render_item_container { my $subtype = $item->getAttribute('ld:subtype'); my $rbmethod = join('_', split('-', $subtype)); my $tg = $item->findnodes('child::ld:item')->[0]; - # df-linked-list has no list[0] - if ($tg and $rbmethod ne 'df_linked_list') { - my $tglen = get_tglen($tg, $cppvar); - push @lines_rb, "$rbmethod($tglen) {"; + if ($tg) { + if ($rbmethod eq 'df_linked_list') { + push @lines_rb, "$rbmethod {"; + } else { + my $tglen = get_tglen($tg, $cppvar); + push @lines_rb, "$rbmethod($tglen) {"; + } indent_rb { render_item($tg, "${cppvar}[0]"); }; diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 54bf18669..bc387de68 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -2,7 +2,7 @@ module DFHack module MemHack class MemStruct attr_accessor :_memaddr - def _at(addr) ; @_memaddr = addr ; dup ; end + def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end def _get ; self ; end def inspect ; _get.inspect ; end end @@ -68,7 +68,7 @@ class Compound < MemStruct DfArray.new(tglen, (yield if tglen)) end def df_linked_list - DfLinkedList.new((yield if block_given?)) + DfLinkedList.new(yield) end def global(glob) @@ -474,13 +474,41 @@ class DfArray < Compound def each ; (0...length).each { |i| yield self[i] } ; end def inspect ; to_a.inspect ; end end -class DfLinkedList < MemStruct +class DfLinkedList < Compound attr_accessor :_tg def initialize(tg) @_tg = tg end - # TODO - def inspect ; "#" ; end + + field(:_ptr, 0) { number 32, false } + field(:_prev, 4) { number 32, false } + field(:_next, 8) { number 32, false } + + def item + addr = _ptr + return if addr == 0 + @_tg._at(addr)._get + end + + def item=(v) + addr = _ptr + raise 'null pointer' if addr == 0 + @_tg.at(addr)._set(v) + end + + def prev + addr = _prev + return if addr == 0 + _at(addr) + end + + def next + addr = _next + return if addr == 0 + _at(addr) + end + + def inspect ; "#" ; end end class Global < MemStruct From 826e0b7dcf28ef0ea756d7d91d0b4568480b19a1 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 20 Apr 2012 20:43:18 +0200 Subject: [PATCH 24/72] ruby: iterate in LinkedList --- plugins/ruby/ruby-memstruct.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index bc387de68..8189832e7 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -508,6 +508,15 @@ class DfLinkedList < Compound _at(addr) end + include Enumerable + def each + o = self + while o + yield o.item if o.item + o = o.next + end + end + def inspect ; "#" ; end end From 93171439093ad8ca641f87ccca0e281e9b0fcf89 Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 21 Apr 2012 15:04:52 +0200 Subject: [PATCH 25/72] ruby: officially dont support stl::deque, add class sizeof --- plugins/ruby/codegen.pl | 2 ++ plugins/ruby/ruby-memstruct.rb | 40 ++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 82bff1137..a37d81389 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -156,6 +156,8 @@ sub render_global_class { push @lines_rb, "class $rbname < $rbparent"; indent_rb { + my $sz = query_cpp("sizeof(*$cppvar)"); + push @lines_rb, "sizeof $sz"; push @lines_rb, "rtti_classname '$rtti_name'" if $has_rtti; render_struct_fields($type, "(*$cppvar)"); }; diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 8189832e7..b5c0f61e9 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -9,7 +9,7 @@ end class Compound < MemStruct class << self - attr_accessor :_fields + attr_accessor :_fields, :_rtti_classname, :_sizeof def field(name, offset) struct = yield @_fields ||= [] @@ -57,15 +57,15 @@ class Compound < MemStruct def stl_bit_vector StlBitVector.new end - def stl_deque(tglen=nil) - StlDeque.new(tglen, (yield if tglen)) + def stl_deque(tglen) + StlDeque.new(tglen, yield) end def df_flagarray DfFlagarray.new end - def df_array(tglen=nil) - DfArray.new(tglen, (yield if tglen)) + def df_array(tglen) + DfArray.new(tglen, yield) end def df_linked_list DfLinkedList.new(yield) @@ -80,12 +80,17 @@ class Compound < MemStruct m.new end def rtti_classname(n) - # TODO store total size for allocate() ? what about non-virtual ones ? DFHack.rtti_register(n, self) + @_rtti_classname = n + end + def sizeof(n) + @_sizeof = n end end def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end def _fields ; self.class._fields.to_a ; end + def _rtti_classname ; self.class._rtti_classname ; end + def _sizeof ; self.class._sizeof ; end def inspect cn = self.class.name.sub(/^DFHack::/, '') cn << ' @' << ('0x%X' % _memaddr) if cn != '' @@ -414,7 +419,8 @@ class StlDeque < MemStruct @_tglen = tglen @_tg = tg end - # TODO + # XXX DF uses stl::deque, so to have a C binding we'd need to single-case every + # possible struct size, like for StlVector. Just ignore it for now, deque are rare enough. def inspect ; "#" ; end end @@ -539,17 +545,19 @@ end # module MemHack @rtti_n2c = {} @rtti_c2n = {} -# vtableptr -> cpp rtti name (cache) +# cpp rtti name -> vtable ptr @rtti_n2v = {} @rtti_v2n = {} def self.rtti_n2c ; @rtti_n2c ; end def self.rtti_c2n ; @rtti_c2n ; end +def self.rtti_n2v ; @rtti_n2v ; end +def self.rtti_v2n ; @rtti_v2n ; end # register a ruby class with a cpp rtti class name -def self.rtti_register(cname, cls) - @rtti_n2c[cname] = cls - @rtti_c2n[cls] = cname +def self.rtti_register(cppname, cls) + @rtti_n2c[cppname] = cls + @rtti_c2n[cls] = cppname end # return the ruby class to use for the cpp object at address if rtti info is available @@ -572,11 +580,11 @@ def self.rtti_readclassname(vptr) end # return the vtable pointer from the cpp rtti name -def self.rtti_getvtable(cname) - unless v = @rtti_n2v[cname] - v = get_vtable(cname) - @rtti_n2v[cname] = v - @rtti_v2n[v] = cname if v != 0 +def self.rtti_getvtable(cppname) + unless v = @rtti_n2v[cppname] + v = get_vtable(cppname) + @rtti_n2v[cppname] = v + @rtti_v2n[v] = cppname if v != 0 end v if v != 0 end From 6fc17b3e1f19360ea48c83ab1fc7036587f09688 Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 21 Apr 2012 21:20:51 +0200 Subject: [PATCH 26/72] ruby: clean helper with offsetof --- plugins/ruby/CMakeLists.txt | 3 + plugins/ruby/codegen.pl | 121 +++++++++++++++++++++++++----------- 2 files changed, 86 insertions(+), 38 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index 6927dbc8a..7057cb2dc 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -6,6 +6,9 @@ if(RUBY_FOUND) DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl ) ADD_EXECUTABLE(ruby-autogen ruby-autogen.cpp) + if(CMAKE_COMPILER_IS_GNUCC) + set_target_properties (ruby-autogen PROPERTIES COMPILE_FLAGS "-Wno-invalid-offsetof") + endif(CMAKE_COMPILER_IS_GNUCC) ADD_CUSTOM_COMMAND( OUTPUT ruby-autogen.offsets COMMAND ruby-autogen ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index a37d81389..b75540a6d 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -147,37 +147,35 @@ sub render_global_class { my $rbparent = ($parent ? rb_ucase($parent) : 'MemHack::Compound'); - my $cppvar = "v_$cpp_var_counter"; - $cpp_var_counter++; + my $cppns = "df::$name"; push @lines_cpp, "}" if @include_cpp; push @lines_cpp, "void cpp_$name(FILE *fout) {"; - push @lines_cpp, " df::$name *$cppvar = (df::$name*)moo;"; push @include_cpp, $name; push @lines_rb, "class $rbname < $rbparent"; indent_rb { - my $sz = query_cpp("sizeof(*$cppvar)"); + my $sz = query_cpp("sizeof($cppns)"); push @lines_rb, "sizeof $sz"; push @lines_rb, "rtti_classname '$rtti_name'" if $has_rtti; - render_struct_fields($type, "(*$cppvar)"); + render_struct_fields($type, "$cppns"); }; push @lines_rb, "end\n"; } sub render_struct_fields { - my ($type, $cppvar) = @_; + my ($type, $cppns) = @_; for my $field ($type->findnodes('child::ld:field')) { my $name = $field->getAttribute('name'); $name = $field->getAttribute('ld:anon-name') if (!$name); if (!$name and $field->getAttribute('ld:anon-compound')) { - render_struct_fields($field, $cppvar); + render_struct_fields($field, $cppns); } next if (!$name); - my $offset = get_offset($cppvar, $name); + my $offset = get_offset($cppns, $name); push @lines_rb, "field(:$name, $offset) {"; indent_rb { - render_item($field, "$cppvar.$name"); + render_item($field, "$cppns"); }; push @lines_rb, "}"; } @@ -204,7 +202,7 @@ sub render_global_objects { push @lines_rb, "field(:$oname, $addr) {"; my $item = $obj->findnodes('child::ld:item')->[0]; indent_rb { - render_item($item, "(*df::global::$oname)"); + render_item($item, 'df::global'); }; push @lines_rb, "}"; @@ -224,27 +222,27 @@ sub render_global_objects { sub render_item { - my ($item, $cppvar) = @_; + my ($item, $pns) = @_; return if (!$item); my $meta = $item->getAttribute('ld:meta'); my $renderer = $item_renderer{$meta}; if ($renderer) { - $renderer->($item, $cppvar); + $renderer->($item, $pns); } else { print "no render item $meta\n"; } } sub render_item_global { - my ($item, $cppvar) = @_; + my ($item, $pns) = @_; my $typename = $item->getAttribute('type-name'); my $subtype = $item->getAttribute('ld:subtype'); if ($subtype and $subtype eq 'enum') { - render_item_number($item, $cppvar); + render_item_number($item, $pns); } else { my $rbname = rb_ucase($typename); push @lines_rb, "global :$rbname"; @@ -252,7 +250,7 @@ sub render_item_global { } sub render_item_number { - my ($item, $cppvar) = @_; + my ($item, $pns) = @_; my $subtype = $item->getAttribute('ld:subtype'); $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'enum' or $subtype eq 'bitfield'); @@ -282,14 +280,15 @@ sub render_item_number { } sub render_item_compound { - my ($item, $cppvar) = @_; + my ($item, $pns) = @_; + my $cppns = $pns . '::' . $item->getAttribute('ld:typedef-name'); my $subtype = $item->getAttribute('ld:subtype'); if (!$subtype || $subtype eq 'bitfield') { push @lines_rb, "compound {"; indent_rb { if (!$subtype) { - render_struct_fields($item, $cppvar); + render_struct_fields($item, $cppns); } else { render_bitfield_fields($item); } @@ -299,14 +298,14 @@ sub render_item_compound { # declare constants render_enum_fields($item); # actual field - render_item_number($item, $cppvar); + render_item_number($item, $cppns); } else { print "no render compound $subtype\n"; } } sub render_item_container { - my ($item, $cppvar) = @_; + my ($item, $pns) = @_; my $subtype = $item->getAttribute('ld:subtype'); my $rbmethod = join('_', split('-', $subtype)); @@ -315,11 +314,11 @@ sub render_item_container { if ($rbmethod eq 'df_linked_list') { push @lines_rb, "$rbmethod {"; } else { - my $tglen = get_tglen($tg, $cppvar); + my $tglen = get_tglen($tg, $pns); push @lines_rb, "$rbmethod($tglen) {"; } indent_rb { - render_item($tg, "${cppvar}[0]"); + render_item($tg, $pns); }; push @lines_rb, "}"; } else { @@ -328,37 +327,37 @@ sub render_item_container { } sub render_item_pointer { - my ($item, $cppvar) = @_; + my ($item, $pns) = @_; my $tg = $item->findnodes('child::ld:item')->[0]; my $ary = $item->getAttribute('is-array'); if ($ary and $ary eq 'true') { - my $tglen = get_tglen($tg, $cppvar); + my $tglen = get_tglen($tg, $pns); push @lines_rb, "pointer_ary($tglen) {"; } else { push @lines_rb, "pointer {"; } indent_rb { - render_item($tg, "${cppvar}[0]"); + render_item($tg, $pns); }; push @lines_rb, "}"; } sub render_item_staticarray { - my ($item, $cppvar) = @_; + my ($item, $pns) = @_; my $count = $item->getAttribute('count'); my $tg = $item->findnodes('child::ld:item')->[0]; - my $tglen = get_tglen($tg, $cppvar); + my $tglen = get_tglen($tg, $pns); push @lines_rb, "static_array($count, $tglen) {"; indent_rb { - render_item($tg, "${cppvar}[0]"); + render_item($tg, $pns); }; push @lines_rb, "}"; } sub render_item_primitive { - my ($item, $cppvar) = @_; + my ($item, $pns) = @_; my $subtype = $item->getAttribute('ld:subtype'); if ($subtype eq 'stl-string') { @@ -369,7 +368,7 @@ sub render_item_primitive { } sub render_item_bytes { - my ($item, $cppvar) = @_; + my ($item, $pns) = @_; my $subtype = $item->getAttribute('ld:subtype'); if ($subtype eq 'padding') { @@ -382,15 +381,13 @@ sub render_item_bytes { } sub get_offset { - my ($cppvar, $fname) = @_; + my ($cppns, $fname) = @_; - # GCC fails with this - #return query_cpp("offsetof(typeof($cppvar), $fname)"); - return query_cpp("((char*)&$cppvar.$fname - (char*)&$cppvar)"); + return query_cpp("offsetof($cppns, $fname)"); } sub get_tglen { - my ($tg, $cppvar) = @_; + my ($tg, $cppns) = @_; if (!$tg) { return 'nil'; @@ -401,17 +398,67 @@ sub get_tglen { return $tg->getAttribute('ld:bits')/8; } elsif ($meta eq 'pointer') { return 4; + } elsif ($meta eq 'container') { + my $subtype = $tg->getAttribute('ld:subtype'); + if ($subtype eq 'stl-vector') { + return query_cpp("sizeof(std::vector)"); + } elsif ($subtype eq 'df-linked-list') { + return 12; + } else { + print "cannot tglen container $subtype\n"; + } + } elsif ($meta eq 'compound') { + my $cname = $tg->getAttribute('ld:typedef-name'); + return query_cpp("sizeof(${cppns}::$cname)"); + } elsif ($meta eq 'static-array') { + my $count = $tg->getAttribute('count'); + my $ttg = $tg->findnodes('child::ld:item')->[0]; + my $ttgl = get_tglen($ttg, $cppns); + if ($ttgl =~ /^\d+$/) { + return $count * $ttgl; + } else { + return "$count*$ttgl"; + } + } elsif ($meta eq 'global') { + my $typename = $tg->getAttribute('type-name'); + my $subtype = $tg->getAttribute('ld:subtype'); + if ($subtype and $subtype eq 'enum') { + my $base = $tg->getAttribute('base-type') || 'int32_t'; + if ($base eq 'int32_t') { + return 4; + } elsif ($base eq 'int16_t') { + return 2; + } elsif ($base eq 'int8_t') { + return 1; + } else { + print "cannot tglen enum $base\n"; + } + } else { + return query_cpp("sizeof(df::$typename)"); + } + } elsif ($meta eq 'primitive') { + my $subtype = $tg->getAttribute('ld:subtype'); + if ($subtype eq 'stl-string') { + return query_cpp("sizeof(std::string)"); + } else { + print "cannot tglen primitive $subtype\n"; + } } else { - return query_cpp("sizeof(${cppvar}[0])"); + print "cannot tglen $meta\n"; } } +my %query_cpp_cache; sub query_cpp { my ($query) = @_; my $ans = $offsets{$query}; return $ans if (defined($ans)); + my $cached = $query_cpp_cache{$query}; + return $cached if (defined($cached)); + $query_cpp_cache{$query} = 1; + push @lines_cpp, " fprintf(fout, \"%s = %d\\n\", \"$query\", $query);"; return "'$query'"; } @@ -464,14 +511,12 @@ if ($output =~ /\.cpp$/) { print FH "#include \"DataDefs.h\"\n"; print FH "#include \"df/$_.h\"\n" for @include_cpp; print FH "#include \n"; - print FH "static void *moo[1024];\n"; + print FH "#include \n"; print FH "$_\n" for @lines_cpp; print FH "}\n"; print FH "int main(int argc, char **argv) {\n"; print FH " FILE *fout;\n"; print FH " if (argc < 2) return 1;\n"; - # sometimes gcc will generate accesses to the structures at 0 just for a sizeof/offsetof, this works around the segfaults... - print FH " for (int i=0 ; i<1024 ; i++) moo[i] = &moo;\n"; print FH " fout = fopen(argv[1], \"w\");\n"; print FH " cpp_$_(fout);\n" for @include_cpp; print FH " fclose(fout);\n"; From 8a05db7a1b688ddc2eb21e9db402add3ea8f0ef6 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 23 Apr 2012 21:12:24 +0200 Subject: [PATCH 27/72] ruby: add map block iterators, fix FlagArray.length --- plugins/ruby/ruby.cpp | 2 +- plugins/ruby/ruby.rb | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 7d94f4acd..f291b757e 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -603,7 +603,7 @@ static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr, VALUE idx) static VALUE rb_dfmemory_bitarray_length(VALUE self, VALUE addr) { DFHack::BitArray *b = (DFHack::BitArray*)rb_num2ulong(addr); - return rb_uint2inum(b->size); + return rb_uint2inum(b->size*8); // b->size is in bytes } static VALUE rb_dfmemory_bitarray_resize(VALUE self, VALUE addr, VALUE sz) { diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index bb402404b..c267d5686 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -30,6 +30,12 @@ module DFHack } nil end + + def p(*a) + a.each { |e| + puts e.inspect + } + end end # register a callback to be called every gframe or more @@ -108,6 +114,33 @@ module DFHack end end + # yields every map block + def each_map_block + (0...world.map.x_count_block).each { |xb| + xl = world.map.block_index[xb] + (0...world.map.y_count_block).each { |yb| + yl = xl[yb] + (0...world.map.z_count_block).each { |z| + p = yl[z] + yield p._getv if p._getp != 0 + } + } + } + end + + # yields every map block for a given z level + def each_map_block_z(z) + (0...world.map.x_count_block).each { |xb| + xl = world.map.block_index[xb] + (0...world.map.y_count_block).each { |yb| + p = xl[yb][z] + yield p._getv if p._getp != 0 + } + } + end + + # center the DF screen on something + # updates the cursor position if visible def center_viewscreen(x, y=nil, z=nil) x = x.pos if x.respond_to?(:pos) x, y, z = x.x, x.y, x.z if x.respond_to?(:x) @@ -167,6 +200,16 @@ module DFHack end end + # try to match a user-specified name to one from the raws + # uses case-switching and substring matching + # eg match_rawname('coal', ['COAL_BITUMINOUS', 'BAUXITE']) => 'COAL_BITUMINOUS' + def match_rawname(name, rawlist) + rawlist.each { |r| return r if name == r } + rawlist.each { |r| return r if name.downcase == r.downcase } + may = rawlist.find_all { |r| r.downcase.index(name.downcase) } + may.first if may.length == 1 + end + def test puts "starting" From c1baebd083ee9b33702f47656b1724718fc8299e Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 24 Apr 2012 00:47:10 +0200 Subject: [PATCH 28/72] ruby: rename find_map_block to map_block_at, add other helper methods --- plugins/ruby/ruby.rb | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index c267d5686..49c2e5024 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -106,7 +106,7 @@ module DFHack # return a map block by tile coordinates # you can also use find_map_block(cursor) or anything that respond to x/y/z - def find_map_block(x=cursor, y=nil, z=nil) + def map_block_at(x, y=nil, z=nil) x = x.pos if x.respond_to?(:pos) x, y, z = x.x, x.y, x.z if x.respond_to?(:x) if x >= 0 and x < world.map.x_count and y >= 0 and y < world.map.y_count and z >= 0 and z < world.map.z_count @@ -114,6 +114,22 @@ module DFHack end end + def map_designation_at(x, y=nil, z=nil) + x = x.pos if x.respond_to?(:pos) + x, y, z = x.x, x.y, x.z if x.respond_to?(:x) + if b = map_block_at(x, y, z) + b.designation[x%16][y%16] + end + end + + def map_occupancy_at(x, y=nil, z=nil) + x = x.pos if x.respond_to?(:pos) + x, y, z = x.x, x.y, x.z if x.respond_to?(:x) + if b = map_block_at(x, y, z) + b.occupancy[x%16][y%16] + end + end + # yields every map block def each_map_block (0...world.map.x_count_block).each { |xb| @@ -139,6 +155,18 @@ module DFHack } end + # return true if the argument is under the cursor + def at_cursor?(obj) + same_pos?(obj, cursor) + end + + # returns true if both arguments are at the same x/y/z + def same_pos?(pos1, pos2) + pos1 = pos1.pos if pos1.respond_to?(:pos) + pos2 = pos2.pos if pos2.respond_to?(:pos) + pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z + end + # center the DF screen on something # updates the cursor position if visible def center_viewscreen(x, y=nil, z=nil) @@ -220,8 +248,8 @@ module DFHack puts "selected unit id: #{u.id}" end - if b = find_map_block - b.designation[cursor.x%16][cursor.y%16].dig = TileDigDesignation::Default + if b = map_block_at(cursor) + map_designation_at(cursor).dig = TileDigDesignation::Default b.flags.designated = true puts "dug cursor tile" end From eca17fff531b88d352ad8b79d872669749139319 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 24 Apr 2012 00:57:07 +0200 Subject: [PATCH 29/72] ruby: add sample plugin (cuttrees, growtrees) --- plugins/ruby/plugins/plant.rb | 97 +++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 plugins/ruby/plugins/plant.rb diff --git a/plugins/ruby/plugins/plant.rb b/plugins/ruby/plugins/plant.rb new file mode 100644 index 000000000..c35b186c3 --- /dev/null +++ b/plugins/ruby/plugins/plant.rb @@ -0,0 +1,97 @@ +module DFHack +def self.each_tree(material=:any) + @raw_tree_name ||= {} + if @raw_tree_name.empty? + df.world.raws.plants.all.each_with_index { |p, idx| + @raw_tree_name[idx] = p.id if p.flags[PlantRawFlags::TREE] + } + end + + if material != :any + mat = match_rawname(material, @raw_tree_name.values) + unless wantmat = @raw_tree_name.index(mat) + raise "invalid tree material #{material}" + end + end + + world.plants.all.each { |plant| + next if not @raw_tree_name[plant.material] + next if wantmat and plant.material != wantmat + yield plant + } +end + +def self.each_shrub(material=:any) + @raw_shrub_name ||= {} + if @raw_tree_name.empty? + df.world.raws.plants.all.each_with_index { |p, idx| + @raw_shrub_name[idx] = p.id if not p.flags[PlantRawFlags::GRASS] and not p.flags[PlantRawFlags::TREE] + } + end + + if material != :any + mat = match_rawname(material, @raw_shrub_name.values) + unless wantmat = @raw_shrub_name.index(mat) + raise "invalid shrub material #{material}" + end + end +end + +SaplingToTreeAge = 120960 +def self.cuttrees(material=nil, count_max=100) + if !material + # list trees + cnt = Hash.new(0) + each_tree { |plant| + next if plant.grow_counter < SaplingToTreeAge + next if find_map_designation(plant).hidden + cnt[plant.material] += 1 + } + cnt.sort_by { |mat, c| c }.each { |mat, c| + name = @raw_tree_name[mat] + puts " #{name} #{c}" + } + else + cnt = 0 + each_tree(material) { |plant| + next if plant.grow_counter < SaplingToTreeAge + b = find_map_block(plant) + d = b.designation[plant.pos.x%16][plant.pos.y%16] + next if d.hidden + if d.dig == TileDigDesignation::No + d.dig = TileDigDesignation::Default + b.flags.designated = true + cnt += 1 + break if cnt == count_max + end + } + puts "Updated #{cnt} plant designations" + end +end + +def self.growtrees(material=nil, count_max=100) + if !material + # list plants + cnt = Hash.new(0) + each_tree { |plant| + next if plant.grow_counter >= SaplingToTreeAge + next if find_map_designation(plant).hidden + cnt[plant.material] += 1 + } + cnt.sort_by { |mat, c| c }.each { |mat, c| + name = @raw_tree_name[mat] + puts " #{name} #{c}" + } + else + cnt = 0 + each_tree(material) { |plant| + next if plant.grow_counter >= SaplingToTreeAge + next if find_map_designation(plant).hidden + p.grow_counter = SaplingToTreeAge + cnt += 1 + break if cnt == count_max + } + puts "Grown #{cnt} sapling" + end +end +end From 7a71204b82cc89fad264045639de683f4e115637 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 24 Apr 2012 19:07:54 +0200 Subject: [PATCH 30/72] ruby: guard against undefined globals, fix inspect for inherited classes --- plugins/ruby/codegen.pl | 13 +++++++++---- plugins/ruby/ruby-memstruct.rb | 8 +++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index b75540a6d..9a34fe188 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -199,12 +199,17 @@ sub render_global_objects { for my $obj (@objects) { my $oname = $obj->getAttribute('name'); my $addr = "DFHack.get_global_address('$oname')"; - push @lines_rb, "field(:$oname, $addr) {"; - my $item = $obj->findnodes('child::ld:item')->[0]; + push @lines_rb, "addr = $addr"; + push @lines_rb, "if addr != 0"; indent_rb { - render_item($item, 'df::global'); + push @lines_rb, "field(:$oname, addr) {"; + my $item = $obj->findnodes('child::ld:item')->[0]; + indent_rb { + render_item($item, 'df::global'); + }; + push @lines_rb, "}"; }; - push @lines_rb, "}"; + push @lines_rb, "end"; push @global_objects, $oname; } diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index b5c0f61e9..12f261cd2 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -95,7 +95,13 @@ class Compound < MemStruct cn = self.class.name.sub(/^DFHack::/, '') cn << ' @' << ('0x%X' % _memaddr) if cn != '' out = "#<#{cn}" - _fields.each { |n, o, s| + fields = _fields + cls = self.class.superclass + while cls.respond_to?(:_fields) + fields += cls._fields.to_a + cls = cls.superclass + end + fields.each { |n, o, s| out << ' ' if out.length != 0 and out[-1, 1] != ' ' if out.length > 1024 out << '...' From bf64c3e23c98ba297ba912f005207cac6431208d Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 24 Apr 2012 19:08:14 +0200 Subject: [PATCH 31/72] ruby/plugin: tweak plants, add growcrops --- plugins/ruby/plugins/plant.rb | 71 ++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/plugins/ruby/plugins/plant.rb b/plugins/ruby/plugins/plant.rb index c35b186c3..2182e4d55 100644 --- a/plugins/ruby/plugins/plant.rb +++ b/plugins/ruby/plugins/plant.rb @@ -1,37 +1,37 @@ module DFHack def self.each_tree(material=:any) - @raw_tree_name ||= {} - if @raw_tree_name.empty? + @raws_tree_name ||= {} + if @raws_tree_name.empty? df.world.raws.plants.all.each_with_index { |p, idx| - @raw_tree_name[idx] = p.id if p.flags[PlantRawFlags::TREE] + @raws_tree_name[idx] = p.id if p.flags[PlantRawFlags::TREE] } end if material != :any - mat = match_rawname(material, @raw_tree_name.values) - unless wantmat = @raw_tree_name.index(mat) + mat = match_rawname(material, @raws_tree_name.values) + unless wantmat = @raws_tree_name.index(mat) raise "invalid tree material #{material}" end end world.plants.all.each { |plant| - next if not @raw_tree_name[plant.material] + next if not @raws_tree_name[plant.material] next if wantmat and plant.material != wantmat yield plant } end def self.each_shrub(material=:any) - @raw_shrub_name ||= {} - if @raw_tree_name.empty? + @raws_shrub_name ||= {} + if @raws_tree_name.empty? df.world.raws.plants.all.each_with_index { |p, idx| - @raw_shrub_name[idx] = p.id if not p.flags[PlantRawFlags::GRASS] and not p.flags[PlantRawFlags::TREE] + @raws_shrub_name[idx] = p.id if not p.flags[PlantRawFlags::GRASS] and not p.flags[PlantRawFlags::TREE] } end if material != :any - mat = match_rawname(material, @raw_shrub_name.values) - unless wantmat = @raw_shrub_name.index(mat) + mat = match_rawname(material, @raws_shrub_name.values) + unless wantmat = @raws_shrub_name.index(mat) raise "invalid shrub material #{material}" end end @@ -48,7 +48,7 @@ def self.cuttrees(material=nil, count_max=100) cnt[plant.material] += 1 } cnt.sort_by { |mat, c| c }.each { |mat, c| - name = @raw_tree_name[mat] + name = @raws_tree_name[mat] puts " #{name} #{c}" } else @@ -79,7 +79,7 @@ def self.growtrees(material=nil, count_max=100) cnt[plant.material] += 1 } cnt.sort_by { |mat, c| c }.each { |mat, c| - name = @raw_tree_name[mat] + name = @raws_tree_name[mat] puts " #{name} #{c}" } else @@ -91,7 +91,50 @@ def self.growtrees(material=nil, count_max=100) cnt += 1 break if cnt == count_max } - puts "Grown #{cnt} sapling" + puts "Grown #{cnt} saplings" + end +end + +def self.growcrops(material=nil, count_max=100) + @raws_plant_name ||= {} + @raws_plant_growdur ||= {} + if @raws_plant_name.empty? + df.world.raws.plants.all.each_with_index { |p, idx| + @raws_plant_name[idx] = p.id + @raws_plant_growdur[idx] = p.growdur + } + end + + if !material + cnt = Hash.new(0) + world.items.other[ItemsOtherId::SEEDS].each { |seed| + next if not seed.flags.in_building + next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' } + next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] + cnt[seed.mat_index] += 1 + } + cnt.sort_by { |mat, c| c }.each { |mat, c| + name = world.raws.plants.all[mat].id + puts " #{name} #{c}" + } + else + if material != :any + mat = match_rawname(material, @raws_plant_name.values) + unless wantmat = @raws_plant_name.index(mat) + raise "invalid plant material #{material}" + end + end + + cnt = 0 + world.items.other[ItemsOtherId::SEEDS].each { |seed| + next if wantmat and seed.mat_index != wantmat + next if not seed.flags.in_building + next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' } + next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] + seed.grow_counter = @raws_plant_growdur[seed.mat_index] + cnt += 1 + } + puts "Grown #{cnt} crops" end end end From d1ea97c272bf64ea012688c9fe13f36c4af85078 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 25 Apr 2012 01:20:35 +0200 Subject: [PATCH 32/72] ruby: README --- plugins/ruby/README | 116 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 plugins/ruby/README diff --git a/plugins/ruby/README b/plugins/ruby/README new file mode 100644 index 000000000..eddbbbc91 --- /dev/null +++ b/plugins/ruby/README @@ -0,0 +1,116 @@ +This plugins embeds a ruby interpreter inside DFHack (ie inside Dwarf Fortress). + +The plugin maps all the structures available in library/xml/ to ruby objects. + +These objects are described in ruby-autogen.rb, they are all in the DFHack:: +module. The toplevel 'df' method returs the DFHack module. + +The plugin does *not* map most of dfhack methods (MapCache, ...) ; only direct +access to the raw DF data structures in memory is provided. + +Some library methods are stored in the ruby.rb file, with shortcuts to read a +map block, find an unit or an item, etc. + +Global objects are stored in the GlobalObjects class ; each object accessor is +mirrored as a DFHack module method. + +The ruby plugin defines 2 dfhack console commands: + rb_load ; load a ruby script. Ex: rb_load hack/plants.rb (no quotes) + rb_eval ; evaluate a ruby expression, show the result in the +console. Ex: rb_eval df.find_unit.name.first_name +You can use single-quotes for strings ; avoid double-quotes that are parsed +and removed by the dfhack console. + +The plugin also interfaces with dfhack 'onupdate' hook. +To register ruby code to be run every graphic frame, use: + handle = df.onupdate_register { puts 'i love flood' } +To stop being called, use: + df.onupdate_unregister handle + + +Exemples +-------- + +For more complex exemples, check the ruby/plugins/ folder. + +Show info on the currently selected unit ('v' or 'k' DF menu) + p df.find_unit.flags1 + +Set a custom nickname to unit with id '123' + df.find_unit(123).name.nickname = 'moo' + +Show current unit profession + p DFHack::Profession::ENUM[df.find_unit.profession] + +Center the screen on unit '123' + df.center_viewscreen(df.find_unit(123)) + +Find an item at a given position, show its C++ classname + df.find_item(df.cursor)._rtti_classname + +Find the raws name of the plant under cursor + plant = df.world.plants.all.find { |p| df.at_cursor?(p) } + df.world.raws.plants.all[plant.mat_index].id + +Dig a channel under the cursor + df.map_designation_at(df.cursor).dig = TileDigDesignation::Channel + df.map_block_at(df.cursor).flags.designated = true + + +Compilation +----------- + +The plugin consists of the ruby.rb file including user comfort functions ; +ruby-memstruct.rb describing basic classes used by the autogenerated code, and +embedded at the beginnig of ruby-autogen.rb, and the generated code. + +The generated code is generated by codegen.pl, which takes the codegen.out.xml +file as input. + +One of the limitations of the xml file is that it does not include structure +offsets, as they depend on the compiler. To overcome that, codegen runs in two +passes. The first pass generates a ruby-autogen.cpp file, that will output the +structure offsets ; the second pass will generate the ruby-autogen.rb using the +output of the compiled ruby-autogen.cpp. + +For exemple, from + + + + + +We generate the cpp + printf("%s = %d", "offsetof(df::unit, language_name)", offsetof(df::unit, language_name)); + printf("%s = %d", "offsetof(df::unit, custom_profession)", offsetof(df::unit, custom_profession)); + printf("%s = %d", "offsetof(df::unit, profession)", offsetof(df::unit, profession)); + +Which generates (on linux) + offsetof(df::unit, name) = 0 + offsetof(df::unit, custom_profession) = 60 + offsetof(df::unit, profession) = 64 + +Which generates + class Unit < MemHack::Compound + field(:name, 0) { global :LanguageName } + field(:custom_profession, 60) { stl_string } + field(:profession, 64) { number 16, true } + +The field method has 2 arguments: the name of the method and the member offset ; +the block specifies the member type. See ruby-memstruct.rb for more information. +Primitive type access is done through native methods in ruby.cpp (vector length, +raw memory access, etc) + +MemHack::Pointers have a special behavior: they accept and forward any method to +the pointed object. To retrieve the pointed object directly, use the _getv +method. Null pointers resolve to 'nil'. +Beware, invalid pointers (!= 0) will crash the plugin and the game. + +This allows to use code such as 'df.world.units.all[0].pos', with all[0] being +really a Pointer (with no 'pos' method). + + +Todo +---- + +New C++ object allocation ; update vector/compound fields with pointers ; bind +onstatechange From b0acad6d5918e3088f0c70aeee9f1a464828aa82 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 25 Apr 2012 17:18:24 +0200 Subject: [PATCH 33/72] ruby: use index-enum xml attr, fix bitfield[i] = 0 --- plugins/ruby/codegen.pl | 12 +++++++- plugins/ruby/ruby-memstruct.rb | 50 ++++++++++++++++++++++++++++------ plugins/ruby/ruby.cpp | 2 +- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 9a34fe188..340b8acc5 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -315,6 +315,7 @@ sub render_item_container { my $subtype = $item->getAttribute('ld:subtype'); my $rbmethod = join('_', split('-', $subtype)); my $tg = $item->findnodes('child::ld:item')->[0]; + my $indexenum = $item->getAttribute('index-enum'); if ($tg) { if ($rbmethod eq 'df_linked_list') { push @lines_rb, "$rbmethod {"; @@ -326,6 +327,9 @@ sub render_item_container { render_item($tg, $pns); }; push @lines_rb, "}"; + } elsif ($indexenum) { + $indexenum = rb_ucase($indexenum); + push @lines_rb, "$rbmethod(:$indexenum)"; } else { push @lines_rb, "$rbmethod"; } @@ -354,7 +358,13 @@ sub render_item_staticarray { my $count = $item->getAttribute('count'); my $tg = $item->findnodes('child::ld:item')->[0]; my $tglen = get_tglen($tg, $pns); - push @lines_rb, "static_array($count, $tglen) {"; + my $indexenum = $item->getAttribute('index-enum'); + if ($indexenum) { + $indexenum = rb_ucase($indexenum); + push @lines_rb, "static_array($count, $tglen, :$indexenum) {"; + } else { + push @lines_rb, "static_array($count, $tglen) {"; + } indent_rb { render_item($tg, $pns); }; diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 12f261cd2..b2c376836 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -36,8 +36,8 @@ class Compound < MemStruct def pointer_ary(tglen) PointerAry.new(tglen, yield) end - def static_array(len, tglen) - StaticArray.new(tglen, len, yield) + def static_array(len, tglen, indexenum=nil) + StaticArray.new(tglen, len, indexenum, yield) end def static_string(len) StaticString.new(len) @@ -61,8 +61,8 @@ class Compound < MemStruct StlDeque.new(tglen, yield) end - def df_flagarray - DfFlagarray.new + def df_flagarray(indexenum=nil) + DfFlagarray.new(indexenum) end def df_array(tglen) DfArray.new(tglen, yield) @@ -250,11 +250,22 @@ class PointerAry < MemStruct def inspect ; "#" ; end end +module IndexEnum + def indexenum(idx) + if idx.kind_of?(::Symbol) or idx.kind_of?(::String) and _indexenum + DFHack.const_get(_indexenum).const_get(idx) || idx + else + idx + end + end +end class StaticArray < MemStruct - attr_accessor :_tglen, :_length, :_tg - def initialize(tglen, length, tg) + include IndexEnum + attr_accessor :_tglen, :_length, :_indexenum, :_tg + def initialize(tglen, length, indexenum, tg) @_tglen = tglen @_length = length + @_indexenum = indexenum @_tg = tg end def _set(a) @@ -266,17 +277,26 @@ class StaticArray < MemStruct @_tg._at(@_memaddr + i*@_tglen) if i >= 0 and i < @_length end def [](i) + i = indexenum(i) i += @_length if i < 0 _tgat(i)._get end def []=(i, v) + i = indexenum(i) i += @_length if i < 0 _tgat(i)._set(v) end include Enumerable def each ; (0...length).each { |i| yield self[i] } ; end - def inspect ; to_a.inspect ; end + def inspect + if _indexenum + e = DFHack.const_get(_indexenum)::ENUM + '[' + (0...length).map { |i| "#{e[i]}=#{self[i].inspect}" }.join(' ') + ']' + else + to_a.inspect + end + end end class StaticString < MemStruct attr_accessor :_length @@ -431,6 +451,11 @@ class StlDeque < MemStruct end class DfFlagarray < MemStruct + include IndexEnum + attr_accessor :_indexenum + def initialize(indexenum) + @_indexenum = indexenum + end def length DFHack.memory_bitarray_length(@_memaddr) end @@ -439,10 +464,12 @@ class DfFlagarray < MemStruct DFHack.memory_bitarray_resize(@_memaddr, len) end def [](idx) + idx = indexenum(idx) idx += length if idx < 0 DFHack.memory_bitarray_isset(@_memaddr, idx) if idx >= 0 and idx < length end def []=(idx, v) + idx = indexenum(idx) idx += length if idx < 0 if idx >= length or idx < 0 raise 'invalid idx' @@ -453,7 +480,14 @@ class DfFlagarray < MemStruct include Enumerable def each ; (0...length).each { |i| yield self[i] } ; end - def inspect ; to_a.inspect ; end + def inspect + if _indexenum + e = DFHack.const_get(_indexenum)::ENUM + '[' + (0...length).map { |i| if self[i] ; e[i] || i ; end }.compact.join(' ') + ']' + else + to_a.inspect + end + end end class DfArray < Compound attr_accessor :_tglen, :_tg diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index f291b757e..e0487bfac 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -278,7 +278,7 @@ static void df_rubythread(void *p) } -#define BOOL_ISFALSE(v) ((v) == Qfalse || (v) == Qnil || (v) == FIX2INT(0)) +#define BOOL_ISFALSE(v) ((v) == Qfalse || (v) == Qnil || (v) == INT2FIX(0)) // main DFHack ruby module static VALUE rb_cDFHack; From bd2e6e74de59095d91e7e9780f3b7ef589b60c1f Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 25 Apr 2012 19:22:04 +0200 Subject: [PATCH 34/72] ruby: add manual c++ object allocator --- plugins/ruby/ruby-memstruct.rb | 51 +++++++++++-- plugins/ruby/ruby.cpp | 53 ++++++++++---- plugins/ruby/ruby.rb | 130 ++++++++++++++++----------------- 3 files changed, 146 insertions(+), 88 deletions(-) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index b2c376836..cb24b19a4 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -17,6 +17,13 @@ class Compound < MemStruct define_method(name) { struct._at(@_memaddr+offset)._get } define_method("#{name}=") { |v| struct._at(@_memaddr+offset)._set(v) } end + def _fields_ancestors + if superclass.respond_to?(:_fields_ancestors) + superclass._fields_ancestors + _fields.to_a + else + _fields.to_a + end + end def number(bits, signed) Number.new(bits, signed) @@ -86,22 +93,41 @@ class Compound < MemStruct def sizeof(n) @_sizeof = n end + + # allocate a new c++ object, return its ruby wrapper + def cpp_new + ptr = DFHack.malloc(_sizeof) + if _rtti_classname and vt = DFHack.rtti_getvtable(_rtti_classname) + DFHack.memory_write_int32(ptr, vt) + # TODO call constructor + end + new._at(ptr)._cpp_init + end + end + def _cpp_init + _fields.each { |n, o, s| + case s + when StlString + DFHack.memory_stlstring_init(@_memaddr+o) + when StlVector32 + # englobes all vectors + DFHack.memory_vector_init(@_memaddr+o) + when Compound + send(n)._cpp_init + end + } + self end def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end def _fields ; self.class._fields.to_a ; end + def _fields_ancestors ; self.class._fields_ancestors.to_a ; end def _rtti_classname ; self.class._rtti_classname ; end def _sizeof ; self.class._sizeof ; end def inspect cn = self.class.name.sub(/^DFHack::/, '') cn << ' @' << ('0x%X' % _memaddr) if cn != '' out = "#<#{cn}" - fields = _fields - cls = self.class.superclass - while cls.respond_to?(:_fields) - fields += cls._fields.to_a - cls = cls.superclass - end - fields.each { |n, o, s| + _fields_ancestors.each { |n, o, s| out << ' ' if out.length != 0 and out[-1, 1] != ' ' if out.length > 1024 out << '...' @@ -209,6 +235,17 @@ class Pointer < MemStruct @_tg._at(addr)._get end + # XXX shaky... + def _set(v) + if v.kind_of?(Pointer) + DFHack.memory_write_int32(@_memaddr, v._getv) + elsif v.kind_of?(MemStruct) + DFHack.memory_write_int32(@_memaddr, v._memaddr) + else + _getv._set(v) + end + end + # these ruby Object methods should be forwarded to the ptr undef id undef type diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index e0487bfac..27ae99d80 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -413,7 +413,11 @@ static VALUE rb_dfget_vtable_ptr(VALUE self, VALUE objptr) static VALUE rb_dfmalloc(VALUE self, VALUE len) { - return rb_uint2inum((long)malloc(FIX2INT(len))); + char *ptr = (char*)malloc(FIX2INT(len)); + if (!ptr) + rb_raise(rb_eRuntimeError, "no memory"); + memset(ptr, 0, FIX2INT(len)); + return rb_uint2inum((long)ptr); } static VALUE rb_dffree(VALUE self, VALUE ptr) @@ -422,17 +426,11 @@ static VALUE rb_dffree(VALUE self, VALUE ptr) return Qtrue; } - // memory reading (buffer) static VALUE rb_dfmemory_read(VALUE self, VALUE addr, VALUE len) { return rb_str_new((char*)rb_num2ulong(addr), rb_num2ulong(len)); } -static VALUE rb_dfmemory_read_stlstring(VALUE self, VALUE addr) -{ - std::string *s = (std::string*)rb_num2ulong(addr); - return rb_str_new(s->c_str(), s->length()); -} // memory reading (integers/floats) static VALUE rb_dfmemory_read_int8(VALUE self, VALUE addr) @@ -464,13 +462,6 @@ static VALUE rb_dfmemory_write(VALUE self, VALUE addr, VALUE raw) return Qtrue; } -static VALUE rb_dfmemory_write_stlstring(VALUE self, VALUE addr, VALUE val) -{ - std::string *s = (std::string*)rb_num2ulong(addr); - int strlen = FIX2INT(rb_funcall(val, rb_intern("length"), 0)); - s->assign(rb_string_value_ptr(&val), strlen); - return Qtrue; -} // memory writing (integers/floats) static VALUE rb_dfmemory_write_int8(VALUE self, VALUE addr, VALUE val) @@ -496,7 +487,35 @@ static VALUE rb_dfmemory_write_float(VALUE self, VALUE addr, VALUE val) } +// stl::string +static VALUE rb_dfmemory_stlstring_init(VALUE self, VALUE addr) +{ + // XXX THIS IS TERRIBLE + std::string *ptr = new std::string; + memcpy((void*)rb_num2ulong(addr), (void*)ptr, sizeof(*ptr)); + return Qtrue; +} +static VALUE rb_dfmemory_read_stlstring(VALUE self, VALUE addr) +{ + std::string *s = (std::string*)rb_num2ulong(addr); + return rb_str_new(s->c_str(), s->length()); +} +static VALUE rb_dfmemory_write_stlstring(VALUE self, VALUE addr, VALUE val) +{ + std::string *s = (std::string*)rb_num2ulong(addr); + int strlen = FIX2INT(rb_funcall(val, rb_intern("length"), 0)); + s->assign(rb_string_value_ptr(&val), strlen); + return Qtrue; +} + + // vector access +static VALUE rb_dfmemory_vec_init(VALUE self, VALUE addr) +{ + std::vector *ptr = new std::vector; + memcpy((void*)rb_num2ulong(addr), (void*)ptr, sizeof(*ptr)); + return Qtrue; +} // vector static VALUE rb_dfmemory_vec8_length(VALUE self, VALUE addr) { @@ -646,19 +665,21 @@ static void ruby_bind_dfhack(void) { rb_define_const(rb_cDFHack, "REBASE_DELTA", rb_dfrebase_delta()); rb_define_singleton_method(rb_cDFHack, "memory_read", RUBY_METHOD_FUNC(rb_dfmemory_read), 2); - rb_define_singleton_method(rb_cDFHack, "memory_read_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_read_stlstring), 1); rb_define_singleton_method(rb_cDFHack, "memory_read_int8", RUBY_METHOD_FUNC(rb_dfmemory_read_int8), 1); rb_define_singleton_method(rb_cDFHack, "memory_read_int16", RUBY_METHOD_FUNC(rb_dfmemory_read_int16), 1); rb_define_singleton_method(rb_cDFHack, "memory_read_int32", RUBY_METHOD_FUNC(rb_dfmemory_read_int32), 1); rb_define_singleton_method(rb_cDFHack, "memory_read_float", RUBY_METHOD_FUNC(rb_dfmemory_read_float), 1); rb_define_singleton_method(rb_cDFHack, "memory_write", RUBY_METHOD_FUNC(rb_dfmemory_write), 2); - rb_define_singleton_method(rb_cDFHack, "memory_write_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_write_stlstring), 2); rb_define_singleton_method(rb_cDFHack, "memory_write_int8", RUBY_METHOD_FUNC(rb_dfmemory_write_int8), 2); rb_define_singleton_method(rb_cDFHack, "memory_write_int16", RUBY_METHOD_FUNC(rb_dfmemory_write_int16), 2); rb_define_singleton_method(rb_cDFHack, "memory_write_int32", RUBY_METHOD_FUNC(rb_dfmemory_write_int32), 2); rb_define_singleton_method(rb_cDFHack, "memory_write_float", RUBY_METHOD_FUNC(rb_dfmemory_write_float), 2); + rb_define_singleton_method(rb_cDFHack, "memory_stlstring_init", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_init), 1); + rb_define_singleton_method(rb_cDFHack, "memory_read_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_read_stlstring), 1); + rb_define_singleton_method(rb_cDFHack, "memory_write_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_write_stlstring), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector_init", RUBY_METHOD_FUNC(rb_dfmemory_vec_init), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector8_length", RUBY_METHOD_FUNC(rb_dfmemory_vec8_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector8_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_ptrat), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector8_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insert), 3); diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 49c2e5024..02af374d3 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -31,11 +31,11 @@ module DFHack nil end - def p(*a) - a.each { |e| - puts e.inspect - } - end + def p(*a) + a.each { |e| + puts_err e.inspect + } + end end # register a callback to be called every gframe or more @@ -114,61 +114,61 @@ module DFHack end end - def map_designation_at(x, y=nil, z=nil) - x = x.pos if x.respond_to?(:pos) - x, y, z = x.x, x.y, x.z if x.respond_to?(:x) - if b = map_block_at(x, y, z) - b.designation[x%16][y%16] - end - end + def map_designation_at(x, y=nil, z=nil) + x = x.pos if x.respond_to?(:pos) + x, y, z = x.x, x.y, x.z if x.respond_to?(:x) + if b = map_block_at(x, y, z) + b.designation[x%16][y%16] + end + end - def map_occupancy_at(x, y=nil, z=nil) - x = x.pos if x.respond_to?(:pos) - x, y, z = x.x, x.y, x.z if x.respond_to?(:x) - if b = map_block_at(x, y, z) - b.occupancy[x%16][y%16] - end - end + def map_occupancy_at(x, y=nil, z=nil) + x = x.pos if x.respond_to?(:pos) + x, y, z = x.x, x.y, x.z if x.respond_to?(:x) + if b = map_block_at(x, y, z) + b.occupancy[x%16][y%16] + end + end - # yields every map block - def each_map_block - (0...world.map.x_count_block).each { |xb| - xl = world.map.block_index[xb] - (0...world.map.y_count_block).each { |yb| - yl = xl[yb] - (0...world.map.z_count_block).each { |z| - p = yl[z] - yield p._getv if p._getp != 0 - } - } - } - end + # yields every map block + def each_map_block + (0...world.map.x_count_block).each { |xb| + xl = world.map.block_index[xb] + (0...world.map.y_count_block).each { |yb| + yl = xl[yb] + (0...world.map.z_count_block).each { |z| + p = yl[z] + yield p._getv if p._getp != 0 + } + } + } + end - # yields every map block for a given z level - def each_map_block_z(z) - (0...world.map.x_count_block).each { |xb| - xl = world.map.block_index[xb] - (0...world.map.y_count_block).each { |yb| - p = xl[yb][z] - yield p._getv if p._getp != 0 - } - } - end + # yields every map block for a given z level + def each_map_block_z(z) + (0...world.map.x_count_block).each { |xb| + xl = world.map.block_index[xb] + (0...world.map.y_count_block).each { |yb| + p = xl[yb][z] + yield p._getv if p._getp != 0 + } + } + end - # return true if the argument is under the cursor - def at_cursor?(obj) - same_pos?(obj, cursor) - end + # return true if the argument is under the cursor + def at_cursor?(obj) + same_pos?(obj, cursor) + end - # returns true if both arguments are at the same x/y/z - def same_pos?(pos1, pos2) - pos1 = pos1.pos if pos1.respond_to?(:pos) - pos2 = pos2.pos if pos2.respond_to?(:pos) - pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z - end + # returns true if both arguments are at the same x/y/z + def same_pos?(pos1, pos2) + pos1 = pos1.pos if pos1.respond_to?(:pos) + pos2 = pos2.pos if pos2.respond_to?(:pos) + pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z + end - # center the DF screen on something - # updates the cursor position if visible + # center the DF screen on something + # updates the cursor position if visible def center_viewscreen(x, y=nil, z=nil) x = x.pos if x.respond_to?(:pos) x, y, z = x.x, x.y, x.z if x.respond_to?(:x) @@ -207,10 +207,10 @@ module DFHack # add an announcement # color = integer, bright = bool - def add_announcement(str, color=0, bright=false) + def add_announcement(str, color=7, bright=false) cont = false while str.length > 0 - rep = Report.cpp_alloc + rep = Report.cpp_new rep.color = color rep.bright = ((bright && bright != 0) ? 1 : 0) rep.year = cur_year @@ -228,15 +228,15 @@ module DFHack end end - # try to match a user-specified name to one from the raws - # uses case-switching and substring matching - # eg match_rawname('coal', ['COAL_BITUMINOUS', 'BAUXITE']) => 'COAL_BITUMINOUS' - def match_rawname(name, rawlist) - rawlist.each { |r| return r if name == r } - rawlist.each { |r| return r if name.downcase == r.downcase } - may = rawlist.find_all { |r| r.downcase.index(name.downcase) } - may.first if may.length == 1 - end + # try to match a user-specified name to one from the raws + # uses case-switching and substring matching + # eg match_rawname('coal', ['COAL_BITUMINOUS', 'BAUXITE']) => 'COAL_BITUMINOUS' + def match_rawname(name, rawlist) + rawlist.each { |r| return r if name == r } + rawlist.each { |r| return r if name.downcase == r.downcase } + may = rawlist.find_all { |r| r.downcase.index(name.downcase) } + may.first if may.length == 1 + end def test puts "starting" From 7a8db179ab9909abd29b3e595ee2e906da5fd1e9 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 25 Apr 2012 20:21:09 +0200 Subject: [PATCH 35/72] ruby: fix plugin/plant.rb, add onstatechange hook, use console proxy, general cleanup, enable build by default --- plugins/CMakeLists.txt | 2 +- plugins/ruby/README | 3 +- plugins/ruby/plugins/plant.rb | 86 +++++++++++++++----------- plugins/ruby/ruby.cpp | 113 +++++++++++++++------------------- plugins/ruby/ruby.rb | 41 ++++++------ 5 files changed, 122 insertions(+), 123 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 66e5a650e..a86885ef9 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -36,7 +36,7 @@ if (BUILD_DWARFEXPORT) add_subdirectory (dwarfexport) endif() -OPTION(BUILD_RUBY "Build ruby binding." OFF) +OPTION(BUILD_RUBY "Build ruby binding." ON) if (BUILD_RUBY) add_subdirectory (ruby) endif() diff --git a/plugins/ruby/README b/plugins/ruby/README index eddbbbc91..62a622843 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -112,5 +112,4 @@ really a Pointer (with no 'pos' method). Todo ---- -New C++ object allocation ; update vector/compound fields with pointers ; bind -onstatechange +Correct c++ object (de)allocation (call ctor etc) diff --git a/plugins/ruby/plugins/plant.rb b/plugins/ruby/plugins/plant.rb index 2182e4d55..ffc415eda 100644 --- a/plugins/ruby/plugins/plant.rb +++ b/plugins/ruby/plugins/plant.rb @@ -42,10 +42,12 @@ def self.cuttrees(material=nil, count_max=100) if !material # list trees cnt = Hash.new(0) - each_tree { |plant| - next if plant.grow_counter < SaplingToTreeAge - next if find_map_designation(plant).hidden - cnt[plant.material] += 1 + suspend { + each_tree { |plant| + next if plant.grow_counter < SaplingToTreeAge + next if map_designation_at(plant).hidden + cnt[plant.material] += 1 + } } cnt.sort_by { |mat, c| c }.each { |mat, c| name = @raws_tree_name[mat] @@ -53,17 +55,19 @@ def self.cuttrees(material=nil, count_max=100) } else cnt = 0 - each_tree(material) { |plant| - next if plant.grow_counter < SaplingToTreeAge - b = find_map_block(plant) - d = b.designation[plant.pos.x%16][plant.pos.y%16] - next if d.hidden - if d.dig == TileDigDesignation::No - d.dig = TileDigDesignation::Default - b.flags.designated = true - cnt += 1 - break if cnt == count_max - end + suspend { + each_tree(material) { |plant| + next if plant.grow_counter < SaplingToTreeAge + b = map_block_at(plant) + d = b.designation[plant.pos.x%16][plant.pos.y%16] + next if d.hidden + if d.dig == TileDigDesignation::No + d.dig = TileDigDesignation::Default + b.flags.designated = true + cnt += 1 + break if cnt == count_max + end + } } puts "Updated #{cnt} plant designations" end @@ -73,10 +77,12 @@ def self.growtrees(material=nil, count_max=100) if !material # list plants cnt = Hash.new(0) - each_tree { |plant| - next if plant.grow_counter >= SaplingToTreeAge - next if find_map_designation(plant).hidden - cnt[plant.material] += 1 + suspend { + each_tree { |plant| + next if plant.grow_counter >= SaplingToTreeAge + next if map_designation_at(plant).hidden + cnt[plant.material] += 1 + } } cnt.sort_by { |mat, c| c }.each { |mat, c| name = @raws_tree_name[mat] @@ -84,12 +90,14 @@ def self.growtrees(material=nil, count_max=100) } else cnt = 0 - each_tree(material) { |plant| - next if plant.grow_counter >= SaplingToTreeAge - next if find_map_designation(plant).hidden - p.grow_counter = SaplingToTreeAge - cnt += 1 - break if cnt == count_max + suspend { + each_tree(material) { |plant| + next if plant.grow_counter >= SaplingToTreeAge + next if map_designation_at(plant).hidden + plant.grow_counter = SaplingToTreeAge + cnt += 1 + break if cnt == count_max + } } puts "Grown #{cnt} saplings" end @@ -107,11 +115,13 @@ def self.growcrops(material=nil, count_max=100) if !material cnt = Hash.new(0) - world.items.other[ItemsOtherId::SEEDS].each { |seed| - next if not seed.flags.in_building - next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' } - next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] - cnt[seed.mat_index] += 1 + suspend { + world.items.other[ItemsOtherId::SEEDS].each { |seed| + next if not seed.flags.in_building + next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' } + next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] + cnt[seed.mat_index] += 1 + } } cnt.sort_by { |mat, c| c }.each { |mat, c| name = world.raws.plants.all[mat].id @@ -126,13 +136,15 @@ def self.growcrops(material=nil, count_max=100) end cnt = 0 - world.items.other[ItemsOtherId::SEEDS].each { |seed| - next if wantmat and seed.mat_index != wantmat - next if not seed.flags.in_building - next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' } - next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] - seed.grow_counter = @raws_plant_growdur[seed.mat_index] - cnt += 1 + suspend { + world.items.other[ItemsOtherId::SEEDS].each { |seed| + next if wantmat and seed.mat_index != wantmat + next if not seed.flags.in_building + next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' } + next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] + seed.grow_counter = @raws_plant_growdur[seed.mat_index] + cnt += 1 + } } puts "Grown #{cnt} crops" end diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 27ae99d80..159b48a75 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -30,7 +30,6 @@ enum RB_command { RB_IDLE, RB_INIT, RB_DIE, - RB_LOAD, RB_EVAL, RB_CUSTOM, }; @@ -70,10 +69,6 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector lock(); if (!r_thread) + // raced with plugin_shutdown ? return CR_OK; r_type = RB_EVAL; - r_command = "DFHack.onupdate"; + r_command = command; m_irun->unlock(); + // could use a condition_variable or something... while (r_type != RB_IDLE) tthread::this_thread::yield(); + // XXX non-atomic with previous r_type change check ret = r_result; m_irun->lock(); @@ -124,36 +121,49 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) return ret; } -static command_result df_rubyload(color_ostream &out, std::vector & parameters) +static command_result plugin_eval_rb(std::string &command) { - command_result ret; + return plugin_eval_rb(command.c_str()); +} + +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +{ + if (!onupdate_active) + return CR_OK; + return plugin_eval_rb("DFHack.onupdate"); +} + +DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event e) +{ + std::string cmd = "DFHack.onstatechange "; + switch (e) { +#define SCASE(s) case SC_ ## s : cmd += ":" # s ; break + SCASE(WORLD_LOADED); + SCASE(WORLD_UNLOADED); + SCASE(MAP_LOADED); + SCASE(MAP_UNLOADED); + SCASE(VIEWSCREEN_CHANGED); + SCASE(CORE_INITIALIZED); + SCASE(BEGIN_UNLOAD); + } + + return plugin_eval_rb(cmd); +} + +static command_result df_rubyload(color_ostream &out, std::vector & parameters) +{ if (parameters.size() == 1 && (parameters[0] == "help" || parameters[0] == "?")) { out.print("This command loads the ruby script whose path is given as parameter, and run it.\n"); return CR_OK; } - // serialize 'accesses' to the ruby thread - m_mutex->lock(); - if (!r_thread) - // raced with plugin_shutdown ? - return CR_OK; - - r_type = RB_LOAD; - r_command = parameters[0].c_str(); - m_irun->unlock(); + std::string cmd = "load '"; + cmd += parameters[0]; // TODO escape singlequotes + cmd += "'"; - // could use a condition_variable or something... - while (r_type != RB_IDLE) - tthread::this_thread::yield(); - // XXX non-atomic with previous r_type change check - ret = r_result; - - m_irun->lock(); - m_mutex->unlock(); - - return ret; + return plugin_eval_rb(cmd); } static command_result df_rubyeval(color_ostream &out, std::vector & parameters) @@ -167,32 +177,14 @@ static command_result df_rubyeval(color_ostream &out, std::vector } std::string full = ""; - full += "DFHack.puts(("; for (unsigned i=0 ; ilock(); - if (!r_thread) - return CR_OK; - - r_type = RB_EVAL; - r_command = full.c_str(); - m_irun->unlock(); - - while (r_type != RB_IDLE) - tthread::this_thread::yield(); - - ret = r_result; - - m_irun->lock(); - m_mutex->unlock(); - - return ret; + return plugin_eval_rb(full); } @@ -219,6 +211,8 @@ static void dump_rb_error(void) Core::printerr(" %s\n", rb_string_value_ptr(&s)); } +static color_ostream_proxy *console_proxy; + // ruby thread main loop static void df_rubythread(void *p) { @@ -233,6 +227,8 @@ static void df_rubythread(void *p) // create the ruby objects to map DFHack to ruby methods ruby_bind_dfhack(); + console_proxy = new color_ostream_proxy(Core::getInstance().getConsole()); + r_result = CR_OK; r_type = RB_IDLE; @@ -251,13 +247,6 @@ static void df_rubythread(void *p) ruby_finalize(); break; - case RB_LOAD: - state = 0; - rb_load_protect(rb_str_new2(r_command), Qfalse, &state); - if (state) - dump_rb_error(); - break; - case RB_EVAL: state = 0; rb_eval_string_protect(r_command, &state); @@ -339,9 +328,7 @@ static VALUE rb_dfrebase_delta(void) static VALUE rb_dfprint_str(VALUE self, VALUE s) { - // TODO color_ostream proxy yadda yadda - //getcore().con.print("%s", rb_string_value_ptr(&s)); - Core::printerr("%s", rb_string_value_ptr(&s)); + console_proxy->print("%s", rb_string_value_ptr(&s)); return Qnil; } @@ -622,7 +609,7 @@ static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr, VALUE idx) static VALUE rb_dfmemory_bitarray_length(VALUE self, VALUE addr) { DFHack::BitArray *b = (DFHack::BitArray*)rb_num2ulong(addr); - return rb_uint2inum(b->size*8); // b->size is in bytes + return rb_uint2inum(b->size*8); // b->size is in bytes } static VALUE rb_dfmemory_bitarray_resize(VALUE self, VALUE addr, VALUE sz) { diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 02af374d3..9a5ab0288 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -55,9 +55,30 @@ module DFHack # this method is called by dfhack every 'onupdate' if onupdate_active is true def onupdate + @onupdate_list ||= [] @onupdate_list.each { |cb| cb.call } end + # register a callback to be called every gframe or more + # ex: DFHack.onstatechange_register { |newstate| puts "state changed to #{newstate}" } + def onstatechange_register(&b) + @onstatechange_list ||= [] + @onstatechange_list << b + @onstatechange_list.last + end + + # delete the callback for onstatechange ; use the value returned by onstatechange_register + def onstatechange_unregister(b) + @onstatechange_list.delete b + end + + # this method is called by dfhack every 'onstatechange' + def onstatechange(newstate) + @onstatechange_list ||= [] + @onstatechange_list.each { |cb| cb.call(newstate) } + end + + # return an Unit # with no arg, return currently selected unit in df UI ('v' or 'k' menu) # with numeric arg, search unit by unit.id @@ -237,26 +258,6 @@ module DFHack may = rawlist.find_all { |r| r.downcase.index(name.downcase) } may.first if may.length == 1 end - - def test - puts "starting" - - suspend { - puts "cursor pos: #{cursor.x} #{cursor.y} #{cursor.z}" - - if u = find_unit - puts "selected unit id: #{u.id}" - end - - if b = map_block_at(cursor) - map_designation_at(cursor).dig = TileDigDesignation::Default - b.flags.designated = true - puts "dug cursor tile" - end - } - - puts "done" - end end end From 03925c923aa55eb5376d89700406196ac77c9984 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 26 Apr 2012 14:16:47 +0200 Subject: [PATCH 36/72] ruby: fix new c++ obj initialization, use xml init-value --- plugins/ruby/README | 2 +- plugins/ruby/codegen.pl | 9 +++++++ plugins/ruby/ruby-memstruct.rb | 49 ++++++++++++++++++++++------------ plugins/ruby/ruby.rb | 6 ++--- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/plugins/ruby/README b/plugins/ruby/README index 62a622843..b9ea35f95 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -112,4 +112,4 @@ really a Pointer (with no 'pos' method). Todo ---- -Correct c++ object (de)allocation (call ctor etc) +Correct c++ object (de)allocation (call ctor etc) ; ability to call vtable methods diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 340b8acc5..f0373cd8b 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -258,6 +258,12 @@ sub render_item_number { my ($item, $pns) = @_; my $subtype = $item->getAttribute('ld:subtype'); + my $initvalue = $item->getAttribute('init-value'); + + $initvalue = 1 if ($initvalue and $initvalue eq 'true'); + # XXX needs pre-declaration of the enum... + $initvalue = rb_ucase($item->getAttribute('type-name')) . '::' . $initvalue if ($subtype and $subtype eq 'enum' and $initvalue =~ /[a-zA-Z]/); + $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'enum' or $subtype eq 'bitfield'); $subtype = 'int32_t' if (!$subtype); @@ -279,9 +285,12 @@ sub render_item_number { push @lines_rb, 'number 8, true'; } elsif ($subtype eq 's-float') { push @lines_rb, 'float'; + return; } else { print "no render number $subtype\n"; + return; } + $lines_rb[$#lines_rb] .= ", $initvalue" if ($initvalue); } sub render_item_compound { diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index cb24b19a4..d5f6aad32 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -4,6 +4,7 @@ class MemStruct attr_accessor :_memaddr def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end def _get ; self ; end + def _cpp_init ; end def inspect ; _get.inspect ; end end @@ -25,8 +26,8 @@ class Compound < MemStruct end end - def number(bits, signed) - Number.new(bits, signed) + def number(bits, signed, initvalue=nil) + Number.new(bits, signed, initvalue) end def float Float.new @@ -101,22 +102,13 @@ class Compound < MemStruct DFHack.memory_write_int32(ptr, vt) # TODO call constructor end - new._at(ptr)._cpp_init + o = new._at(ptr) + o._cpp_init + o end end def _cpp_init - _fields.each { |n, o, s| - case s - when StlString - DFHack.memory_stlstring_init(@_memaddr+o) - when StlVector32 - # englobes all vectors - DFHack.memory_vector_init(@_memaddr+o) - when Compound - send(n)._cpp_init - end - } - self + _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init } end def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end def _fields ; self.class._fields.to_a ; end @@ -152,10 +144,11 @@ class Compound < MemStruct end class Number < MemStruct - attr_accessor :_bits, :_signed - def initialize(bits, signed) + attr_accessor :_bits, :_signed, :_initvalue + def initialize(bits, signed, initvalue) @_bits = bits @_signed = signed + @_initvalue = initvalue end def _get @@ -177,6 +170,10 @@ class Number < MemStruct when 64; DFHack.memory_write_int32(@_memaddr, v & 0xffffffff) ; DFHack.memory_write_int32(@memaddr+4, v>>32) end end + + def _cpp_init + _set(@_initvalue) if @_initvalue + end end class Float < MemStruct # _get/_set defined in ruby.cpp @@ -187,6 +184,10 @@ class Float < MemStruct def _set(v) DFHack.memory_write_float(@_memaddr, v) end + + def _cpp_init + _set(0.0) + end end class BitField < MemStruct attr_accessor :_shift, :_len @@ -308,6 +309,9 @@ class StaticArray < MemStruct def _set(a) a.each_with_index { |v, i| self[i] = v } end + def _cpp_init + _length.times { |i| _tgat(i)._cpp_init } + end alias length _length alias size _length def _tgat(i) @@ -334,6 +338,7 @@ class StaticArray < MemStruct to_a.inspect end end + def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end end class StaticString < MemStruct attr_accessor :_length @@ -373,6 +378,10 @@ class StlVector32 < MemStruct v.each_with_index { |e, i| self[i] = e } # patch entries end + def _cpp_init + DFHack.memory_vector_init(@_memaddr) + end + def clear delete_at(length-1) while length > 0 end @@ -475,6 +484,10 @@ class StlString < MemStruct def _set(v) DFHack.memory_write_stlstring(@_memaddr, v) end + + def _cpp_init + DFHack.memory_stlstring_init(@_memaddr) + end end class StlDeque < MemStruct attr_accessor :_tglen, :_tg @@ -496,6 +509,7 @@ class DfFlagarray < MemStruct def length DFHack.memory_bitarray_length(@_memaddr) end + # TODO _cpp_init def size ; length ; end def resize(len) DFHack.memory_bitarray_resize(@_memaddr, len) @@ -538,6 +552,7 @@ class DfArray < Compound def length ; _length ; end def size ; _length ; end + # TODO _cpp_init def _tgat(i) @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length end diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 9a5ab0288..eec66d56b 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -228,12 +228,12 @@ module DFHack # add an announcement # color = integer, bright = bool - def add_announcement(str, color=7, bright=false) + def add_announcement(str, color=nil, bright=nil) cont = false while str.length > 0 rep = Report.cpp_new - rep.color = color - rep.bright = ((bright && bright != 0) ? 1 : 0) + rep.color = color if color + rep.bright = ((bright && bright != 0) ? 1 : 0) if bright != nil rep.year = cur_year rep.time = cur_year_tick rep.flags.continuation = cont From 9b7fbcc477eb57caca60904c951bb4104be28fc7 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 26 Apr 2012 15:37:10 +0200 Subject: [PATCH 37/72] ruby: fix warning in codegen with initvalue --- plugins/ruby/codegen.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index f0373cd8b..2a0f0b2d6 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -262,7 +262,7 @@ sub render_item_number { $initvalue = 1 if ($initvalue and $initvalue eq 'true'); # XXX needs pre-declaration of the enum... - $initvalue = rb_ucase($item->getAttribute('type-name')) . '::' . $initvalue if ($subtype and $subtype eq 'enum' and $initvalue =~ /[a-zA-Z]/); + $initvalue = rb_ucase($item->getAttribute('type-name')) . '::' . $initvalue if ($initvalue and $subtype and $subtype eq 'enum' and $initvalue =~ /[a-zA-Z]/); $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'enum' or $subtype eq 'bitfield'); $subtype = 'int32_t' if (!$subtype); From 461c7e20cdb6d08bb9f371b814d7cea3150d6641 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 26 Apr 2012 19:37:58 +0200 Subject: [PATCH 38/72] ruby: add vector#binsearch --- plugins/ruby/ruby-memstruct.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index d5f6aad32..ef3030859 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -421,6 +421,24 @@ class StlVector32 < MemStruct to_a.inspect end end + # do a binary search in an ordered vector for a specific target attribute + # ex: world.history.figures.binsearch(unit.hist_figure_id) + def binsearch(target, field=:id) + o_start = 0 + o_end = length - 1 + while o_end >= o_start + o_half = o_start + (o_end-o_start)/2 + obj = self[o_half] + oval = obj.send(field) + if oval == target + return obj + elsif oval < target + o_start = o_half+1 + else + o_end = o_half-1 + end + end + end end class StlVector16 < StlVector32 def length From 2ee1887870f15255f9833fa1eb53019c439ed11f Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 26 Apr 2012 19:38:15 +0200 Subject: [PATCH 39/72] ruby: add plugins/unit exemple --- plugins/ruby/plugins/unit.rb | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 plugins/ruby/plugins/unit.rb diff --git a/plugins/ruby/plugins/unit.rb b/plugins/ruby/plugins/unit.rb new file mode 100644 index 000000000..cf62c5e01 --- /dev/null +++ b/plugins/ruby/plugins/unit.rb @@ -0,0 +1,52 @@ +module DFHack +# returns an Array of all units that are current fort citizen (dwarves, on map, not hostile) +def self.unit_citizens + race = ui.race_id + civ = ui.civ_id + world.units.other[0].find_all { |u| + u.race == race and u.civ_id == civ and !u.flags1.dead and !u.flags1.merchant and + !u.flags1.diplomat and !u.flags2.resident and !u.flags3.ghostly and + !u.curse.add_tags1.OPPOSED_TO_LIFE and !u.curse.add_tags1.CRAZED and + u.mood != MoodType::Berserk + # TODO check curse ; currently this should keep vampires, but may include werebeasts + } +end + +# list workers (citizen, not crazy / child / inmood / noble) +def self.unit_workers + unit_citizens.find_all { |u| + u.mood == MoodType::None and + Profession::ENUM[u.profession] != :CHILD and + Profession::ENUM[u.profession] != :BABY and + # TODO MENIAL_WORK_EXEMPTION_SPOUSE + !unit_entitypositions(u).find { |pos| pos.flags[:MENIAL_WORK_EXEMPTION] } + } +end + +# list currently idle workers +def self.unit_idlers + unit_workers.find_all { |u| + # current_job includes eat/drink/sleep/pickupequip + !u.job.current_job._getv and + # filter 'attend meeting' + u.meetings.length == 0 and + # filter soldiers (TODO check schedule) + u.military.squad_index == -1 and + # filter 'on break' + !u.status.misc_traits.find { |t| id = UnitMiscTrait::ENUM[t.id] ; id == :OnBreak } + } +end + +def self.unit_entitypositions(unit) + list = [] + return list if not hf = world.history.figures.binsearch(unit.hist_figure_id) + hf.entity_links.each { |el| + next if el._rtti_classname != 'histfig_entity_link_positionst' + next if not ent = world.entities.all.binsearch(el.entity_id) + next if not pa = ent.positions.assignments.binsearch(el.assignment_id) + next if not pos = ent.positions.own.binsearch(pa.position_id) + list << pos + } + list +end +end From ba7d4f8eddd40994d7b75f664ece1fa288449c7e Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 26 Apr 2012 20:03:56 +0200 Subject: [PATCH 40/72] ruby: extract additional info for enums --- plugins/ruby/codegen.pl | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 2a0f0b2d6..54639c4b5 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -45,6 +45,7 @@ my %item_renderer = ( 'bytes' => \&render_item_bytes, ); +my %global_types; my %seen_enum_name; sub render_global_enum { @@ -65,7 +66,32 @@ sub render_enum_fields { my $idxname = 'ENUM'; $idxname .= '_' while ($seen_enum_name{$idxname}); $seen_enum_name{$idxname}++; - push @lines_rb, "$idxname = {}"; + push @lines_rb, "$idxname = Hash.new"; + + my %attr_type; + for my $attr ($type->findnodes('child::enum-attr')) { + my $rbattr = rb_ucase($attr->getAttribute('name')); + my $typeattr = $attr->getAttribute('type-name'); + if ($typeattr) { + if ($global_types{$typeattr}) { + $attr_type{$rbattr} = 'symbol'; + } else { + $attr_type{$rbattr} = 'naked'; + } + } else { + $attr_type{$rbattr} = 'quote'; + } + + my $def = $attr->getAttribute('default-value'); + if ($def) { + $def = ":$def" if ($attr_type{$rbattr} eq 'symbol'); + $def = "'$def'" if ($attr_type{$rbattr} eq 'quote'); + push @lines_rb, "$rbattr = Hash.new($def)"; + } else { + push @lines_rb, "$rbattr = Hash.new"; + } + } + for my $item ($type->findnodes('child::enum-item')) { $value = $item->getAttribute('value') || ($value+1); my $elemname = $item->getAttribute('name'); # || "unk_$value"; @@ -75,6 +101,14 @@ sub render_enum_fields { $rbelemname .= '_' while ($seen_enum_name{$rbelemname}); $seen_enum_name{$rbelemname}++; push @lines_rb, "$rbelemname = $value ; ${idxname}[$value] = :$rbelemname"; + for my $iattr ($item->findnodes('child::item-attr')) { + my $ian = $iattr->getAttribute('name'); + my $iav = $iattr->getAttribute('value'); + my $rbattr = rb_ucase($ian); + $iav = ":$iav" if ($attr_type{$rbattr} eq 'symbol'); + $iav = "'$iav'" if ($attr_type{$rbattr} eq 'quote'); + $lines_rb[$#lines_rb] .= " ; ${rbattr}[$value] = $iav"; + } } } } @@ -115,7 +149,6 @@ sub render_bitfield_fields { } -my %global_types; my $cpp_var_counter = 0; my %seen_class; sub render_global_class { From 51ecd15fa832aa6cae5a2559baf24896c2d9c160 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 26 Apr 2012 20:55:52 +0200 Subject: [PATCH 41/72] ruby: use symbols for enums everywhere --- plugins/ruby/codegen.pl | 34 +++++++++++++++++++++++++++------- plugins/ruby/plugins/plant.rb | 16 ++++++++-------- plugins/ruby/plugins/unit.rb | 12 ++++++------ plugins/ruby/ruby-memstruct.rb | 27 +++++++++++++++++---------- plugins/ruby/ruby.rb | 10 +++++----- 5 files changed, 63 insertions(+), 36 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 54639c4b5..47930a5da 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -47,7 +47,7 @@ my %item_renderer = ( my %global_types; -my %seen_enum_name; +our %seen_enum_name; sub render_global_enum { my ($name, $type) = @_; @@ -72,6 +72,7 @@ sub render_enum_fields { for my $attr ($type->findnodes('child::enum-attr')) { my $rbattr = rb_ucase($attr->getAttribute('name')); my $typeattr = $attr->getAttribute('type-name'); + # find how we need to encode the attribute values: string, symbol (for enums), raw (number, bool) if ($typeattr) { if ($global_types{$typeattr}) { $attr_type{$rbattr} = 'symbol'; @@ -85,6 +86,7 @@ sub render_enum_fields { my $def = $attr->getAttribute('default-value'); if ($def) { $def = ":$def" if ($attr_type{$rbattr} eq 'symbol'); + $def =~ s/'/\\'/g if ($attr_type{$rbattr} eq 'quote'); $def = "'$def'" if ($attr_type{$rbattr} eq 'quote'); push @lines_rb, "$rbattr = Hash.new($def)"; } else { @@ -106,6 +108,7 @@ sub render_enum_fields { my $iav = $iattr->getAttribute('value'); my $rbattr = rb_ucase($ian); $iav = ":$iav" if ($attr_type{$rbattr} eq 'symbol'); + $iav =~ s/'/\\'/g if ($attr_type{$rbattr} eq 'quote'); $iav = "'$iav'" if ($attr_type{$rbattr} eq 'quote'); $lines_rb[$#lines_rb] .= " ; ${rbattr}[$value] = $iav"; } @@ -137,10 +140,14 @@ sub render_bitfield_fields { for my $field ($type->findnodes('child::ld:field')) { my $count = $field->getAttribute('count') || 1; my $name = $field->getAttribute('name'); + my $type = $field->getAttribute('type-name'); + my $enum = rb_ucase($type) if ($type and $global_types{$type}); $name = $field->getAttribute('ld:anon-name') if (!$name); print "bitfield $name !number\n" if (!($field->getAttribute('ld:meta') eq 'number')); if ($count == 1) { push @lines_rb, "field(:$name, 0) { bit $shift }" if ($name); + } elsif ($enum) { + push @lines_rb, "field(:$name, 0) { bits $shift, $count, :$enum }" if ($name); } else { push @lines_rb, "field(:$name, 0) { bits $shift, $count }" if ($name); } @@ -189,7 +196,7 @@ sub render_global_class { indent_rb { my $sz = query_cpp("sizeof($cppns)"); push @lines_rb, "sizeof $sz"; - push @lines_rb, "rtti_classname '$rtti_name'" if $has_rtti; + push @lines_rb, "rtti_classname :$rtti_name" if $has_rtti; render_struct_fields($type, "$cppns"); }; push @lines_rb, "end\n"; @@ -292,10 +299,13 @@ sub render_item_number { my $subtype = $item->getAttribute('ld:subtype'); my $initvalue = $item->getAttribute('init-value'); + my $typename = $item->getAttribute('type-name'); + $typename = rb_ucase($typename) if $typename; + $typename = $pns if (!$typename and $subtype eq 'enum'); # compound enum $initvalue = 1 if ($initvalue and $initvalue eq 'true'); - # XXX needs pre-declaration of the enum... - $initvalue = rb_ucase($item->getAttribute('type-name')) . '::' . $initvalue if ($initvalue and $subtype and $subtype eq 'enum' and $initvalue =~ /[a-zA-Z]/); + $initvalue = ":$initvalue" if ($initvalue and $typename and $initvalue =~ /[a-zA-Z]/); + $initvalue ||= 'nil' if $typename; $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'enum' or $subtype eq 'bitfield'); $subtype = 'int32_t' if (!$subtype); @@ -324,6 +334,7 @@ sub render_item_number { return; } $lines_rb[$#lines_rb] .= ", $initvalue" if ($initvalue); + $lines_rb[$#lines_rb] .= ", :$typename" if ($typename); } sub render_item_compound { @@ -342,10 +353,19 @@ sub render_item_compound { }; push @lines_rb, "}" } elsif ($subtype eq 'enum') { - # declare constants - render_enum_fields($item); + my @namecomponents = split('::', $cppns); + shift @namecomponents; + my $enumclassname = join('_', map { rb_ucase($_) } @namecomponents); + local %seen_enum_name; + push @lines_rb, "class ::DFHack::$enumclassname"; + indent_rb { + # declare constants + render_enum_fields($item); + }; + push @lines_rb, "end\n"; + # actual field - render_item_number($item, $cppns); + render_item_number($item, $enumclassname); } else { print "no render compound $subtype\n"; } diff --git a/plugins/ruby/plugins/plant.rb b/plugins/ruby/plugins/plant.rb index ffc415eda..64f17b493 100644 --- a/plugins/ruby/plugins/plant.rb +++ b/plugins/ruby/plugins/plant.rb @@ -3,7 +3,7 @@ def self.each_tree(material=:any) @raws_tree_name ||= {} if @raws_tree_name.empty? df.world.raws.plants.all.each_with_index { |p, idx| - @raws_tree_name[idx] = p.id if p.flags[PlantRawFlags::TREE] + @raws_tree_name[idx] = p.id if p.flags[:TREE] } end @@ -25,7 +25,7 @@ def self.each_shrub(material=:any) @raws_shrub_name ||= {} if @raws_tree_name.empty? df.world.raws.plants.all.each_with_index { |p, idx| - @raws_shrub_name[idx] = p.id if not p.flags[PlantRawFlags::GRASS] and not p.flags[PlantRawFlags::TREE] + @raws_shrub_name[idx] = p.id if not p.flags[:GRASS] and not p.flags[:TREE] } end @@ -61,8 +61,8 @@ def self.cuttrees(material=nil, count_max=100) b = map_block_at(plant) d = b.designation[plant.pos.x%16][plant.pos.y%16] next if d.hidden - if d.dig == TileDigDesignation::No - d.dig = TileDigDesignation::Default + if d.dig == :No + d.dig = :Default b.flags.designated = true cnt += 1 break if cnt == count_max @@ -116,9 +116,9 @@ def self.growcrops(material=nil, count_max=100) if !material cnt = Hash.new(0) suspend { - world.items.other[ItemsOtherId::SEEDS].each { |seed| + world.items.other[:SEEDS].each { |seed| next if not seed.flags.in_building - next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' } + next if not seed.itemrefs.find { |ref| ref._rtti_classname == :general_ref_building_holderst } next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] cnt[seed.mat_index] += 1 } @@ -137,10 +137,10 @@ def self.growcrops(material=nil, count_max=100) cnt = 0 suspend { - world.items.other[ItemsOtherId::SEEDS].each { |seed| + world.items.other[:SEEDS].each { |seed| next if wantmat and seed.mat_index != wantmat next if not seed.flags.in_building - next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' } + next if not seed.itemrefs.find { |ref| ref._rtti_classname == :general_ref_building_holderst } next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] seed.grow_counter = @raws_plant_growdur[seed.mat_index] cnt += 1 diff --git a/plugins/ruby/plugins/unit.rb b/plugins/ruby/plugins/unit.rb index cf62c5e01..05ff17dae 100644 --- a/plugins/ruby/plugins/unit.rb +++ b/plugins/ruby/plugins/unit.rb @@ -7,7 +7,7 @@ def self.unit_citizens u.race == race and u.civ_id == civ and !u.flags1.dead and !u.flags1.merchant and !u.flags1.diplomat and !u.flags2.resident and !u.flags3.ghostly and !u.curse.add_tags1.OPPOSED_TO_LIFE and !u.curse.add_tags1.CRAZED and - u.mood != MoodType::Berserk + u.mood != :Berserk # TODO check curse ; currently this should keep vampires, but may include werebeasts } end @@ -15,9 +15,9 @@ end # list workers (citizen, not crazy / child / inmood / noble) def self.unit_workers unit_citizens.find_all { |u| - u.mood == MoodType::None and - Profession::ENUM[u.profession] != :CHILD and - Profession::ENUM[u.profession] != :BABY and + u.mood == :None and + u.profession != :CHILD and + u.profession != :BABY and # TODO MENIAL_WORK_EXEMPTION_SPOUSE !unit_entitypositions(u).find { |pos| pos.flags[:MENIAL_WORK_EXEMPTION] } } @@ -33,7 +33,7 @@ def self.unit_idlers # filter soldiers (TODO check schedule) u.military.squad_index == -1 and # filter 'on break' - !u.status.misc_traits.find { |t| id = UnitMiscTrait::ENUM[t.id] ; id == :OnBreak } + !u.status.misc_traits.find { |t| id == :OnBreak } } end @@ -41,7 +41,7 @@ def self.unit_entitypositions(unit) list = [] return list if not hf = world.history.figures.binsearch(unit.hist_figure_id) hf.entity_links.each { |el| - next if el._rtti_classname != 'histfig_entity_link_positionst' + next if el._rtti_classname != :histfig_entity_link_positionst next if not ent = world.entities.all.binsearch(el.entity_id) next if not pa = ent.positions.assignments.binsearch(el.assignment_id) next if not pos = ent.positions.own.binsearch(pa.position_id) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index ef3030859..c283a0ec1 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -26,8 +26,8 @@ class Compound < MemStruct end end - def number(bits, signed, initvalue=nil) - Number.new(bits, signed, initvalue) + def number(bits, signed, initvalue=nil, enum=nil) + Number.new(bits, signed, initvalue, enum) end def float Float.new @@ -35,8 +35,8 @@ class Compound < MemStruct def bit(shift) BitField.new(shift, 1) end - def bits(shift, len) - BitField.new(shift, len) + def bits(shift, len, enum=nil) + BitField.new(shift, len, enum) end def pointer Pointer.new((yield if block_given?)) @@ -144,11 +144,12 @@ class Compound < MemStruct end class Number < MemStruct - attr_accessor :_bits, :_signed, :_initvalue - def initialize(bits, signed, initvalue) + attr_accessor :_bits, :_signed, :_initvalue, :_enum + def initialize(bits, signed, initvalue, enum) @_bits = bits @_signed = signed @_initvalue = initvalue + @_enum = enum end def _get @@ -159,10 +160,12 @@ class Number < MemStruct when 64;(DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + (DFHack.memory_read_int32(@_memaddr+4) << 32) end v &= (1 << @_bits) - 1 if not @_signed + v = DFHack.const_get(@_enum)::ENUM[v] || v if @_enum v end def _set(v) + v = DFHack.const_get(@_enum).const_get(v) if @_enum and v.kind_of?(::Symbol) case @_bits when 32; DFHack.memory_write_int32(@_memaddr, v) when 16; DFHack.memory_write_int16(@_memaddr, v) @@ -190,10 +193,11 @@ class Float < MemStruct end end class BitField < MemStruct - attr_accessor :_shift, :_len - def initialize(shift, len) + attr_accessor :_shift, :_len, :_enum + def initialize(shift, len, enum=nil) @_shift = shift @_len = len + @_enum = enum end def _mask (1 << @_len) - 1 @@ -204,7 +208,9 @@ class BitField < MemStruct if @_len == 1 ((v & 1) == 0) ? false : true else - v & _mask + v &= _mask + v = DFHack.const_get(@_enum)::ENUM[v] || v if @_enum + v end end @@ -213,6 +219,7 @@ class BitField < MemStruct # allow 'bit = 0' v = (v && v != 0 ? 1 : 0) end + v = DFHack.const_get(@_enum).const_get(v) if @_enum and v.kind_of?(::Symbol) v = (v & _mask) << @_shift ori = DFHack.memory_read_int32(@_memaddr) & 0xffffffff @@ -683,7 +690,7 @@ end # try to read the rtti classname from an object vtable pointer def self.rtti_readclassname(vptr) unless n = @rtti_v2n[vptr] - n = @rtti_v2n[vptr] = get_rtti_classname(vptr) + n = @rtti_v2n[vptr] = get_rtti_classname(vptr).to_sym @rtti_n2v[n] = vptr end n diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index eec66d56b..49c07f812 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -86,13 +86,13 @@ module DFHack def find_unit(what=:selected) if what == :selected case ui.main.mode - when UiSidebarMode::ViewUnits + when :ViewUnits # nobody selected => idx == 0 v = world.units.other[0][ui_selected_unit] v if v and v.pos.z == cursor.z - when UiSidebarMode::LookAround + when :LookAround k = ui_look_list.items[ui_look_cursor] - k.unit if k.type == UiLookList::Unit + k.unit if k.type == :Unit end elsif what.kind_of?(Integer) world.units.all.find { |u| u.id == what } @@ -110,9 +110,9 @@ module DFHack def find_item(what=:selected) if what == :selected case ui.main.mode - when UiSidebarMode::LookAround + when :LookAround k = ui_look_list.items[ui_look_cursor] - k.item if k.type == UiLookList::Item + k.item if k.type == :Item end elsif what.kind_of?(Integer) world.items.all.find { |i| i.id == what } From 2cc5333157d15f51bc6e3c2889f5a9f7be947f7f Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 27 Apr 2012 03:02:37 +0200 Subject: [PATCH 42/72] ruby: cleanup codegen enum --- plugins/ruby/codegen.pl | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 47930a5da..55985fc0b 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -47,13 +47,11 @@ my %item_renderer = ( my %global_types; -our %seen_enum_name; sub render_global_enum { my ($name, $type) = @_; my $rbname = rb_ucase($name); push @lines_rb, "class $rbname"; - %seen_enum_name = (); indent_rb { render_enum_fields($type); }; @@ -64,8 +62,6 @@ sub render_enum_fields { my $value = -1; my $idxname = 'ENUM'; - $idxname .= '_' while ($seen_enum_name{$idxname}); - $seen_enum_name{$idxname}++; push @lines_rb, "$idxname = Hash.new"; my %attr_type; @@ -100,8 +96,6 @@ sub render_enum_fields { if ($elemname) { my $rbelemname = rb_ucase($elemname); - $rbelemname .= '_' while ($seen_enum_name{$rbelemname}); - $seen_enum_name{$rbelemname}++; push @lines_rb, "$rbelemname = $value ; ${idxname}[$value] = :$rbelemname"; for my $iattr ($item->findnodes('child::item-attr')) { my $ian = $iattr->getAttribute('name'); @@ -169,7 +163,6 @@ sub render_global_class { return if $seen_class{$name}; $seen_class{$name}++; - %seen_enum_name = (); my $rtti_name = $type->getAttribute('original-name') || $type->getAttribute('type-name'); @@ -228,8 +221,6 @@ sub render_global_objects { my $sname = 'global_objects'; my $rbname = rb_ucase($sname); - %seen_enum_name = (); - push @lines_cpp, "}" if @include_cpp; push @lines_cpp, "void cpp_$sname(FILE *fout) {"; push @include_cpp, $sname; @@ -356,7 +347,6 @@ sub render_item_compound { my @namecomponents = split('::', $cppns); shift @namecomponents; my $enumclassname = join('_', map { rb_ucase($_) } @namecomponents); - local %seen_enum_name; push @lines_rb, "class ::DFHack::$enumclassname"; indent_rb { # declare constants From 551df9bb734e2512078145708395bbb1a806dfc7 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 27 Apr 2012 03:07:03 +0200 Subject: [PATCH 43/72] ruby: use binsearch in find_unit/find_item --- plugins/ruby/ruby.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 49c07f812..b529634bc 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -95,7 +95,7 @@ module DFHack k.unit if k.type == :Unit end elsif what.kind_of?(Integer) - world.units.all.find { |u| u.id == what } + world.units.all.binsearch(what) elsif what.respond_to?(:x) or what.respond_to?(:pos) what = what.pos if what.respond_to?(:pos) x, y, z = what.x, what.y, what.z @@ -115,7 +115,7 @@ module DFHack k.item if k.type == :Item end elsif what.kind_of?(Integer) - world.items.all.find { |i| i.id == what } + world.items.all.binsearch(what) elsif what.respond_to?(:x) or what.respond_to?(:pos) what = what.pos if what.respond_to?(:pos) x, y, z = what.x, what.y, what.z From 22628fe9bb4681891075e7ded6763366ce6f717b Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 27 Apr 2012 14:13:44 +0200 Subject: [PATCH 44/72] codegen: all class-type have a vtable --- plugins/ruby/codegen.pl | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 55985fc0b..7165e57bb 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -164,18 +164,11 @@ sub render_global_class { return if $seen_class{$name}; $seen_class{$name}++; - my $rtti_name = $type->getAttribute('original-name') || - $type->getAttribute('type-name'); - - my $has_rtti = $parent; - if (!$parent and $type->getAttribute('ld:meta') eq 'class-type') { - for my $anytypename (keys %global_types) { - my $anytype = $global_types{$anytypename}; - if ($anytype->getAttribute('ld:meta') eq 'class-type') { - my $anyparent = $anytype->getAttribute('inherits-from'); - $has_rtti = 1 if ($anyparent and $anyparent eq $name); - } - } + my $rtti_name; + if ($type->getAttribute('ld:meta') eq 'class-type') { + $rtti_name = $type->getAttribute('original-name') || + $type->getAttribute('type-name') || + $name; } my $rbparent = ($parent ? rb_ucase($parent) : 'MemHack::Compound'); @@ -189,7 +182,7 @@ sub render_global_class { indent_rb { my $sz = query_cpp("sizeof($cppns)"); push @lines_rb, "sizeof $sz"; - push @lines_rb, "rtti_classname :$rtti_name" if $has_rtti; + push @lines_rb, "rtti_classname :$rtti_name" if $rtti_name; render_struct_fields($type, "$cppns"); }; push @lines_rb, "end\n"; From fcfd7062eb6bff110dbaa7f713182df841594bea Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 27 Apr 2012 17:11:01 +0200 Subject: [PATCH 45/72] ruby: tweak inspect, autodereference pointers --- plugins/ruby/README | 10 ++-- plugins/ruby/ruby-memstruct.rb | 105 +++++++++++++++------------------ plugins/ruby/ruby.rb | 4 +- 3 files changed, 54 insertions(+), 65 deletions(-) diff --git a/plugins/ruby/README b/plugins/ruby/README index b9ea35f95..41e165459 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -100,13 +100,11 @@ the block specifies the member type. See ruby-memstruct.rb for more information. Primitive type access is done through native methods in ruby.cpp (vector length, raw memory access, etc) -MemHack::Pointers have a special behavior: they accept and forward any method to -the pointed object. To retrieve the pointed object directly, use the _getv -method. Null pointers resolve to 'nil'. -Beware, invalid pointers (!= 0) will crash the plugin and the game. +MemHack::Pointers are automatically dereferenced ; so a vector of pointer to +Units will yield Units directly. Null pointers yield the 'nil' value. -This allows to use code such as 'df.world.units.all[0].pos', with all[0] being -really a Pointer (with no 'pos' method). +This allows to use code such as 'df.world.units.all[0].pos', with 'all' being +really a vector of pointer. Todo diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index c283a0ec1..adde84699 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -1,11 +1,11 @@ module DFHack module MemHack +INSPECT_SIZE_LIMIT=1024 class MemStruct attr_accessor :_memaddr def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end def _get ; self ; end def _cpp_init ; end - def inspect ; _get.inspect ; end end class Compound < MemStruct @@ -121,7 +121,7 @@ class Compound < MemStruct out = "#<#{cn}" _fields_ancestors.each { |n, o, s| out << ' ' if out.length != 0 and out[-1, 1] != ' ' - if out.length > 1024 + if out.length > INSPECT_SIZE_LIMIT out << '...' break end @@ -132,6 +132,8 @@ class Compound < MemStruct def inspect_field(n, o, s) if s.kind_of?(BitField) and s._len == 1 send(n) ? n.to_s : '' + elsif s.kind_of?(Pointer) + s._at(@_memaddr+o).inspect elsif n == :_whole "_whole=0x#{_whole.to_s(16)}" else @@ -237,7 +239,7 @@ class Pointer < MemStruct DFHack.memory_read_int32(@_memaddr) & 0xffffffff end - def _getv + def _get addr = _getp return if addr == 0 @_tg._at(addr)._get @@ -246,28 +248,23 @@ class Pointer < MemStruct # XXX shaky... def _set(v) if v.kind_of?(Pointer) - DFHack.memory_write_int32(@_memaddr, v._getv) + DFHack.memory_write_int32(@_memaddr, v._getp) elsif v.kind_of?(MemStruct) DFHack.memory_write_int32(@_memaddr, v._memaddr) else - _getv._set(v) + _get._set(v) end end - # these ruby Object methods should be forwarded to the ptr - undef id - undef type - def method_missing(*a) - _getv.send(*a) - end - def respond_to?(q) - _getv.respond_to?(q) - end - def inspect - cn = (@_tg ? @_tg.class.name.sub(/^DFHack::/, '') : '') - cn = @_tg._glob if cn == 'MemHack::Global' - "#" + ptr = _getp + if ptr == 0 + 'NULL' + else + cn = (@_tg ? @_tg.class.name.sub(/^DFHack::/, '') : '') + cn = @_tg._glob if cn == 'MemHack::Global' + "#" + end end end class PointerAry < MemStruct @@ -304,6 +301,25 @@ module IndexEnum end end end +module Enumerable + include ::Enumerable + def each ; (0...length).each { |i| yield self[i] } ; end + def inspect + enum = DFHack.const_get(_indexenum)::ENUM if _indexenum + out = '[' + each { |i| + out << ', ' if out.length > 1 + if out.length > INSPECT_SIZE_LIMIT + out << '...' + break + end + out << "#{enum[i]}=" if enum + out << i.inspect + } + out << ']' + end + def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end +end class StaticArray < MemStruct include IndexEnum attr_accessor :_tglen, :_length, :_indexenum, :_tg @@ -336,16 +352,6 @@ class StaticArray < MemStruct end include Enumerable - def each ; (0...length).each { |i| yield self[i] } ; end - def inspect - if _indexenum - e = DFHack.const_get(_indexenum)::ENUM - '[' + (0...length).map { |i| "#{e[i]}=#{self[i].inspect}" }.join(' ') + ']' - else - to_a.inspect - end - end - def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end end class StaticString < MemStruct attr_accessor :_length @@ -420,14 +426,6 @@ class StlVector32 < MemStruct end include Enumerable - def each ; (0...length).each { |i| yield self[i] } ; end - def inspect - if _tg and _tg.kind_of?(Pointer) - length > 0 ? "[#{length}*#{self[0].inspect}]" : '[]' - else - to_a.inspect - end - end # do a binary search in an ordered vector for a specific target attribute # ex: world.history.figures.binsearch(unit.hist_figure_id) def binsearch(target, field=:id) @@ -555,15 +553,6 @@ class DfFlagarray < MemStruct end include Enumerable - def each ; (0...length).each { |i| yield self[i] } ; end - def inspect - if _indexenum - e = DFHack.const_get(_indexenum)::ENUM - '[' + (0...length).map { |i| if self[i] ; e[i] || i ; end }.compact.join(' ') + ']' - else - to_a.inspect - end - end end class DfArray < Compound attr_accessor :_tglen, :_tg @@ -594,8 +583,6 @@ class DfArray < Compound end include Enumerable - def each ; (0...length).each { |i| yield self[i] } ; end - def inspect ; to_a.inspect ; end end class DfLinkedList < Compound attr_accessor :_tg @@ -608,27 +595,32 @@ class DfLinkedList < Compound field(:_next, 8) { number 32, false } def item - addr = _ptr - return if addr == 0 - @_tg._at(addr)._get + # With the current xml structure, currently _tg designate + # the type of the 'next' and 'prev' fields, not 'item'. + # List head has item == NULL, so we can safely return nil. + + #addr = _ptr + #return if addr == 0 + #@_tg._at(addr)._get end def item=(v) - addr = _ptr - raise 'null pointer' if addr == 0 - @_tg.at(addr)._set(v) + #addr = _ptr + #raise 'null pointer' if addr == 0 + #@_tg.at(addr)._set(v) + raise 'null pointer' end def prev addr = _prev return if addr == 0 - _at(addr) + @_tg._at(addr)._get end def next addr = _next return if addr == 0 - _at(addr) + @_tg._at(addr)._get end include Enumerable @@ -639,8 +631,7 @@ class DfLinkedList < Compound o = o.next end end - - def inspect ; "#" ; end + def inspect ; "#" ; end end class Global < MemStruct diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index b529634bc..90ce8b88f 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -159,7 +159,7 @@ module DFHack yl = xl[yb] (0...world.map.z_count_block).each { |z| p = yl[z] - yield p._getv if p._getp != 0 + yield p if p } } } @@ -171,7 +171,7 @@ module DFHack xl = world.map.block_index[xb] (0...world.map.y_count_block).each { |yb| p = xl[yb][z] - yield p._getv if p._getp != 0 + yield p if p } } end From 6800f459f5f2161c6e7528fa12565efd19a4d49c Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 27 Apr 2012 17:24:15 +0200 Subject: [PATCH 46/72] ruby: fix bitfield looking for enum value --- plugins/ruby/codegen.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 7165e57bb..4add74ddd 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -282,10 +282,12 @@ sub render_item_number { my ($item, $pns) = @_; my $subtype = $item->getAttribute('ld:subtype'); + my $meta = $item->getAttribute('ld:meta'); my $initvalue = $item->getAttribute('init-value'); my $typename = $item->getAttribute('type-name'); + undef $typename if ($meta and $meta eq 'bitfield-type'); $typename = rb_ucase($typename) if $typename; - $typename = $pns if (!$typename and $subtype eq 'enum'); # compound enum + $typename = $pns if (!$typename and $subtype and $subtype eq 'enum'); # compound enum $initvalue = 1 if ($initvalue and $initvalue eq 'true'); $initvalue = ":$initvalue" if ($initvalue and $typename and $initvalue =~ /[a-zA-Z]/); From 9bceb7a4437affca2b94092a3745a1335d4fb1e3 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 27 Apr 2012 17:31:39 +0200 Subject: [PATCH 47/72] ruby: typo in enumerable --- plugins/ruby/ruby-memstruct.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index adde84699..cb99dbefb 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -303,6 +303,7 @@ module IndexEnum end module Enumerable include ::Enumerable + attr_accessor :_indexenum def each ; (0...length).each { |i| yield self[i] } ; end def inspect enum = DFHack.const_get(_indexenum)::ENUM if _indexenum From 3446fd024e354e1614c90218b21cdf756bb4350c Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 27 Apr 2012 17:38:31 +0200 Subject: [PATCH 48/72] ruby: tweak inspect --- plugins/ruby/ruby-memstruct.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index cb99dbefb..813399760 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -127,13 +127,14 @@ class Compound < MemStruct end out << inspect_field(n, o, s) } + out.chomp!(' ') out << '>' end def inspect_field(n, o, s) if s.kind_of?(BitField) and s._len == 1 send(n) ? n.to_s : '' elsif s.kind_of?(Pointer) - s._at(@_memaddr+o).inspect + "#{n}=#{s._at(@_memaddr+o).inspect}" elsif n == :_whole "_whole=0x#{_whole.to_s(16)}" else From bc218db59643ed99cb27fdc28a5af136f69c0253 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 27 Apr 2012 17:46:51 +0200 Subject: [PATCH 49/72] ruby: more minor tweaks --- plugins/ruby/ruby-memstruct.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 813399760..2e935b9c1 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -262,8 +262,9 @@ class Pointer < MemStruct if ptr == 0 'NULL' else - cn = (@_tg ? @_tg.class.name.sub(/^DFHack::/, '') : '') - cn = @_tg._glob if cn == 'MemHack::Global' + cn = '' + cn = @_tg.class.name.sub(/^DFHack::/, '').sub(/^MemHack::/, '') if @_tg + cn = @_tg._glob if cn == 'Global' "#" end end @@ -320,6 +321,7 @@ module Enumerable } out << ']' end + def empty? ; length == 0 ; end def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end end class StaticArray < MemStruct From a7afe04f3bd46dda12728f2cc002f7c9443f055f Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 27 Apr 2012 17:59:54 +0200 Subject: [PATCH 50/72] ruby: name nested compounds, to allow cpp_new etc --- plugins/ruby/codegen.pl | 14 ++++++++------ plugins/ruby/ruby-memstruct.rb | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 4add74ddd..9adff9d20 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -328,8 +328,13 @@ sub render_item_compound { my $cppns = $pns . '::' . $item->getAttribute('ld:typedef-name'); my $subtype = $item->getAttribute('ld:subtype'); + + my @namecomponents = split('::', $cppns); + shift @namecomponents; + my $classname = join('_', map { rb_ucase($_) } @namecomponents); + if (!$subtype || $subtype eq 'bitfield') { - push @lines_rb, "compound {"; + push @lines_rb, "compound(:$classname) {"; indent_rb { if (!$subtype) { render_struct_fields($item, $cppns); @@ -339,10 +344,7 @@ sub render_item_compound { }; push @lines_rb, "}" } elsif ($subtype eq 'enum') { - my @namecomponents = split('::', $cppns); - shift @namecomponents; - my $enumclassname = join('_', map { rb_ucase($_) } @namecomponents); - push @lines_rb, "class ::DFHack::$enumclassname"; + push @lines_rb, "class ::DFHack::$classname"; indent_rb { # declare constants render_enum_fields($item); @@ -350,7 +352,7 @@ sub render_item_compound { push @lines_rb, "end\n"; # actual field - render_item_number($item, $enumclassname); + render_item_number($item, $classname); } else { print "no render compound $subtype\n"; } diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 2e935b9c1..3b30b0e9d 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -82,8 +82,9 @@ class Compound < MemStruct def global(glob) Global.new(glob) end - def compound(&b) + def compound(name=nil, &b) m = Class.new(Compound) + DFHack.const_set(name, m) if name m.instance_eval(&b) m.new end From ec8b090abe8e3527d8db9904814587a572aa8309 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 27 Apr 2012 18:50:27 +0200 Subject: [PATCH 51/72] ruby: add plugins/buildbed.rb (segfaults) --- plugins/ruby/plugins/buildbed.rb | 87 ++++++++++++++++++++++++++++++++ plugins/ruby/ruby-memstruct.rb | 10 +++- 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 plugins/ruby/plugins/buildbed.rb diff --git a/plugins/ruby/plugins/buildbed.rb b/plugins/ruby/plugins/buildbed.rb new file mode 100644 index 000000000..5e544c826 --- /dev/null +++ b/plugins/ruby/plugins/buildbed.rb @@ -0,0 +1,87 @@ +module DFHack +def self.buildbedhere + suspend { + raise 'where to ?' if cursor.x < 0 + + # TODO tweak that more + item = world.items.all.find { |i| + i.kind_of?(ItemBedst) and + i.itemrefs.empty? and + i.jobs.empty? + } + raise 'no free bed, build more !' if not item + + puts 'alloc c++' + job = Job.cpp_new + joblink = JobListLink.cpp_new + jobitemref = JobItemRef.cpp_new + refbuildingholder = GeneralRefBuildingHolderst.cpp_new + building = BuildingBedst.cpp_new + itemtjobs = Item_TJobs.cpp_new + + puts 'update global counters' + # TODO + buildingid = world.buildings.all[-1].id + 1 + jobid = df.job_next_id + df.job_next_id = jobid+1 + + puts 'init objects' + job.id = jobid + job.list_link = joblink + job.job_type = :ConstructBuilding + job.unk2 = -1 # XXX + job.pos = cursor + job.completion_timer = -1 + # unk4a/4b uninit + job.mat_type = item.mat_type + job.mat_index = item.mat_index + job.unk5 = -1 + job.unk6 = -1 + job.item_subtype = -1 + job.hist_figure_id = -1 + job.items << jobitemref + job.references << refbuildingholder + + lastjob = world.job_list + lastjob = lastjob.next while lastjob.next + lastjob.next = joblink + joblink.prev = lastjob + joblink.item = job + + jobitemref.item = item + jobitemref.role = :Hauled + jobitemref.job_item_idx = -1 + + refbuildingholder.building_id = buildingid + + building.x1 = building.x2 = building.centerx = cursor.x + building.y1 = building.y2 = building.centery = cursor.y + building.z = cursor.z + building.mat_type = item.mat_type + building.mat_index = item.mat_index + building.race = ui.race_id + building.id = buildingid + building.jobs << job + building.anon_3 = -1 + building.anon_4 = -1 + + itemtjobs.unk1 = 2 + itemtjobs.job = job + + item.flags.in_job = true + item.jobs << itemtjobs + + puts 'update vectors' + world.buildings.all << building + # XXX + world.buildings.other[0] << building + world.buildings.other[4] << building + world.buildings.other[8] << building + world.buildings.other[9] << building + world.buildings.other[10] << building + world.buildings.other[32] << building + # not in bad + + } +end +end diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 3b30b0e9d..53a4158fc 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -111,7 +111,13 @@ class Compound < MemStruct def _cpp_init _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init } end - def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end + def _set(h) + case h + when Hash; h.each { |k, v| send("_#{k}=", v) } + when Compound; _fields.each { |n, o, s| send("#{n}=", h.send(n)) } + else raise 'wut?' + end + end def _fields ; self.class._fields.to_a ; end def _fields_ancestors ; self.class._fields_ancestors.to_a ; end def _rtti_classname ; self.class._rtti_classname ; end @@ -695,7 +701,7 @@ end # return the vtable pointer from the cpp rtti name def self.rtti_getvtable(cppname) unless v = @rtti_n2v[cppname] - v = get_vtable(cppname) + v = get_vtable(cppname.to_s) @rtti_n2v[cppname] = v @rtti_v2n[v] = cppname if v != 0 end From 5fdeac9913bd47522cc4052a05b30690ab80d21d Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 3 May 2012 02:52:53 +0200 Subject: [PATCH 52/72] ruby: fix StaticArray#inspect with indexenum, allow df.cursor = [x, y, z] --- plugins/ruby/ruby-memstruct.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 53a4158fc..5989050d6 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -114,12 +114,14 @@ class Compound < MemStruct def _set(h) case h when Hash; h.each { |k, v| send("_#{k}=", v) } - when Compound; _fields.each { |n, o, s| send("#{n}=", h.send(n)) } + when Array; names = _field_names ; raise 'bad size' if names.length != h.length ; names.zip(h).each { |n, a| send("#{n}=", a } + when Compound; _field_names.each { |n| send("#{n}=", h.send(n)) } else raise 'wut?' end end def _fields ; self.class._fields.to_a ; end def _fields_ancestors ; self.class._fields_ancestors.to_a ; end + def _field_names ; _fields_ancestors.map { |n, o, s| n } ; end def _rtti_classname ; self.class._rtti_classname ; end def _sizeof ; self.class._sizeof ; end def inspect @@ -317,14 +319,14 @@ module Enumerable def inspect enum = DFHack.const_get(_indexenum)::ENUM if _indexenum out = '[' - each { |i| + each_with_index { |e, idx| out << ', ' if out.length > 1 if out.length > INSPECT_SIZE_LIMIT out << '...' break end - out << "#{enum[i]}=" if enum - out << i.inspect + out << "#{enum[idx] || idx}=" if enum + out << e.inspect } out << ']' end From 8952eabd62321c3db7b10126561c5c796fe85636 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 3 May 2012 03:33:50 +0200 Subject: [PATCH 53/72] ruby: typo --- plugins/ruby/ruby-memstruct.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 5989050d6..82b158779 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -114,7 +114,7 @@ class Compound < MemStruct def _set(h) case h when Hash; h.each { |k, v| send("_#{k}=", v) } - when Array; names = _field_names ; raise 'bad size' if names.length != h.length ; names.zip(h).each { |n, a| send("#{n}=", a } + when Array; names = _field_names ; raise 'bad size' if names.length != h.length ; names.zip(h).each { |n, a| send("#{n}=", a) } when Compound; _field_names.each { |n| send("#{n}=", h.send(n)) } else raise 'wut?' end From ba1e3c4b23566582ca4a824833673bddba384683 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 3 May 2012 18:49:12 +0200 Subject: [PATCH 54/72] ruby: vmethod call --- plugins/ruby/codegen.pl | 45 ++++++++++++++++++++++++++++++++-- plugins/ruby/ruby-memstruct.rb | 16 +++++++++++- plugins/ruby/ruby.cpp | 16 ++++++++++++ 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 9adff9d20..079410791 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -29,7 +29,7 @@ sub rb_ucase { my %global_type_renderer = ( 'enum-type' => \&render_global_enum, - 'struct-type' => \&render_global_class, + 'struct-type' => \&render_global_struct, 'class-type' => \&render_global_class, 'bitfield-type' => \&render_global_bitfield, ); @@ -150,7 +150,24 @@ sub render_bitfield_fields { } -my $cpp_var_counter = 0; +sub render_global_struct { + my ($name, $type) = @_; + + my $rbname = rb_ucase($name); + + my $cppns = "df::$name"; + push @lines_cpp, "}" if @include_cpp; + push @lines_cpp, "void cpp_$name(FILE *fout) {"; + push @include_cpp, $name; + + push @lines_rb, "class $rbname < MemHack::Compound"; + indent_rb { + my $sz = query_cpp("sizeof($cppns)"); + push @lines_rb, "sizeof $sz"; + render_struct_fields($type, "$cppns"); + }; + push @lines_rb, "end\n"; +} my %seen_class; sub render_global_class { my ($name, $type) = @_; @@ -184,6 +201,8 @@ sub render_global_class { push @lines_rb, "sizeof $sz"; push @lines_rb, "rtti_classname :$rtti_name" if $rtti_name; render_struct_fields($type, "$cppns"); + my $vms = $type->findnodes('child::virtual-methods')->[0]; + render_class_vmethods($vms) if $vms; }; push @lines_rb, "end\n"; } @@ -206,6 +225,28 @@ sub render_struct_fields { push @lines_rb, "}"; } } +sub render_class_vmethods { + my ($vms) = @_; + my $voff = 0; + for my $meth ($vms->findnodes('child::vmethod')) { + my $name = $meth->getAttribute('name'); + if ($name) { + my @argnames; + for my $arg ($meth->findnodes('child::ld:field')) { + my $nr = $#argnames + 1; + push @argnames, lcfirst($arg->getAttribute('name') || "arg$nr"); + } + push @lines_rb, "def $name(" . join(', ', @argnames) . ')'; + indent_rb { + push @lines_rb, "DFHack.vmethod_call(self, $voff" . join('', map { ", $_" } @argnames) . ')' + # TODO interpret return type (eg pointer to vector) + }; + push @lines_rb, 'end'; + } + $voff += 4; + } + +} sub render_global_objects { my (@objects) = @_; diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 82b158779..e5da44f10 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -191,7 +191,6 @@ class Number < MemStruct end end class Float < MemStruct - # _get/_set defined in ruby.cpp def _get DFHack.memory_read_float(@_memaddr) end @@ -710,6 +709,21 @@ def self.rtti_getvtable(cppname) v if v != 0 end +def self.vmethod_call(obj, voff, a0=0, a1=0, a2=0, a3=0, a4=0) + vmethod_do_call(obj._memaddr, voff, vmethod_arg(a0), vmethod_arg(a1), vmethod_arg(a2), vmethod_arg(a3)) +end + +def self.vmethod_arg(arg) + case arg + when nil, false; 0 + when true; 1 + when Integer; arg + #when String; [arg].pack('p').unpack('L')[0] # raw pointer to buffer + when MemHack::Compound; arg._memaddr + else raise "bad vmethod arg #{arg.class}" + end +end + end diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 159b48a75..76d6a0431 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -630,6 +630,21 @@ static VALUE rb_dfmemory_bitarray_set(VALUE self, VALUE addr, VALUE idx, VALUE v } +/* call an arbitrary object virtual method */ +static VALUE rb_dfvcall(VALUE self, VALUE cppobj, VALUE cppvoff, VALUE a0, VALUE a1, VALUE a2, VALUE a3) +{ +#ifdef WIN32 + __thiscall +#endif + int (*fptr)(char **me, int, int, int, int); + char **that = (char**)rb_num2ulong(cppobj); + int ret; + fptr = (decltype(fptr))*(void**)(*that + rb_num2ulong(cppvoff)); + ret = fptr(that, rb_num2ulong(a0), rb_num2ulong(a1), rb_num2ulong(a2), rb_num2ulong(a3)); + return rb_uint2inum(ret); +} + + // define module DFHack and its methods static void ruby_bind_dfhack(void) { @@ -649,6 +664,7 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1); rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1); rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1); + rb_define_singleton_method(rb_cDFHack, "vmethod_do_call", RUBY_METHOD_FUNC(rb_dfvcall), 6); rb_define_const(rb_cDFHack, "REBASE_DELTA", rb_dfrebase_delta()); rb_define_singleton_method(rb_cDFHack, "memory_read", RUBY_METHOD_FUNC(rb_dfmemory_read), 2); From 138461634ecf9b66b28484cddb1cedd68be6d029 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 3 May 2012 19:19:58 +0200 Subject: [PATCH 55/72] ruby: fix vtable for linux destructor --- plugins/ruby/codegen.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 079410791..81ca15137 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -201,7 +201,7 @@ sub render_global_class { push @lines_rb, "sizeof $sz"; push @lines_rb, "rtti_classname :$rtti_name" if $rtti_name; render_struct_fields($type, "$cppns"); - my $vms = $type->findnodes('child::virtual-methods')->[0]; + my $vms = $type->findnodes('child::virtual-methods')->[0]; render_class_vmethods($vms) if $vms; }; push @lines_rb, "end\n"; @@ -243,9 +243,10 @@ sub render_class_vmethods { }; push @lines_rb, 'end'; } + # on linux, the destructor uses 2 entries + $voff += 4 if $meth->getAttribute('is-destructor') and $^O =~ /linux/i; $voff += 4; } - } sub render_global_objects { From e2a978a4fa3c71d025b007e18c09490a1c482fcb Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 5 May 2012 22:43:12 +0200 Subject: [PATCH 56/72] ruby: avoid infinite recursion in inspect --- plugins/ruby/ruby-memstruct.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index e5da44f10..ddc62982e 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -124,10 +124,13 @@ class Compound < MemStruct def _field_names ; _fields_ancestors.map { |n, o, s| n } ; end def _rtti_classname ; self.class._rtti_classname ; end def _sizeof ; self.class._sizeof ; end + @@inspecting = {} # avoid infinite recursion on mutually-referenced objects def inspect cn = self.class.name.sub(/^DFHack::/, '') cn << ' @' << ('0x%X' % _memaddr) if cn != '' out = "#<#{cn}" + return out << ' ...>' if @@inspecting[_memaddr] + @@inspecting[_memaddr] = true _fields_ancestors.each { |n, o, s| out << ' ' if out.length != 0 and out[-1, 1] != ' ' if out.length > INSPECT_SIZE_LIMIT @@ -137,6 +140,7 @@ class Compound < MemStruct out << inspect_field(n, o, s) } out.chomp!(' ') + @@inspecting.delete _memaddr out << '>' end def inspect_field(n, o, s) From f18aa9d947d3c11ffeb8e8a823d454dfc29f2c24 Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 5 May 2012 23:56:26 +0200 Subject: [PATCH 57/72] ruby: add link_job --- plugins/ruby/ruby.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 90ce8b88f..99767b3e1 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -258,6 +258,20 @@ module DFHack may = rawlist.find_all { |r| r.downcase.index(name.downcase) } may.first if may.length == 1 end + + # link a job to the world + # allocate & set job.id, allocate a JobListLink, link to job & world.job_list + def link_job(job) + lastjob = world.job_list + lastjob = lastjob.next while lastjob.next + joblink = JobListLink.cpp_new + joblink.prev = lastjob + joblink.item = job + job.list_link = joblink + job.id = df.job_next_id + df.job_next_id += 1 + lastjob.next = joblink + end end end From 96a62a886532a93da0695da500b10f6810ad8f61 Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 5 May 2012 23:58:03 +0200 Subject: [PATCH 58/72] ruby: fix buildbed sample --- plugins/ruby/plugins/buildbed.rb | 51 +++++++------------------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/plugins/ruby/plugins/buildbed.rb b/plugins/ruby/plugins/buildbed.rb index 5e544c826..892a75dc1 100644 --- a/plugins/ruby/plugins/buildbed.rb +++ b/plugins/ruby/plugins/buildbed.rb @@ -3,51 +3,30 @@ def self.buildbedhere suspend { raise 'where to ?' if cursor.x < 0 - # TODO tweak that more item = world.items.all.find { |i| i.kind_of?(ItemBedst) and i.itemrefs.empty? and - i.jobs.empty? + !i.flags.in_job } raise 'no free bed, build more !' if not item - puts 'alloc c++' job = Job.cpp_new - joblink = JobListLink.cpp_new jobitemref = JobItemRef.cpp_new refbuildingholder = GeneralRefBuildingHolderst.cpp_new building = BuildingBedst.cpp_new - itemtjobs = Item_TJobs.cpp_new + itemjob = SpecificRef.cpp_new - puts 'update global counters' - # TODO - buildingid = world.buildings.all[-1].id + 1 - jobid = df.job_next_id - df.job_next_id = jobid+1 + buildingid = df.building_next_id + df.building_next_id += 1 - puts 'init objects' - job.id = jobid - job.list_link = joblink job.job_type = :ConstructBuilding - job.unk2 = -1 # XXX job.pos = cursor - job.completion_timer = -1 - # unk4a/4b uninit job.mat_type = item.mat_type job.mat_index = item.mat_index - job.unk5 = -1 - job.unk6 = -1 - job.item_subtype = -1 job.hist_figure_id = -1 job.items << jobitemref job.references << refbuildingholder - lastjob = world.job_list - lastjob = lastjob.next while lastjob.next - lastjob.next = joblink - joblink.prev = lastjob - joblink.item = job - jobitemref.item = item jobitemref.role = :Hauled jobitemref.job_item_idx = -1 @@ -62,26 +41,18 @@ def self.buildbedhere building.race = ui.race_id building.id = buildingid building.jobs << job - building.anon_3 = -1 - building.anon_4 = -1 + building.specific_squad = -1 - itemtjobs.unk1 = 2 - itemtjobs.job = job + itemjob.type = :JOB + itemjob.job = job item.flags.in_job = true - item.jobs << itemtjobs + item.specific_refs << itemjob - puts 'update vectors' + map_block_at(cursor).occupancy[cursor.x%16][cursor.y%16].building = :Planned + link_job job world.buildings.all << building - # XXX - world.buildings.other[0] << building - world.buildings.other[4] << building - world.buildings.other[8] << building - world.buildings.other[9] << building - world.buildings.other[10] << building - world.buildings.other[32] << building - # not in bad - + building.categorize(true) } end end From bfb26f1c1fb67d54a76f2f383e9882fcf0024630 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 6 May 2012 00:49:23 +0200 Subject: [PATCH 59/72] ruby: deconstructbld --- plugins/ruby/plugins/buildbed.rb | 12 ++++++++++++ plugins/ruby/ruby-memstruct.rb | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/plugins/buildbed.rb b/plugins/ruby/plugins/buildbed.rb index 892a75dc1..bb886428e 100644 --- a/plugins/ruby/plugins/buildbed.rb +++ b/plugins/ruby/plugins/buildbed.rb @@ -55,4 +55,16 @@ def self.buildbedhere building.categorize(true) } end +def self.deconstructbldhere + suspend { + raise "'q'uery a building" if ui.main.mode != :QueryBuilding or not building = world.selected_building + job = Job.cpp_new + refbuildingholder = GeneralRefBuildingHolderst.cpp_new + job.job_type = :DestroyBuilding + refbuildingholder.building_id = building.id + job.references << refbuildingholder + building.jobs << job + link_job job + } +end end diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index ddc62982e..cf8a54d9e 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -1,6 +1,6 @@ module DFHack module MemHack -INSPECT_SIZE_LIMIT=1024 +INSPECT_SIZE_LIMIT=16384 class MemStruct attr_accessor :_memaddr def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end @@ -13,6 +13,7 @@ class Compound < MemStruct attr_accessor :_fields, :_rtti_classname, :_sizeof def field(name, offset) struct = yield + return if not struct @_fields ||= [] @_fields << [name, offset, struct] define_method(name) { struct._at(@_memaddr+offset)._get } From 7ee4c584b936991457bd1ce5ef091e1ad0c72169 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 6 May 2012 22:42:06 +0200 Subject: [PATCH 60/72] ruby: tweak enums, pointerary --- plugins/ruby/codegen.pl | 9 +++++++-- plugins/ruby/ruby-memstruct.rb | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 81ca15137..4f0d04ebe 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -65,6 +65,7 @@ sub render_enum_fields { push @lines_rb, "$idxname = Hash.new"; my %attr_type; + my %attr_list; for my $attr ($type->findnodes('child::enum-attr')) { my $rbattr = rb_ucase($attr->getAttribute('name')); my $typeattr = $attr->getAttribute('type-name'); @@ -80,7 +81,10 @@ sub render_enum_fields { } my $def = $attr->getAttribute('default-value'); - if ($def) { + if ($attr->getAttribute('is-list')) { + push @lines_rb, "$rbattr = Hash.new { |h, k| h[k] = [] }"; + $attr_list{$rbattr} = 1; + } elsif ($def) { $def = ":$def" if ($attr_type{$rbattr} eq 'symbol'); $def =~ s/'/\\'/g if ($attr_type{$rbattr} eq 'quote'); $def = "'$def'" if ($attr_type{$rbattr} eq 'quote'); @@ -101,10 +105,11 @@ sub render_enum_fields { my $ian = $iattr->getAttribute('name'); my $iav = $iattr->getAttribute('value'); my $rbattr = rb_ucase($ian); + my $op = ($attr_list{$rbattr} ? '<<' : '='); $iav = ":$iav" if ($attr_type{$rbattr} eq 'symbol'); $iav =~ s/'/\\'/g if ($attr_type{$rbattr} eq 'quote'); $iav = "'$iav'" if ($attr_type{$rbattr} eq 'quote'); - $lines_rb[$#lines_rb] .= " ; ${rbattr}[$value] = $iav"; + $lines_rb[$#lines_rb] .= " ; ${rbattr}[:$rbelemname] $op $iav"; } } } diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index cf8a54d9e..4393cc76c 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -294,6 +294,12 @@ class PointerAry < MemStruct (DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + delta end + def _get + addr = _getp + return if addr == 0 + self + end + def [](i) addr = _getp(i) return if addr == 0 @@ -305,7 +311,7 @@ class PointerAry < MemStruct @_tg._at(addr)._set(v) end - def inspect ; "#" ; end + def inspect ; ptr = _getp ; (ptr == 0) ? 'NULL' : "#" ; end end module IndexEnum def indexenum(idx) From 607cd9c8f2171ee82f72761148b7efe0b2ddf965 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 10 May 2012 23:27:42 +0200 Subject: [PATCH 61/72] ruby: tweak enums --- plugins/ruby/codegen.pl | 49 +++++++++++++++++++++------------- plugins/ruby/plugins/unit.rb | 2 +- plugins/ruby/ruby-memstruct.rb | 49 +++++++++++++++++++--------------- plugins/ruby/ruby.rb | 4 +-- 4 files changed, 61 insertions(+), 43 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 4f0d04ebe..4857f0d30 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -27,13 +27,6 @@ sub rb_ucase { return join("", map { ucfirst $_ } (split('_', $name))); } -my %global_type_renderer = ( - 'enum-type' => \&render_global_enum, - 'struct-type' => \&render_global_struct, - 'class-type' => \&render_global_class, - 'bitfield-type' => \&render_global_bitfield, -); - my %item_renderer = ( 'global' => \&render_item_global, 'number' => \&render_item_number, @@ -51,7 +44,7 @@ sub render_global_enum { my ($name, $type) = @_; my $rbname = rb_ucase($name); - push @lines_rb, "class $rbname"; + push @lines_rb, "class $rbname < MemHack::Enum"; indent_rb { render_enum_fields($type); }; @@ -61,8 +54,8 @@ sub render_enum_fields { my ($type) = @_; my $value = -1; - my $idxname = 'ENUM'; - push @lines_rb, "$idxname = Hash.new"; + push @lines_rb, "ENUM = Hash.new"; + push @lines_rb, "NUME = Hash.new"; my %attr_type; my %attr_list; @@ -100,7 +93,7 @@ sub render_enum_fields { if ($elemname) { my $rbelemname = rb_ucase($elemname); - push @lines_rb, "$rbelemname = $value ; ${idxname}[$value] = :$rbelemname"; + push @lines_rb, "ENUM[$value] = :$rbelemname ; NUME[:$rbelemname] = $value"; for my $iattr ($item->findnodes('child::item-attr')) { my $ian = $iattr->getAttribute('name'); my $iav = $iattr->getAttribute('value'); @@ -119,6 +112,10 @@ sub render_enum_fields { sub render_global_bitfield { my ($name, $type) = @_; + push @lines_cpp, "}" if @include_cpp; + push @lines_cpp, "void cpp_$name(FILE *fout) {"; + push @include_cpp, $name; + my $rbname = rb_ucase($name); push @lines_rb, "class $rbname < MemHack::Compound"; indent_rb { @@ -146,7 +143,7 @@ sub render_bitfield_fields { if ($count == 1) { push @lines_rb, "field(:$name, 0) { bit $shift }" if ($name); } elsif ($enum) { - push @lines_rb, "field(:$name, 0) { bits $shift, $count, :$enum }" if ($name); + push @lines_rb, "field(:$name, 0) { bits $shift, $count, $enum }" if ($name); } else { push @lines_rb, "field(:$name, 0) { bits $shift, $count }" if ($name); } @@ -367,7 +364,7 @@ sub render_item_number { return; } $lines_rb[$#lines_rb] .= ", $initvalue" if ($initvalue); - $lines_rb[$#lines_rb] .= ", :$typename" if ($typename); + $lines_rb[$#lines_rb] .= ", $typename" if ($typename); } sub render_item_compound { @@ -391,7 +388,7 @@ sub render_item_compound { }; push @lines_rb, "}" } elsif ($subtype eq 'enum') { - push @lines_rb, "class ::DFHack::$classname"; + push @lines_rb, "class ::DFHack::$classname < MemHack::Enum"; indent_rb { # declare constants render_enum_fields($item); @@ -425,7 +422,7 @@ sub render_item_container { push @lines_rb, "}"; } elsif ($indexenum) { $indexenum = rb_ucase($indexenum); - push @lines_rb, "$rbmethod(:$indexenum)"; + push @lines_rb, "$rbmethod($indexenum)"; } else { push @lines_rb, "$rbmethod"; } @@ -457,7 +454,7 @@ sub render_item_staticarray { my $indexenum = $item->getAttribute('index-enum'); if ($indexenum) { $indexenum = rb_ucase($indexenum); - push @lines_rb, "static_array($count, $tglen, :$indexenum) {"; + push @lines_rb, "static_array($count, $tglen, $indexenum) {"; } else { push @lines_rb, "static_array($count, $tglen) {"; } @@ -602,12 +599,26 @@ if ($offsetfile) { my $doc = XML::LibXML->new()->parse_file($input); $global_types{$_->getAttribute('type-name')} = $_ foreach $doc->findnodes('/ld:data-definition/ld:global-type'); +my @nonenums; for my $name (sort { $a cmp $b } keys %global_types) { my $type = $global_types{$name}; my $meta = $type->getAttribute('ld:meta'); - my $renderer = $global_type_renderer{$meta}; - if ($renderer) { - $renderer->($name, $type); + if ($meta eq 'enum-type') { + render_global_enum($name, $type); + } else { + push @nonenums, $name; + } +} + +for my $name (@nonenums) { + my $type = $global_types{$name}; + my $meta = $type->getAttribute('ld:meta'); + if ($meta eq 'struct-type') { + render_global_struct($name, $type); + } elsif ($meta eq 'class-type') { + render_global_class($name, $type); + } elsif ($meta eq 'bitfield-type') { + render_global_bitfield($name, $type); } else { print "no render global type $meta\n"; } diff --git a/plugins/ruby/plugins/unit.rb b/plugins/ruby/plugins/unit.rb index 05ff17dae..9a00b2bfa 100644 --- a/plugins/ruby/plugins/unit.rb +++ b/plugins/ruby/plugins/unit.rb @@ -3,7 +3,7 @@ module DFHack def self.unit_citizens race = ui.race_id civ = ui.civ_id - world.units.other[0].find_all { |u| + world.units.active.find_all { |u| u.race == race and u.civ_id == civ and !u.flags1.dead and !u.flags1.merchant and !u.flags1.diplomat and !u.flags2.resident and !u.flags3.ghostly and !u.curse.add_tags1.OPPOSED_TO_LIFE and !u.curse.add_tags1.CRAZED and diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 4393cc76c..40f35bb42 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -160,6 +160,25 @@ class Compound < MemStruct end end +class Enum + # number -> symbol + def self.enum + # ruby weirdness, needed to make the constants 'virtual' + @enum ||= const_get(:ENUM) + end + # symbol -> number + def self.nume + @nume ||= const_get(:NUME) + end + + def self.to_i(i) + nume[i] || i + end + def self.to_sym(i) + enum[i] || i + end +end + class Number < MemStruct attr_accessor :_bits, :_signed, :_initvalue, :_enum def initialize(bits, signed, initvalue, enum) @@ -177,12 +196,12 @@ class Number < MemStruct when 64;(DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + (DFHack.memory_read_int32(@_memaddr+4) << 32) end v &= (1 << @_bits) - 1 if not @_signed - v = DFHack.const_get(@_enum)::ENUM[v] || v if @_enum + v = @_enum.to_sym(v) if @_enum v end def _set(v) - v = DFHack.const_get(@_enum).const_get(v) if @_enum and v.kind_of?(::Symbol) + v = @_enum.to_i(v) if @_enum case @_bits when 32; DFHack.memory_write_int32(@_memaddr, v) when 16; DFHack.memory_write_int16(@_memaddr, v) @@ -225,7 +244,7 @@ class BitField < MemStruct ((v & 1) == 0) ? false : true else v &= _mask - v = DFHack.const_get(@_enum)::ENUM[v] || v if @_enum + v = @_enum.to_sym(v) if @_enum v end end @@ -235,7 +254,7 @@ class BitField < MemStruct # allow 'bit = 0' v = (v && v != 0 ? 1 : 0) end - v = DFHack.const_get(@_enum).const_get(v) if @_enum and v.kind_of?(::Symbol) + v = @_enum.to_i(v) if @_enum v = (v & _mask) << @_shift ori = DFHack.memory_read_int32(@_memaddr) & 0xffffffff @@ -313,21 +332,11 @@ class PointerAry < MemStruct def inspect ; ptr = _getp ; (ptr == 0) ? 'NULL' : "#" ; end end -module IndexEnum - def indexenum(idx) - if idx.kind_of?(::Symbol) or idx.kind_of?(::String) and _indexenum - DFHack.const_get(_indexenum).const_get(idx) || idx - else - idx - end - end -end module Enumerable include ::Enumerable attr_accessor :_indexenum def each ; (0...length).each { |i| yield self[i] } ; end def inspect - enum = DFHack.const_get(_indexenum)::ENUM if _indexenum out = '[' each_with_index { |e, idx| out << ', ' if out.length > 1 @@ -335,7 +344,7 @@ module Enumerable out << '...' break end - out << "#{enum[idx] || idx}=" if enum + out << "#{_indexenum.to_sym(idx)}=" if _indexenum out << e.inspect } out << ']' @@ -344,7 +353,6 @@ module Enumerable def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end end class StaticArray < MemStruct - include IndexEnum attr_accessor :_tglen, :_length, :_indexenum, :_tg def initialize(tglen, length, indexenum, tg) @_tglen = tglen @@ -364,12 +372,12 @@ class StaticArray < MemStruct @_tg._at(@_memaddr + i*@_tglen) if i >= 0 and i < @_length end def [](i) - i = indexenum(i) + i = _indexenum.to_i(i) if _indexenum i += @_length if i < 0 _tgat(i)._get end def []=(i, v) - i = indexenum(i) + i = _indexenum.to_i(i) if _indexenum i += @_length if i < 0 _tgat(i)._set(v) end @@ -547,7 +555,6 @@ class StlDeque < MemStruct end class DfFlagarray < MemStruct - include IndexEnum attr_accessor :_indexenum def initialize(indexenum) @_indexenum = indexenum @@ -561,12 +568,12 @@ class DfFlagarray < MemStruct DFHack.memory_bitarray_resize(@_memaddr, len) end def [](idx) - idx = indexenum(idx) + idx = _indexenum.to_i(idx) if _indexenum idx += length if idx < 0 DFHack.memory_bitarray_isset(@_memaddr, idx) if idx >= 0 and idx < length end def []=(idx, v) - idx = indexenum(idx) + idx = _indexenum.to_i(idx) if _indexenum idx += length if idx < 0 if idx >= length or idx < 0 raise 'invalid idx' diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 99767b3e1..94544adf6 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -88,7 +88,7 @@ module DFHack case ui.main.mode when :ViewUnits # nobody selected => idx == 0 - v = world.units.other[0][ui_selected_unit] + v = world.units.active[ui_selected_unit] v if v and v.pos.z == cursor.z when :LookAround k = ui_look_list.items[ui_look_cursor] @@ -261,7 +261,7 @@ module DFHack # link a job to the world # allocate & set job.id, allocate a JobListLink, link to job & world.job_list - def link_job(job) + def job_link(job) lastjob = world.job_list lastjob = lastjob.next while lastjob.next joblink = JobListLink.cpp_new From dfe86f9bd7e0c6def51d6d5179f9018c1e70d950 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 11 May 2012 01:34:03 +0200 Subject: [PATCH 62/72] ruby: interpret types in vmethod signatures --- plugins/ruby/README | 2 +- plugins/ruby/codegen.pl | 56 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/plugins/ruby/README b/plugins/ruby/README index 41e165459..7238c161d 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -40,7 +40,7 @@ Set a custom nickname to unit with id '123' df.find_unit(123).name.nickname = 'moo' Show current unit profession - p DFHack::Profession::ENUM[df.find_unit.profession] + p df.find_unit.profession Center the screen on unit '123' df.center_viewscreen(df.find_unit(123)) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 4857f0d30..5c60fcb41 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -234,14 +234,23 @@ sub render_class_vmethods { my $name = $meth->getAttribute('name'); if ($name) { my @argnames; + my @argargs; for my $arg ($meth->findnodes('child::ld:field')) { my $nr = $#argnames + 1; - push @argnames, lcfirst($arg->getAttribute('name') || "arg$nr"); + my $argname = lcfirst($arg->getAttribute('name') || "arg$nr"); + push @argnames, $argname; + if ($arg->getAttribute('ld:meta') eq 'global' and $arg->getAttribute('ld:subtype') eq 'enum') { + push @argargs, rb_ucase($arg->getAttribute('type-name')) . ".to_i($argname)"; + } else { + push @argargs, $argname; + } } push @lines_rb, "def $name(" . join(', ', @argnames) . ')'; indent_rb { - push @lines_rb, "DFHack.vmethod_call(self, $voff" . join('', map { ", $_" } @argnames) . ')' - # TODO interpret return type (eg pointer to vector) + my $args = join('', map { ", $_" } @argargs); + my $call = "DFHack.vmethod_call(self, $voff$args)"; + my $ret = $meth->findnodes('child::ret-type')->[0]; + render_class_vmethod_ret($call, $ret); }; push @lines_rb, 'end'; } @@ -251,6 +260,47 @@ sub render_class_vmethods { } } +sub render_class_vmethod_ret { + my ($call, $ret) = @_; + if (!$ret) { + push @lines_rb, "$call ; nil"; + return; + } + my $retmeta = $ret->getAttribute('ld:meta') || ''; + if ($retmeta eq 'global') { # enum + my $retname = $ret->getAttribute('type-name'); + if ($retname and $global_types{$retname} and + $global_types{$retname}->getAttribute('ld:meta') eq 'enum-type') { + push @lines_rb, rb_ucase($retname) . ".to_sym($call)"; + } else { + print "vmethod global nonenum $call\n"; + push @lines_rb, $call; + } + } elsif ($retmeta eq 'number') { + my $retsubtype = $ret->getAttribute('ld:subtype'); + my $retbits = $ret->getAttribute('ld:bits'); + push @lines_rb, "val = $call"; + if ($retsubtype eq 'bool') { + push @lines_rb, "(val & 1) != 0"; + } elsif ($ret->getAttribute('ld:unsigned')) { + push @lines_rb, "val & ((1 << $retbits) - 1)"; + } else { # signed + push @lines_rb, "val &= ((1 << $retbits) - 1)"; + push @lines_rb, "((val >> ($retbits-1)) & 1) == 0 ? val : val - (1 << $retbits)"; + } + } elsif ($retmeta eq 'pointer') { + push @lines_rb, "ptr = $call"; + push @lines_rb, "class << self"; + indent_rb { + render_item($ret->findnodes('child::ld:item')->[0]); + }; + push @lines_rb, "end._at(ptr) if ptr != 0"; + } else { + print "vmethod unkret $call\n"; + push @lines_rb, $call; + } +} + sub render_global_objects { my (@objects) = @_; my @global_objects; From 2629db5ed169534d3f868e7b0fc7825df7d5e6c7 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 13 May 2012 01:40:44 +0200 Subject: [PATCH 63/72] ruby: add building creation code --- plugins/ruby/plugins/buildbed.rb | 70 -------- plugins/ruby/plugins/building.rb | 267 +++++++++++++++++++++++++++++++ plugins/ruby/ruby.rb | 18 +++ 3 files changed, 285 insertions(+), 70 deletions(-) delete mode 100644 plugins/ruby/plugins/buildbed.rb create mode 100644 plugins/ruby/plugins/building.rb diff --git a/plugins/ruby/plugins/buildbed.rb b/plugins/ruby/plugins/buildbed.rb deleted file mode 100644 index bb886428e..000000000 --- a/plugins/ruby/plugins/buildbed.rb +++ /dev/null @@ -1,70 +0,0 @@ -module DFHack -def self.buildbedhere - suspend { - raise 'where to ?' if cursor.x < 0 - - item = world.items.all.find { |i| - i.kind_of?(ItemBedst) and - i.itemrefs.empty? and - !i.flags.in_job - } - raise 'no free bed, build more !' if not item - - job = Job.cpp_new - jobitemref = JobItemRef.cpp_new - refbuildingholder = GeneralRefBuildingHolderst.cpp_new - building = BuildingBedst.cpp_new - itemjob = SpecificRef.cpp_new - - buildingid = df.building_next_id - df.building_next_id += 1 - - job.job_type = :ConstructBuilding - job.pos = cursor - job.mat_type = item.mat_type - job.mat_index = item.mat_index - job.hist_figure_id = -1 - job.items << jobitemref - job.references << refbuildingholder - - jobitemref.item = item - jobitemref.role = :Hauled - jobitemref.job_item_idx = -1 - - refbuildingholder.building_id = buildingid - - building.x1 = building.x2 = building.centerx = cursor.x - building.y1 = building.y2 = building.centery = cursor.y - building.z = cursor.z - building.mat_type = item.mat_type - building.mat_index = item.mat_index - building.race = ui.race_id - building.id = buildingid - building.jobs << job - building.specific_squad = -1 - - itemjob.type = :JOB - itemjob.job = job - - item.flags.in_job = true - item.specific_refs << itemjob - - map_block_at(cursor).occupancy[cursor.x%16][cursor.y%16].building = :Planned - link_job job - world.buildings.all << building - building.categorize(true) - } -end -def self.deconstructbldhere - suspend { - raise "'q'uery a building" if ui.main.mode != :QueryBuilding or not building = world.selected_building - job = Job.cpp_new - refbuildingholder = GeneralRefBuildingHolderst.cpp_new - job.job_type = :DestroyBuilding - refbuildingholder.building_id = building.id - job.references << refbuildingholder - building.jobs << job - link_job job - } -end -end diff --git a/plugins/ruby/plugins/building.rb b/plugins/ruby/plugins/building.rb new file mode 100644 index 000000000..15212092d --- /dev/null +++ b/plugins/ruby/plugins/building.rb @@ -0,0 +1,267 @@ +module DFHack + +# allocate a new building object +def self.building_alloc(type, subtype=-1, custom=-1) + type = BuildingType.to_sym(type) + cls = rtti_n2c[BuildingType::Classname[type].to_sym] + raise "invalid building type #{type.inspect}" if not cls + bld = cls.cpp_new + bld.race = ui.race_id + bld.setSubtype(subtype) if subtype != -1 + bld.setCustomType(custom) if custom != -1 + case type + when :Furnace; bld.melt_remainder[world.raws.inorganics.length] = 0 + when :Coffin; bld.initBurialFlags + when :Trap; bld.unk_cc = 500 if bld.trap_type == :PressurePlate + end + bld +end + +# used by building_setsize +def self.building_check_bridge_support(bld) + x1 = bld.x1-1 + x2 = bld.x2+1 + y1 = bld.y1-1 + y2 = bld.y2+1 + z = bld.z + (x1..x2).each { |x| + (y1..y2).each { |y| + next if ((x == x1 or x == x2) and + (y == y1 or y == y2)) + if mb = map_block_at(x, y, z) and tile = mb.tiletype[x%16][y%16] and TiletypeShape::BasicShape[Tiletype::Shape[tile]] == :Open + bld.gate_flags.has_support = true + return + end + } + } + bld.gate_flags.has_support = false +end + +# sets x2/centerx/y2/centery from x1/y1/bldtype +# x2/y2 preserved for :FarmPlot etc +def self.building_setsize(bld) + bld.x2 = bld.x1 if bld.x1 > bld.x2 + bld.y2 = bld.y1 if bld.y1 > bld.y2 + case bld.getType + when :Bridge + bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2 + bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2 + building_check_bridge_support(bld) + when :FarmPlot, :RoadDirt, :RoadPaved, :Stockpile, :Civzone + bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2 + bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2 + when :TradeDepot, :Shop + bld.x2 = bld.x1+4 + bld.y2 = bld.y1+4 + bld.centerx = bld.x1+2 + bld.centery = bld.y1+2 + when :SiegeEngine, :Windmill, :Wagon + bld.x2 = bld.x1+2 + bld.y2 = bld.y1+2 + bld.centerx = bld.x1+1 + bld.centery = bld.y1+1 + when :AxleHorizontal + if bld.is_vertical == 1 + bld.x2 = bld.centerx = bld.x1 + bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2 + else + bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2 + bld.y2 = bld.centery = bld.y1 + end + when :WaterWheel + if bld.is_vertical == 1 + bld.x2 = bld.centerx = bld.x1 + bld.y2 = bld.y1+2 + bld.centery = bld.y1+1 + else + bld.x2 = bld.x1+2 + bld.centerx = bld.x1+1 + bld.y2 = bld.centery = bld.y1 + end + when :Workshop, :Furnace + # Furnace = Custom or default case only + case bld.type + when :Quern, :Millstone, :Tool + bld.x2 = bld.centerx = bld.x1 + bld.y2 = bld.centery = bld.y1 + when :Siege, :Kennels + bld.x2 = bld.x1+4 + bld.y2 = bld.y1+4 + bld.centerx = bld.x1+2 + bld.centery = bld.y1+2 + when :Custom + if bdef = world.raws.buildings.all.binsearch(bld.getCustomType) + bld.x2 = bld.x1 + bdef.dim_x - 1 + bld.y2 = bld.y1 + bdef.dim_y - 1 + bld.centerx = bld.x1 + bdef.workloc_x + bld.centery = bld.y1 + bdef.workloc_y + end + else + bld.x2 = bld.x1+2 + bld.y2 = bld.y1+2 + bld.centerx = bld.x1+1 + bld.centery = bld.y1+1 + end + when :ScrewPump + case bld.direction + when :FromEast + bld.x2 = bld.centerx = bld.x1+1 + bld.y2 = bld.centery = bld.y1 + when :FromSouth + bld.x2 = bld.centerx = bld.x1 + bld.y2 = bld.centery = bld.y1+1 + when :FromWest + bld.x2 = bld.x1+1 + bld.y2 = bld.centery = bld.y1 + bld.centerx = bld.x1 + else + bld.x2 = bld.x1+1 + bld.y2 = bld.centery = bld.y1 + bld.centerx = bld.x1 + end + when :Well + bld.bucket_z = bld.z + bld.x2 = bld.centerx = bld.x1 + bld.y2 = bld.centery = bld.y1 + when :Construction + bld.x2 = bld.centerx = bld.x1 + bld.y2 = bld.centery = bld.y1 + bld.setMaterialAmount(1) + return + else + bld.x2 = bld.centerx = bld.x1 + bld.y2 = bld.centery = bld.y1 + end + bld.setMaterialAmount((bld.x2-bld.x1+1)*(bld.y2-bld.y1+1)/4+1) +end + +# set building at position, with optional width/height +def self.building_position(bld, pos, w=nil, h=nil) + bld.x1 = pos.x + bld.y1 = pos.y + bld.z = pos.z + bld.x2 = bld.x1+w-1 if w + bld.y2 = bld.y1+h-1 if h + building_setsize(bld) +end + +# set map occupancy/stockpile/etc for a building +def self.building_setoccupancy(bld) + stockpile = (bld.getType == :Stockpile) + complete = (bld.getBuildStage >= bld.getMaxBuildStage) + extents = (bld.room.extents and bld.isExtentShaped) + + z = bld.z + (bld.x1..bld.x2).each { |x| + (bld.y1..bld.y2).each { |y| + next if !extents or bld.room.extents[bld.room.width*(y-bld.room.y)+(x-bld.room.x)] == 0 + next if not mb = map_block_at(x, y, z) + des = mb.designation[x%16][y%16] + des.pile = stockpile + des.dig = :No + if complete + bld.updateOccupancy(x, y) + else + mb.occupancy[x%16][y%16].building = :Planned + end + } + } +end + +# link bld into other rooms if it is inside their extents +def self.building_linkrooms(bld) + didstuff = false + world.buildings.other[:ANY_FREE].each { |ob| + next if !ob.is_room or ob.z != bld.z + next if !ob.room.extents or !ob.isExtentShaped or ob.room.extents[ob.room.width*(bld.y1-ob.room.y)+(bld.x1-ob.room.x)] == 0 + didstuff = true + ob.children << bld + bld.parents << ob + } + ui.equipment.update.buildings = true if didstuff +end + +# link the building into the world, set map data, link rooms, bld.id +def self.building_link(bld) + bld.id = df.building_next_id + df.building_next_id += 1 + + world.buildings.all << bld + bld.categorize(true) + building_setoccupancy(bld) if bld.isSettingOccupancy + building_linkrooms(bld) +end + +# set a design for the building +def self.building_createdesign(bld, rough=true) + job = bld.jobs[0] + job.mat_type = bld.mat_type + job.mat_index = bld.mat_index + if bld.needsDesign + bld.design = BuildingDesign.cpp_new + bld.design.flags.rough = rough + end +end + +# creates a job to build bld, return it +def self.building_linkforconstruct(bld) + building_link bld + ref = GeneralRefBuildingHolderst.cpp_new + ref.building_id = bld.id + job = Job.cpp_new + job.job_type = :ConstructBuilding + job.pos = [bld.centerx, bld.centery, bld.z] + job.references << ref + bld.jobs << job + job_link job + job +end + +# construct a building with items or JobItems +def self.building_construct(bld, items) + job = building_linkforconstruct(bld) + rough = false + items.each { |item| + if items.kind_of?(JobItem) + item.quantity = (bld.x2-bld.x1+1)*(bld.y2-bld.y1+1)/4+1 if item.quantity < 0 + job.job_items << item + else + job_attachitem(job, item, :Hauled) + end + rough = true if item.getType == :BOULDER + bld.mat_type = item.getMaterial if bld.mat_type == -1 + bld.mat_index = item.getMaterialIndex if bld.mat_index == -1 + } + building_createdesign(bld, rough) +end + +# creates a job to deconstruct the building +def self.building_deconstruct(bld) + job = Job.cpp_new + refbuildingholder = GeneralRefBuildingHolderst.cpp_new + job.job_type = :DestroyBuilding + refbuildingholder.building_id = building.id + job.references << refbuildingholder + building.jobs << job + job_link job + job +end + +# exemple usage +def self.buildbed(pos=cursor) + suspend { + raise 'where to ?' if pos.x < 0 + + item = world.items.all.find { |i| + i.kind_of?(ItemBedst) and + i.itemrefs.empty? and + !i.flags.in_job + } + raise 'no free bed, build more !' if not item + + bld = building_alloc(:Bed) + building_position(bld, pos) + building_construct(bld, [item]) + } +end +end diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 94544adf6..8c218eb63 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -272,6 +272,24 @@ module DFHack df.job_next_id += 1 lastjob.next = joblink end + + # attach an item to a job, flag item in_job + def job_attachitem(job, item, role=:Hauled, filter_idx=-1) + if role != :TargetContainer + item.flags.in_job = true + end + + itemlink = SpecificRef.cpp_new + itemlink.type = :JOB + itemlink.job = job + item.specific_refs << itemlink + + joblink = JobItemRef.cpp_new + joblink.item = item + joblink.role = role + joblink.job_item_idx = filter_idx + job.items << joblink + end end end From df3e1bb51757045230bd8fdaaaf85e76949e93c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 20 May 2012 17:35:34 +0200 Subject: [PATCH 64/72] Sync submodules --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 62fda323c..f5fc9418c 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 62fda323c5976fd51588d310a16cf442124d1412 +Subproject commit f5fc9418c0f8e9491db1f387587d8b6dbddd855a From 8ff4f1a78e89556ca82b97105425487786aad06e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 20 May 2012 22:58:02 +0200 Subject: [PATCH 65/72] Disable ruby plugin by default. --- plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 2c54aebad..3afdbcacc 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -36,7 +36,7 @@ if (BUILD_DWARFEXPORT) add_subdirectory (dwarfexport) endif() -OPTION(BUILD_RUBY "Build ruby binding." ON) +OPTION(BUILD_RUBY "Build ruby binding." OFF) if (BUILD_RUBY) add_subdirectory (ruby) endif() From 87a26ffa51853dfce93684155cda3342433f23a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 21 May 2012 18:49:27 +0200 Subject: [PATCH 66/72] Track structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index f5fc9418c..230511eef 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit f5fc9418c0f8e9491db1f387587d8b6dbddd855a +Subproject commit 230511eef7a6987e34f3e63cfc3eb738ee92b41f From 34f08c0223db45203cb2de760b6a1edca76f39ee Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 21 May 2012 21:30:53 +0400 Subject: [PATCH 67/72] Handle the stocks screen in the focus strings. --- library/modules/Gui.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index f69e11778..2c4322286 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -391,6 +391,17 @@ DEFINE_GET_FOCUS_STRING_HANDLER(layer_assigntrade) focus += "/Items"; } +DEFINE_GET_FOCUS_STRING_HANDLER(stores) +{ + if (!screen->in_right_list) + focus += "/Categories"; + else if (screen->in_group_mode) + focus += "/Groups"; + else + focus += "/Items"; +} + + std::string Gui::getFocusString(df::viewscreen *top) { if (!top) From 7866ab239e4a76afe95f349c0f99686238321cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 21 May 2012 20:28:17 +0200 Subject: [PATCH 68/72] Track structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 230511eef..234d0f57a 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 230511eef7a6987e34f3e63cfc3eb738ee92b41f +Subproject commit 234d0f57a927f306f2052fc2f45d38b3201ddee6 From 81022451c2c474a3f362eddb091ce0444c51243c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 21 May 2012 22:29:03 +0400 Subject: [PATCH 69/72] Support sorting items in stocks, and correct unit sorting in joblist. --- library/modules/Gui.cpp | 22 +++++++++++++++++++++- library/xml | 2 +- plugins/sort.cpp | 24 +++++++++++++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 2c4322286..2290110a8 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -41,6 +41,8 @@ using namespace std; #include "MiscUtils.h" using namespace DFHack; +#include "modules/Job.h" + #include "DataDefs.h" #include "df/world.h" #include "df/global_objects.h" @@ -58,6 +60,7 @@ using namespace DFHack; #include "df/viewscreen_layer_militaryst.h" #include "df/viewscreen_petst.h" #include "df/viewscreen_tradegoodsst.h" +#include "df/viewscreen_storesst.h" #include "df/ui_unit_view_mode.h" #include "df/ui_sidebar_menus.h" #include "df/ui_look_list.h" @@ -353,6 +356,9 @@ DEFINE_GET_FOCUS_STRING_HANDLER(pet) if (vector_get(screen->trainer_unit, screen->trainer_cursor)) focus += "/Unit"; break; + + default: + break; } } @@ -636,7 +642,13 @@ static df::unit *getAnyUnit(df::viewscreen *top) using df::global::ui_selected_unit; if (VIRTUAL_CAST_VAR(screen, df::viewscreen_joblistst, top)) - return vector_get(screen->units, screen->cursor_pos); + { + if (auto unit = vector_get(screen->units, screen->cursor_pos)) + return unit; + if (auto job = vector_get(screen->jobs, screen->cursor_pos)) + return Job::getWorker(job); + return NULL; + } if (VIRTUAL_CAST_VAR(screen, df::viewscreen_unitlistst, top)) return vector_get(screen->units[screen->page], screen->cursor_pos[screen->page]); @@ -792,6 +804,14 @@ static df::item *getAnyItem(df::viewscreen *top) return vector_get(screen->trader_items, screen->trader_cursor); } + if (VIRTUAL_CAST_VAR(screen, df::viewscreen_storesst, top)) + { + if (screen->in_right_list && !screen->in_group_mode) + return vector_get(screen->items, screen->item_cursor); + + return NULL; + } + if (!Gui::dwarfmode_hotkey(top)) return NULL; diff --git a/library/xml b/library/xml index 230511eef..c3c76adcf 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 230511eef7a6987e34f3e63cfc3eb738ee92b41f +Subproject commit c3c76adcf6dab5d733b256e00ecbe40709fba8ff diff --git a/plugins/sort.cpp b/plugins/sort.cpp index 9274a4cfb..5748a065b 100644 --- a/plugins/sort.cpp +++ b/plugins/sort.cpp @@ -6,6 +6,7 @@ #include "modules/Gui.h" #include "modules/Translation.h" #include "modules/Units.h" +#include "modules/Job.h" #include "LuaTools.h" @@ -22,6 +23,7 @@ #include "df/viewscreen_tradegoodsst.h" #include "df/viewscreen_dwarfmodest.h" #include "df/viewscreen_petst.h" +#include "df/viewscreen_storesst.h" #include "df/layer_object_listst.h" #include "df/assign_trade_status.h" @@ -273,7 +275,16 @@ DEFINE_SORT_HANDLER(unit_sorters, joblist, "", jobs) { PARSE_SPEC("units", parameters); - if (compute_order(*pout, L, top, &order, jobs->units)) + std::vector units; + for (size_t i = 0; i < jobs->units.size(); i++) + { + auto unit = jobs->units[i]; + if (!unit && jobs->jobs[i]) + unit = Job::getWorker(jobs->jobs[i]); + units.push_back(unit); + } + + if (compute_order(*pout, L, top, &order, units)) { reorder_cursor(&jobs->cursor_pos, order); reorder_vector(&jobs->units, order); @@ -526,6 +537,17 @@ DEFINE_SORT_HANDLER(item_sorters, layer_assigntrade, "/Items", bring) } } +DEFINE_SORT_HANDLER(item_sorters, stores, "/Items", stocks) +{ + PARSE_SPEC("items", parameters); + + if (compute_order(*pout, L, top, &order, stocks->items)) + { + reorder_cursor(&stocks->item_cursor, order); + reorder_vector(&stocks->items, order); + } +} + static bool item_list_hotkey(df::viewscreen *screen) { auto focus = Gui::getFocusString(screen); From 20794ebf191d21d5acce274d70bdad4012deaa44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 21 May 2012 21:40:51 +0200 Subject: [PATCH 70/72] Bump version to 0.34.10-r1 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d16390c45..bfd56b72a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,10 +60,10 @@ endif() # set up versioning. set(DF_VERSION_MAJOR "0") set(DF_VERSION_MINOR "34") -set(DF_VERSION_PATCH "07") +set(DF_VERSION_PATCH "10") set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}") -SET(DFHACK_RELEASE "r2" CACHE STRING "Current release revision.") +SET(DFHACK_RELEASE "r1" CACHE STRING "Current release revision.") set(DFHACK_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}-${DFHACK_RELEASE}") add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}") From 246df435117a6ae4da08dae8cf015be6d2d4e5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 30 May 2012 01:08:07 +0200 Subject: [PATCH 71/72] Track clsocket --- depends/clsocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/clsocket b/depends/clsocket index 49fa80061..54cad7ca5 160000 --- a/depends/clsocket +++ b/depends/clsocket @@ -1 +1 @@ -Subproject commit 49fa800615a4e5c872164bcb4122030d2ebda9cf +Subproject commit 54cad7ca5431c8e27cfb90f51fdfaf69211f0a4f From 0749233ed1cab8eb53216df8a074bb989c4af04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 30 May 2012 01:12:29 +0200 Subject: [PATCH 72/72] And again... --- depends/clsocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/clsocket b/depends/clsocket index 54cad7ca5..c85e9fb35 160000 --- a/depends/clsocket +++ b/depends/clsocket @@ -1 +1 @@ -Subproject commit 54cad7ca5431c8e27cfb90f51fdfaf69211f0a4f +Subproject commit c85e9fb35d3510c5dcc367056cda3237d77a7add