Tutorial de Tradução de ROMs
Artigo V
Versão 1.0: 03/08/99
Conteúdo:
1 - Modelos de armazenamento de texto
1.1 - Strings delimitadas
1.2 - Strings de tamanho fixo
1.3 - Ponteiros
2 - Dicas de tradução
2.1 - Procura relativa
2.2 - Facilidade com tabelas
3 - Software Relacionado
3.1 - Relative Search
3.2 - Hexposure
3.3 - TaBuLar

Serão apresentadas aqui as técnicas mais comumente utilizadas nas ROMs para o armazenamento de textos. Os modelos variam de jogo para jogo e é necessário conhecer que modelo está sendo utilizado e saber como trabalhar com ele.
Para este estudo, você deve estar familiarizado com o conceito de string. Strings podem ser palavras ou frases contidas na ROM, ou seja, um conjunto de caracteres que é exibido na tela agrupadamente.
Neste modelo, as strings são armazenadas em seqüência dentro da ROM, sendo que uma é separada da outra por caracteres especiais, que dependem do jogo em questão. Quando exibe uma string no vídeo, o sistema copia o conjunto de caracteres que vai de uma posição inicial até onde for encontrado o caracter delimitador. A posição inicial pode ser o fim da string anterior, isto é, a posição do delimitador anterior. Quando este é o caso, a tradução é bastante facilitada, pois ganha-se flexibilidade na determinação do comprimento das strings. Se a tradução de uma string ficou menor que o texto original, é possível recuar o caracter delimitador, liberando-se espaço que eventualmente seja necessário para a tradução das strings seguintes. Aqui está um pequeno exemplo, onde o delimitador é representado por um asterisco:
Fragmento original da ROM (28 bytes): *P1*P2*Strength*Score*Enemy* Fragmento traduzido (28 bytes): *J1*J1*Força*Pontos*Inimigo*
Observe que "Pontos" e "Inimigo" ocupariam mais espaço do que "Score" e "Enemy", mas a mudança foi possível porque "Strength" ficou menor quando traduzida para "Força" e os delimitadores puderam ser recuados.
Para descobrir se o jogo utiliza este método é só procurar pelos bytes delimitadores entre as strings. Se você observar algum byte que se repete entre elas, tente movê-lo algumas posições para traz ou para frente e verifique os resultados. Faça algo como no exemplo acima e veja se as strings aparecem perfeitamente na tela, sem truncamentos. Se tudo parecer normal, o esquema usado deve ser este mesmo e as técnicas acima são válidas.
Aqui já teremos um pouco mais de dificuldade na tradução e é possível que ocorra uma perda de flexibilidade. Como você pode imaginar pelo seu nome, neste modelo as strings estão armazenadas em seqüência e são apresentadas em grupos de caracteres de um comprimento fixo. Isto significa que, para cada string necessária, o jogo exibe um número X de caracteres, começando de onde terminou a string anterior.
Este modelo pode ser implementado de duas maneiras principais:
A) O comprimento das strings está determinado em algum ponto do código do programa.
Para um certo grupo de strings que aparece em um momento do jogo, o comprimento é exatamente o mesmo. O programador provavelmente vai escolher o valor da maior string e preencher as que forem mais curtas com espaços em branco. Você facilmente reconhece este caso quando as strings curtas possuem esses espaços adicionais para atingir um certo comprimento. Nesta situação, a princípio você terá que se contentar com o comprimento das strings que foi utilizado e tentar enquadrar os textos nesses espaços. Este caso não é tão comum, podendo aparecer em menus e telas de opções, onde os textos são curtos.
B) Cada string tem seu comprimento determinado por um byte de controle.
Aqui o comprimento não é tão fixo assim... Este é um caso comum, no qual cada string é precedida por um byte que indica o comprimento dela. Se você observar que as strings são separadas por bytes de valores variáveis, verifique se esses valores não são exatamente o tamanho da string (às vezes podem ser o tamanho da string + 1 ou o tamanho - 1, fique atento). Nessa situação você resolve o problema da falta de espaço aumentando o valor desse byte, traduzindo a string e deslocando os textos seguintes, se necessário. Tenha cuidado, no entanto, pois o deslocamento do início do texto seguinte pode corromper a sua exibição. Há casos em que a técnica é possível e outros em que não. O problema ocorre quando são utilizados ponteiros para indicar o início das strings (ver seção seguinte).
OBS.: Apesar de terem sido apresentados separadamente, os dois modos de armazenamento de textos vistos até agora podem aparecer ao mesmo tempo nos jogos. É possível, por exemplo, que as strings sejam de tamanho "fixo" e estejam separadas por caracteres delimitadores. Neste caso, esses caracteres podem indicar a formatação do texto no vídeo, determinando "novo parágrafo", "nova tela", etc. Observe como o texto é apresentado e procure na ROM por bytes que possam estar tendo essa função. Você pode inclusive incluí-los na tabela de caracteres para facilitar a reformatação do texto traduzido.
1.3.1 - Introdução
Vamos agora ao método mais poderoso para o armazenamento de textos e também o que permite maior flexibilidade nas traduções. Se o jogo que você estiver traduzindo usar ponteiros, você terá grandes chances de solucionar os problemas de falta de espaço para os textos.
Em programação, ponteiros são conhecidos como variáveis que armazenam endereços de memória. Aqui o significado é exatamente esse. Os ponteiros serão bytes da ROM que indicarão onde se encontram determinadas informações, mais especificamente, onde está o começo de cada string. Vejamos como funcionam os ponteiros no caso do NES que é o nosso tema no momento, apesar do método ser aplicável a qualquer outro sistema com pequenas modificações.
Como foi dito, os ponteiros são grupos de bytes que apontarão para o início das strings. Isto significa que o valor de um certo ponteiro será o endereço (também chamado "offset") do primeiro byte da string com a qual ele está relacionado. No Nintendo os ponteiros costumam ter um tamanho de dois bytes. Para um certo conjunto de strings, os ponteiros relacionados estão geralmente um pouco acima delas, se você visualizar a ROM em um editor hexadecimal. Isto é, estão na direção do início do arquivo, partindo-se de onde estão os textos. Nem todos os jogos utilizam ponteiros. Para descobrir se um certo jogo faz uso deles, aprenda agora como eles funcionam e você facilmente poderá verificar se eles são ou não utilizados em uma ROM.
Os valores dos ponteiros são, portanto, calculados pelo endereço do byte inicial da string, fazendo-se pequenas modificações nesse valor. Para observar melhor esse processo de cálculo, visualize agora esta imagem.
1.3.2 - Encontrando os ponteiros
Na imagem aparecem a tabela de ponteiros (como é chamado o conjunto de ponteiros de um trecho da ROM) e uma parte dos textos de um certo jogo sendo editado no Hexposure. Tudo o que é necessário para o cálculo do primeiro ponteiro (que aponta para a primeira string) está demarcado com retângulos vermelhos (três, no total). A primeira string (a que estamos interessados) começa com "Se você acertar muitas topeiras...". Posicionamos então o cursor sobre o "S" (marcado com um dos retângulos) com o qual a string começa. O endereço desse "S" aparece no canto inferior esquerdo, no campo "Ofs" (de "offset"). Este valor também está demarcado e é exatamente 01B7EFh. O retângulo que falta mencionar é o que aparece no alto da imagem e que está indicando nada menos que o próprio ponteiro para a primeira string! Você pode observar que o valor desse ponteiro é DFB7h e ficou comprovado que o ponteiro estava um pouco acima da string e tinha um tamanho de dois bytes.
Agora só falta você saber como transformar o endereço inicial da string em um valor de ponteiro. Na verdade aquele endereço (01B7EFh) não é o endereço verdadeiro do "S" na ROM. Isso porque o cabeçalho do formato .NES tem 16 bytes de comprimento e, portanto, desloca todos os endereços 16 bytes adiante. A primeira providência é então subtrair dezesseis (10h) do endereço apresentado no editor. Com isso, o endereço passa a ser 01B7DFh.
Observe agora que o endereço tem um tamanho de três bytes significativos (lembre-se de que cada dois dígitos hexadecimais formam um byte). Você deve truncar esse valor para apenas dois bytes simplesmente descartando o byte mais significativo (01h). Agora você tem o novo valor B7DFh.
Por último, é preciso trocar os dois bytes de posição um com o outro. Isso é devido à arquitetura do procesador 6502 utilizado no Nintendo, que armazena uma "palavra de memória" (um conjunto de dois bytes) colocando o byte menos significativo na frente. Com isto temos finalmente o valor do ponteiro DFB7h.
Os retângulos verdes da imagem indicam a segunda string e o segundo ponteiro daquele trecho, só para você ter uma idéia. Faça os cálculos e veja como vai dar certo também.
1.3.3 - Resumo
Como calcular o valor do ponteiro e encontrá-lo, passo-a-passo:
1) Obtenha o endereço do primeiro byte da string (exemplo: 01F5D3h).
2) Subtraia 10h (ex.: 01F5C3h).
3) Se o valor tiver três bytes, descarte o mais significativo (ex.: F5C3h).
4) Troque de lugar os dois bytes restantes (ex.: C3F5h).
5) Procure por este valor a partir da string em direção ao início do arquivo.
1.3.4 - Aplicações
Conchecendo o funcionamento dos ponteiros, fica fácil imaginar o que se pode fazer com eles. Alterando os seus valores, é possível redirecionar todos os textos para os quais isso seja necessário. Uma aplicação bem simples é o caso no qual houve falta de espaço para a tradução de uma string, mas uma outra string já está traduzida e tem espaço sobrando depois dela. Se for possível, você poderá trocar de lugar os ponteiros e também as strings para que a maior delas fique localizada onde houver mais espaço disponível.
Por outro lado, você também pode redirecionar textos para locais não utilizados da ROM. É comum encontrar nas ROMs espaços em branco (com bytes de valor 0 ou FFh, por exemplo). Depois que você tiver certeza de que um certo pedaço da ROM não é utilizado, você pode escrever aí os textos longos e redirecionar os ponteiros da forma apropriada. No caso do Nintendo, se estiverem sendo utiliados ponteiros de dois bytes, esse espaço em branco não pode estar mais de 64KB longe do endereço original do texto. Isto significa que o endereço original e o redirecionado só podem diferir pelos dois bytes menos significativos. Por exemplo, é possível redirecionar o endereço 012378h para 013050h, mas não para 024675h. Ou seja, a partir do terceiro byte, os endereços têm de ser iguais (01 e 01, no exemplo).
Novamente, deve ser esclarecido que o modelo de ponteiros pode aparecer juntamente com outra técnica, como as descritas acima. Os ponteiros costumam marcar apenas o início das strings. Para indicar o final, podem aparecer caracteres delimitadores, por exemplo. Você também deve saber que a implementação de tudo isso varia de jogo para jogo. Aqui foram apresentados os aspectos mais gerais do assunto. Estude bastante a ROM que estiver traduzindo para entender como funciona o armazenamento de textos naquele caso específico.

