-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathchr2png.py
82 lines (66 loc) · 2.7 KB
/
chr2png.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © 2018 Taylor C. Richberger <[email protected]>
# This code is released under the license described in the LICENSE file
from __future__ import division, absolute_import, print_function, unicode_literals
from itertools import count, chain
import sys
import locale
import argparse
import png
def bytetobits(byte):
'''Takes a byte (as an int) and returns an iterator to iterate per-bit'''
# All bits from large to small (left-to-right)
for bit in list(reversed(range(8))):
yield bool(byte & (1 << bit))
def processtile(rawtile):
'''Take in a raw 16-byte array for a tile and process it into its final
form. Yield index numbers. This is a generator that yields generators of
rows.'''
rawtile = list(rawtile)
# generator for a plane. Each plane is a generator of generators, where
# each inner generator yields a row bit-by-bit
first = (bytetobits(byte) for byte in rawtile[:8])
second = (bytetobits(byte) for byte in rawtile[8:])
# yield row generators. First is bit 0
for row0, row1 in zip(first, second):
yield (int(bit0) | (int(bit1) << 1) for bit0, bit1 in zip(row0, row1))
def main():
parser = argparse.ArgumentParser(description='Convert from CHR to PNG')
parser.add_argument('-V', '--version', action='version', version='0.1.0')
parser.add_argument('-i', '--input', help='Input CHR file (default stdin)',
type=argparse.FileType('rb'), default=sys.stdin.buffer)
parser.add_argument('-o', '--output', help='Output PNG file (default stdout)',
type=argparse.FileType('wb'), default=sys.stdout.buffer)
args = parser.parse_args()
tiles = []
for tilenum in count():
rawtile = args.input.read(16)
if len(rawtile) == 0:
break
if len(rawtile) != 16:
raise RuntimeError('Input file not a multiple of 16 bytes')
tiles.append(processtile(rawtile))
palette = (
# Black
(0x00, 0x00, 0x00, 0xff),
# Red
(0xff, 0x00, 0x00, 0xff),
# Green
(0x00, 0xff, 0x00, 0xff),
# Blue
(0x00, 0x00, 0xff, 0xff),
)
rowlist = []
for y in range(32):
starttile = y * 16
tilerow = tiles[starttile:starttile + 16]
# This is a little mystical. Basically, it uses zip to interleave
# individual rows in each tile in the row, and then chain to combine
# them, so that you get full-length PNG rows
rowlist.extend(chain(*rows) for rows in zip(*tilerow))
writer = png.Writer(128, 256, palette=palette, bitdepth=2)
writer.write(args.output, rowlist)
if __name__ == '__main__':
locale.setlocale(locale.LC_ALL, '')
main()