Corrigir um bug em um jogo de 2000 no Shockwave

imagem

Histórico de substituição de byte único


Cartoon Cartoon Summer Resort


Era o verão de 2000. Eu completei seis anos, acabei de terminar a primeira série e as férias começaram. Isso significava que eu podia brincar por um longo tempo na rua, assistir desenhos animados e ligar o computador do meu pai com o Windows 98 para procurar jogos em uma terra completamente nova e desconhecida chamada Internet. Um dos meus favoritos era o site do Cartoon Network. Nele, encontrei muitos jogos fascinantes baseados em desenhos animados na televisão. Naquele verão, eles lançaram uma série de jogos chamados Cartoon Cartoon Summer Resort.


Jogabilidade do primeiro episódio de Cartoon Cartoon Summer Resort

Este jogo era um RPG / aventura bidimensional com uma vista superior de lado, composta por quatro episódios. O jogador controlava um personagem de desenho animado nas férias no resort com outros personagens de desenho animado. Em cada episódio, um problema apareceu no resort que precisava ser resolvido. O jogador teve que resolvê-lo interagindo com os personagens e encontrando objetos ou trocando-os.

Voltando 18 anos


Durante um dos recentes ataques de nostalgia, lembrei-me deste jogo e percebi que o jogaria novamente. Ela tinha quase duas décadas de idade, por isso era difícil encontrar um elo de trabalho.

Além disso, nenhum navegador moderno lançaria o antigo e vulnerável Shockwave player ... exceto o Internet Explorer. Provavelmente fui a primeira pessoa a encontrar um motivo legítimo para usar o Internet Explorer em 2018.

Depois de brincar um pouco, notei momentos que não podiam ser ignorados. Por exemplo, música de fundo monótona tocando em um loop sem fim e reconhecimento insuficiente de colisões.

O mesmo bug


Depois de algum tempo, encontrei um bug no jogo:

Ao percorrer áreas onde nada deve acontecer, às vezes aparece uma caixa de diálogo.

Como pode ser visto na animação, no jogo você pode alugar um barco para se movimentar pela água. Quando você tenta alugar outro barco, a mensagem "Hoje não há mais barcos alugados!" Aparece. Se você navegar para o norte e caminhar pela borda direita da ilha, verá o mesmo texto que deve aparecer no píer do barco.


Nesse estágio, qualquer pessoa sã que respeite seu tempo e energia consideraria o bug um incômodo menor, lembrando-se de que era um jogo medíocre para crianças, criado duas décadas atrás, e continuaria a jogar. Mas eu não.

Já possuindo todo o meu conhecimento de programação e olhando para o jogo, percebi esse bug como estranhamente atraente. Pareceu-me que eu havia descoberto uma tumba antiga e nela encontrei um quebra-cabeça intocado que poderia ter desaparecido, que nunca foi resolvido por ninguém. Para mim, esse bug foi uma oportunidade de aprender e descobrir algo novo. Curiosamente, foram precisamente essas oportunidades que o processo do jogo me deu na infância. Há algo quase poético em como algo pode, inadvertidamente, representar novos desafios se você o observar de um ângulo diferente.

Jogo de Desconstrução


Para consertar o erro, eu precisava entender o funcionamento interno do jogo. Depois de examinar o problema, descobri que o jogo foi criado no Shockwave no aplicativo Director . Ao trabalhar com o Director, os projetos são salvos como um .dir (Director). Este arquivo é semelhante ao arquivo PSD para Photoshop. Assim como um arquivo PSD contém informações não destrutivas sobre camadas e texto, um projeto .dir economiza todos os recursos, código-fonte bruto e outras informações que ajudam no processo de desenvolvimento. O Director usou a linguagem de script proprietária Lingo para animar cenas.

Se o jogo fosse salvo em um arquivo .dir , eu poderia abri-lo no Director e aprender facilmente como o jogo funciona. No entanto, o jogo é publicado como um arquivo .dcr . O arquivo .dcr é uma versão compilada do projeto Director. Ou seja, todo o código-fonte é compilado no bytecode em execução na plataforma Shockwave. Esse processo é semelhante ao modo como um arquivo PSD é simplificado e se torna uma imagem PNG. A imagem PNG (no nosso caso, o arquivo DCR) é menor em tamanho, mas não contém informações sobre camadas e edições, e destina-se apenas à distribuição.

