Jump to content
Sign in to follow this  
taude

Traduction Dragon Quest V

Recommended Posts

Bonjour,

j'ouvre ce post pour tenir un journal, partager mes avancées et difficultés, mes réussites, sollicité de l'aide et recueillir vos retours sur le projet que je me suis lancer de traduire dragon quest V sur snes.

Je vais commencer par faire une rétrospective des premiers jours pour revenir dans le présent par ailleur.

Jour 1 recherche d'outil mise en pratique de la technique de la recherche relative:

J'ai pris le temps de me documenter sur TRAF et d'essayer de comprendre les outils et techniques nécessaires pour traduire une ROM.

Je découvre comment patcher une ROM à l'aide d'un fichier IPS et d'un patcher, ce qui m'est utile pour convertir ma ROM de Dragon Quest V en anglais.

Je conclus ensuite qu'il me faudrait un logiciel capable de faire de la recherche relative, de créer des tables de conversion, d'ouvrir la ROM pour l'afficher en hexadécimal et de traduire les valeurs hexadécimales en texte. Le programme Olympus de Lyssal regroupe tous ces aspects. Je lance le jeu et cherche, grâce à Olympus, les textes qui composent les menus et les trouve. Victoire ! J'arrive même à modifier le texte du menu en changeant les valeurs hexadécimales ! Mais pour trouver les textes du jeu, je dois compléter ma table de conversion.

Je comprends que les hexadécimaux qui constituent la table sont en fait les adresses des sprites des lettres, et je m'équipe donc d'un logiciel qui permet de visualiser les sprites d'une ROM (Tile Molester). Après plusieurs itérations, je trouve les sprites qui sont en 1bpp (1 couche de couleurs).

Je retourne sur Olympus, crée ma table, lance le jeu, et recherche les premières lignes de dialogues dans la ROM, mais là, je ne trouve rien.

Jour 2 : Débogage

Je relance mon apprentissage avec des tutoriels et apprends que si je ne peux pas voir le texte, c'est probablement parce qu'il est compressé. Je commence à m'inquiéter car les méthodes de décompression semblent très complexes et j'ai besoin de savoir quel type de compression est utilisé. Je tente de rechercher des bouts de phrases et des syllabes grâce à ma table, je navigue dans toute la ROM et ne trouve rien de concluant. Je me dis que le patch IPS doit bien modifier les textes, ce qui devrait me permettre de mieux comprendre comment ce texte est constitué. Je trouve l'outil IPS Peek, je regarde ce qui est modifié en grande quantité et rien ne semble avoir de sens.

Je décide donc de lancer le jeu en mode débogage et d'essayer de comprendre quelle est la routine d'écriture du texte. Je finis par utiliser Bsnes-plus et j'arrive à m'arrêter, plus ou moins au moment où le texte est écrit. Mais là, je me heurte à mes limites de connaissance en ASM et je n'arrive à rien.

Jour 3-6 : Apprentissage de l'ASM SNES et ChatGPT

Je passe ces jours à essayer de trouver des points d'arrêt en remontant la stack trace qui me permettent de m'arrêter uniquement une seule fois avant que le texte ne s'affiche et juste après que les premières lettres s'affichent. Grâce à la stack trace, je trouve un endroit qui semble être une bonne entrée et finis par voir que le dialogue s'écrit en VRAM avant d'être affiché, et je pose un breakpoint au moment où cet endroit en VRAM est écrit.

Je commence alors un long et fastidieux travail qui consiste à essayer de comprendre, étape par étape, ce que fait le code. Je demande à ChatGPT l'utilité de tel opcode, de tel registre, je me renseigne sur le hardware de la SNES.

Je n'avance pas sur mon problème, mais j'ai appris beaucoup de choses ! Par exemple, ce qu'est le DMA et comment cela fonctionne. Je me fais des tableaux avec les opcodes et leurs fonctionnalités. ChatGPT m'aide réellement, car au lieu d'avoir une simple phrase d'explication pour l'opcode, je peux lui poser des questions de base et il m'aide à élargir mes recherches dans les documentations, me permettant de mieux saisir le contexte.

Jour 7 : Tournant crucial avec le dump de mémoire et l'éditeur hexadécimal

