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

Funções da RGSS301.dll

Iniciado por Sotelie, 11/01/2020 às 19:16

11/01/2020 às 19:16 Última edição: 24/09/2020 às 22:41 por Syureri
Creio aqui que quem usa o Ace já deva ter ao menos ouvido falar do RGD, este que por sua vez é uma reimplementação do RGSS utilizando Direct3D 9 (versão mais recente é a 12).

Este executável é uma bênção para quem ainda usa o Ace, visto que não quebra a EULA do programa (é permitido criar um executável do zero, não modificar o existente) e remove muitas das limitações nativas da engine.

Achando a ideia genial, lá fui eu fazer uns experimentos e tentar ver se conseguia fazer a mesma coisa, porém utilizando OpenGL moderno. E consegui, porém como ainda estou ocupado desenvolvendo minha própria framework, decidi deixar isso pra depois. Abaixo estarei deixando informações sobre algumas das funções exportadas na RGSS301.dll que me foram bem úteis durante o processo, visto que não há documentação sobre elas e o único jeito de saber quais são os parâmetros ou valor de retorno é via tentativa e erro. A propósito, esses parâmetros foram "adivinhados" e testados, o que quer dizer que se o método requer um int de 32 (int) ou 16 (short) bits, é difícil saber se o parâmetro correto é signed ou unsigned.

A sintaxe das assinaturas das funções serão em C/C++, visto que é mais fácil de se traduzir para C# (DLLImport, por exemplo).





[box2 class=titlebg title=RGSSSetupRTP]
int RGSSSetupRTP(const wchar_t* iniPath, wchar_t* errorMessageBuffer, int bufferLength);
// - iniPath: Uma string com o caminho (não sei se é necessário ser absoluto) do arquivo ini do jogo.
// - errorMessageBuffer: Um bloco de memória que será preenchido com uma mensagem de erro, se esta função retornar 0 (false).
// - bufferLength: O tamanho do bloco de memória que será preenchido com a mensagem de erro.
// Retorna 0 em caso de erro, qualquer outro número indica sucesso.

Esta função irá configurar o RTP da engine. Não há como saber exatamente o que ela faz por trás dos panos, mas assumo que ela irá identificar onde o RTP está instalado na máquina do usuário.[/box2]

[box2 class=titlebg title=RGSSSetupFonts]
void RGSSSetupFonts();

Configura as fontes. Assim como RGSSSetupRTP, creio que ele verifica as fontes que estão instaladas na máquina do usuário.[/box2]

[box2 class=titlebg title=RGSSInitialize3]
void RGSSInitialize3(void* rgssDLL);
// - rgssDLL: HMODULE ou HINSTANCE em C++. A DLL que foi carregada via LoadLibrary.

Esta função irá iniciar a Virtual Machine do Ruby e preparar todas as classes padrões do RGSS (ou a maioria delas). Esta função pode ter nomes diferentes dependendo da versão da DLL, mas ao menos na RGSS301.dll ele possui essa assinatura.[/box2]

[box2 class=titlebg title=RGSSEval]
int RGSSEval(const char* script);
// - script: Uma string, de preferência UTF-8, contendo o código a ser executado.
// Retorna 6 em caso de erro, 0 caso nenhum erro ocorra.

Executa um script em ruby. Toda vez que essa função é chamada, a linha de execução sempre irá voltar a 1 (o que é ótimo visto que você pode executar os scripts um por um). Deve ser chamado após RGSSInitialize3, quando a Virtual Machine tiver sido iniciada.[/box2]

[box2 class=titlebg title=RGSSErrorMessage]
const wchar_t* RGSSErrorMessage();
// Retorna uma string contendo a mensagem de erro caso RGSSEval tenha retornado 6.

Obtém a mensagem de erro do RGSSEval. Aparentemente ela sempre começa com "Script '(eval)'", fazendo com que você só precise substituir (eval) pelo nome do script que foi executado.[/box2]

[box2 class=titlebg title=RGSSGetRTPPath]
int RGSSGetRTPPath(int rtpNumber);
// - rtpNumber: O número do RTP no arquivo INI do jogo. 1 geralmente escolhe o RTP padrão.
// Retorna o ID do RTP, que deve ser usado no RGSSGetPathWithRTP para obter o caminho do RTP.

Obtém uma ID que deve ser usada no RGSSGetPathWithRTP para obter o caminho de um RTP instalado na máquina do usuário.[/box2]

[box2 class=titlebg title=RGSSGetPathWithRTP]
const wchar_t* RGSSGetPathWithRTP(int rtpID);
// - rtpID: O ID do RTP, que deve ser obtido usando RGSSGetRTPPath.
// Retorna uma string representando o diretório onde o RTP está instalado.

Obtém uma string contendo o diretório onde um RTP específico está instalado. Usado em conjunto com RGSSGetRTPPath.[/box2]

[box2 class=titlebg title=RGSSFinalize]
void RGSSFinalize();

Finaliza a Virtual Machine do ruby. Eu não sei se é necessário passar um HMODULE ou HINSTANCE como argumento assim como RGSSInitialize3, mas faço apenas por precaução.[/box2]

