K0lb3 / UnityPy

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

can't read typetree from assets #201

Closed orangespoons closed 1 year ago

orangespoons commented 1 year ago

Code

import UnityPy
import json
from typing import Dict

class FakeNode:
    def __init__(self, **kwargs):
        self.__dict__.update(**kwargs)

with open("./assembly_typetrees.json", "r", encoding="utf-8") as f:
    trees = json.load(f)

filepath = "C:/steamgames/SteamLibrary/steamapps/common/Monster Prom/MonsterProm_Data/sharedassets0.assets"
env = UnityPy.load(filepath)
for obj in env.objects:
    if obj.type.name == "MonoBehaviour":
        d = obj.read()
        if obj.serialized_type and obj.serialized_type.nodes:
            tree = obj.read_typetree()
        else:
            if not d.m_Script:
                continue
            script = d.m_Script.read()
            if script.m_ClassName not in trees:
                continue
            nodes = FakeNode(**trees[script.m_ClassName])
            tree = obj.read_typetree(nodes)

Error

PS C:\Users\USER\Desktop\bundle_mb>  & 'C:\Users\USER\AppData\Local\Programs\Python\Python311\python.exe' 'c:\Users\USER\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher' '49738' '--' 'c:\Users\USER\Desktop\bundle_mb\main.py'
Traceback (most recent call last):
  File "c:\Users\USER\Desktop\bundle_mb\main.py", line 22, in <module>
    if not d.m_Script:
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\UnityPy\classes\PPtr.py", line 90, in __bool__
    return True if self.get_obj() else False
                   ^^^^^^^^^^^^^^
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\UnityPy\classes\PPtr.py", line 46, in get_obj
    self.assets_file.load_dependencies([external_name])
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\UnityPy\files\SerializedFile.py", line 312, in load_dependencies
    self.environment.load_file(file_id.path, True)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\UnityPy\environment.py", line 110, in load_file
    file = self.fs.open(file, "rb")
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\fsspec\spec.py", line 1241, in open
    f = self._open(
        ^^^^^^^^^^^
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\fsspec\implementations\local.py", line 184, in _open
    return LocalFileOpener(path, mode, fs=self, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\fsspec\implementations\local.py", line 315, in __init__
    self._open()
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\fsspec\implementations\local.py", line 320, in _open
    self.f = open(self.path, mode=self.mode)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'C:/Users/USER/Desktop/bundle_mb/globalgamemanagers.assets'

Bug The absolute path of sharedassets0.assets was correctly passed UnityPy.load(), but the error message says that it is looking for assets in "C:/Users/USER/Desktop/bundle_mb/globalgamemanagers.assets". It's not the asset I wanted to load, and even trying to load it from where the main.py file is located, not from the game folder.

this example is too difficult for me. So, I manually dumped the typetree and tried to read the monobehaviour.

assembly_typetrees.json was generated using TypetreeGenerator (https://github.com/K0lb3/TypeTreeGenerator), command I use: TypeTreeGenerator>TypeTreeGeneratorCLI.exe -p "C:\steamgames\SteamLibrary\steamapps\common\Monster Prom\MonsterProm_Data\Managed" -a "Assembly-CSharp.dll" -v 2017.4.40f1 -d json -o "assembly_typetrees.json"

If I move globalgamemanagers.assets to the folder where main.py is, The error message changes to:

PS C:\Users\USER\Desktop\bundle_mb>  c:; cd 'c:\Users\USER\Desktop\bundle_mb'; & 'C:\Users\USER\AppData\Local\Programs\Python\Python311\python.exe' 'c:\Users\USER\.vscode\extensions\ms-python.python-2023.14.0\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher' '49817' '--' 'c:\Users\USER\Desktop\bundle_mb\main.py' 
Traceback (most recent call last):
  File "c:\Users\USER\Desktop\bundle_mb\main.py", line 22, in <module>
    if not d.m_Script:
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\UnityPy\classes\PPtr.py", line 90, in __bool__
    return True if self.get_obj() else False
                   ^^^^^^^^^^^^^^
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\UnityPy\classes\PPtr.py", line 46, in get_obj
    self.assets_file.load_dependencies([external_name])
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\UnityPy\files\SerializedFile.py", line 312, in load_dependencies
    self.environment.load_file(file_id.path, True)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\UnityPy\environment.py", line 110, in load_file
    file = self.fs.open(file, "rb")
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\fsspec\spec.py", line 1241, in open
    f = self._open(
        ^^^^^^^^^^^
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\fsspec\implementations\local.py", line 184, in _open
    return LocalFileOpener(path, mode, fs=self, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\fsspec\implementations\local.py", line 315, in __init__
    self._open()
  File "C:\Users\USER\AppData\Local\Programs\Python\Python311\Lib\site-packages\fsspec\implementations\local.py", line 320, in _open
    self.f = open(self.path, mode=self.mode)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'C:/Users/USER/Desktop/bundle_mb/library/unity default resources'

so I create "library" folder and move "unity default resources" assets. error message changes to:

Traceback (most recent call last):
  File "c:\Users\USER\Desktop\bundle_mb\main.py", line 27, in <module>
    nodes = FakeNode(**trees[script.m_ClassName])
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: __main__.FakeNode() argument after ** must be a mapping, not list

trees[script.m_ClassName]:

[{'m_Type': 'MonoBehaviour', 'm_Name': 'Base', 'm_MetaFlag': 0, 'm_Level': 0}, {'m_Type': 'PPtr<GameObject>', 'm_Name': 'm_GameObject', 'm_MetaFlag': 0, 'm_Level': 1}, {'m_Type': 'int', 'm_Name': 'm_FileID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'SInt64', 'm_Name': 'm_PathID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'UInt8', 'm_Name': 'm_Enabled', 'm_MetaFlag': 16384, 'm_Level': 1}, {'m_Type': 'PPtr<MonoScript>', 'm_Name': 'm_Script', 'm_MetaFlag': 0, 'm_Level': 1}, {'m_Type': 'int', 'm_Name': 'm_FileID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'SInt64', 'm_Name': 'm_PathID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'string', 'm_Name': 'm_Name', 'm_MetaFlag': 0, 'm_Level': 1}, {'m_Type': 'Array', 'm_Name': 'Array', 'm_MetaFlag': 16384, 'm_Level': 2}, {'m_Type': 'int', 'm_Name': 'size', 'm_MetaFlag': 0, 'm_Level': 3}, {'m_Type': 'char', 'm_Name': 'data', 'm_MetaFlag': 0, 'm_Level': 3}, {'m_Type': 'PPtr<Material>', 'm_Name': 'mMat', 'm_MetaFlag': 0, 'm_Level': 1}, {'m_Type': 'int', 'm_Name': 'm_FileID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'SInt64', 'm_Name': 'm_PathID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'Rect', 'm_Name': 'mUVRect', 'm_MetaFlag': 16384, 'm_Level': 1}, {'m_Type': 'float', 'm_Name': 'm_XMin', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'float', 'm_Name': 'm_YMin', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'float', 'm_Name': 'm_Width', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'float', 'm_Name': 'm_Height', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'BMFont', 'm_Name': 'mFont', 'm_MetaFlag': 0, 'm_Level': 1}, {'m_Type': 'SInt32', 'm_Name': 'mSize', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'SInt32', 'm_Name': 'mBase', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'SInt32', 'm_Name': 'mWidth', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'SInt32', 'm_Name': 'mHeight', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'string', 'm_Name': 'mSpriteName', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'Array', 'm_Name': 'Array', 'm_MetaFlag': 16384, 'm_Level': 3}, {'m_Type': 'int', 'm_Name': 'size', 'm_MetaFlag': 0, 'm_Level': 4}, {'m_Type': 'char', 'm_Name': 'data', 'm_MetaFlag': 0, 'm_Level': 4}, {'m_Type': 'List`1', 'm_Name': 'mSaved', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'Array', 'm_Name': 'Array', 'm_MetaFlag': 0, 'm_Level': 3}, {'m_Type': 'int', 'm_Name': 'size', 'm_MetaFlag': 0, 'm_Level': 4}, {'m_Type': 'BMGlyph', 'm_Name': 'data', 'm_MetaFlag': 0, 'm_Level': 4}, {'m_Type': 'SInt32', 'm_Name': 'index', 'm_MetaFlag': 0, 'm_Level': 5}, {'m_Type': 'SInt32', 'm_Name': 'x', 'm_MetaFlag': 0, 'm_Level': 5}, {'m_Type': 'SInt32', 'm_Name': 'y', 'm_MetaFlag': 0, 'm_Level': 5}, {'m_Type': 'SInt32', 'm_Name': 'width', 'm_MetaFlag': 0, 'm_Level': 5}, {'m_Type': 'SInt32', 'm_Name': 'height', 'm_MetaFlag': 0, 'm_Level': 5}, {'m_Type': 'SInt32', 'm_Name': 'offsetX', 'm_MetaFlag': 0, 'm_Level': 5}, {'m_Type': 'SInt32', 'm_Name': 'offsetY', 'm_MetaFlag': 0, 'm_Level': 5}, {'m_Type': 'SInt32', 'm_Name': 'advance', 'm_MetaFlag': 0, 'm_Level': 5}, {'m_Type': 'SInt32', 'm_Name': 'channel', 'm_MetaFlag': 0, 'm_Level': 5}, {'m_Type': 'List`1', 'm_Name': 'kerning', 'm_MetaFlag': 0, 'm_Level': 5}, {'m_Type': 'Array', 'm_Name': 'Array', 'm_MetaFlag': 0, 'm_Level': 6}, {'m_Type': 'int', 'm_Name': 'size', 'm_MetaFlag': 0, 'm_Level': 7}, {'m_Type': 'SInt32', 'm_Name': 'data', 'm_MetaFlag': 0, 'm_Level': 7}, {'m_Type': 'PPtr<UIAtlas>', 'm_Name': 'mAtlas', 'm_MetaFlag': 0, 'm_Level': 1}, {'m_Type': 'int', 'm_Name': 'm_FileID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'SInt64', 'm_Name': 'm_PathID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'PPtr<UIFont>', 'm_Name': 'mReplacement', 'm_MetaFlag': 0, 'm_Level': 1}, {'m_Type': 'int', 'm_Name': 'm_FileID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'SInt64', 'm_Name': 'm_PathID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'List`1', 'm_Name': 'mSymbols', 'm_MetaFlag': 0, 'm_Level': 1}, {'m_Type': 'Array', 'm_Name': 'Array', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'int', 'm_Name': 'size', 'm_MetaFlag': 0, 'm_Level': 3}, {'m_Type': 'BMSymbol', 'm_Name': 'data', 'm_MetaFlag': 0, 'm_Level': 3}, {'m_Type': 'string', 'm_Name': 'sequence', 'm_MetaFlag': 0, 'm_Level': 4}, {'m_Type': 'Array', 'm_Name': 'Array', 'm_MetaFlag': 16384, 'm_Level': 5}, {'m_Type': 'int', 'm_Name': 'size', 'm_MetaFlag': 0, 'm_Level': 6}, {'m_Type': 'char', 'm_Name': 'data', 'm_MetaFlag': 0, 'm_Level': 6}, {'m_Type': 'string', 'm_Name': 'spriteName', 'm_MetaFlag': 0, 'm_Level': 4}, {'m_Type': 'Array', 'm_Name': 'Array', 'm_MetaFlag': 16384, 'm_Level': 5}, {'m_Type': 'int', 'm_Name': 'size', 'm_MetaFlag': 0, 'm_Level': 6}, {'m_Type': 'char', 'm_Name': 'data', 'm_MetaFlag': 0, 'm_Level': 6}, {'m_Type': 'PPtr<Font>', 'm_Name': 'mDynamicFont', 'm_MetaFlag': 0, 'm_Level': 1}, {'m_Type': 'int', 'm_Name': 'm_FileID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'SInt64', 'm_Name': 'm_PathID', 'm_MetaFlag': 0, 'm_Level': 2}, {'m_Type': 'SInt32', 'm_Name': 'mDynamicFontSize', 'm_MetaFlag': 0, 'm_Level': 1}, {'m_Type': 'SInt32', 'm_Name': 'mDynamicFontStyle', 'm_MetaFlag': 0, 'm_Level': 1}]
  1. Why does the program find files in the folder where the program is located and not in the path I entered?
  2. I manually moved the files the program requires, but I get a TypeError. What should I do to solve it?

To Reproduce

JunkBeat commented 1 year ago

I have the same problems with the gamemanagers file. Moreover, it looks for additional files in the "library" folder, when all modern Unity games use "Resources" folder instead

It looks like you are not handling the typestrees file correctly. Try the following code:

nodes = trees[script.m_ClassName]
tree = obj.read_typetree(nodes)

or

from UnityPy.helpers.TypeTreeHelper import TypeTreeNode

nodes = []
for item in trees[script.m_ClassName]:
    nodes.append(TypeTreeNode(**item))
tree = obj.read_typetree(nodes)
orangespoons commented 1 year ago

@JunkBeat Thanks!