Content de mes nouvelles compétences, je réalise que je n'avance pas sur le fond du problème. Je me remets en question et décide de tenter une nouvelle approche. Je fais un dump de la WRAM avant et après les breakpoints que j'avais définis, puis je fais une comparaison avec HxD. Je finis par identifier une zone de la RAM autour de $E00 qui semble avoir changé de manière suspecte. Je relance la ROM, édite cette zone avant que l'écriture n'apparaisse et, bingo, le texte affiché change. Je pose un breakpoint sur l'écriture de cette zone et je suis sûr que ce qui aboutit à cette écriture est bien le processus de décompression. Je remonte la stack, lis le code et ne comprends rien...

Jour 8 : Passage à Mesen-S et l'algorithme de compression

Ce qui me bloque, c'est que je ne comprends pas pourquoi, à partir d'une zone de la ROM, un octet est lu avec des masques successifs de 01, puis 02, puis 04, et en fonction du résultat obtenu, il va chercher un octet dans une autre partie de la ROM, et parfois soit continue de chercher dans cette partie en changeant le masque, soit va chercher un octet dans une zone différente de la ROM pour l'afficher. Je pense avoir affaire à un algorithme de type LZ. Mais en comparant avec des tutoriels, je ne comprends toujours pas comment le texte est compressé.

Je change d'émulateur pour Mesen-S et me sens plus à l'aise avec son debugger.

Jour 9 : Compréhension de l'algorithme de décompression

Après une bonne nuit de sommeil, je comprends que l'algorithme se base sur un arbre binaire. En fonction de la valeur binaire de la première zone examinée, si c'est 1, on va à droite, si c'est 0, à gauche. En fonction de la valeur trouvée dans l'arbre, il sait s'il doit continuer à naviguer dans l'arbre ou s'il doit aller chercher la valeur. Dans l'arbre, il récupère 4 octets : les deux derniers déterminent s'il doit s'arrêter (8x 1000 ****) ou continuer (0x 0000 ****), et dans tous les cas, les deux autres octets sont des adresses, soit pour l'arbre binaire, soit pour la table des valeurs.

Jour 10-11 : Création de l'outil de compression/décompression du texte

Je me lance donc dans un code Python aider par chatGPT et crée une interface qui permet de décompresser le texte, ce que j'arrive à faire assez rapidement en dumpant les éléments nécessaires de la ROM, comme l'arbre binaire et la table des valeurs. J'arrive à traduire les valeurs trouvées en lettres en injectant des valeurs dans le jeu grâce à mes découvertes précédentes. Je finis par traduire les éléments récupérés dans le patch via IPS Peek, ce qui me permet de rajouter encore des éléments dans la table de conversion en lettres. Je découvre aussi qu'il y a des valeurs dans l'arbre qui finissent par 9x et conclus que ce sont des éléments qui contrôlent l'affichage du texte (fin de phrase, attente d'entrée du joueur, etc.). J'obtiens une bonne décompression et décide de me lancer dans la compression.

Pour ce faire, j'ai la flemme d'intégrer la logique inverse qui me demanderait de parcourir l'arbre dans les deux sens inverse et de prendre le plus court chemin vers la racine. J'ai donc l'idée de construire une table à partir des éléments de décompression : dès que je décompresse une valeur, je stocke la lettre et la valeur binaire et je me servirais de cette table pour compresser le texte.

Ceci fait, j'ai un outil qui me permet de compresser et décompresser le texte et j'arrive à injecter le texte français de toute la première scène. Mais je ne suis pas satisfait : 1) j'aimerais ajouter les accents absents à priori 2) je me dis que si je ne comprends pas exactement comment sont pointés les dialogues, je vais me retrouver avec des problèmes par la suite où le texte affiché ne sera plus le bon.

Screenshot-2024-09-02-160153.pngScreenshot-2024-09-02-160327.png

A bientôt pour la suite qui sera en direct et merci de m'avoir lue ;p

  • Upvote 1

Share this post


Link to post
Share on other sites

J12 : Décompression des sprites des textes

J'ai décidé de décompresser les sprites des textes pour ajouter un outil d'affichage de ces sprites dans mon logiciel de traduction. L'objectif initial est de pouvoir afficher l'intégralité des sprites disponibles afin de vérifier s'il y en a des non utilisés ou déjà présents dont j'aurais besoin, comme les accents. À terme, cela devrait me permettre, en implémentant la logique inverse, d'ajouter de nouveaux sprites.

J'ai essayé de comprendre l'algorithme de compression. De ce que je comprends, la valeur du caractère décompressé sert à naviguer dans une liste de valeurs qui permet d'accéder ligne par ligne au sprite, sachant que certaines lettres partagent les mêmes lignes. Comprendre l'intégralité de cet algorithme me semble être une tâche trop complexe pour mon petit cerveau, donc j'ai décidé de me concentrer sur sa réimplémention en Python. La tâche est compliquée car elle me demande de réimplémenter des logiques d'opérateurs ASM comme ROR en prenant en compte la retenue (carry). Mais en cette fin de journée, j'ai réussi à retrouver la première ligne d'un sprite de lettre à partir de sa valeur décompressée. Etape suivante: réussir a décompresser toutes les lignes des lettres qui d'ailleurs peuvent avoir une hauteur variable.

Share this post


Link to post
Share on other sites

Franchement chapeau pour tes explications, c’est vraiment intéressant. 

Bon moi je suis loin d’avoir ces compétences,  des que ces compressé dans la rom je suis coincé. Là j’essaie d’apprendre à  duper les script.

Share this post


Link to post
Share on other sites

Merci pour ton retour positif, Yoda. Je suis heureux que le récit te plaise. Il est également là pour laisser une trace pour tous ceux qui voudraient se lancer, afin d'éviter les erreurs que j'ai commises et que je vais commettre !

Jour 13 : Décompression des sprites utilisés dans les dialogues

Aujourd'hui, j'ai pu finaliser l'extraction des sprites. Cela m'a demandé pas mal de débogage de mon code Python et de la ROM SNES pour pouvoir complètement rétro-ingénierer l'algorithme. Le résultat, c'est que j'ai pu explorer l'ensemble des caractères affichables et apprendre à mieux connaître cet algorithme. J'ai également repéré des adresses vides, idéales pour créer mes propres sprites, et qui semblent avoir été pensées pour fonctionner avec des sprites non compressés ! J'ai donc pu faire des tests d'implémentation de nouvelles lettres, et cela semble fonctionner. Mais il y a un hic : d'une manière ou d'une autre, la taille, la hauteur et le positionnement vertical des lettres sont gérés quelque part au cours du processus, mais pour le moment, je suis passé à côté. Sans cette compréhension, je ne pourrais pas intégrer proprement les nouvelles lettres. Cela va donc être ma recherche pour les prochains jours.

  Screenshot-2024-09-03-223425.png

Share this post


Link to post
Share on other sites

Merci de ton intérêt arcoroce! j'espère que cette traduction aussi trouvera son publique!

 

Jour 14 : Injection des nouvelles lettres

Aujourd'hui, j'ai fait un grand pas en avant ! J'ai réussi à intégrer les nouvelles lettres de bout en bout. J'ai commencé à essayer de comprendre comment étaient définis la hauteur, la largeur et la ligne de flottaison des caractères. Pour cela, j'ai tenté de repérer quelle valeur était mise en WRAM et correspondait, et j'ai fini par trouver deux valeurs situées à peu près là où le texte est décompressé, qui variaient de manière logique entre les caractères en majuscules et en minuscules. En retraçant leur initialisation dans le débogueur, je me suis aperçu qu'elles provenaient de la liste des valeurs permettant de retrouver les pointeurs vers les sprites des lettres.

En expérimentant avec l'algorithme rétro-ingénieré et en essayant de comprendre comment étaient faites les informations dans la liste, j'ai fini par comprendre la "compression" des sprites et l'utilité des valeurs dans la liste : en fait, la liste se présente sous la forme de 10 octets, les trois premiers servent à associer l'élément de la liste à la valeur de la lettre décompressée, le suivant à la largeur de la lettre, les quatre suivants à déterminer l'adresse, puis les deux derniers permettent entre autres de calculer la hauteur du sprite. En expérimentant, je me suis aperçu que la hauteur sert aussi à définir la ligne de flottaison !

