Усі сторінки форматів ↓
Графіка й медіа

PCX — растр ZSoft Paintbrush

Джерело: coreprime/kbot @ 0ab97bf · Редагувати на GitHub

PCX — растровий формат з індексованими кольорами з початку 90-х. Total Annihilation використовує його для портретів юнітів (unitpics/*.pcx), панелей інтерфейсу й кількох зображень попереднього перегляду мап. У TA: Kingdoms PCX розміром 1×1 ще й слугує носієм палітр окремих сторін: рушій читає вбудовану палітру з PCX і застосовує її до сусіднього файлу анімації .taf.

Портрет юніта ARMCOM, сконвертований із PCX
unitpics/armcom.pcx → PNG (палітра TA)

Tip

Спробуйте самі.

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

Див. довідник CLI kbot pcx.

З Go. Скористайтеся пакетом 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 — загальновідомий формат, а не формат Cavedog

Cavedog не вигадувала PCX — він старший за Windows. Тож, на відміну від HPI, GAF чи TNT, на момент виходу TA структура на диску вже була відкритою специфікацією. Ця сторінка описує лише те, що стосується PCX, знайденого в інсталяції TA; повну специфікацію див. в оригінальному технічному довіднику ZSoft.

Підмножина, що стосується TA:

  • 8 бітів на піксель, 1 площина — лише палітрові зображення. 24-бітні, 4-бітні й багатоплощинні варіанти існують, але Cavedog таких не постачає.
  • Кодування RLE (Encoding = 1). PCX без стиснення формально допустимі, але трапляються рідко.
  • 256-колірна палітра VGA, дописана після піксельних даних, перед якою стоїть один байт-маркер 0x0C. TA завжди використовує цю структуру; 16-колірна палітра в заголовку ігнорується.

Заголовок (128 байтів)

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;
Поле Значення в TA Примітки
Manufacturer 0x0A Усе інше відхиляйте — це не PCX.
Version 5 TA 3.1c завантажує лише версію 5.
BitsPerPixel × NumPlanes 8 × 1 = 8 Єдина конфігурація, яку постачає TA, і єдина, яку декодує гра.
Encoding 1 RLE — див. нижче. Гра декодує RLE незалежно від значення цього байта.
BytesPerLine XMax − XMin + 1 Для TA має дорівнювати ширині. Гра ігнорує це поле й читає рівно стільки байтів на рядок, скільки становить ширина; графічні редактори, що доповнюють рядки до парної кількості, створюють файли, рядки яких у грі зсуваються.

Обчислений розмір зображення

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

Гра обчислює їх у повних цілих числах, тож ширина 65 536 можлива, і відхиляє файл, максимум якого менший за мінімум. Стандартний читач вважає кінцеві байти кожного рядка розгортки (BytesPerLine − Width) доповненням; гра — ні (див. нижче).


Піксельні дані RLE

Для кожного з Height рядків розгортки повторюйте:

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
    }
}

Далі стандартний читач відкидає кінцеве доповнення (BytesPerLine − Width байтів). TA 3.1c натомість декодує рівно Width пікселів на рядок, ідучи безперервно від заголовка, тож будь-яке доповнення стає першими пікселями наступного рядка; серія, що виходить за кінець рядка, там і обрізається.

Important

Байти-літерали з двома встановленими старшими бітами мають екрануватися через RLE. Голий байт зі значенням 0xC0–0xFF як літерал неприпустимий; його треба записати як серію довжиною 1 (0xC1, 0xFF для одного 0xFF). Програми запису, які про це забувають, створюють файли, що декодуються лише один рядок, а далі розсинхронізуються.


Вбудована палітра (256 записів × 3 байти)

Після останнього байта RLE файл закінчується 769 байтами:

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

Щоб знайти палітру, не розбираючи RLE, перейдіть до fileSize - 769 і перевірте маркер. Стандартний читач без маркера повертається до 16-колірної палітри із заголовка (попередні перегляди kbot використовують палітру TA). TA 3.1c завжди бере останні 768 байтів як палітру, хоч з маркером, хоч без нього, і відхиляє файл, коротший за 896 байтів (заголовок плюс палітра).

Note

В інсталяції TA вбудована палітра зазвичай буде стандартною палітрою TA — але не завжди. Портрети для окремих місій інколи постачаються з власними палітрами, а palettes/guipal.pcx — це навмисний носій палітри розміром 1×1 піксель зі спеціальним варіантом, налаштованим для інтерфейсу.


Що TA 3.1c робить із PCX

Гра читає із заголовка лише поля виробника, версії й меж:

  • Вона завантажує лише версію 5 і відхиляє файл, у якого XMax/YMax менший за мінімум або який коротший за 896 байтів.
  • Вона ігнорує Encoding, BitsPerPixel, NumPlanes і BytesPerLine: кожен файл декодується з RLE як 8-бітні одноплощинні дані, по Width байтів на рядок. Тому 24-бітний файл показується як сміття, а рядки з доповненням зсуваються.
  • Палітра — це останні 768 байтів, хоч би що стояло перед ними.
  • Фони малюються непрозорими: індекс палітри 0 — звичайний чорний.
  • Піксельні дані, що закінчуються передчасно, її не зупиняють: вона продовжує малювати з останнього прочитаного байта.

Графічні редактори читають такі файли стандартним способом, тож фон із мода може правильно виглядати в редакторі (і в попередньому перегляді kbot), але неправильно — у грі. kbot pcx describe завершується розділом TA 3.1c, де перелічено кожну відмінність (pcx.Reader.Compat у kbot-io); MCP-інструмент pcx_describe повертає її як game_loads / game_issues, а оглядач ресурсів показує значок поруч із попереднім переглядом:

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

Розібраний приклад — 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

Це все, що потрібно, щоб відрендерити файл вручну: зображення 96×96, використано 41 різний індекс палітри, палітру дописано в кінці. kbot pcx convert -f png експортує його як палітровий PNG, щоб індекси кольорів пережили round-trip назад у PCX.


TA: Kingdoms — прийом із «PCX-палітрою»

Коли ви конвертуєте .gaf із TA для TA: Kingdoms, рушію потрібна палітра, але в .taf (перейменованому .gaf) її немає. Прийом полягає в тому, щоб покласти поруч із .taf PCX розміром 1×1 з тим самим базовим ім'ям файлу:

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

Рушій читає вбудовану палітру mywidget.pcx і ігнорує його піксель. Для анімацій юнітів і об'єктів палітра неявно визначається стороною (Aramon, Taros, Veruna, Zhon); для анімацій інтерфейсу ви кладете окремий PCX для кожної послідовності.

Найпоширеніший підводний камінь тут — використання палітри, експортованої з PSP: її значення RGB трохи відрізняються від канонічної палітри TA, і на деяких послідовностях це може призвести до падіння TA:K. kbot pal swatch вивантажить будь-яку палітру в PNG, щоб ви могли порівняти її з еталоном.


Підводні камені

Warning

Для TA записуйте BytesPerLine, що дорівнює ширині. Стандартний читач відкидає доповнення, коли BytesPerLine > Width, але TA 3.1c ігнорує BytesPerLine і декодує Width байтів на рядок, тож доповнення перекошує кожен наступний рядок. kbot pcx describe попереджає про це.

  • Гра використовує останні 768 байтів як палітру незалежно від того, чи стоїть перед ними маркер 0x0C. Стандартні читачі без маркера повертаються до 16-колірної палітри із заголовка.
  • У TA завантажується лише версія 5.
  • Поле заголовка PaletteInfo ненадійне. Вважайте його лише рекомендаційним; припускайте, що палітра кольорова, якщо тільки не у кожному записі вбудованої палітри R==G==B.
  • Файли unitpics/*.pcx у TA мають розмір 96×96 за домовленістю — формат цього не вимагає. Моди інколи постачають більші або неквадратні портрети.

Типові розміри

Ресурс Розміри Розмір файлу
unitpics/*.pcx (портрет юніта) 96 × 96 3–12 КБ
Панель інтерфейсу (bitmaps/*.pcx) різні, часто завширшки 64–256 px 5–80 КБ
Попередній перегляд мапи 252 × 252 30–80 КБ
Носій палітри TA:K 1 × 1 ~800 байтів (заголовок + палітра)
Накладні витрати на заголовок завжди 128 байтів —
Вбудована палітра завжди 769 байтів (якщо є) —

Див. також

  • PAL — коли палітра натомість міститься в окремому файлі.
  • GAF — файли .taf у TA: Kingdoms поєднуються з PCX-носієм палітри.
  • Глосарій — paletted image, embedded palette.