-
Notifications
You must be signed in to change notification settings - Fork 4
/
mat.py
146 lines (117 loc) · 4.81 KB
/
mat.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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import io
import itertools
import os
import struct
import sys
from PIL import Image, ImageOps
def _decode_16bit(data, color_extraction):
sr, sg, sb = [x for x in color_extraction['channel_shifts']]
mr, mg, mb = [(1 << bits) - 1 for bits in color_extraction['channel_bits']]
er, eg, eb = [x for x in color_extraction['channel_expands']]
colors = []
for i in range(0, len(data), 2):
val = data[i] | (data[i + 1] << 8)
r = ((val >> sr) & mr) << er
g = ((val >> sg) & mg) << eg
b = ((val >> sb) & mb) << eb
a = 255
colors.append((r, g, b, a))
return colors
def _read_colors(f, color_extraction, count, colormap):
frames = []
for i in range(count):
mat_type, color, res1, res2, res3, res4 = struct.unpack(
'iIiiii', f.read(24))
if mat_type != 0:
raise Exception("Not a color!")
if colormap:
pixel_bytes = [colormap[color]]
else:
pixel_bytes = [(i, i, i, 255)]
# pixel_bytes contains a single pixels in RGBA order
x = bytes(itertools.chain.from_iterable(pixel_bytes))
img = Image.frombytes('RGBA', (1, 1), x)
frames.append(img)
return frames
def _set_alpha(rgba, lhs, rhs):
return (rgba[0], rgba[1], rgba[2], 0) if lhs == rhs else rgba
def _read_textures(f, color_extraction, count, colormap):
# load the headers
for i in range(count):
mat_type, transparent_color, res1, res2, res3, res4, res5, res6, res7, idx = struct.unpack(
'iIiiiiiiii', f.read(40))
if mat_type != 8 or idx != i:
raise Exception("Not a texture!")
# load the data
frames = []
for i in range(count):
width, height, has_transparency, res2, res3, mipmaps = struct.unpack(
'iiiiii', f.read(24))
ts = transparent_color if has_transparency else -1
pixel_bytes = None
org_width = width
org_height = height
for j in range(mipmaps):
if color_extraction['total_bits'] == 8:
data = f.read(width * height)
if j == 0: # only load first mipmap
if has_transparency:
if colormap:
pixel_bytes = [_set_alpha(
colormap[i], i, ts) for i in data]
else:
pixel_bytes = [_set_alpha(
(i, i, i, 255), i, ts) for i in data]
else:
if colormap:
pixel_bytes = [colormap[i] for i in data]
else:
pixel_bytes = [(i, i, i, 255) for i in data]
elif color_extraction['total_bits'] == 16:
data = f.read(width * height * 2)
if j == 0: # only load first mipmap
pixel_bytes = _decode_16bit(data, color_extraction)
else:
raise Exception("Invalid file!")
if width != 1:
width //= 2
if height != 1:
height //= 2
# pixel_bytes contains the pixels for the largest mipmap level in RGBA order
x = bytes(itertools.chain.from_iterable(pixel_bytes))
img = Image.frombytes('RGBA', (org_width, org_height), x)
img = ImageOps.flip(img)
frames.append(img)
return frames
def load_frames_from_file(f, colormap=None):
ident, version, mat_type, count, res1, res2 = struct.unpack(
'Iiiiii', f.read(24))
if ident != 542392653 or version != 50:
raise Exception("Invalid file!")
bits, red_bits, green_bits, blue_bits, red_shift, green_shift, blue_shift, red_expand, green_expand, blue_expand, res3, res4, res5 = struct.unpack(
'iiiiiiiiiiiii', f.read(52))
color_extraction = {
'total_bits': bits,
'channel_bits': (red_bits, green_bits, blue_bits),
'channel_shifts': (red_shift, green_shift, blue_shift),
'channel_expands': (red_expand, green_expand, blue_expand)
}
if mat_type == 0:
return _read_colors(f, color_extraction, count, colormap)
elif mat_type == 2:
return _read_textures(f, color_extraction, count, colormap)
else:
raise Exception("Invalid file!")
def load_frames_from_bytes(b, colormap=None):
return load_frames_from_file(io.BytesIO(b), colormap=colormap)
if __name__ == "__main__":
colormap = None
if len(sys.argv) > 2:
import cmp
with open(sys.argv[2], 'rb') as cmpfile:
colormap = cmp.read_from_file(cmpfile)
with open(sys.argv[1], 'rb') as matfile:
frames = load_frames_from_file(matfile, colormap=colormap)
with io.BytesIO() as output:
frames[0].save(output, format='PNG')
os.write(1, output.getvalue()) # write to stdout