Funções ocultas obtidas por engenharia reversa

11/01/2020 às 21:56 #1 Última edição: 11/01/2020 às 22:09 por Brandt
São bem interessantes essas funções mesmo, dá pra fazer uns negócios legais.

Além desse exemplo do RGD, se me permite o merchan, eu lembro de uma ferramenta que eu fiz pra executar código arbitrário dentro de jogos do RMVXAce (inclusive os encriptados usando a criptografia padrão do RM) usando o RGSSEval pra modificar a própria função eval do Ruby. Não sei se postei aqui, mas no condado tem o link: https://www.condadobraveheart.com/forum/index.php?topic=835.0
Infelizmente, o código parece ter se perdido no meu HD antigo.

É um exemplo legal do que dá pra fazer com essas funções além de simular o próprio Game.exe.

Aliás, fazendo isso dá pra pegar uns códigos Ruby internos que o RGSS roda e que não aparecem nos scripts padrão. É bem interessante.

Lembro de ter usado o DLL Export Viewer pra pegar as funções, não sei se você chegou a ver alguma ferramenta do tipo.

As próprias ferramentas do desenvolvedor do Visual Studio tem um visualizador de exports também.




Em outra nota, que tal abrir um repositório open source pra esse RGSS + OpenGL aí? Eu andei fuçando nisso também, acho que seria produtivo montar um negócio aberto mais colaborativo (que na minha visão é uma das coisas que faz falta no RGSS e no RGD).
~ Masked

12/01/2020 às 01:05 #2 Última edição: 14/02/2020 às 19:19 por Syureri
Ah sim, acabei usando o dumpbin (contido no Visual Studio Command Prompt), creio que por ter usado objdump no linux (via WSL) uns dias atrás. Ele e o dependency walker, embora os dois façam a mesma coisa haha'

E sim, eu planejo abrir um repositório no github pra essa budega assim que me desocupar com essa framework que estou trabalhando (que por sinal também será open source). Software de código aberto é bom e eu quero mesmo fazer uma versão dessas pro Ace que anda precisando muito (maior problema do RGD na minha opinião é não terem liberado o código :feelsbatman:)

14/01/2020 às 16:42 #3 Última edição: 14/01/2020 às 16:47 por Valentine
A ideia é muito boa, já tentei fazer alguns executáveis usando Ruby + SFML (e até Ruby + DirectX 9), porém não deu muito certo. A forma como a API gráfica original do RM trabalha é um pouco diferente do DirectX, Open GL etc. e mais cedo ou mais tarde você acaba tendo que prejudicar o desempenho da nova API pra adaptar aos scripts padrões (windows, sprites etc.) do RM.

Seria interessante fazer o que o RGD não fez: código open source (para que eu possa incluir bibliotecas do Ruby, como Socket, e instalar gems) e atualizar a versão do Ruby (2.6 +).

A API do RPG Maker é o que ferra mesmo, a arquitetura que escolheram (se é que pensaram em alguma) é beem ruim de se trabalhar em cima. Eu tava tentando fazer um executável usando minha framework por cima mas não dá muito certo não, visto que frameworks chamam seu código, não o contrário como no caso do RM. Outro fator prejudicial é que o ambiente de scripting parece uma prisão, já começa com os scripts embutidos em um rvdata2 comprimidos via zlib, não tem como usar gems, a API padrão do RGSS3 não contém a maioria das classes fundamentais que se espera de uma lib voltada a jogos. A única ajuda que se tem é usando a Win32API, que por si só é horrível. Não tem como passar callbacks, passar ou receber strings é uma tristeza, não tem como usar structs, pra passar um float precisa usar Array.pack porque os argumentos só aceitam ints, longs e pointers... não tem como criar um código organizado com aquilo lá. Qualquer um que usar DLLImport em C# nota a diferença.

Pensei até em usar IronRuby, mas ele não possui suporte pra fibers (uma versão bem mais simples que Enumerators em .NET), coisa que o RM inventou de abusar no Game_Interpreter. Também tentei usar MRuby, foi o que mais deu resultados. Porém como MRuby foi feito pra ser embutido, e ser pequeno, ele consequentemente não possui algumas classes da biblioteca principal que alguns scripts usam, sendo necessário implementar elas na mão (ao menos temos mruby-gems pra ajudar). Por outro lado é a melhor opção, visto que você pode implementar e consumir tipos e funções em C diretamente sem frescura de Win32API (tem até a gem FFI que é muito melhor).

Outro benefício de usar MRuby é que ele é multiplataforma, como os arquivos rvdata2 e rgss3a não são específicos de plataforma seria bem mais simples fazer jogos do Ace funcionarem tanto no Windows quanto sistemas baseados em Unix (Linux, Android (nesse caso sem chance, visto que atividades do Android funcionam na base de eventos e os scripts padrões começam um loop infinito), macOS (esse aqui nem me importo tanto, só funciona OpenGL 4.1 no máximo e eles enfiam Object-C na tua goela >.>)).

Acho que vou experimentar com MRuby novamente. Estou vendo que serão necessários alguns sacrifícios mas pode valer a pena. Se eu conseguir algum resultado decente postarei aqui open source pros senhores o/

