닌텐도 DS모르는 용어는 용어집에서
17장 그림과 글자
DS의 2D 그림도 타일과 팔레트지만 픽셀을 바이트에 붙여 담습니다. 게임이 정한 그림 형식과 폰트를 읽고, 2비트 픽셀 넷의 순서를 그려 보며 찾습니다.
DS의 2D 그림도 타일과 팔레트 쉽게
DS에는 2D 엔진(2D 그림을 그리는 회로)이 A·B 둘 있어 화면을 하나씩 맡습니다(어느 쪽이 위 화면인지는 바꿀 수 있습니다). 원리는 슈퍼 패미콤과 같습니다(7장). 화면은 주로 8×8픽셀 타일을 이어 붙여 그리고(타일 없이 픽셀을 줄줄이 놓는 비트맵 화면 모드도 있습니다), 타일의 픽셀은 색 번호를, 팔레트는 그 번호가 실제로 무슨 색인지를 정합니다. 색 하나는 2바이트로 빨강·초록·파랑을 5비트씩 적고, 팔레트의 0번 색은 투명입니다.
다른 점은 픽셀을 바이트에 담는 방법입니다. 슈퍼 패미콤은 비트평면이라 한 픽셀의 비트가 여러 바이트에 흩어집니다. DS는 한 픽셀의 비트를 한 바이트 안에 붙여 담습니다. 4bpp(16색)면 바이트 하나에 픽셀 둘, 아래 4비트가 왼쪽 픽셀입니다. 8bpp(256색)면 바이트 하나가 픽셀 하나입니다. 그래서 타일 한 줄을 헥스 에디터로 보면 색 번호가 거의 그대로 보입니다.
게임이 정한 그림 형식 쉽게
롬 안의 그림이 늘 하드웨어 타일 그대로 들어 있지는 않습니다. 게임은 그림을 자기 형식으로 담아 두고, 화면에 낼 때 VRAM에 맞게 옮겨 놓습니다. 그래서 그림 파일을 고치려면 그 게임의 형식부터 알아야 합니다.
고스트 트릭의 타이틀 위 화면은 그래픽 묶음 cpac_3d.bin 안의 한 항목입니다. LZ11(16장)을 풀면 sdt.로 시작하는 그림이 나옵니다. 헤더 0x0C부터 4바이트씩 픽셀 위치(0x24), 픽셀 크기(0xC000), 팔레트 위치(0xC024), 팔레트 크기(0x200), 가로 256, 세로 192가 적혀 있습니다. 픽셀 하나가 1바이트(8bpp)라 256 × 192 = 0xC000바이트이고, 팔레트는 256색 × 2바이트 = 0x200바이트입니다. 타일로 자르지 않고 한 줄씩 늘어놓은 그림입니다(노트).
폰트: 글자 하나 = 그림 한 장 쉽게
대사의 2바이트 값(16장)은 폰트 파일에서 몇 번째 그림인지를 가리킵니다. 고스트 트릭에는 대사 폰트가 세 개 있습니다. 일본판에서 셋은 0x00–0x3F(숫자와 영문)만 같고, 그 뒤는 다릅니다. 대화창·말풍선 폰트는 0x40부터 히라가나인데 font_db.imb는 악센트 붙은 라틴 문자입니다. 한글패치 v1.1은 세 폰트의 0x12C–0xA59에 같은 한글을 같은 순서로 그려, 한글 번호는 어느 폰트에서나 같은 글자가 되게 했습니다.
| 파일 | 쓰는 곳 | 글자 모양 | 일본판 | 한글패치 |
|---|---|---|---|---|
fonts2bpp_new.imb | 대화창 | 16×24, 2bpp, 글자당 96바이트 | 1,350자(129,600바이트) | 3,203자(307,488바이트) |
font_db.imb | 모름(확인하지 않음) | 16×16, 4bpp, 글자당 128바이트 | 256자(32,768바이트) | 3,203자(409,984바이트) |
balloon_font.imb | 말풍선 | 16×16, 4bpp, 글자당 128바이트 | 256자(32,768바이트) | 3,203자(409,984바이트) |
세 폰트 모두 8×8 타일이 아니라 글자 하나를 한 줄씩 늘어놓은 비트맵(선형 비트맵)입니다. 대화창 폰트는 16픽셀 한 줄이 2bpp로 4바이트, 24줄이라 글자 하나가 96바이트입니다. 글자 번호 × 96이 그 글자의 위치라 「가」(0x12C)는 0x7080에 있습니다. 대사를 읽던 글자표와 폰트 그림 순서가 같은 번호를 쓰므로, 둘 중 하나만 바꾸면 엉뚱한 글자가 나옵니다.
2비트 픽셀 넷의 순서 찾기 쉽게
2bpp면 바이트 하나에 픽셀 넷이 들어갑니다. 어느 2비트가 몇 번째 픽셀인지는 게임이 정하기 나름입니다. DS 하드웨어의 4bpp처럼 아래 비트부터일 수도, 반대일 수도 있습니다. 고스트 트릭 대화창 폰트는 둘 다 아닙니다. 위 4비트가 앞 두 픽셀이고, 4비트 안에서는 아래 2비트가 먼저라, 픽셀 0–3이 비트 4, 6, 0, 2에서 시작합니다.
순서는 그려 보면 압니다. 글자는 굵은 획이라 순서가 맞으면 가로로 이웃한 픽셀 값이 비슷하게 이어지고, 틀리면 획이 세로 줄무늬로 갈라집니다. 아래에서 순서를 바꿔 보세요. 처음 순서(위 비트부터)는 툴킷 krpatch linear --order msb와 같습니다.
더 깊이24가지를 모두 그려 고르는 법
2비트 칸 넷을 픽셀 넷에 놓는 방법은 4! = 24가지입니다. 사람이 보고 고를 수도 있지만, 「가로로 이웃한 픽셀 값 차이의 합」처럼 매끄러움을 숫자로 재면 폰트 전체로 확인할 수 있습니다. 한글 2,350자 전체로 재도 (4, 6, 0, 2)가 가장 작고, 다음 순서(2, 0, 6, 4)보다 약 5% 작습니다. 위젯의 네 글자만으로 재면 2위는 (4, 0, 6, 2)로 다릅니다. 찾은 순서로 픽셀을 읽는 식은 이렇습니다.
pixel(x, y) = (data[글자 × 96 + y × 4 + x ÷ 4] >> SHIFT[x mod 4]) & 3
SHIFT = (4, 6, 0, 2)2비트는 네 단계라 바탕(0)과 글자(3) 사이에 중간 농도 둘(1, 2)을 써서 획 가장자리를 부드럽게 합니다. 16×16 4bpp인 font_db.imb와 balloon_font.imb는 DS 하드웨어처럼 아래 4비트가 앞 픽셀이라 --order lsb로 바로 그려집니다.
실제 패치에서 실제 패치에서
고스트 트릭 v1.1: 폰트 세 개를 3,203자로 늘리기
일본판 대화창 폰트는 1,350자, 나머지 두 폰트는 256자였습니다. v1.1은 세 폰트를 모두 3,203자로 맞추고 0x12C부터 한글 2,350자를 KS X 1001 순서로 그렸습니다(노트). 폰트 파일이 커졌으니 롬을 다시 묶을 때 자리를 옮겨야 했습니다(15장). 0xA5A부터의 553자는 v1.2 대본에서는 쓰지 않습니다.
고스트 트릭 v1.2: 그림은 하나만
v1.2는 폰트를 v1.1 그대로 두고, 그림은 타이틀 위 화면 하나만 고쳤습니다. v1.1이 137–177줄에 그려 넣은 안내 문구를 일본판 같은 그림의 픽셀로 되돌렸습니다. v1.1이 팔레트를 바꾸지 않아서 팔레트는 그대로 두고 픽셀의 색 번호만 바꿨습니다(노트).
직접 해 보기 직접 해 보기
4bpp 폰트는 툴킷으로 바로 그립니다. 대화창 폰트는 lsb·msb 어느 쪽으로 그려도 줄무늬가 생깁니다.
krpatch nds unpack game.nds work/
krpatch linear work/data/font_db.imb font_db.png --size 16x16 --bpp 4 --order lsb --count 256
krpatch linear work/data/fonts2bpp_new.imb wrong.png --size 16x24 --bpp 2 --order msb --count 256대화창 폰트는 찾은 순서로 직접 읽습니다. 한글패치 롬이면 0x12C는 「가」입니다.
>>> data = open('work/data/fonts2bpp_new.imb', 'rb').read()
>>> SHIFT = (4, 6, 0, 2)
>>> glyph = 0x12C
>>> for y in range(24):
... print(''.join(' .:#'[(data[glyph * 96 + y * 4 + x // 4] >> SHIFT[x % 4]) & 3] for x in range(16)))실제 패치에서 이 개념이 쓰인 롬 분석 노트
직접 해 보기 kr-patch-toolkit 명령 설명
참고 자료
- GBATEK, LCD VRAM Character Data — 8×8 타일 하나가 4bpp면 32바이트, 8bpp면 64바이트이고, 4bpp는 바이트의 아래 4비트가 왼쪽 픽셀이라는 점
- GBATEK, LCD Color Palettes — 색 하나 2바이트(빨강·초록·파랑 5비트씩), BG·OBJ 팔레트가 각각 512바이트·256색, 0번 색은 투명
- kr-patch-toolkit, linear.py — 타일이 아니라 한 줄씩 저장한 비트맵을 lsb·msb 순서로 그리는 명령
- kr-patches, 고스트 트릭 tools/font_store.py — 대화창 폰트 16×24 2bpp와 픽셀 순서 SHIFT = (4, 6, 0, 2)
- kr-patches, 고스트 트릭 docs/ANALYSIS.md — 폰트 세 개의 크기와 글자 수, 글자 번호 나누기, 타이틀 그림 sdt. 헤더
댓글