Rom Hack & Fan Game

La gestion des offsets

Enfin, je vais pouvoir mieux gérer mes offsets ! Mais au fait, c’est quoi un offset ?

Il faut déjà savoir qu’une ROM GBA est écrite en hexadécimal, un système utilisant 16 caractères :

  • Les chiffres de 0 à 9
  • Les lettre de A à F

Bref, dans votre ROM, vous avez donc deux de ces caractères qui sont groupés (qui forment donc une paire), et c’est cela qu’on appelle un octet. Et votre ROM, c’est ça, une suite d’octets. Et toutes les données de votre jeu sont écrites ainsi.

Des octets dans HxD

Voilà, c’est à ça que ressemble une rom ouverte par un éditeur hexadécimal. Ne vous inquiétez pas si vous ne comprenez rien, c’est normal.

Donc, en théorie, on pourrait tout modifier juste en touchant aux octets via un éditeur hexadécimal. Bon, en pratique, c’est assez long et fastidieux, c’est pour cela que l’on se sert de logiciels qui font ces modifications pour vous.

Mais les offsets dans tout ça ?

Alors pour faire simple, un offset, c’est une adresse (pas tout à fait vrai, mais c’est grosso modo ce qu’il faut concevoir pour comprendre la suite). Et, à cette adresse, vous allez trouver une suite d’octets, comme vous pouvez le voir sur le screenshot ci-dessus. Il y a des offsets « occupés », un ou plusieurs octets y sont modifiés, et des offsets « libres », qui contiennent pour octets des suites de FF. Attention cependant, un octet FF, ne signifiera pas systématiquement que l’octet est vierge.

On peut effectuer plusieurs types de travaux sur ces offsets, directement modifier les données écrites à cet offset, ou écrire sur des octets vierges.

On peut effectuer les modifications sur offset libre sans soucis d’effacer d’anciennes données si c’est bien fait. Mais la plupart du temps, il faudra repointer. Cela consiste à utiliser un pointeur, une « commande » qui va aller à un offset donné pour récupérer et transcrire le contenu à cet offset donné.

Prenons un exemple avec un script basique :

'----------
#org 0xoffset1
msgbox 0x8offset2 MSG_SIGN
end
#org 0xoffset2
= Texte.

Dans ce script extrêmement rudimentaire, le « 8 » devant l’offset2 sur la ligne du msgbox signifie que les octets qui vont être utilisés dans ce script sont ceux se trouvant à l’offset donné après le « 8 ».


Petite explication concernant le « 8 » (cependant, ce n'est pas très important, il n'est pas primordial de comprendre ce point pour la suite).

En réalité, lorsque vous jouez à votre jeu, il y a beaucoup plus d'offsets utilisés que ceux que vous pouvez observer dans votre éditeur hexadécimal. Pour faire simple, il y a des offsets de 0x00000000 à 0x07FFFFFF en plus de ce que vous pouvez voir dans l'éditeur hexadécimal.
Après ces offsets, il y a une suite d'offsets commençant par 0x08XXXXXX : ce sont ces offsets là que vous pouvez voir dans votre éditeur hexadécimal. Par exemple, si vous trouvez une adresse libre à l'offset 0xA48000, son « véritable » offset est 0x08A48000. C'est pourquoi, si vous voulez pointer vers cet offset dans un script, vous devez utiliser 0x8 devant l'offset choisi !


C’est valable pour toutes les modifications, comme pour les sprites, ou encore les attaques, etc…

Parfois, les logiciels ont aussi besoin de repointer pour écrire de nouvelles données, vous aurez donc à donner un offset libre contenant suffisamment d’espace libre pour accepter les modifications, et le logiciel fera le lien avec le pointeur et le reste tout seul.

Mais comment bien définir l’offset pour les modifications de la rom ?

Pour les repoints classiques, le plus souvent sur Advance-Map, mais la méthode est valable pour les autres logiciels.
Il n’y a pas de technique à proprement parler, mais il est vrai qu’un peu de méthodologie et de logique s’impose pour éviter les bugs les plus fréquents.
Le logiciel dont on va se servir est FSF (Free Space Finder). Il va repérer les suites de « FF », soit les espaces libres de votre ROM, et va fournir un offset à partir de ces données. Il suffit de lui donner la taille de l'espace nécessaire, en octets, ainsi que l’intervalle de recherche.
Regardons cela de plus près :

