La section 9 est divisée en trois :
Position | Taille | Description | |
---|---|---|---|
Header | |||
0 | 2 octets | 0 | |
2 | 2 octets | 1 (utilise des palettes) ou 2 (n'utilise pas de palettes) | |
4 | 1 octet | 1 (activé, si 0 le jeu ne lit pas la suite) | |
5 | 7 octets | PALETTE | |
12 | 20 octets | Activité des premiers pixels des palettes -actPixPal- | |
32 | 4 octets | 0 | |
36 | 4 octets | BACK |
Zone Sprites 1 | |||
---|---|---|---|
40 | 2 octets | Pseudo-Largeur | |
42 | 2 octets | Pseudo-Hauteur | |
44 | 2 octets | Nombre de sprites Infos 1 | |
46 | 2 octets | Si =1 : utilise des palettes, Si =2 : utilise les couleurs directement | |
48 | 2 octets | 0 | |
50 | nbSprites1 * 52 | Sprites Infos 1 | |
50 + NbSprite1 * 52 | 2 octets | 0 |
52 + NbSprite1 * 52 | 1 octet | Si =0 La zone 2 n'existe pas. | |
---|---|---|---|
Zone Sprites 2 | |||
53 + NbSprite1 * 52 | 2 octets | Pseudo-Largeur | 640 |
55 + NbSprite1 * 52 | 2 octets | Pseudo-Hauteur | 480 |
57 + NbSprite1 * 52 | 2 octets | Nombre de sprites Infos 2 | |
59 + NbSprite1 * 52 | 16 octets | ??? (en savoir plus...) | |
75 + NbSprite1 * 52 | 2 octets | 0 | |
77 + NbSprite1 * 52 | nbSprites2 * 52 | Sprites Infos 2 | |
77 + 52 (NbSprite1 + NbSprite2) | 2 octets | 0 |
79 + 52 (NbSprite1 + NbSprite2) | 1 octet | Si =0 La zone 3 n'existe pas. | |
---|---|---|---|
Zone Sprites 3 | |||
80 + 52 (NbSprite1 + NbSprite2) | 2 octets | Pseudo-Largeur | 640 |
82 + 52 (NbSprite1 + NbSprite2) | 2 octets | Pseudo-Hauteur | 480 |
84 + 52 (NbSprite1 + NbSprite2) | 2 octets | Nombre de sprites Infos 3 | |
86 + 52 (NbSprite1 + NbSprite2) | 12 octets | 0 | |
98 + 52 (NbSprite1 + NbSprite2) | nbSprites3 * 52 | Sprites Infos 3 | |
98 + 52 (NbSprite1 + NbSprite2 + NbSprite3) | 2 octets | 0 |
100 + 52 (NbSprite1 + NbSprite2 + NbSprite3) | 1 octet | Si =0 La zone 4 n'existe pas. | |
---|---|---|---|
Zone Sprites 4 | |||
101 + 52 (NbSprite1 + NbSprite2 + NbSprite3) | 2 octets | Pseudo-Largeur | 640 |
103 + 52 (NbSprite1 + NbSprite2 + NbSprite3) | 2 octets | Pseudo-Hauteur | 480 |
105 + 52 (NbSprite1 + NbSprite2 + NbSprite3) | 2 octets | Nombre de sprites Infos 4 | |
107 + 52 (NbSprite1 + NbSprite2 + NbSprite3) | 2 octets | 0 | |
109 + 52 (NbSprite1 + NbSprite2 + NbSprite3) | 8 octets | ??? (en savoir plus...) | |
117 + 52 (NbSprite1 + NbSprite2 + NbSprite3) | 2 octets | 0 | |
119 + 52 (NbSprite1 + NbSprite2 + NbSprite3) | nbSprites4 * 52 | Sprites Infos 4 | |
119 + 52 (NbSprite1 + NbSprite2 + NbSprite3 + NbSprite4) | 2 octets | 0 |
Données | |||
---|---|---|---|
121 + 52 (NbSprite1 + NbSprite2 + NbSprite3 + NbSprite4) | 7 octets | TEXTURE | |
128 + 52 (NbSprite1 + NbSprite2 + NbSprite3 + NbSprite4) | nb de pages * 65 540 + 84 | Données de l'image | |
212 + 52 (NbSprite1 + NbSprite2 + NbSprite3 + NbSprite4) + nb de pages * 65 540 | 3 octets | END |
Dans chaque zone, Il y a nbSprites sprites Infos de 52 octets. Un Sprite Infos fait donc 52 octets, il commence et finit toujours par 2 octets nuls. Un sprite info donne tout ce qu'il faut pour construire un sprite de 16*16 pixels (ou 32*32 pixels) et pour le placer dans l'image finale.
Voila comment est fait un sprite Infos (en fonction de la zone) :
0 | 2 octets | 0 | |
---|---|---|---|
2 | 2 octets | Position cible X | |
4 | 2 octets | Position cible Y | |
6 | 2 octets | Inutilisé | |
8 | 2 octets | Inutilisé | |
10 | 2 octets | Position source X | |
12 | 2 octets | Position source Y | |
14 | 2 octets | Inutilisé | |
16 | 2 octets | Inutilisé | |
18 | 2 octets | Inutilisé (largeur tile : 16 ou 0) | |
20 | 2 octets | Inutilisé (hauteur tile : 16 ou 0) | |
22 | 2 octets | Numéro palette | Commence à la palette n°0 |
24 | 2 octets | Id | 4095 |
26 | 1 octet | Inutilisé | |
27 | 1 octet | Inutilisé | |
28 | 1 octet | 0 | |
29 | 1 octet | 0 | |
30 | 2 octets | 0 | |
32 | 2 octets | Page | |
34 | 2 octets | Inutilisé | |
36 | 2 octets | Octets par couleur | 0 (peu de couleurs), 1 (palettes) ou 2 (sans palettes) |
38 | 4 octets | Inutilisé | |
42 | 4 octets | Coordonnée X texture | |
46 | 4 octets | Coordonnée Y texture | |
50 | 2 octets | 0 |
0 | 2 octets | 0 | |
---|---|---|---|
2 | 2 octets | Position cible X | |
4 | 2 octets | Position cible Y | |
6 | 2 octets | Inutilisé | |
8 | 2 octets | Inutilisé | |
10 | 2 octets | Position source X | |
12 | 2 octets | Position source Y | |
14 | 2 octets | Position source X (2) (pour les effectPages) | |
16 | 2 octets | Position source Y (2) (pour les effectPages) | |
18 | 2 octets | Largeur bloc | 16 |
20 | 2 octets | Hauteur bloc | 16 |
22 | 2 octets | Numéro palette | Commence à la palette n°0 |
24 | 2 octets | Id | |
26 | 1 octet | Paramètre | |
27 | 1 octet | État | |
28 | 1 octet | Transparence par addition | Booléen |
29 | 1 octet | Transparence par addition ? | Booléen |
30 | 2 octets | Transparence par addition ? | 0, 1, 2 ou 3 |
32 | 2 octets | Page | |
34 | 2 octets | EffectPage | |
36 | 2 octets | Octets par couleur | 0 (peu de couleurs), 1 (palettes) ou 2 (sans palettes) |
38 | 4 octets | Coordonnée Z | |
42 | 4 octets | Coordonnée X texture | |
46 | 4 octets | Coordonnée Y texture | |
50 | 2 octets | 0 |
0 | 2 octets | 0 | |
---|---|---|---|
2 | 2 octets | Position cible X | |
4 | 2 octets | Position cible Y | |
6 | 2 octets | 0 | |
8 | 2 octets | 0 | |
10 | 2 octets | Position source X | |
12 | 2 octets | Position source Y | |
14 | 2 octets | Position source X (2) (pour les effectPages) | |
16 | 2 octets | Position source Y (2) (pour les effectPages) | |
18 | 2 octets | Largeur bloc | 32 |
20 | 2 octets | Hauteur bloc | 32 |
22 | 2 octets | Numéro palette | Commence à la palette n°0 |
24 | 2 octets | Id | 4096 |
26 | 1 octet | Paramètre | |
27 | 1 octet | État | |
28 | 1 octet | Transparence par addition | Booléen |
29 | 1 octet | Transparence par addition ? | Booléen |
30 | 2 octets | Transparence par addition ? | 0 ou 1 |
32 | 2 octets | Page | |
34 | 2 octets | effectPage | |
36 | 2 octets | Type de palette | 0 (peu de couleurs) ou 1 (palette normale) ou 2 (sans palettes) |
38 | 4 octets | 0 | |
42 | 4 octets | Coordonnée X texture | |
46 | 4 octets | Coordonnée Y texture | |
50 | 2 octets | 0 |
0 | 2 octets | 0 | |
---|---|---|---|
2 | 2 octets | Position cible X | |
4 | 2 octets | Position cible Y | |
6 | 2 octets | 0 | |
8 | 2 octets | 0 | |
10 | 2 octets | Position source X | |
12 | 2 octets | Position source Y | |
14 | 2 octets | Position source X (2) (pour les effectPages) | |
16 | 2 octets | Position source Y (2) (pour les effectPages) | |
18 | 2 octets | Largeur bloc | 32 |
20 | 2 octets | Hauteur bloc | 32 |
22 | 2 octets | Numéro palette | Commence à la palette n°0 |
24 | 2 octets | Id | 0 |
26 | 1 octet | Paramètre | |
27 | 1 octet | État | |
28 | 1 octet | Transparence par addition | Booléen |
29 | 1 octet | Transparence par addition ? | Booléen |
30 | 2 octets | Transparence par addition ? | 0, 1 ou 3 |
32 | 2 octets | Page | |
34 | 2 octets | effectPage | |
36 | 2 octets | Type de palette | 0 (peu de couleurs) ou 1 (palette normale) |
38 | 4 octets | Coordonnée Z | 0 ou 999 |
42 | 4 octets | Coordonnée X texture | |
46 | 4 octets | Coordonnée Y texture | |
50 | 2 octets | 0 |
Dans la deuxième et troisième zone, il y a les deux octets que j'appelle “paramètre” et “états”. Ceux-ci permettent de donner un numéro à un petit bout de l'image qui sera sous plusieurs états différents.
Sur Final Fantasy VII, le plus grand numéro de paramètre observé est 51, on peut aller en théorie jusqu'au numéro 256 (soit 255 paramètres).
Les états sont rangés dans un octet où chaque bit correspond à un état (il y a donc 8 états). Si un bit est à 1, l'état correspondant existe. Par exemple si l'octet des états est à 00000010, l'état 2 est activé.
Elles sont toujours précédées par le mot-clé “TEXTURE”.
Les données de l'image sont séparées en 42 pages. Si le premier mot de la page est différent de zéro, c'est qu'il y a des données dans la page, dans le cas contraire la page ne fait que deux octets de longueur.
Position | Taille | Description | |
---|---|---|---|
0 | 2 octets | Si =0, il n'y a pas de page. | |
Header | |||
2 | 2 octets | 0 (sprites 16*16) ou 1 (sprites 32*32) | |
4 | 2 octets | coulPix | Si =1 : un octet = un pixel (utilisation d'une palette) ; Si =2 : deux octets = un pixel |
Données de la page | |||
6 | 65536 * coulPix | Données de la page |
Si vous voulez afficher directement ces pages, vous devez afficher des images de 256*256 pixels (utilisez les palettes). Comme dans l'illustration, vous verrez des lignes de 16 pixels de hauteur.
Je vous conseille vivement de collecter tous les points de départ des pages dans un tableau bien rangé, par exemple :
[0] => 0 [1] => 65542 [15] => 131110
Par la suite je vais appeler ce tableau “$pages”.
Pour correctement construire l'image, nous avons besoins des véritables dimensions, et de la liste des premiers états de chaque paramètre.
Pour cela nous allons parcourir toutes les zones Sprites Infos, et à chaque Sprite Info récupérer les position cibles. Je vous laisse comprendre mon code (qui est en PHP…).
$largeurMax=0;$hauteurMax=0; $largeurMin=0;$hauteurMin=0; for($zone = 0 ; $zone < $nbZones ; $zone++) { for($numSpriteInfo = 0 ; $numSpriteInfo < $nbSpritesInfo[$zone] ; $numSpriteInfo++){ $spriteInfo = substr($spriteData[$zone], 52*$numSpriteInfo, 52*$numSpriteInfo+52); $page = bin2dec_2($Block,32); if(($page2 = bin2dec_2($Block,34)) != 0) $page = $page2; if(empty($pages[$page])) continue;//Si référence à une page inexistante (fix trnad_3) $X = bin2dec_2($spriteInfo, 2); $Y = bin2dec_2($spriteInfo, 4); //Unsigned int 2 signed int, ça c'est si vous ne pouvez pas déclarer une variable signed int ^^ if($X > 32768) $X-=65536; if($Y > 32768) $Y-=65536; if($X==10000 || $X==-3184) continue;//fix if($X > $largeurMax && $X > 0) $largeurMax = $X; elseif($X < 0 && -$X > $largeurMin) $largeurMin = -$X; if($Y > $hauteurMax && $Y > 0) $hauteurMax = $Y; elseif($Y < 0 && -$Y > $hauteurMin) $hauteurMin = -$Y; //Listing des paramètres $param = bin2dec_2($spriteInfo, 26); if($param && ($paramList[$param % 256] > $param || !$paramList[$param % 256])) $paramList[$param % 256] = $param; } } $largeur = $largeurMax + $largeurMin + 16; $hauteur = $hauteurMax + $hauteurMin + 16;
Pour construire une image, on utilise les Infos sprites. Dans la zone 1, un sprite est une image de 16*16 pixels. On construit cette image grâce aux Infos sprites (positions et autres infos), aux données de l'image (pour créer les pixels) et aux palettes (pour les couleurs).
Regardez plus haut pour savoir où trouver la zone 1 des Infos sprites, et comment elle est faite. C'est la plus simple des zones. Elle est scindée en nbSpritesInfos parties de 52 octets chacune. Ces 52 octets vous donnent les infos pour pouvoir construire le sprite.
$SrcX = bin2dec_2($spriteInfo, 10); $SrcY = bin2dec_2($spriteInfo, 12); $page = bin2dec_2($spriteInfo, 32); $uneCoul = bin2dec_2($imageData, $pages[$Page]+4); //les deux derniers octets du header de la page (qui disent si 1 octet = 1 pixel ou si 2 octets = 1 pixel) $pos = $pages[$page] + 6 + ($srcY * 256 + $srcX) * $uneCoul; //Position de départ du sprite dans les données de l'image
Ce code nous donne le point de départ du sprite dans la partie données de l'image. Elle utilise les variables extraites des infos du sprite.
Explications :
Il vous faut extraire à partir de StartOffset un carré de 16*16 pixels. Mais remontez un peu et regardez l'illustration. Prendre 16*16 pixels, c'est pas si simple. Au début on prend l'octet StartOffset, puis l'octet StartOffset + 1, etc, jusqu'à l'octet StartOffset + 15. On a une ligne ! Mais on est bien embêté pour passer à la ligne suivante ! Il faut aller à StartOffset + 256, donc en gros faire un bond de 240 octets… jusqu'à ce qu'on ait finalement 16*16 octets, pour construire le sprite.
$Palette = substr($PaletteData, 512*$Pal, 512); //Partie données de la section palette
Pour chaque octets du sprite de 16*16 pixels, il faut aller chercher la couleur dans la palette (le numéro de palette est donné dans les données du sprites). Un exemple : vous avez l'octet 0x5d, alors il faut aller chercher la 0x5d-ième couleur de la palette. Comme il y a deux octets par couleur dans une palette, la position dans la palette est 0x5d*2. Et pour savoir quelle palette utiliser, prenez l'info dans les sprites data (ça va de 0 à nbPalette - 1). J'ai développé dans l'article palette la manière de traduire les deux octets de la couleur en une couleur RVB.
Les infos du sprite vous donnent des positions cibles. Mais l'origine se trouve bien souvent (pas tout le temps) au milieu de l'écran, on se retrouve donc avec des coordonnées parfois négatives. Voila comment retrouver des coordonnées positives (xCible et yCible sont deux signed int extraits du Sprite infos en cours, largeurMin et hauteurMin sont les plus grandes valeurs cible en dessous de 0, en valeur absolue) :
xCible = xCible + largeurMin; yCible = yCible + hauteurMin;
Voila, vous pouvez tracer le petit carré.
Dans la zone 2, on utilise le même procédé que dans la zone 1, sauf pour les infos sprites qui ont un effectPage supérieur à 0. En tout cas en traçant la zone 1 et la zone 2 sauf les effectPage, vous aurez déjà un beau background. Allez faire un tour en bas de l'article pour prendre conscience de la séparation page/effectPage de la zone 2.
Vous devez utiliser les positions source X (2) et Y (2) :
$srcX2 = bin2dec_2($spriteInfo, 14); $srcY2 = bin2dec_2($spriteInfo, 16); $id = bin2dec_2($spriteInfo, 24); $effectPage = bin2dec_2($spriteInfo, 34); if($effectPage && $id!=4095) { //Quand id==4095, cela signifie qu'on est dans la zone 1, pour éviter les problème j'évite d'utiliser les effectPage si je suis dans la zone 1 $srcX = $srcX2; $srcY = $srcY2; $page = $effectPage; }
Dans le cas d'un effectPage, la plupart des couleurs sont indiquées comme transparentes. En fait quand on trace un pixel transparent, il ne faut pas utiliser le canal alpha, mais regarder quelles sont les couleurs du pixel qui était déjà tracé là où on veut tracer notre pixel transparent, et additionner ces couleurs avec les couleurs du pixel transparent pour obtenir les véritables couleurs !
Exemple : vous voulez tracer un pixel transparent, et là où vous voulez le tracer il y a déjà un pixel de couleur r=56 v=64 b=28, et votre pixel est de couleur r=8 v=16 b=8 ; la nouvelle couleur sera donc r=56+8 v=64+16 b=28+8.
J'ai réussi à trouver les bonnes conditions pour n'afficher que le premier état de chaque paramètre. Quand on a collecté des informations précédemment, on a créé le liste $paramList qui contient les numéros des premiers états de chaque paramètre. Si vous avez suivit la construction de paramList, les conditions suivantes devraient fonctionner, on ne trace que si le paramètre = 0 ou bien si le paramètre = premier état.
$param == $paramList[$param % 256]
Changement majeur : on trace des sprites de 32*32 pixels. Cette zone est une sorte de background répété au centre de l'écran (comme la propriété background: repeat;
en css).
Sprites de 32*32. Cette zone est une sorte de background fixé là où se trouve la caméra (comme la propriété background: fixed;
en css).
L'ordre des couches est donné par l'Id des infos Sprites. Cet id est compris entre 0 et 4096 (inclus), avec 0 le plus près et 4096 le plus éloigné. Dans les scripts, on peut changer cette valeur entre -32768 et +32767 (signed short). Il est donc judicieux de trier les tiles par leur Id.
Par défaut, les couches ont des id prédéfinis :
La zone 4 est toujours devant, la zone 3 derrière, la zone 1 juste devant la 3 et la 2 ça dépend. Ces valeurs peuvent changer par les scripts, si vous voulez optimiser l'affichage, il faut savoir lire les scripts .