// blindly copied imports from fastdwarf #include "Core.h" #include "Console.h" #include "Export.h" #include "PluginManager.h" #include "VersionInfo.h" #include "DataDefs.h" #include "df/world.h" #include "df/unit.h" #include "tinythread.h" using namespace DFHack; // DFHack stuff static int df_loadruby(void); static void df_unloadruby(void); static void df_rubythread(void*); static command_result df_rubyeval(color_ostream &out, std::vector & parameters); static void ruby_bind_dfhack(void); // inter-thread communication stuff enum RB_command { RB_IDLE, RB_INIT, RB_DIE, RB_EVAL, }; tthread::mutex *m_irun; tthread::mutex *m_mutex; static volatile RB_command r_type; static volatile command_result r_result; static const char *r_command; static tthread::thread *r_thread; static int onupdate_active; DFHACK_PLUGIN("ruby") DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { onupdate_active = 0; // fail silently instead of spamming the console with 'failed to initialize' // if libruby is not present, the error is still logged in stderr.log if (!df_loadruby()) return CR_OK; // the ruby thread sleeps trying to lock this // when it gets it, it runs according to r_type // when finished, it sets r_type to IDLE and unlocks m_irun = new tthread::mutex(); // when any thread is going to request something to the ruby thread, // lock this before anything, and release when everything is done m_mutex = new tthread::mutex(); r_type = RB_INIT; // create the dedicated ruby thread // df_rubythread starts the ruby interpreter and goes to type=IDLE when done r_thread = new tthread::thread(df_rubythread, 0); // wait until init phase 1 is done while (r_type != RB_IDLE) tthread::this_thread::yield(); // ensure the ruby thread sleeps until we have a command to handle m_irun->lock(); // check return value from rbinit if (r_result == CR_FAILURE) return CR_FAILURE; commands.push_back(PluginCommand("rb_eval", "Ruby interpreter. Eval() a ruby string.", df_rubyeval)); return CR_OK; } DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { // if dlopen failed if (!r_thread) return CR_OK; // ensure ruby thread is idle m_mutex->lock(); r_type = RB_DIE; r_command = NULL; // start ruby thread m_irun->unlock(); // wait until ruby thread ends after RB_DIE r_thread->join(); // cleanup everything delete r_thread; r_thread = 0; delete m_irun; // we can release m_mutex, other users will check r_thread m_mutex->unlock(); delete m_mutex; // dlclose libruby df_unloadruby(); return CR_OK; } // send a single ruby line to be evaluated by the ruby thread DFhackCExport command_result plugin_eval_ruby(const char *command) { // if dlopen failed if (!r_thread) return CR_FAILURE; // wrap all ruby code inside a suspend block // if we dont do that and rely on ruby code doing it, we'll deadlock in // onupdate CoreSuspender suspend; command_result ret; // ensure ruby thread is idle m_mutex->lock(); if (!r_thread) // raced with plugin_shutdown return CR_OK; r_type = RB_EVAL; r_command = command; // wake ruby thread up m_irun->unlock(); // semi-active loop until ruby thread is done while (r_type != RB_IDLE) tthread::this_thread::yield(); ret = r_result; // block ruby thread m_irun->lock(); // let other plugin_eval_ruby run m_mutex->unlock(); return ret; } DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { if (!r_thread) return CR_OK; // ruby sets this flag when needed, to avoid lag running ruby code every // frame if not necessary // TODO dynamic check on df::cur_year{_tick} if (!onupdate_active) return CR_OK; return plugin_eval_ruby("DFHack.onupdate"); } DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event e) { if (!r_thread) return CR_OK; 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); // if we go through plugin_eval at BEGIN_UNLOAD, it'll // try to get the suspend lock and deadlock at df exit case SC_BEGIN_UNLOAD : return CR_OK; #undef SCASE } return plugin_eval_ruby(cmd.c_str()); } static command_result df_rubyeval(color_ostream &out, std::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; } // reconstruct the text from dfhack console line std::string full = ""; for (unsigned i=0 ; i> 1) #define RUBY_METHOD_FUNC(func) ((VALUE(*)(...))func) void (*ruby_sysinit)(int *, const char ***); void (*ruby_init)(void); void (*ruby_init_loadpath)(void); void (*ruby_script)(const char*); void (*ruby_finalize)(void); ID (*rb_intern)(const char*); VALUE (*rb_funcall)(VALUE, ID, int, ...); VALUE (*rb_define_module)(const char*); void (*rb_define_singleton_method)(VALUE, const char*, VALUE(*)(...), int); VALUE (*rb_gv_get)(const char*); VALUE (*rb_str_new)(const char*, long); char* (*rb_string_value_ptr)(VALUE*); VALUE (*rb_eval_string_protect)(const char*, int*); VALUE (*rb_ary_shift)(VALUE); VALUE (*rb_float_new)(double); double (*rb_num2dbl)(VALUE); VALUE (*rb_int2inum)(long); VALUE (*rb_uint2inum)(unsigned long); unsigned long (*rb_num2ulong)(VALUE); // end of rip(ruby.h) DFHack::DFLibrary *libruby_handle; // load the ruby library, initialize function pointers static int df_loadruby(void) { const char *libpath = #if defined(WIN32) "./libruby.dll"; #elif defined(__APPLE__) "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/libruby.1.dylib"; #else "hack/libruby.so"; #endif libruby_handle = OpenPlugin(libpath); if (!libruby_handle) { fprintf(stderr, "Cannot initialize ruby plugin: failed to load %s\n", libpath); return 0; } // ruby_sysinit is optional (ruby1.9 only) ruby_sysinit = (decltype(ruby_sysinit))LookupPlugin(libruby_handle, "ruby_sysinit"); #define rbloadsym(s) if (!(s = (decltype(s))LookupPlugin(libruby_handle, #s))) return 0 rbloadsym(ruby_init); rbloadsym(ruby_init_loadpath); rbloadsym(ruby_script); rbloadsym(ruby_finalize); rbloadsym(rb_intern); rbloadsym(rb_funcall); rbloadsym(rb_define_module); rbloadsym(rb_define_singleton_method); rbloadsym(rb_gv_get); rbloadsym(rb_str_new); rbloadsym(rb_string_value_ptr); rbloadsym(rb_eval_string_protect); rbloadsym(rb_ary_shift); rbloadsym(rb_float_new); rbloadsym(rb_num2dbl); rbloadsym(rb_int2inum); rbloadsym(rb_uint2inum); rbloadsym(rb_num2ulong); #undef rbloadsym return 1; } static void df_unloadruby(void) { if (libruby_handle) { ClosePlugin(libruby_handle); libruby_handle = 0; } } // 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)); } static color_ostream_proxy *console_proxy; // ruby thread main loop static void df_rubythread(void *p) { int state, running; if (ruby_sysinit) { // ruby1.9 specific API static int argc; static const char *argv[] = { "dfhack", 0 }; ruby_sysinit(&argc, (const char ***)&argv); } // 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(); console_proxy = new color_ostream_proxy(Core::getInstance().getConsole()); // ensure noone bothers us while we load data defs in the background m_mutex->lock(); // tell the main thread our initialization is finished r_result = CR_OK; r_type = RB_IDLE; // load the default ruby-level definitions in the background state=0; rb_eval_string_protect("require './hack/ruby/ruby'", &state); if (state) dump_rb_error(); // ready to go m_mutex->unlock(); running = 1; while (running) { // sleep waiting 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_EVAL: state = 0; rb_eval_string_protect(r_command, &state); if (state) dump_rb_error(); break; } r_result = CR_OK; r_type = RB_IDLE; m_irun->unlock(); tthread::this_thread::yield(); } } #define BOOL_ISFALSE(v) ((v) == Qfalse || (v) == Qnil || (v) == INT2FIX(0)) // main DFHack ruby module static VALUE rb_cDFHack; // DFHack module ruby methods, binds specific 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 = (BOOL_ISFALSE(val) ? 0 : 1); return Qtrue; } static VALUE rb_dfprint_str(VALUE self, VALUE s) { console_proxy->print("%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; } static VALUE rb_dfget_global_address(VALUE self, VALUE name) { return rb_uint2inum(Core::getInstance().vinfo->getAddress(rb_string_value_ptr(&name))); } static VALUE rb_dfget_vtable(VALUE self, VALUE name) { 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 // XXX virtual classes only! dark pointer arithmetic, use with caution ! static VALUE rb_dfget_rtti_classname(VALUE self, VALUE vptr) { 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_new(typestring, strlen(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)); } // raw memory access // used by the ruby class definitions // XXX may cause game crash ! double-check your addresses ! static VALUE rb_dfmalloc(VALUE self, VALUE len) { char *ptr = (char*)malloc(FIX2INT(len)); if (!ptr) return Qnil; memset(ptr, 0, FIX2INT(len)); return rb_uint2inum((long)ptr); } static VALUE rb_dffree(VALUE self, VALUE ptr) { free((void*)rb_num2ulong(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)); } // memory reading (integers/floats) static VALUE rb_dfmemory_read_int8(VALUE self, VALUE addr) { return rb_int2inum(*(char*)rb_num2ulong(addr)); } static VALUE rb_dfmemory_read_int16(VALUE self, VALUE addr) { return rb_int2inum(*(short*)rb_num2ulong(addr)); } static VALUE rb_dfmemory_read_int32(VALUE self, VALUE addr) { 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)); memcpy((void*)rb_num2ulong(addr), rb_string_value_ptr(&raw), strlen); return Qtrue; } // 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_dfmemory_write_int16(VALUE self, VALUE addr, VALUE val) { *(short*)rb_num2ulong(addr) = rb_num2ulong(val); return Qtrue; } static VALUE rb_dfmemory_write_int32(VALUE self, VALUE addr, VALUE val) { *(int*)rb_num2ulong(addr) = rb_num2ulong(val); return Qtrue; } static VALUE rb_dfmemory_write_float(VALUE self, VALUE addr, VALUE val) { *(float*)rb_num2ulong(addr) = rb_num2dbl(val); return Qtrue; } // 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) { std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum(v->size()); } static VALUE rb_dfmemory_vec8_ptrat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } 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; } 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; } // 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 VALUE rb_dfmemory_vec16_ptrat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } static VALUE rb_dfmemory_vec16_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_vec16_delete(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); return Qtrue; } // vector 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_ptrat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } 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; } // 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); 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); 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) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(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*8); // b->size is in bytes } 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; } /* call an arbitrary object virtual method */ #ifdef WIN32 __declspec(naked) static int raw_vcall(char **that, unsigned long voff, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) { // __thiscall requires that the callee cleans up the stack // here we dont know how many arguments it will take, so // we simply fix esp across the funcall __asm { push ebp mov ebp, esp push a3 push a2 push a1 push a0 mov ecx, that mov eax, [ecx] add eax, voff call [eax] mov esp, ebp pop ebp ret } } #else static int raw_vcall(char **that, unsigned long voff, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) { int (*fptr)(char **me, int, int, int, int); fptr = (decltype(fptr))*(void**)(*that + voff); return fptr(that, a0, a1, a2, a3); } #endif // call an arbitrary vmethod, convert args/ret to native values for raw_vcall static VALUE rb_dfvcall(VALUE self, VALUE cppobj, VALUE cppvoff, VALUE a0, VALUE a1, VALUE a2, VALUE a3) { return rb_int2inum(raw_vcall((char**)rb_num2ulong(cppobj), rb_num2ulong(cppvoff), rb_num2ulong(a0), rb_num2ulong(a1), rb_num2ulong(a2), rb_num2ulong(a3))); } // define module DFHack and its methods static void ruby_bind_dfhack(void) { rb_cDFHack = rb_define_module("DFHack"); 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, "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, "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, "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_singleton_method(rb_cDFHack, "memory_read", RUBY_METHOD_FUNC(rb_dfmemory_read), 2); 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_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); 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_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_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); 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); }