Free Space Finder

Donc, nous avons en violet l’offset à partir duquel la recherche va avoir lieu. Personnellement, je vous conseille fortement de prendre un offset libre quelques octets après les offsets occupés par les données de bases du jeu, plutôt que commencer la recherche au début de la ROM. Ce la évite les erreurs irréversibles. Par exemple, pour rouge feu, a partir de l’offset 800000 il n’y a que des « FF », donc pas de risque d’endommager les données de base du jeu en touchant aux offsets à partir de cette valeur. Pour connaître votre offset de départ, ouvrez votre ROM avec un éditeur hexadécimal comme HxD, et regardez à partir de quel offset tous les octets sont vierges (FF).
En bleu, les options basique de recherches : Rechercher, suivante (cf le paragraphe qui suit sur l’intervalle de recherche), et copier pour copier l’offset que vous donne le logiciel.
On a aussi un petit convertisseur hexadécimal/décimal en bas à droite.
Nous avons, en rouge, le nombre d’octets libres que vous souhaitez, dans 99% des cas, c’est le logiciel qui vous donne l’espace nécessaire.

Recherche d’offsets classique

Recherche d'un offset classique

Dans ce cas, il y a 1044 octets nécessaires, vous pouvez cependant chercher un offset plus grand, car de toute façon, seulement les 1044 premiers octets seront modifiés, les autres resteront des FF.
Alors, pour Advance-Map, vous pouvez cocher « spécification », et mettre dans offset l’offset obtenu grâce à FSF. La plupart des logiciels fonctionnent ainsi pour la gestion d’offset.

Vous pouvez, en plus, sur Advance-Map, effectuer une « double vérification », il suffit de cocher « spécifier zone de recherche », et de mettre en offset de départ, celui fourni par FSF, et en offset de fin $FFFFFF (qui est tout simplement le dernier de la rom). Vous faites « OK », et vous sélectionnez un des offsets proposés.
Encadré en vert, c’est l’intervalle de recherche. Il s’agit du nombre d’octets que FSF va laisser vierge avant de rechercher un nouvel offset lorsque vous faites « suivant », donc en gros, ça laisse un nombre défini de FF vide avant de chercher un nouvel offset. Bien gérer cela permet d’éviter de nombreux bugs dus au chevauchement des offsets (modification d’octets qui empiètent sur d’autres octets modifiés). C’est surtout utilisé pour les scripts, vu qu’il faut un certain nombre d’offset de libre avant de compiler le tout en 1 fois, on ne peut donc pas effectuer une recherche « classique ».

Recherches d’offsets pour les scripts

Je vous donne donc une méthode qui, selon moi, est la plus sûre, même si elle est un peu longue. On va utiliser :

  • Advance-Map
  • Free Space Finder
  • XSE

Je vais prendre un script assez simple, un script yes/no (cf tutos de script), mais avec plusieurs pointeurs pour bien illustrer mes propos :

'----------
#org 0xoffset1
lock
faceplayer
msgbox 0x8offset2 MSG_YESNO
compare LASTRESULT 0x1
if 0x1 goto 0x8offset3
release
end
#org 0xoffset3
msgbox 0x8offset4 MSG_NORMAL
release
end

#org 0xoffset2
= Oui ou non?
#org 0xoffset4
= Ok.

Ok, donc on doit trouver un offset principal pour notre script (celui que l’on verra sur A-map), deux offsets pour les textes, et un offset secondaire pour la 2eme partie du script.

La première question qui vient à l’esprit, c’est « mais combien d’octets libres doit faire l’offset ? ». Alors, si vous avez du temps à perdre, vous pouvez toujours calculer grâce à XSE. En faisant F1, apparaît la liste des commandes ainsi que les octets nécessaires. Si vous êtes comme moi et que vous n'avez pas de temps à perdre, vous prenez des offsets très grands pour être sûr d’avoir suffisamment d’octets à disposition (entre 1000 et 10000 pour un script). De toute façon, comme dit précédemment, les octets non nécessaires ne seront pas utilisés, pourquoi s’embêter ?

Bon, voyons voir ce que ça donne en pratique :

Recherche de l'offset d'une zone vierge assez grande pour accueillir le script principal

Voilà, on a notre offset principal. On le met sur A-map ou XSE, et on ouvre/décompile, on a ça :

Décompilation sur XSE

