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