Ponteiros
De ROMHackingWiki
| Este artigo é somente um esboço e seu conteúdo ainda pode ser melhorado. Você pode ajudar a ROMHackingWiki melhorando-o. |
| Esse artigo contém apenas informações textuais. Você pode ajudar a ROMHackingWiki ilustrando-o. |
| Este é um artigo intermediário sobre o ROMHacking, leia-o com bastante atenção! |
Todo ROMHacker quando no inicio de seu aprendizado já deve ter se deparado com ponteiros, tido por alguns como 'monstros', geralmente é a primeira grande dificuldade de quem se aventura no mundo da tradução, e por isso também está no topo da lista de motivos para desistências de projetos.
Tabela de conteúdo |
O que são?
Ponteiros podem ser descritos como referências que apontam para um determinado lugar, posição ou offset.
Um exemplo abstrato de ponteiro é o índice de um livro, que contém de forma organizada o conteúdo e o número da página que devemos abrir para encontrá-lo.
Vamos assumir um jogo de RPG, em uma cidade existem diversos NPCs, e que o jogador pode escolher arbitrariamente falar com qualquer um deles. O que provavelmente vai acontecer é que, ao se iniciar o diálogo, o personagem possuirá algum tipo de ponteiro que indicará a posição nos arquivos do jogo em que o texto que ele deve exibir estará.
Para entendermos isso melhor, suponhamos que existam vários diálogos, cada um numerado de 0 até K, sendo K um número inteiro.
- Exemplo:
Índice Diálogo 0 João: "Eu sou o João"1 Pedro: "Meu nome é Pedro José!"2 João: "Você de novo! Já disse que sou o João!"3 Mario: "Levei uma pancada na nuca, mas estou bem!"
- Cada diálogo foi número de 0 até 3, isso serve de referência para que o jogo saiba qual diálogo chamar na hora que você conversar com alguém!
- Vamos supor que você primeiro vá falar com Pedro. Ao conversar com Pedro o jogo acessará o índice 1 que nada mais é que o ponteiro da fala de Pedro. Caso você fale com Mario, o ponteiro será o índice 3!
Aplicação no ROMHacking
No ROMHacking não é diferente, os ponteiros são valores usados que apontam para a posição de determinados textos ou blocos de textos. Nesse caso, os ponteiros serão representados por sequências de bytes que vão "apontar" para o offset(endereço) do texto ou bloco quem questão. Até esse ponto você pode ter percebido que os ponteiros não são nada mais que referências, redirecionamentos ou índices. Entenda isso do modo que lhe for mais agradavél. Vamos entender agora os tipos mais comuns de ponteiros aplicados no ROMHacking
Tipos
As formas em que os ponteiros são encontrados varia de acordo com as especificações do sistema ou vontade dos programadores, mas a idéia continua sempre a mesma. Abaixo segue uma lista com os tipos mais comuns de ponteiros.
Formas Mais Comuns
Por Offset
Este tipo de ponteiro guarda o valor da posição real dos textos na ROM, ou seja, eles guardam o offset do inicio do texto ou bloco de texto.
Como por exemplo:
Offset - Texto
++++++++++++++
0050h - ABCDE
0055h - FGHIJ
005Ah - KLMNO
Nesta tabela temos o offset do inicio do texto em hexadecimal, e o texto que será encontrado no mesmo. Logo, os nossos ponteiros terão os valores 0x0050, 0x0055 e 0x005A respectivamente. Simples não? Essa é a forma mais encontrada em jogos.
Relativo
Assume um determinado local como ponto de referência, e guarda o número de bytes entre o inicio do texto até a referência inicial. Sendo assim, usando a mesma tabela anterior:
Offset - Texto
++++++++++++++
0x0050 - ABCDE
0x0055 - FGHIJ
0x005A - KLMNO
Assumimos que a posição tida como referência seja a do inicio do texto(0x0050), o nosso primeiro ponteiro teria o valor de 0x00, o segundo 0x05 e o terceiro 0x0A. Na hora de calcular a posição real do texto, o jogo vai somar a posição relativa ao valor do ponteiro, por isto o nome de Ponteiro Relativo.
Formas Menos Comuns
...
Tabelas
Como encontrar a tabela de ponteiros de uma ROM de Super Nintendo
Pré-requisito: conhecer um pouco de edição hexadecimal
Nesta seção você aprenderá a encontrar a tabela de ponteiros para um jogo de Snes.
Digamos que em uma rom de Super Nintendo, o texto "my dad" comece no offset "12E0FF". Esta é a única informação que você precisa, portanto agora vamos achar a tabela:
PASSO 1: O primeiro passo é pesquisar qual é o header do Super Nintendo. O header é um conjunto de bits que existe no início de uma ROM de Super Nintendo, mas que para nós ele só atrapalha, "empurrando" os bits que queremos achar. Cada rom tem um header diferente e fixo, o do Super Nintendo é "200h".
PASSO 2: Pegue o offset do texto citado (12E0FF) e subtraia pelo header do Super Nintendo (200h). Pode fazer isto na calculadora do windows mesmo, clique em 'exibir >> científica' e escolha "Hex" nos checklists à esquerda. Você encontrou o valor "12DEFF", mas este ainda não é o ponteiro.
PASSO 3: Retire do valor encontrado os dois primeiros números (ou letras) da esquerda de forma que fique apenas 4 números (ou letras). Neste caso ficará assim: "DEFF".
PASSO 4: Agora inverta-os pegando os símbolos de dois em dois (vai ficar "FFDE") e pronto, "FFDE" é o ponteiro que aponta para o começo do texto "my dad".
PASSO 5: Agora que você tem o valor, é só procurar na rom o hexadecimal "FFDE" mais próximo do texto, e quando você tiver suspeito de que achou, mude seu valor, só pra ver o que acontece na rom. Se ao rodar a rom alguma coisa acontecer, é porque era ele o ponteiro. Se não era ele, a rom vai funcionar como se nada tivesse acontecido ou irá travar. Se nada acontecer volte e arrume! Sempre tenha backups!!
Resumindo: Eu passei o cursor em cima do 'm' de 'my dad', eu olhei numa parte escrita 'offset' e vi escrito 12E0FF. Pronto, agora eu peguei esse valor 12E0FF, e retirei 200h, (por que para acharmos ponteiros temos que pegar o offset e subtrair pelo valor do header do videogame), quando fiz 12E0FF - 200 achei 12DEFF, mesmo quando achei o 12DEFF, tirei os números da esquerda, deixando somente 1 par, (pois é sempre assim que estão os ponteiros), ficou DEFF, e depois disso inverti, ficando FF DE. E isso é o ponteiro, quando você achar 1, em seguida, você achou a tabela de ponteiros...
Métodos
...
Cabeçalhos
Veja a definição de Cabeçalho clicando [aqui][1].
Por que inverter?
Precisamos seguir a ordem de bytes(Big-Endian/Middle-Endian/Little-Endian) dos sistemas nos quais mexemos, na maioria dos casos, arquiteturas como a do NES, SNES, Game Boy, Game Boy Advanced e outros consoles famosos são Little-Endian, ou seja, a ordem que os bytes são armazenados é invertida, por exemplo:
Se temos o valor 123, de acordo com o sistema decimal o número de maior magnitude é o da esquerda(centenas, dezenas, unidades) e o armazenarmos em um sistema Little-Endian, eles serão armazenados da seguinte forma: 321.
Isso afeta tudo na ROM, seguindo o exemplo anterior, o valor do nosso ponteiro continuaria 100h, mas por conta da arquitetura do sistema ele é armazenado na forma de 0001h, e é por isso que costumamos inverter os bytes do offset ao se terminar o cálculo do ponteiro.
O SNES é um sistema 16bits, ou seja ele pode "lidar com 2 bytes por vez", mas ele possui um sistema que "divide" suas ROMs em blocos de dados chamados Bancos(do inglês Banks), a maioria das ROMS assume que o ponteiro e o texto estejam no mesmo 'pedaço' da ROM e por isso, usamos apenas 2 bytes do offset no ponteiro, se nosso texto estivesse no offset 123456h, primeiro teriamos que excluír o header, depois disto feito, como é assumido pelo sistema que o texto esteja em uma parte pré-determinada da ROM, excluímos o byte mais significante do offset, ele será adicionado automaticamente depois, sendo assim, nosso header fica 3256h, e como o sistema do SNES inverte os bytes para armazená-los, precisamos inverter esse valor para encontrar o verdadeiro valor do nosso ponteiro, que nesse caso está armazenado como 5632h.
No GBA, provavelmente devido ao tamanho das ROMs e a quantidade de textos nelas, o 3º byte(o mais significante) não é adicionado automáticamente pelo sistema, por isso, nesse sistema é comum encontrarmos ponteiros de 3 bytes, que apontam direto para o offset do texto sem nenhuma alteração por não possuir nenhum cabeçalho. O GBA também é um sistema Little-Endian e por isso os bytes dos ponteiros são armazenados de forma invertida, o mesmo offset do exemplo anterior, 123456h, seria armazenado como 563412h.
Programação
Exemplo de Calculo para Ponteiros de NES :
header := StrToInt('$' + '10');
offset := IntToHex(valor - header,6);
buffer := Copy(offset,5,2) + Copy(offset,3,2);
offset = offset - 0x10; // Subtrai o Header do Offset inverte = (((offset &0xFF) << 8) | (offset >> 8));
def pointer_2bytes(offset):
offset = offset - 0x10
return ((offset &0xFF) << 8) | (offset >> 8)
$valor = hexdec($campo_valor);]
$header = "10";
$f_calcula = dechex($valor - hexdec($header));
$f_separador = wordwrap($f_calcula, 2, "x", 2);
$f_inverte = explode("x", $f_separador);
printf("Ponteiro: ".$f_inverte[1].$f_inverte[0]."\n");
Curiosidades
...
Finalizando
A edição de ponteiros é extremamente útil, com o seu uso evitamos o uso de abreviações nas traduções, aumentando a qualidade do resultado final.




