Bug withTexture2DConverter
Code
if __name__ == "__main__":
filename = "22"
env = UnityPy.load(f"assetbundles/{filename}")
for obj in env.objects:
if obj.type.name == "Texture2D":
data = obj.read()
# data.image.save(f"workspace/mod/{filename}.png")
img = Image.open(f"workspace/mod/{filename}.png")
data.set_image(img)
data.save()
break
with open(f"workspace/mod/{filename}u", "wb") as f:
f.write(env.file.save(packer="none"))
Error No error message.
Bug When I work with a series of textures in ETC2_RGBA8 format, 192*256 size, the texture in the modified file becomes blurry, however, there is no problem for other textures that are also in ETC2_RGBA8 format, but in different sizes.
To Reproduce
- a copy of the file that causes the problem
- following data:
- Python version 3.8.10
- UnityPy version 1.10.7
That sounds like it might be related to compression? Do you recompress the textures / assign to .image?
Does assign to .image mean something like this
for obj in env.objects:
if obj.type.name == “Texture2D”:
data = obj.read()
img = data.image
img.save("test.png")
Yes, the image I used above was saved to png like this
It actually calls Texture2DConverter.image_to_texture2d and Texture2DConverter.get_image_from_texture2d
I've also tried using ectpak directly.
from PIL import Image
from UnityPy.export import Texture2DConverter
from UnityPy.enums import TextureFormat
import etcpak
img = Image.open("jialimaoxian_2_rw.png")
r, g, b, a = img.split()
raw_img = Image.merge("RGBA", (b, g, r, a)).tobytes()
enc_img = etcpak.compress_to_etc2_rgba(raw_img, img.width, img.height)
tex_format = TextureFormat.ETC2_RGBA8
tex = texture_2d(enc_img, tex_format, img.width, img.height)
new_tex = Texture2DConverter.get_image_from_texture2d(tex)
new_tex = new_tex.transpose(Image.FLIP_TOP_BOTTOM)
new_tex.save("new_test.png")
Also experiencing apparent secondary compression, I'm not sure if it's a parameter issue because I didn't find the relevant parameter I'm guessing this might be a problem with ectpak, but I'm not sure if etcpak 2.0 is less so
I'm using TexToolWrap.dll from UABEA as a temporary handler, for ETC2_RGBA8 UABEA uses PVRTexLib
import ctypes
from ctypes import c_uint, c_int, c_void_p
dll = ctypes.CDLL(r"F:\Rider\UABEA\TexToolWrap\bin\x64\Release\TexToolWrap.dll")
dll.DecodeByPVRTexLib.argtypes = [c_void_p, c_void_p, c_int, c_uint, c_uint]
dll.DecodeByPVRTexLib.restype = c_uint
dll.EncodeByPVRTexLib.argtypes = [c_void_p, c_void_p, c_int, c_int, c_uint, c_uint]
dll.EncodeByPVRTexLib.restype = c_uint
def DecodePVRTexLib(data, width, height, format) -> np.ndarray:
data = (ctypes.c_ubyte * len(data)).from_buffer_copy(data)
width = c_uint(width)
height = c_uint(height)
format = c_int(format)
size = width.value * height.value * 4
out = (ctypes.c_ubyte * size)()
dll.DecodeByPVRTexLib(data, out, format, width, height)
return np.frombuffer(out, dtype=np.uint8).reshape((height.value, width.value, 4))
def RGBAToFormatByteSize(format, width, height) -> int:
if format == 4 or format == 5:
return int(width) * int(height) * 4
elif format == 47:
blockCountX = (width + 3) / 4
blockCountY = (height + 3) / 4
return int(blockCountX) * int(blockCountY) * 16
else:
raise Exception("Texture format is not correct")
def EncodeByPVRTexLib(data, mode, level, width, height) -> bytes:
data = (ctypes.c_ubyte * len(data)).from_buffer_copy(data)
buf = (ctypes.c_ubyte * RGBAToFormatByteSize(mode, width, height))()
mode = c_int(mode)
level = c_int(level)
width = c_uint(width)
height = c_uint(height)
size = dll.EncodeByPVRTexLib(data, buf, mode, level, width, height)
if size == len(buf):
return bytes(buf)
else:
raise Exception("Encode failed")
This treatment does not encounter the aforementioned problem of secondary compression But PVRTexLib is not open source, and although he provides a Python package it only seems to support x64 and I wasn't able to run it correctly.
Thanks for the more detailed explanation.
Looks like it's an issue with etcpak considering your explanation. etcpak 2 likely won't fix the issue, I'm with you on that.
I guess the only real good overall open source solution might be basis_universal which is a bit of a pain to wrap, but I guess that I can attempt it again, as I gained quite some additional experience with it in between now and the last time I tried to make a wrapper for it.