-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpvr_ccz_decoder.py
More file actions
80 lines (60 loc) · 2.85 KB
/
pvr_ccz_decoder.py
File metadata and controls
80 lines (60 loc) · 2.85 KB
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
import zlib
import math
from PIL import Image
import texture2ddecoder
def pvr_ccz_decode(data, logging=False, exc=None):
# extract PVR payload from CCZ (big endian)
if data[0:4] != b"CCZ!":
raise exc("not a valid CCZ container !")
ccz_version = int.from_bytes(data[4:8], "big")
ccz_compression_type = int.from_bytes(data[8:12], "big")
ccz_decompressed_size = int.from_bytes(data[12:16], "big")
ccz_payload = data[16:]
if ccz_compression_type != 0:
raise Exception(f"invalid compression type {ccz_compression_type} !")
logging and print(f"version : {ccz_version} | compression type : {ccz_compression_type} | decompressed size : {ccz_decompressed_size} | compressed size : {len(ccz_payload)}")
data = zlib.decompress(ccz_payload)
if len(data) != ccz_decompressed_size:
raise Exception("invalid decompressed size for PVR !")
# parse PVR (little endian)
if data[0:4] != b"PVR\x03":
raise Exception("invalid PVR header / version !")
# 4:8 uint32_t flags;
pvr_pixel_format = int.from_bytes(data[8:16], "little") # 8:16 uint64_t pixelFormat; - note: sometimes seems to be uint32_t ?
# 16:20 uint32_t colourSpace;
# 20:24 uint32_t channelType;
pvr_height = int.from_bytes(data[24:28], "little") # 24:28 uint32_t height;
pvr_width = int.from_bytes(data[28:32], "little") # 28:32 uint32_t width;
# 32:36 uint32_t depth;
# 36:40 uint32_t numSurfaces;
# 40:44 uint32_t numFaces;
pvr_mipmap_count = int.from_bytes(data[44:48], "little") # 44:48 uint32_t mipMapCount;
pvr_metadata_size = int.from_bytes(data[48:52], "little") # 48:52 uint32_t metaDataSize;
if pvr_metadata_size > 0:
raise Exception("non-null PVR metadata size !")
pixel_formats = {
6: "ETC1",
23: "ETC2_RGBA",
578721384203708274: "RGBA8888"
}
if pvr_pixel_format not in pixel_formats:
raise Exception(f"pixel format {pvr_pixel_format} not implemented !")
pvr_pixel_format = pixel_formats[pvr_pixel_format]
logging and print(f"pixel format : {pvr_pixel_format} | size (wh) : {pvr_width}x{pvr_height}")
if pvr_mipmap_count > 1:
raise Exception(f"mipmap count {pvr_mipmap_count} not implemented !")
data = data[52:]
if pvr_pixel_format == "ETC1":
# ETC1 spec specifies 4x4 block size of 8 bytes each
bw, bh = math.ceil(pvr_width / 4), math.ceil(pvr_height / 4)
data_size = bw * bh * 8
if data_size != (ccz_decompressed_size - 52):
raise Exception("PVR payload size mismatch !")
raw = texture2ddecoder.decode_etc1(data, pvr_width, pvr_height)
img = Image.frombytes("RGBA", (pvr_width, pvr_height), raw, "raw", ("BGRA")) # reload RGB888 as PNG
elif pvr_pixel_format == "ETC2_RGBA":
raw = texture2ddecoder.decode_etc2a8(data, pvr_width, pvr_height) # etc2 & full 8-bit alpha
img = Image.frombytes("RGBA", (pvr_width, pvr_height), raw, "raw", ("BGRA")).convert("RGBA")
elif pvr_pixel_format == "RGBA8888":
img = Image.frombytes("RGBA", (pvr_width, pvr_height), data)
return img