From 75892bb4d30623294cf3b209bacd75805c036e68 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 19 May 2026 00:20:38 +0800 Subject: [PATCH 1/4] feat: read preload table to get path id --- UnityPy/environment.py | 6 ++++++ UnityPy/files/ObjectReader.py | 3 +++ UnityPy/helpers/ContainerHelper.py | 26 +++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/UnityPy/environment.py b/UnityPy/environment.py index a44d228f..1fcc465a 100644 --- a/UnityPy/environment.py +++ b/UnityPy/environment.py @@ -203,9 +203,15 @@ def search(item): return search(self) + def _build_container_index(self) -> None: + for f in self.cabs.values(): + if isinstance(f, SerializedFile): + f.container.parse_preload_table() + @property def container(self) -> ContainerHelper: """Returns a dictionary of all objects in the Environment.""" + self._build_container_index() container = [] for f in self.cabs.values(): if isinstance(f, SerializedFile) and not f.is_dependency: diff --git a/UnityPy/files/ObjectReader.py b/UnityPy/files/ObjectReader.py index fc03b874..b5fe1dbd 100644 --- a/UnityPy/files/ObjectReader.py +++ b/UnityPy/files/ObjectReader.py @@ -198,6 +198,9 @@ def peek_name(self) -> Union[str, None]: @property def container(self): + env = self.assets_file.environment + if env is not None: + env._build_container_index() return self.assets_file._container.path_dict.get(self.path_id) @property diff --git a/UnityPy/helpers/ContainerHelper.py b/UnityPy/helpers/ContainerHelper.py index 95c3d30e..f2ad977c 100644 --- a/UnityPy/helpers/ContainerHelper.py +++ b/UnityPy/helpers/ContainerHelper.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Dict, Generator, Iterator, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, Generator, Iterator, List, Optional, Tuple, Union from attrs import define @@ -16,13 +16,37 @@ class ContainerHelper: container: List[Tuple[str, AssetInfo]] container_dict: Dict[str, PPtr[Object]] path_dict: Dict[int, str] + _preload_table: Optional[List[PPtr[Object]]] = None def __init__(self, container: Union[List[Tuple[str, AssetInfo]], AssetBundle]) -> None: + preload_table: Optional[List[PPtr[Object]]] = None if not isinstance(container, (list)): + preload_table = container.m_PreloadTable container = container.m_Container self.container = container self.container_dict = {key: value.asset for key, value in container} self.path_dict = {value.asset.path_id: key for key, value in container} + self._preload_table = preload_table + + def parse_preload_table(self) -> None: + if self._preload_table is None: + return + + for path, info in self.container: + start = info.preloadIndex + size = info.preloadSize + if start < 0 or size <= 0 or start + size > len(self._preload_table): + continue + for pptr in self._preload_table[start : start + size]: + if not pptr: + continue + try: + target = pptr.deref() + except (FileNotFoundError, KeyError): + continue + target.assets_file._container.path_dict.setdefault(pptr.path_id, path) + + self._preload_table = None def items(self) -> Generator[Tuple[str, PPtr[Object]], None, None]: return ((key, value.asset) for key, value in self.container) From 177e4933ae04f46ce5775cb478ea1d0b003bd121 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sat, 13 Jun 2026 19:41:53 +0800 Subject: [PATCH 2/4] refactor: only build container index once --- UnityPy/environment.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/UnityPy/environment.py b/UnityPy/environment.py index 1fcc465a..7d378909 100644 --- a/UnityPy/environment.py +++ b/UnityPy/environment.py @@ -32,6 +32,7 @@ class Environment: local_files: List[str] local_files_simple: List[str] typetree_generator: Optional["TypeTreeGenerator"] = None + _container_index_built: bool = False def __init__(self, *args: FileSourceType, fs: Optional[AbstractFileSystem] = None, path: Optional[str] = None): self.files = {} @@ -39,6 +40,7 @@ def __init__(self, *args: FileSourceType, fs: Optional[AbstractFileSystem] = Non self.fs = fs or LocalFileSystem() self.local_files = [] self.local_files_simple = [] + self._container_index_built = False if path is None: # if no path is given, use the current working directory @@ -204,6 +206,7 @@ def search(item): return search(self) def _build_container_index(self) -> None: + self._container_index_built = True for f in self.cabs.values(): if isinstance(f, SerializedFile): f.container.parse_preload_table() @@ -211,7 +214,8 @@ def _build_container_index(self) -> None: @property def container(self) -> ContainerHelper: """Returns a dictionary of all objects in the Environment.""" - self._build_container_index() + if not self._container_index_built: + self._build_container_index() container = [] for f in self.cabs.values(): if isinstance(f, SerializedFile) and not f.is_dependency: @@ -255,6 +259,7 @@ def register_cab(self, name: str, item: Union[SerializedFile, EndianBinaryReader The file to register. """ self.cabs[simplify_name(name)] = item + self._container_index_built = False def get_cab(self, name: str) -> Union[SerializedFile, EndianBinaryReader, None]: """ From c631634d82194ed28dedc180873fc074c8ae72c0 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sat, 13 Jun 2026 19:45:12 +0800 Subject: [PATCH 3/4] fix: encapsulate lock --- UnityPy/environment.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/UnityPy/environment.py b/UnityPy/environment.py index 7d378909..fe8a8b1d 100644 --- a/UnityPy/environment.py +++ b/UnityPy/environment.py @@ -206,7 +206,9 @@ def search(item): return search(self) def _build_container_index(self) -> None: - self._container_index_built = True + if not self._container_index_built: + self._container_index_built = True + for f in self.cabs.values(): if isinstance(f, SerializedFile): f.container.parse_preload_table() @@ -214,8 +216,7 @@ def _build_container_index(self) -> None: @property def container(self) -> ContainerHelper: """Returns a dictionary of all objects in the Environment.""" - if not self._container_index_built: - self._build_container_index() + self._build_container_index() container = [] for f in self.cabs.values(): if isinstance(f, SerializedFile) and not f.is_dependency: From 3a1fe647fb7732546cd37725d68786c0e6c05835 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sat, 13 Jun 2026 19:48:19 +0800 Subject: [PATCH 4/4] fix: condition --- UnityPy/environment.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UnityPy/environment.py b/UnityPy/environment.py index fe8a8b1d..c4a60aea 100644 --- a/UnityPy/environment.py +++ b/UnityPy/environment.py @@ -206,9 +206,10 @@ def search(item): return search(self) def _build_container_index(self) -> None: - if not self._container_index_built: - self._container_index_built = True + if self._container_index_built: + return + self._container_index_built = True for f in self.cabs.values(): if isinstance(f, SerializedFile): f.container.parse_preload_table()