On écrit notre script, et là, il nous faut un offset pour le texte (offset2). Le problème, c’est que comme on n’a pas compilé notre script en entier, si vous faites recherche, ben on retombe sur notre offset principal. C’est là qu‘intervient l’intervalle de recherche, ici j’en ai pris un de 8000, ainsi, FSF ira chercher un offset 8000 octets plus loin, laissant cet espace a l’offset principal. On fait suivant, et on a notre second offset :

Recherche d'un offset avec un écart suffisant pour mettre le deuxième script

Alors ensuite, pour éviter tout problème de chevauchement d’offset, on peut faire la manip suivante : allez dans XSE avec notre script ouvert, et on ouvre le visualiseur hexadécimal (ctrl+h), puis on va a l’offset obtenu via FSF, et on va quelques « FF » plus loin, ce qui permet de laisser une marge de sécurité. Sur une ROM peu modifiée, c’est peu utile, mais si vous avez beaucoup travaillé sur votre jeu, vous allez chercher des octets libres entourés d’octets modifiés, et ça devient très facile de les faire se chevaucher et d’obtenir des bugs.

Vérification pour éviter un chevauchement de données

FSF nous avait donné 80FE59, pas de soucis, il est libre. Mais regardez, l’offset est tangent a un autre offset occupé. Ce qui augmente les probabilités de bugs, donc on prend l’offset en dessous : 80FE65, comme ça, on a bien notre marge de sécurité.

NB : Cette méthode est applicable à toutes les recherches d’offsets, pas seulement pour les offsets secondaires d’un script, cela fonctionne pour tout. L’avantage du visualiseur hexadécimal de XSE est que vous pouvez visualiser les offsets et les octets tout en travaillant sur la ROM. Cependant, pour des modifications en touchant directement à l’hexadécimal, utilisez HxD.

Bon, on applique la méthode pour tous les autres offsets et on se retrouve avec ça :

Script en entier sur XSE

On compile, on décompile, et on vérifie que tout est bon :

Vérification de la réussite de la compilation

Tout est ok, on n’a pas de bug et le script est fonctionnel.

J’ai un bug, que faire ?!

Abordons une partie sensible, les bugs. Au vu de la complexité et de la fragilité d’une ROM, une mauvaise manip au niveau des offsets peut causer de gros dommages à votre jeu, parfois irréversibles, parfois réparables. Regardons ça de plus prêt.

Quelques exemples de bugs

On va commencer par de l'observation ! Je vais vous montrer quelques exemples de bugs que l'on peut obtenir si on gère mal les offsets.

1. Un texte déformé

Voici un script basique :

'---------------
#org 0x800384
msgbox 0x8800385 MSG_FACE '"Bonjour !"
end


'---------
' Strings
'---------
#org 0x800385
= Bonjour !

Ok, il semble fonctionner correctement. Cependant, si on compile et qu'on décompile, voyons ce qu'on obtient...

'---------------
#org 0x800384
msgbox 0x8800385 MSG_YESNO '" <Â\h80ËÌÈÁÒ\h80À ÉÀFÊ\h71ËÁ"
end


'---------
' Strings
'---------
#org 0x800385
= <Â\h80ËÌÈÁÒ\h80À ÉÀFÊ\h71ËÁ

En réalité, vous l'aurez remarqué, les deux offsets utilisés (l'offset de base, principal, et l'offset du message) n'ont qu'un octet de différence. En fait, dans ce cas présent, c'est un peu comme si vous disiez à la commande msgbox de s'afficher elle-même. Voici le résultat !

Texte déformé dans le jeu

2. Le script entier bousillé

Si on a vu qu'un texte pouvait être transformé en n'importe quoi, un script entier peut faire la même chose. Un exemple rapide : imaginons que je fasse un script avec pas mal de commandes sur un offset avec très peu d'espace libre. À la décompilation du script, voici le résultat :

'---------------
#org 0x800384
loadpointer 0xBC 0xE3DEE2E3

On obtient n'importe quoi, et si on essaie de faire marcher ce script InGame, le jeu freeze. On a, dans le même genre, l'apparition de nombreuses commandes nop dans les scripts : c'est dû au même problème, un espace insuffisant à l'offset indiqué pour le script désiré.

3. Un sprite explosé