16/01/2020 às 15:03 #5 Última edição: 16/01/2020 às 15:05 por Valentine
Citação de: Syureri online 15/01/2020 às 12:56
A API do RPG Maker é o que ferra mesmo, a arquitetura que escolheram (se é que pensaram em alguma) é beem ruim de se trabalhar em cima. Eu tava tentando fazer um executável usando minha framework por cima mas não dá muito certo não, visto que frameworks chamam seu código, não o contrário como no caso do RM. Outro fator prejudicial é que o ambiente de scripting parece uma prisão, já começa com os scripts embutidos em um rvdata2 comprimidos via zlib, não tem como usar gems, a API padrão do RGSS3 não contém a maioria das classes fundamentais que se espera de uma lib voltada a jogos. A única ajuda que se tem é usando a Win32API, que por si só é horrível. Não tem como passar callbacks, passar ou receber strings é uma tristeza, não tem como usar structs, pra passar um float precisa usar Array.pack porque os argumentos só aceitam ints, longs e pointers... não tem como criar um código organizado com aquilo lá. Qualquer um que usar DLLImport em C# nota a diferença.

Pensei até em usar IronRuby, mas ele não possui suporte pra fibers (uma versão bem mais simples que Enumerators em .NET), coisa que o RM inventou de abusar no Game_Interpreter. Também tentei usar MRuby, foi o que mais deu resultados. Porém como MRuby foi feito pra ser embutido, e ser pequeno, ele consequentemente não possui algumas classes da biblioteca principal que alguns scripts usam, sendo necessário implementar elas na mão (ao menos temos mruby-gems pra ajudar). Por outro lado é a melhor opção, visto que você pode implementar e consumir tipos e funções em C diretamente sem frescura de Win32API (tem até a gem FFI que é muito melhor).

Outro benefício de usar MRuby é que ele é multiplataforma, como os arquivos rvdata2 e rgss3a não são específicos de plataforma seria bem mais simples fazer jogos do Ace funcionarem tanto no Windows quanto sistemas baseados em Unix (Linux, Android (nesse caso sem chance, visto que atividades do Android funcionam na base de eventos e os scripts padrões começam um loop infinito), macOS (esse aqui nem me importo tanto, só funciona OpenGL 4.1 no máximo e eles enfiam Object-C na tua goela >.>)).

Acho que vou experimentar com MRuby novamente. Estou vendo que serão necessários alguns sacrifícios mas pode valer a pena. Se eu conseguir algum resultado decente postarei aqui open source pros senhores o/
Já tentei usar IronRuby, porém, só reforcei o que eu já sabia: é horrível usar projetos abandonados e desatualizados. Nem tem uma documentação decente e o desempenho vai lá pra baixo. Depois eu pensei em programar tudo em Ruby e usar a gem Ocra (que "morreu" na versão 2.1.5 do Ruby) pra fazer um executável e parece que ficou pior ainda. MRuby eu acho que não cheguei a tentar.

Aproveitando estarei deixando aqui um exemplo de um executável bem simples que usa o Game Loop padrão do RGSS3 em C++. Só colocar em qualquer projeto em C++ no Visual Studio e mudar o subsistema no Linker Options de Console (se estiver selecionado) para Windows. Esse código é uma versão mais voltada pra c++ do código do(a) taroxd.

Espero que ele possa dar uma ideia de como começar, caso alguém queira experimentar o/

Deixando aqui os métodos não exportados da Virtual Machine do ruby que podem ser encontrados na RGSS301.dll.
Como não quis me dar o trabalho de criar um repositório, criei 3 gists contendo o código necessário para carregar e testar esses métodos.

RubyStuff.hpp: Um header que contém tanto algumas definições de CRuby quanto os endereços dos métodos não exportados. Tenha em mente que nem todos os métodos de Ruby estão disponíveis visto que a RGSS301.dll foi compilada com a opção de remover símbolos inutilizados. Nem todos os endereços estão aí também, mas creio que a partir deles seja possível descobrir os outros o/

RubyStuff.cpp: Uma unidade de compilação responsável por carregar esses métodos da DLL.

main.cpp: Um main.cpp simples, que carrega a DLL, carrega os métodos, define um módulo de testes, cria um método dentro dele e, finalmente, o chama através de um eval.


É nozes, meus chegados (o/

Uau!
Fico muito feliz que em 2020 ainda existam pessoas interessadas no VX Ace, Ruby, e cia.
Tenho um pouco de experiência com OllyDBG, IDA. Faz tempo que não mexo, mas é como andar de bicicleta, né?
Nós deveríamos unir forças pra fazer um VX Ace++, com RGSS4!

Ace sempre estará em nossos corações xD Eventualmente estarei atualizando esse tópico com novas funções encontradas na DLL pra ajudar caso alguém precise o/




O tópico foi atualizado com mais algumas das funções ocultas da VM do Ruby encontrados na RGSS301.dll. Agradecimentos ao Brandt também pela ajuda em encontrá-las.

RGSS3.hpp
RGSS3.cpp