This content is not available in your language... So if you don't understand the language... well, at least you can appreciate the pictures of the post, right?
This content is not available in your language... So if you don't understand the language... well, at least you can appreciate the pictures of the post, right?
Eu gastei horas fazendo algo praticamente inútil.
Eu estava pensando em implementar uma VM que executa jogos feitos com o GameMaker: Studio 1. (Sabia que o ".exe" de jogos feitos pelo GM:S é uma VM? Igual como Java funciona, ele executa bytecode. E é por isso que eu consegui fazer o Droidtale, um port não oficial do Undertale para o Android)
O objetivo era conseguir rodar Undertale (e outros jogos feitos pelo GM:S) em uma VM feita em Kotlin, sendo possível portar a VM para outras plataformas (e assim portando os jogos também).
Claro, é bem mais fácil apenas recompilar o jogo para a plataforma desejada, mas existem os seguintes problemas:
Caso você tenha o GameMaker: Studio com os exports que você deseja...
Caso você tenha (ou não tenha) o GameMaker: Studio sem ter os exports que você deseja...
Por isso decidi fazer uma VM, porque não deve ser tão difícil... né?
Não, não é fácil. E sim MUITO difícil, já que você precisa implementar uma VM que interpreta o código gerado pelo GM:S para executar o código do jogo.
Eu usei as seguintes documentações para fazer a minha VM:
Enquanto estava fazendo o meu projeto, descobri o Luna, um projeto parecido com o meu, que tenta implementar uma VM para jogos do GameMaker: Studio, mas o Luna foi feito para jogos que usam o bytecode das novas versões do GameMaker: Studio 2, enquanto eu estou tentando implementar o bytecode utilizado no GameMaker: Studio 1 (já que o objetivo era emular o Undertale). O Luna é bastante interessante já que o dev consegiu rodar um jogo simples feito no GM:S2! Vale a pena conferir o blog do criador para ver o progresso.
Mas mesmo assim resolvi continuar o meu projeto, afinal, imagina o Undertale rodando no seu navegador? Então fui para a ação e programação, eu reutilizei partes do meu antigo projeto FriskEuphoria, que foi originalmente criado para fazer unpack e pack do data.win
do Undertale e de outros jogos feitos no GameMaker: Studio.
Criei um projeto vazio no GameMaker: Studio 1 com apenas uma sala vazia e um objeto que no "Create" possuia o seguinte código:
show_message("owo whats this???");
show_message("uwu whats this???");
show_message(";w; whats this???");
Como reaproveitei o código do FriskEuphoria, a parte de extrair as strings já tinha sido feita, yay!
Então parti para a parte de fazer unpack da parte do código... que foi difícil, já que era a primeira vez que eu implementei um programa que faz parse e executa bytecode.
E depois de ler MUITA a documentação e muito trial and error, você precisa fazer o seguinte para ler o chunk de "CODE"
CODE
)data.win
aonde está o conteúdo)gml_Object_owo_Create_0
E quando ler o código, cada "instrução" são 32 bits, por exemplo:
A VM do GameMaker: Studio é stack-based (parecido como a VM do Java funciona).
Para você parsear qual op code você está lendo, você precisa pular os 3 primeiros bytes e ler, no exemplo acima, é um op code 0xC0
, que é um push de uma constante.
Depois você volta para o começo da leitura das instruções para ler os 3 primeiros bytes que foram pulados, já que eles possuem informações importantes sobre o op code.
No caso do 0xC0
, você precisa pular os dois primeiros bytes (que servem para nada), você lê o byte que indica o tipo do push (neste caso é 0x06
, que significa que estamos fazendo push de uma "String"), e aí você pula mais um byte (que seria o op code) e leia o próximo byte, que é o ID de referência da String, que, neste caso, é 0x03
...
E olhando no dump de strings, qual é a string que fica na posição 3? owo whats this???
! Então estamos pelo caminho certo!!
E depois precisei parsear as outras instruções, eu acabei implementando as seguintes instruções e tipos:
Ah, e também tive que fazer unpack do chunk FUNC
, já que quando você usa uma função do GameMaker: Studio (exemplo: show_message
) a função é colocada na seção FUNC
e ao usar a instrução de call, ele passa o ID de referência da função.
E, finalmente, fiz a VM executar o código (coloquei para o show_message
mostrar a mensagem no console)... e deu certo!
Claro, se eu implementasse todos os op codes que o GM:S usa e implementar todas as funções do GM:S, seria possível executar qualquer jogo feito no GameMaker: Studio na VM.
E por isso isso que eu fiz é inútil/idiota: É legal como um conceito, mas irá demorar MUITO para implementar uma VM completa que consiga interpretar todos os bytecodes e implementar todas as funções que o GameMaker: Studio possui. Claro, é possível eu apenas implementar os bytecodes e funções que o Undertale utiliza, só para servir como proof of concept... mas quem sabe eu decida fazer isso outro dia.