Bien, maintenant que vous avez vu qu'un script pouvait n'en faire qu'à sa tête, je vais vous montrer un dernier exemple, qui touche un tout autre domaine : les images, plus précisément les sprites. Prenons ce script :

'---------------
#org 0x800974
showpokepic 0x1 0x1 0x1
end

Ce script affiche un sprite de Bulbizarre à l'écran. Cependant, si je compile un script ou que j'écris une donnée quelconque à l'offset de l'image de Bulbizarre, que va-t-il se passer ? C'est simple, le jeu ne se pose pas de questions, et va charger les données à l'offset prévu pour l'image de Bulbizarre, même si ce n'est pas ce qu'on attend.
Dans le cas présent, après avoir touché à l'offset de l'image de Bulbizarre, voici ce que j'obtiens :

Sprite explosé

Un joli sprite qui nous aiderait à ne pas choisir Bulbizarre comme starter !

Et ensuite, que faire contre ces bugs ?

Vous avez vu que de nombreux bugs peuvent survenir si on ne fait pas attention à comment on manipule nos offsets. Cependant, il arrive des fois que, même en ayant beaucoup d'expérience, en faisant très attention et en prenant de nombreuses précautions, on détruit des données stockées précédemment. Je parle en connaissance de cause, j'ai déjà détruit des dizaines de ROM comme ça !

Enfin bref, il y a deux sortes de bugs provoqués par cette mauvaise gestion des offsets. Le premier type, ils sont de l'ordre des trois bugs présentés précédemment : ils ne sont absolument pas graves ni dangereux pour votre projet. En effet, il est très simple de retaper un texte, ou, au pire, de refaire son script en entier. Pour ce qui est du sprite, il est très simple avec Advance Sprite Editor ou NSE de réinsérer un sprite « clean ».

En revanche, certaine fois, vous provoquerez de graves bugs, très mauvais pour votre ROM. Pour mon top personnel, j'en ai fait des belles : faire disparaître le sprite du héros, faire un tremblement de terre continu, reset incessant du jeu après l'écran de démarre, ou encore des choses beaucoup plus surprenantes comme je vous laisse les découvrir :

Bug graphiqueBug graphiqueBug graphique

Des bugs comme ceux-ci sont beaucoup plus embêtants, puisqu'on ne sait pas d'où ils viennent, et il est alors très difficile de restituer les données comme elles l'étaient aux offsets abîmés.

Dans ces cas là, il n'y a pas vraiment de solution miracle : il faut faire des back-ups régulièrement. Si on tel bug survient, utilisez une ancienne back-up afin de pouvoir continuer sans garder le bug provoqué.

Rapide conclusion

Bon, maintenant, vous devriez commencer à cerner le sujet et à savoir ce qu'est un offset, ainsi que son fonctionnement.

Pour récapituler :

Un offset est une adresse dans votre ROM. À cette adresse, sont stockées des informations telles que des messages, des images, des scripts, ou tout autre données du jeu. Le jeu, lorsqu'il a besoin de ces données, va les chercher à l'offset qu'on lui indique, comme dans les scripts, par exemple.

Chaque donnée stockée a une taille qui lui est propre. Ainsi, si votre message fait 100 octets de longueur, le script va charger 100 octets à partir de l'offset indiqué. Vous aurez alors besoin d'un espace libre minimum de 100 octets à un offset, offset que vous pouvez trouver en utilisant un logiciel comme FSF ou bien en regardant directement dans un éditeur hexadécimal.

Si vous enregistrez des données sans leur donner l'espace dont elles ont besoin, alors vous provoquerez différents bugs, de grandeur et de gravité variables. Les plus bénins seront simples à corriger, mais certains seront beaucoup plus coriaces et embêtants : on ne le redira jamais assez, il vous faut faire des back-ups régulièrement afin d'éviter ce genre de problème.

Enfin, voici un petit schéma récapitulatif de ce tutoriel :

J'ai tout compris !

Gardez bien en tête ce schéma : si vous l'avez compris, vous avez tout compris !

Merci de nous avoir lu, nous espérons que ce tutoriel vous aura été utile et que vous ferez moins d'erreurs ou que vous rencontrerez moins de problèmes concernant les offsets !

Merci de ne pas copier ce tutoriel, même partiellement, ou encore de ne prendre aucune image ou ressource sans nous en demander la permission.

Si vous avez des questions, dirigez-vous sur le topic dédié du forum.

Par Myst et Mickey'

Par Loris