L'été dernier, j'ai commencé le reverse engineering Animal Crossing pour le GameCube. Je voulais explorer la possibilité de créer des mods pour ce jeu. De plus, je voulais documenter le processus de création de didacticiels pour les personnes intéressées par le piratage des ROM et le reverse engineering. Dans cet article, je parlerai des fonctionnalités de débogage des développeurs qui sont restées dans le jeu, et partagerai également comment j'ai découvert des combinaisons de triche qui peuvent être utilisées pour les déverrouiller.
new_Debug_mode
En étudiant les symboles de débogage restants, j'ai remarqué les noms des fonctions et des variables contenant le mot «débogage», et j'ai décidé qu'il serait intéressant de voir s'il restait une fonctionnalité de débogage dans le jeu. Si j'arrive à activer les fonctions de débogage ou de développement, cela m'aidera à créer des mods.
La première fonction que j'ai remarquée était
new_Debug_mode
. Il est appelé par la fonction d'
entry
, qui démarre immédiatement après la fin de l'écran du logo Nintendo. Elle ne fait que placer la structure d'octets
0x1C94
et y enregistrer un pointeur.
Après avoir été appelé en
entry
dans la structure hébergée à l'offset
0xD4
immédiatement avant d'appeler
mainproc
valeur 0 est définie.
Pour voir ce qui se passe lorsque la valeur n'est pas nulle, j'ai corrigé l'instruction
li r0, 0
à
80407C8C
, en la remplaçant par
li r0, 1
. Les octets bruts de l'instruction
li r0, 0
sont
38 00 00 00
où la valeur assignée est à la fin de l'instruction, donc je pourrais simplement remplacer les octets par
38 00 00 01
et obtenir
li r0, 1
. Pour créer des instructions de manière plus fiable, vous pouvez utiliser quelque chose comme
kstool
:
$ kstool ppc32be "li 0, 1"
li 0, 1 = [ 38 00 00 01 ]
Dans l'émulateur Dolphin, ce patch peut être appliqué en allant dans l'onglet "Patches" dans les propriétés du jeu et en le saisissant comme suit:
Après avoir attribué la valeur 1, un graphique intéressant est apparu en bas de l'écran:
Cela ressemblait à un indicateur de performance: les petites barres en bas de l'écran ont augmenté ou diminué. (Plus tard, lorsque j'ai regardé les noms des fonctions qui dessinent ce graphique, j'ai constaté qu'elles affichent en fait des mesures d'utilisation du processeur et de la mémoire.)
C'était super, mais pas particulièrement utile. Après avoir attribué la valeur 1, ma ville a cessé de se charger, donc rien d'autre n'a pu être fait ici.
Mode Zuru
J'ai recommencé à chercher d'autres références aux fonctions de débogage, et plusieurs fois j'ai rencontré quelque chose appelé «mode zuru». Les branches de blocs de code avec fonctionnalité de débogage vérifiaient souvent la variable
zurumode_flag
.
zzz_LotsOfDebug
(le nom que j'ai trouvé moi-même) dans la fonction
game_move_first
montrée ci-dessus est appelé uniquement lorsque
zurumode_flag
pas égal à zéro.
À la recherche de fonctions associées à cette valeur, j'ai trouvé celles-ci:
zurumode_init
zurumode_callback
zurumode_update
zurumode_cleanup
À première vue, leur objectif est mystérieux, ils jonglent avec des bits dans les décalages d'une variable appelée
osAppNMIBuffer
.
Voici à quoi ressemble le travail de ces fonctions à première vue:
zurumode_init
- Définit
zurumode_flag
à 0 - Vérifie plusieurs bits dans
osAppNMIBuffer
- Enregistre un pointeur sur la fonction
zurumode_callback
dans la structure padmgr
- Appelle
zurumode_update
zurumode_update
- Vérifie plusieurs bits dans
osAppNMIBuffer
- En fonction de la valeur de ces bits,
zurumode_flag
met à jour - Imprime une chaîne de format sur la console du système d'exploitation.
Ceci est généralement utile pour donner du contexte au code, mais il y avait beaucoup de caractères non imprimables dans la ligne. Le seul texte reconnaissable était «zurumode_flag» et «% d».
En supposant qu'il pourrait s'agir d'un texte japonais avec un codage de caractères multi-octets, j'ai passé la chaîne via l'outil de reconnaissance de codage et j'ai découvert que la chaîne était codée avec Shift-JIS. En traduction, la ligne signifiait simplement "La valeur de zurumode_flag est passée de% d à% d". Cela ne nous donne pas beaucoup de nouvelles informations, mais maintenant nous savons que Shift-JIS est utilisé: dans les fichiers binaires et les tables de lignes, il y a beaucoup plus de lignes dans cet encodage.
zurumode_callback
- Appelle
zerumode_check_keycheck
- Vérifie plusieurs bits dans
osAppNMIBuffer
- La valeur
zurumode_flag
- Appelle
zurumode_update
zerumode_check_keycheck
jusqu'à ce que nous nous rencontrions à cause d'une orthographe différente ... qu'est-ce que c'est?
Une énorme fonction complexe qui fait beaucoup plus de travail sur les bits avec des valeurs sans nom.
À ce stade, j'ai décidé de prendre du recul et d'étudier d'autres fonctions et variables de débogage, car je n'étais pas sûr de l'importance du mode zuru. De plus, je n'ai pas compris ce que signifie «vérification des clés» ici. Est-il possible qu'il s'agisse d'une clé cryptographique?
Retour au débogage
À cette époque, j'ai remarqué un problème avec ma façon de charger les symboles de débogage dans l'IDA. Le fichier
foresta.map
sur le disque du jeu contient de nombreuses adresses et noms de fonctions et de variables. Au début, je n'ai pas vu que les adresses de chaque section recommencent à zéro, j'ai donc écrit un script simple qui ajoute une entrée de nom pour chaque ligne du fichier.
J'ai écrit de nouveaux scripts IDA pour corriger le chargement des tables de symboles pour différentes sections du programme:
.text
,
.rodata
,
.data
et
.bss
. La section
.text
contient toutes les fonctions, donc je l'ai fait pour que cette fois, lorsque je définirai le nom, le script reconnaîtra automatiquement les fonctions à chaque adresse.
Dans les sections de données, il a maintenant créé un segment pour chaque objet binaire (par exemple,
m_debug.o
, qui était censé être du code compilé pour quelque chose appelé
m_debug
), et a défini l'espace et les noms pour chaque élément de données.
Cela m'a donné beaucoup plus d'informations, mais j'ai dû définir manuellement le type de données pour chaque élément de données, car j'ai défini chaque objet de données comme un tableau d'octets simple. (Avec le recul, je comprends qu'il serait préférable de supposer que des fragments de 4 octets contenaient des entiers 32 bits, car il y en avait beaucoup et beaucoup contenaient des adresses de fonctions et de données importantes pour la construction de références croisées.)
En étudiant le nouveau segment
.bss
pour
m_debug_mode.o
, j'ai trouvé plusieurs variables de la forme
quest_draw_status
et
event_status
. C'est intéressant car je voulais que des informations utiles, pas seulement un graphique de performances, soient affichées en mode débogage. Heureusement, à partir de ces enregistrements de données, il y avait des références croisées à un énorme morceau de code vérifiant
debug_print_flg
.
À l'aide d'un débogueur dans l'émulateur Dolphin, j'ai défini un point d'arrêt à l'emplacement de la fonction où
debug_print_flg
vérifié (à
8039816C
) pour comprendre comment cette vérification fonctionne. Mais le programme n'est jamais passé à ce point d'arrêt.
Voyons pourquoi cela se produit: cette fonction est appelée par
game_debug_draw_last
. Devinez quelle valeur est vérifiée avant son appel conditionnel?
zurumode_flag
! Que diable se passe-t-il?
J'ai défini un point d'arrêt sur ce chèque (
80404E18
) et cela a fonctionné tout de suite. La valeur de
zurumode_flag
était nulle, donc en exécution normale, le programme aurait manqué l'appel à cette fonction. J'ai inséré une instruction de branche NOP à la place (remplacée par une instruction qui ne fait rien) pour vérifier ce qui se passe lorsque la fonction est appelée.
Dans le débogueur Dolphin, cela peut être fait en interrompant le jeu, en cliquant avec le bouton droit sur les instructions et en sélectionnant «Insérer nop»:
Il ne s'est rien passé. Ensuite, j'ai vérifié ce qui se passait à l'intérieur de la fonction et découvert une autre construction de branchement qui a contourné tout ce qui était intéressant à
803981a8
. J'ai également inséré NOP à la place, et la lettre "D" est apparue dans le coin supérieur droit de l'écran.
Dans cette fonction à
8039816C
(je l'ai appelée
zzz_DebugDrawPrint
), il y a encore un tas de code intéressant, mais il n'est pas appelé. Si vous regardez cette fonction sous la forme d'un graphique, vous pouvez voir qu'il existe une série d'opérateurs de branchement qui ignorent les blocs de code dans la fonction:
Après avoir inséré NOP au lieu de plusieurs autres constructions de ramification, j'ai commencé à voir diverses choses intéressantes à l'écran:
La question suivante était de savoir comment activer cette fonctionnalité de débogage sans modifier le code.
En outre, dans certaines constructions de branche,
zurumode_flag
se produit à nouveau dans cette fonction de dessin de débogage. J'ai ajouté un autre patch afin que dans
zurumode_update
drapeau
zurumode_update
zurumode_flag
toujours attribuer la valeur 2, car lorsqu'il ne se compare pas à 0, il est comparé spécifiquement à la valeur 2.
Après avoir redémarré le jeu, j'ai vu dans le coin supérieur droit de l'écran un tel message "msg. non. "
Le nombre 687 est l'identifiant d'enregistrement du dernier message affiché. Je l'ai vérifié en utilisant le programme de visualisation de table que j'ai écrit au tout début de l'analyse, mais vous pouvez également le vérifier en utilisant l'
éditeur de table de chaînes avec une interface graphique complète , que j'ai écrite pour pirater les ROM. Voici à quoi ressemble le message dans l'éditeur:
À ce stade, il est devenu clair que l'étude du mode zuru n'était plus supprimée - elle est directement liée aux fonctions de débogage du jeu.
Retour au mode Zuru à nouveau
zurumode_init
initialise plusieurs choses:
0xC(padmgr_class)
reçoit la valeur de l'adresse zurumode_callback
0x10(padmgr_class)
reçoit la valeur d'adresse de padmgr_class
elle-même0x4(zuruKeyCheck)
reçoit la valeur du dernier bit du mot chargé à partir de 0x3C(osAppNMIBuffer)
.
J'ai compris ce qu'est
padmgr
, une abréviation de «gamepad manager». Cela signifie qu'il peut y avoir une combinaison spéciale de touches (boutons) qui peuvent être saisies sur la manette de jeu pour activer le mode zuru, ou une sorte de dispositif de débogage ou une fonction de la console du développeur qui peut être utilisée pour envoyer un signal pour l'activer.
zurumode_init
n'est exécuté que lors du premier chargement du jeu (lorsque le bouton de réinitialisation est enfoncé, cela ne fonctionne pas).
Après avoir défini un point d'arrêt à l'adresse
8040efa4
, auquel la valeur
0x4(zuruKeyCheck)
est attribuée
0x4(zuruKeyCheck)
, nous pouvons voir que lors du chargement sans appuyer sur les touches, la valeur est définie sur 0. Si vous le remplacez par 1, une chose intéressante se produit:
La lettre "D" apparaît à nouveau dans le coin supérieur droit (cette fois est verte, pas jaune), et certaines informations d'assemblage sont également affichées:
[CopyDate: 02/08/01 00:16:48 ]
[Date: 02-07-31 12:52:00]
[Creator:SRD@SRD036J]
Un patch qui définit toujours
0x4(zuruKeyCheck)
à 1 au début ressemble à ceci:
8040ef9c 38c00001
Cela semble être la bonne façon d'initialiser le mode zuru. Après cela, diverses actions peuvent être nécessaires pour obtenir l'affichage de certaines informations de débogage. En commençant le jeu, en vous promenant dessus et en parlant à un villageois, nous ne verrons aucun des messages mentionnés ci-dessus (à l'exception de la lettre "D" dans le coin).
Les suspects les plus probables sont
zurumode_update
et
zurumode_callback
.
zurumode_update
zurumode_update
abord appelé dans
zurumode_init
puis constamment appelé par
zurumode_callback
.
Il vérifie à nouveau le dernier bit
0x3C(osAppNMIBuffer)
puis, en fonction de cette valeur, met à jour
zurumode_flag
.
Si le bit est nul, le drapeau est mis à zéro.
Sinon, l'instruction suivante est exécutée, la valeur complète
0x3c(osAppNMIBuffer)
étant
r5
:
extrwi r3, r5, 1, 28
Il extrait le 28e bit de
r5
et le stocke dans
r3
.
Ensuite, 1 est ajouté au résultat, c'est-à-dire que le résultat final est toujours 1 ou 2.
Ensuite,
zurumode_flag
comparé au résultat précédent, en fonction du nombre de 28e et dernier bits définis sur
0x3c(osAppNMIBuffer)
: 0, 1 ou 2.
Cette valeur est écrite dans
zurumode_flag
. Si cela ne change rien, la fonction se ferme et renvoie la valeur d'indicateur actuelle. S'il modifie la valeur, une chaîne de blocs de code beaucoup plus complexe est exécutée.
Un message s'affiche en japonais: la même «valeur zurumode_flag est passée de% d à% d», dont nous avons parlé plus haut.
Ensuite, une série de fonctions est appelée avec différents arguments, selon que le drapeau est devenu égal à zéro ou non. Le code assembleur de cette partie est monotone, je vais donc montrer son pseudocode:
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) }
Notez que si l'indicateur est nul, l'argument 0 est passé à JC_JUTDbPrint_setVisible.
Si l'indicateur n'est pas égal à zéro et que le bit 25 ou le bit 31 sont définis sur
0x3C(osAppNMIBuffer)
,
setVisible
argument 1.
Il s'agit de la première clé pour activer le mode zuru: le dernier bit
0x3C(osAppNMIBuffer)
doit être défini sur 1 pour afficher les informations de débogage et définir
zurumode_flag
valeur non nulle.
zurumode_callback
zurumode_callback
est situé au
8040ee74
et est probablement appelé par une fonction liée à la manette de jeu. Après avoir inséré un point d'arrêt dans le débogueur Dolphin, la pile d'appels nous montre qu'il est réellement appelé à partir de
padmgr_HandleRetraceMsg
.
L'une de ses premières actions a été d'exécuter
zerucheck_key_check
. Cette fonction est complexe, mais il semble qu'en général elle est conçue pour lire et mettre à jour la valeur de
zuruKeyCheck
. Avant de passer à la fonction de vérification des touches, j'ai décidé de vérifier comment cette valeur est utilisée dans le reste de la fonction de rappel.
Ensuite, il vérifie à nouveau pour certains bits dans
0x3c(osAppNMIBuffer)
. Si le bit 26 est défini, ou si le bit 25 est défini et que
padmgr_isConnectedController(1)
renvoie une valeur différente de zéro, le dernier bit de
0x3c(osAppNMIBuffer)
sur 1!
Si aucun de ces bits n'est défini, ou que le bit 25 est défini, mais que
padmgr_isConnectedController(1)
renvoie 0, la fonction vérifie si l'octet à l'adresse
0x4(zuruKeyCheck)
est égal à zéro. S'il est égal, il réinitialise le dernier bit de la valeur d'origine et le
0x3c(osAppNMIBuffer)
sur
0x3c(osAppNMIBuffer)
. Sinon, il définit toujours le dernier bit sur 1.
En pseudo code, cela ressemble à ceci:
x = osAppNMIBuffer[0x3c] if (BIT(x, 26) || (BIT(x, 25) && isConnectedController(1)) || zuruKeyCheck[4] != 0) { osAppNMIBuffer[0x3c] = x | 1
Après cela, si le bit 26 n'est pas défini, la fonction passe à l'appel de
zurumode_update
, puis se
zurumode_update
.
Si le bit est défini, alors si
0x4(zuruKeyCheck)
pas égal à zéro, alors il charge une chaîne de format dans laquelle il affiche ce qui suit: "ZURU% d /% d".
Pour résumer le sous-total
Voici ce qui se passe:
padmgr_HandleRetraceMsg
appelle
zurumode_callback
. Je suppose que ce «message de retour de poignée» signifie qu'il scanne simplement les frappes du contrôleur. À chaque analyse, il peut provoquer une série de rappels différents.
Lorsque
zurumode_callback
exécuté
zurumode_callback
il vérifie les frappes (boutons) en cours. Il semble qu'elle vérifie un bouton spécifique ou une combinaison de boutons.
Le dernier bit du tampon NMI est mis à jour en fonction des bits spécifiques de sa valeur actuelle, ainsi que de la valeur de l'un des octets
zuruKeyCheck
(
0x4(zuruKeyCheck)
).
Ensuite,
zurumode_update
est
zurumode_update
et vérifie ce bit. S'il est égal à 0, alors l'indicateur de mode zuru est mis à 0. S'il est égal à 1, alors l'indicateur de mode passe à 1 ou 2, selon que le bit 28 est activé.
Il existe trois façons d'activer le mode zuru:- Le bit 26 est défini sur
0x3C(osAppNMIBuffer)
- Le bit 25 est défini sur
0x3C(osAppNMIBuffer)
et le contrôleur est connecté au port 2 0x4(zuruKeyCheck)
pas nul
osAppNMIBuffer
Intéressé par ce
osAppNMIBuffer
signifie
osAppNMIBuffer
, j'ai commencé à chercher «NMI» et j'ai trouvé des liens dans le contexte Nintendo vers «interruption non masquable». Il s'avère que le nom de cette variable est entièrement mentionné dans la documentation développeur de Nintendo 64:
osAppNMIBuffer est un tampon de 64 octets qui est effacé lors d'un redémarrage à froid. Si le système redémarre en raison de NMI, l'état de ce tampon ne change pas.
En fait, il s'agit d'un petit morceau de mémoire enregistré lors d'un redémarrage «doux» (avec le bouton de réinitialisation). Le jeu peut utiliser ce tampon pour stocker toutes les données pendant que la console est sur le réseau. L'original Animal Crossing est sorti sur Nintendo 64, il est donc logique que quelque chose de similaire ait dû apparaître dans le code.
Si nous allons dans le fichier
boot.dol
binaire (tout ce qui est montré ci-dessus était dans
foresta.rel
), alors sa fonction
main
a beaucoup de liens vers
osAppNMIBuffer
. Un rapide coup d'œil montre qu'il existe une série de vérifications qui peuvent conduire à
0x3c(osAppNMIBuffer)
valeurs de différents bits
0x3c(osAppNMIBuffer)
aide d'opérations OR.
Les valeurs d'opérande OR suivantes peuvent être intéressantes:
- Bit 31: 0x01
- Bit 30: 0x02
- Bit 29: 0x04
- Bit 28: 0x08
- Bit 27: 0x10
- Bit 26: 0x20
Nous nous souvenons que les bits 25, 26 et 28 sont particulièrement intéressants: 25 et 26 déterminent si le mode zuru est activé et le bit 28 détermine le niveau du drapeau (1 ou 2).
Le bit 31 est également intéressant, mais il semble qu'il change en fonction des valeurs des autres.
Bit 26
Tout d'abord: à l'adresse
800062e0
il y a une instruction
ori r0, r0, 0x20
avec une valeur tampon de
0x3c
. Il active le bit 26, qui active toujours le mode zuru.
Pour que le bit soit défini, le huitième octet renvoyé par
DVDGetCurrentDiskID
doit être
0x99
. Cet identifiant est situé au tout début de l'image disque du jeu et est chargé en mémoire à
80000000
. Dans une version commerciale régulière du jeu, l'ID ressemble à ceci:
47 41 46 45 30 31 00 00 GAFE01..
En remplaçant le dernier octet de l'identifiant par un
0x99
pour
0x99
, nous obtenons l'image suivante au démarrage du jeu:
Et ce qui suit s'affiche dans la console du système d'exploitation:
06:43:404 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: ZURUMODE2 ENABLE
08:00:288 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: osAppNMIBuffer[15]=0x00000078
Tous les autres correctifs peuvent être supprimés, après quoi la lettre D apparaît à nouveau dans le coin supérieur droit de l'écran, mais aucun message de débogage n'est activé.
Bit 25
Le bit 25 est utilisé conjointement avec la vérification du port du contrôleur 2. Qu'est-ce qui le fait s'allumer?
Il s'avère qu'il doit utiliser le même contrôle que pour le bit 28: la version doit être supérieure ou égale à
0x90
. Si le bit 26 est défini (l'ID est
0x99
), ces deux bits seront également définis et le mode zuru sera toujours activé.
Cependant, si la version est comprise entre
0x90
et
0x98
, le mode zuru ne s'active pas instantanément. Rappelez la vérification effectuée dans
zurumode_callback
- le mode ne sera activé que si le bit 25 est défini
et padmgr_isConnectedController(1)
renvoie une valeur différente de zéro.
Une fois que le contrôleur est connecté au port 2 (l'argument
isConnectedController
a zéro indexation), le mode zuru est activé. La lettre D et les informations sur l'assemblage apparaissent sur l'écran initial, et nous ... pouvons contrôler l'affichage du débogage à l'aide des boutons du deuxième contrôleur!
Certains boutons effectuent des actions qui non seulement modifient l'affichage, mais aussi, par exemple, augmentent la vitesse du jeu.
zerucheck_key_check
Le dernier mystère reste
0x4(zuruKeyCheck)
. Il s'avère que cette valeur est mise à jour avec une énorme fonction complexe que j'ai montrée ci-dessus:
En utilisant le débogueur d'émulateur Dolphin, j'ai pu déterminer que la valeur vérifiée par cette fonction est un ensemble de bits correspondant aux pressions de bouton sur le deuxième contrôleur.
Le suivi des clics sur les boutons est stocké dans une valeur de 16 bits à
0x2(zuruKeyCheck)
. Lorsque le contrôleur n'est pas connecté, la valeur est
0x7638
.
2 octets contenant les drapeaux des pressions des boutons du contrôleur sont téléchargés puis mis à jour au début de
zerucheck_key_check
. La nouvelle valeur est transmise avec le registre
r4
fonction
padmgr_HandleRetraceMsg
lorsqu'elle appelle la fonction de rappel.
Vers la fin de
zerucheck_key_check
il y a un autre endroit où
0x4(zuruKeyCheck)
mis à jour
0x4(zuruKeyCheck)
.
Il n'apparaissait pas dans la liste de références croisées car il l'utilisait comme adresse de base r3
, et nous ne pouvons connaître la valeur r3
qu'en regardant la valeur qui lui est affectée avant d'appeler cette fonction.À l'adresse, la 8040ed88
valeur est r4
écrite 0x4(zuruKeyCheck)
. Juste avant cela, mais il est écrit du même endroit, puis XOR à partir de 1. La tâche de cette opération est de basculer la valeur d'octet (en fait le dernier bit) entre 0 et 1. (Si la valeur est 0, le résultat deXOR à partir de 1 sera 1 . Si la valeur est 1, le résultat sera 0. Voir la table de vérité pour XOR.)Plus tôt, lorsque j'ai étudié les valeurs en mémoire, je n'ai pas remarqué ce comportement, mais je vais essayer de casser cette instruction dans le débogueur pour comprendre ce qui se passe. La valeur d'origine est chargée à 8040ed7c
.Sans toucher aux boutons du contrôleur, je ne parviendrai pas à ce point d'arrêt sur l'écran initial. Pour entrer dans ce bloc de code, la valeur r5
doit devenir égale 0xb
avant l'instruction de branchement qui précède le point d'arrêt ( 8040ed74
). Parmi les nombreux chemins différents menant à ce bloc, un seul attribue une r5
valeur 0xb
devant lui à l'adresse 8040ed68
.Notez que pour atteindre le bloc affectant la r5
valeur 0xB
, la valeur r0
doit être égale juste avant 0x1000
. En suivant les blocs de la chaîne jusqu'au début de la fonction, nous pouvons voir toutes les restrictions nécessaires pour réaliser ce bloc:- 8040ed74: la valeur
r5
doit être égale0xB
- 8040ed60: la valeur
r0
doit être égale0x1000
- 8040ebe8: la valeur
r5
doit être égale0xA
- 8040ebe4: la valeur
r5
doit être inférieure0x5B
- 8040eba4: la valeur
r5
doit être supérieure0x7
- 8040eb94: la valeur
r6
doit être 1 - 8040eb5c: la valeur
r0
ne doit pas être 0 - 8040eb74: les valeurs du bouton du port 2 doivent changer

Ici, nous avons atteint le point où les anciennes valeurs de bouton sont chargées et les nouvelles valeurs sont enregistrées. Viennent ensuite quelques opérations appliquées aux nouvelles et anciennes valeurs: L'opération XOR marque tous les bits qui ont changé entre les deux valeurs. Ensuite, l'opération ET masque la nouvelle entrée pour définir tous les bits qui ne sont pas actuellement définis sur l'état 0. Le résultat en est un ensemble de nouveaux bits (pressions de bouton) dans la nouvelle valeur. S'il n'est pas vide, nous sommes sur la bonne voie. Pour faire une différence , le quatrième des boutons de suivi 16 bits doit changer. Après avoir inséré un point d'arrêt après l'opération XOR / AND, j'ai découvert que le bouton START déclenche cet état. La question suivante est de savoir comment le rendre initialement égalold_vals = old_vals XOR new_vals
old_vals = old_vals AND new_vals
r0
r0
0x1000
r5
0xA
. r5
et r6
sont chargés depuis 0x0(zuruKeyCheck)
le début de la fonction de test clé et mis à jour plus près de la fin, lorsque nous n'entrons pas dans le bloc de code qui comprend 0x4(zuruKeyCheck)
.Il y a plusieurs endroits avant cela où la r5
valeur est affectée 0xA
:8040ed38
8040ed34
: la valeur r0
doit être égale 0x4000
(le bouton B est enfoncé)8040ebe0
: la valeur r5
doit être égale0x5b
8040eba4
: la valeur r5
doit être supérieure0x7
- alors tout se passe comme avant ...
r5
devrait commencer par 0x5b
8040ed00
8040ecfc
: la valeur r0
doit être égale 0xC000
(A et B sont pressés)8040ebf8
: la valeur r5
doit être> = 98040ebf0
: la valeur r5
doit être inférieure à 108040ebe4
: la valeur r5
doit être inférieure0x5b
8040eba4
: r5
devrait être plus0x7
- alors tout se passe comme avant ...
r5
devrait commencer à 98040ed50
8040ed4c
: la valeur r0
doit être égale 0x8000
(bouton A enfoncé)8040ec04
: la valeur r5
doit être inférieure0x5d
8040ebe4
: la valeur r5
doit être supérieure0x5b
8040eba4
: la valeur r5
doit être supérieure0x7
- alors tout se passe comme avant ...
r5
devrait commencer par 0x5c
Il semble qu'il y ait une sorte d'état entre les frappes, après quoi vous devez entrer une certaine séquence de combos à partir des boutons, en terminant par une pression sur START. Il semble que A et / ou B devraient aller juste avant START.Si vous tracez le chemin du code qui définit la r5
valeur à 9, alors un modèle apparaît: r5
- c'est une valeur croissante qui peut soit augmenter lorsqu'une r0
valeur appropriée est trouvée, soit zéro. Les cas les plus étranges lorsque ce n'est pas une valeur dans la plage de 0x0
à0xB
, se produisent lors du traitement d'étapes avec plusieurs boutons, par exemple, lorsque A et B sont enfoncés simultanément. Une personne qui essaie d'entrer dans ce combo ne peut généralement pas appuyer sur les deux boutons exactement en même temps tout en suivant la manette de jeu, vous devez donc traiter le bouton sur lequel vous appuyez. premier.Nous continuons d'explorer différents chemins de code:r5
prend la valeur 9 lorsque vous appuyez sur DROITE à l'adresse 8040ece8
.r5
prend la valeur 8 lorsque le bouton droit C à l'adresse est enfoncé 8040eccc
.r5
prend la valeur 7 lorsque le bouton gauche C est pressé à l'adresse 8040ecb0
.r5
prend la valeur 6 lorsque GAUCHE est pressé à l'adresse 8040ec98
.r5
prend la valeur 5 (et r6 prend la valeur 1) lorsque BAS est pressé à l'adresse 8040ec7c
.r5
prend la valeur 4 lorsque le bouton supérieur C à l'adresse est enfoncé 8040ec64
.r5
prend la valeur 3 lorsque le bouton inférieur C à l'adresse est enfoncé 8040ec48
.r5
prend la valeur 2 lorsque vous appuyez sur UP à l'adresse 8040ec30
.r5
prend la valeur 1 (et r6
prend la valeur 1) lorsque Z est pressé à l'adresse 8040ec1c
.
La séquence actuelle est:Z, UP, C-DOWN, C-UP, DOWN, LEFT, C-LEFT, C-RIGHT, RIGHT, A + B, STARTAvant de vérifier Z, une autre condition est vérifiée: bien qu'un nouveau bouton doive être enfoncé Z, les drapeaux actuels doivent être égaux 0x2030
: les pare-chocs gauche et droit doivent également être enfoncés (ils ont les valeurs de 0x10
et 0x20
). De plus, UP / DOWN / LEFT / RIGHT sont des boutons D-pad, pas un stick analogique.Code de triche
Le combo complet ressemble à ceci:- Tenez les pare-chocs L + R et appuyez sur Z
- D-up
- C-DOWN
- C-up
- D-down
- D-gauche
- C-gauche
- C-DROIT
- D-DROIT
- A + B
- COMMENCER
Ça marche! Connectez le contrôleur au deuxième port et entrez le code, après quoi les informations de débogage apparaissent. Après cela, vous pouvez commencer à appuyer sur les boutons du deuxième (voire du troisième) contrôleur pour effectuer diverses actions.Ce combo fonctionnera sans patcher le numéro de version du jeu. Il peut même être utilisé dans une copie régulière du jeu au détail sans outils de triche ou mods de console. Une nouvelle saisie des combos désactive le mode zuru.Le message "ZURU% d /% d" est zurumode_callback
utilisé pour afficher l'état de cette combinaison si vous le saisissez lorsque l'ID de disque est déjà égal 0x99
(probablement pour déboguer le code de triche lui-même). Le premier nombre correspond à votre position actuelle dans la séquence correspondante r5
. Le second prend la valeur 1, lorsque certains boutons de la séquence sont maintenus, ils peuvent correspondre au moment où la r6
valeur est affectée 1.La plupart des messages n'expliquent pas ce qu'ils font à l'écran, donc pour comprendre leur fonction, vous devez trouver des fonctions qui les traitent. Par exemple, une longue ligne d'étoiles bleues et rouges en haut de l'écran sont des espaces réservés pour afficher l'état de diverses quêtes. Lorsque la quête est active, certains numéros y apparaissent, indiquant l'état de la quête.L'écran noir affiché en appuyant sur Z est une console pour afficher les messages de débogage, en particulier pour les aspects de bas niveau, tels que l'allocation de mémoire, les erreurs de tas et autres mauvaises exceptions. Par comportement, fault_callback_scroll
on peut supposer qu'il est utilisé pour afficher ces erreurs avant le redémarrage du système. Il ne lance aucune de ces erreurs, mais peut les amener à imprimer quelques caractères parasites avec plusieurs NOP. Je pense qu'à l'avenir, il sera très utile pour afficher vos propres messages de débogage:Après avoir fait tout cela, j'ai découvert que passer en mode débogage en corrigeant l'ID deversion est 0x99
déjà connu des autres: https://tcrf.net/Animal_Crossing#Debug_Mode . (Il y a aussi de bonnes notes sur le lien qui indiquent divers messages et parlent d'autres choses qui peuvent être faites avec le contrôleur dans le port 3.) Cependant, pour autant que je sache, personne n'a encore publié la combinaison de triche.C’est tout.
Il y a d'autres fonctionnalités pour les développeurs que j'aimerais explorer, comme l'écran de débogage de la carte et l'écran de sélection de l'émulateur NES, et comment les activer sans utiliser de correctifs.De plus, je publierai des articles sur la rétro-ingénierie des systèmes de dialogue, des événements et des quêtes dans le but de créer des mods.