Les pointeurs NES et Super NES

De T.R.A.F - Wiki
Aller à : navigation, rechercher

Introduction

Ce document a été créé pour aider les gens qui désirent se documenter un peu plus sur les tables de pointeurs et sur la manière d'étendre les ROMs Super NES. Le seul programme dont vous aurez besoin pour la suite est un éditeur hexadécimal. Ce document ne s'appliquant qu'à la NES et à la Super NES, il est possible que mes exemples ne fonctionnent pas avec les ROMs issues d'autres consoles de jeu. En revanche, certains principes pourront vous être très utiles.

Différents types de textes

Longueur de texte Variable

Le script d'une ROM est généralement stocké dans un groupement de plusieurs textes. Dans ce cas,les textes apparaîtront les uns après les autres et seront généralement séparé par une valeur hexadécimale, laquelle signifiera la fin du groupement. Maintenant, il faut garder à l'esprit que ces groupements (appelons-les "bloc de texte") ne sont pas nécessairement disposés dans le même ordre que les textes apparaissant dans le jeu.

En ce qui concerne les ROMs NES, le texte est en général stocké de cette manière : des blocs de texte avec une table de pointeurs dirigeant vers ces blocs.

Mais, surtout dans les ROMs Super NES, il existe d'autres façons de stocker du texte : par exemple, le texte est toujours présenté sous forme de blocs mais il inclura quelques octets entre chacun de ces blocs et ne possédera pas de table de pointeurs : il est possible en théorie de changer la longueur de chaque groupement individuel sans aucun problème ! En fait, le seul inconvénient avec le texte stocké de cette façon est que cela le rends plus dur à extraire et à manipuler. Un exemples de ROM dont le script est stocké de cette façon : The Romancing Saga (RPG sur Super NES).

Longueur de texte constante

Dans d'autre cas, certains textes peuvent être stockés dans un format de "longueur constante". C'est généralement dans ce système que sont stockés les noms des monstres, objets etc. : les menus du jeu. La saga des Final Fantasy stocke le texte dans ce format. Qu'est ce qu'une "longueur de texte constante" ? Il s'agit de bandes de texte (noms d'objets par exemple) qui ont une certaine longueur et qui ne sont pas séparés par une valeur hexadécimale laquelle définit en général la fin d'un bloc (un FF, par exemple). Ils sont collés les uns aux autres, ce qui ne rend pas leur lecture facile. Par exemple, les objets dans Final Fantasy IV sont longs de 9 caractères, et ils se suivent de cette façon :

  Tent     FenixDownPotion   DarkSwordIronArrow
  ---------=========---------=========---------
  9 octets 9 octets 9 octets 9 octets 9 octets

Comme vous pouvez le voir il n'y a pas de valeur hexadécimale spécifique qui les sépare. ce qui veut dire que vous pouvez utiliser les espaces comme vous l'entendez (lorsque les noms font moins de 9 lettres) : si vous regardez bien il y a 5 espaces à utiliser pour "Tent". Toutefois, il vous sera impossible d'utiliser plus d'un certain nombre de lettres pour chaque objet (la longueur étant définie "constante"). La seule façon de pouvoir augmenter le nombre de lettres que vous pouvez utiliser pour les "textes à longueur constante" sera d'apprendre l'assembleur...

Texte flottant

Souvent, on peut trouver des textes flottant disséminés dans la ROM, c'est-à-dire qu'ils ne se trouvent pas dans des blocs de texte tels que définis plus haut. La seule façon d'avoir davantage de place pour les traduire est de les déplacer quelque part dans la ROM où il y a de la place... pourvu que vous trouviez les pointeurs... Cependant, il existe un problème : étant donné que les textes sont éparpillés, les pointeurs ne sont pas nécessairement dans une table de pointeurs. Il devient donc bien plus difficile de les détecter. Vous trouverez plus de détails un peu plus loin dans ce document.

Les headers