Uma técnica bastante utilizada para as traduções e que possui várias aplicações é a procura relativa. Ela é geralmente empregada nos casos em que não se possui uma tabela de caracteres para o jogo. Esse tipo de procura é baseado não nos valores absolutos dos bytes sendo pesquisados, mas sim na diferença dos seus valores. Se em um certo jogo as letras têm valores que são uma seqüência contínua, você pode basear a procura no código ASCII ou no próprio alfabeto, de uma maneira geral.
Um exemplo prático: se você fizer uma procura relativa pela seqüência "ABD" e o programa sendo utilizado encontrar seqüências como "FGI" ou "OPR", elas serão exibidas como tendo atingido os requisitos da procura. Isto porque em todas essas seqüências, a semelhança está no fato de que do primeiro para o segundo caracter há uma diferença de 1 (AB, FG, OP) e do segundo para o terceiro há uma diferença de 2 (BD, GI, PR). Veja as seções 3.1 e 3.2 para mais detalhes sobre programas de procura relativa.
Para tornar mais prático o processo de criação de tabelas já existem diversos programas, dos quais um muito interessante é o TaBuLar. Aqui está um pequeno guia para criar tabelas com ele (mais informações na seção 3.3):
1) Obtenha de alguma forma o valor da letra "A" no jogo sendo traduzido.
2) Abra o programa TaBuLar e clique no espaço que corresponde ao valor do "A" (os valores na coluna da esquerda indicam o dígito mais significativo).
3) Clique no menu "Insert | Capital English Alphabet".
4) Adicione outros caracteres cujos valores você saiba.
5) Clique no menu "File | Save Table". Escolha o formato desejado para o arquivo (nesse tutorial temos utilizado o formato do Hexposure). Finalmente, forneça um nome para que o arquivo seja salvo.

Este é um dos programas mais populares para procura relativa. O site oficial é http://fly.to/vale. Lá o autor disponibiliza mais alguns softwares interessantes. O uso deste programa é bem simples, como mostrado a seguir:
O Hexposure também é muito prático para as procuras relativas. Tecle <F6>, digite a seqüência para a procura e aperte <ENTER>. A primeira ocorrência será exibida. Para ver as seguintes, vá apertando <F3>. O programa ainda lhe dá a opção de salvar uma tabela de caracteres baseada na procura relativa. Este é outro excelente modo de começar a construção de uma tabela.
Você pode copiar o Hexposure em: http://www.zophar.net/trans/tranutil.html.
Este programa, discutido na seção 2.2, está disponível em: http://members.tripod.com/court_jester_z/.

| Anterior | Índice |
© Skewer by ERTR |