Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug withTexture2DConverter #238

Open
AXiX-official opened this issue Jun 3, 2024 · 3 comments
Open

Bug withTexture2DConverter #238

AXiX-official opened this issue Jun 3, 2024 · 3 comments
Assignees

Comments

@AXiX-official
Copy link
Contributor

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 AXiX-official added the bug Something isn't working label Jun 3, 2024
@K0lb3
Copy link
Owner

K0lb3 commented Aug 30, 2024

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

@K0lb3 K0lb3 self-assigned this Aug 30, 2024
@AXiX-official
Copy link
Contributor Author

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.

@K0lb3
Copy link
Owner

K0lb3 commented Sep 1, 2024

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 K0lb3 added dependency-bug and removed bug Something isn't working labels Sep 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants