UnityPy icon indicating copy to clipboard operation
UnityPy copied to clipboard

Bug withTexture2DConverter

Open AXiX-official opened this issue 1 year ago • 3 comments

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

AXiX-official avatar Jun 03 '24 08:06 AXiX-official

That sounds like it might be related to compression? Do you recompress the textures / assign to .image?

K0lb3 avatar Aug 30 '24 22:08 K0lb3

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.

AXiX-official avatar Aug 31 '24 01:08 AXiX-official

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.

K0lb3 avatar Sep 01 '24 19:09 K0lb3