ReverseBox 0.18.2__tar.gz → 0.18.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/PKG-INFO +1 -1
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/ReverseBox.egg-info/PKG-INFO +1 -1
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/ReverseBox.egg-info/SOURCES.txt +4 -0
- ReverseBox-0.18.4/reversebox/compression/compression_refpack.py +68 -0
- ReverseBox-0.18.4/reversebox/image/decoders/bumpmap_decoder.py +58 -0
- ReverseBox-0.18.4/reversebox/image/decoders/n64_decoder.py +100 -0
- ReverseBox-0.18.4/reversebox/image/decoders/yuv_decoder.py +519 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_ps2.py +4 -4
- ReverseBox-0.18.4/reversebox/libs/__init__.py +0 -0
- ReverseBox-0.18.4/reversebox/libs/refpack.dll +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/setup.py +1 -1
- ReverseBox-0.18.2/reversebox/compression/compression_refpack.py +0 -55
- ReverseBox-0.18.2/reversebox/libs/refpack.dll +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/LICENSE +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/README.md +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/ReverseBox.egg-info/dependency_links.txt +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/ReverseBox.egg-info/requires.txt +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/ReverseBox.egg-info/top_level.txt +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/checksum_adler32.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/checksum_fletcher16.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/checksum_fletcher32.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/checksum_internet_ipv4_header.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/checksum_sum8.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/checksum_sum8_2s_complement.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/checksum_unix_sum_bsd16.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/checksum_unix_sum_sysv.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/checksum_xor8.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/common/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/common/common.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/common/logger.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/compression/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/compression/compression_jcalg1.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/compression/compression_lzo.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/compression/compression_mio0.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/compression/compression_zlib.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc16_arc.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc16_ccitt.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc16_dnp.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc16_ea_crcf.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc16_kermit.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc16_modbus.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc16_sick.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc32_asobo.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc32_iso_hdlc.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc64_asobo.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc64_go_iso.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc8.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/crc/crc_unix_cksum.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/encryption/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/encryption/encryption_rot13.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/encryption/encryption_xor_basic.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/encryption/encryption_xor_basic_key_guesser.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/encryption/encryption_xor_gianas_return_zda.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/encryption/encryption_xor_retro64_eco.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/hash/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/hash/hash_ap.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/hash/hash_djb2.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/hash/hash_fnv.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/hash/hash_md2.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/hash/hash_md5.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/hash/hash_sdbm.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/hash/hash_sha1.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/hash/hash_sha2.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/common.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/compression/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/compression/compression_gst.py +0 -0
- {ReverseBox-0.18.2/reversebox/image/file_formats → ReverseBox-0.18.4/reversebox/image/decoders}/__init__.py +0 -0
- {ReverseBox-0.18.2/reversebox/image/palettes → ReverseBox-0.18.4/reversebox/image/file_formats}/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/file_formats/image_dds.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/image_decoder.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/image_encoder.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/image_formats.py +0 -0
- {ReverseBox-0.18.2/reversebox/image/swizzling → ReverseBox-0.18.4/reversebox/image/palettes}/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/palettes/palette_random.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/pillow_wrapper.py +0 -0
- {ReverseBox-0.18.2/reversebox/io_files → ReverseBox-0.18.4/reversebox/image/swizzling}/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_3ds.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_bc.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_cmpr.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_gamecube.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_gst.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_morton.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_morton_dreamcast.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_morton_ps4.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_psp.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_psvita.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_switch.py +0 -0
- {ReverseBox-0.18.2/reversebox/libs → ReverseBox-0.18.4/reversebox/io_files}/__init__.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/io_files/bytes_handler.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/io_files/bytes_helper_functions.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/io_files/check_file.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/io_files/file_handler.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/io_files/mod_handler.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/io_files/translation_text_handler.py +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/libs/ea_swizzle.dll +0 -0
- {ReverseBox-0.18.2 → ReverseBox-0.18.4}/setup.cfg +0 -0
|
@@ -63,6 +63,10 @@ reversebox/image/image_formats.py
|
|
|
63
63
|
reversebox/image/pillow_wrapper.py
|
|
64
64
|
reversebox/image/compression/__init__.py
|
|
65
65
|
reversebox/image/compression/compression_gst.py
|
|
66
|
+
reversebox/image/decoders/__init__.py
|
|
67
|
+
reversebox/image/decoders/bumpmap_decoder.py
|
|
68
|
+
reversebox/image/decoders/n64_decoder.py
|
|
69
|
+
reversebox/image/decoders/yuv_decoder.py
|
|
66
70
|
reversebox/image/file_formats/__init__.py
|
|
67
71
|
reversebox/image/file_formats/image_dds.py
|
|
68
72
|
reversebox/image/palettes/__init__.py
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright © 2024 Bartłomiej Duda
|
|
3
|
+
License: GPL-3.0 License
|
|
4
|
+
"""
|
|
5
|
+
import ctypes
|
|
6
|
+
from ctypes import c_char
|
|
7
|
+
|
|
8
|
+
from reversebox.common.common import get_dll_path
|
|
9
|
+
|
|
10
|
+
# fmt: off
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RefpackHandler:
|
|
14
|
+
def compress_data(self, uncompressed_data: bytes) -> bytes:
|
|
15
|
+
if len(uncompressed_data) == 0:
|
|
16
|
+
raise Exception("Data is empty! Nothing to compress!")
|
|
17
|
+
|
|
18
|
+
maxback: int = 131072
|
|
19
|
+
quick: int = 0
|
|
20
|
+
|
|
21
|
+
c_uncompressed_data_len = ctypes.c_uint(len(uncompressed_data))
|
|
22
|
+
c_uncompressed_data = (ctypes.c_ubyte * len(uncompressed_data))(*uncompressed_data)
|
|
23
|
+
c_compressed_data_buffer = (c_char * int(len(uncompressed_data)*5))() # just in case
|
|
24
|
+
c_options_maxback = ctypes.c_uint(maxback)
|
|
25
|
+
c_options_quick = ctypes.c_uint(quick)
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
refpack_dll_path: str = get_dll_path("refpack.dll")
|
|
29
|
+
refpack_dll_file = ctypes.CDLL(refpack_dll_path)
|
|
30
|
+
compressed_data_size = refpack_dll_file.refpack_compress(ctypes.byref(c_compressed_data_buffer), c_uncompressed_data, c_uncompressed_data_len, ctypes.byref(c_options_maxback), ctypes.byref(c_options_quick))
|
|
31
|
+
except Exception as error:
|
|
32
|
+
raise Exception(f"Error while decompressing refpack data! Error: {error}")
|
|
33
|
+
return bytes(bytearray(c_compressed_data_buffer)[:compressed_data_size])
|
|
34
|
+
|
|
35
|
+
def _get_output_buffer_size(self, compressed_data: bytes) -> int:
|
|
36
|
+
current_input_offset: int = 0
|
|
37
|
+
compression_type = compressed_data[current_input_offset]
|
|
38
|
+
current_input_offset += 1
|
|
39
|
+
compression_type = (compression_type << 8) + compressed_data[current_input_offset]
|
|
40
|
+
current_input_offset += 1
|
|
41
|
+
|
|
42
|
+
if compression_type & 0x100:
|
|
43
|
+
current_input_offset += 3 # skip uncompressed_size
|
|
44
|
+
|
|
45
|
+
uncompressed_size: int = compressed_data[current_input_offset]
|
|
46
|
+
current_input_offset += 1
|
|
47
|
+
uncompressed_size = (uncompressed_size << 8) + compressed_data[current_input_offset]
|
|
48
|
+
current_input_offset += 1
|
|
49
|
+
uncompressed_size = (uncompressed_size << 8) + compressed_data[current_input_offset]
|
|
50
|
+
current_input_offset += 1
|
|
51
|
+
return uncompressed_size
|
|
52
|
+
|
|
53
|
+
def decompress_data(self, compressed_data: bytes) -> bytes:
|
|
54
|
+
if len(compressed_data) < 5:
|
|
55
|
+
raise Exception("Compressed data too short!")
|
|
56
|
+
if compressed_data[:2] != b"\x10\xFB":
|
|
57
|
+
raise Exception("Wrong refpack compression header!")
|
|
58
|
+
|
|
59
|
+
uncompressed_data_buffer = (c_char * self._get_output_buffer_size(compressed_data))()
|
|
60
|
+
compressed_data_len = ctypes.c_uint(len(compressed_data))
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
refpack_dll_path: str = get_dll_path("refpack.dll")
|
|
64
|
+
refpack_dll_file = ctypes.CDLL(refpack_dll_path)
|
|
65
|
+
uncompressed_data_size = refpack_dll_file.refpack_decompress(ctypes.byref(uncompressed_data_buffer), compressed_data, ctypes.byref(compressed_data_len))
|
|
66
|
+
except Exception as error:
|
|
67
|
+
raise Exception(f"Error while decompressing refpack data! Error: {error}")
|
|
68
|
+
return bytes(bytearray(uncompressed_data_buffer)[:uncompressed_data_size])
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright © 2024 Bartłomiej Duda
|
|
3
|
+
License: GPL-3.0 License
|
|
4
|
+
"""
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
from reversebox.common.logger import get_logger
|
|
8
|
+
from reversebox.image.image_formats import ImageFormats
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
# fmt: off
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BumpmapDecoder:
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
# 16-bit normal map. Each texel consist of a pair of 8-bit values (S and R)
|
|
21
|
+
# which represent a normal in spherical coordinates.
|
|
22
|
+
# S = polar angle. 0-255 maps to 0-89 degrees.
|
|
23
|
+
# R = azimuthal angle. 0-255 maps to 0-359 degrees.
|
|
24
|
+
# Used in DTEX Texconv Tool
|
|
25
|
+
def _decode_bumpmap_sr_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
26
|
+
rgba_data = bytearray(img_width * img_height * 4)
|
|
27
|
+
|
|
28
|
+
for i in range(img_width * img_height):
|
|
29
|
+
S = image_data[i * 2]
|
|
30
|
+
R = image_data[i * 2 + 1]
|
|
31
|
+
|
|
32
|
+
polar_angle = S * 89.0 / 255.0
|
|
33
|
+
azimuthal_angle = R * 359.0 / 255.0
|
|
34
|
+
|
|
35
|
+
polar_radians = math.radians(polar_angle)
|
|
36
|
+
azimuthal_radians = math.radians(azimuthal_angle)
|
|
37
|
+
|
|
38
|
+
nx = math.sin(polar_radians) * math.cos(azimuthal_radians)
|
|
39
|
+
ny = math.sin(polar_radians) * math.sin(azimuthal_radians)
|
|
40
|
+
nz = math.cos(polar_radians)
|
|
41
|
+
|
|
42
|
+
r_value = int((nx + 1.0) * 127.5)
|
|
43
|
+
g_value = int((ny + 1.0) * 127.5)
|
|
44
|
+
b_value = int((nz + 1.0) * 127.5)
|
|
45
|
+
|
|
46
|
+
rgba_data[i * 4] = r_value
|
|
47
|
+
rgba_data[i * 4 + 1] = g_value
|
|
48
|
+
rgba_data[i * 4 + 2] = b_value
|
|
49
|
+
rgba_data[i * 4 + 3] = 0xFF
|
|
50
|
+
|
|
51
|
+
return rgba_data
|
|
52
|
+
|
|
53
|
+
def decode_bumpmap_image_main(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats) -> bytes:
|
|
54
|
+
|
|
55
|
+
if image_format == ImageFormats.BUMPMAP_SR:
|
|
56
|
+
return self._decode_bumpmap_sr_image(image_data, img_width, img_height)
|
|
57
|
+
else:
|
|
58
|
+
raise Exception(f"Image format not supported by BUMPMAP decoder! Image_format: {image_format}")
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright © 2024 Bartłomiej Duda
|
|
3
|
+
License: GPL-3.0 License
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from reversebox.common.logger import get_logger
|
|
7
|
+
from reversebox.image.common import crop_image, get_storage_wh
|
|
8
|
+
from reversebox.image.image_formats import ImageFormats
|
|
9
|
+
from reversebox.io_files.bytes_handler import BytesHandler
|
|
10
|
+
from reversebox.io_files.bytes_helper_functions import get_uint8, get_uint16
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
# fmt: off
|
|
15
|
+
# mypy: ignore-errors
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class N64Decoder:
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
def _decode_n64_rgba32_image(self, image_data: bytes, img_width: int, img_height: int, block_width: int, block_height: int) -> bytes:
|
|
24
|
+
_width, _height = get_storage_wh(img_width, img_height, block_width, block_height)
|
|
25
|
+
output_data = bytearray(_width * _height * 4)
|
|
26
|
+
offset = 0
|
|
27
|
+
|
|
28
|
+
for y in range(0, _height, block_height):
|
|
29
|
+
for x in range(0, _width, block_width):
|
|
30
|
+
for y2 in range(block_height):
|
|
31
|
+
for x2 in range(block_width):
|
|
32
|
+
idx = (((y + y2) * _width) + (x + x2)) * 4
|
|
33
|
+
output_data[idx + 0] = image_data[offset + 1]
|
|
34
|
+
output_data[idx + 1] = image_data[offset + 32]
|
|
35
|
+
output_data[idx + 2] = image_data[offset + 33]
|
|
36
|
+
output_data[idx + 3] = image_data[offset + 0]
|
|
37
|
+
offset += 2
|
|
38
|
+
offset += 32
|
|
39
|
+
|
|
40
|
+
output_data = crop_image(output_data, _width, _height, 32, img_width, img_height)
|
|
41
|
+
return output_data
|
|
42
|
+
|
|
43
|
+
def _decode_n64_cmpr_image(self, image_data: bytes, img_width: int, img_height: int, block_width: int, block_height: int) -> bytes:
|
|
44
|
+
_width, _height = get_storage_wh(img_width, img_height, block_width, block_height)
|
|
45
|
+
output_data = bytearray(_width * _height * 4)
|
|
46
|
+
image_data_handler = BytesHandler(image_data)
|
|
47
|
+
|
|
48
|
+
def _decode_rgb565_pixel(pixel_int: int) -> bytes:
|
|
49
|
+
p = bytearray(4)
|
|
50
|
+
p[0] = ((pixel_int >> 11) & 0x1F) * 0xFF // 0x1F
|
|
51
|
+
p[1] = ((pixel_int >> 5) & 0x3F) * 0xFF // 0x3F
|
|
52
|
+
p[2] = ((pixel_int >> 0) & 0x1F) * 0xFF // 0x1F
|
|
53
|
+
p[3] = 0xFF
|
|
54
|
+
return p
|
|
55
|
+
|
|
56
|
+
current_offset: int = 0
|
|
57
|
+
for y in range(0, _height, block_height):
|
|
58
|
+
for x in range(0, _width, block_width):
|
|
59
|
+
for y2 in range(0, block_height, 4):
|
|
60
|
+
for x2 in range(0, block_width, 4):
|
|
61
|
+
|
|
62
|
+
c0 = get_uint16(image_data_handler.get_bytes(current_offset, 2), ">")
|
|
63
|
+
current_offset += 2
|
|
64
|
+
c1 = get_uint16(image_data_handler.get_bytes(current_offset, 2), ">")
|
|
65
|
+
current_offset += 2
|
|
66
|
+
|
|
67
|
+
c = [
|
|
68
|
+
_decode_rgb565_pixel(c0),
|
|
69
|
+
_decode_rgb565_pixel(c1),
|
|
70
|
+
bytearray(4),
|
|
71
|
+
bytearray(4)
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
if c0 > c1:
|
|
75
|
+
for i in range(4):
|
|
76
|
+
c[2][i] = int((2 * c[0][i] + c[1][i]) / 3)
|
|
77
|
+
c[3][i] = int((2 * c[1][i] + c[0][i]) / 3)
|
|
78
|
+
else:
|
|
79
|
+
for i in range(4):
|
|
80
|
+
c[2][i] = int((c[0][i] + c[1][i]) * .5)
|
|
81
|
+
c[3][i] = 0
|
|
82
|
+
|
|
83
|
+
for y3 in range(4):
|
|
84
|
+
b = get_uint8(image_data_handler.get_bytes(current_offset, 1), ">")
|
|
85
|
+
current_offset += 1
|
|
86
|
+
for x3 in range(4):
|
|
87
|
+
idx = (((y + y2 + y3) * _width) + (x + x2 + x3)) * 4
|
|
88
|
+
output_data[idx: idx + 4] = c[(b >> (6 - (x3 * 2))) & 0x3]
|
|
89
|
+
|
|
90
|
+
output_data = crop_image(output_data, _width, _height, 32, img_width, img_height)
|
|
91
|
+
return output_data
|
|
92
|
+
|
|
93
|
+
def decode_n64_image_main(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats) -> bytes:
|
|
94
|
+
|
|
95
|
+
if image_format == ImageFormats.N64_RGBA32:
|
|
96
|
+
return self._decode_n64_rgba32_image(image_data, img_width, img_height, 4, 4)
|
|
97
|
+
elif image_format == ImageFormats.N64_CMPR:
|
|
98
|
+
return self._decode_n64_cmpr_image(image_data, img_width, img_height, 8, 8)
|
|
99
|
+
else:
|
|
100
|
+
raise Exception(f"Image format not supported by N64 decoder! Image_format: {image_format}")
|
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright © 2024 Bartłomiej Duda
|
|
3
|
+
License: GPL-3.0 License
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from reversebox.common.logger import get_logger
|
|
8
|
+
from reversebox.image.image_formats import ImageFormats
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
# fmt: off
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class YUVDecoder:
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
def _check_if_yuv_image_dimensions_are_correct(self, img_width: int, img_height: int) -> bool:
|
|
21
|
+
MIN_IMAGE_WIDTH = 4
|
|
22
|
+
MIN_IMAGE_HEIGHT = 4
|
|
23
|
+
|
|
24
|
+
if img_width < MIN_IMAGE_WIDTH or img_height < MIN_IMAGE_HEIGHT:
|
|
25
|
+
raise Exception("YUV image to small to convert!")
|
|
26
|
+
|
|
27
|
+
return True
|
|
28
|
+
|
|
29
|
+
def _limit_rgb_value(self, f: float) -> int:
|
|
30
|
+
i: int = int(f + 0.5)
|
|
31
|
+
if i < 0:
|
|
32
|
+
i = 0
|
|
33
|
+
if i > 255:
|
|
34
|
+
i = 255
|
|
35
|
+
return i
|
|
36
|
+
|
|
37
|
+
def _yuv_to_rgb(self, Y, U, V) -> tuple:
|
|
38
|
+
R = Y + 1.140 * (V - 128)
|
|
39
|
+
G = Y - 0.395 * (U - 128) - 0.581 * (V - 128)
|
|
40
|
+
B = Y + 2.032 * (U - 128)
|
|
41
|
+
|
|
42
|
+
R = self._limit_rgb_value(R)
|
|
43
|
+
G = self._limit_rgb_value(G)
|
|
44
|
+
B = self._limit_rgb_value(B)
|
|
45
|
+
|
|
46
|
+
return R, G, B
|
|
47
|
+
|
|
48
|
+
def _decode_yuy2_pixel(self, Y: float, U: float, V: float) -> bytes:
|
|
49
|
+
p = bytearray(4)
|
|
50
|
+
|
|
51
|
+
C: float = Y - 16.0
|
|
52
|
+
D: float = U - 128.0
|
|
53
|
+
E: float = V - 128.0
|
|
54
|
+
|
|
55
|
+
R: float = 1.164383 * C + 1.596027 * E
|
|
56
|
+
G: float = 1.164383 * C - (0.391762 * D) - (0.812968 * E)
|
|
57
|
+
B: float = 1.164383 * C + 2.017232 * D
|
|
58
|
+
|
|
59
|
+
p[0] = self._limit_rgb_value(R)
|
|
60
|
+
p[1] = self._limit_rgb_value(G)
|
|
61
|
+
p[2] = self._limit_rgb_value(B)
|
|
62
|
+
p[3] = 0xFF
|
|
63
|
+
return p
|
|
64
|
+
|
|
65
|
+
def _decode_yuv422_yuy2_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
66
|
+
is_width_odd: bool = True if (img_width & 1) else False
|
|
67
|
+
current_yuv_offset: int = 0
|
|
68
|
+
current_pixel_number: int = 0
|
|
69
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
70
|
+
|
|
71
|
+
for y in range(img_height):
|
|
72
|
+
for x in range(0, img_width, 2):
|
|
73
|
+
|
|
74
|
+
Y0: float = float(image_data[current_yuv_offset])
|
|
75
|
+
U: float = float(image_data[current_yuv_offset + 1])
|
|
76
|
+
Y1: float = float(image_data[current_yuv_offset + 2])
|
|
77
|
+
V: float = float(image_data[current_yuv_offset + 3])
|
|
78
|
+
|
|
79
|
+
pixel1 = self._decode_yuy2_pixel(Y0, U, V)
|
|
80
|
+
pixel2 = self._decode_yuy2_pixel(Y1, U, V)
|
|
81
|
+
output_texture_data[current_pixel_number * 4:(current_pixel_number + 1) * 4] = pixel1
|
|
82
|
+
output_texture_data[(current_pixel_number + 1) * 4:(current_pixel_number + 2) * 4] = pixel2
|
|
83
|
+
|
|
84
|
+
if is_width_odd and x == img_width - 1:
|
|
85
|
+
current_yuv_offset += 4
|
|
86
|
+
current_pixel_number += 1
|
|
87
|
+
else:
|
|
88
|
+
current_yuv_offset += 4
|
|
89
|
+
current_pixel_number += 2
|
|
90
|
+
|
|
91
|
+
return output_texture_data
|
|
92
|
+
|
|
93
|
+
def _decode_yuv420_nv12_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
94
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
95
|
+
|
|
96
|
+
p: int = img_height
|
|
97
|
+
for i in range(0, img_height, 2):
|
|
98
|
+
for j in range(0, img_width, 2):
|
|
99
|
+
Y00 = float(image_data[i * img_width + j])
|
|
100
|
+
Y01 = float(image_data[i * img_width + j + 1])
|
|
101
|
+
Y10 = float(image_data[(i + 1) * img_width + j])
|
|
102
|
+
Y11 = float(image_data[(i + 1) * img_width + j + 1])
|
|
103
|
+
U = float(image_data[p * img_width + j])
|
|
104
|
+
V = float(image_data[p * img_width + j + 1])
|
|
105
|
+
|
|
106
|
+
R = Y00 + 1.140 * (V - 128.0)
|
|
107
|
+
G = Y00 - 0.395 * (U - 128.0) - 0.581 * (V - 128.0)
|
|
108
|
+
B = Y00 + 2.032 * (U - 128.0)
|
|
109
|
+
output_texture_data[i * img_width * 4 + j * 4] = self._limit_rgb_value(R)
|
|
110
|
+
output_texture_data[i * img_width * 4 + j * 4 + 1] = self._limit_rgb_value(G)
|
|
111
|
+
output_texture_data[i * img_width * 4 + j * 4 + 2] = self._limit_rgb_value(B)
|
|
112
|
+
output_texture_data[i * img_width * 4 + j * 4 + 3] = 0xFF
|
|
113
|
+
|
|
114
|
+
R = Y01 + 1.140 * (V - 128.0)
|
|
115
|
+
G = Y01 - 0.395 * (U - 128.0) - 0.581 * (V - 128.0)
|
|
116
|
+
B = Y01 + 2.032 * (U - 128.0)
|
|
117
|
+
output_texture_data[i * img_width * 4 + j * 4 + 4] = self._limit_rgb_value(R)
|
|
118
|
+
output_texture_data[i * img_width * 4 + j * 4 + 5] = self._limit_rgb_value(G)
|
|
119
|
+
output_texture_data[i * img_width * 4 + j * 4 + 6] = self._limit_rgb_value(B)
|
|
120
|
+
output_texture_data[i * img_width * 4 + j * 4 + 7] = 0xFF
|
|
121
|
+
|
|
122
|
+
R = Y10 + 1.140 * (V - 128.0)
|
|
123
|
+
G = Y10 - 0.395 * (U - 128.0) - 0.581 * (V - 128.0)
|
|
124
|
+
B = Y10 + 2.032 * (U - 128.0)
|
|
125
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4] = self._limit_rgb_value(R)
|
|
126
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 1] = self._limit_rgb_value(G)
|
|
127
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 2] = self._limit_rgb_value(B)
|
|
128
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 3] = 0xFF
|
|
129
|
+
|
|
130
|
+
R = Y11 + 1.140 * (V - 128.0)
|
|
131
|
+
G = Y11 - 0.395 * (U - 128.0) - 0.581 * (V - 128.0)
|
|
132
|
+
B = Y11 + 2.032 * (U - 128.0)
|
|
133
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 4] = self._limit_rgb_value(R)
|
|
134
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 5] = self._limit_rgb_value(G)
|
|
135
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 6] = self._limit_rgb_value(B)
|
|
136
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 7] = 0xFF
|
|
137
|
+
|
|
138
|
+
p += 1
|
|
139
|
+
|
|
140
|
+
return output_texture_data
|
|
141
|
+
|
|
142
|
+
def _decode_yuv420_nv21_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
143
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
144
|
+
|
|
145
|
+
p: int = img_height
|
|
146
|
+
for i in range(0, img_height, 2):
|
|
147
|
+
for j in range(0, img_width, 2):
|
|
148
|
+
Y00 = float(image_data[i * img_width + j])
|
|
149
|
+
Y01 = float(image_data[i * img_width + j + 1])
|
|
150
|
+
Y10 = float(image_data[(i + 1) * img_width + j])
|
|
151
|
+
Y11 = float(image_data[(i + 1) * img_width + j + 1])
|
|
152
|
+
U = float(image_data[p * img_width + j + 1])
|
|
153
|
+
V = float(image_data[p * img_width + j])
|
|
154
|
+
|
|
155
|
+
R = Y00 + 1.140 * (V - 128.0)
|
|
156
|
+
G = Y00 - 0.395 * (U - 128.0) - 0.581 * (V - 128.0)
|
|
157
|
+
B = Y00 + 2.032 * (U - 128.0)
|
|
158
|
+
output_texture_data[i * img_width * 4 + j * 4] = self._limit_rgb_value(R)
|
|
159
|
+
output_texture_data[i * img_width * 4 + j * 4 + 1] = self._limit_rgb_value(G)
|
|
160
|
+
output_texture_data[i * img_width * 4 + j * 4 + 2] = self._limit_rgb_value(B)
|
|
161
|
+
output_texture_data[i * img_width * 4 + j * 4 + 3] = 0xFF
|
|
162
|
+
|
|
163
|
+
R = Y01 + 1.140 * (V - 128.0)
|
|
164
|
+
G = Y01 - 0.395 * (U - 128.0) - 0.581 * (V - 128.0)
|
|
165
|
+
B = Y01 + 2.032 * (U - 128.0)
|
|
166
|
+
output_texture_data[i * img_width * 4 + j * 4 + 4] = self._limit_rgb_value(R)
|
|
167
|
+
output_texture_data[i * img_width * 4 + j * 4 + 5] = self._limit_rgb_value(G)
|
|
168
|
+
output_texture_data[i * img_width * 4 + j * 4 + 6] = self._limit_rgb_value(B)
|
|
169
|
+
output_texture_data[i * img_width * 4 + j * 4 + 7] = 0xFF
|
|
170
|
+
|
|
171
|
+
R = Y10 + 1.140 * (V - 128.0)
|
|
172
|
+
G = Y10 - 0.395 * (U - 128.0) - 0.581 * (V - 128.0)
|
|
173
|
+
B = Y10 + 2.032 * (U - 128.0)
|
|
174
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4] = self._limit_rgb_value(R)
|
|
175
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 1] = self._limit_rgb_value(G)
|
|
176
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 2] = self._limit_rgb_value(B)
|
|
177
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 3] = 0xFF
|
|
178
|
+
|
|
179
|
+
R = Y11 + 1.140 * (V - 128.0)
|
|
180
|
+
G = Y11 - 0.395 * (U - 128.0) - 0.581 * (V - 128.0)
|
|
181
|
+
B = Y11 + 2.032 * (U - 128.0)
|
|
182
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 4] = self._limit_rgb_value(R)
|
|
183
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 5] = self._limit_rgb_value(G)
|
|
184
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 6] = self._limit_rgb_value(B)
|
|
185
|
+
output_texture_data[(i + 1) * img_width * 4 + j * 4 + 7] = 0xFF
|
|
186
|
+
|
|
187
|
+
p += 1
|
|
188
|
+
|
|
189
|
+
return output_texture_data
|
|
190
|
+
|
|
191
|
+
def _decode_yuv422_uyvy_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
192
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
193
|
+
|
|
194
|
+
j: int = 0
|
|
195
|
+
for i in range(0, len(image_data), 4):
|
|
196
|
+
U = float(image_data[i])
|
|
197
|
+
Y0 = float(image_data[i + 1])
|
|
198
|
+
V = float(image_data[i + 2])
|
|
199
|
+
Y1 = float(image_data[i + 3])
|
|
200
|
+
|
|
201
|
+
R0 = Y0 + 1.140 * (V - 128.0)
|
|
202
|
+
G0 = Y0 - 0.395 * (U - 128.0) - 0.581 * (V - 128.0)
|
|
203
|
+
B0 = Y0 + 2.032 * (U - 128.0)
|
|
204
|
+
|
|
205
|
+
R1 = Y1 + 1.140 * (V - 128.0)
|
|
206
|
+
G1 = Y1 - 0.395 * (U - 128.0) - 0.581 * (V - 128.0)
|
|
207
|
+
B1 = Y1 + 2.032 * (U - 128.0)
|
|
208
|
+
|
|
209
|
+
output_texture_data[j] = self._limit_rgb_value(R0)
|
|
210
|
+
output_texture_data[j + 1] = self._limit_rgb_value(G0)
|
|
211
|
+
output_texture_data[j + 2] = self._limit_rgb_value(B0)
|
|
212
|
+
output_texture_data[j + 3] = 0xFF
|
|
213
|
+
|
|
214
|
+
output_texture_data[j + 4] = self._limit_rgb_value(R1)
|
|
215
|
+
output_texture_data[j + 5] = self._limit_rgb_value(G1)
|
|
216
|
+
output_texture_data[j + 6] = self._limit_rgb_value(B1)
|
|
217
|
+
output_texture_data[j + 7] = 0xFF
|
|
218
|
+
|
|
219
|
+
j += 8
|
|
220
|
+
|
|
221
|
+
return output_texture_data
|
|
222
|
+
|
|
223
|
+
def _decode_yuv444p_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
224
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
225
|
+
|
|
226
|
+
num_pixels: int = img_width * img_height
|
|
227
|
+
y_plane_start: int = 0
|
|
228
|
+
u_plane_start: int = num_pixels
|
|
229
|
+
v_plane_start: int = num_pixels * 2
|
|
230
|
+
|
|
231
|
+
for i in range(img_height):
|
|
232
|
+
for j in range(img_width):
|
|
233
|
+
y_index = y_plane_start + i * img_width + j
|
|
234
|
+
u_index = u_plane_start + i * img_width + j
|
|
235
|
+
v_index = v_plane_start + i * img_width + j
|
|
236
|
+
|
|
237
|
+
Y = image_data[y_index]
|
|
238
|
+
U = image_data[u_index]
|
|
239
|
+
V = image_data[v_index]
|
|
240
|
+
|
|
241
|
+
R = Y + 1.140 * (V - 128)
|
|
242
|
+
G = Y - 0.395 * (U - 128) - 0.581 * (V - 128)
|
|
243
|
+
B = Y + 2.032 * (U - 128)
|
|
244
|
+
|
|
245
|
+
R = self._limit_rgb_value(R)
|
|
246
|
+
G = self._limit_rgb_value(G)
|
|
247
|
+
B = self._limit_rgb_value(B)
|
|
248
|
+
|
|
249
|
+
rgba_index = (i * img_width + j) * 4
|
|
250
|
+
output_texture_data[rgba_index] = R
|
|
251
|
+
output_texture_data[rgba_index + 1] = G
|
|
252
|
+
output_texture_data[rgba_index + 2] = B
|
|
253
|
+
output_texture_data[rgba_index + 3] = 0xFF
|
|
254
|
+
|
|
255
|
+
return output_texture_data
|
|
256
|
+
|
|
257
|
+
def _decode_yuv410p_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
258
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
259
|
+
num_pixels: int = img_width * img_height
|
|
260
|
+
uv_width: int = img_width // 4
|
|
261
|
+
uv_height: int = img_height // 4
|
|
262
|
+
uv_size: int = uv_width * uv_height
|
|
263
|
+
|
|
264
|
+
y_plane_start: int = 0
|
|
265
|
+
u_plane_start: int = num_pixels
|
|
266
|
+
v_plane_start: int = num_pixels + uv_size
|
|
267
|
+
|
|
268
|
+
for i in range(img_height):
|
|
269
|
+
for j in range(img_width):
|
|
270
|
+
y_index = y_plane_start + i * img_width + j
|
|
271
|
+
u_index = u_plane_start + (i // 4) * uv_width + (j // 4)
|
|
272
|
+
v_index = v_plane_start + (i // 4) * uv_width + (j // 4)
|
|
273
|
+
|
|
274
|
+
Y = image_data[y_index]
|
|
275
|
+
U = image_data[u_index]
|
|
276
|
+
V = image_data[v_index]
|
|
277
|
+
|
|
278
|
+
R = Y + 1.140 * (V - 128)
|
|
279
|
+
G = Y - 0.395 * (U - 128) - 0.581 * (V - 128)
|
|
280
|
+
B = Y + 2.032 * (U - 128)
|
|
281
|
+
|
|
282
|
+
R = self._limit_rgb_value(R)
|
|
283
|
+
G = self._limit_rgb_value(G)
|
|
284
|
+
B = self._limit_rgb_value(B)
|
|
285
|
+
|
|
286
|
+
rgba_index = (i * img_width + j) * 4
|
|
287
|
+
output_texture_data[rgba_index] = R
|
|
288
|
+
output_texture_data[rgba_index + 1] = G
|
|
289
|
+
output_texture_data[rgba_index + 2] = B
|
|
290
|
+
output_texture_data[rgba_index + 3] = 0xFF
|
|
291
|
+
|
|
292
|
+
return output_texture_data
|
|
293
|
+
|
|
294
|
+
def _decode_yuv420p_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
295
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
296
|
+
num_pixels: int = img_width * img_height
|
|
297
|
+
uv_width: int = img_width // 2
|
|
298
|
+
uv_height: int = img_height // 2
|
|
299
|
+
uv_size: int = uv_width * uv_height
|
|
300
|
+
|
|
301
|
+
y_plane_start: int = 0
|
|
302
|
+
u_plane_start: int = num_pixels
|
|
303
|
+
v_plane_start: int = num_pixels + uv_size
|
|
304
|
+
|
|
305
|
+
for i in range(img_height):
|
|
306
|
+
for j in range(img_width):
|
|
307
|
+
y_index = y_plane_start + i * img_width + j
|
|
308
|
+
u_index = u_plane_start + (i // 2) * uv_width + (j // 2)
|
|
309
|
+
v_index = v_plane_start + (i // 2) * uv_width + (j // 2)
|
|
310
|
+
|
|
311
|
+
Y = image_data[y_index]
|
|
312
|
+
U = image_data[u_index]
|
|
313
|
+
V = image_data[v_index]
|
|
314
|
+
|
|
315
|
+
R = Y + 1.140 * (V - 128)
|
|
316
|
+
G = Y - 0.395 * (U - 128) - 0.581 * (V - 128)
|
|
317
|
+
B = Y + 2.032 * (U - 128)
|
|
318
|
+
|
|
319
|
+
R = self._limit_rgb_value(R)
|
|
320
|
+
G = self._limit_rgb_value(G)
|
|
321
|
+
B = self._limit_rgb_value(B)
|
|
322
|
+
|
|
323
|
+
rgba_index = (i * img_width + j) * 4
|
|
324
|
+
output_texture_data[rgba_index] = R
|
|
325
|
+
output_texture_data[rgba_index + 1] = G
|
|
326
|
+
output_texture_data[rgba_index + 2] = B
|
|
327
|
+
output_texture_data[rgba_index + 3] = 0xFF
|
|
328
|
+
|
|
329
|
+
return output_texture_data
|
|
330
|
+
|
|
331
|
+
def _decode_yuv422p_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
332
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
333
|
+
num_pixels: int = img_width * img_height
|
|
334
|
+
uv_width: int = img_width // 2
|
|
335
|
+
uv_size: int = uv_width * img_height
|
|
336
|
+
|
|
337
|
+
y_plane_start: int = 0
|
|
338
|
+
u_plane_start: int = num_pixels
|
|
339
|
+
v_plane_start: int = num_pixels + uv_size
|
|
340
|
+
|
|
341
|
+
for i in range(img_height):
|
|
342
|
+
for j in range(img_width):
|
|
343
|
+
y_index = y_plane_start + i * img_width + j
|
|
344
|
+
u_index = u_plane_start + i * uv_width + (j // 2)
|
|
345
|
+
v_index = v_plane_start + i * uv_width + (j // 2)
|
|
346
|
+
|
|
347
|
+
Y = image_data[y_index]
|
|
348
|
+
U = image_data[u_index]
|
|
349
|
+
V = image_data[v_index]
|
|
350
|
+
|
|
351
|
+
R = Y + 1.140 * (V - 128)
|
|
352
|
+
G = Y - 0.395 * (U - 128) - 0.581 * (V - 128)
|
|
353
|
+
B = Y + 2.032 * (U - 128)
|
|
354
|
+
|
|
355
|
+
R = self._limit_rgb_value(R)
|
|
356
|
+
G = self._limit_rgb_value(G)
|
|
357
|
+
B = self._limit_rgb_value(B)
|
|
358
|
+
|
|
359
|
+
rgba_index = (i * img_width + j) * 4
|
|
360
|
+
output_texture_data[rgba_index] = R
|
|
361
|
+
output_texture_data[rgba_index + 1] = G
|
|
362
|
+
output_texture_data[rgba_index + 2] = B
|
|
363
|
+
output_texture_data[rgba_index + 3] = 0xFF
|
|
364
|
+
|
|
365
|
+
return output_texture_data
|
|
366
|
+
|
|
367
|
+
def _decode_yuv411p_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
368
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
369
|
+
num_pixels: int = img_width * img_height
|
|
370
|
+
uv_width: int = img_width // 4
|
|
371
|
+
uv_size: int = uv_width * img_height
|
|
372
|
+
|
|
373
|
+
y_plane_start: int = 0
|
|
374
|
+
u_plane_start: int = num_pixels
|
|
375
|
+
v_plane_start: int = num_pixels + uv_size
|
|
376
|
+
|
|
377
|
+
for i in range(img_height):
|
|
378
|
+
for j in range(img_width):
|
|
379
|
+
y_index = y_plane_start + i * img_width + j
|
|
380
|
+
u_index = u_plane_start + i * uv_width + (j // 4)
|
|
381
|
+
v_index = v_plane_start + i * uv_width + (j // 4)
|
|
382
|
+
|
|
383
|
+
Y = image_data[y_index]
|
|
384
|
+
U = image_data[u_index]
|
|
385
|
+
V = image_data[v_index]
|
|
386
|
+
|
|
387
|
+
R = Y + 1.140 * (V - 128)
|
|
388
|
+
G = Y - 0.395 * (U - 128) - 0.581 * (V - 128)
|
|
389
|
+
B = Y + 2.032 * (U - 128)
|
|
390
|
+
|
|
391
|
+
R = self._limit_rgb_value(R)
|
|
392
|
+
G = self._limit_rgb_value(G)
|
|
393
|
+
B = self._limit_rgb_value(B)
|
|
394
|
+
|
|
395
|
+
rgba_index = (i * img_width + j) * 4
|
|
396
|
+
output_texture_data[rgba_index] = R
|
|
397
|
+
output_texture_data[rgba_index + 1] = G
|
|
398
|
+
output_texture_data[rgba_index + 2] = B
|
|
399
|
+
output_texture_data[rgba_index + 3] = 0xFF
|
|
400
|
+
|
|
401
|
+
return output_texture_data
|
|
402
|
+
|
|
403
|
+
def _decode_yuv411_uyyvyy411_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
404
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
405
|
+
|
|
406
|
+
def set_pixel(out_rgba_array, row, col, R, G, B):
|
|
407
|
+
out_index = (row * img_width + col) * 4
|
|
408
|
+
out_rgba_array[out_index] = R
|
|
409
|
+
out_rgba_array[out_index + 1] = G
|
|
410
|
+
out_rgba_array[out_index + 2] = B
|
|
411
|
+
out_rgba_array[out_index + 3] = 0xFF
|
|
412
|
+
|
|
413
|
+
for i in range(img_height):
|
|
414
|
+
for j in range(0, img_width, 4):
|
|
415
|
+
index: int = int(i * img_width * 1.5 + j * 1.5)
|
|
416
|
+
|
|
417
|
+
U = image_data[index]
|
|
418
|
+
Y0 = image_data[index + 1]
|
|
419
|
+
Y1 = image_data[index + 2]
|
|
420
|
+
V = image_data[index + 3]
|
|
421
|
+
Y2 = image_data[index + 4]
|
|
422
|
+
Y3 = image_data[index + 5]
|
|
423
|
+
|
|
424
|
+
R0, G0, B0 = self._yuv_to_rgb(Y0, U, V)
|
|
425
|
+
R1, G1, B1 = self._yuv_to_rgb(Y1, U, V)
|
|
426
|
+
R2, G2, B2 = self._yuv_to_rgb(Y2, U, V)
|
|
427
|
+
R3, G3, B3 = self._yuv_to_rgb(Y3, U, V)
|
|
428
|
+
|
|
429
|
+
set_pixel(output_texture_data, i, j, R0, G0, B0)
|
|
430
|
+
set_pixel(output_texture_data, i, j + 1, R1, G1, B1)
|
|
431
|
+
set_pixel(output_texture_data, i, j + 2, R2, G2, B2)
|
|
432
|
+
set_pixel(output_texture_data, i, j + 3, R3, G3, B3)
|
|
433
|
+
|
|
434
|
+
return output_texture_data
|
|
435
|
+
|
|
436
|
+
def _decode_yuv440p_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
437
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
438
|
+
y_plane_size: int = img_width * img_height
|
|
439
|
+
uv_plane_size: int = img_width * (img_height // 2)
|
|
440
|
+
|
|
441
|
+
def set_pixel(out_rgba_array, row, col, R, G, B):
|
|
442
|
+
out_index = (row * img_width + col) * 4
|
|
443
|
+
out_rgba_array[out_index] = R
|
|
444
|
+
out_rgba_array[out_index + 1] = G
|
|
445
|
+
out_rgba_array[out_index + 2] = B
|
|
446
|
+
out_rgba_array[out_index + 3] = 0xFF
|
|
447
|
+
|
|
448
|
+
y_plane = image_data[0:y_plane_size]
|
|
449
|
+
u_plane = image_data[y_plane_size:y_plane_size + uv_plane_size]
|
|
450
|
+
v_plane = image_data[y_plane_size + uv_plane_size:y_plane_size + 2 * uv_plane_size]
|
|
451
|
+
|
|
452
|
+
for i in range(img_height):
|
|
453
|
+
for j in range(img_width):
|
|
454
|
+
Y = y_plane[i * img_width + j]
|
|
455
|
+
U = u_plane[(i // 2) * img_width + j]
|
|
456
|
+
V = v_plane[(i // 2) * img_width + j]
|
|
457
|
+
|
|
458
|
+
R, G, B = self._yuv_to_rgb(Y, U, V)
|
|
459
|
+
set_pixel(output_texture_data, i, j, R, G, B)
|
|
460
|
+
|
|
461
|
+
return output_texture_data
|
|
462
|
+
|
|
463
|
+
def _decode_yuva420p_image(self, image_data: bytes, img_width: int, img_height: int) -> bytes:
|
|
464
|
+
output_texture_data = bytearray(img_width * img_height * 4)
|
|
465
|
+
y_plane_size: int = img_width * img_height
|
|
466
|
+
uv_plane_size: int = (img_width // 2) * (img_height // 2)
|
|
467
|
+
|
|
468
|
+
def set_pixel(out_rgba_array, row, col, R, G, B, A):
|
|
469
|
+
out_index = (row * img_width + col) * 4
|
|
470
|
+
out_rgba_array[out_index] = R
|
|
471
|
+
out_rgba_array[out_index + 1] = G
|
|
472
|
+
out_rgba_array[out_index + 2] = B
|
|
473
|
+
out_rgba_array[out_index + 3] = A
|
|
474
|
+
|
|
475
|
+
y_plane = image_data[0:y_plane_size]
|
|
476
|
+
u_plane = image_data[y_plane_size:y_plane_size + uv_plane_size]
|
|
477
|
+
v_plane = image_data[y_plane_size + uv_plane_size:y_plane_size + 2 * uv_plane_size]
|
|
478
|
+
a_plane = image_data[y_plane_size + 2 * uv_plane_size:]
|
|
479
|
+
|
|
480
|
+
for i in range(img_height):
|
|
481
|
+
for j in range(img_width):
|
|
482
|
+
Y = y_plane[i * img_width + j]
|
|
483
|
+
U = u_plane[(i // 2) * (img_width // 2) + (j // 2)]
|
|
484
|
+
V = v_plane[(i // 2) * (img_width // 2) + (j // 2)]
|
|
485
|
+
A = a_plane[i * img_width + j]
|
|
486
|
+
R, G, B = self._yuv_to_rgb(Y, U, V)
|
|
487
|
+
set_pixel(output_texture_data, i, j, R, G, B, A)
|
|
488
|
+
|
|
489
|
+
return output_texture_data
|
|
490
|
+
|
|
491
|
+
def decode_yuv_image_main(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats) -> bytes:
|
|
492
|
+
self._check_if_yuv_image_dimensions_are_correct(img_width, img_height)
|
|
493
|
+
|
|
494
|
+
if image_format == ImageFormats.YUV422_YUY2:
|
|
495
|
+
return self._decode_yuv422_yuy2_image(image_data, img_width, img_height)
|
|
496
|
+
elif image_format == ImageFormats.YUV420_NV12:
|
|
497
|
+
return self._decode_yuv420_nv12_image(image_data, img_width, img_height)
|
|
498
|
+
elif image_format == ImageFormats.YUV420_NV21:
|
|
499
|
+
return self._decode_yuv420_nv21_image(image_data, img_width, img_height)
|
|
500
|
+
elif image_format == ImageFormats.YUV422_UYVY:
|
|
501
|
+
return self._decode_yuv422_uyvy_image(image_data, img_width, img_height)
|
|
502
|
+
elif image_format == ImageFormats.YUV444P:
|
|
503
|
+
return self._decode_yuv444p_image(image_data, img_width, img_height)
|
|
504
|
+
elif image_format == ImageFormats.YUV410P:
|
|
505
|
+
return self._decode_yuv410p_image(image_data, img_width, img_height)
|
|
506
|
+
elif image_format == ImageFormats.YUV420P:
|
|
507
|
+
return self._decode_yuv420p_image(image_data, img_width, img_height)
|
|
508
|
+
elif image_format == ImageFormats.YUV422P:
|
|
509
|
+
return self._decode_yuv422p_image(image_data, img_width, img_height)
|
|
510
|
+
elif image_format == ImageFormats.YUV411P:
|
|
511
|
+
return self._decode_yuv411p_image(image_data, img_width, img_height)
|
|
512
|
+
elif image_format == ImageFormats.YUV411_UYYVYY411:
|
|
513
|
+
return self._decode_yuv411_uyyvyy411_image(image_data, img_width, img_height)
|
|
514
|
+
elif image_format == ImageFormats.YUV440P:
|
|
515
|
+
return self._decode_yuv440p_image(image_data, img_width, img_height)
|
|
516
|
+
elif image_format == ImageFormats.YUVA420P:
|
|
517
|
+
return self._decode_yuva420p_image(image_data, img_width, img_height)
|
|
518
|
+
else:
|
|
519
|
+
raise Exception(f"Image format not supported by yuv decoder! Image_format: {image_format}")
|
|
@@ -130,7 +130,7 @@ def _convert_ps2_16bit(image_data: bytes, width: int, height: int, swizzle_flag:
|
|
|
130
130
|
return converted_data
|
|
131
131
|
|
|
132
132
|
|
|
133
|
-
# special 4-bit swizzle/unswizzle function used in SSH files from EA PS2 games
|
|
133
|
+
# special "type 3" 4-bit swizzle/unswizzle function used in SSH files from EA PS2 games
|
|
134
134
|
def _convert_ps2_ea_4bit(image_data: bytes, img_width: int, img_height: int, bpp: int, swizzle_flag: bool) -> bytes:
|
|
135
135
|
if bpp != 4:
|
|
136
136
|
raise Exception(f"Not supported bpp={bpp} for EA swizzle!")
|
|
@@ -140,11 +140,11 @@ def _convert_ps2_ea_4bit(image_data: bytes, img_width: int, img_height: int, bpp
|
|
|
140
140
|
|
|
141
141
|
try:
|
|
142
142
|
ea_swizzle_dll_path: str = get_dll_path("ea_swizzle.dll")
|
|
143
|
-
|
|
143
|
+
ea_swizzle_dll_file = ctypes.CDLL(ea_swizzle_dll_path)
|
|
144
144
|
if not swizzle_flag:
|
|
145
|
-
|
|
145
|
+
ea_swizzle_dll_file.unswizzle4(image_data, byref(converted_data_buffer), img_width, img_height)
|
|
146
146
|
else:
|
|
147
|
-
|
|
147
|
+
ea_swizzle_dll_file.swizzle4(image_data, byref(converted_data_buffer), img_width, img_height)
|
|
148
148
|
except Exception as error:
|
|
149
149
|
raise Exception(f"Error while unswizzling data! Error: {error}")
|
|
150
150
|
|
|
File without changes
|
|
Binary file
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Copyright © 2024 Bartłomiej Duda
|
|
3
|
-
License: GPL-3.0 License
|
|
4
|
-
"""
|
|
5
|
-
import ctypes
|
|
6
|
-
from ctypes import byref, c_char
|
|
7
|
-
|
|
8
|
-
from reversebox.common.common import get_dll_path
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class RefpackHandler:
|
|
12
|
-
def compress_data(self, input_data: bytes) -> bytes:
|
|
13
|
-
return input_data # TODO - fix this
|
|
14
|
-
|
|
15
|
-
def _get_output_buffer_size(self, compressed_data: bytes) -> int:
|
|
16
|
-
current_input_offset: int = 0
|
|
17
|
-
compression_type = compressed_data[current_input_offset]
|
|
18
|
-
current_input_offset += 1
|
|
19
|
-
compression_type = (compression_type << 8) + compressed_data[
|
|
20
|
-
current_input_offset
|
|
21
|
-
]
|
|
22
|
-
current_input_offset += 1
|
|
23
|
-
|
|
24
|
-
if compression_type & 0x100:
|
|
25
|
-
current_input_offset += 3 # skip uncompressed_size
|
|
26
|
-
|
|
27
|
-
uncompressed_size: int = compressed_data[current_input_offset]
|
|
28
|
-
current_input_offset += 1
|
|
29
|
-
uncompressed_size = (uncompressed_size << 8) + compressed_data[
|
|
30
|
-
current_input_offset
|
|
31
|
-
]
|
|
32
|
-
current_input_offset += 1
|
|
33
|
-
uncompressed_size = (uncompressed_size << 8) + compressed_data[
|
|
34
|
-
current_input_offset
|
|
35
|
-
]
|
|
36
|
-
current_input_offset += 1
|
|
37
|
-
return uncompressed_size
|
|
38
|
-
|
|
39
|
-
def decompress_data(self, compressed_data: bytes) -> bytes:
|
|
40
|
-
if len(compressed_data) < 5:
|
|
41
|
-
raise Exception("Compressed data too short!")
|
|
42
|
-
if compressed_data[:2] != b"\x10\xFB":
|
|
43
|
-
raise Exception("Wrong refpack compression header!")
|
|
44
|
-
uncompressed_data_buffer = (
|
|
45
|
-
c_char * self._get_output_buffer_size(compressed_data)
|
|
46
|
-
)()
|
|
47
|
-
try:
|
|
48
|
-
refpack_dll_path: str = get_dll_path("refpack.dll")
|
|
49
|
-
refpack_dll_file = ctypes.CDLL(refpack_dll_path)
|
|
50
|
-
uncompressed_data_size = refpack_dll_file.unrefpack(
|
|
51
|
-
compressed_data, byref(uncompressed_data_buffer), 1
|
|
52
|
-
)
|
|
53
|
-
except Exception as error:
|
|
54
|
-
raise Exception(f"Error while decompressing refpack data! Error: {error}")
|
|
55
|
-
return bytes(bytearray(uncompressed_data_buffer)[:uncompressed_data_size])
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/checksum/checksum_internet_ipv4_header.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/encryption/encryption_xor_basic_key_guesser.py
RENAMED
|
File without changes
|
{ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/encryption/encryption_xor_gianas_return_zda.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ReverseBox-0.18.2/reversebox/io_files → ReverseBox-0.18.4/reversebox/image/swizzling}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ReverseBox-0.18.2 → ReverseBox-0.18.4}/reversebox/image/swizzling/swizzle_morton_dreamcast.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|