forked from K0lb3/UnityPy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhelper.py
More file actions
111 lines (92 loc) · 3.14 KB
/
Copy pathhelper.py
File metadata and controls
111 lines (92 loc) · 3.14 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
from enum import Enum
from io import BytesIO
from struct import pack, unpack
from typing import List, Iterator, Tuple, Union, BinaryIO, get_origin, get_args
# save original int class
_int = int
class CustomIntWrapper(int):
__size: int
__format: str
@classmethod
def read_from(cls, f: BytesIO):
return cls(
unpack("<" + getattr(cls, "__format"), f.read(getattr(cls, "__size")))[0]
)
def CustomIntWrapperFactory(name: str, __size: int, __format: str) -> CustomIntWrapper:
return type(name, (CustomIntWrapper,), {"__size": __size, "__format": __format})
byte = CustomIntWrapperFactory("byte", 1, "B")
short = CustomIntWrapperFactory("short", 2, "h")
ushort = CustomIntWrapperFactory("ushort", 2, "H")
int = CustomIntWrapperFactory("int", 4, "i")
uint = CustomIntWrapperFactory("uint", 4, "I")
long = CustomIntWrapperFactory("long", 8, "q")
ulong = CustomIntWrapperFactory("ulong", 8, "Q")
class Version:
Min: float
Max: float
def __new__(cls, Min: float = 0, Max: float = 99):
spec = []
if Min:
spec.append(f"Min={Min}")
if Max != 99:
spec.append(f"Max={Max}")
newclass = type(
f"Version ({', '.join(spec)})", (Version,), {"Min": Min, "Max": Max}
)
return newclass
@classmethod
def check_compatiblity(cls, version):
return cls.Min <= version <= cls.Max
class MetaDataClass:
version: float
size: int
parseString: str
def __init__(self, reader: BinaryIO = None) -> None:
if not (self.version):
raise NotImplementedError(
"Using an unversioned MetaDataClass isn't possible."
)
if reader:
self.read_from(reader)
def read_from(self, reader: BytesIO):
self.__dict__.update(
zip(
self.__annotations__.keys(),
unpack(self.parseString, reader.read(self.size)),
)
)
def write_to(self, writer: BytesIO):
writer.write(
pack(
"<" + self.parseString,
(self.get(key) for key in self.__annotations__.keys()),
)
)
@classmethod
def generate_versioned_subclass(cls, version: float):
# fetch fields & calculate size
compatible_fields = {}
size = 0
parseString = []
for key, clz in cls.__annotations__.items():
if get_origin(clz) == Union:
clz, *version_checks = get_args(clz)
if not any(
version_check.check_compatiblity(version)
for version_check in version_checks
):
continue
compatible_fields[key] = clz
size += getattr(clz, "__size")
parseString.append(getattr(clz, "__format"))
newclass = type(
f"{cls.__name__} - V{version:.1f}",
(MetaDataClass,),
{
"__annotations__": compatible_fields,
"size": size,
"version": version,
"parseString": "".join(parseString),
},
)
return newclass