O TEMA DO FÓRUM ESTÁ EM MANUTENÇÃO. FEEDBACKS AQUI: ACESSAR

DLL Utils

Iniciado por Brandt, 05/07/2019 às 20:15



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

Código: 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
~ Masked

Opa isso é útil hahah xD.

Como a maioria da codificação que fiz no Ace era mais visual e manipulação das bibliotecas do RGSS/Ruby, usei pouco a Win32API, mas quando usei isso seria bem bom xD (Tipo o de full keyboard). Se eu voltar a mexer com o Ace isso certamente poderá ser útil  :=p: