No verão passado, comecei a engenharia reversa Animal Crossing para o GameCube. Eu queria explorar a possibilidade de criar mods para este jogo. Além disso, queria documentar o processo para criar tutoriais para pessoas interessadas em invadir ROMs e engenharia reversa. Neste post, falarei sobre os recursos de depuração para desenvolvedores que permaneceram no jogo e também compartilharei como descobri combos de truques que podem ser usados para desbloqueá-los.
new_Debug_mode
Estudando os símbolos de depuração restantes, notei os nomes das funções e variáveis que contêm a palavra "debug" e decidi que seria interessante ver se alguma funcionalidade de depuração permanecia no jogo. Se eu conseguir ativar as funções de depuração ou desenvolvimento, isso me ajudará no processo de criação de mods.
A primeira função que notei foi 
new_Debug_mode . É chamado pela função de 
entry , que inicia imediatamente após a tela do logotipo da Nintendo terminar. Tudo o que ela faz é colocar a estrutura de byte 
0x1C94 e salvar um ponteiro nela.
Depois de ser chamado na 
entry na estrutura hospedada no deslocamento 
0xD4 imediatamente antes de chamar o 
mainproc valor 0 é definido.
Para ver o que acontece quando o valor não é zero, 
80407C8C a instrução 
li r0, 0 em 
80407C8C , substituindo-a por 
li r0, 1 . Os bytes brutos da instrução 
li r0, 0 são 
38 00 00 00 onde o valor atribuído está no final da instrução, então eu poderia substituir os bytes por 
38 00 00 01 e obter 
li r0, 1 . Como uma maneira mais confiável de criar instruções, você pode usar algo como o 
kstool :
$ kstool ppc32be "li 0, 1"
li 0, 1 = [ 38 00 00 01 ]No emulador Dolphin, esse patch pode ser aplicado acessando a guia "Patches" nas propriedades do jogo e inserindo-o da seguinte maneira:
Depois de atribuir o valor 1, um gráfico interessante apareceu na parte inferior da tela:
Parecia um indicador de desempenho: as pequenas barras na parte inferior da tela aumentavam ou diminuíam. (Mais tarde, quando examinei os nomes das funções que desenham esse gráfico, descobri que elas realmente exibem métricas de uso de CPU e memória.)
Foi ótimo, mas não particularmente útil. Depois de atribuir o valor 1, minha cidade parou de carregar, para que nada mais pudesse ser feito aqui.
Modo Zuru
Novamente, comecei a procurar outras referências a funções de depuração e várias vezes me deparei com algo chamado "modo zuru". As ramificações dos blocos de código com funcionalidade de depuração costumavam verificar a variável 
zurumode_flag .
zzz_LotsOfDebug (o nome que eu mesmo 
game_move_first ) na função 
game_move_first mostrada acima é chamado apenas quando 
zurumode_flag não 
zurumode_flag igual a zero.
Procurando funções associadas a esse valor, encontrei estas:
- zurumode_init
- zurumode_callback
- zurumode_update
- zurumode_cleanup
À primeira vista, seu objetivo é misterioso, eles manipulam bits nos desvios de uma variável chamada 
osAppNMIBuffer .
Aqui está como o trabalho dessas funções olhou à primeira vista:
zurumode_init
- Define zurumode_flagcomo 0
- Verifica vários bits no osAppNMIBuffer
- Salva um ponteiro para a função zurumode_callbackna estruturapadmgr
- Chamadas zurumode_update
zurumode_update
- Verifica vários bits no osAppNMIBuffer
- Dependendo do valor desses bits, o zurumode_flagatualiza
- Imprime uma string de formato no console do SO.
Isso geralmente é útil para contextualizar o código, mas havia muitos caracteres não imprimíveis na linha. O único texto reconhecível foi "zurumode_flag" e "% d".
Supondo que poderia ser texto em japonês com codificação de caracteres multibyte, passei a string pela ferramenta de reconhecimento de codificação e descobri que a string foi codificada com Shift-JIS. Na tradução, a linha simplesmente significava "O valor de zurumode_flag mudou de% d para% d". Isso não nos fornece muitas informações novas, mas agora sabemos que o Shift-JIS é usado: em arquivos binários e tabelas de linhas, há muito mais linhas nessa codificação.
zurumode_callback
- Chamadas zerumode_check_keycheck
- Verifica vários bits no osAppNMIBuffer
- O valor zurumode_flagestá sendozurumode_flag
- Chamadas zurumode_update
zerumode_check_keycheck até nos conhecermos por causa de uma ortografia diferente ... o que é?
Uma função enorme e complexa que trabalha muito mais em bits com valores sem nome.
Nesse ponto, decidi dar um passo atrás e estudar outras funções e variáveis de depuração, porque não tinha certeza da importância do modo zuru. Além disso, não entendi o que “verificação de chave” significa aqui. É possível que essa seja uma chave criptográfica?
Voltar à depuração
Nessa época, notei um problema com minha maneira de carregar símbolos de depuração na IDA. O arquivo 
foresta.map no disco do jogo contém muitos endereços e nomes de funções e variáveis. No começo, não vi que os endereços de cada seção recomeçam do zero, então escrevi um script simples que adiciona uma entrada de nome para cada linha do arquivo.
Escrevi novos scripts da IDA para corrigir o carregamento de tabelas de símbolos para diferentes seções do programa: 
.text , 
.rodata , 
.data e 
.bss . A seção 
.text contém todas as funções, então eu fiz isso para que, desta vez, quando eu definir o nome, o script reconheceria automaticamente as funções em cada endereço.
Nas seções de dados, ele agora criou um segmento para cada objeto binário (por exemplo, 
m_debug.o , que deveria ser o código compilado para algo chamado 
m_debug ) e definiu o espaço e os nomes de cada parte dos dados.
Isso me deu muito mais informações, mas tive que definir manualmente o tipo de dados para cada parte dos dados, porque defini cada objeto de dados como uma matriz de bytes simples. (Olhando para trás, entendo que seria melhor supor que fragmentos de 4 bytes continha números inteiros de 32 bits, porque havia muitos e muitos endereços contidos de funções e dados importantes para a criação de referências cruzadas.)
Estudando o novo segmento 
.bss para 
m_debug_mode.o , encontrei várias variáveis no formato 
quest_draw_status e 
event_status . Isso é interessante porque eu queria que informações úteis, não apenas um gráfico de desempenho, fossem exibidas no modo de depuração. Felizmente, nesses registros de dados, havia referências cruzadas para um grande pedaço de código verificando 
debug_print_flg .
Usando um depurador no emulador Dolphin, defino um ponto de interrupção no local da função em que 
debug_print_flg verificado (no 
8039816C ) para entender como essa verificação funciona. Mas o programa nunca passou para esse ponto de interrupção.
Vamos 
game_debug_draw_last por que isso acontece: essa função é chamada por 
game_debug_draw_last . Adivinha qual valor é verificado antes de sua chamada condicional? 
zurumode_flag ! Que diabos está acontecendo?
Defina um ponto de interrupção nessa verificação ( 
80404E18 ) e funcionou imediatamente. O valor de 
zurumode_flag era zero, portanto, na execução normal, o programa teria perdido a chamada para esta função. Em vez disso, inseri uma instrução de ramificação NOP (substituí-a por uma instrução que não faz nada) para verificar o que acontece quando a função é chamada.
No depurador Dolphin, isso pode ser feito pausando o jogo, clicando com o botão direito do mouse nas instruções e selecionando “Insert nop”:
Nada aconteceu. Depois, verifiquei o que estava acontecendo dentro da função e descobri outra construção ramificada que contornava tudo de interessante que acontecia em 
803981a8 . Também inseri o NOP e a letra “D” apareceu no canto superior direito da tela.
Nesta função no 
8039816C (eu chamei de 
zzz_DebugDrawPrint ), ainda há um monte de código interessante, mas não é chamado. Se você olhar para esta função na forma de um gráfico, poderá ver que há uma série de operadores de ramificação que ignoram os blocos de código em toda a função:
Depois de inserir o NOP em vez de várias outras construções ramificadas, comecei a ver várias coisas interessantes na tela:
A próxima pergunta era como ativar essa funcionalidade de depuração sem alterar o código.
Além disso, em algumas construções de ramificação, 
zurumode_flag ocorre novamente nessa função de desenho de depuração. 
zurumode_update outro patch para que em 
zurumode_update sinalizador 
zurumode_update sempre 
zurumode_flag atribuído ao valor 2, porque quando não se compara com 0, é comparado especificamente com o valor 2.
Depois de reiniciar o jogo, vi no canto superior direito da tela uma mensagem "msg. não "
O número 687 é o identificador de registro da mensagem exibida mais recentemente. Eu o verifiquei usando o programa visualizador de tabelas que escrevi no início da análise, mas você também pode verificá-lo usando o 
editor de tabelas de strings com uma GUI completa , que escrevi para invadir ROMs. Aqui está a aparência da postagem no editor:
Nesse ponto, ficou claro que o estudo do modo zuru não era mais retirado - está diretamente relacionado às funções de depuração do jogo.
Voltar ao modo Zuru novamente
zurumode_init inicializa várias coisas:
- 0xC(padmgr_class)recebe o valor do endereço- zurumode_callback
- 0x10(padmgr_class)recebe o valor do endereço da própria- padmgr_class
- 0x4(zuruKeyCheck)recebe o valor do último bit na palavra carregada de- 0x3C(osAppNMIBuffer).
Eu descobri o que 
padmgr o 
padmgr , uma abreviação de "gamepad manager". Isso significa que pode haver uma combinação especial de teclas (botões) que podem ser inseridas no gamepad para ativar o modo zuru, ou algum tipo de dispositivo de depuração ou função do console do desenvolvedor que pode ser usado para enviar um sinal para ativá-lo.
zurumode_init é executado apenas na primeira inicialização do jogo (quando o botão de reset é pressionado, ele não funciona).
Depois de definir um ponto de interrupção no endereço 
8040efa4 , no qual o valor 
0x4(zuruKeyCheck) é atribuído 
0x4(zuruKeyCheck) , podemos ver que, ao carregar sem pressionar as teclas, o valor é definido como 0. Se você substituí-lo por 1, algo interessante acontece:
A letra “D” aparece novamente no canto superior direito (desta vez é verde, não amarelo), e algumas informações de montagem também são exibidas:
[CopyDate: 02/08/01 00:16:48 ]
[Date: 02-07-31 12:52:00]
[Creator:SRD@SRD036J]Um patch que sempre define 
0x4(zuruKeyCheck) como 1 no início é assim:
8040ef9c 38c00001Esta parece ser a maneira correta de inicializar o modo zuru. Após isso, várias ações podem ser necessárias para obter a exibição de certas informações de depuração. Iniciando o jogo, dando uma volta nele e conversando com um morador, não veremos nenhuma das mensagens mencionadas acima (exceto a letra "D" no canto).
Os suspeitos mais prováveis são 
zurumode_update e 
zurumode_callback .
zurumode_update
zurumode_update chamado primeiro em 
zurumode_init e depois constantemente chamado por 
zurumode_callback .
Ele verifica novamente o último bit 
0x3C(osAppNMIBuffer) e, com base nesse valor, atualiza 
zurumode_flag .
Se o bit for zero, o sinalizador será definido como zero.
Caso contrário, a seguinte instrução é executada, com o valor completo 
0x3c(osAppNMIBuffer) sendo 
r5 :
extrwi r3, r5, 1, 28Extrai o 28º bit de 
r5 e o armazena em 
r3 .
Em seguida, 1 é adicionado ao resultado, ou seja, o resultado final é sempre 1 ou 2.
Em seguida, 
zurumode_flag comparado com o resultado anterior, dependendo de quantos dos 28º e últimos bits estão configurados para 
0x3c(osAppNMIBuffer) : 0, 1 ou 2.
Este valor é gravado em 
zurumode_flag . Se não mudar nada, a função sai e retorna o valor atual do sinalizador. Se ele mudar o valor, será executada uma cadeia muito mais complexa de blocos de código.
Uma mensagem é exibida em japonês: o mesmo "valor zurumode_flag alterado de% d para% d", sobre o qual falamos acima.
Em seguida, uma série de funções é chamada com argumentos diferentes, dependendo se o sinalizador se tornou igual a zero ou não. O código assembler desta parte é monótono, então mostrarei seu pseudocódigo:
 if (flag_changed_to_zero) { JC_JUTAssertion_changeDevice(2) JC_JUTDbPrint_setVisible(JC_JUTDbPrint_getManager(), 0) } else if (BIT(nmiBuffer, 25) || BIT(nmiBuffer, 31)) { JC_JUTAssertion_changeDevice(3) JC_JUTDbPrint_setVisible(JC_JUTDbPrint_getManager(), 1) } 
Observe que, se o sinalizador for zero, o argumento 0 será passado para JC_JUTDbPrint_setVisible.
Se o sinalizador não for igual a 
0x3C(osAppNMIBuffer) E o bit 25 ou 31 são definidos como 
0x3C(osAppNMIBuffer) , então 
setVisible passado o argumento 1.
Esta é a primeira chave para ativar o modo zuru: o último bit 
0x3C(osAppNMIBuffer) deve ser definido como 1 para exibir informações de depuração e definir 
zurumode_flag valor diferente de zero.
zurumode_callback
zurumode_callback está localizado em 
8040ee74 e provavelmente é chamado por uma função relacionada ao gamepad. Após inserir um ponto de interrupção no depurador Dolphin, a pilha de chamadas nos mostra que na verdade está sendo chamada de 
padmgr_HandleRetraceMsg .
Uma de suas primeiras ações foi executar 
zerucheck_key_check . Essa função é complexa, mas parece que em geral foi projetada para ler e atualizar o valor de 
zuruKeyCheck . Antes de passar para a função de verificação de tecla, decidi verificar como esse valor é usado no restante da função de retorno de chamada.
Em seguida, ele novamente verifica alguns bits em 
0x3c(osAppNMIBuffer) . Se o bit 26 estiver definido ou se o bit 25 estiver definido e 
padmgr_isConnectedController(1) retornar um valor diferente de zero, o último bit em 
0x3c(osAppNMIBuffer) como 1!
Se nenhum desses bits estiver definido ou o bit 25 estiver definido, mas 
padmgr_isConnectedController(1) retornar 0, a função verificará se o byte no endereço 
0x4(zuruKeyCheck) é igual a zero. Se igual, ele redefine o último bit no valor original e o grava de volta em 
0x3c(osAppNMIBuffer) . Caso contrário, ele ainda define o último bit como 1.
No pseudo-código, fica assim:
 x = osAppNMIBuffer[0x3c] if (BIT(x, 26) || (BIT(x, 25) && isConnectedController(1)) || zuruKeyCheck[4] != 0) { osAppNMIBuffer[0x3c] = x | 1  
Depois disso, se o bit 26 não estiver definido, a função continuará chamando 
zurumode_update e será encerrada.
Se o bit estiver definido, se 
0x4(zuruKeyCheck) não 
0x4(zuruKeyCheck) igual a zero, ele carregará uma string de formato na qual exibirá o seguinte: “ZURU% d /% d”.
Para resumir o subtotal
Aqui está o que acontece:
padmgr_HandleRetraceMsg chama 
zurumode_callback . Eu suponho que essa "mensagem de retração de identificador" significa que simplesmente verifica as teclas digitadas no controlador. Com cada varredura, isso pode causar uma série de retornos de chamada diferentes.
Quando 
zurumode_callback executado 
zurumode_callback ele verifica os pressionamentos de tecla atuais (botões). Parece que ela está verificando um botão específico ou uma combinação de botões.
O último bit no NMI Buffer é atualizado dependendo dos bits específicos em seu valor atual, bem como do valor de um dos bytes 
zuruKeyCheck ( 
0x4(zuruKeyCheck) ).
Então 
zurumode_update é 
zurumode_update e verifica esse bit. Se for 0, o sinalizador do modo zuru será definido como 0. Se for 1, o sinalizador de modo mudará para 1 ou 2, dependendo se o bit 28 estiver definido.
Existem três maneiras de ativar o modo zuru:- O bit 26 está definido como 0x3C(osAppNMIBuffer)
- O bit 25 está definido como 0x3C(osAppNMIBuffer)e o controlador está conectado à porta 2
- 0x4(zuruKeyCheck)não- 0x4(zuruKeyCheck)zero
osAppNMIBuffer
Interessado no que significa 
osAppNMIBuffer , comecei a procurar por "NMI" e encontrei links no contexto da Nintendo como "interrupção não mascarável". Acontece que o nome dessa variável é totalmente mencionado na documentação do desenvolvedor do Nintendo 64:
o osAppNMIBuffer é um buffer de 64 bytes que é limpo após uma reinicialização a frio. Se o sistema reiniciar devido à NMI, o estado desse buffer não será alterado.
De fato, esse é um pequeno pedaço de memória salva durante uma reinicialização "suave" (com o botão de reinicialização). O jogo pode usar esse buffer para armazenar quaisquer dados enquanto o console estiver na rede. O Animal Crossing original foi lançado no Nintendo 64, por isso é lógico que algo semelhante deveria aparecer no código.
Se formos para o arquivo binário 
boot.dol (tudo mostrado acima estava em 
foresta.rel ), sua função 
main possui muitos links para 
osAppNMIBuffer . Uma rápida olhada mostra que há uma série de verificações que podem levar à 
0x3c(osAppNMIBuffer) valores de diferentes bits 
0x3c(osAppNMIBuffer) usando operações OR.
Os seguintes valores do operando OR podem ser interessantes:
- Bit 31: 0x01
- Bit 30: 0x02
- Bit 29: 0x04
- Bit 28: 0x08
- Bit 27: 0x10
- Bit 26: 0x20
Lembramos que os bits 25, 26 e 28 são especialmente interessantes: 25 e 26 determinam se o modo zuru está ativado e o bit 28 determina o nível da flag (1 ou 2).
O bit 31 também é interessante, mas parece que muda dependendo dos valores dos outros.
Bit 26
Primeiro: no endereço 
800062e0 há uma instrução 
ori r0, r0, 0x20 com um valor de buffer de 
0x3c . Ele define o bit 26, que sempre ativa o modo zuru.
Para que o bit seja definido, o oitavo byte retornado de 
DVDGetCurrentDiskID deve ser 
0x99 . Este identificador está localizado no início da imagem de disco do jogo e é carregado na memória em 
80000000 . Em um lançamento de varejo regular do jogo, o ID fica assim:
47 41 46 45 30 31 00 00 GAFE01..Substituindo o último byte do identificador por um 
0x99 para 
0x99 , obtemos a seguinte imagem ao iniciar o jogo:
E o seguinte é exibido no console do SO:
06:43:404 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: ZURUMODE2 ENABLE
08:00:288 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: osAppNMIBuffer[15]=0x00000078Todos os outros patches podem ser removidos, após o que a letra D aparece novamente no canto superior direito da tela, mas não há mais mensagens de depuração ativadas.
Bit 25
O bit 25 é usado em conjunto com a verificação da porta do controlador 2. O que faz com que ele seja ligado?
Acontece que ele deve usar a mesma verificação do bit 28: a versão deve ser maior ou igual a 
0x90 . Se o bit 26 estiver definido (o ID é 
0x99 ), os dois bits também serão definidos e o modo zuru ainda será ativado.
No entanto, se a versão estiver no intervalo de 
0x90 a 
0x98 , o modo zuru não será ativado instantaneamente. Lembre-se da verificação realizada em 
zurumode_callback - o modo será ativado apenas se o bit 25 estiver definido 
e padmgr_isConnectedController(1) retornar um valor diferente de zero.
Após o controlador ser conectado à porta 2 (o argumento 
isConnectedController possui zero indexação), o modo zuru é ativado. A letra D e as informações sobre a montagem aparecem na tela inicial, e nós ... podemos controlar a exibição da depuração usando os botões do segundo controlador!
Alguns botões executam ações que não apenas alteram a exibição, mas também, por exemplo, aumentam a velocidade do jogo.
zerucheck_key_check
O último mistério permanece 
0x4(zuruKeyCheck) . Acontece que esse valor é atualizado com uma enorme função complexa que mostrei acima:
Usando o depurador do emulador Dolphin, consegui determinar que o valor verificado por esta função é um conjunto de bits correspondente ao pressionamento do botão no segundo controlador.
O rastreamento de cliques no botão é armazenado em um valor de 16 bits em 
0x2(zuruKeyCheck) . Quando o controlador não está conectado, o valor é 
0x7638 .
2 bytes contendo sinalizadores das teclas pressionadas do controlador são baixados e atualizados no início de 
zerucheck_key_check . O novo valor é passado com o registro 
r4 função 
padmgr_HandleRetraceMsg quando chama a função de retorno de chamada.
Perto do final de 
zerucheck_key_check há outro local em que 
0x4(zuruKeyCheck) atualizado 
0x4(zuruKeyCheck) .
Ele não apareceu na lista de referências cruzadas porque é usado como o endereço base r3, e podemos descobrir o valor r3apenas olhando para qual valor é atribuído a ele antes de chamar esta função.No endereço, o 8040ed88valor é r4gravado em 0x4(zuruKeyCheck). Logo antes disso, mas ele é gravado no mesmo local e, em seguida, XORs a partir de 1. A tarefa desta operação é alternar o valor do byte (e de fato o último bit) entre 0 e 1. (Se o valor for 0, o resultado deXOR de 1 será 1). Se o valor for 1, o resultado será 0. Consulte a tabela verdade para XOR.)Anteriormente, quando estudei os valores na memória, não percebi esse comportamento, mas tentarei quebrar essa instrução no depurador para entender o que está acontecendo. O valor original é carregado em 8040ed7c.Sem tocar nos botões do controlador, não chegarei a esse ponto de interrupção na tela inicial. Para entrar nesse bloco de código, o valor r5deve se tornar igual 0xbantes da instrução de ramificação anterior ao ponto de interrupção ( 8040ed74). Dos muitos caminhos diferentes que levam a esse bloco, apenas um atribui um r5valor 0xbà sua frente no endereço 8040ed68.Observe que, para alcançar o bloco que atribui o r5valor 0xB, o valor r0deve ser igual logo antes dele 0x1000. Após os blocos da cadeia até o início da função, podemos ver todas as restrições necessárias para atingir esse bloco:- 8040ed74: o valor r5deve ser igual0xB
- 8040ed60: o valor r0deve ser igual0x1000
- 8040ebe8: o valor r5deve ser igual0xA
- 8040ebe4: o valor r5deve ser menor0x5B
- 8040eba4: o valor r5deve ser maior0x7
- 8040eb94: o valor r6deve ser 1
- 8040eb5c: o valor r0não deve ser 0
- 8040eb74: os valores do botão da porta 2 devem mudar

