forked from K0lb3/UnityPy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTextureSwizzler.py
More file actions
127 lines (108 loc) · 5.38 KB
/
TextureSwizzler.py
File metadata and controls
127 lines (108 loc) · 5.38 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
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
# based on https://github.com/nesrak1/AssetsTools.NET/blob/dev/AssetsTools.NET.Texture/Swizzle/SwitchSwizzle.cs
from typing import Dict, List, Optional, Tuple, Union
from ..enums import BuildTarget, TextureFormat
GOB_X_TEXEL_COUNT = 4
GOB_Y_TEXEL_COUNT = 8
TEXEL_BYTE_SIZE = 16
TEXELS_IN_GOB = GOB_X_TEXEL_COUNT * GOB_Y_TEXEL_COUNT
GOB_MAP = [(((v >> 3) & 0b10) | ((v >> 1) & 0b1), ((v >> 1) & 0b110) | (v & 0b1)) for v in range(TEXELS_IN_GOB)]
def ceil_divide(a: int, b: int) -> int:
return (a + b - 1) // b
def deswizzle(
data: Union[bytes, bytearray, memoryview],
width: int,
height: int,
block_width: int,
block_height: int,
texels_per_block: int,
) -> bytearray:
block_count_x = ceil_divide(width, block_width)
block_count_y = ceil_divide(height, block_height)
gob_count_x = block_count_x // GOB_X_TEXEL_COUNT
gob_count_y = block_count_y // GOB_Y_TEXEL_COUNT
new_data = bytearray(len(data))
data_view = memoryview(data)
for i in range(gob_count_y // texels_per_block):
for j in range(gob_count_x):
base_gob_dst_x = j * 4
for k in range(texels_per_block):
base_gob_dst_y = (i * texels_per_block + k) * GOB_Y_TEXEL_COUNT
for gob_x, gob_y in GOB_MAP:
dst_offset = ((base_gob_dst_y + gob_y) * block_count_x + (base_gob_dst_x + gob_x)) * TEXEL_BYTE_SIZE
new_data[dst_offset : dst_offset + TEXEL_BYTE_SIZE] = data_view[:TEXEL_BYTE_SIZE]
data_view = data_view[TEXEL_BYTE_SIZE:]
return new_data
def swizzle(
data: Union[bytes, bytearray, memoryview],
width: int,
height: int,
block_width: int,
block_height: int,
texels_per_block: int,
) -> bytearray:
block_count_x = ceil_divide(width, block_width)
block_count_y = ceil_divide(height, block_height)
gob_count_x = block_count_x // GOB_X_TEXEL_COUNT
gob_count_y = block_count_y // GOB_Y_TEXEL_COUNT
new_data = bytearray(len(data))
data_view = memoryview(new_data)
for i in range(gob_count_y // texels_per_block):
for j in range(gob_count_x):
base_gob_dst_x = j * 4
for k in range(texels_per_block):
base_gob_dst_y = (i * texels_per_block + k) * GOB_Y_TEXEL_COUNT
for gob_x, gob_y in GOB_MAP:
src_offset = ((base_gob_dst_y + gob_y) * block_count_x + (base_gob_dst_x + gob_x)) * TEXEL_BYTE_SIZE
data_view[:TEXEL_BYTE_SIZE] = data[src_offset : src_offset + TEXEL_BYTE_SIZE]
data_view = data_view[TEXEL_BYTE_SIZE:]
return new_data
# this should be the amount of pixels that can fit 16 bytes
TEXTURE_FORMAT_BLOCK_SIZE_MAP: Dict[TextureFormat, Tuple[int, int]] = {
TextureFormat.Alpha8: (16, 1), # 1 byte per pixel
TextureFormat.ARGB4444: (8, 1), # 2 bytes per pixel
TextureFormat.RGBA32: (4, 1), # 4 bytes per pixel
TextureFormat.ARGB32: (4, 1), # 4 bytes per pixel
TextureFormat.ARGBFloat: (1, 1), # 16 bytes per pixel (?)
TextureFormat.RGB565: (8, 1), # 2 bytes per pixel
TextureFormat.R16: (8, 1), # 2 bytes per pixel
TextureFormat.DXT1: (8, 4), # 8 bytes per 4x4=16 pixels
TextureFormat.DXT5: (4, 4), # 16 bytes per 4x4=16 pixels
TextureFormat.RGBA4444: (8, 1), # 2 bytes per pixel
TextureFormat.BGRA32: (4, 1), # 4 bytes per pixel
TextureFormat.BC6H: (4, 4), # 16 bytes per 4x4=16 pixels
TextureFormat.BC7: (4, 4), # 16 bytes per 4x4=16 pixels
TextureFormat.BC4: (8, 4), # 8 bytes per 4x4=16 pixels
TextureFormat.BC5: (4, 4), # 16 bytes per 4x4=16 pixels
TextureFormat.ASTC_RGB_4x4: (4, 4), # 16 bytes per 4x4=16 pixels
TextureFormat.ASTC_RGB_5x5: (5, 5), # 16 bytes per 5x5=25 pixels
TextureFormat.ASTC_RGB_6x6: (6, 6), # 16 bytes per 6x6=36 pixels
TextureFormat.ASTC_RGB_8x8: (8, 8), # 16 bytes per 8x8=64 pixels
TextureFormat.ASTC_RGB_10x10: (10, 10), # 16 bytes per 10x10=100 pixels
TextureFormat.ASTC_RGB_12x12: (12, 12), # 16 bytes per 12x12=144 pixels
TextureFormat.ASTC_RGBA_4x4: (4, 4), # 16 bytes per 4x4=16 pixels
TextureFormat.ASTC_RGBA_5x5: (5, 5), # 16 bytes per 5x5=25 pixels
TextureFormat.ASTC_RGBA_6x6: (6, 6), # 16 bytes per 6x6=36 pixels
TextureFormat.ASTC_RGBA_8x8: (8, 8), # 16 bytes per 8x8=64 pixels
TextureFormat.ASTC_RGBA_10x10: (10, 10), # 16 bytes per 10x10=100 pixels
TextureFormat.ASTC_RGBA_12x12: (12, 12), # 16 bytes per 12x12=144 pixels
TextureFormat.RG16: (8, 1), # 2 bytes per pixel
TextureFormat.R8: (16, 1), # 1 byte per pixel
}
def get_padded_texture_size(width: int, height: int, block_width: int, block_height: int, texels_per_block: int):
width = ceil_divide(width, block_width * GOB_X_TEXEL_COUNT) * block_width * GOB_X_TEXEL_COUNT
height = (
ceil_divide(height, block_height * GOB_Y_TEXEL_COUNT * texels_per_block)
* block_height
* GOB_Y_TEXEL_COUNT
* texels_per_block
)
return width, height
def get_switch_gobs_per_block(platform_blob: List[int]) -> int:
return 1 << int.from_bytes(platform_blob[8:12], "little")
def is_switch_swizzled(platform: Union[BuildTarget, int], platform_blob: Optional[List[int]]) -> bool:
if platform != BuildTarget.Switch:
return False
if not platform_blob or len(platform_blob) < 12:
return False
gobs_per_block = get_switch_gobs_per_block(platform_blob)
return gobs_per_block > 1