all repos — mgba @ ee6bbaf61c0890552b103e2a577eb94f9d453c77

mGBA Game Boy Advance Emulator

tools/make-dotcode.py (view raw)

  1import numpy as np
  2import PIL.Image
  3import PIL.ImageChops
  4import sys
  5
  6blocksize = 104
  7pow = [0] * 256
  8rev = [0] * 256
  9rev[0] = 0xFF
 10x = 1
 11for i in range(0xFF):
 12    pow[i]=x
 13    rev[x]=i
 14    x *= 2
 15    if x >= 0x100:
 16        x ^= 0x187
 17gg = [0] * 16
 18gg[0] = pow[0x78]
 19for i in range(16):
 20    gg[i] = 1
 21    for j in range(i + 1):
 22        j = i - j
 23        if j == 0:
 24            y = 0
 25        else:
 26            y = gg[j - 1]
 27        x = gg[j]
 28        if x != 0:
 29            x = rev[x] + 0x78 + i
 30            if x >= 0xFF:
 31                x -= 0xFF
 32            y ^= pow[x]
 33        gg[j] = y
 34for i in range(16):
 35    gg[i] = rev[gg[i]]
 36
 37print(*[hex(x) for x in gg])
 38
 39def interleave(data, header):
 40    data = data.reshape([-1, 48]).T
 41    new_data = np.zeros((64, data.shape[1]), dtype=data.dtype)
 42    for i, row in enumerate(data.T):
 43        new_data[:,i] = rs(row)
 44    data = new_data.reshape([-1])
 45    new_data = np.zeros(data.shape[0] + (102 - data.shape[0] % 102), dtype=data.dtype)
 46    new_data[:data.shape[0]] = data
 47    new_data = new_data.reshape([-1, 102])
 48    data = new_data
 49    new_data = np.zeros((data.shape[0], 104), dtype=data.dtype)
 50    new_data[:,2:] = data
 51    for i in range(new_data.shape[0]):
 52        x = (i * 2) % len(header)
 53        new_data[i,:2] = header[x:x + 2]
 54    data = new_data.reshape([-1])
 55    return data
 56
 57def rs(data):
 58    new_data = np.zeros(data.shape[0] + 16, dtype=data.dtype)
 59    new_data[:data.shape[0]] = data
 60    new_data = np.flipud(new_data)
 61    for i in range(data.shape[0]):
 62        i = new_data.shape[0] - i - 1
 63        z = rev[new_data[i] ^ new_data[15]]
 64        for j in range(16):
 65            j = 15 - j
 66            if j == 0:
 67                x = 0
 68            else:
 69                x = new_data[j - 1]
 70            if z != 0xFF:
 71                y = gg[j]
 72                if y != 0xFF:
 73                    y += z
 74                    if y >= 0xFF:
 75                        y -= 0xFF
 76                    x ^= pow[y]
 77            new_data[j] = x
 78    new_data[:16] = ~new_data[:16]
 79    new_data = np.flipud(new_data)
 80    return new_data
 81
 82def bin2raw(data):
 83    if len(data) == 1344:
 84        header = np.array([0x00, 0x02, 0x00, 0x01, 0x40, 0x10, 0x00, 0x1C], dtype=np.uint8)
 85    else:
 86        header = np.array([0x00, 0x03, 0x00, 0x19, 0x40, 0x10, 0x00, 0x2C], dtype=np.uint8)
 87    header = rs(header)
 88    new_data = interleave(np.frombuffer(data, np.uint8), header)
 89    return new_data.tobytes()
 90
 91with open(sys.argv[1], 'rb') as f:
 92    data = f.read()
 93size = len(data)
 94
 95if size in (1344, 2112):
 96    data = bin2raw(data)
 97    size = len(data)
 98    with open('dotcode.raw', 'wb') as f:
 99        f.write(data)
100
101blocks = size // blocksize
102height = 36
103width = 35
104margin = 2
105
106dots = np.zeros((width * blocks + margin * 2 + 1, height + margin * 2), dtype=np.bool)
107anchor = np.array([[0, 1, 1, 1, 0],
108                   [1, 1, 1, 1, 1],
109                   [1, 1, 1, 1, 1],
110                   [1, 1, 1, 1, 1],
111                   [0, 1, 1, 1, 0]], dtype=np.bool)
112alignment = np.array([1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], dtype=np.bool)
113nybbles = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 1], [0, 0, 0, 1, 0], [1, 0, 0, 1, 0],
114           [0, 0, 1, 0, 0], [0, 0, 1, 0, 1], [0, 0, 1, 1, 0], [1, 0, 1, 1, 0],
115           [0, 1, 0, 0, 0], [0, 1, 0, 0, 1], [0, 1, 0, 1, 0], [1, 0, 1, 0, 0],
116           [0, 1, 1, 0, 0], [0, 1, 1, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 0]]
117addr = [0x03FF]
118for i in range(1, 54):
119    addr.append(addr[i - 1] ^ ((i & -i) * 0x769))
120    if (i & 0x07) == 0:
121        addr[i] ^= 0x769
122    if (i & 0x0F) == 0:
123        addr[i] ^= 0x769 << 1
124    if (i & 0x1F) == 0:
125        addr[i] ^= (0x769 << 2) ^ 0x769
126
127base = 1 if blocks == 18 else 25
128for i in range(blocks + 1):
129    dots[i * width:i * width + 5, 0:5] = anchor
130    dots[i * width:i * width + 5, height + margin * 2 - 5:height + margin * 2] = anchor
131    dots[i * width + margin, margin + 5] = 1
132    a = addr[base + i]
133    for j in range(16):
134        dots[i * width + margin, margin + 14 + j] = a & (1 << (15 - j))
135for i in range(blocks):
136    dots[i * width:(i + 1) * width, margin] = alignment
137    dots[i * width:(i + 1) * width, height + margin - 1] = alignment
138    block = []
139    for byte in data[i * blocksize:(i + 1) * blocksize]:
140        block.extend(nybbles[byte >> 4])
141        block.extend(nybbles[byte & 0xF])
142    j = 0
143    for y in range(3):
144        dots[i * width + margin + 5:i * width + margin + 31, margin + 2 + y] = block[j:j + 26]
145        j += 26
146    for y in range(26):
147        dots[i * width + margin + 1:i * width + margin + 35, margin + 5 + y] = block[j:j + 34]
148        j += 34
149    for y in range(3):
150        dots[i * width + margin + 5:i * width + margin + 31, margin + 31 + y] = block[j:j + 26]
151        j += 26
152im = PIL.Image.fromarray(dots.T)
153im = PIL.ImageChops.invert(im)
154im.save('dotcode.png')