K0lb3 / UnityPy

UnityPy is python module that makes it possible to extract/unpack and edit Unity assets
MIT License
761 stars 113 forks source link

Texture2D 2022.2 bug #226

Closed JunkBeat closed 5 months ago

JunkBeat commented 5 months ago
  1. There are bugs related to reading/writing Texture2D in Unity 2022.2. To fix this I changed the code as follows and it worked:
if version >= (2022, 2, 0):  # 2022.2.0f1 and up
            self.m_IgnoreMipmapLimit = reader.read_boolean()
            reader.align_stream()
            self.m_MipmapLimitGroupName = reader.read_aligned_string()
            reader.align_stream()

. . .

if version >= (2022, 2, 0):  # 2022.2.0f1 and up
            writer.write_boolean(self.m_IgnoreMipmapLimit)
            writer.align_stream()
            writer.write_aligned_string(self.m_MipmapLimitGroupName)
            writer.align_stream()

Example file: https://drive.google.com/file/d/1TyA0H9myZyvk46MYVAVDW_aiu-Cm2miQ/view?usp=sharing

  1. I also encountered problems with poor compression in dxt1/dxt5. The visual artifacts appear on some textures. You can see it visually here (I used the same libraries as UnityPy): https://drive.google.com/file/d/1MZTIq6J_WSYR2IlZELi0rexfxKDO-WCZ/view?usp=sharing
K0lb3 commented 5 months ago

You're right, there has to be an align_stream call after reading the m_IgnoreMipmapLimit value. The second align stream isn't necessary through, as read_aligned_string already calls align_stream.

The visual artifacts are caused by the c-library that is used for compression wolfpld/etcpak. As said library doesn't have quality settings, there isn't really anything that can be done about the visual artifacts, besides using a different library for compression, or simply enforcing rgba8 as texture format, which increases the asset size by 4x of the texture size, but should work for all normal material textures.

K0lb3 commented 5 months ago

You can try using K0lb3/ispc_texcomp_py which I never came around to make a good build workflow for.

JunkBeat commented 5 months ago

You can try using K0lb3/ispc_texcomp_py which I never came around to make a good build workflow for.

Okay, thanks for the advice, I'll try it.

JunkBeat commented 5 months ago

What am I doing wrong? I'm trying to compress a 512x512 rgba image, but I get an error:

RGBASurface_new
RGBASurface_init
Traceback (most recent call last):
  File "dxt_bad_compression\pycompression_test.py", line 22, in <module>
    surface = ispc_texcomp_py.RGBASurface(raw, img.width, img.height, stride)
TypeError: The stride value is too big!
It has to be the size of a single row in bytes. (e.g. for RGBA -> 4 * width)

Here's my code:

import texture2ddecoder
import ispc_texcomp_py
from PIL import Image

img = Image.open("img_2.png")
raw = img.tobytes("raw", "RGBA")

# compress
stride = img.width * 4
surface = ispc_texcomp_py.RGBASurface(raw, img.width, img.height, stride)
data: bytes = ispc_texcomp_py.CompressBlocksBC3(surface)

# decompress
dimg = Image.frombytes("RGBA", img.size, texture2ddecoder.decode_bc3(data, img.width, img.height), "raw", "BGRA")
dimg.show()

However, this error does not occur if I specify a number one less, that is, 2047, and here is the result after decompression: изображение_2024-01-26_000045747

UPD. I looked at the code of your module and, out of curiosity, set the stride to 0, and it seems that now everything worked perfectly!

K0lb3 commented 5 months ago

UPD. I looked at the code of your module and, out of curiosity, set the stride to 0, and it seems that now everything worked perfectly!

Great to hear. It's a bit weird that stride 0 worked, but as long as it works.

JunkBeat commented 5 months ago

It's a bit weird that stride 0 worked, but as long as it works.

Yeah, I was surprised too, but when I did the calculations I was convinced that both options (set to 0 or multiply the width by 4) would lead to the correct result, even if the width differs from the height:

#Case 1. Height > Width
len = 698368
width = 341
height = 512

stride = width * 4 #1364
# if stride set to 0:
stride = len / height #1364

if (stride * height > len): #1364 * 512 == 698368
    print("Error")

#Case 2. Height < Width
len = 33554432
width = 4096
height = 2048

stride = width * 4 #16384
# if stride set to 0:
stride = len / height #16384

if (stride * height > len): #16384 * 2048 == 33554432
    print("Error")

You have an incorrect condition in this line: if (self->surf.stride * self->surf.height >= view.len) Instead of >= there should be >

UPD. However, yesterday I came across an rgba texture 490x490, the module could not compress it correctly until I enlarged it to 512x512. Is there a way to fix this without having to change the texture size? It seems that the dimensions must be multiples of the power of 2?