Un header est une série d'octets qui se trouve au début de la ROM (d'où son nom : header = en-tête) permettant de définir certains paramètres qui lui son propres. Ce sont donc les premières lignes que l'ont voit dans l'éditeur hexadécimal. Attention ! Toutes les roms n'ont pas de header.

Les headers NES

Les 16 premiers octets d'une ROM NES représentent le header. En d'autres mots le header couvre $10 octets (16 décimal = 10 hexadécimal). Il s'agit d'un nombre important qu'il faut retenir pour éditer la table des pointeurs des ROMs NES. Nous y reviendrons plus tard.

Le header des Roms NES ressemble généralement à ceci :

  4E 45 53 1A 10 00 13 00 00 00 00 00 00 00 00 00

Vous remarquerez que les 3 premiers octets 4E 45 53 forment le mot "NES" avec une table ASCII standard.

Les headers Super NES

Le header des ROMs Super NES est bien plus grand que celui des ROMs NES. Il couvre en effet 512 octets soit $200 octets (512 décimal = 200 hexadécimal). Il s'agit également d'un nombre très important à retenir.

Le header des ROMs Super NES ressemble à ceci :

  80 00 00 00 00 00 00 00 AA BB 04 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Les informations situées à l'intérieur ne sont pas nécessairement importantes. A une époque, si votre rom n'avait pas de header, il fallait en ajouter une sous peine de ne pas voir le jeu fonctionner (il suffisait d'utiliser des outils dédiés comme SNES Tool).

Les tables de pointeurs

Tables de pointeurs NES

Pour trouver une table de pointeurs pour le texte de votre ROM, il est nécessaire de connaître où le texte se situe dans ladite ROM et quels octets codent les fins de ligne et de section. une fois que vous avez trouvé le début d'un bloc de texte, la table des pointeurs se trouve en général immédiatement avant le texte. Prenez une adresse du début d'une ligne (par exemple, prenons 012D63) et prenons les 4 derniers digits (2D63). Les tables des pointeurs stockent des paires d'octets (codage 16 bits).

Vous vous rappeler de l'header de $10 (16 décimal) octets ? Eh bien comme il n'intervient pas dans la programmation proprement dite du jeu, il faut le soustraire à votre pointeur : $2D63 - $10 = $2D53.

Ensuite, comme les pointeurs fonctionnent par paire d'octets, nous allons couper 2D53 en deux : facile, il s'agit de 2D et 53. Le processeur de la NES étant 'Little Endian', il faut inverser ces deux octets : 53 2D. Voici donc un pointeur classique NES que l'on rencontre dans la plupart des jeux de cette console.

Cependant, tout n'est pas nécessairement aussi rose. Il peut arriver que l'on ait un offset sur le premier octet : au lieu de soustraire simplement $10, on doit soustraire $8010, $5010, etc. Du coup, lorsque vous recherchez une table de pointeurs, dans notre cas, il faut trouver xx53 (xx étant une inconnue).

Voici un exemple de table de pointeurs suivi par du texte dans une ROM NES (mais on va faire comme si vous ne le saviez pas - on va partir du principe que, lors de votre recherche, vous avez découvert que le texte se situait dans les environs) :

  019D30 :           349D 3B9D 419D 449D 499D 4D9D
  019D40 : 529D 569D E5E3 F6CD 4ADA 70F8 F9FA FBF0
  019D50 : 704C FE70 4BDD CEF3 705C F64D 704C F6CF
  019D60 : CE70 F5DD CB70 4BF0 DDF1 F2DD F370

Dans ce jeu, $70 signifie la fin d'un bloc de texte (section). On en trouve justement un en $019D4A. Donc, en $19D4B, il devrait y avoir le début d'un autre bloc de texte. Si je soustrait l'offset du header et que j'inverse les deux derniers octets du résultat, j'obtiens 3B9D. Et, Ô miracle, il y a justement un pointeur qui s'écrit de cette manière en $19D36 !

Vous noterez que l'octet 9D se répète, ce qui est bien normal si l'on réfléchit bien : pour des petites sections de texte, le script va rester dans des adresses du style $019DXX, puis $19EXX, puis $19FXX, puis $1A0XX, etc. ce qui signifie que le deuxième octet de chaque pointeur va valoir 9D, puis 9E, puis 9F puis A0, etc. en s'incrémentant. C'est également de cette manière que l'on retrouve des tables de pointeurs : on trouve des octets qui se répètent à interval régulier et qui s'incrémentent.

Tables des pointeurs Super NES

La table des pointeurs des ROMs Super NES fonctionne selon le même principe excepté le fait que vous n'avez pas à soustraire le header (et pour cause : celui-là n'est pas nécessaire au fonctionnement de la ROM. A une époque, il en fallait un pour que les émulateurs puissent correctement interprêter les données de la ROM. Aujourd'hui, les ROMs 'standardisées' comme celles du GoodSNES ne possèdent plus de header). Ceci dit, le système de pointeurs est plus complexe que pour la NES (mais pas forcément plus compliqué).

Lorsqu'il n'y a pas de tables de pointeurs mais que vous trouvez des octets bizarres entre vos blocs de texte (comme c'est le cas dans Romancing Saga), triffouillez un peu ces octets pour voir ce qu'il se passe et tirez-en les conclusions qui s'imposent.

En tout état de cause, la section qui va suivre est spécialement dédiée au déplacement de texte dans une ROM Super NES.

Déplacer le script dans une ROM Super NES

Bouger du texte a l'intérieur des banks

A présent, nous allons passer aux choses sérieuses ! Nous allons parler de ces textes qui ne possèdent pas de pointeurs et que vous ne pouvez pas bouger comme vous le désireriez. Les textes "flottants" sont également concernés par cette méthode.

La plupart du temps le texte doit bouger à l'intérieur des banks. Une bank est un segment de mémoire alloué au stockage de données qui vont être interprétés par la console. Comme la ROM est plus grande que la mémoire vive de la console, on est obligé de compartimenter les données dans ces banks. Bref, il s'agit d'une partie de la ROM dans laquelle des données sont stockées.

Une bank dans une "low-rom" est de 32kB ($8000) et dans une high-rom, elle est de 64kb ($10000). Dans une low-rom les données de $000200 à $0081FF constituent la première bank, puis celles de $008200 à $0101FF constituent la deuxième, etc. : $8000 séparent chaque bank de données. Dans une high-rom la première bank est comprise entre $000200 et $0101FF, la deuxième entre $020200 et $0301FF, etc. : $10000 séparent chaque bank.

La plupart du temps, le texte "flottant" de la bank peut seulement être déplacé dans sa bank d'origine (à moins que vous ne soyez très fort en assembleur auquel cas vous pourriez transférer ce texte dans une autre bank). Pour faire cela, nous allons commencer par convertir l'adresse du début du texte en pointeur (comme vu précédemment avec la NES).

Prenons un exemple concret en allant trifouiller la ROM de Final Fantasy IV à l'adresse $09DEC6. Si le jeu possède un header, il faut soustraire la taille de ce dernier ($200 si vous vous rappelez bien). Donc $09DEC6 - $200 = $09DCC6 (c'est également la valeur que vous aurez si vous avez une ROM sans header). Le texte ne peut être bougé que dans sa bank qui se trouve comprise entre $098000 et $09FFFF (sans header et sachant quela ROM est de type low-rom).

La prochaine étape consiste à ne garder que les deux derniers octets de l'adresse : DC C6. Tout comme pour la NES, il suffit ensuite d'inverser cette paire : C6 DC. Faites donc une recherche de ces deux octets à l'intérieur de la bank et vous tomberez certainement sur la table de pointeurs !

Une remarque cependant : L'adresse de pointeur que vous recherchez dans une low-rom doit toujours être comprise entre $8000 et $FFFF pour une low-rom. Donc, en admettant que vous cherchiez le pointeur du texte situé en $0926B0 (après avoir soustrait le header éventuel), vous voyez que les deux derniers octets, 26 B0, ne sont absolument pas compris dans cet intervalle : si vous inversez, vous allez trouver un pointeur B0 26 que vous ne retrouverez pas dans votre bank (ou alors, cela ne sera pas un pointeur). Dans ce cas-là, il faut rajouter la valeur de $8000 qui correspond au début de la bank : $26B0 + $8000 = $A6B0. En inversant, vous trouverez $B0A6 et, là, Ô joie, vous allez retrouver votre pointeur ! Dans une high-rom, le pointeur peut posséder n'importe quelle valeur comprise entre 0000 et FFFF puisque la bank est de 64kB au lieu de 32kB.

Bouger un bloc de texte large dans différentes banks

Généralement de larges blocs de textes auront des pointers de 24 bits (3 octets au lieu de 2 précédemment : on voit bien que des pointeurs de 2 octets ne permettent pas de changer de bank) qui vous permettront de bouger le texte pratiquement où vous le voulez dans la ROM ! C'est là qu'étendre physiquement une ROM pour avoir de la place devient pratique (expliqué dans la section suivante) ! Bref, en tout état de cause, vous allez devoir déplacer le texte (ou des morceaux de textes) là où il y a assez de place pour le mettre.

Dans un premier temps, cherchez l'endroit où votre texte commence (logique). Rappelez-vous que vous allez devoir bouger votre table de pointeurs et votre texte séparément (cela peut également s'appliquer à du texte "flottant" mais leur pointeur se trouve en général à l'intérieur de la même bank). Les bouger revient à les mettre quelque part puis à recalculer les pointeurs excepté qu'il vaudrait mieux savoir de quelle bank il s'agit : manipuler les 3 octets d'un pointeur 24 bits ne se fait pas de la même manière que les 2 octets d'un pointeur 16 bits. La charte qui suit vous permet d'identifier la conversion entre l'adresse de votre texte dans la ROM et la façon dont la Super NES va lire cette adresse :

  ADRESSE HEXA    low-rom SNES
  -----------------------------
  000000-007FFF = 008000-00FFFF
  008000-00FFFF = 018000-01FFFF
  010000-017FFF = 028000-02FFFF
  018000-01FFFF = 038000-03FFFF
  020000-027FFF = 048000-04FFFF
  028000-02FFFF = 058000-05FFFF
  030000-037FFF = 068000-06FFFF
  038000-03FFFF = 078000-07FFFF
  040000-047FFF = 088000-08FFFF
  048000-04FFFF = 098000-09FFFF
  050000-057FFF = 0A8000-0AFFFF
  058000-05FFFF = 0B8000-0BFFFF
  060000-067FFF = 0C8000-0DFFFF
  068000-06FFFF = 0D8000-0CFFFF
  070000-077FFF = 0E8000-0EFFFF
  078000-07FFFF = 0F8000-0FFFFF
  080000-087FFF = 108000-10FFFF
  088000-08FFFF = 118000-11FFFF
  090000-097FFF = 128000-12FFFF
  098000-09FFFF = 138000-13FFFF
  0A0000-0A7FFF = 148000-14FFFF
  0A8000-0AFFFF = 158000-15FFFF
  0B0000-0B7FFF = 168000-16FFFF
  0B8000-0BFFFF = 178000-17FFFF
  0C0000-0C7FFF = 188000-18FFFF
  0C8000-0CFFFF = 198000-19FFFF
  0D0000-0D7FFF = 1A8000-1AFFFF
  0D8000-0DFFFF = 1B8000-1BFFFF
  0E0000-0E7FFF = 1C8000-1CFFFF
  0E8000-0EFFFF = 1D8000-1DFFFF
  0F0000-0F7FFF = 1E8000-1EFFFF
  0F8000-0FFFFF = 1F8000-1FFFFF
  100000-107FFF = 208000-20FFFF
  108000-10FFFF = 218000-21FFFF
  110000-117FFF = 228000-22FFFF
  118000-11FFFF = 238000-23FFFF
  120000-127FFF = 248000-24FFFF
  128000-12FFFF = 258000-25FFFF
  130000-137FFF = 268000-26FFFF
  138000-13FFFF = 278000-27FFFF
  140000-147FFF = 288000-28FFFF
  148000-14FFFF = 298000-29FFFF
  150000-157FFF = 2A8000-2AFFFF
  158000-15FFFF = 2B8000-2BFFFF
  160000-167FFF = 2C8000-2CFFFF
  168000-16FFFF = 2D8000-2DFFFF
  170000-177FFF = 2E8000-2EFFFF
  178000-17FFFF = 2F8000-2FFFFF

On voit ici ce qui a été dit plus haut : les adresses low-rom ressemblent à des valeurs comprises entre $XX8000 $XXFFFF.

Avec un exemple, vous allez tout de suite comprendre à quoi sert cette charte. Disons que vous avez un bloc de texte à l'adresse $080400 (moins l'éventuel header). Si on jette un oeil à la charte, on voit que l'adresse low-rom correspondante est $108400. Découpons cette adresse en 3 octets (ce qui donne 10 84 00) puis inversons-les : nous trouvons $008410. A présent, il vous suffit de chercher ce pointeur et souvenez-vous qu'il peut apparaître plus d'une fois dans la ROM (et que certains ne sont pas des pointeurs mais juste des octets qui possèdent la même séquence). Lorsque vous recalculerez vos pointeurs, vous devrez utiliser la même technique (fort heureusement, des logiciels font cela automatiquement, de nos jours).

