Skip to content

Instantly share code, notes, and snippets.

@orzFly
Last active May 11, 2019 09:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save orzFly/9641099 to your computer and use it in GitHub Desktop.
Save orzFly/9641099 to your computer and use it in GitHub Desktop.
RGSSRuntime Module 1.0 for RPG Maker XP/VX/VX Ace
=begin
RGSSRuntime Module 1.0 for RPG Maker XP/VX/VX Ace
http://orzFly.com/html/rgssruntime.html
Copyright 2013-2014 Yeechan Lu a.k.a. orzFly <i@orzFly.com>
Partial Copyright 2013-2014 Seiran A. [http://seiran.mist.so/]
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=end
unless defined? RGSSRuntime
module RGSSRuntime; end
class << RGSSRuntime
ASCIICHAR = [0].pack('C')
GetModuleHandleA = Win32API.new("kernel32", "GetModuleHandleA", "p", "l")
LoadAccelerators = Win32API.new("user32", "LoadAccelerators", "ll", "l")
VirtualQuery = Win32API.new("kernel32", "VirtualQuery", "lpl", "l")
RtlMoveMemory_pl = Win32API.new("kernel32", "RtlMoveMemory", "pli", "v")
RtlMoveMemory_lp = Win32API.new("kernel32", "RtlMoveMemory", "lpi", "v")
GetWindowTextA = Win32API.new("user32", "GetWindowTextA", "lpi", "i")
GetWindowTextW = Win32API.new("user32", "GetWindowTextW", "lpi", "i")
GetPrivateProfileStringA = Win32API.new('kernel32', 'GetPrivateProfileStringA','ppppip', 'v')
GetWindowThreadProcessId = Win32API.new("user32", "GetWindowThreadProcessId", "LP", "L")
GetWindow = Win32API.new("user32", "GetWindow", "LL", "L")
GetClassName = Win32API.new("user32", "GetClassName", "LPL", "L")
GetCurrentThreadId = Win32API.new("kernel32", "GetCurrentThreadId", "V", "L")
GetForegroundWindow = Win32API.new("user32", "GetForegroundWindow", "V", "L")
GetCurrentProcess = Win32API.new('kernel32', 'GetCurrentProcess', '', 'L')
EnumProcessModules = Win32API.new('psapi', 'EnumProcessModules', 'lplp', 'L')
GetModuleFileNameA = Win32API.new("kernel32", "GetModuleFileNameA", "lpi", "l")
GetProcAddress = Win32API.new("kernel32", "GetProcAddress", "lp", "l")
AnsiStringLength = Win32API.new("kernel32", "lstrlenA", "l", "l")
class CPtr
attr_accessor :address
def initialize(address)
@address = address
end
def [](ptr, size)
return nil if address.nil?
return nil if ptr.nil?
buf = "\0" * size
RtlMoveMemory_pl.call(buf, @address + ptr, size)
buf
end
def []=(ptr, size, data)
return nil if address.nil?
return nil if ptr.nil?
raise ArgumentError if data.length != size
RtlMoveMemory_lp.call(@address + ptr, data, size)
data
end
NIL = CPtr.new(nil)
end
def initialize_runtime
@initialized ||= lambda {
@rgss_module_path, @rgss_instance = search_for_rgss_module
@rgss_module_identify = search_for_rgss_module_identify
@rgss_version = {
# "rgss100j" => [1, 0, :japanese], # no properly Win32API support
"rgss102j" => [1, 2, :japanese],
"rgss102e" => [1, 2, :english],
"rgss103j" => [1, 3, :japanese],
"rgss104j" => [1, 4, :japanese],
"rgss104e" => [1, 4, :english],
"rgss200e" => [2, 0, :english],
"rgss200j" => [2, 0, :japanese],
"rgss202e" => [2, 2, :english],
"rgss202j" => [2, 2, :japanese],
"rgss300" => [3, 0, nil],
"rgss301" => [3, 1, nil],
}[File.basename(@rgss_module_identify.downcase, ".dll")]
@runtime_handle = search_for_runtime_handle
true
}.call
end
def rgss_version_major
@rgss_version[0] rescue nil
end
def rgss_version_minor
@rgss_version[1] rescue nil
end
def rgss_language
@rgss_version[2] rescue nil
end
def search_for_rgss_module_identify
dll = CPtr.new(rgss_instance)
signature = dll[60, 4].unpack('L').first
return if dll[signature, 2] != "PE"
newhdr = dll[newhdrofs = signature+4, 20].unpack('SSLLLSS')
optheader = dll[opthdrofs = newhdrofs+20, newhdr[5]].unpack('SCCLLLLLLLLLSSSSSSLLLLSSL*')
exp = dll[*dll[opthdrofs+96, 8].unpack('LL')]
export = exp.unpack('LLSSLLLLLLL')
name = lambda{ |ptr| dll[ptr, AnsiStringLength.call(dll.address + ptr)] }.call(export[4])
end
def search_for_rgss_module
process = GetCurrentProcess.call
num = ASCIICHAR * 4
EnumProcessModules.call process, nil, 0, num
num = num.unpack('L')[0]
x = ASCIICHAR * (num * 8)
lnum = ASCIICHAR * 4
EnumProcessModules.call process, x, num, lnum
x = x.unpack('L*')
x.each do |xx|
buf = ASCIICHAR * 1024
GetModuleFileNameA.call xx, buf, 1024
buf = buf.gsub(/\0.+$/){}
return [buf, xx] if GetProcAddress.call(xx, "RGSSEval") != 0
end
['', 0]
end
def search_for_runtime_handle
pattern = []
pattern[0] = LoadAccelerators.call(rgss_instance, rgss_version_major == 3 ? 0x65 : 0x66)
pattern[1] = hwnd = player_window_handle
pat = pattern.pack("LL")
if rgss_version_major == 1
title = ASCIICHAR * 256
GetWindowTextA.call(hwnd, title, 256)
else
title = ASCIICHAR * 512
GetWindowTextW.call(hwnd, title, 512)
end
paddr = 0
mi = ASCIICHAR * 28
milen = mi.length
buffer = ASCIICHAR * 4096
this = nil
catch(:this) do
while VirtualQuery.call(paddr, mi, milen) == milen
i = Hash[*[:BaseAddress, :AllocationBase, :AllocationProtect, :RegionSize, :State, :Protect, :Type].zip(mi.unpack("LLLLLLL")).flatten]
if i[:State] == 0x1000 && i[:Type] == 0x20000 && i[:Protect] == 0x04
cur = i[:BaseAddress]
last = i[:BaseAddress] + i[:RegionSize]
while cur < last
size = [last - cur, 4096].min
RtlMoveMemory_pl.call(buffer, cur, size)
if (index = buffer.index(pat)) && index < size
if (buffer.index(title) == index + 8)
this = index + cur - 4
throw :this
end
end
cur += buffer.length
end
end
paddr = i[:BaseAddress] + i[:RegionSize]
end
end
this
end
def player_window_handle
@player_window_handle ||= lambda {
threadID = GetCurrentThreadId.call
hWnd = GetWindow.call(GetForegroundWindow.call, 0)
while hWnd != 0
if threadID == GetWindowThreadProcessId.call(hWnd, 0)
className = " " * 11
GetClassName.call(hWnd, className, 12)
break if className == "RGSS Player"
end
hWnd = GetWindow.call(hWnd, 2)
end
hWnd
}.call
end
def player_module_name
@player_module_name ||= File.basename(player_module_path)
end
def player_module_path
@player_module_path ||= lambda {
buf = ASCIICHAR * 1024
GetModuleFileNameA.call 0, buf, 1024
buf = buf.gsub(/\0.+$/){}
}.call
end
def rgss_module_name
@rgss_module_name ||= File.basename(rgss_module_path)
end
def rgss_module_path
@rgss_module_path
end
def rgss_instance
@rgss_instance
end
def memory
@memory ||= CPtr.new(0)
end
def smooth_mode_ptr
@smooth_mode_ptr ||= lambda {
return CPtr::NIL if @runtime_handle.nil?
return CPtr::NIL if rgss_version_major != 1
CPtr.new(@runtime_handle + 348)
}.call
end
def wait_for_vsync_ptr
@wait_for_vsync_ptr ||= lambda {
return CPtr::NIL if @runtime_handle.nil?
if rgss_version_major == 1
return CPtr.new(memory[@runtime_handle + 308, 4].unpack("L").first + {
2 => 236, 3 => 236, 4 => 244
}[rgss_version_minor]) rescue CPtr::NIL
elsif rgss_version_major == 2 || rgss_version_major == 3
return CPtr.new(@runtime_handle + 528) rescue CPtr::NIL
end
CPtr::NIL
}.call
end
def smooth_mode
smooth_mode_ptr[0, 4].unpack("L").first == 1
end
def smooth_mode=(value)
smooth_mode_ptr[0, 4] = [value ? 1 : 0].pack("L")
end
def wait_for_vsync
wait_for_vsync_ptr[0, 4].unpack("L").first == 1
end
def wait_for_vsync=(value)
wait_for_vsync_ptr[0, 4] = [value ? 1 : 0].pack("L")
end
end
RGSSRuntime.initialize_runtime
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment