Toutes les pages de formats ↓
Graphismes et médias

PCX — bitmap ZSoft Paintbrush

Source : coreprime/kbot @ 0ab97bf · Modifier sur GitHub

Le PCX est un format bitmap en couleurs indexées du début des années 90. Total Annihilation l'utilise pour les portraits d'unités (unitpics/*.pcx), les panneaux d'interface et une poignée d'images d'aperçu de cartes. Dans TA: Kingdoms, un PCX de 1×1 sert aussi de porteur pour les palettes par camp : le moteur lit la palette intégrée au PCX et l'applique au fichier d'animation .taf voisin.

portrait de l'unité ARMCOM converti depuis le PCX
unitpics/armcom.pcx → PNG (palette de TA)

Tip

Essayez vous-même.

kbot pcx describe unitpics/armcom.pcx        # full header + what TA will do with it
kbot pcx info     unitpics/armcom.pcx        # one-line summary
kbot pcx convert  unitpics/armcom.pcx -f png -o armcom.png

Consultez la référence de kbot pcx de la CLI.

Depuis Go. Utilisez formats/pcx :

import "github.com/coreprime/kbot/formats/pcx"

f, _ := os.Open("unitpics/armcom.pcx")
defer f.Close()
r, _ := pcx.LoadFromReader(f)
fmt.Println(r.Width(), "×", r.Height(), "@", r.BitsPerPixel(), "bpp")
img, _ := r.Decode()                          // returns image.Image

Le PCX est un format public, pas un format de Cavedog

Cavedog n'a pas inventé le PCX, qui est antérieur à Windows. Contrairement au HPI, au GAF ou au TNT, sa disposition sur disque était donc déjà une spécification ouverte quand TA est sorti. Cette page ne couvre que ce qui compte quand on trouve un PCX dans une installation de TA ; pour la spécification complète, voir la référence technique d'origine de ZSoft.

Le sous-ensemble qui concerne TA :

  • 8 bits par pixel, 1 plan — uniquement des images à palette. Les variantes 24 bits, 4 bits et multiplans existent, mais Cavedog n'en livre aucune.
  • Codage RLE (Encoding = 1). Les PCX non compressés sont techniquement légaux mais rares.
  • Palette VGA de 256 couleurs ajoutée après les données de pixels, précédée d'un unique octet marqueur 0x0C. TA utilise toujours cette disposition ; la palette de 16 couleurs de l'en-tête est ignorée.

En-tête (128 octets)

typedef struct {
    uint8  Manufacturer;     // Always 0x0A (ZSoft signature)
    uint8  Version;          // 0–5 (TA images are typically version 5)
    uint8  Encoding;         // 1 = RLE
    uint8  BitsPerPixel;     // Bits per plane per pixel (8 in TA)
    uint16 XMin, YMin;       // Image bounds (inclusive)
    uint16 XMax, YMax;
    uint16 HorzDPI, VertDPI;
    uint8  Palette16[48];    // Legacy 16-colour palette (unused in TA)
    uint8  Reserved;
    uint8  NumPlanes;        // 1 for 256-colour images
    uint16 BytesPerLine;     // Encoded bytes per scan line per plane
    uint16 PaletteInfo;      // 1 = colour, 2 = grayscale
    uint16 HorzScreen, VertScreen;
    uint8  Filler[54];       // Pad to 128 bytes (Cavedog leaves it zeroed)
} PCXHeader;
Champ Valeur dans TA Remarques
Manufacturer 0x0A Rejetez toute autre valeur : ce n'est pas un PCX.
Version 5 TA 3.1c ne charge que la version 5.
BitsPerPixel × NumPlanes 8 × 1 = 8 La seule configuration que livre TA, et la seule que le jeu décode.
Encoding 1 RLE — voir plus bas. Le jeu décode en RLE quelle que soit la valeur de l'octet.
BytesPerLine XMax − XMin + 1 Doit être égal à la largeur pour TA. Le jeu l'ignore et lit exactement « largeur » octets par ligne ; les éditeurs d'images qui complètent les lignes jusqu'à un nombre pair produisent des fichiers dont les lignes sont décalées dans le jeu.

Taille calculée de l'image

Width  = XMax − XMin + 1
Height = YMax − YMin + 1

Le jeu calcule ces valeurs sur des entiers pleins : une largeur de 65 536 est donc possible, et il refuse un fichier dont le maximum est inférieur au minimum. Un lecteur standard traite les octets en fin de chaque ligne de balayage (BytesPerLine − Width) comme du remplissage ; pas le jeu (voir plus bas).


Données de pixels RLE

Pour chacune des Height lignes de balayage, on répète :

while (decoded < BytesPerLine) {
    uint8 b = read();
    if ((b & 0xC0) == 0xC0) {
        int count = b & 0x3F;        // 1..63 reps
        uint8 value = read();
        emit(value, count);
    } else {
        emit(b, 1);                  // literal
    }
}

Un lecteur standard jette ensuite le remplissage de fin de ligne (BytesPerLine − Width octets). TA 3.1c, lui, décode exactement Width pixels par ligne, à la suite depuis l'en-tête, si bien que tout remplissage devient les premiers pixels de la ligne suivante ; une plage qui franchit la fin d'une ligne y est coupée.

Important

Les octets littéraux dont les deux bits de poids fort sont à un doivent être échappés en RLE. Un octet nu de valeur 0xC0–0xFF est interdit comme littéral ; vous devez l'émettre sous forme de plage de longueur 1 (0xC1, 0xFF pour un seul 0xFF). Les outils d'écriture qui l'oublient produisent des fichiers qui se décodent pendant une ligne, puis se désynchronisent.


Palette intégrée (256 entrées × 3 octets)

Après le dernier octet RLE, le fichier se termine par 769 octets :

   1 byte  : marker, always 0x0C
 256 × 3   : R, G, B per entry (8-bit each)

Pour trouver la palette sans analyser le RLE, placez-vous à fileSize - 769 et vérifiez le marqueur. Un lecteur standard qui ne trouve pas le marqueur se rabat sur la palette de 16 couleurs de l'en-tête (les aperçus de kbot utilisent la palette de TA). TA 3.1c prend toujours les 768 derniers octets comme palette, marqueur ou pas, et refuse un fichier de moins de 896 octets (en-tête plus palette).

Note

Dans une installation de TA, la palette intégrée est en général la palette standard de TA — mais pas toujours. Les portraits propres à une mission livrent parfois leur propre palette, et palettes/guipal.pcx est délibérément un porteur de palette de 1×1 pixel, avec une variante réglée pour l'interface.


Ce que TA 3.1c fait d'un PCX

Le jeu ne lit que les champs fabricant, version et dimensions de l'en-tête :

  • Il ne charge que la version 5 et refuse un fichier dont XMax/YMax est inférieur au minimum ou qui fait moins de 896 octets.
  • Il ignore Encoding, BitsPerPixel, NumPlanes et BytesPerLine : chaque fichier est décodé par plages comme des données 8 bits sur un seul plan, à raison de Width octets par ligne. Un fichier 24 bits s'affiche donc comme du charabia, et les lignes avec remplissage sont décalées.
  • La palette est faite des 768 derniers octets, quoi qui les précède.
  • Les fonds sont dessinés opaques : l'index de palette 0 est un noir ordinaire.
  • Des données de pixels qui se terminent trop tôt ne l'arrêtent pas : il continue de dessiner à partir du dernier octet lu.

Les éditeurs d'images lisent ces fichiers de la façon standard : un fond de mod peut donc paraître correct dans un éditeur (et dans l'aperçu de kbot) et faux dans le jeu. kbot pcx describe se termine par une section TA 3.1c qui liste chaque différence (le pcx.Reader.Compat de kbot-io), l'outil MCP pcx_describe la renvoie sous forme de game_loads / game_issues, et l'explorateur de ressources l'affiche comme un badge à côté de l'aperçu :

TA 3.1c:
  ⚠ The game will load this file but draw it differently:
    ⚠ BytesPerLine is 4 but the width is 3: the game ignores BytesPerLine and decodes 3 bytes per row, so the rows shift

Exemple commenté — unitpics/armcom.pcx

$ kbot pcx describe unitpics/armcom.pcx
Manufacturer:  0x0A
Version:       5
Encoding:      1 (RLE)
BitsPerPixel:  8
Planes:        1
Bounds:        (0,0) - (95,95)   →  96 × 96 px
BytesPerLine:  96
Palette:       embedded (768 bytes after 0x0C marker)
Unique colors: 41

Cela vous dit tout ce qu'il faut pour rendre le fichier à la main : image de 96×96, 41 index de palette distincts utilisés, palette ajoutée à la fin. kbot pcx convert -f png l'exporte en PNG à palette, pour que les index de couleur survivent à l'aller-retour vers le PCX.


TA: Kingdoms — l'astuce du « PCX de palette »

Quand vous convertissez un .gaf de TA pour TA: Kingdoms, le moteur a besoin d'une palette, mais le .taf (un .gaf renommé) n'en a pas. L'astuce consiste à déposer à côté du .taf un PCX de 1×1 portant le même nom de base :

ui/mywidget.taf      ← animation data
ui/mywidget.pcx      ← 1×1 paletted PCX carrying the 256-colour palette

Le moteur lit la palette intégrée de mywidget.pcx et ignore son pixel. Pour les animations d'unités et d'éléments de décor, la palette est implicite selon le camp (Aramon, Taros, Veruna, Zhon) ; pour les animations d'interface, vous déposez un PCX par séquence.

Le piège le plus courant ici est d'utiliser une palette exportée par PSP, qui a des valeurs RVB légèrement différentes de celles de la palette de référence de TA et peut faire planter TA:K sur certaines séquences. kbot pal swatch exporte n'importe quelle palette en PNG pour que vous puissiez la comparer à la référence.


Pièges

Warning

Écrivez un BytesPerLine égal à la largeur pour TA. Un lecteur standard laisse de côté le remplissage quand BytesPerLine > Width, mais TA 3.1c ignore BytesPerLine et décode Width octets par ligne : le remplissage décale alors toutes les lignes suivantes. kbot pcx describe le signale.

  • Le jeu utilise les 768 derniers octets comme palette, que le marqueur 0x0C les précède ou non. Sans le marqueur, les lecteurs standard se rabattent sur la palette de 16 couleurs de l'en-tête.
  • Seule la version 5 se charge dans TA.
  • Le champ PaletteInfo de l'en-tête n'est pas fiable. Ne le considérez que comme indicatif ; supposez une palette en couleurs, sauf si R==G==B pour chaque entrée de la palette intégrée.
  • Les fichiers unitpics/*.pcx de TA font 96×96 par convention — le format ne l'impose pas. Des mods livrent parfois des portraits plus grands ou non carrés.

Tailles typiques

Ressource Dimensions Taille du fichier
unitpics/*.pcx (portrait d'unité) 96 × 96 3–12 Ko
Panneau d'interface (bitmaps/*.pcx) variables, souvent 64–256 px de large 5–80 Ko
Aperçu de carte 252 × 252 30–80 Ko
Porteur de palette de TA:K 1 × 1 ~800 octets (en-tête + palette)
Surcoût de l'en-tête toujours 128 octets —
Palette intégrée toujours 769 octets (quand elle est présente) —

Voir aussi

  • PAL — quand la palette se trouve plutôt dans un fichier séparé.
  • GAF — les fichiers .taf de TA: Kingdoms vont de pair avec un PCX porteur de palette.
  • Glossaire — image à palette, palette intégrée.