用法:

下载请见标题下的两个按钮。


# coding: utf-8
def assert! bool, message=nil
  raise ArgumentError, message || "assertion fail" unless bool
ensure
  $@.shift if $@
end

# api.call(nil, true, false, buf('LL'))
class NilClass; def to_int() 0 end end
class TrueClass; def to_int() 1 end end
class FalseClass; def to_int() 0 end end

class Buffer
  def self.buf(template=nil)
    if template.nil?
      @buf && @buf.unpack
    else
      @buf = Buffer.new template
    end
  end
  TemplateSizes = Hash[{
    %w[C c       A a x] => 1,
    %w[S s n v]         => 2,
    %w[L l N V F f e g] => 4,
    %w[Q q     D d E G] => 8,
  }.map { |k, v| k.map { |l| [l, v] } }.flatten(1)]
  def self.parse(template)
    template = template.gsub(/(\w)(\d+)/) { |m| m[0] * Integer(m[1..-1]) }
    [].pack("x#{template.chars.map { |c| TemplateSizes[c] }.inject(:+)}")
  end
  def initialize(template)
    assert!(String === template)
    @template = template
    @buffer = Buffer.parse template
  end
  attr_reader :buffer
  def unpack(template = @template)
    @buffer.unpack template
  end
  def load(*data)
    @template.scan(/\w(?:\d*)/).zip(data).reduce(0) { |s, (t, d)|
      n = TemplateSizes[t[0]] * (t.length > 1 ? Integer(t[1..-1]) : 1)
      @buffer[s, n] = [*d].pack(t) unless t.nil? || d.nil?
      s + n
    }; self
  end
  def inspect
    "#<Buffer #{unpack.inspect}>"
  end
  def to_str
    @buffer
  end
  def to_int
    [@buffer].pack('p').unpack('L')[0]
  end
end
def buf(*args) Buffer.buf(*args) end

# api('user32', 'GetCursorPos').call(buf('LL')); p buf #=> [12, 34]
class Api
  def initialize dll, fun
    assert!(String === dll && String === fun)
    @dll, @fun = dll, fun
  end
  attr_reader :dll, :fun
  alias func fun
  def call *args
    import = args.map { |e| Integer === e ? 'L' : 'p' }
    @instance ||= Win32API.new(@dll, @fun, import, 'i')
    @instance.call(*args)
  end
  alias [] call
  def inspect
    "#<Api #@dll.#@fun>"
  end
end
def api(*args) Api.new(*args) end

def ptr2str pointer
  assert!(Integer === pointer)
  len = api('kernel32', 'lstrlen').call pointer
  return '' if len.zero?
  str = [].pack("x#{len}")
  api('kernel32', 'RtlMoveMemory').call str, pointer, len
  str
end

class String
  def to_ws()   unpack('U*').pack('S*') end
  def from_ws() unpack('S*').pack('U*') end
  def to_ptr()  [self].pack('p').unpack('L')[0] end
end

class Dll
  def initialize dll
    @dll = dll
    @api = {}
  end
  def method_missing fun, *args
    @api[fun] ||= Api.new(@dll, fun.to_s)
    @api[fun].call *args
  end
  def inspect
    "#<Dll #@dll>"
  end
  def to_a
    @api.values
  end
end
def dll(name) Dll.new(name) end

Kernel32 = dll('kernel32')
User32 = dll('user32')

class << Graphics
  def window_hwnd
    @_hwnd ||= get_hwnd
  end
  private
  # https://rpg.blue/thread-160672-1-2.html
  def get_hwnd
    threadID = Kernel32.GetCurrentThreadId
    hWnd = User32.GetWindow(User32.GetForegroundWindow, 0)
    buffer = buf('A12')
    while hWnd != 0
      if threadID == User32.GetWindowThreadProcessId(hWnd, 0)
        User32.GetClassName(hWnd, buffer, 12)
        break if buffer.unpack[0] == 'RGSS Player'
      end
      hWnd = User32.GetWindow(hWnd, 2)
    end
    hWnd
  end
end unless defined? Graphics.window_hwnd
Graphics.window_hwnd

Api::Proc = Kernel32.GetCurrentProcess # should be -1
raise 'cannot open process' if Api::Proc == 0

def Api.readmem(ptr_from, size)
  buff = [].pack("x#{size}")
  Kernel32.ReadProcessMemory(Api::Proc, buff, ptr_from, size, 0)
  buff
end

def Api.writemem(ptr_to, size, buffer)
  Kernel32.WriteProcessMemory(Api::Proc, ptr_to, buffer, size, 0)
end

def Api.getaddr(dll, func)
  lib = Kernel32.LoadLibrary([dll].pack('Z*'))
  Kernel32.GetProcAddress(lib, [func].pack('Z*'))
end

def Api.malloc(size)
  Kernel32.GlobalAlloc(0, size)
end

def Api.free(ptr)
  Kernel32.GlobalFree(ptr)
end

def Api.unprotect(addr, len)
  Kernel32.VirtualProtect(addr, len, 0x40, buf('L'))
end

def Api.getmodule(name="RGSS#{RGSS_VERSION.split('.')}")
  Kernel32.GetModuleHandle(name)
end

def Api.errorp(func='')
  code = Kernel32.GetLastError
  Kernel32.FormatMessageW(0x1300, 0, code, 0x0400, buf('L'), 0, 0)
  msgbox "#{func} failed with code #{code}: #{ptr2str(buf[0]).from_ws}"
  Kernel32.LocalFree(buf[0])
end