koro 2.0.0rc3__py3-none-any.whl → 2.0.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- koro/__init__.py +8 -8
- koro/slot/__init__.py +21 -21
- koro/slot/bin.py +158 -155
- koro/slot/file.py +70 -67
- koro/slot/save.py +138 -137
- koro/slot/xml.py +845 -845
- koro/stage/__init__.py +99 -98
- koro/stage/model.py +288 -288
- koro/stage/part.py +1754 -1754
- {koro-2.0.0rc3.dist-info → koro-2.0.1.dist-info}/LICENSE +24 -24
- koro-2.0.1.dist-info/METADATA +52 -0
- koro-2.0.1.dist-info/RECORD +14 -0
- {koro-2.0.0rc3.dist-info → koro-2.0.1.dist-info}/WHEEL +1 -1
- koro-2.0.0rc3.dist-info/METADATA +0 -21
- koro-2.0.0rc3.dist-info/RECORD +0 -14
- {koro-2.0.0rc3.dist-info → koro-2.0.1.dist-info}/top_level.txt +0 -0
koro/__init__.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
from .slot import *
|
2
|
-
from .slot.bin import *
|
3
|
-
from .slot.file import *
|
4
|
-
from .slot.save import *
|
5
|
-
from .slot.xml import *
|
6
|
-
from .stage import *
|
7
|
-
from .stage.model import *
|
8
|
-
from .stage.part import *
|
1
|
+
from .slot import *
|
2
|
+
from .slot.bin import *
|
3
|
+
from .slot.file import *
|
4
|
+
from .slot.save import *
|
5
|
+
from .slot.xml import *
|
6
|
+
from .stage import *
|
7
|
+
from .stage.model import *
|
8
|
+
from .stage.part import *
|
koro/slot/__init__.py
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
|
3
|
-
from ..stage import Stage
|
4
|
-
|
5
|
-
__all__ = ["Slot"]
|
6
|
-
|
7
|
-
|
8
|
-
class Slot(ABC):
|
9
|
-
__slots__ = ()
|
10
|
-
|
11
|
-
def __bool__(self) -> bool:
|
12
|
-
"""Return whether this slot is filled."""
|
13
|
-
return self.load() is not None
|
14
|
-
|
15
|
-
@abstractmethod
|
16
|
-
def load(self) -> Stage | None:
|
17
|
-
pass
|
18
|
-
|
19
|
-
@abstractmethod
|
20
|
-
def save(self, data: Stage | None, /) -> None:
|
21
|
-
pass
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
|
3
|
+
from ..stage import Stage
|
4
|
+
|
5
|
+
__all__ = ["Slot"]
|
6
|
+
|
7
|
+
|
8
|
+
class Slot(ABC):
|
9
|
+
__slots__ = ()
|
10
|
+
|
11
|
+
def __bool__(self) -> bool:
|
12
|
+
"""Return whether this slot is filled."""
|
13
|
+
return self.load() is not None
|
14
|
+
|
15
|
+
@abstractmethod
|
16
|
+
def load(self) -> Stage | None:
|
17
|
+
pass
|
18
|
+
|
19
|
+
@abstractmethod
|
20
|
+
def save(self, data: Stage | None, /) -> None:
|
21
|
+
pass
|
koro/slot/bin.py
CHANGED
@@ -1,155 +1,158 @@
|
|
1
|
-
from itertools import chain
|
2
|
-
from typing import Final
|
3
|
-
|
4
|
-
from ..stage import Stage
|
5
|
-
from .file import FileSlot
|
6
|
-
from .xml import XmlSlot
|
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
|
-
buffer[:
|
99
|
-
data_index + 1024 - buffer_index
|
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
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
1
|
+
from itertools import chain
|
2
|
+
from typing import Final
|
3
|
+
|
4
|
+
from ..stage import Stage
|
5
|
+
from .file import FileSlot
|
6
|
+
from .xml import XmlSlot
|
7
|
+
|
8
|
+
|
9
|
+
__all__ = ["BinSlot"]
|
10
|
+
|
11
|
+
|
12
|
+
class BinSlot(FileSlot):
|
13
|
+
__slots__ = ()
|
14
|
+
|
15
|
+
@staticmethod
|
16
|
+
def compress(data: bytes, /) -> bytes:
|
17
|
+
buffer: bytearray = bytearray(1024)
|
18
|
+
buffer_index: int = 958
|
19
|
+
chunk: bytearray
|
20
|
+
data_index: int = 0
|
21
|
+
output: Final[bytearray] = bytearray(
|
22
|
+
b"\x00\x00\x00\x01\x00\x00\x00\x08"
|
23
|
+
+ len(data).to_bytes(4, byteorder="big")
|
24
|
+
+ b"\x00\x00\x00\x01"
|
25
|
+
)
|
26
|
+
reference_indices: list[int]
|
27
|
+
test_buffer: bytearray
|
28
|
+
test_length: int
|
29
|
+
test_reference_indicies: list[int]
|
30
|
+
while data_index < len(data):
|
31
|
+
chunk = bytearray(b"\x00")
|
32
|
+
for bit in range(8):
|
33
|
+
if data_index >= len(data):
|
34
|
+
chunk[0] >>= 8 - bit
|
35
|
+
output.extend(chunk)
|
36
|
+
return output + b"\x00" * (len(output) & 1)
|
37
|
+
if len(data) - data_index <= 2:
|
38
|
+
buffer[buffer_index] = data[data_index]
|
39
|
+
buffer_index = buffer_index + 1 & 1023
|
40
|
+
chunk[0] = chunk[0] >> 1 | 128
|
41
|
+
chunk.append(data[data_index])
|
42
|
+
data_index += 1
|
43
|
+
continue
|
44
|
+
reference_indices = []
|
45
|
+
for i in chain(range(buffer_index, 1024), range(buffer_index)):
|
46
|
+
if data[data_index] == buffer[i]:
|
47
|
+
reference_indices.append(i)
|
48
|
+
if not reference_indices:
|
49
|
+
buffer[buffer_index] = data[data_index]
|
50
|
+
buffer_index = buffer_index + 1 & 1023
|
51
|
+
chunk[0] = chunk[0] >> 1 | 128
|
52
|
+
chunk.append(data[data_index])
|
53
|
+
data_index += 1
|
54
|
+
continue
|
55
|
+
test_buffer = buffer.copy()
|
56
|
+
test_buffer[buffer_index] = data[data_index]
|
57
|
+
for i in reference_indices.copy():
|
58
|
+
if data[data_index + 1] != test_buffer[i - 1023]:
|
59
|
+
reference_indices.remove(i)
|
60
|
+
if not reference_indices:
|
61
|
+
buffer[buffer_index] = data[data_index]
|
62
|
+
buffer_index = buffer_index + 1 & 1023
|
63
|
+
chunk[0] = chunk[0] >> 1 | 128
|
64
|
+
chunk.append(data[data_index])
|
65
|
+
data_index += 1
|
66
|
+
continue
|
67
|
+
test_buffer[buffer_index - 1023] = data[data_index + 1]
|
68
|
+
for i in reference_indices.copy():
|
69
|
+
if data[data_index + 2] != test_buffer[i - 1022]:
|
70
|
+
reference_indices.remove(i)
|
71
|
+
if not reference_indices:
|
72
|
+
buffer[buffer_index] = data[data_index]
|
73
|
+
buffer_index = buffer_index + 1 & 1023
|
74
|
+
chunk[0] = chunk[0] >> 1 | 128
|
75
|
+
chunk.append(data[data_index])
|
76
|
+
data_index += 1
|
77
|
+
continue
|
78
|
+
test_length = 4
|
79
|
+
test_reference_indicies = reference_indices.copy()
|
80
|
+
while test_length <= min(66, len(data) - data_index):
|
81
|
+
test_buffer[buffer_index + test_length - 1026] = data[
|
82
|
+
data_index + test_length - 2
|
83
|
+
]
|
84
|
+
for i in test_reference_indicies.copy():
|
85
|
+
if (
|
86
|
+
data[data_index + test_length - 1]
|
87
|
+
!= test_buffer[i + test_length - 1025]
|
88
|
+
):
|
89
|
+
test_reference_indicies.remove(i)
|
90
|
+
if test_reference_indicies:
|
91
|
+
reference_indices = test_reference_indicies.copy()
|
92
|
+
else:
|
93
|
+
break
|
94
|
+
test_length += 1
|
95
|
+
chunk[0] >>= 1
|
96
|
+
test_length -= 1
|
97
|
+
if buffer_index + test_length >= 1024:
|
98
|
+
buffer[buffer_index:] = data[
|
99
|
+
data_index : data_index + 1024 - buffer_index
|
100
|
+
]
|
101
|
+
buffer[: buffer_index + test_length - 1024] = data[
|
102
|
+
data_index + 1024 - buffer_index : data_index + test_length
|
103
|
+
]
|
104
|
+
else:
|
105
|
+
buffer[buffer_index : buffer_index + test_length] = data[
|
106
|
+
data_index : data_index + test_length
|
107
|
+
]
|
108
|
+
buffer_index = buffer_index + test_length & 1023
|
109
|
+
chunk.extend(
|
110
|
+
(
|
111
|
+
reference_indices[0] & 255,
|
112
|
+
reference_indices[0] >> 2 & 192 | test_length - 3,
|
113
|
+
)
|
114
|
+
)
|
115
|
+
data_index += test_length
|
116
|
+
output.extend(chunk)
|
117
|
+
return bytes(output)
|
118
|
+
|
119
|
+
@staticmethod
|
120
|
+
def decompress(data: bytes, /) -> bytes:
|
121
|
+
buffer: Final[bytearray] = bytearray(1024)
|
122
|
+
buffer_index: int = 958
|
123
|
+
handle: int | bytearray
|
124
|
+
flags: int
|
125
|
+
offset: int
|
126
|
+
raw: Final[bytearray] = bytearray(data[:15:-1])
|
127
|
+
ref: bytes
|
128
|
+
result: Final[bytearray] = bytearray()
|
129
|
+
result_size: Final[int] = int.from_bytes(data[8:12], byteorder="big")
|
130
|
+
while len(result) < result_size:
|
131
|
+
flags = raw.pop()
|
132
|
+
for _ in range(8):
|
133
|
+
if flags & 1:
|
134
|
+
handle = raw.pop()
|
135
|
+
buffer[buffer_index] = handle
|
136
|
+
buffer_index = buffer_index + 1 & 1023
|
137
|
+
result.append(handle)
|
138
|
+
else:
|
139
|
+
if len(raw) < 2:
|
140
|
+
return result
|
141
|
+
ref = bytes((raw.pop() for _ in range(2)))
|
142
|
+
offset = (ref[1] << 2 & 768) + ref[0]
|
143
|
+
handle = bytearray()
|
144
|
+
for i in range((ref[1] & 63) + 3):
|
145
|
+
handle.append(buffer[offset + i - 1024])
|
146
|
+
buffer[buffer_index] = handle[-1]
|
147
|
+
buffer_index = buffer_index + 1 & 1023
|
148
|
+
result.extend(handle)
|
149
|
+
flags >>= 1
|
150
|
+
return bytes(result)
|
151
|
+
|
152
|
+
@staticmethod
|
153
|
+
def deserialize(data: bytes, /) -> Stage:
|
154
|
+
return XmlSlot.deserialize(BinSlot.decompress(data))
|
155
|
+
|
156
|
+
@staticmethod
|
157
|
+
def serialize(stage: Stage, /) -> bytes:
|
158
|
+
return BinSlot.compress(XmlSlot.serialize(stage))
|
koro/slot/file.py
CHANGED
@@ -1,67 +1,70 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
from os import remove
|
3
|
-
from os.path import isfile
|
4
|
-
from typing import TYPE_CHECKING, Any
|
5
|
-
|
6
|
-
from ..stage import Stage
|
7
|
-
from . import Slot
|
8
|
-
|
9
|
-
if TYPE_CHECKING:
|
10
|
-
from _typeshed import StrOrBytesPath
|
11
|
-
else:
|
12
|
-
StrOrBytesPath = Any
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
def
|
55
|
-
return
|
56
|
-
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from os import remove
|
3
|
+
from os.path import isfile
|
4
|
+
from typing import TYPE_CHECKING, Any
|
5
|
+
|
6
|
+
from ..stage import Stage
|
7
|
+
from . import Slot
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from _typeshed import StrOrBytesPath
|
11
|
+
else:
|
12
|
+
StrOrBytesPath = Any
|
13
|
+
|
14
|
+
|
15
|
+
__all__ = ["FileSlot"]
|
16
|
+
|
17
|
+
|
18
|
+
class FileSlot(Slot, ABC):
|
19
|
+
__match_args__ = ("path",)
|
20
|
+
__slots__ = ("_path",)
|
21
|
+
|
22
|
+
_path: StrOrBytesPath
|
23
|
+
|
24
|
+
def __init__(self, path: StrOrBytesPath, /) -> None:
|
25
|
+
self._path = path
|
26
|
+
|
27
|
+
def __bool__(self) -> bool:
|
28
|
+
return isfile(self.path)
|
29
|
+
|
30
|
+
@staticmethod
|
31
|
+
@abstractmethod
|
32
|
+
def deserialize(data: bytes, /) -> Stage:
|
33
|
+
pass
|
34
|
+
|
35
|
+
def __eq__(self, other: object, /) -> bool:
|
36
|
+
if isinstance(other, FileSlot) and (
|
37
|
+
isinstance(other, type(self)) or isinstance(self, type(other))
|
38
|
+
):
|
39
|
+
return self.path == other.path
|
40
|
+
else:
|
41
|
+
return NotImplemented
|
42
|
+
|
43
|
+
def __hash__(self) -> int:
|
44
|
+
return hash(self.path)
|
45
|
+
|
46
|
+
def load(self) -> Stage | None:
|
47
|
+
try:
|
48
|
+
with open(self.path, "rb") as f:
|
49
|
+
return self.deserialize(f.read())
|
50
|
+
except FileNotFoundError:
|
51
|
+
return None
|
52
|
+
|
53
|
+
@property
|
54
|
+
def path(self) -> StrOrBytesPath:
|
55
|
+
return self._path
|
56
|
+
|
57
|
+
def __repr__(self) -> str:
|
58
|
+
return f"{type(self).__name__}({self.path!r})"
|
59
|
+
|
60
|
+
def save(self, data: Stage | None) -> None:
|
61
|
+
if data is None:
|
62
|
+
remove(self.path)
|
63
|
+
else:
|
64
|
+
with open(self.path, "wb") as f:
|
65
|
+
f.write(self.serialize(data))
|
66
|
+
|
67
|
+
@staticmethod
|
68
|
+
@abstractmethod
|
69
|
+
def serialize(stage: Stage, /) -> bytes:
|
70
|
+
pass
|