Isso significava que eu estava segurando um objeto binário de 500 KB sem documentação de sua estrutura. Mesmo se eu descobrisse como encontrar bytecode de baixo nível, parece que ninguém estava usando o bytecode Lingo de engenharia reversa e não documentou o princípio da plataforma Shockwave. Toda essa informação é proprietária, pertence à Adobe, que não tem motivos para publicá-la. As chances de entender o jogo pareciam bastante sombrias.

Descompressão


Sentindo-me derrotado porque provavelmente não consegui eliminar esse bug, decidi descobrir se os recursos poderiam ser de alguma forma extraídos do jogo. Imaginei que provavelmente encontraria uma seção de dados compactados ou algo semelhante. Após a pesquisa, encontrei alguns programas chamados offzip e packzip . Essas ferramentas podem procurar dados zlib em binários arbitrários, mostrar deslocamentos e extraí-los em arquivos separados.

Corri o zip com um arquivo DCR e, para minha surpresa, ele realmente encontrou os arquivos! 249 peças, para ser mais preciso.

$ ./offzip.exe -a 1.dcr

- open input file: 1.dcr
- zip data to check: 32 bytes
- zip windowBits: 15
- seek offset: 0x00000000 (0)
+------------+-----+----------------------------+----------------------+
| hex_offset | ... | zip -> unzip size / offset | spaces before | info |
+------------+-----+----------------------------+----------------------+
0x00000026 . 164 -> 214 / 0x000000ca _ 38 8:7:26:0:1:7b6349f6
0x000000d3 .. 3932 -> 9169 / 0x0000102f _ 9 8:7:26:0:1:c1079d84
...
0x00080490 . 265 -> 472 / 0x00080599 _ 0 8:7:26:0:1:04d6b43f
0x00080599 . 209 -> 366 / 0x0008066a _ 0 8:7:26:0:1:7da3ba08

- 249 valid compressed streams found
- 0x0004040d -> 0x001565c8 bytes covering the 50% of the file


Extraí todos esses arquivos em uma pasta e comecei a estudar os resultados. Havia 206 arquivos .dat , 38 arquivos .fff , 4 .atn e um único arquivo .ini .


Descobertas


Comecei com o arquivo INI, mas não adiantou. Era uma tabela simples de conversão de fontes do Diretório 7.0 entre Windows e Mac. Então eu mudei para os arquivos DAT. A maioria deles tinha 1 KB, então comecei com um enorme 144 KB. Abri em um editor hexadecimal e estudei. Principalmente eram dados ilegíveis. No entanto, com o tempo, encontrei algumas palavras que pareciam ser identificadores do Lingo.


A análise dos grandes arquivos DAT me deu algumas pistas, eles mantiveram algumas mensagens interessantes. Descobri que o Photoshop 3.0 provavelmente era usado para gráficos. Eu também aprendi que o jogo tinha uma ferramenta interna de edição de mapas chamada Map-O-Matic v1 . Eu gostaria de ver como ele parecia e foi criado.

Também encontrei o nome da empresa que desenvolvia o jogo: Funny Garbage . O nome do desenvolvedor líder também estava no arquivo, mas não o nomeei. Foi ótimo descobrir o autor do jogo, que teimosamente tentei consertar depois de quase 20 anos, e finalmente ver o rosto da pessoa que se tornou a provável causa dessa agonia. Todas essas migalhas de informações eram obviamente interessantes, mas não ajudaram muito.

Avanço


Então comecei a estudar arquivos .fff em um editor hexadecimal. Para minha grande surpresa, todos os dados nesses arquivos eram legíveis e pareciam dados de mapa:


Extraí manualmente alguns desses dados e os limpei em um editor de texto. O que eu fiz foi muito parecido com uma matriz JSON:

