PCX — Bitmap ZSoft Paintbrush
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.tafche gli sta accanto.
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,NumPlaneseBytesPerLine: ogni file viene decodificato in run-length come dati a 8 bit su un solo piano,Widthbyte 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
0x0Cli 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
PaletteInfodell'intestazione è inaffidabile. Consideralo solo indicativo; presupponi palette a colori a meno che nella palette incorporata ogni voce abbia R==G==B. - I file
unitpics/*.pcxdi 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) | — |