mirror of
https://github.com/veekun/pokedex.git
synced 2024-08-20 18:16:34 +00:00
b12166648e
The bulk of the data is in the same format as ORAS, so most of the changes were just tracking down where files moved to. The code was a mess, and is still a mess. Oh, well. - Made pretty good progress on dumping ORAS encounters; remaining work largely boils down to figuring out names for individual zones. - Hacked the CLIM decoder to also work with SUMO's slightly modified box sprite format, FLIM. - Added a Nintendo-flavored ETC1 decoder; this is the format used for Pokédex sprites in SUMO. - Cleaned up sprite dumping a wee bit.
111 lines
4 KiB
Python
111 lines
4 KiB
Python
"""Parse ETC1, a terrible micro block-based image compression format.
|
|
|
|
Please enjoy the docs.
|
|
https://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
|
|
"""
|
|
import io
|
|
import itertools
|
|
|
|
|
|
three_bit_twos_complement = [0, 1, 2, 3, -4, -3, -2, -1]
|
|
|
|
etc1_modifier_tables = [
|
|
( 2, 8, -2, -8),
|
|
( 5, 17, -5, -17),
|
|
( 9, 29, -9, -29),
|
|
(13, 42, -13, -42),
|
|
(18, 60, -18, -60),
|
|
(24, 80, -24, -80),
|
|
(33, 106, -33, -106),
|
|
(47, 183, -47, -183),
|
|
]
|
|
|
|
def decode_etc1(data):
|
|
# TODO sizes are hardcoded here
|
|
f = io.BytesIO(data)
|
|
f.read(0x80)
|
|
outpixels = [[None] * 128 for _ in range(128)]
|
|
for blocky in range(0, 128, 8):
|
|
for blockx in range(0, 128, 8):
|
|
for z in range(4):
|
|
row = f.read(16)
|
|
if not row:
|
|
raise RuntimeError
|
|
|
|
alpha = row[:8]
|
|
etc1 = int.from_bytes(row[8:], 'big')
|
|
diffbit = row[12] & 2
|
|
flipbit = row[12] & 1
|
|
lopixelbits = int.from_bytes(row[8:10], 'little')
|
|
hipixelbits = int.from_bytes(row[10:12], 'little')
|
|
|
|
if diffbit:
|
|
red1 = row[15] >> 3
|
|
red2 = max(0, red1 + three_bit_twos_complement[row[15] & 0x7])
|
|
green1 = row[14] >> 3
|
|
green2 = max(0, green1 + three_bit_twos_complement[row[14] & 0x7])
|
|
blue1 = row[13] >> 3
|
|
blue2 = max(0, blue1 + three_bit_twos_complement[row[13] & 0x7])
|
|
|
|
red1 = (red1 << 3) | (red1 >> 2)
|
|
green1 = (green1 << 3) | (green1 >> 2)
|
|
blue1 = (blue1 << 3) | (blue1 >> 2)
|
|
red2 = (red2 << 3) | (red2 >> 2)
|
|
green2 = (green2 << 3) | (green2 >> 2)
|
|
blue2 = (blue2 << 3) | (blue2 >> 2)
|
|
else:
|
|
red1 = row[15] >> 4
|
|
red2 = row[15] & 0xf
|
|
green1 = row[14] >> 4
|
|
green2 = row[14] & 0xf
|
|
blue1 = row[13] >> 4
|
|
blue2 = row[13] & 0xf
|
|
|
|
red1 = (red1 << 4) | red1
|
|
green1 = (green1 << 4) | green1
|
|
blue1 = (blue1 << 4) | blue1
|
|
red2 = (red2 << 4) | red2
|
|
green2 = (green2 << 4) | green2
|
|
blue2 = (blue2 << 4) | blue2
|
|
base1 = red1, green1, blue1
|
|
base2 = red2, green2, blue2
|
|
|
|
codeword1 = row[12] >> 5
|
|
codeword2 = (row[12] >> 2) & 0x7
|
|
table1 = etc1_modifier_tables[codeword1]
|
|
table2 = etc1_modifier_tables[codeword2]
|
|
|
|
def nybbles(b):
|
|
for byte in b:
|
|
yield (byte & 0xf) << 4
|
|
yield byte >> 4 << 4
|
|
it = nybbles(alpha)
|
|
|
|
for c in range(4):
|
|
for r in range(4):
|
|
x = blockx + c
|
|
y = blocky + r
|
|
if z in (1, 3):
|
|
x += 4
|
|
if z in (2, 3):
|
|
y += 4
|
|
|
|
if flipbit:
|
|
# Horizontal
|
|
whichblock = 1 if r < 2 else 2
|
|
else:
|
|
whichblock = 1 if c < 2 else 2
|
|
if whichblock == 1:
|
|
table = table1
|
|
base = base1
|
|
else:
|
|
table = table2
|
|
base = base2
|
|
|
|
pixelbit = c * 4 + r
|
|
idx = 2 * ((hipixelbits >> pixelbit) & 1) + ((lopixelbits >> pixelbit) & 1)
|
|
mod = table[idx]
|
|
color = tuple(min(255, max(0, b + mod)) for b in base) + (next(it),)
|
|
outpixels[y][x] = color
|
|
|
|
return 128, 128, 4, None, outpixels
|