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
144 lines (123 loc) · 4.55 KB
/
Copy pathWebFile.py
File metadata and controls
144 lines (123 loc) · 4.55 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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
from .BundleFile import BundleFile
from .File import File
from .SerializedFile import SerializedFile
from ..enums import FileType
from ..helpers import CompressionHelper, ImportHelper
from ..streams import EndianBinaryReader, EndianBinaryWriter
class WebFile(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, environment=None):
"""Constructor Method
"""
self.files = {}
self.is_changed = False
# 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 signature != "UnityWebData1.0":
return
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((name, offset, length))
# read file data and convert it
for name, offset, length in files:
reader.Position = offset
data = reader.read(length)
typ, item = ImportHelper.check_file_type(data)
if typ == FileType.BundleFile:
item = BundleFile(item, environment)
elif typ == FileType.WebFile:
item = WebFile(item, environment)
if typ == FileType.AssetsFile:
# pre-check if resource file
if name.endswith((".resS", ".resource", ".config", ".xml", ".dat")):
environment.resources[name] = item
else:
# try to load the file as serialized file
try:
item = SerializedFile(item, environment)
environment.assets[name] = item
except ValueError:
environment.resources[name] = item
self.files[name] = item
item.parent = self
def save(
self,
files: 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