Closed AXiX-official closed 4 months ago
I fixed code in Texture2D.py
tring to edit an existed cab and get error.
in Texture2D.py
@image_data.setter
def image_data(self, data: bytes):
self._image_data = data
# ignore writing to cab for now until it's more stable
if self.version >= (5, 3) and self.m_StreamData.path:
path = self.m_StreamData.path
cab = self.assets_file.get_writeable_cab(path.split("/")[-1])
if cab:
self.m_StreamData.offset = cab.Position
cab.write(data)
self.m_StreamData.size = len(data)
self.m_StreamData.path = cab.path
and i got this
Traceback (most recent call last):
File "F:\blhx\lsns-decryptor\main.py", line 19, in <module>
data.image = pil_img
File "F:\blhx\lsns-decryptor\venv\lib\site-packages\UnityPy\classes\Texture2D.py", line 40, in image
self.image_data = img_data
File "F:\blhx\lsns-decryptor\venv\lib\site-packages\UnityPy\classes\Texture2D.py", line 72, in image_data
cab = self.assets_file.get_writeable_cab(path.split("/")[-1])
File "F:\blhx\lsns-decryptor\venv\lib\site-packages\UnityPy\files\SerializedFile.py", line 417, in get_writeable_cab
cab = self.parent.get_writeable_cab(name)
File "F:\blhx\lsns-decryptor\venv\lib\site-packages\UnityPy\files\File.py", line 106, in get_writeable_cab
raise ValueError(
ValueError: This cab already exists and isn't an EndianBinaryWriter
can i remove this cab and add modified cab myself?
Hello. Can you provide the bundle file? I'm just curious, maybe I can help.
ui_jiazai.zip
the origin file uses unityCN encrypted,I made a decrypt version according to the RazTools/Studio
but the problem i met has nothing to do with this,because it's a widely existed problem.the source code said writing to cab for is not stable
.
I tested two other games in version 2019.4.40f1、2018.4.34f1(one is azur lane)and got the same,though in those games this dosent matter.
use UABEA can remove cab record and use unitypy add a new modfied one,but it changed file struct a low makes my encrypt application strike
It's strange but UnityPy thinks the file is encrypted LookupError: The BundleFile is encrypted, but no key was provided!
haha,it's becauese in that version of decryptor i forgot fix header's flags and blocks' flag so UnityPy thinks the file is encrypted. however,i said it's not file's problem.editing cab is a not fully supported feature in both unitypy and uabea ui_jiazai.zip
I'm happy to tell you that I managed to solve this problem.
In fact if you simply remove the cab-****.resS
file via uabea's remove
and then add the cab
using unitypy it does corrupt the file. The problem is that when uabea removes the .resS
file, it does not remove the corresponding reference in the cab
that references it, and when it adds the cab
later using unitypy, it will add additional records to it. (You can verify this yourself by looking at the contents of the modified file).
So my solution to this is, in cases where the .resS
file I need to work with is just Texture2D
data, to somehow convert the modified.png
that I need to replace to the textual2D binary that unity uses, e.g. by using the unitypy
img_data, tex_format = Texture2DConverter.image_to_texture2d(
img, self.m_TextureFormat
)
Then find the corresponding data in the unpacked bundlefile and replace it directly before repacking. This completely solves the problem I encountered above. After that I might write a program to do it? Maybe
You can write an embedded texture using typetree changing just a few values (not in resS).
from PIL import Image
for obj in env.objects:
if obj.type.name == "Texture2D":
data = obj.read()
fp = os.path.join(replace_dir, data.name)
pil_img = Image.open(fp)
# UnityPy converts to original format by default
data.set_image(pil_img)
data.save_via_tree()
classes\Texture2D.py
def save_via_tree(self):
tree = self.read_typetree()
if "m_MipMap" in tree:
tree["m_MipMap"] = self.m_MipMap
else:
tree["m_MipCount"] = self.m_MipCount
tree["m_TextureFormat"] = self.m_TextureFormat
tree["m_CompleteImageSize"] = len(self.image_data)
tree["image data"] = self.image_data
# Reset StreamData
tree["m_StreamData"] = {
"offset": 0,
"size": 0,
"path": ""
}
self.save_typetree_dump(tree)
classes\Object.py
def save_typetree_dump(self, tree: dict, nodes: list = None, writer: EndianBinaryWriter = None):
return self.reader.save_typetree(tree, nodes, writer)
Saving
for name, file in env.files.items():
if hasattr(file, "is_changed") and file.is_changed:
file_name = os.path.basename(name)
with open(os.path.join(dest, file_name), "wb") as f:
f.write(file.save(packer="original"))
thanks for your reply
my solution be like this
import os
from PIL import Image
import UnityPy
from UnityPy.export import Texture2DConverter
dir = r"work"
src = r"test.asset"
dst = r"work\test.asset.fix"
# set unityCN key for unityCN
# bundle_key = ""
# UnityPy.set_assetbundle_decrypt_key(bundle_key)
# load file
env = UnityPy.load(os.path.join(dir, src))
# view Texture2D info
# some file has more than one texture2D data store in .resS
textureList = {}
totalSize = 0
for obj in env.objects:
if obj.type.name == "Texture2D":
data = obj.read()
# print(data.name)
# print(f"offset:{data.m_StreamData.offset}")
# print(f"size:{data.m_StreamData.size}")
# print(f"path:{data.m_StreamData.path}")
# print(f"TextureFormat:{data.m_TextureFormat}")
textureList[data.name] = {
"offset": data.m_StreamData.offset,
"size": data.m_StreamData.size,
"path": data.m_StreamData.path,
"TextureFormat": data.m_TextureFormat
}
totalSize += data.m_StreamData.size
# convert data
# toLoadImg should be the same wide/height as toEditTexture
# so that img_data has the same size as data.m_StreamData.size
toEditTextureName = "example"
toLoadImg = os.path.join(dir, "example.fix.png")
img = Image.open(toLoadImg)
toEditTexture = textureList[toEditTextureName]
img_data, tex_format = Texture2DConverter.image_to_texture2d(
img, toEditTexture["TextureFormat"]
)
# replace data with different size is possible
# when needed i will add support
if len(img_data) != toEditTexture["size"]:
raise ValueError(f"data size missmatch,except {toEditTexture['size']} but recieved {len(img_data)}")
# save file with no compression
newFileData = bytearray(env.file.save(packer="none"))
size = len(newFileData)
begin = size - totalSize + toEditTexture["offset"]
newFileData[begin:begin + toEditTexture["size"]] = img_data
with open(dst, 'wb') as f:
f.write(newFileData)
env = UnityPy.load(dst)
with open(dst, 'wb') as f:
f.write(env.file.save(packer="lz4"))
Code I use example code to edit Texture2D
and save like this
Error I got
mabye because the game implemented its own dynamic resource loading strategy
Bug however,using assetstudio to see dump information about Texture2D before and after edit, something broken
before
after
i think that's the problem,i tried uabea and got the same result
To Reproduce