koro 2.0.0rc3__py3-none-any.whl → 2.0.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
koro/slot/save.py CHANGED
@@ -1,137 +1,137 @@
1
- from enum import Enum, unique
2
- from io import BytesIO
3
- from operator import index as ix
4
- from os.path import basename, dirname, join
5
- from typing import TYPE_CHECKING, Annotated, Any, Literal, SupportsIndex
6
- from collections.abc import Mapping, Sequence
7
-
8
- from ..stage import Stage
9
-
10
- from . import Slot
11
- from .xml import XmlSlot
12
-
13
- if TYPE_CHECKING:
14
- from _typeshed import StrOrBytesPath
15
- else:
16
- StrOrBytesPath = Any
17
-
18
-
19
- __all__ = ["EditorPage", "get_slots", "SaveSlot"]
20
-
21
-
22
- @unique
23
- class EditorPage(Enum):
24
- ORIGINAL = 0
25
- FRIEND = 1
26
- HUDSON = 2
27
-
28
-
29
- class SaveSlot(Slot):
30
- __match_args__ = ("path", "page", "index")
31
- __slots__ = ("_offset", "_path")
32
-
33
- _offset: Literal[8, 156392, 312776, 469160]
34
- _path: str | bytes
35
-
36
- def __init__(
37
- self,
38
- path: StrOrBytesPath,
39
- page: EditorPage,
40
- index: Annotated[
41
- SupportsIndex,
42
- Literal[
43
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
44
- ],
45
- ],
46
- ) -> None:
47
- index = ix(index) - 1
48
- if index in range(0, 20):
49
- self._offset = 8 + 156864 * (index & 3) # type: ignore[assignment]
50
- self._path = join(path, f"ed{(index >> 2) + 5 * page.value:02}.dat") # type: ignore[arg-type]
51
- else:
52
- raise ValueError("index must be between 1 and 20")
53
-
54
- def __bool__(self) -> bool:
55
- try:
56
- with open(self._path, "rb") as f:
57
- f.seek(self._offset)
58
- return f.read(1) != b"\x00"
59
- except FileNotFoundError:
60
- return False
61
-
62
- def __eq__(self, other: Any, /) -> bool:
63
- if isinstance(other, SaveSlot):
64
- return self._path == other._path and self._offset == other._offset
65
- else:
66
- return NotImplemented
67
-
68
- def __hash__(self) -> int:
69
- return hash((self._offset, self._path))
70
-
71
- @property
72
- def index(
73
- self,
74
- ) -> Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]:
75
- return (int(basename(self._path)[2:4]) % 5 >> 2 | self._offset // 156864) + 1 # type: ignore[return-value]
76
-
77
- def load(self) -> Stage | None:
78
- try:
79
- with open(self._path, "rb") as f:
80
- f.seek(self._offset)
81
- with BytesIO() as b:
82
- block: bytearray = bytearray()
83
- while True:
84
- block.clear()
85
- f.readinto1(block)
86
- if len(b.getbuffer()) + len(block) > 156864:
87
- del block[156864 - len(b.getbuffer()) :]
88
- if block[-1]:
89
- b.write(block)
90
- else:
91
- while block:
92
- if block[len(block) >> 1]:
93
- b.write(block[: (len(block) >> 1) + 1])
94
- del block[: (len(block) >> 1) + 1]
95
- else:
96
- del block[len(block) >> 1 :]
97
- data: bytes = b.getvalue()
98
- if data:
99
- return XmlSlot.deserialize(data)
100
- else:
101
- return None
102
- except FileNotFoundError:
103
- return None
104
-
105
- @property
106
- def page(self) -> EditorPage:
107
- return EditorPage(int(basename(self._path)[2:4]) // 5)
108
-
109
- @property
110
- def path(self) -> StrOrBytesPath:
111
- return dirname(self._path)
112
-
113
- def __repr__(self) -> str:
114
- return f"{type(self).__name__}({self.path!r}, {self.page!r}, {self.index!r})"
115
-
116
- def save(self, data: Stage | None) -> None:
117
- binary: bytes = b"" if data is None else XmlSlot.serialize(data)
118
- if len(binary) > 156864:
119
- raise ValueError("serialized level data is too large to save")
120
- try:
121
- with open(self._path, "xb") as f:
122
- f.write(bytes(638976))
123
- if data is None:
124
- return
125
- except FileExistsError:
126
- pass
127
- with open(self._path, "r+b") as f:
128
- f.seek(self._offset)
129
- f.write(binary)
130
- f.write(bytes(156864 - len(binary)))
131
-
132
-
133
- def get_slots(save: StrOrBytesPath, /) -> Mapping[EditorPage, Sequence[SaveSlot]]:
134
- return {
135
- page: tuple(SaveSlot(save, page, i) for i in range(1, 21))
136
- for page in EditorPage
137
- }
1
+ from enum import Enum, unique
2
+ from io import BytesIO
3
+ from operator import index as ix
4
+ from os.path import basename, dirname, join
5
+ from typing import TYPE_CHECKING, Annotated, Any, Literal, SupportsIndex
6
+ from collections.abc import Mapping, Sequence
7
+
8
+ from ..stage import Stage
9
+
10
+ from . import Slot
11
+ from .xml import XmlSlot
12
+
13
+ if TYPE_CHECKING:
14
+ from _typeshed import StrOrBytesPath
15
+ else:
16
+ StrOrBytesPath = Any
17
+
18
+
19
+ __all__ = ["EditorPage", "get_slots", "SaveSlot"]
20
+
21
+
22
+ @unique
23
+ class EditorPage(Enum):
24
+ ORIGINAL = 0
25
+ FRIEND = 1
26
+ HUDSON = 2
27
+
28
+
29
+ class SaveSlot(Slot):
30
+ __match_args__ = ("path", "page", "index")
31
+ __slots__ = ("_offset", "_path")
32
+
33
+ _offset: Literal[8, 156392, 312776, 469160]
34
+ _path: str | bytes
35
+
36
+ def __init__(
37
+ self,
38
+ path: StrOrBytesPath,
39
+ page: EditorPage,
40
+ index: Annotated[
41
+ SupportsIndex,
42
+ Literal[
43
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
44
+ ],
45
+ ],
46
+ ) -> None:
47
+ index = ix(index) - 1
48
+ if index in range(0, 20):
49
+ self._offset = 8 + 156864 * (index & 3) # type: ignore[assignment]
50
+ self._path = join(path, f"ed{(index >> 2) + 5 * page.value:02}.dat") # type: ignore[arg-type]
51
+ else:
52
+ raise ValueError("index must be between 1 and 20")
53
+
54
+ def __bool__(self) -> bool:
55
+ try:
56
+ with open(self._path, "rb") as f:
57
+ f.seek(self._offset)
58
+ return f.read(1) != b"\x00"
59
+ except FileNotFoundError:
60
+ return False
61
+
62
+ def __eq__(self, other: Any, /) -> bool:
63
+ if isinstance(other, SaveSlot):
64
+ return self._path == other._path and self._offset == other._offset
65
+ else:
66
+ return NotImplemented
67
+
68
+ def __hash__(self) -> int:
69
+ return hash((self._offset, self._path))
70
+
71
+ @property
72
+ def index(
73
+ self,
74
+ ) -> Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]:
75
+ return (int(basename(self._path)[2:4]) % 5 >> 2 | self._offset // 156864) + 1 # type: ignore[return-value]
76
+
77
+ def load(self) -> Stage | None:
78
+ try:
79
+ with open(self._path, "rb") as f:
80
+ f.seek(self._offset)
81
+ with BytesIO() as b:
82
+ block: bytearray = bytearray()
83
+ while True:
84
+ block.clear()
85
+ block.extend(f.read1())
86
+ if len(b.getbuffer()) + len(block) > 156864:
87
+ del block[156864 - len(b.getbuffer()) :]
88
+ if block[-1]:
89
+ b.write(block)
90
+ else:
91
+ while block:
92
+ if block[len(block) >> 1]:
93
+ b.write(block[: (len(block) >> 1) + 1])
94
+ del block[: (len(block) >> 1) + 1]
95
+ else:
96
+ del block[len(block) >> 1 :]
97
+ data: bytes = b.getvalue()
98
+ if data:
99
+ return XmlSlot.deserialize(data)
100
+ else:
101
+ return None
102
+ except FileNotFoundError:
103
+ return None
104
+
105
+ @property
106
+ def page(self) -> EditorPage:
107
+ return EditorPage(int(basename(self._path)[2:4]) // 5)
108
+
109
+ @property
110
+ def path(self) -> StrOrBytesPath:
111
+ return dirname(self._path)
112
+
113
+ def __repr__(self) -> str:
114
+ return f"{type(self).__name__}({self.path!r}, {self.page!r}, {self.index!r})"
115
+
116
+ def save(self, data: Stage | None) -> None:
117
+ binary: bytes = b"" if data is None else XmlSlot.serialize(data)
118
+ if len(binary) > 156864:
119
+ raise ValueError("serialized stage data is too large to save")
120
+ try:
121
+ with open(self._path, "xb") as f:
122
+ f.write(bytes(638976))
123
+ if data is None:
124
+ return
125
+ except FileExistsError:
126
+ pass
127
+ with open(self._path, "r+b") as f:
128
+ f.seek(self._offset)
129
+ f.write(binary)
130
+ f.write(bytes(156864 - len(binary)))
131
+
132
+
133
+ def get_slots(save: StrOrBytesPath, /) -> Mapping[EditorPage, Sequence[SaveSlot]]:
134
+ return {
135
+ page: tuple(SaveSlot(save, page, i) for i in range(1, 21))
136
+ for page in EditorPage
137
+ }