forked from K0lb3/UnityPy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWebFile.py
More file actions
112 lines (93 loc) · 3.58 KB
/
WebFile.py
File metadata and controls
112 lines (93 loc) · 3.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from typing import Optional
from ..helpers import CompressionHelper
from ..streams import EndianBinaryReader, EndianBinaryWriter
from . import File
class WebFile(File.File):
"""A package which can hold other WebFiles, Bundles and SerialiedFiles.
It may be compressed via gzip or brotli.
files -- list of all files in the WebFile
"""
def __init__(self, reader: EndianBinaryReader, parent: File, name=None, **kwargs):
"""Constructor Method"""
super().__init__(parent=parent, name=name, **kwargs)
# check compression
magic = reader.read_bytes(2)
reader.Position = 0
if magic == CompressionHelper.GZIP_MAGIC:
self.packer = "gzip"
data = CompressionHelper.decompress_gzip(reader.bytes)
reader = EndianBinaryReader(data, endian="<")
else:
reader.Position = 0x20
magic = reader.read_bytes(6)
reader.Position = 0
if CompressionHelper.BROTLI_MAGIC == magic:
self.packer = "brotli"
data = CompressionHelper.decompress_brotli(reader.bytes)
reader = EndianBinaryReader(data, endian="<")
else:
self.packer = "none"
reader.endian = "<"
# signature check
signature = reader.read_string_to_null()
if not signature.startswith(("UnityWebData", "TuanjieWebData")):
raise ValueError(f"Invalid WebFile signature: {signature!r}. Expected 'UnityWebData' or 'TuanjieWebData'.")
self.signature = signature
# read header -> contains file headers
head_length = reader.read_int()
files = []
while reader.Position < head_length:
offset = reader.read_int()
length = reader.read_int()
path_length = reader.read_int()
name = bytes(reader.read_bytes(path_length)).decode("utf-8")
files.append(File.DirectoryInfo(name, offset, length))
self.read_files(reader, files)
def save(
self,
files: Optional[dict] = None,
packer: str = "none",
signature: str = "UnityWebData1.0",
) -> bytes:
# solve defaults
if not files:
files = self.files
if not packer:
packer = self.packer
# get raw data
files = {name: f.bytes if isinstance(f, EndianBinaryReader) else f.save() for name, f in files.items()}
# create writer
writer = EndianBinaryWriter(endian="<")
# signature
writer.write_string_to_null(signature)
# data offset
offset = sum(
[
writer.Position, # signature
sum(len(path.encode("utf-8")) for path in files.keys()), # path of each file
4 * 3 * len(files), # 3 ints per file
4, # offset int
]
)
writer.write_int(offset)
# 1. file headers
for name, data in files.items():
# offset
writer.write_int(offset)
# length
length = len(data)
writer.write_int(length)
offset += length
# path
enc_path = name.encode("utf-8")
writer.write_int(len(enc_path))
writer.write(enc_path)
# 2. file data
for data in files.values():
writer.write(data)
if packer == "gzip":
return CompressionHelper.compress_gzip(writer.bytes)
elif packer == "brotli":
return CompressionHelper.compress_brotli(writer.bytes)
else:
return writer.bytes