PCX — растр ZSoft Paintbrush
Джерело: coreprime/kbot @ 0ab97bf · Редагувати на GitHub
PCX — растровий формат з індексованими кольорами з початку 90-х. Total Annihilation використовує його для портретів юнітів (
unitpics/*.pcx), панелей інтерфейсу й кількох зображень попереднього перегляду мап. У TA: Kingdoms PCX розміром 1×1 ще й слугує носієм палітр окремих сторін: рушій читає вбудовану палітру з PCX і застосовує її до сусіднього файлу анімації.taf.
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 байтів (якщо є) | — |