bqio, по коду могу добавить.
CRAM_PATH = join("data", "cram.bin") можно записать как CRAM_PATH = "data/cram.bin", косая черта работает везде (Win, Linux) в отличие от обратной косой (возможно есть нюансы), ещё удобно использовать pathlib вместо os.path: CRAM_PATH = Path("data") / "cram.bin".
Вместо:
_tile.write(pk("<B", pixel[0]))
_tile.write(pk("<B", pixel[1]))
_tile.write(pk("<B", pixel[2]))
Проще _tile.write(pk("<BBB", *pixel)) или _tile.write(bytes(pixel)), а bytearray() вместо BytesIO() в данном случае, но по скорости не знаю.
Если записывать в tile_apply_pallete() не [(R,G,B),(R,G,B),...], а [R, G, B, R, G, B, ...], то вместо tile_to_buf() остаётся tile_buf = bytes(tile), в таком случае можно хранить цвета палитры как bytes, тогда в tile_apply_pallete() можно _tile = bytearray(); for pal_idx in tile: _tile += pallete[pal_idx >> 4] + pallete[pal_idx & 0xF]; return _tile, или сделать палитру сразу для двух цветов, чтобы _tile += pallete[pal_idx].
Можно не применять палитру вручную, а сохранять как индексированный PNG, тогда остаётся сделать преобразование индексов 0b12345678 > 0b00001234 0b00005678 и указать палитру.
UPD: Ещё заметил pallete_4bpp_to_rgb(up("<H", fp.read(2))[0]), т. е. в файле у вас палитра неправильная, потому что: hex(unpack("<H", b"\x01\x23")[0]) # = 0x2301.
Я бы всё писал проще в одном файле, для одноразового кода нормально
![Улыбка :)](//www.emu-land.net/forum/Smileys/default/smiley.gif)