-
-
Notifications
You must be signed in to change notification settings - Fork 155
Description
Code
In this example, we have
>>> sf.externals
[<FileIdentifier(globalgamemanagers.assets)>, <FileIdentifier(Resources/unity_builtin_extra)>, <FileIdentifier(Library/unity default resources)>]
however, the directory Library
does not exist, and unity default resources
is located in Resources
. i.e., there is some error in the game assets.
import UnityPy
am = UnityPy.AssetsManager()
am.load_file("/path/to/sharedassets0.assets")
sf, = am.assets
clips = [v for v in sf.objects.values() if v.type == 82]
clip = clips[0].read()
clip.samples
Alternately:
conv = UnityPy.export.AudioClipConverter
conv.extract_audioclip_samples(clip)
Error
>>> conv.extract_audioclip_samples(obj)
Traceback (most recent call last):
File "<python-input-33>", line 1, in <module>
conv.extract_audioclip_samples(obj)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
File "/path/to/python3.13/site-packages/UnityPy/export/AudioClipConverter.py", line 97, in extract_audioclip_samples
audio_data = get_resource_data(
resource.m_Source,
...<2 lines>...
resource.m_Size,
)
File "/path/to/python3.13/site-packages/UnityPy/helpers/ResourceReader.py", line 28, in get_resource_data
assets_file.load_dependencies(possible_names)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/path/to/python3.13/site-packages/UnityPy/files/SerializedFile.py", line 364, in load_dependencies
self.environment.load_file(file_id.path, True)
~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
File "/path/to/python3.13/site-packages/UnityPy/environment.py", line 125, in load_file
file = os.path.join(self.path, file)
File "<frozen posixpath>", line 77, in join
TypeError: expected str, bytes or os.PathLike object, not NoneType
Bug
The key code region that triggers the error is in SerializedFile
:
def load_dependencies(self, possible_dependencies: list = []):
...
for file_id in self.externals:
self.environment.load_file(file_id.path, True)
for dependency in possible_dependencies:
try:
self.environment.load_file(dependency, True)
except FileNotFoundError:
pass
Specifically, there are two issues. The first issue is that there is no error handling when a file_id.path
in self.externals
does not exist, as there is in the case that a dependency
does not exist just below. If this were the only issue, I would just make a PR as it's a simple fix for an edge case of errant game asset data. However, there is another issue that I'm not sure how to fix in a way that aligns with your design intentions for this module, as, even if the error is handled, in both the case of non-existent dependencies and non-existent externals, the UnityPy still crashes, because Environment.loadfile()
raises a TypeError
, and not a FileNotFoundError
when the file does not exist.
The reason is simple. On line 125 of Environment.py:
file = os.path.join(self.path, file)
self.path
may be None
, as we see in the __init__
method above:
def __init__(self, *args: FileSourceType, fs: Optional[AbstractFileSystem] = None):
self.files = {}
self.cabs = {}
self.path = None
self.fs = fs or LocalFileSystem()
self.local_files = []
self.local_files_simple = []
if args:
for arg in args:
if isinstance(arg, str):
if self.fs.isfile(arg):
if ntpath.splitext(arg)[-1] in [".apk", ".zip"]:
self.load_zip_file(arg)
else:
self.path = ntpath.dirname(arg)
if reSplit.match(arg):
self.load_files([arg])
else:
self.load_file(arg)
elif self.fs.isdir(arg):
self.path = arg
self.load_folder(arg)
else:
self.path = None
self.load_file(file=arg)
if len(self.files) == 1:
self.file = list(self.files.values())[0]
if self.path == "":
self.path = os.getcwd()
letting sf.environment.path = os.cwd()
in the above example seems to resolve the error for me, but I'm not sure if it's causing other issues later on. Either way, it seems as though either Environment.load_file
should handle the case where self.path is None
or environments should always be initialized such that self.path
is a string. In particular, the case
if self.path == "":
self.path = os.getcwd()
seems to be intending to account for the instance where self.path
is set by any keyword arguments, but it seems as though if that is the case, that self.path
will be None
.
For a temporary workaround, I just set sf.environment.path = os.getcwd()
before trying to unpack any samples.
To Reproduce
- I used the free Peripeteia demo
- following data:
- Python 3.13
- UnityPy 1.22.5