background: add support for 7shades output

This commit is contained in:
Zack Buhman 2026-01-05 19:44:02 -06:00
parent 0f4f1b69a9
commit 134edfa010
2 changed files with 201 additions and 76 deletions

31
7shades_binary_spec.txt Normal file
View File

@ -0,0 +1,31 @@
- 32 byte header that is read as 8 longword values as follows:
struct {
uint32_t file_type_id; // must be the constant value "0x00000005"
uint32_t size_of_cel_data; // bytes
uint32_t size_of_map_data; // bytes
uint32_t tile_character_size; // (slChar) one of CHAR_SIZE_*
uint32_t tile_color_mode; // (slChar) one of COL_TYPE_*
uint32_t plane_size; // (slPlane) one of PL_SIZE_*
uint32_t map_data; // (slPage) one of PNB_*, one of CN_*
uint16_t height; // must be the constant value "1"
uint16_t width; // must be the constant value "1"
}
-A file type ID - arbitrarily chosen as [5] for this binary type
-# of bytes of Cel Data in binary
-# of bytes of Map Data in binary
-Tile Character Size (Macros defined in sl_def.h)
-Tile Character Color Mode (Macros defined in sl_def.h)
-Plane Unit Size (Macros defined in sl_def.h)
-Map data config (Macros defined in sl_def.h)
-Plane dimensions (Height<<16|Width)
- Pallet data block of 0, 32, or 512 bytes based on the color mode indicated in header
- Map data block of size indicated in header
- Cel data block of size indicated in header

View File