old_vals = old_vals XOR new_vals
old_vals = old_vals AND new_valsr0r00x1000r50xA. r5e r6são carregados desde 0x0(zuruKeyCheck)o início da função de teste principal e atualizados mais perto do fim, quando não entramos no bloco de código que o inclui 0x4(zuruKeyCheck).Existem vários lugares antes disso onde o r5valor é atribuído 0xA:8040ed38
- 8040ed34: o valor- r0deve ser igual- 0x4000(o botão B é pressionado)
- 8040ebe0: o valor- r5deve ser igual- 0x5b
- 8040eba4: o valor- r5deve ser maior- 0x7
- então tudo corre como antes ...
r5 deve começar com 0x5b8040ed00
- 8040ecfc: o valor- r0deve ser igual- 0xC000(A e B são pressionados)
- 8040ebf8: o valor- r5deve ser> = 9
- 8040ebf0: o valor- r5deve ser menor que 10
- 8040ebe4: o valor- r5deve ser menor- 0x5b
- 8040eba4:- r5deve ser mais- 0x7
- então tudo corre como antes ...
r5 deve começar às 98040ed50
- 8040ed4c: o valor- r0deve ser igual- 0x8000(botão A pressionado)
- 8040ec04: o valor- r5deve ser menor- 0x5d
- 8040ebe4: o valor- r5deve ser maior- 0x5b
- 8040eba4: o valor- r5deve ser maior- 0x7
- então tudo corre como antes ...
r5deve começar com 0x5cParece que existe algum tipo de estado entre as teclas, após o qual você precisa inserir uma certa sequência de combos a partir dos botões, terminando com a tecla START. Parece que A e / ou B devem ir logo antes de INICIAR.Se você rastrear o caminho do código que define o r5valor como 9, surge um padrão: r5- é um valor crescente que pode aumentar quando um r0valor adequado é encontrado ou zero. Os casos mais estranhos, onde não é um valor na faixa de 0x0até0xB, ocorre ao processar etapas com vários botões, por exemplo, quando A e B. são pressionados simultaneamente.Uma pessoa que tenta entrar nesse combo geralmente não pode pressionar os dois botões exatamente ao mesmo tempo enquanto acompanha o gamepad, portanto, é necessário processar o botão pressionado primeiro.Continuamos a explorar diferentes caminhos de código:- r5assume o valor 9 quando RIGHT é pressionado no endereço- 8040ece8.
- r5assume o valor 8 quando o botão direito C no endereço é pressionado- 8040eccc.
- r5assume o valor 7 quando o botão esquerdo C é pressionado no endereço- 8040ecb0.
- r5assume o valor 6 quando ESQUERDA é pressionado no endereço- 8040ec98.
- r5assume o valor 5 (e r6 assume o valor 1) quando DOWN é pressionado no endereço- 8040ec7c.
- r5assume o valor 4 quando o botão superior C no endereço é pressionado- 8040ec64.
- r5assume o valor 3 quando o botão inferior C no endereço é pressionado- 8040ec48.
- r5assume o valor 2 quando UP é pressionado no endereço- 8040ec30.
- r5assume o valor 1 (e- r6assume o valor 1) quando Z é pressionado no endereço- 8040ec1c.
A sequência atual é:Z, CIMA, C-BAIXO, C-UP, BAIXO, ESQUERDA, C-ESQUERDA, C-DIREITA, DIREITA, A + B, INÍCIOAntes de verificar Z, mais uma condição é verificada: embora um novo botão deva ser pressionado Z, os sinalizadores atuais devem ser iguais0x2030: os pára-choques esquerdo e direito também devem ser pressionados (eles têm valores de 0x10e 0x20). Além disso, ACIMA / ABAIXO / ESQUERDA / DIREITA são botões D-pad, não um stick analógico.Batota código
A combinação completa fica assim:- Segure os para-choques L + R e pressione Z
- D-up
- C-DOWN
- C-up
- D-down
- D-esquerda
- C-esquerda
- C-DIREITA
- D-DIREITA
- A + B
- INICIAR
Isso funciona! Conecte o controlador à segunda porta e insira o código, após o qual as informações de depuração serão exibidas. Depois disso, você pode começar a pressionar os botões no segundo (ou até terceiro) controlador para executar várias ações.Essa combinação funcionará sem corrigir o número da versão do jogo. Pode até ser usado em uma cópia regular do jogo, sem nenhuma ferramenta de trapaça ou mods de console. Voltar a entrar em combos desativa o modo zuru.A mensagem "ZURU% d /% d" ézurumode_callbackusada para exibir o status dessa combinação se você a inserir quando o ID do disco já for igual 0x99(provavelmente para depurar o próprio código de fraude). O primeiro número é sua posição atual na sequência, correspondente r5. O segundo pega o valor 1, quando certos botões da sequência são mantidos, eles podem corresponder a quando o r6valor é atribuído 1.A maioria das mensagens não explica o que está fazendo na tela; portanto, para entender sua finalidade, é necessário encontrar funções que as processem. Por exemplo, uma longa linha de estrelas azuis e vermelhas na parte superior da tela são espaços reservados para exibir o status de várias missões. Quando a missão está ativa, alguns números aparecem lá, informando o status da missão.A tela preta exibida ao pressionar Z é um console para exibir mensagens de depuração, especificamente para aspectos de baixo nível, como alocação de memória, erros de heap e outras exceções ruins. Por comportamento, fault_callback_scrollpode-se supor que ele seja usado para exibir esses erros antes que o sistema reinicie. Ele não inicia nenhum desses erros, mas pode causar a impressão de alguns caracteres ilegíveis com vários NOPs. Eu acho que no futuro será muito útil para exibir suas próprias mensagens de depuração:Depois de fazer tudo isso, descobri que entrar no modo de depuração corrigindo o ID daversão 0x99já é conhecido por outras pessoas: https://tcrf.net/Animal_Crossing#Debug_Mode . (Também existem boas notas no link que indicam várias mensagens e conversam sobre outras coisas que podem ser feitas com o controlador na porta 3.) No entanto, até onde eu sei, ninguém publicou a combinação de truques ainda.Isso é tudo.
 Há outros recursos de desenvolvedor que eu gostaria de explorar, como a tela de depuração do cartão e a tela de seleção do emulador NES e como ativá-los sem usar patches.Além disso, publicarei artigos sobre engenharia reversa de sistemas de diálogo, eventos e missões com o objetivo de criar mods.