Sooo, i think noone has made research about this.
im going to explain some things with some examples and then someone searchs an utility for this. Sorry if my non native english fails somewhere, also, im not good at redactions, so this will look messy and redundant sometimes.
If anyone has made a C extension for ruby (the normal one, not RGSS),you know theres a lot of functions to write ruby in pure , like:
This is the native interface for the interpreter itself and every C extension for it.
Obviously all these functions are inside the RGSSX0XX.dll so it can setup and run the scripts.
Now im gonna explain how to use them, there are a lot of ways, using a custom Game.exe, injecting new code on the DLL itself, using another DLL, etc. Im gonna explain the first one using the chinese RGSS Player Ex rewrite as a base and using RGSS102J.dll.
First, the source for the new game.exe:
http://hi.baidu.com/tvga/blog/item/a4da ... 6e0a9.html
Now, lets see how it calls the exported function RGSSInitialize from the DLL:
First, the library is loaded into memory.
Then it defines a function prototype with the return value and the needed parameters.
Creates a function pointer and gets the address of the exported function from the DLL.
Roughly, a function is just a pointer that points to an address with instructions, so you can play with them like you do with normal variables. So, how do we get a pointer to a function that is not exported? we cant use GetProcAddress for this, time for imagination.
Now ill tell you how I make it, theres probably a simpler way that I dont know right now.
First, you need to find the function itself with a debugger (Load the dll on the debugger and find it, theres different ways that im not gonna cover here if noone wants) and note down the address, in this "tutorial" Ill use the rb_define_module function, which address in my DLL is 0x10020D70.
Now the cheap trick: since the memory addresses are not the same as the file addresses, we are going to use the address of an exported function as base for the rest, in this case RGSSGameMain. The address for RGSSGameMain in the file is 0x10003B00.
Time for some code!
Here we are declaring the function prototype for both functions, their pointers and getting the RGSSGameMain address, now what?
While the addresses change from the file to memory, the distance between two instructions is always the same (at least in XP), this means we just need to substract our function address (got it from the debugger) with the exported function address:
Now we this new number like this:
Yay! now we have our pointer pointing to the correct memory address, this mean we can now use the function without problems:
If we compile it and run the game, we will find that the module Dahrkael is defined by default.
And thats it. this can be used to avoid the Win32API overhead, to make some secret code or who knows (i dont know lol).
Another example:
also found an useful pair of functions, rb_funcall2 and rb_intern.
This mean you can call any method of from any class (if you have the class ID)
Then in ruby:
=> Life.testing
=> Message Box saying "The answer is 42"
Ill leave the DLL i used plus some basic function here so someone can try to do something more useful.
http://bb.ohsk.net/uploads/RGSS102J.zip
aaaand now the functions I labeled with the file address, to convert them to memory address see above:
Final note: I hope this will reduce all those ugly Win32API+pure ruby scripts to just a couple of calls, because everything is done internally.
Someone wants to rewrite the Graphics.snap_to_bitmap script this way? :box:
Now, questions, feedback, threats, go ahead.
im going to explain some things with some examples and then someone searchs an utility for this. Sorry if my non native english fails somewhere, also, im not good at redactions, so this will look messy and redundant sometimes.
If anyone has made a C extension for ruby (the normal one, not RGSS),you know theres a lot of functions to write ruby in pure , like:
Code:
rb_define_class()
rb_define_method()
rb_new_str()
Obviously all these functions are inside the RGSSX0XX.dll so it can setup and run the scripts.
Now im gonna explain how to use them, there are a lot of ways, using a custom Game.exe, injecting new code on the DLL itself, using another DLL, etc. Im gonna explain the first one using the chinese RGSS Player Ex rewrite as a base and using RGSS102J.dll.
First, the source for the new game.exe:
http://hi.baidu.com/tvga/blog/item/a4da ... 6e0a9.html
Now, lets see how it calls the exported function RGSSInitialize from the DLL:
Code:
hRgssCore = ::LoadLibraryA("RGSS102J.dll");
typedef void (*RGSSInitialize) (HMODULE hRgssDll);
RGSSInitialize pRGSSInitialize = NULL;
pRGSSInitialize = ::GetProcAddress(hRgssCore, "RGSSInitialize");
Then it defines a function prototype with the return value and the needed parameters.
Creates a function pointer and gets the address of the exported function from the DLL.
Roughly, a function is just a pointer that points to an address with instructions, so you can play with them like you do with normal variables. So, how do we get a pointer to a function that is not exported? we cant use GetProcAddress for this, time for imagination.
Now ill tell you how I make it, theres probably a simpler way that I dont know right now.
First, you need to find the function itself with a debugger (Load the dll on the debugger and find it, theres different ways that im not gonna cover here if noone wants) and note down the address, in this "tutorial" Ill use the rb_define_module function, which address in my DLL is 0x10020D70.
Now the cheap trick: since the memory addresses are not the same as the file addresses, we are going to use the address of an exported function as base for the rest, in this case RGSSGameMain. The address for RGSSGameMain in the file is 0x10003B00.
Time for some code!
Code:
typedef void (*RGSSGameMain) (HWND hWnd, const char* pScriptNames, char** pRgssadName);
typedef unsigned long (*rb_define_moduleF) (const char* name);
rb_define_moduleF rb_define_module = NULL;
RGSSGameMain pRGSSGameMain = NULL;
hRgssCore = ::LoadLibraryA("RGSS102J.dll");
pRGSSGameMain = ::GetProcAddress(hRgssCore, "RGSSGameMain");
While the addresses change from the file to memory, the distance between two instructions is always the same (at least in XP), this means we just need to substract our function address (got it from the debugger) with the exported function address:
Code:
rb_define_module address -> 0x10020D70
RGSSGameMain address -> 0x10003B00
Distance between them -> 0x1D270
Code:
rb_define_module = (rb_define_moduleF) ((int)pRGSSGameMain + 0x1D270);
Code:
unsigned long rb_mDahrkael = rb_define_module("Dahrkael");
And thats it. this can be used to avoid the Win32API overhead, to make some secret code or who knows (i dont know lol).
Another example:
Code:
VALUE rb_mLife = rb_define_module("Life");
rb_define_const(rb_mLife, "MEANING", INT2FIX(42));
This mean you can call any method of from any class (if you have the class ID)
Code:
VALUE testing()
{
VALUE string = rb_str_new("The answer is 42", 16);
rb_funcall2(rb_cObject, rb_intern("print"), 1, string);
return 2;
}
rb_define_singleton_function(rb_mLife, "testing", testing, 0);
=> Life.testing
=> Message Box saying "The answer is 42"
Ill leave the DLL i used plus some basic function here so someone can try to do something more useful.
http://bb.ohsk.net/uploads/RGSS102J.zip
Code:
typedef VALUE (*rb_define_moduleF) (const char* name);
typedef void (*rb_define_constF) (VALUE parent, const char* name, VALUE value);
typedef VALUE (*rb_f_requireF) (VALUE obj, VALUE fname);
typedef VALUE (*rb_str_newF) (char *ptr, long len);
typedef ID (*rb_internF) (const char* name);
typedef VALUE (*rb_funcall2F) (VALUE recv, ID mid, int argc, int argv);
typedef void (*rb_define_singleton_functionF) (VALUE parent,const char* name,VALUE(*)(),int argc);
rb_define_module = (rb_define_moduleF) ((int)pRGSSGameMain + 0x1D270);
rb_define_const = (rb_define_constF) ((int)pRGSSGameMain + 0x7EC00);
rb_f_require = (rb_f_requireF) ((int)pRGSSGameMain + 0x35430);
rb_str_new = (rb_str_newF) ((int)pRGSSGameMain + 0x73680);
rb_define_singleton_function = (rb_define_singleton_functionF) ((int)pRGSSGameMain + 0x1DA10);
rb_funcall2 = (rb_funcall2F) ((int)pRGSSGameMain + 0x266F0);
rb_intern = (rb_internF) ((int)pRGSSGameMain + 0x5330A);
Code:
rb_define_class 0x10020AB0
rb_define_class_under 0x10020BB0
rb_define_module 0x10020D70
rb_define_method 0x10021330
rb_define_singleton_method 0x10021510
rb_scan_args 0x10021620
rb_warn 0x100257A0
rb_name_error 0x10025D60
rb_raise 0x10026090
rb_notimplement 0x10026170
rb_check_type 0x10026940
rb_secure 0x10027030
rb_print_undef_str 0x10027180
rb_funcall2 0x1002A1F0
rb_f_caller 0x1002A4E0
load_failed 0x1002ABA0
rb_f_throw 0x1002D780
eval_string_with_cref 0x10032680
rb_f_eval 0x10032BB0
rb_eval_cmd 0x10032F00
rb_f_catch 0x10036730
raise_method_missing 0x10036DE0
method_missing 0x10036F10
rb_search_method_entry 0x10037860
rb_class_inherited 0x10037B20
rb_require_safe 0x100381A0
rb_require 0x10038460
rb_f_require 0x10038F30
rb_obj_clone 0x10051CF0
rb_obj_dup 0x10051D80
rb_intern 0x10056E0A
rb_id2name 0x10057040
str_new 0x100770F0
rb_str_new 0x10077180
mod_av_set 0x100825F0
rb_define_const 0x10082700
rb_cvar_set 0x10083010
rb_cvar_get 0x10083110
rb_cv_set 0x10083190
rb_cv_get 0x100831D0
rb_mod_remove_cvar 0x10083210
Final note: I hope this will reduce all those ugly Win32API+pure ruby scripts to just a couple of calls, because everything is done internally.
Someone wants to rewrite the Graphics.snap_to_bitmap script this way? :box:
Now, questions, feedback, threats, go ahead.