Pour les high-rom puisque les banks sont dans des bloc de 64kB. Comme nous venons de le voir, leurs pointeurs n'ont pas à être compris entre $XX8000 et $XXFFFF mais entre $XX0000 et $XXFFFF, c'est-à-dire n'importe où ! Il n'y a donc pas de conversion particulière sauf qu'il faut en général ajouter $C00000 à la valeur de départ : mettons qu'on ait une adresse de $1AE180 (moins l'éventuel header) ; on a alors $1AE180 + $C00000 = $DAE180 soit $80E1DA en inversant les octets. Ca y est ! Vous avez votre pointeur !

Etendre une ROM Super NES

Si vous avez des pointeurs 24 bits (voire 32 bits) alors, comme on l'a vu, vous pourrez déplacer vos textes où bon vous semble. Et, quand on manque de place, il est très utile de pouvoir en rajouter à la fin de la ROM : c'est ce qu'on appelle "étendre une ROM". Naturellement, l'espace que vous devrez ajouter devra être un multiple de 32 ko soit $8000 pour les low-rom et 64 ko soit $10000 pour les high-rom. En général, il a été constaté que la ROM était plus stable si sa taille était multiple de 4 Mb ($80000) :

   Mb        HEXA     Taille ~
  ----------------------------
  4  Mb    $080000     0.5 Mo
  8  Mb    $100000     1.0 Mo
  12 Mb    $180000     1.5 Mo
  16 Mb    $200000     2.0 Mo
  20 Mb    $280000     2.5 Mo
  24 Mb    $300000     3.0 Mo

Bref, je suis certain que vous avez compris le fonctionnement (s'il y a un header, rajouter $200 aux valeurs hexadécimales - colonne du centre). Quel est l'outil qui permet d'étendre une ROM comme bon vous semble ? Normalement, les éditeur hexadécimaux vous le permettent. A vous de jouer !

Deux outils sont très utiles pour manipuler tout ça :

Lunar Adress qui permet de calculer automatiquement les adresses (avec support lorom/hirom, et détection du jeu)

Lunar Expand qui permet d'agrandir facilement les roms.


Etendre une ROM NES est faisable (physiquement, c'est tout-à-fait faisable) mais il est beaucoup plus ardu de déplacer des textes étant donné qu'il n'y a que des pointeurs 16 bits. Cela requiert l'utilisation de l'assembleur afin que la console puisse aller chercher les textes ainsi relocalisés dans ces nouvelles banks.