[
#member: "block.104",
#type: #FLOR,
#location: [16, 9],
#width: 64,
#WSHIFT: 0,
#height: 32,
#HSHIFT: 0,
#data: [
#item: [
#name: "",
#type: #WALL,
#visi: [
#visiObj: "",
#visiAct: "",
#inviObj: "",
#inviAct: ""
],
#COND: [[#hasObj: "", #hasAct: "", #giveObj: "sunscreen", #giveAct: "gotscreen"], #none, #none, #none]
],
#move: [#U: 0, #d: 0, #L: 0, #R: 0, #COND: 1, #TIMEA: 0, #TIMEB: 0],
#message: [
[#text: "You bought the sun screen.", #plrObj: "", #plrAct: ""],
[#text: "No more sunscreen today!", #plrObj: "", #plrAct: "gotscreen"]
]
]
]


Foi muito importante, porque eu consegui aprender muito sobre como o jogo funcionava.

  1. O jogo espera que dados de mapa, texto e evento estejam em objetos Lingo, como JSON
  2. Cada entrada #member é um bloco, bloco ou caractere #member .
  3. As coordenadas e os tamanhos dos #member podem ser editados.

Sabendo que as caixas de diálogo do jogo foram salvas nesses arquivos, escrevi uma linha curta para exportar apenas uma caixa de diálogo para um arquivo:

grep -a -o '#text: "[^"]*' Uncompressed/*.fff | awk '{print $0,"\r"}' > Dialogue.txt

Usando este arquivo, posso encontrar rapidamente o texto incorreto e ver em qual arquivo ele estava:


O texto incorreto está em 0004eda0.fff ou em 0004f396.fff . No nosso caso, o texto do bug apareceu no primeiro arquivo. Sabemos disso, porque logo após é uma mensagem que recebemos ao interagir com Og, que é um personagem no mesmo mapa que o bloco com o bug.

Bug fix


Agora eu poderia abrir 0004eda0.fff e encontrar a linha sobre o barco no editor hexadecimal. Tendo encontrado, consegui encontrar o objeto #member associado a ele. Em seguida, altere suas propriedades e salve o arquivo. Em seguida, eu o packzip novamente e o remendo no arquivo de origem do jogo DCR usando o packzip .


$ ./packzip -o 0x0004EDA0 Uncompressed/0004eda0.fff test.dcr

Quando altero o tipo de bloco de block.11 para block.13 e block.13 patch do jogo, posso ver claramente o contorno do bloco de erros:


Alterando o ID do bloco, você pode ver os limites da área do problema

A correção de bug em si é ridiculamente simples. Tudo o que eu precisava fazer era alterar o identificador #message desse #message erros para #fessage :


Agora, se corrigirmos as alterações e retornarmos a essa área, as mensagens não aparecerão novamente!


Corrigido um bug no jogo, mudando literalmente 1 byte

Por que é possível corrigir o erro?


Só posso supor que o mecanismo do jogo provavelmente verifique o bloco em que está quando o jogador se move. Se alguma condição for atendida, ele exibirá a mensagem correspondente a esse bloco da matriz #message . Depois de alterar #message para #fessage link para a matriz #message que o código está procurando não é mais encontrado. Ele considera este um objeto vazio (ou indefinido?) E não exibe nada.

Considere um exemplo em JS:

 function foo(bar) { if (bar["message"] !== undefined) { // display the message } } 

Suponha que não possamos alterar a função foo() , mas precisamos alterar o resultado. Temos acesso aos dados transmitidos a ela. Você pode renomear a propriedade de message do objeto passado e a função pensará que não estava lá.

Como esse bug apareceu?


Suponho que por causa da preguiça. Os desenvolvedores usaram um editor de mapas para criar esta área. Muito provavelmente, eles copiaram cartões feitos no passado para novas áreas e depois os mudaram. Isso é mais fácil do que criar tudo do zero. Mas, como os dados do evento e do texto são misturados, eles provavelmente são esquecidos em alguns lugares e esquecem de excluí-los ou substituí-los. Isso parece o mais lógico, porque o diálogo incorreto costumava ser retirado de uma placa vizinha, onde era usado corretamente.

Por que gastar tanto trabalho em algo tão insignificante?


Eu não sei Talvez devido à nostalgia?

Source: https://habr.com/ru/post/pt425601/


All Articles