banner.bin and banner_s.bin are both found in the /FONT/ folder. They contain data for the dungeon title font.
File Structure
The file uses SIR0 headers to store its pointers. General SIR0 details can be found in the main SIR0 documentation. The sections below will cover only banner.bin-specific blocks of data.
Name | Offset | Size (Per Element) | # of Elements | Description |
---|---|---|---|---|
SIR0 Header | 0x00 | 16 Bytes | 1 | Details in the SIR0 documentation |
Glyph Textures | Pointed by Glyph Metadata | Varies | Specified by Content Header | Each element is read nibble-by-nibble to assemble the glyph. |
Glyph Metadata | Pointed by Content Header | 8 Bytes | Specified by Content Header | Each element contains a pointer to the glyph texture, the letter it represents, and how many pixels wide the letter is. |
Content Header | Pointed by SIR0 Header | 12 Bytes | 1 | Contains the pointer to Glyph Metadata, and the number of elements in that data block. |
Pointer Offsets List | Pointed by SIR0 Header | 1 Byte | Varies | Details in the SIR0 documentation |
SIR0 Padding | After Pointer Offsets List | Varies | --- | Details in the SIR0 documentation |
Content Header
3 Sections containing 4 bytes each:
AA AA AA AA BB BB BB BB CC CC CC CC
A. Pointer to the start of the Glyph Metadata block. Little-Endian.
B. Pointer to the number of elements in the Glyph Metadata block. Little-Endian.
C. Unknown.
Glyph Metadata
This is an array of elements, each 8 bytes. Contains 3 sections:
AA AA AA AA BB BB CC CC
A. Pointer to this element's texture data in the Glyph Textures block. Little-Endian.
B. The letter that the glyph represents. Little-Endian. The encoding varies by language, which is covered in the strings documentation.
C. The number of pixels to move the cursor to the right, when drawing the text in-game. Little-Endian. Appears to be signed.
Glyph Textures
Individual elements in the Glyph Textures block have a variable length. Data is read nibble-by-nibble:
First, the high bit of the nibble is checked.
If it is 1, then the game will read the next nibble as pixel data and draw it N times. N is the other 3 bits of the first nibble.
If it is 0, then the game will read the next N nibbles as pixel data and draw them in that order. N is the other 3 bits of the first nibble.
The game draws pixels in order of left to right, top to bottom. It moves on to the next row when 24 pixels have been drawn, and ends the parsing when 24 rows have been drawn.
Parsing
For convenience, the following python code is provided below to parse these files:
import sys
import os
import re
import shutil
import math
import struct
import glob
from PIL import Image
GLYPH_SIZE_D = 24
PALETTE_MAP = [0,8,24,41,57,74,90,107,123,140,156,173,189,206,214,231]
def ReadNextNib(reader, next_nibbles):
if len(next_nibbles) > 0:
return next_nibbles.pop()
byte = int.from_bytes(reader.read(1), 'little')
next_nibbles.append(byte & 15)
return byte >> 4
def ExportDChar(reader, char_val):
pixels = []
next_nibbles = []
while len(pixels) < GLYPH_SIZE_D * GLYPH_SIZE_D:
control_nib = ReadNextNib(reader, next_nibbles)
draw_repeat = control_nib >> 3
draw_amount = control_nib & 7
if draw_repeat == 1:
pixel_nib = ReadNextNib(reader, next_nibbles)
pixel_val = PALETTE_MAP[pixel_nib]
for ii in range(draw_amount):
pixels.append(pixel_val)
else:
for ii in range(draw_amount):
pixel_nib = ReadNextNib(reader, next_nibbles)
pixel_val = PALETTE_MAP[pixel_nib]
pixels.append(pixel_val)
return_img = Image.new('RGBA', (GLYPH_SIZE_D, GLYPH_SIZE_D), (0, 0, 0, 0))
datas = [(0, 0, 0, 0)] * (GLYPH_SIZE_D * GLYPH_SIZE_D)
for yy in range(GLYPH_SIZE_D):
for xx in range(GLYPH_SIZE_D):
pixel_idx = yy * GLYPH_SIZE_D + xx
if pixels[pixel_idx] > 0:
datas[pixel_idx] = (255,255,255,pixels[pixel_idx])
return_img.putdata(datas)
return return_img
def ReadChars(reader, amt):
return_imgs = []
for ii in range(amt):
glyph_ptr = int.from_bytes(reader.read(4), 'little')
glyph_val = int.from_bytes(reader.read(2), 'little')
advance = int.from_bytes(reader.read(2), 'little')
cur_ptr = reader.tell()
reader.seek(glyph_ptr)
return_img = ExportDChar(reader, glyph_val)
return_imgs.append((return_img, glyph_val, advance))
reader.seek(cur_ptr)
return return_imgs
def ReadFont(input_file):
output_parent, basename = os.path.split(input_file)
dir, _ = os.path.splitext(basename)
output_dir = os.path.join(output_parent, dir)
if not os.path.exists(output_dir):
os.mkdir(output_dir)
with open(input_file, "rb") as reader:
##Read SIR0 Header: ptr to font header
reader.seek(4)
font_ptr = int.from_bytes(reader.read(4),'little')
##Read font header: ptr to charmap, amount of chars
reader.seek(font_ptr)
list_ptr = int.from_bytes(reader.read(4),'little')
char_amt = int.from_bytes(reader.read(4),'little')
_ = int.from_bytes(reader.read(4), 'little')#don't know what this is
reader.seek(list_ptr)
imgs = ReadChars(reader, char_amt, mode)
for idx, img_pair in enumerate(imgs):
img = img_pair[0]
code = img_pair[1]
advance = img_pair[2]
if advance > 0:
img = img.crop((0,0,advance,GLYPH_SIZE_D))
img.save(os.path.join(output_dir, str(code) + '.png'))
- 1
Recommended Comments
There are no comments to display.