Reverse Engineering Comanche Terrain Maps
Comanche 지형 맵 역공학
1992년 게임 Comanche의 지형 파일 8개를 분석해 색상 맵과 높이 맵을 BMP로 변환하는 과정을 설명합니다. 파일은 독자 형식이 아니라 앞부분의 서명만 바꾼 PCX였으며, 헤더와 RLE 데이터, 팔레트를 확인해 이를 검증합니다.
- 주제
AI 요약
1992년작 비행 시뮬레이터 Comanche: Maximum Overkill은 당시의 386 PC에서 산과 협곡을 사실적으로 그려냈습니다. 이 게임은 다각형 대신 Kyle Freeman이 만든 Voxel Space 기법을 사용했습니다. 이 글은 게임 데이터에 들어 있는 지형 파일을 역공학해 색상 맵과 높이 맵을 추출하고, ANSI C 프로그램으로 BMP 이미지로 저장하는 과정을 따라갑니다.
파일에서 형식 찾기
게임 폴더에는 C1~C4.DTA와 D1~D4.DTA, 모두 8개의 지형 파일이 있습니다. C는 색상 맵, D는 높이 맵으로 추정합니다. Voxel Space 지형 하나에는 1024×1024 픽셀의 색상 맵과 높이 맵이 필요하며, 각각 1바이트씩 총 1,048,576바이트입니다. 파일이 이보다 작으므로 압축 데이터가 들어 있다고 봅니다.
헥스 덤프를 살펴보면 파일 시작 8바이트에 Kyle DTA가 있습니다. 그 뒤 오프셋 0x08과 0x0A에는 리틀엔디언 값 1023이 들어 있어, 각각 최대 좌표를 나타내는 PCX 헤더와 맞아떨어집니다. 오프셋 0x42의 값 1024는 스캔라인 바이트 수를 가리킵니다. 0x80부터 데이터가 시작하는 점까지 종합하면, 이 파일은 PCX의 앞 8바이트를 Kyle DTA로 덮어쓴 형식입니다. 나머지 헤더 필드와 데이터 배치는 표준 PCX와 같습니다. 첫 8바이트를 표준 PCX 헤더로 되돌리면 PCX를 읽는 이미지 뷰어에서 파일을 열 수 있습니다.
RLE 압축과 팔레트
PCX는 Run-Length Encoding(RLE)으로 픽셀을 압축합니다. 바이트의 상위 2비트가 1이면 하위 6비트가 반복 횟수이고, 다음 바이트가 반복할 픽셀 값입니다. 예를 들어 C2 01은 값 1을 두 번 출력합니다. 상위 2비트가 1이 아닌 바이트는 그대로 픽셀 값으로 기록합니다. 픽셀 값 자체가 0xC0 이상이면 압축 표시와 혼동되지 않도록, 인코더가 반복 횟수 1을 나타내는 C1과 값을 함께 씁니다.
C 프로그램은 128바이트 헤더 뒤에서 이 규칙에 따라 데이터를 읽고, 1,048,576픽셀을 채우면 멈춥니다. 각 파일에서 픽셀 데이터 뒤에 769바이트가 남습니다. 첫 바이트 0x0C는 팔레트 표시자이며, 뒤의 768바이트는 256색 RGB 팔레트입니다. PCX 팔레트 성분은 0~255지만 VGA DAC는 0~63을 받으므로, VGA 하드웨어에 전달할 때는 각 값을 2비트 오른쪽으로 이동해야 합니다.
높이 맵에도 팔레트가 들어 있지만 회색조가 아닙니다. 16단계씩 색상 띠를 나눈 의사 색상 팔레트로, 원본 팔레트를 적용하면 높이 16단위마다 색이 달라지는 등고선처럼 보입니다. 글에서는 높이 값을 그대로 읽도록 회색조 팔레트로 바꿉니다. 네 맵의 높이 값은 대략 0~120이므로 결과 이미지가 어둡게 보이는 것이 정상입니다.
BMP로 저장하고 결과 검증하기
출력 형식은 압축하지 않은 8비트 BMP입니다. 파일 헤더와 정보 헤더, 256색 팔레트, 픽셀 데이터를 씁니다. BMP 팔레트는 RGB가 아니라 BGR 순서로 저장하며, 색마다 4바이트를 사용합니다. 양수 높이를 지정하면 픽셀 행을 아래에서 위 순서로 기록해야 하고, 행 크기는 4바이트 배수로 맞춥니다. 코드는 리틀엔디언 값을 바이트 단위로 기록해 CPU의 바이트 순서에 기대지 않습니다. 1024픽셀 너비에서는 행 패딩이 필요하지 않습니다.
전체 도구는 파일의 서명과 크기를 읽고, RLE를 풀고, 팔레트를 복사한 뒤 BMP를 저장하는 약 150줄의 ANSI C 프로그램입니다. GIF로 바로 저장하지 않은 이유는 GIF의 LZW 인코더가 별도 구현을 요구하기 때문입니다. 8비트 BMP는 256색 팔레트를 그대로 담으므로 ImageMagick 같은 도구로 변환해도 색 정보가 손실되지 않습니다.
디코딩 결과는 기존에 온라인에 유통되던 맵과 대조했습니다. D1.DTA의 높이 맵은 온라인 map0과 MD5 해시까지 일치합니다. C4.DTA의 색상 맵도 map3과 모든 픽셀의 RGB 값이 같습니다. 나머지 맵은 물 영역에서 차이가 납니다. 온라인 버전은 호수와 강에 색상 순환 애니메이션용으로 보이는 팔레트 항목 251~254를 사용하며, 일부 높이 값도 1만큼 다릅니다. 원본 1992년판과 이후 판본의 데이터가 달라졌을 가능성을 보여줍니다.
지형 제작 방식
1994년 잡지 프로필에 따르면 Freeman은 500만~600만 폴리곤으로 모델링해도 원하는 현실감이 나오지 않자 직접 지형 제작 도구를 만들었습니다. 지형을 점토처럼 손으로 다듬은 뒤, 수백만 년의 침식과 지진, 풍화 과정을 시뮬레이션하는 루틴을 적용했습니다. 당시 제작 도구의 각 복셀에는 높이와 색상뿐 아니라 암석, 토양, 물의 밀도 정보도 들어갔습니다. 침식 알고리즘은 재료 밀도를 고려해 부드러운 토양은 깎고 단단한 암석은 남겼습니다. 게임에 실린 DTA 파일에는 이 정보가 남지 않고, 지점마다 색상과 높이를 나타내는 8비트 값만 저장됐습니다.
작성자는 예상과 달리 복잡한 자체 압축 형식이 아니라 널리 쓰이던 PCX가 발견됐다고 설명합니다. 당시 개발자들은 이미 존재하는 그래픽 도구와 파일 형식을 활용하고, 렌더러처럼 필요한 부분에만 직접 기술을 투입했습니다. 오래된 파일을 분석할 때는 앞부분의 바이트, 크기와 개수로 보이는 값, 당시 널리 쓰인 형식의 흔적부터 살펴보는 접근을 보여줍니다.
Lobsters 반응
- @bitshift — 높이 맵 파일에도 팔레트가 들어 있다는 점이 재미있었습니다. 단순한 회색조가 아닙니다. 파란색, 주황색 등 16단계 색상 띠로 나뉘고, 위쪽에는 회색 띠가 있으며 쓰지 않는 항목은 자홍색입니다. 이 팔레트를 적용해 D 파일을 그림 편집 프로그램에서 열면 색상 띠마다 높이 16단위를 나타내는 등고선 지도가 됩니다. Kyle Freeman에게 확인해 보겠지만, NovaLogic의 아티스트도 지형 파일을 그리고 확인할 때 바로 이런 화면을 썼을 것 같습니다. 이 얘기를 해놓고 스크린샷을 안 올리면 안 되죠! :-)
- @gustavo-pezzi — 올렸습니다!
원문: Pikuma / 번역·요약: Trawling