모든 형식 페이지 ↓
그래픽·미디어

PCX — ZSoft Paintbrush 비트맵

출처: coreprime/kbot @ 0ab97bf · GitHub에서 편집

PCX는 90년대 초의 인덱스 색상 비트맵 형식입니다. Total Annihilation은 이 형식을 유닛 초상화(unitpics/*.pcx), GUI 패널, 몇몇 맵 미리 보기 이미지에 씁니다. TA: Kingdoms에서는 1×1 PCX가 진영별 팔레트를 담는 파일 역할도 합니다 — 엔진은 PCX에 내장된 팔레트를 읽어 같은 이름의 .taf 애니메이션 파일에 적용합니다.

PCX에서 변환한 ARMCOM 유닛 초상화
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의 형식이 아니라 널리 쓰이던 공용 형식입니다

PCX는 Cavedog이 만든 것이 아닙니다 — Windows보다도 오래된 형식입니다. 그래서 HPI, GAF, TNT와 달리, TA가 출시될 때 디스크상 구조는 이미 공개된 명세였습니다. 이 페이지는 TA 설치본에서 PCX를 만났을 때 필요한 내용만 다룹니다. 전체 명세는 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인 런으로 내보내야 합니다(0xFF 하나는 0xC1, 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는 GUI에 맞게 조정한 변형 팔레트를 담으려고 일부러 만든 1×1 픽셀 파일입니다.


TA 3.1c가 PCX를 다루는 방식

게임은 헤더에서 제조사, 버전, 범위 필드만 읽습니다.

  • 버전 5만 로드하며, XMax/YMax가 최솟값보다 작거나 896바이트보다 짧은 파일은 거부합니다.
  • Encoding, BitsPerPixel, NumPlanes, BytesPerLine은 무시합니다: 모든 파일을 행마다 Width바이트씩, 8비트 단일 플레인 데이터로 런 렝스 디코딩합니다. 그래서 24비트 파일은 쓰레기로 보이고, 패딩이 있는 행은 밀립니다.
  • 팔레트는 앞에 무엇이 있든 마지막 768바이트입니다.
  • 배경은 불투명하게 그립니다: 팔레트 인덱스 0은 평범한 검은색입니다.
  • 픽셀 데이터가 일찍 끝나도 멈추지 않습니다: 마지막으로 읽은 바이트로 계속 그립니다.

이미지 편집기는 이런 파일을 표준 방식으로 읽으므로, 모드의 배경 그림이 편집기에서 (그리고 kbot 미리 보기에서) 제대로 보여도 게임에서는 틀리게 보일 수 있습니다. kbot pcx describe는 출력 끝에 모든 차이(kbot-io의 pcx.Reader.Compat)를 나열하는 TA 3.1c 절을 보여 주고, 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로 내보내므로, PCX로 다시 왕복 변환해도 색상 인덱스가 보존됩니다.


TA: Kingdoms — "팔레트 PCX" 요령

TA의 .gaf를 TA: Kingdoms용으로 변환하면 엔진에는 팔레트가 필요하지만 .taf(확장자를 바꾼 .gaf)에는 팔레트가 없습니다. 요령은 .taf 옆에 같은 기본 이름의 1×1 PCX를 두는 것입니다.

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

엔진은 mywidget.pcx의 내장 팔레트를 읽고 픽셀은 무시합니다. 유닛/지형지물 애니메이션의 팔레트는 진영(Aramon, Taros, Veruna, Zhon)에 따라 암묵적으로 정해지고, UI 애니메이션에는 시퀀스마다 PCX를 둡니다.

여기서 가장 흔한 함정은 PSP에서 내보낸 팔레트를 쓰는 것입니다. 이 팔레트는 표준 TA 팔레트와 RGB 값이 약간 달라서 일부 시퀀스에서 TA:K를 멈추게 할 수 있습니다. kbot pal swatch는 어떤 팔레트든 PNG로 덤프해 주므로 기준 팔레트와 비교해 볼 수 있습니다.


주의할 점

Warning

TA용으로는 BytesPerLine을 너비와 같게 쓰세요. 표준 리더는 BytesPerLine > Width일 때 패딩을 버리지만, TA 3.1c는 BytesPerLine을 무시하고 행마다 Width바이트를 디코딩하므로 패딩이 이후의 모든 행을 비틀어 놓습니다. kbot pcx describe가 이를 경고합니다.

  • 게임은 0x0C 표식이 앞에 있든 없든 끝의 768바이트를 팔레트로 씁니다. 표식이 없으면 표준 리더는 헤더의 16색 팔레트로 대신합니다.
  • TA에서는 버전 5만 로드됩니다.
  • 헤더의 PaletteInfo 필드는 믿을 수 없습니다. 참고용으로만 취급하고, 내장 팔레트의 모든 항목이 R==G==B가 아니라면 색상 팔레트라고 가정하세요.
  • TA의 unitpics/*.pcx 파일은 관례상 96×96입니다 — 형식이 강제하는 것은 아닙니다. 모드에 따라 더 크거나 정사각형이 아닌 초상화를 배포하기도 합니다.

일반적인 크기

에셋 크기 파일 크기
unitpics/*.pcx(유닛 초상화) 96 × 96 3–12 KB
GUI 패널(bitmaps/*.pcx) 다양함, 흔히 너비 64–256 px 5–80 KB
맵 미리 보기 252 × 252 30–80 KB
TA:K 팔레트를 담는 파일 1 × 1 약 800바이트(헤더 + 팔레트)
헤더 오버헤드 항상 128바이트 —
내장 팔레트 항상 769바이트(있을 경우) —

함께 보기

  • PAL — 팔레트가 별도 파일에 있는 경우.
  • GAF — TA: Kingdoms의 .taf 파일은 팔레트를 담는 .pcx와 짝을 이룹니다.
  • 용어집 — paletted image, embedded palette.