Quod erat demonstrandum
DLLUtils | v1.0.0 | por Masked
para RPG Maker VX Ace
Descrição
Implementa funções e classes auxiliares para integração com DLLs C de 32-bit através da interface DL do Ruby.
Como muitos devem saber, o RGSS dispõe da interface Win32API para acesso a funções de DLLs, o que permite o desenvolvimento de scripts que jamais poderiam ser feitos sem ela, como sistemas que permitem usar o mouse ou até exploradores de labirinto em primeira pessoa.
De fato, a Win32API seria suficiente, não fosse por uma coisa: é um pé no saco mexer com structs e ponteiros nela, jesus.
O intuito desse script é prover uma interface mais amigável ao programador, e permitir fácil manipulação de structs, ponteiros, e ponteiros de structs!
Vale ressaltar que esse script não adiciona funcionalidades ao jogo por si só, apenas disponibiliza as ferramentas para que outros scripts o façam em seus próprios sistemas.
Instruções
Leia a declaração das funções no script. No futuro montarei uma documentação mais acessível para isso.
Script
Ruby:
#==============================================================================
# DLL Utils | v1.0.0 | por Masked
#
# para RPG Maker VX Ace
#------------------------------------------------------------------------------
# Implementa funções e classes auxiliares para integração com DLLs C de 32-bit
# através da interface DL do Ruby.
#==============================================================================
#==============================================================================
# Features
#------------------------------------------------------------------------------
# - Importações facilitadas de funções de DLLs por nome e tipo de retorno
# - Declarações de tipos via typedef
# - Suporte a structs e ponteiros via módulo CStruct e método Object#cptr
#==============================================================================
($modules ||= {})[:dll_utils] = 1.0
#==============================================================================
# ** DLLImporter
#------------------------------------------------------------------------------
# Este módulo tem funções auxiliares para importação de funções de DLLs.
#==============================================================================
module DLLImporter
include DL
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
@@loaded_dll = nil
@@dll_cache = {}
@@imported_functions = {}
@@types = {
long: TYPE_LONG,
int: TYPE_INT,
short: TYPE_SHORT,
char: TYPE_CHAR,
bool: TYPE_CHAR,
double: TYPE_DOUBLE,
float: TYPE_FLOAT,
void: TYPE_VOID,
pointer: TYPE_VOIDP
}
#--------------------------------------------------------------------------
# * Cria um alias para um tipo
# existing : Nome do tipo existente
# new : Nome do novo tipo
#--------------------------------------------------------------------------
def typedef(existing, new)
raise "Undefined type `#{existing}'" unless @@types.has_key?(existing)
@@types[new] = @@types[existing]
end
#--------------------------------------------------------------------------
# * Obtém o tamanho de um tipo
#--------------------------------------------------------------------------
def self.real_type(type)
@@types[type]
end
#--------------------------------------------------------------------------
# * Determina se um tipo é numérico inteiro
#--------------------------------------------------------------------------
def self.integer?(type)
[
TYPE_LONG_LONG,
TYPE_LONG,
TYPE_INT,
TYPE_SHORT,
TYPE_CHAR
].include? type
end
#--------------------------------------------------------------------------
# * Determina se um tipo é numérico de ponto flutuante
#--------------------------------------------------------------------------
def self.float?(type)
[TYPE_DOUBLE, TYPE_FLOAT].include? type
end
#--------------------------------------------------------------------------
# * Obtém o tamanho de um tipo
#--------------------------------------------------------------------------
def self.sizeof(type)
{
TYPE_LONG => SIZEOF_LONG,
TYPE_INT => SIZEOF_INT,
TYPE_SHORT => SIZEOF_SHORT,
TYPE_CHAR => SIZEOF_CHAR,
TYPE_DOUBLE => SIZEOF_DOUBLE,
TYPE_FLOAT => SIZEOF_FLOAT,
TYPE_VOIDP => SIZEOF_VOIDP
}[type] || sizeof(real_type(type))
end
#--------------------------------------------------------------------------
# * Seleciona uma DLL para importar funções
# dllname : Nome da DLL que será aberta
#--------------------------------------------------------------------------
def with_dll(dllname)
@@dll_cache[dllname] ||= DL.dlopen(dllname)
@@loaded_dll = @@dll_cache[dllname]
yield
@@loaded_dll = nil
end
#--------------------------------------------------------------------------
# * Importa uma função da DLL
# symbol : Nome da função como Symbol
# return_type : Tipo de retorno da função
# name : Nome para o método que será criado (Opcional)
#--------------------------------------------------------------------------
def import(symbol, return_type, name = nil)
raise "`import' outside `with_dll' block" if @@loaded_dll.nil?
name ||= symbol
name = name.to_s.to_sym
unless @@imported_functions[symbol]
fname = symbol.to_s
func = DL::CFunc.new(@@loaded_dll[fname], @@types[return_type], fname)
@@imported_functions[symbol] = func
end
define_method(name) do |*args|
args.collect! do |a|
a.is_a?(Float) ? [a].pack('g').unpack('H16').first.to_i(16) : a
end
@@imported_functions[symbol].call(args)
end
end
end
#==============================================================================
# Object
#------------------------------------------------------------------------------
# Adição de algumas funções obscuras para uso com DLLs na classe mãe de todos
# os objetos (a.k.a.: Tudo)
#==============================================================================
class Object
#---------------------------------------------------------------------------
# Obtém o ponteiro C do objeto e o retorna na forma de inteiro
#---------------------------------------------------------------------------
def cptr
@_cptr ||= DL::CPtr[self].to_i
end
end
#==============================================================================
# ** CStruct
#------------------------------------------------------------------------------
# Este módulo define classes e funções auxiliares para criação e manipulação
# de objetos compatíveis com structs C que podem ser passados para funções.
#==============================================================================
module CStruct
#---------------------------------------------------------------------------
# * Cria uma struct C com os campos desejados
#---------------------------------------------------------------------------
def self.create
t = Template.new
yield t
t
end
end
#==============================================================================
# ** CStruct::Template
#------------------------------------------------------------------------------
# Esta classe serve como modelo para construção de structs compatíveis com as
# usadas em funções de DLLs.
#==============================================================================
class CStruct::Template
#--------------------------------------------------------------------------
# * Construtor
# fields : Hash de campos e tamanhos em bytes
#--------------------------------------------------------------------------
def initialize
@fields = []
@offsets = []
@types = []
end
#--------------------------------------------------------------------------
# * Define um atributo no template
# name : Nome (Symbol) do atributo
# type : Tipo do atributo
#--------------------------------------------------------------------------
def attr(name, type)
if @fields.empty?
@offsets << 0
else
@offsets << @offsets[-1] + DLLImporter.sizeof(@types[-1])
end
@types << DLLImporter.real_type(type)
@fields << name.to_sym
end
#--------------------------------------------------------------------------
# * Tamanho da struct em bytes
#--------------------------------------------------------------------------
def size
@types.map { |t| DLLImporter.sizeof(t) }.reduce(:+)
end
#--------------------------------------------------------------------------
# * Campos da struct
#--------------------------------------------------------------------------
def fields
Hash[@fields.zip(@offsets.zip(@types))]
end
#--------------------------------------------------------------------------
# * Cria ou copia uma instância da struct
# other : Struct a ser copiada
#--------------------------------------------------------------------------
def new(other = nil)
raise ArgumentError if other and other.size != size
data = other ? other.data : "\0" * size
CStruct::Instance.new(data, fields)
end
end
#==============================================================================
# ** CStruct::Instance
#------------------------------------------------------------------------------
# Esta classe representa uma instância de CStruct.
#==============================================================================
class CStruct::Instance
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
attr_reader :data
#--------------------------------------------------------------------------
# * Construtor
# data : Dados em memória da instância
#--------------------------------------------------------------------------
def initialize(data, fields)
@data = data
@fields = fields
@fields.keys.each { |field| struct_get_set(field) }
end
#--------------------------------------------------------------------------
# * Converte um valor da struct para um tipo nativo do ruby
#--------------------------------------------------------------------------
def self.convert(type, data)
if DLLImporter.integer?(type)
data.unpack('q*l*s*c*').first
elsif DLLImporter.float?(type)
[[data].unpack('Q*L*').first.to_s(16)].pack('H16').unpack('G*g*').first
else
DL::CPtr.new(data.unpack('L'))
end
end
#--------------------------------------------------------------------------
# * Codifica um valor para dado binário
#--------------------------------------------------------------------------
def self.encode(value)
if value.is_a? Float
[value].pack('g').unpack('H16').first.to_i(16)
elsif value.is_a? Integer
[value].pack('q')
else
value.to_s
end
end
#--------------------------------------------------------------------------
# * Obtém um atributo da struct
#--------------------------------------------------------------------------
def [](name)
type = @fields[name][1]
CStruct::Instance::convert type, raw(name)
end
#--------------------------------------------------------------------------
# * Obtém um atributo da struct
#--------------------------------------------------------------------------
def []=(name, value)
offset, type = @fields[name]
size = DLLImporter.sizeof(type)
value = CStruct::Instance::encode(value)
data[offset, size] = value[0, size]
end
#--------------------------------------------------------------------------
# * Obtém o valor cru de um atributo da struct como String
#--------------------------------------------------------------------------
def raw(name)
offset, type = @fields[name]
data[offset, DLLImporter.sizeof(type)]
end
#--------------------------------------------------------------------------
# * Converte o objeto em string
#--------------------------------------------------------------------------
def to_s
'{ ' + @fields.keys.map do |f|
".#{f} = #{self[f]}"
end.join(', ') + ' }'
end
#--------------------------------------------------------------------------
# * Obtém o ponteiro da struct
#--------------------------------------------------------------------------
def cptr
@data.cptr
end
#--------------------------------------------------------------------------
# * Define métodos de leitura e escrita de atributos da struct
#--------------------------------------------------------------------------
private
def struct_get_set(attr)
define_singleton_method(attr) do
self[attr]
end
define_singleton_method("#{attr}=".to_sym) do |value|
self[attr] = value
end
end
end
Licensa: Creative Commons Attribution-ShareAlike 4.0 International