PyLibMS 4.0.4__tar.gz → 4.1.1__tar.gz
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.
- {pylibms-4.0.4 → pylibms-4.1.1}/PKG-INFO +2 -1
- {pylibms-4.0.4 → pylibms-4.1.1}/PyLibMS.egg-info/PKG-INFO +2 -1
- pylibms-4.1.1/PyLibMS.egg-info/requires.txt +2 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/flowchart/definitions/node.py +2 -12
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/flowchart/flw3.py +27 -27
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/flowchart/msbf.py +1 -1
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/config.py +96 -24
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/definitions/nodes.py +5 -5
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Tomodachi Life NA-EU.yaml +3 -21
- {pylibms-4.0.4 → pylibms-4.1.1}/pyproject.toml +5 -2
- pylibms-4.0.4/PyLibMS.egg-info/requires.txt +0 -1
- {pylibms-4.0.4 → pylibms-4.1.1}/LICENSE +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/MANIFEST.in +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/PyLibMS.egg-info/SOURCES.txt +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/PyLibMS.egg-info/dependency_links.txt +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/PyLibMS.egg-info/top_level.txt +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/README.md +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/field/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/field/io.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/field/lms_datatype.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/field/lms_field.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/lms_constants.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/lms_exceptions.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/lms_fileinfo.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/stream/fileinfo.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/stream/hashtable.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/common/stream/section.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/fileio/encoding.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/fileio/io.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/flowchart/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/flowchart/definitions/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/flowchart/definitions/flowchart.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/flowchart/definitions/lms_nodeexceptions.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/flowchart/definitions/node_type.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/flowchart/msbfio.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/definitions/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/definitions/field/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/lms_messagetext.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/msbt.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/msbtentry.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/msbtio.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/section/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/section/atr1.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/section/nli1.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/section/tsy1.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/section/txt2.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/tag/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/tag/io/param_io.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/tag/io/tag_io.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/tag/lms_tag.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/message/tag/lms_tagexceptions.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/definitions/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/definitions/attribute.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/definitions/color.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/definitions/style.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/definitions/tag.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/msbp.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/msbpread.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/section/ali2.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/section/ati2.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/section/clr1.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/section/string.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/section/syl3.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/section/tag2.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/section/tgg2.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/project/section/tgp2.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/definitions/__init__.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/definitions/attribute.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/definitions/tags.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/definitions/value.py +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Badge Arcade.yaml +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Brain Age Concentration Training.yaml +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Kirby Planet Robobot.yaml +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Super Mario 3D Land.yaml +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Super Mario 3D World + Bowsers Fury.yaml +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Super Mario Odyssey.yaml +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/The Legend of Zelda Echos of Wisdom.yaml +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/The Legend of Zelda a Link Between Worlds.yaml +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Tomodachi Life Living The Dream.yaml +0 -0
- {pylibms-4.0.4 → pylibms-4.1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyLibMS
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: Python library built for the libMessageStudio (LMS) proprietary file formats from Nintendo. Supports MSBT, MSBP, and MSBF.
|
|
5
5
|
Author: AbdyyEee
|
|
6
6
|
License-Expression: MIT
|
|
@@ -11,6 +11,7 @@ Requires-Python: >=3.12
|
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Requires-Dist: PyYAML>=6.0.1
|
|
14
|
+
Requires-Dist: requests>=2.34.0
|
|
14
15
|
Dynamic: license-file
|
|
15
16
|
|
|
16
17
|
# PylibMS
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyLibMS
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: Python library built for the libMessageStudio (LMS) proprietary file formats from Nintendo. Supports MSBT, MSBP, and MSBF.
|
|
5
5
|
Author: AbdyyEee
|
|
6
6
|
License-Expression: MIT
|
|
@@ -11,6 +11,7 @@ Requires-Python: >=3.12
|
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Requires-Dist: PyYAML>=6.0.1
|
|
14
|
+
Requires-Dist: requests>=2.34.0
|
|
14
15
|
Dynamic: license-file
|
|
15
16
|
|
|
16
17
|
# PylibMS
|
|
@@ -122,8 +122,8 @@ class LMS_MessageNode(LMS_BaseNode):
|
|
|
122
122
|
stream_next_node
|
|
123
123
|
)
|
|
124
124
|
|
|
125
|
-
self.
|
|
126
|
-
self.
|
|
125
|
+
self.msbt_index = msbt_index
|
|
126
|
+
self.label_index = label_index
|
|
127
127
|
self._msbt = msbt
|
|
128
128
|
|
|
129
129
|
@classmethod
|
|
@@ -147,16 +147,6 @@ class LMS_MessageNode(LMS_BaseNode):
|
|
|
147
147
|
"""
|
|
148
148
|
return cls(None, None, file_index, msbt.entries.index(entry), msbt)
|
|
149
149
|
|
|
150
|
-
@property
|
|
151
|
-
def msbt_index(self) -> int:
|
|
152
|
-
"""Index of the MSBT file the node references in a folder/archive."""
|
|
153
|
-
return self._msbt_index
|
|
154
|
-
|
|
155
|
-
@property
|
|
156
|
-
def label_index(self) -> int:
|
|
157
|
-
"""Index of the MSBT entry the node references in the file."""
|
|
158
|
-
return self._entry_index
|
|
159
|
-
|
|
160
150
|
@property
|
|
161
151
|
def msbt(self) -> MSBT | None:
|
|
162
152
|
"""MSBT instance the node references. Optional, and must be set with `set_msbt` or by providing a MSBT file on reading."""
|
|
@@ -9,7 +9,7 @@ from lms.flowchart.definitions.node_type import LMS_NodeType, LMS_NodeParameterT
|
|
|
9
9
|
from lms.message.msbt import MSBT
|
|
10
10
|
from lms.titleconfig.definitions.nodes import NodeConfig, NodeDefinition
|
|
11
11
|
|
|
12
|
-
NO_NEXT_NODE =
|
|
12
|
+
NO_NEXT_NODE = 0xFFFF
|
|
13
13
|
FLW3_HEADER_SIZE = 16
|
|
14
14
|
NODE_SIZE = 16
|
|
15
15
|
|
|
@@ -50,7 +50,7 @@ def read_flw3(reader: FileReader, config: NodeConfig | None, msbt: MSBT | None,
|
|
|
50
50
|
reader.skip(2 + 4)
|
|
51
51
|
|
|
52
52
|
condition_id = reader.read_uint16()
|
|
53
|
-
case_count = reader.
|
|
53
|
+
case_count = reader.read_uint16()
|
|
54
54
|
table_index = reader.read_uint16()
|
|
55
55
|
end = reader.tell()
|
|
56
56
|
|
|
@@ -86,7 +86,7 @@ def read_flw3(reader: FileReader, config: NodeConfig | None, msbt: MSBT | None,
|
|
|
86
86
|
case LMS_NodeType.JUMP:
|
|
87
87
|
reader.seek(node_data)
|
|
88
88
|
next_flowchart_id = read_next_node_id(reader)
|
|
89
|
-
unknown_short0a = reader.
|
|
89
|
+
unknown_short0a = reader.read_uint16()
|
|
90
90
|
node = LMS_JumpNode(i, next_flowchart_id, unknown_short0a)
|
|
91
91
|
reader.skip(4)
|
|
92
92
|
|
|
@@ -121,7 +121,7 @@ def read_flw3(reader: FileReader, config: NodeConfig | None, msbt: MSBT | None,
|
|
|
121
121
|
|
|
122
122
|
|
|
123
123
|
def read_next_node_id(reader: FileReader) -> int | None:
|
|
124
|
-
return None if (id := reader.
|
|
124
|
+
return None if (id := reader.read_uint16()) == NO_NEXT_NODE else id
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
def get_config_definition(config: NodeConfig,
|
|
@@ -163,15 +163,15 @@ def evaluate_node_parameter(reader: FileReader,
|
|
|
163
163
|
# Default parameter reading
|
|
164
164
|
match parameter_type:
|
|
165
165
|
case LMS_NodeParameterType.PARAM_32_0 | LMS_NodeParameterType.PARAM_32_1:
|
|
166
|
-
return reader.
|
|
166
|
+
return reader.read_uint32()
|
|
167
167
|
case LMS_NodeParameterType.PARAM_16_16:
|
|
168
|
-
return reader.
|
|
168
|
+
return reader.read_uint16(), reader.read_uint16()
|
|
169
169
|
case LMS_NodeParameterType.PARAM_16_8_8:
|
|
170
|
-
return reader.
|
|
170
|
+
return reader.read_uint16(), reader.read_uint8(), reader.read_uint8()
|
|
171
171
|
case LMS_NodeParameterType.PARAM_8_8_16:
|
|
172
|
-
return reader.
|
|
172
|
+
return reader.read_uint8(), reader.read_uint8(), reader.read_uint16()
|
|
173
173
|
case LMS_NodeParameterType.PARAM_8_8_8_8:
|
|
174
|
-
return reader.
|
|
174
|
+
return reader.read_uint8(), reader.read_uint8(), reader.read_uint8(), reader.read_uint8()
|
|
175
175
|
case LMS_NodeParameterType.STRING:
|
|
176
176
|
return reader.read_string_offset(section_start)
|
|
177
177
|
case _:
|
|
@@ -227,11 +227,11 @@ def write_flw3(writer: FileWriter, nodes: list[LMS_BaseNode], stream_ids: dict[L
|
|
|
227
227
|
starting_index = len(branch_table)
|
|
228
228
|
for branch in node.branches.values():
|
|
229
229
|
if branch is None:
|
|
230
|
-
branch_table.append(
|
|
230
|
+
branch_table.append(NO_NEXT_NODE)
|
|
231
231
|
else:
|
|
232
232
|
branch_table.append(stream_ids[branch])
|
|
233
233
|
|
|
234
|
-
writer.
|
|
234
|
+
writer.write_uint16(NO_NEXT_NODE)
|
|
235
235
|
|
|
236
236
|
writer.write_uint16(node.condition_id)
|
|
237
237
|
writer.write_uint16(node.case_count)
|
|
@@ -268,11 +268,11 @@ def write_flw3(writer: FileWriter, nodes: list[LMS_BaseNode], stream_ids: dict[L
|
|
|
268
268
|
writer.write_bytes(b"\x00" * 4)
|
|
269
269
|
|
|
270
270
|
write_next_node_id(writer, stream_ids[node.next_flowchart])
|
|
271
|
-
writer.
|
|
271
|
+
writer.write_uint16(node.unknown_short0a)
|
|
272
272
|
writer.skip(4)
|
|
273
273
|
|
|
274
274
|
for node_id in branch_table:
|
|
275
|
-
writer.
|
|
275
|
+
writer.write_uint16(node_id)
|
|
276
276
|
|
|
277
277
|
string_offset = FLW3_HEADER_SIZE + (NODE_SIZE * node_count) + (2 * len(branch_table))
|
|
278
278
|
|
|
@@ -303,24 +303,24 @@ def write_node_parameter(writer: FileWriter,
|
|
|
303
303
|
|
|
304
304
|
match parameter_type:
|
|
305
305
|
case LMS_NodeParameterType.PARAM_32_0 | LMS_NodeParameterType.PARAM_32_1:
|
|
306
|
-
writer.
|
|
306
|
+
writer.write_uint32(value)
|
|
307
307
|
case LMS_NodeParameterType.PARAM_16_16:
|
|
308
|
-
writer.
|
|
309
|
-
writer.
|
|
308
|
+
writer.write_uint16(value[0])
|
|
309
|
+
writer.write_uint16(value[1])
|
|
310
310
|
case LMS_NodeParameterType.PARAM_16_8_8:
|
|
311
|
-
writer.
|
|
312
|
-
writer.
|
|
313
|
-
writer.
|
|
311
|
+
writer.write_uint16(value[0])
|
|
312
|
+
writer.write_uint8(value[1])
|
|
313
|
+
writer.write_uint8(value[2])
|
|
314
314
|
case LMS_NodeParameterType.PARAM_8_8_16:
|
|
315
|
-
writer.
|
|
316
|
-
writer.
|
|
317
|
-
writer.
|
|
315
|
+
writer.write_uint8(value[0])
|
|
316
|
+
writer.write_uint8(value[1])
|
|
317
|
+
writer.write_uint16(value[2])
|
|
318
318
|
case LMS_NodeParameterType.PARAM_8_8_8_8:
|
|
319
|
-
writer.
|
|
320
|
-
writer.
|
|
321
|
-
writer.
|
|
322
|
-
writer.
|
|
319
|
+
writer.write_uint8(value[0])
|
|
320
|
+
writer.write_uint8(value[1])
|
|
321
|
+
writer.write_uint8(value[2])
|
|
322
|
+
writer.write_uint8(value[3])
|
|
323
323
|
|
|
324
324
|
|
|
325
325
|
def write_next_node_id(writer: FileWriter, id: int | None) -> None:
|
|
326
|
-
writer.
|
|
326
|
+
writer.write_uint16(NO_NEXT_NODE if id is None else id)
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import hashlib
|
|
2
|
+
import os.path
|
|
3
|
+
import pathlib
|
|
4
|
+
from typing import Literal, get_args
|
|
2
5
|
|
|
6
|
+
import requests
|
|
3
7
|
import yaml
|
|
4
8
|
|
|
5
9
|
from lms.common.field.lms_datatype import LMS_DataType
|
|
@@ -10,6 +14,8 @@ from lms.titleconfig.definitions.nodes import NodeConfig, NodeDefinition
|
|
|
10
14
|
from lms.titleconfig.definitions.tags import TagConfig, TagDefinition
|
|
11
15
|
from lms.titleconfig.definitions.value import ValueDefinition
|
|
12
16
|
|
|
17
|
+
PRESETS_URL = "https://api.github.com/repos/AbdyyEee/PyLibMS/contents/lms/titleconfig/presets"
|
|
18
|
+
|
|
13
19
|
|
|
14
20
|
class TitleConfig:
|
|
15
21
|
"""
|
|
@@ -20,9 +26,17 @@ class TitleConfig:
|
|
|
20
26
|
ATTR_KEY = "attribute_definitions"
|
|
21
27
|
NODE_KEY = "node_definitions"
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
GAME_PRESET = Literal[
|
|
30
|
+
"Badge Arcade",
|
|
31
|
+
"Brain Age Concentration Training",
|
|
32
|
+
"Kirby Planet Robobot",
|
|
33
|
+
"Super Mario Odyssey",
|
|
34
|
+
"Super Mario 3D Land",
|
|
35
|
+
"Super Mario 3D World + Bowsers Fury",
|
|
36
|
+
"The Legend of Zelda a Link Between Worlds",
|
|
37
|
+
"The Legend of Zelda Echos of Wisdom",
|
|
38
|
+
"Tomodachi Life Living The Dream",
|
|
39
|
+
"Tomodachi Life NA-EU",
|
|
26
40
|
]
|
|
27
41
|
|
|
28
42
|
def __init__(
|
|
@@ -42,6 +56,84 @@ class TitleConfig:
|
|
|
42
56
|
"""The name of the game for the titleconfig."""
|
|
43
57
|
return self._game
|
|
44
58
|
|
|
59
|
+
@classmethod
|
|
60
|
+
def get_preset_list(cls) -> tuple[str, ...]:
|
|
61
|
+
"""Get the current preset list."""
|
|
62
|
+
return get_args(cls.GAME_PRESET)
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def preset_has_update(cls, game: GAME_PRESET) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Checks whether a preset has an available update.
|
|
68
|
+
|
|
69
|
+
:param game: the game preset.
|
|
70
|
+
"""
|
|
71
|
+
preset_list = cls._request_preset_list()
|
|
72
|
+
|
|
73
|
+
if f"{game}.yaml" not in os.listdir("presets"):
|
|
74
|
+
raise FileNotFoundError(f"Preset '{game}.yaml' was not found!")
|
|
75
|
+
|
|
76
|
+
path = os.path.join("presets", f"{game}.yaml")
|
|
77
|
+
|
|
78
|
+
with open(path, "rb") as f:
|
|
79
|
+
data = f.read().replace(b"\r\n", b"\n")
|
|
80
|
+
|
|
81
|
+
# Blob calculation https://git-scm.com/book/en/v2/Git-Internals-Git-Objects.html
|
|
82
|
+
local_hash = hashlib.sha1(f"blob {len(data)}\0".encode() + data).hexdigest()
|
|
83
|
+
|
|
84
|
+
return local_hash != preset_list[game.lower()]["sha"]
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def download_preset(cls, game: GAME_PRESET):
|
|
88
|
+
"""
|
|
89
|
+
Fetches a preset from the repository and loads it to ./presets
|
|
90
|
+
|
|
91
|
+
:param game: the game preset.
|
|
92
|
+
|
|
93
|
+
List of presets:
|
|
94
|
+
|
|
95
|
+
https://github.com/AbdyyEee/PylibMS/tree/main/lms/titleconfig/presets
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
preset_list = cls._request_preset_list()
|
|
99
|
+
|
|
100
|
+
if game.lower() not in preset_list:
|
|
101
|
+
raise FileNotFoundError(f"Preset '{game}' not found.")
|
|
102
|
+
|
|
103
|
+
raw = cls._request_preset_file(game, preset_list)
|
|
104
|
+
|
|
105
|
+
if "presets" not in os.path.join(os.getcwd(), "presets", f"{game}.yaml"):
|
|
106
|
+
pathlib.Path("presets").mkdir()
|
|
107
|
+
|
|
108
|
+
path = os.path.join("presets", f"{game}.yaml")
|
|
109
|
+
with open(path, "wb") as f:
|
|
110
|
+
f.write(raw.content)
|
|
111
|
+
|
|
112
|
+
@classmethod
|
|
113
|
+
def _request_preset_list(cls) -> dict[str, dict]:
|
|
114
|
+
result = {}
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
folder_response = requests.get(PRESETS_URL, timeout=10)
|
|
118
|
+
folder_response.raise_for_status()
|
|
119
|
+
except requests.exceptions.RequestException as e:
|
|
120
|
+
raise RuntimeError("An error occurred fetching the preset list!") from e
|
|
121
|
+
|
|
122
|
+
for preset in folder_response.json():
|
|
123
|
+
result[os.path.basename(preset["path"]).lower().removesuffix(".yaml")] = preset
|
|
124
|
+
|
|
125
|
+
return result
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
def _request_preset_file(cls, game: str, preset_list: dict) -> requests.Response:
|
|
129
|
+
try:
|
|
130
|
+
raw_response = requests.get(preset_list[game.lower()]["download_url"], timeout=10)
|
|
131
|
+
raw_response.raise_for_status()
|
|
132
|
+
except requests.exceptions.RequestException as e:
|
|
133
|
+
raise RuntimeError(f"An error occurred fetching the file data for preset '{game}'") from e
|
|
134
|
+
|
|
135
|
+
return raw_response
|
|
136
|
+
|
|
45
137
|
@property
|
|
46
138
|
def tag_config(self) -> TagConfig | None:
|
|
47
139
|
"""The loaded tag config instance."""
|
|
@@ -75,26 +167,6 @@ class TitleConfig:
|
|
|
75
167
|
"""The loaded node configuration."""
|
|
76
168
|
return self._node_config
|
|
77
169
|
|
|
78
|
-
@classmethod
|
|
79
|
-
def load_preset(cls, game: str):
|
|
80
|
-
"""
|
|
81
|
-
Loads an existing preset from a game.
|
|
82
|
-
|
|
83
|
-
:param game: the game preset.
|
|
84
|
-
|
|
85
|
-
The list of presets are found with `TitleConfig.preset_list`.
|
|
86
|
-
"""
|
|
87
|
-
# TODO: Load presets via requests instead of hard-coded files in the library
|
|
88
|
-
preset_map = {preset.lower(): preset for preset in cls.PRESET_LIST}
|
|
89
|
-
|
|
90
|
-
if game.lower() not in preset_map:
|
|
91
|
-
raise FileNotFoundError(f"Preset '{game}' not found.")
|
|
92
|
-
|
|
93
|
-
actual_name = preset_map[game.lower()]
|
|
94
|
-
|
|
95
|
-
with resources.open_text("lms.titleconfig.presets", f"{actual_name}.yaml") as f:
|
|
96
|
-
return cls.load_config(f.read())
|
|
97
|
-
|
|
98
170
|
@classmethod
|
|
99
171
|
def load_file(cls, file_path: str):
|
|
100
172
|
"""
|
|
@@ -69,16 +69,16 @@ class NodeDefinition:
|
|
|
69
69
|
if datatype_from_dict is None:
|
|
70
70
|
match parameter_type:
|
|
71
71
|
case LMS_NodeParameterType.PARAM_32_0 | LMS_NodeParameterType.PARAM_32_1:
|
|
72
|
-
datatype_from_dict = LMS_DataType.
|
|
72
|
+
datatype_from_dict = LMS_DataType.UINT32
|
|
73
73
|
case LMS_NodeParameterType.PARAM_16_16:
|
|
74
|
-
datatype_from_dict = (LMS_DataType.
|
|
74
|
+
datatype_from_dict = (LMS_DataType.UINT16, LMS_DataType.UINT16)[i]
|
|
75
75
|
case LMS_NodeParameterType.PARAM_16_8_8:
|
|
76
|
-
datatype_from_dict = (LMS_DataType.
|
|
76
|
+
datatype_from_dict = (LMS_DataType.UINT16, LMS_DataType.UINT8, LMS_DataType.UINT8)[i]
|
|
77
77
|
case LMS_NodeParameterType.PARAM_8_8_16:
|
|
78
|
-
datatype_from_dict = (LMS_DataType.
|
|
78
|
+
datatype_from_dict = (LMS_DataType.UINT8, LMS_DataType.UINT8, LMS_DataType.UINT16)[i]
|
|
79
79
|
case LMS_NodeParameterType.PARAM_8_8_8_8:
|
|
80
80
|
datatype_from_dict = \
|
|
81
|
-
(LMS_DataType.
|
|
81
|
+
(LMS_DataType.UINT8, LMS_DataType.UINT8, LMS_DataType.UINT8, LMS_DataType.UINT8)[i]
|
|
82
82
|
case LMS_NodeParameterType.STRING:
|
|
83
83
|
datatype_from_dict = LMS_DataType.STRING
|
|
84
84
|
definition = ValueDefinition(parameter["name"], data.get("description", ""), datatype_from_dict,
|
|
@@ -3,61 +3,43 @@ node_definitions:
|
|
|
3
3
|
branch:
|
|
4
4
|
- id: 0
|
|
5
5
|
name: EvaluateExpression
|
|
6
|
+
description: ''
|
|
6
7
|
parameter_type: string
|
|
7
8
|
parameters:
|
|
8
9
|
- name: exp
|
|
9
|
-
description: ''
|
|
10
10
|
- id: 1
|
|
11
11
|
name: SwitchVariable
|
|
12
|
-
description: ''
|
|
13
12
|
parameter_type: string
|
|
14
13
|
parameters:
|
|
15
14
|
- name: var
|
|
16
|
-
description: ''
|
|
17
15
|
event:
|
|
18
16
|
- id: 3
|
|
19
17
|
name: SetVariable
|
|
20
|
-
description: ''
|
|
21
18
|
parameter_type: string
|
|
22
19
|
parameters:
|
|
23
20
|
- name: expression
|
|
24
|
-
description: ''
|
|
25
21
|
- id: 43
|
|
26
22
|
name: SetMiiPosition
|
|
27
|
-
description: ''
|
|
28
23
|
parameter_type: string
|
|
29
24
|
parameters:
|
|
30
25
|
- name: pos
|
|
31
|
-
description: ''
|
|
32
|
-
- id: 48
|
|
33
|
-
name: SetupMessage
|
|
34
|
-
description: ''
|
|
35
|
-
parameter_type: p16_16
|
|
36
|
-
parameters:
|
|
37
|
-
- name: val1
|
|
38
|
-
- name: val2
|
|
39
26
|
- id: 58
|
|
40
27
|
name: SetupButtonPrompt
|
|
41
|
-
description: ''
|
|
42
28
|
next_node_dependency: true
|
|
43
29
|
parameter_type: string
|
|
44
30
|
parameters:
|
|
45
31
|
- name: var
|
|
46
|
-
description: ''
|
|
47
32
|
- id: 90
|
|
48
33
|
name: LoadSEGroup
|
|
49
|
-
description: ''
|
|
50
34
|
parameter_type: string
|
|
51
35
|
parameters:
|
|
52
36
|
- name: group
|
|
53
37
|
description: ''
|
|
54
38
|
- id: 96
|
|
55
39
|
name: PlaySE
|
|
56
|
-
description: ''
|
|
57
40
|
parameter_type: string
|
|
58
41
|
parameters:
|
|
59
42
|
- name: name
|
|
60
|
-
description: ''
|
|
61
43
|
tag_definitions:
|
|
62
44
|
groups:
|
|
63
45
|
0: System
|
|
@@ -96,7 +78,7 @@ tag_definitions:
|
|
|
96
78
|
description: 'Alters the size of the text.'
|
|
97
79
|
parameters:
|
|
98
80
|
- name: percent
|
|
99
|
-
description: 'The size as a percentage'
|
|
81
|
+
description: 'The size as a percentage.'
|
|
100
82
|
datatype: uint16
|
|
101
83
|
- name: Color
|
|
102
84
|
group_id: 0
|
|
@@ -7466,4 +7448,4 @@ attribute_definitions:
|
|
|
7466
7448
|
- Relation
|
|
7467
7449
|
- name: SolvedCount
|
|
7468
7450
|
description: ''
|
|
7469
|
-
datatype: uint8
|
|
7451
|
+
datatype: uint8
|
|
@@ -7,7 +7,7 @@ include = ["lms*"]
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "PyLibMS"
|
|
10
|
-
version = "4.
|
|
10
|
+
version = "4.1.1"
|
|
11
11
|
requires-python = ">=3.12"
|
|
12
12
|
description = "Python library built for the libMessageStudio (LMS) proprietary file formats from Nintendo. Supports MSBT, MSBP, and MSBF."
|
|
13
13
|
readme = "README.md"
|
|
@@ -15,7 +15,10 @@ authors = [{ name = "AbdyyEee" }]
|
|
|
15
15
|
keywords = ["nintendo", "msbf", "msbt", "msbp", "modding", "lms", "libmessagestudio", "flw3"]
|
|
16
16
|
license = "MIT"
|
|
17
17
|
license-files = ["LICENSE*"]
|
|
18
|
-
dependencies = [
|
|
18
|
+
dependencies = [
|
|
19
|
+
"PyYAML>=6.0.1",
|
|
20
|
+
"requests>=2.34.0"
|
|
21
|
+
]
|
|
19
22
|
|
|
20
23
|
[project.urls]
|
|
21
24
|
Documentation = "https://github.com/AbdyyEee/PylibMS/wiki"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
PyYAML>=6.0.1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Brain Age Concentration Training.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Super Mario 3D World + Bowsers Fury.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/The Legend of Zelda Echos of Wisdom.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{pylibms-4.0.4 → pylibms-4.1.1}/lms/titleconfig/presets/Tomodachi Life Living The Dream.yaml
RENAMED
|
File without changes
|
|
File without changes
|