@ -1,3 +1,4 @@
import io
import sys
from pprint import pprint, pformat
import textwrap
@ -27,21 +28,18 @@ def pack_old_palette_chunk(old_palette_chunk):
for color in old_palette_chunk.packets[0].colors:
f.write(pack_bgr555(*color))
def pack_palette_chunk(palette_chunk):
with open("palette.bin", "wb") as f:
def pack_palette_chunk(f, palette_chunk):
assert palette_chunk.first_color_index_to_change == 0
for entry in palette_chunk.entries:
color = (entry.red, entry.green, entry.blue)
f.write(pack_bgr555(*color))
print("palette.bin", f.tell(), file=sys.stderr)
def pack_palette(palette):
def pack_palette(f, palette):
if type(palette) is PaletteChunk:
pack_palette_chunk(palette)
pack_palette_chunk(f, palette)
elif type(palette) is OldPaletteChunk:
pack_old_palette_chunk(palette)
pack_old_palette_chunk(f, palette)
else:
assert False, type(palette)
@ -82,8 +80,7 @@ def pack_character_1x1(tileset_chunk, offset):
return bytes(buf)
def pack_character_patterns(filename, tileset_chunk):
with open(filename, "wb") as f:
def pack_character_patterns(f, tileset_chunk):
for i in range(tileset_chunk.number_of_tiles):
offset = tileset_chunk.tile_width * tileset_chunk.tile_height * i
@ -96,10 +93,7 @@ def pack_character_patterns(filename, tileset_chunk):
f.write(buf)
print(filename, f.tell(), file=sys.stderr)
def pack_pattern_name_table(filename, cel_chunk, x_cells, y_cells):
with open(filename, "wb") as f:
def pack_pattern_name_table(f, cel_chunk, x_cells, y_cells):
assert type(cel_chunk.data) == CelChunk_CompressedTilemap
#assert cel_chunk.data.width_in_number_of_tiles <= 64
#assert cel_chunk.data.height_in_number_of_tiles <= 64
@ -139,22 +133,19 @@ def pack_pattern_name_table(filename, cel_chunk, x_cells, y_cells):
f.write(pack_index(pattern))
print(filename, f.tell(), file=sys.stderr)
def generate_separate_files(mem):
tilesets, layers, palette, cel_chunks = parse_file(mem)
with open(sys.argv[1], 'rb') as f:
buf = f.read()
mem = memoryview(buf)
with open("palette.bin", "wb") as f:
pack_palette(f, palette)
tilesets, layers, palette, cel_chunks = parse_file(buf)
pack_palette(palette)
for tileset_index, tileset_chunk in sorted(tilesets.items(), key=itemgetter(0)):
for tileset_index, tileset_chunk in sorted(tilesets.items(), key=itemgetter(0)):
filename = f"character_pattern__tileset_{tileset_index}.bin"
pack_character_patterns(filename, tileset_chunk)
print(filename)
with open(filename, "wb") as f:
pack_character_patterns(f, tileset_chunk)
for layer_index, cel_chunk in sorted(cel_chunks.items(), key=itemgetter(0)):
filename = f"pattern_name_table__layer_{layer_index}.bin"
for layer_index, cel_chunk in sorted(cel_chunks.items(), key=itemgetter(0)):
#layers[layer_index]
print(f"layer={layer_index} layer_name={layers[layer_index].layer_name} tileset={layers[layer_index].tileset_index}");
tileset_chunk = tilesets[layers[layer_index].tileset_index]
@ -162,7 +153,110 @@ for layer_index, cel_chunk in sorted(cel_chunks.items(), key=itemgetter(0)):
x_cells = 64 // (tileset_chunk.tile_width // 8)
y_cells = 64 // (tileset_chunk.tile_height // 8)
pack_pattern_name_table(filename, cel_chunk, x_cells, y_cells)
filename = f"pattern_name_table__layer_{layer_index}.bin"
print(filename)
with open(filename, "wb") as f:
pack_pattern_name_table(f, cel_chunk, x_cells, y_cells)
#for layer_index, layer_chunk in enumerate(layers):
# print(f"layer={layer_index} layer_name={layer_chunk.layer_name} tileset={layer_chunk.tileset_index}");
PNCN0__N0PNB__2WORD = (0 << 15) # PNB_2WORD
PNCN0__N0PNB__1WORD = (1 << 15) # PNB_1WORD
PNCN0__N0CNSM = (1 << 14) # CN_12BIT
PLSZ__N0PLSZ__1x1 = (0b00 << 0) # PL_SIZE_1x1
PLSZ__N0PLSZ__2x1 = (0b01 << 0) # PL_SIZE_2x1
PLSZ__N0PLSZ__2x2 = (0b11 << 0) # PL_SIZE_2x2
CHCTLA__N0CHCN__16_COLOR = (0b000 << 4) # COL_TYPE_16
CHCTLA__N0CHCN__256_COLOR = (0b001 << 4) # COL_TYPE_256
CHCTLA__N0CHCN__2048_COLOR = (0b010 << 4) # COL_TYPE_2048
CHCTLA__N0CHCN__32K_COLOR = (0b011 << 4) # COL_TYPE_32768
CHCTLA__N0CHCN__16M_COLOR = (0b100 << 4) # COL_TYPE_1M
CHCTLA__N0CHSZ__1x1_CELL = (0 << 0)
CHCTLA__N0CHSZ__2x2_CELL = (1 << 0)
def header_7shades(size_of_cel_data, # bytes
size_of_map_data, # bytes
tile_character_size, # CHCTLA__N0CHSZ
tile_color_mode, # CHCTLA__N0CHCN
plane_size, # PLSZ
map_data, # PNCN0
): #
file_type_id = 0x5
width = 1
height = 1
return struct.pack(">IIIIIIIHH",
file_type_id,
size_of_cel_data,
size_of_map_data,
tile_character_size,
tile_color_mode,
plane_size,
map_data,
width,
height)
def palette_pad_7shades(f_tmp, palette_size):
# - Pallet data block of 0, 32, or 512 bytes based on the color mode indicated in header
for valid_size in [32, 512]:
if palette_size > valid_size:
continue
while palette_size < valid_size:
f_tmp.write(bytes([0]))
palette_size += 1
break
else:
assert False, palette_size
return palette_size
def generate_7shades(mem):
tilesets, layers, palette, cel_chunks = parse_file(mem)
f_tmp = io.BytesIO()
pack_palette(f_tmp, palette)
palette_size = f_tmp.getbuffer().nbytes
palette_size = palette_pad_7shades(f_tmp, palette_size)
print("palette_size", palette_size)
character_patterns_start = f_tmp.getbuffer().nbytes
for tileset_index, tileset_chunk in sorted(tilesets.items(), key=itemgetter(0)):
pack_character_patterns(f_tmp, tileset_chunk)
break
character_patterns_end = f_tmp.getbuffer().nbytes
size_of_cel_data = character_patterns_end - character_patterns_start
print("character_patterns_size", size_of_cel_data)
pattern_name_tables_start = f_tmp.getbuffer().nbytes
for layer_index, cel_chunk in sorted(cel_chunks.items(), key=itemgetter(0)):
tileset_chunk = tilesets[layers[layer_index].tileset_index]
x_cells = 64 // (tileset_chunk.tile_width // 8)
y_cells = 64 // (tileset_chunk.tile_height // 8)
pack_pattern_name_table(f_tmp, cel_chunk, x_cells, y_cells)
pattern_name_tables_end = f_tmp.getbuffer().nbytes
size_of_map_data = pattern_name_tables_end - pattern_name_tables_start
print("pattern_name_table_size", size_of_map_data)
tile_character_size = CHCTLA__N0CHSZ__2x2_CELL
tile_color_mode = CHCTLA__N0CHCN__256_COLOR
plane_size = PLSZ__N0PLSZ__2x2
map_data = PNCN0__N0PNB__2WORD
header = header_7shades(size_of_cel_data,
size_of_map_data,
tile_character_size,
tile_color_mode,
plane_size,
map_data)
with open("7shades.bin", "wb") as f:
f.write(header)
f.write(f_tmp.getvalue())
with open(sys.argv[1], 'rb') as f:
buf = f.read()
mem = memoryview(buf)
#generate_separate_files(mem)
generate_7shades(mem)