Tutte le pagine dei formati ↓
Grafica e media

PCX — Bitmap ZSoft Paintbrush

Fonte: coreprime/kbot @ 0ab97bf · Modifica su GitHub

PCX è un formato bitmap a colori indicizzati dei primi anni '90. Total Annihilation lo usa per i ritratti delle unità (unitpics/*.pcx), per i pannelli dell'interfaccia e per una manciata di immagini di anteprima delle mappe. In TA: Kingdoms un PCX 1×1 fa anche da contenitore per le palette di ciascuna fazione: il motore legge la palette incorporata nel PCX e la applica al file di animazione .taf che gli sta accanto.

ritratto dell'unità ARMCOM convertito da PCX
unitpics/armcom.pcx → PNG (palette di TA)

Tip

Provalo tu stesso.

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

Consulta il riferimento CLI di kbot pcx.

Da Go. Usa 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

PCX è un formato comune, non di Cavedog

Non è stata Cavedog a inventare il PCX: è più vecchio di Windows. Quindi, a differenza di HPI, GAF o TNT, la sua struttura su disco era già una specifica aperta quando uscì TA. Questa pagina tratta solo ciò che conta quando trovi un PCX dentro un'installazione di TA; per la specifica completa, vedi il riferimento tecnico originale di ZSoft.

Il sottoinsieme che interessa TA:

  • 8 bit per pixel, 1 piano: solo immagini con palette. Le varianti a 24 bit, a 4 bit e a più piani esistono, ma Cavedog non ne distribuisce nessuna.
  • Codifica RLE (Encoding = 1). I PCX non compressi sono tecnicamente validi ma poco comuni.
  • Palette VGA a 256 colori accodata dopo i dati dei pixel, preceduta da un singolo byte marcatore 0x0C. TA usa sempre questa struttura; la palette a 16 colori dell'intestazione viene ignorata.

Intestazione (128 byte)

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;
Campo Valore in TA Note
Manufacturer 0x0A Rifiuta qualsiasi altro valore: non è un PCX.
Version 5 TA 3.1c carica solo la versione 5.
BitsPerPixel × NumPlanes 8 × 1 = 8 L'unica configurazione che TA distribuisce e l'unica che il gioco decodifica.
Encoding 1 RLE: vedi sotto. Il gioco decodifica in RLE qualunque cosa dica il byte.
BytesPerLine XMax − XMin + 1 Per TA deve essere uguale alla larghezza. Il gioco lo ignora e legge per ogni riga esattamente tanti byte quanto è larga l'immagine; gli editor di immagini che riempiono le righe fino a un numero pari di byte producono file le cui righe nel gioco risultano sfalsate.

Dimensioni calcolate dell'immagine

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

Il gioco le calcola con interi completi, quindi è possibile una larghezza di 65.536, e rifiuta un file il cui massimo è inferiore al minimo. Un lettore standard tratta i byte finali di ogni riga di scansione (BytesPerLine − Width) come riempimento; il gioco no (vedi sotto).


Dati dei pixel in RLE

Per ognuna delle Height righe di scansione, ripeti:

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 lettore standard poi scarta il riempimento finale (BytesPerLine − Width byte). TA 3.1c invece decodifica esattamente Width pixel per riga, di seguito a partire dall'intestazione, quindi l'eventuale riempimento diventa i primi pixel della riga successiva; un run che oltrepassa la fine di una riga viene troncato lì.

Important

I byte letterali con i due bit alti impostati vanno protetti con l'RLE. Un byte isolato di valore 0xC0–0xFF non è valido come letterale; devi scriverlo come run di lunghezza 1 (0xC1, 0xFF per un singolo 0xFF). I programmi che scrivono PCX e se ne dimenticano producono file che si decodificano per una riga e poi perdono la sincronizzazione.


Palette incorporata (256 voci × 3 byte)

Dopo l'ultimo byte RLE, il file termina con 769 byte:

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

Per trovare la palette senza analizzare l'RLE, posizionati a fileSize - 769 e verifica il marcatore. Un lettore standard, senza il marcatore, ripiega sulla palette a 16 colori dell'intestazione (le anteprime di kbot usano la palette di TA). TA 3.1c prende sempre gli ultimi 768 byte come palette, marcatore o no, e rifiuta un file più corto di 896 byte (intestazione più palette).

Note