J'ai également compris que les sprites n'étaient pas vraiment compressés. En fait, ils sont codés avec des largeurs variables, ce qui rend leur visualisation impossible avec Tile Molester. J'ai donc pu créer les sprites des lettres accentuées, il me restait à permettre de compresser de telles lettres. J'ai donc cherché des places disponibles dans l'arbre binaire pour associer les nouvelles valeurs des lettres décompressées dans la table des valeurs de la ROM. Pour cela, toujours dans l'optique où j'ai la flemme de parcourir l'arbre binaire dans le sens inverse (j'en avais des vertiges rien que de penser à coder cet algorithme), j'ai brut forcé l'arbre d'Huffman en testant toutes les valeurs binaires de 0 à FFFF (en testant aussi toutes les combinaisons de nombre de zéros en début) et en regardant s'il y avait une valeur lorsque j'atteignais la table des valeurs, si non je récupérais la représentation binaire du chemin. J'ai trié ces chemins et gardé les plus courts pour encoder mes nouvelles lettres. J'ai ensuite modifié mon logiciel de compression/décompression pour prendre en compte ces nouvelles lettres et le tour était joué. J'ai également commencé à créer un patch IPS car, mine de rien, les modifications commencent à être suffisamment importantes pour être contraignantes à entrer à la main dans l'émulateur.

J'ai également fait un tour dans la ROM pour essayer de comprendre comment les dialogues étaient pointés, et j'ai trouvé ce qui semble être une jolie table de pointeurs ! Mais cela sera pour le prochain épisode.

Screenshot-2024-09-04-231713.pngScreenshot-2024-09-04-230527.png

  • Like 1
  • Upvote 1

Share this post


Link to post
Share on other sites

Jour 15 : Exploitation de la table de pointeurs / Création du logiciel de traduction

Bonjour à tous,

Aujourd'hui, j'ai finalisé l'ajout de tous les caractères manquants (é, è, à, ù, â, û, ô, ê, ç) dans l'ensemble de la chaîne de texte et testé cette mise à jour sur la première séquence du jeu.

Ensuite, je me suis lancé dans l'exploitation de la table des pointeurs que j'avais repérée hier. Cette table organise les pointeurs de la manière suivante : il s'agit de groupes de 6 octets où les 21 bits de gauche décrivent l'adresse du dialogue, et les 3 bits de droite indiquent l'offset (en nombre de bits) à partir duquel le dialogue commence dans la chaîne compressée selon la méthode de Huffman.

J'ai donc développé un programme en Python qui intègre ces fonctionnalités pour naviguer dans la table des pointeurs et afficher les dialogues en les décompressant. L'idée est que nous puissions ouvrir la ROM patchée en anglais ; si le checksum correspond à la ROM adéquate, l'interface se charge. Cette interface permet alors de naviguer de dialogue en dialogue.

L'objectif final est de pouvoir modifier les conversations et mettre à jour les pointeurs dans la table en conséquence. Pour cela, j'ai créé des fonctions pour la conversion des adresses ROM (lorom/cpu) et pour générer un patch IPS.

De plus, j'ai ajouté une fonctionnalité intéressante : la possibilité de traduire automatiquement les dialogues en cours grâce à ChatGPT. J'ai mis en place un prompt qui assure que les éléments nécessaires pour le scripting des dialogues sont préservés au mieux. J'ai également ajouté un bouton permettant de traduire l'intégralité des textes et mis en place un petit système de base de données pour sauvegarder ces traductions et les modifications manuelles. Cela me permet de générer la traduction en attendant d'intégrer le système de modification des pointeurs pour créer le patch IPS.

Les prochaines étapes consisteront donc à développer cette logique pour tester une première version du jeu avec des dialogues intégralement traduits.

À suivre !

Screenshot-2024-09-06-014133.png

  • Like 2

Share this post


Link to post
Share on other sites

Hé bé, joli projet !

Tu vas finir par faire un éditeur de texte complet pour le jeu !
Pour ce qui est de la traduction, tu trouvera peut être des personnes pour ce genre de projet ?
Cet opus est sorti sur NDS si jamais tu veux reprendre cette "base"...

Au fait, tu connais https://problemkaputt.de/fullsnes.htm

Share this post


Link to post
Share on other sites

Merci pour tes encouragements @M0nsieurL.

J'ai conscience d'être peut-être le seul client pour cette traduction ! J'ai également la version DS, mais je rêve d'ajouter ce jeu mythique à ma collection SNES et de le terminer sur cette plateforme ! Si jamais je suis débordé, je tenterai peut-être de trouver de l'aide pour la traduction sur des sites de fans de DQ, sait-on jamais ?

De plus, je vois ce projet, qui me semble complexe, comme un défi et un projet pédagogique. Si j'arrive à le terminer, je me sentirai capable de traduire n'importe quel jeu. Je pourrai alors m'attaquer à des projets qui me plaisent et qui sont attendus par la communauté, comme par exemple Shadowrun !

Pour le site oui je le connais ça fait partie des ressource que j'utilise en complément de chatgpt! Mais merci pour ton aide elle est bienvenue!

 

Jour 16/17 : gestion de l'insertion des traductions, optimisation de la compression et génération du patch IPS.

Comme évoqué précédemment, je me suis attelé à l'insertion des textes en gérant la table de pointeurs. Cette tâche consistait à mettre à jour cette table en fonction du nouveau contenu traduit et à insérer ce contenu dans la ROM. Je me suis confronté à deux difficultés principales : la première était la jonction entre les différents blocs de dialogue. Étant donné que la compression est binaire, l'adresse vers un octet n'était pas suffisante. En effet, en plus de l'adresse, il est nécessaire de prendre en compte un offset qui définit la position du premier bit de la conversation dans cet octet. Je gérais déjà le bit de début, mais pas le bit de fin, qui devenait lors de l'insertion le bit de début de la conversation suivante. Après quelques itérations, je suis parvenu à modifier la table pour prendre en compte cet offset de fin. Pour information, dans la table de pointeurs, les 4 premiers bits sont destinés à cet offset, et les 20 suivants au pointeur de la conversation.

La deuxième difficulté concernait le changement de banque lors de l'insertion. Pour donner un peu de contexte, les dialogues sont stockés de manière contiguë dans des banques différentes. Je me suis beaucoup interrogé à ce sujet : fallait-il que je remplisse la fin de la banque avec le début de la conversation, qui coupait la banque, et recopier ce même début en début de banque suivante ? Ou bien traverser les banques avec la conversation ? J'ai finalement choisi cette dernière solution après quelques tests. De plus, la traduction dépassait le nombre de banques prévu initialement. J'ai donc scanné la ROM dans l'espoir de trouver des banques vides (et il y en avait) et ajouté la logique de remplissage de ces banques si nécessaire. Néanmoins, cela ne me convenait pas, car je me suis aperçu que j'avais besoin de trois banques supplémentaires pour insérer toute la traduction. J'ai donc eu une idée : optimiser l'arbre de Huffman en fonction de la traduction du jeu. C'est-à-dire compter le nombre réel de caractères dans la traduction et choisir, pour les lettres/éléments de script les plus utilisés, les chemins les plus courts dans l'arbre afin d'obtenir une représentation binaire des éléments la plus courte possible. Là aussi, je me suis confronté à plusieurs difficultés. Mon idée première était de substituer directement les valeurs dans la table des valeurs, ce que j'ai fait avant de me rendre compte que ces valeurs étaient directement liées à la manière de récupérer les informations de sprite. Cela m'aurait donc obligé à modifier la table des sprites et leurs informations associées, ce qui me semblait trop fastidieux. Je suis donc revenu en arrière et ai décidé de conserver la valeur cible des éléments, mais de changer directement l'adresse pointée de ces éléments dans l'arbre de Huffman. Après quelques itérations, j'ai réussi à intégrer cette logique.

Enfin, au cours de mes tests, je suis tombé sur les phases de combat dont les textes n'étaient pas présents dans les dialogues que j'extrayais. Et cela est normal : ils utilisent les sprites de lettres des menus, et donc les valeurs compressées ainsi que la table des pointeurs ne sont pas les mêmes. Je me suis contenté de faire du repérage pour identifier où se situaient ces tables et ces dialogues, et j'ai décidé de remettre cela à plus tard.

En conclusion, j'ai avancé ma traduction jusqu'au manoir des Lenoir et finalisé la logique d'injection des textes ainsi que la production du patch IPS (avec la modification de l'arbre d'Huffman mes nouveaux sprites ect..). Mon prochain objectif est d'ajouter des éléments de vérification et de correction algorithmique de la traduction afin de pouvoir automatiser la traduction et de m'assurer de pouvoir livrer rapidement un patch complet sans bug.

Share this post


Link to post
Share on other sites

C'est bien normal, tu t'attaques à une série que j'affectionne particulièrement.
Une fois finalisée et si tu la rend publique, ta traduction trônera dans ma petite collection (et même si je n'ai plus le temps de jouer, je demanderai à mon petit gars de se faire ce RPG légendaire, il a 9 ans).
Si tu as le temps, tu pourrais coder un petit quelque chose pour les déterminants ? (Quand on trouve un objet), enfin c'est peut être stupéfatoire, mais toujours sympathique, plutôt qu'un "Vous trouvez : Feuille D'Yggdrasil".
"Vous trouvez une Feuille d'Yggdrasil"

Hiei- s'était attaqué à la traduction de ce jeu il me semble, il y a des années, je ne sais pas s'il lui reste des choses ou si ma mémoire me fait défaut.

Shadowrun ?? J'ai un script traduit (grossièrement tout de même) qui traîne depuis au moins 10 ans dans mes archives... Peut être plus tard !

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...