Files
pgames-manager/backend/app/converter/unity_bundle.py
T
mrleo1nid c8d83b3fe7 feat: add mod editing, revision merges and secure game storage
Add immutable revisions, PGVN editor, asset replacement, merge and rebase workflows, authenticated media delivery and reachability GC. Harden the script interpreter, pin unrpyc and remove tracked build metadata. Include current compatibility checks, Naninovel import and regression tests.
2026-10-25 04:42:48 +03:00

66 lines
3.2 KiB
Python

"""Stream UnityFS nodes to a private workspace; do not load media bundles into RAM."""
from pathlib import Path
def unpack(source: Path, target: Path) -> list[Path]:
from UnityPy.streams import EndianBinaryReader
from UnityPy.helpers.CompressionHelper import DECOMPRESSION_MAP
target.mkdir(parents=True, exist_ok=True)
with source.open("rb") as stream:
reader = EndianBinaryReader(stream)
if reader.read_string_to_null() != "UnityFS":
raise ValueError(f"Неподдерживаемый Unity-контейнер: {source.name}")
version = reader.read_u_int()
reader.read_string_to_null()
reader.read_string_to_null()
size = reader.read_long()
packed, raw, flags = reader.read_u_int(), reader.read_u_int(), reader.read_u_int()
if version not in {6, 7, 8} or flags & ~0x3ff:
raise ValueError(f"Неподдерживаемый UnityFS: версия {version}, flags={flags}")
if version >= 7:
reader.align_stream(16)
start = reader.Position
if flags & 0x80:
reader.Position = size - packed
info = EndianBinaryReader(DECOMPRESSION_MAP[flags & 63](reader.read_bytes(packed), raw))
if flags & 0x80:
reader.Position = start
info.read_bytes(16)
count = info.read_int()
if not 0 <= count <= 1_000_000:
raise ValueError("Некорректное количество блоков UnityFS")
blocks = [(info.read_u_int(), info.read_u_int(), info.read_u_short()) for _ in range(count)]
count = info.read_int()
if not 0 <= count <= 100_000:
raise ValueError("Некорректное количество файлов UnityFS")
nodes = [(info.read_long(), info.read_long(), info.read_u_int(), info.read_string_to_null())
for _ in range(count)]
if flags & 0x200:
reader.align_stream(16)
handles, paths = [], []
total = sum(b[0] for b in blocks)
try:
for offset, length, _, name in nodes:
path = (target / name).resolve()
if (not path.is_relative_to(target.resolve()) or path in paths or
offset < 0 or length < 0 or offset + length > total):
raise ValueError(f"Некорректный файл UnityFS: {name}")
path.parent.mkdir(parents=True, exist_ok=True)
handles.append((offset, length, path.open("wb")))
paths.append(path)
position = 0
for raw_size, packed_size, block_flags in blocks:
chunk = DECOMPRESSION_MAP[block_flags & 63](reader.read_bytes(packed_size), raw_size)
if len(chunk) != raw_size:
raise ValueError("Повреждённый блок UnityFS")
for offset, length, handle in handles:
left, right = max(position, offset), min(position + raw_size, offset + length)
if left < right:
handle.write(chunk[left - position:right - position])
position += raw_size
finally:
for _, _, handle in handles:
handle.close()
return paths