In un'installazione di TA la palette incorporata sarà di solito la palette standard di TA, ma non sempre. I ritratti specifici di alcune missioni a volte hanno palette proprie, e palettes/guipal.pcx è un contenitore di palette da 1×1 pixel, voluto, con una variante su misura per l'interfaccia.


Che cosa fa TA 3.1c con un PCX

Il gioco legge dell'intestazione solo i campi del produttore, della versione e dell'estensione dell'immagine:

  • Carica solo la versione 5 e rifiuta un file il cui XMax/YMax è inferiore al minimo o che è più corto di 896 byte.
  • Ignora Encoding, BitsPerPixel, NumPlanes e BytesPerLine: ogni file viene decodificato in run-length come dati a 8 bit su un solo piano, Width byte per riga. Un file a 24 bit appare quindi come immagine senza senso, e le righe con riempimento risultano sfalsate.
  • La palette sono gli ultimi 768 byte, qualunque cosa li preceda.
  • I fondali vengono disegnati opachi: l'indice 0 della palette è un normale nero.
  • Se i dati dei pixel finiscono prima del previsto, il gioco non si ferma: continua a disegnare a partire dall'ultimo byte letto.

Gli editor di immagini leggono questi file nel modo standard, quindi un fondale di una mod può sembrare corretto in un editor (e nell'anteprima di kbot) e sbagliato nel gioco. kbot pcx describe termina con una sezione TA 3.1c che elenca ogni differenza (pcx.Reader.Compat di kbot-io), lo strumento MCP pcx_describe la restituisce come game_loads / game_issues, e l'esploratore di asset la mostra come un badge accanto all'anteprima:

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

Esempio svolto — 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

Questo ti dice tutto ciò che serve per disegnare il file a mano: immagine 96×96, 41 indici della palette distinti in uso, palette accodata alla fine. kbot pcx convert -f png lo esporta come PNG con palette, così gli indici dei colori sopravvivono al round-trip verso il PCX.


TA: Kingdoms — il trucco del "PCX di palette"

Quando converti un .gaf di TA per TA: Kingdoms, il motore ha bisogno di una palette, ma il .taf (un .gaf rinominato) non ne ha. Il trucco è mettere accanto al .taf un PCX 1×1 con lo stesso nome di base:

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

Il motore legge la palette incorporata in mywidget.pcx e ne ignora l'unico pixel. Per le animazioni di unità ed elementi la palette è implicita per fazione (Aramon, Taros, Veruna, Zhon); per le animazioni dell'interfaccia metti un PCX per ogni sequenza.

L'errore più comune qui è usare una palette esportata da PSP, che ha valori RGB leggermente diversi da quelli della palette canonica di TA e può far andare in crash TA:K su alcune sequenze. kbot pal swatch scarica qualsiasi palette in un PNG, così puoi confrontarla con quella di riferimento.


Insidie

Warning

Per TA, scrivi BytesPerLine uguale alla larghezza. Un lettore standard scarta il riempimento quando BytesPerLine > Width, ma TA 3.1c ignora BytesPerLine e decodifica Width byte per riga, quindi il riempimento sfalsa ogni riga successiva. kbot pcx describe lo segnala.

  • Il gioco usa gli ultimi 768 byte come palette, che il marcatore 0x0C li preceda o no. I lettori standard, senza il marcatore, ripiegano sulla palette a 16 colori dell'intestazione.
  • In TA si carica solo la versione 5.
  • Il campo PaletteInfo dell'intestazione è inaffidabile. Consideralo solo indicativo; presupponi palette a colori a meno che nella palette incorporata ogni voce abbia R==G==B.
  • I file unitpics/*.pcx di TA sono 96×96 per convenzione, non per vincolo del formato. Le mod a volte distribuiscono ritratti più grandi o non quadrati.

Dimensioni tipiche

Elemento Dimensioni Dimensione del file
unitpics/*.pcx (ritratto dell'unità) 96 × 96 3–12 KB
Pannello dell'interfaccia (bitmaps/*.pcx) varie, spesso larghe 64–256 px 5–80 KB
Anteprima della mappa 252 × 252 30–80 KB
Contenitore di palette di TA:K 1 × 1 ~800 byte (intestazione + palette)
Costo dell'intestazione sempre 128 byte —
Palette incorporata sempre 769 byte (quando presente) —

Vedi anche

  • PAL: quando invece la palette sta in un file separato.
  • GAF: i file .taf di TA: Kingdoms si abbinano a un .pcx contenitore di palette.
  • Glossario: immagine con palette (paletted image), palette incorporata (embedded palette).