pyddtas 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyddtas/core.py ADDED
@@ -0,0 +1,223 @@
1
+ from ctypes import sizeof
2
+ from typing import Type, Literal
3
+ from dataclasses import dataclass
4
+ import zlib
5
+
6
+ try: import orjson as json
7
+ except: import json
8
+
9
+ # Dataclass format:
10
+ # someParam: type # exact json key in TAS file
11
+ #
12
+ # if exact key not provided, its literally like class parameter
13
+
14
+ @dataclass
15
+ class TASInput:
16
+ direction: int # m_Direction
17
+ fire: int # m_Fire
18
+ hook: int # m_Hook
19
+ jump: int # m_Jump
20
+ nextWeapon: int # m_NextWeapon
21
+ playerFlags: int # m_PlayerFlags
22
+ prevWeapon: int # m_PrevWeapon
23
+ targetX: int # m_TargetX
24
+ targetY: int # m_TargetY
25
+ wantedWeapon: int # m_WantedWeapon
26
+
27
+ def todict(self):
28
+ return {
29
+ "m_Direction": self.direction,
30
+ "m_Fire": self.fire,
31
+ "m_Hook": self.hook,
32
+ "m_Jump": self.jump,
33
+ "m_NextWeapon": self.nextWeapon,
34
+ "m_PlayerFlags": self.playerFlags,
35
+ "m_PrevWeapon": self.prevWeapon,
36
+ "m_TargetX": self.targetX,
37
+ "m_TargetY": self.targetY,
38
+ "m_WantedWeapon": self.wantedWeapon
39
+ }
40
+
41
+ def tojson(self, indent: int | str | None = None):
42
+ return json.dumps(self.todict(), indent=indent)
43
+
44
+ @classmethod
45
+ def fromdict(cls: Type[TASInput], obj: dict, errors: Literal['show', 'ignore'] = "show"):
46
+ if errors == "ignore": getVal = lambda key, default: obj.get(key, default)
47
+ else: getVal = lambda key, default: obj[key]
48
+
49
+ return cls(
50
+ direction=getVal("m_Direction", 0),
51
+ fire=getVal("m_Fire", 0),
52
+ hook=getVal("m_Hook", 0),
53
+ jump=getVal("m_Jump", 0),
54
+ nextWeapon=getVal("m_NextWeapon", 0),
55
+ playerFlags=getVal("m_PlayerFlags", 0),
56
+ prevWeapon=getVal("m_PrevWeapon", 0),
57
+ targetX=getVal("m_TargetX", 0),
58
+ targetY=getVal("m_TargetY", 0),
59
+ wantedWeapon=getVal("m_WantedWeapon", 0),
60
+ )
61
+
62
+ @classmethod
63
+ def fromjson(cls: Type[TASInput], obj: str | bytes, errors: Literal['show', 'ignore'] = "show"):
64
+ return cls.fromdict(
65
+ json.loads(obj),
66
+ errors
67
+ )
68
+
69
+ @dataclass
70
+ class TASPos:
71
+ x: int
72
+ y: int
73
+
74
+ def todict(self):
75
+ return {
76
+ "x": self.x,
77
+ "y": self.y
78
+ }
79
+
80
+ def tojson(self, indent: int | str | None = None):
81
+ return json.dumps(self.todict(), indent=indent)
82
+
83
+ @classmethod
84
+ def fromdict(cls: Type[TASPos], obj: dict, errors: Literal['show', 'ignore'] = "show"):
85
+ if errors == "ignore": getVal = lambda key, default: obj.get(key, default)
86
+ else: getVal = lambda key, default: obj[key]
87
+
88
+ return cls(
89
+ x=getVal("x", 0),
90
+ y=getVal("y", 0)
91
+ )
92
+
93
+ @classmethod
94
+ def fromjson(cls: Type[TASPos], obj: str | bytes, errors: Literal['show', 'ignore'] = "show"):
95
+ return cls.fromdict(
96
+ json.loads(obj),
97
+ errors
98
+ )
99
+
100
+ @dataclass
101
+ class TASTick:
102
+ input: TASInput
103
+ pos: TASPos
104
+
105
+ def todict(self):
106
+ return {
107
+ "input": self.input.todict(),
108
+ "pos": self.pos.todict()
109
+ }
110
+
111
+ def tojson(self, indent: int | str | None = None):
112
+ return json.dumps(self.todict(), indent=indent)
113
+
114
+ @classmethod
115
+ def fromdict(cls: Type[TASTick], obj: dict, errors: Literal['show', 'ignore'] = "show"):
116
+ if errors == "ignore": getVal = lambda key, default: obj.get(key, default)
117
+ else: getVal = lambda key, default: obj[key]
118
+
119
+ return cls(
120
+ input=TASInput.fromdict(getVal("input", {})),
121
+ pos=TASPos.fromdict(getVal("pos", {}))
122
+ )
123
+
124
+ @classmethod
125
+ def fromjson(cls: Type[TASTick], obj: str | bytes, errors: Literal['show', 'ignore'] = "show"):
126
+ return cls.fromdict(
127
+ json.loads(obj),
128
+ errors
129
+ )
130
+
131
+ @dataclass
132
+ class TASInfo:
133
+ author: str
134
+ date_modified: str
135
+ description: str
136
+ map: str
137
+ map_name: str | None = None # has no key in TAS file, PyTAS value
138
+ map_hash: str | None = None # has no key in TAS file, PyTAS value
139
+
140
+ def todict(self):
141
+ return {
142
+ "author": self.author,
143
+ "date_modified": self.date_modified,
144
+ "description": self.description,
145
+ "map": self.map
146
+ }
147
+
148
+ def tojson(self, indent: int | str | None = None):
149
+ return json.dumps(self.todict(), indent=indent)
150
+
151
+ @classmethod
152
+ def fromdict(cls: Type[TASInfo], obj: dict, errors: Literal['show', 'ignore'] = "show"):
153
+ if errors == "ignore": getVal = lambda key, default: obj.get(key, default)
154
+ else: getVal = lambda key, default: obj[key]
155
+
156
+ return cls(
157
+ author=getVal("author", ""),
158
+ date_modified=getVal("date_modified", ""),
159
+ description=getVal("description", ""),
160
+ map=getVal("map", ""),
161
+ map_name=getVal("map", "").split("_")[0] if len(getVal("map", "").split("_")) == 2 else None,
162
+ map_hash=getVal("map", "").split("_")[1] if len(getVal("map", "").split("_")) == 2 else None
163
+ )
164
+
165
+ @classmethod
166
+ def fromjson(cls: Type[TASInfo], obj: str | bytes, errors: Literal['show', 'ignore'] = "show"):
167
+ return cls.fromdict(
168
+ json.loads(obj),
169
+ errors
170
+ )
171
+
172
+ @dataclass
173
+ class TASData:
174
+ info: TASInfo
175
+ replay_dummy: list[TASTick]
176
+ replay_local: list[TASTick]
177
+
178
+ def todict(self):
179
+ return {
180
+ "info": self.info.todict(),
181
+ "replay_dummy": [x.todict() for x in self.replay_dummy],
182
+ "replay_local": [x.todict() for x in self.replay_local]
183
+ }
184
+
185
+ def tojson(self, indent: int | str | None = None):
186
+ return json.dumps(self.todict(), indent=indent)
187
+
188
+ @classmethod
189
+ def fromdict(cls: Type[TASData], obj: dict, errors: Literal['show', 'ignore'] = "show"):
190
+ if errors == "ignore": getVal = lambda key, default: obj.get(key, default)
191
+ else: getVal = lambda key, default: obj[key]
192
+
193
+ return cls(
194
+ info=TASInfo.fromdict(getVal("info", {})),
195
+ replay_dummy=[TASTick.fromdict(x) for x in getVal("replay_dummy", [])],
196
+ replay_local=[TASTick.fromdict(x) for x in getVal("replay_local", [])]
197
+ )
198
+
199
+ @classmethod
200
+ def fromjson(cls: Type[TASData], obj: str | bytes, errors: Literal['show', 'ignore'] = "show"):
201
+ return cls.fromdict(
202
+ json.loads(obj),
203
+ errors
204
+ )
205
+
206
+ @dataclass
207
+ class TASFile:
208
+ tas_data: TASData
209
+
210
+ def tobytes(self):
211
+ data = self.tas_data.tojson().encode("utf-8")
212
+ uncsize = len(data).to_bytes(4, "little")
213
+ compressed = zlib.compress(data)
214
+ return uncsize + compressed
215
+
216
+ @classmethod
217
+ def frombytes(cls: Type[TASFile], data: bytes, ignore_size: bool = False):
218
+ uncsize = int.from_bytes(data[:4], "little")
219
+ data = zlib.decompress(data[4:])
220
+ if len(data) != uncsize and not ignore_size: raise ValueError("Uncompressed size from header is not equal to real uncompressed size. (damaged file)")
221
+ return cls(
222
+ TASData.fromdict(json.loads(data))
223
+ )
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyddtas
3
+ Version: 0.1.0
4
+ Author-email: nesquikcode <nesquik@nishine.ru>
5
+ Requires-Python: >=3.11
6
+ Provides-Extra: fast
7
+ Requires-Dist: orjson>=3.10; extra == "fast"
@@ -0,0 +1,5 @@
1
+ pyddtas/core.py,sha256=PzwJRycTIBoVai_nSrLA7Z4V5UdEUOUbjYJE-gGyUac,7439
2
+ pyddtas-0.1.0.dist-info/METADATA,sha256=YFznqhxGaz15wOJVfnkDHbMg90pbMtuw7P8n5geaXLA,195
3
+ pyddtas-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
4
+ pyddtas-0.1.0.dist-info/top_level.txt,sha256=G6PfuX0HldtGmzyi4DN109Ndm44WqUuuuP5Xt2QWHl0,8
5
+ pyddtas-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ pyddtas