PyLibMS 3.1.2__tar.gz → 3.1.3__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.
Potentially problematic release.
This version of PyLibMS might be problematic. Click here for more details.
- {pylibms-3.1.2 → pylibms-3.1.3}/PKG-INFO +1 -1
- {pylibms-3.1.2 → pylibms-3.1.3}/PyLibMS.egg-info/PKG-INFO +1 -1
- pylibms-3.1.3/lms/common/lms_fileinfo.py +11 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/definitions/lms_messagetext.py +4 -4
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/msbt.py +35 -18
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/msbtio.py +20 -19
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/tag/lms_tag.py +15 -9
- {pylibms-3.1.2 → pylibms-3.1.3}/pyproject.toml +1 -1
- pylibms-3.1.2/lms/common/lms_fileinfo.py +0 -11
- {pylibms-3.1.2 → pylibms-3.1.3}/LICENSE +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/MANIFEST.in +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/PyLibMS.egg-info/SOURCES.txt +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/PyLibMS.egg-info/dependency_links.txt +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/PyLibMS.egg-info/requires.txt +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/PyLibMS.egg-info/top_level.txt +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/README.md +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/lms_datatype.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/lms_exceptions.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/stream/fileinfo.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/stream/hashtable.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/stream/section.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/fileio/encoding.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/fileio/io.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/definitions/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/definitions/field/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/definitions/field/io.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/definitions/field/lms_field.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/msbtentry.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/section/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/section/atr1.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/section/tsy1.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/section/txt2.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/tag/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/tag/io/param_io.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/tag/io/tag_io.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/tag/lms_tagexceptions.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/definitions/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/definitions/attribute.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/definitions/color.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/definitions/style.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/definitions/tag.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/msbp.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/msbpread.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/ali2.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/ati2.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/clr1.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/string.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/syl3.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/tag2.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/tgg2.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/tgp2.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/config.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/definitions/__init__.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/definitions/attribute.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/definitions/tags.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/definitions/value.py +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Badge Arcade.yaml +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Brain Age Concentration Training.yaml +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Kirby Planet Robobot.yaml +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Super Mario 3D Land.yaml +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Super Mario 3D World + Bowsers Fury.yaml +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Super Mario Odyssey.yaml +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/The Legend of Zelda Echos of Wisdom.yaml +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/The Legend of Zelda a Link Between Worlds.yaml +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Tomodachi Life.yaml +0 -0
- {pylibms-3.1.2 → pylibms-3.1.3}/setup.cfg +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from lms.fileio.encoding import FileEncoding
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class LMS_FileInfo:
|
|
8
|
+
is_big_endian: bool = False
|
|
9
|
+
encoding: FileEncoding = FileEncoding.UTF16
|
|
10
|
+
version: int = 3
|
|
11
|
+
section_count: int = 2
|
|
@@ -16,13 +16,13 @@ class LMS_MessageText:
|
|
|
16
16
|
message: str | list[str | LMS_ControlTag],
|
|
17
17
|
tag_config: TagConfig | None = None,
|
|
18
18
|
):
|
|
19
|
+
self._tag_config = tag_config
|
|
20
|
+
|
|
19
21
|
if isinstance(message, str):
|
|
20
22
|
self._set_parts(message)
|
|
21
23
|
else:
|
|
22
24
|
self._parts = message
|
|
23
25
|
|
|
24
|
-
self._tag_config = tag_config
|
|
25
|
-
|
|
26
26
|
def __iter__(self):
|
|
27
27
|
return iter(self._parts)
|
|
28
28
|
|
|
@@ -118,11 +118,11 @@ class LMS_MessageText:
|
|
|
118
118
|
|
|
119
119
|
:param tag: the tag string.
|
|
120
120
|
"""
|
|
121
|
-
if re.
|
|
121
|
+
if re.fullmatch(LMS_DecodedTag.TAG_FORMAT, tag):
|
|
122
122
|
if self._tag_config is None:
|
|
123
123
|
raise ValueError("TagConfig is required to append decoded tags.")
|
|
124
124
|
self._parts.append(LMS_DecodedTag.from_string(tag, self._tag_config))
|
|
125
|
-
elif re.
|
|
125
|
+
elif re.fullmatch(LMS_EncodedTag.TAG_FORMAT, tag):
|
|
126
126
|
self._parts.append(LMS_EncodedTag.from_string(tag))
|
|
127
127
|
else:
|
|
128
128
|
raise ValueError(f"Invalid format for tag '{tag}'.")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import overload
|
|
2
2
|
|
|
3
3
|
from lms.common.lms_fileinfo import LMS_FileInfo
|
|
4
|
-
from lms.message.definitions.field.lms_field import LMS_FieldMap
|
|
4
|
+
from lms.message.definitions.field.lms_field import FieldValue, LMS_FieldMap
|
|
5
5
|
from lms.message.definitions.lms_messagetext import LMS_MessageText
|
|
6
6
|
from lms.message.msbtentry import MSBTEntry
|
|
7
7
|
from lms.titleconfig.definitions.attribute import AttributeConfig
|
|
@@ -15,11 +15,11 @@ class MSBT:
|
|
|
15
15
|
|
|
16
16
|
def __init__(
|
|
17
17
|
self,
|
|
18
|
-
info: LMS_FileInfo,
|
|
19
|
-
attribute_config: AttributeConfig | None,
|
|
20
|
-
tag_config: TagConfig | None,
|
|
18
|
+
info: LMS_FileInfo | None = None,
|
|
19
|
+
attribute_config: AttributeConfig | None = None,
|
|
20
|
+
tag_config: TagConfig | None = None,
|
|
21
21
|
):
|
|
22
|
-
self._info = info
|
|
22
|
+
self._info = info if info is not None else LMS_FileInfo()
|
|
23
23
|
|
|
24
24
|
self._entries: list[MSBTEntry] = []
|
|
25
25
|
|
|
@@ -33,9 +33,13 @@ class MSBT:
|
|
|
33
33
|
self.uses_encoded_attributes = True
|
|
34
34
|
|
|
35
35
|
self.unsupported_sections: dict[str, bytes] = {}
|
|
36
|
-
self.section_list: list[str] = []
|
|
37
36
|
|
|
38
|
-
#
|
|
37
|
+
# Default section list used for writing
|
|
38
|
+
# The section order is usually fixed at LBL1 -> TXT2 -> ATR1 -> TSY1
|
|
39
|
+
# However, ATR1 and TSY1 are not in the default list ince usually these are not required sections
|
|
40
|
+
# The order of additional sections is preserved when reading
|
|
41
|
+
self.section_list: list[str] = ["LBL1", "TXT2"]
|
|
42
|
+
|
|
39
43
|
self._attribute_config = attribute_config
|
|
40
44
|
self._tag_config = tag_config
|
|
41
45
|
|
|
@@ -73,8 +77,9 @@ class MSBT:
|
|
|
73
77
|
def add_entry(
|
|
74
78
|
self,
|
|
75
79
|
name: str,
|
|
80
|
+
*,
|
|
76
81
|
text: str | None = None,
|
|
77
|
-
attribute: dict | bytes | None = None,
|
|
82
|
+
attribute: dict[str, FieldValue] | bytes | None = None,
|
|
78
83
|
style_index: int | None = None,
|
|
79
84
|
) -> None:
|
|
80
85
|
"""
|
|
@@ -88,27 +93,39 @@ class MSBT:
|
|
|
88
93
|
if name in [entry.name for entry in self.entries]:
|
|
89
94
|
raise KeyError(f"The label '{name}' already exists!")
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
match attribute:
|
|
97
|
+
case dict():
|
|
98
|
+
if self._attribute_config is None:
|
|
99
|
+
raise ValueError(
|
|
100
|
+
"The attribute config must have been provided when reading to add decoded attributes!"
|
|
101
|
+
)
|
|
102
|
+
converted_attr = LMS_FieldMap.from_dict(
|
|
103
|
+
attribute, self._attribute_config.definitions
|
|
95
104
|
)
|
|
96
|
-
|
|
97
|
-
attribute
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
case bytes():
|
|
106
|
+
converted_attr = attribute
|
|
107
|
+
case None:
|
|
108
|
+
converted_attr = None
|
|
109
|
+
case _:
|
|
110
|
+
raise TypeError("The provided attributes are not type bytes or dict!")
|
|
101
111
|
|
|
102
112
|
if text is not None:
|
|
103
113
|
message_text = LMS_MessageText(text, self._tag_config)
|
|
104
114
|
else:
|
|
105
115
|
message_text = ""
|
|
106
116
|
|
|
117
|
+
# Insert the section magics in the correct position that when writing the sections will be properly recognized
|
|
118
|
+
if attribute is not None and "ATR1" not in self.section_list:
|
|
119
|
+
self.section_list.insert(2, "ATR1")
|
|
120
|
+
|
|
121
|
+
if style_index is not None and "TSY1" not in self.section_list:
|
|
122
|
+
self.section_list.insert(3, "TSY1")
|
|
123
|
+
|
|
107
124
|
self._entries.append(
|
|
108
125
|
MSBTEntry(
|
|
109
126
|
name,
|
|
110
127
|
message=message_text,
|
|
111
|
-
attribute=
|
|
128
|
+
attribute=converted_attr,
|
|
112
129
|
style_index=style_index,
|
|
113
130
|
)
|
|
114
131
|
)
|
|
@@ -46,24 +46,6 @@ def read_msbt_path(
|
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
def write_msbt_path(file_path: str, file: MSBT) -> None:
|
|
50
|
-
"""
|
|
51
|
-
Writes a MSBT file to a given file path.
|
|
52
|
-
|
|
53
|
-
:param file_path: the path to write the file to.
|
|
54
|
-
:param msbt: the MSBT file object.
|
|
55
|
-
|
|
56
|
-
## Usage
|
|
57
|
-
```
|
|
58
|
-
write_msbt_path("path/to/file.msbt", msbt)
|
|
59
|
-
...
|
|
60
|
-
```
|
|
61
|
-
"""
|
|
62
|
-
with open(file_path, "wb") as stream:
|
|
63
|
-
data = write_msbt(file)
|
|
64
|
-
stream.write(data)
|
|
65
|
-
|
|
66
|
-
|
|
67
49
|
def read_msbt(
|
|
68
50
|
stream: BinaryIO | bytes,
|
|
69
51
|
*,
|
|
@@ -112,7 +94,8 @@ def read_msbt(
|
|
|
112
94
|
case _:
|
|
113
95
|
file.unsupported_sections[magic] = reader.read_bytes(size)
|
|
114
96
|
|
|
115
|
-
file.
|
|
97
|
+
if not file.section_exists(magic):
|
|
98
|
+
file.section_list.append(magic)
|
|
116
99
|
|
|
117
100
|
for i, label in labels.items():
|
|
118
101
|
text = None if messages is None else messages[i]
|
|
@@ -125,6 +108,24 @@ def read_msbt(
|
|
|
125
108
|
return file
|
|
126
109
|
|
|
127
110
|
|
|
111
|
+
def write_msbt_path(file_path: str, file: MSBT) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Writes a MSBT file to a given file path.
|
|
114
|
+
|
|
115
|
+
:param file_path: the path to write the file to.
|
|
116
|
+
:param msbt: the MSBT file object.
|
|
117
|
+
|
|
118
|
+
## Usage
|
|
119
|
+
```
|
|
120
|
+
write_msbt_path("path/to/file.msbt", msbt)
|
|
121
|
+
...
|
|
122
|
+
```
|
|
123
|
+
"""
|
|
124
|
+
with open(file_path, "wb") as stream:
|
|
125
|
+
data = write_msbt(file)
|
|
126
|
+
stream.write(data)
|
|
127
|
+
|
|
128
|
+
|
|
128
129
|
def write_msbt(file: MSBT) -> bytes:
|
|
129
130
|
"""Writes a MSBT file and returns the data.
|
|
130
131
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import re
|
|
2
|
-
from typing import TypeGuard
|
|
2
|
+
from typing import TypeGuard
|
|
3
3
|
|
|
4
|
-
from lms.message.definitions.field.lms_field import
|
|
5
|
-
convert_string_to_type)
|
|
4
|
+
from lms.message.definitions.field.lms_field import LMS_FieldMap
|
|
6
5
|
from lms.message.tag.lms_tagexceptions import LMS_InvalidTagFormatError
|
|
7
6
|
from lms.titleconfig.definitions.tags import TagConfig, TagDefinition
|
|
8
7
|
|
|
@@ -12,16 +11,19 @@ type LMS_ControlTag = LMS_EncodedTag | LMS_DecodedTag
|
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
def is_tag(obj: object) -> TypeGuard[LMS_ControlTag]:
|
|
14
|
+
"""Typeguard for a tag."""
|
|
15
15
|
return isinstance(obj, (LMS_EncodedTag, LMS_DecodedTag))
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class LMS_EncodedTag:
|
|
19
|
-
"""
|
|
19
|
+
"""
|
|
20
|
+
A class that represents an encoded tag.
|
|
20
21
|
|
|
21
22
|
Example encoded tags:
|
|
22
23
|
- `[0:3 00-00-00-FF]`
|
|
23
24
|
- `[0:4]`
|
|
24
|
-
- `[1:0 01-00-00-CD]`
|
|
25
|
+
- `[1:0 01-00-00-CD]`
|
|
26
|
+
"""
|
|
25
27
|
|
|
26
28
|
TAG_FORMAT = re.compile(r"\[\s*(\/)?\s*(\d+)\s*:\s*(\d+)(?:[^\]]*)\]")
|
|
27
29
|
PARAMETER_FORMAT = re.compile(r"^\s*([0-9A-Fa-f]{2})(\s*-\s*[0-9A-Fa-f]{2})*\s*$")
|
|
@@ -118,14 +120,18 @@ class LMS_EncodedTag:
|
|
|
118
120
|
|
|
119
121
|
|
|
120
122
|
class LMS_DecodedTag:
|
|
121
|
-
"""
|
|
123
|
+
"""
|
|
124
|
+
A class that represents a decoded tag.
|
|
122
125
|
|
|
123
|
-
Example
|
|
126
|
+
Example decoded tags:
|
|
124
127
|
- `[System:Color r="0" g="255" b="255" a="255"]`
|
|
125
128
|
- `[System:Pagebreak]`
|
|
126
|
-
- `[Mii:Nickname buffer="1" type="Text" conversion="None"]`
|
|
129
|
+
- `[Mii:Nickname buffer="1" type="Text" conversion="None"]`
|
|
130
|
+
"""
|
|
127
131
|
|
|
128
|
-
TAG_FORMAT = re.compile(
|
|
132
|
+
TAG_FORMAT = re.compile(
|
|
133
|
+
r"\[\s*(/)?\s*([A-Za-z][\w]*)\s*:\s*([A-Za-z]+)(?:\s+[^\]]*)?\s*\]"
|
|
134
|
+
)
|
|
129
135
|
PARAMETER_FORMAT = re.compile(r'(\w+)="([^"]*)"')
|
|
130
136
|
|
|
131
137
|
def __init__(
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "PyLibMS"
|
|
7
|
-
version = "3.1.
|
|
7
|
+
version = "3.1.3"
|
|
8
8
|
requires-python = ">=3.12"
|
|
9
9
|
description = "Python library built for the libMessageStudio (LMS) proprietary file formats from Nintendo. Supports MSBT, MSBP, and MSBF."
|
|
10
10
|
readme = "README.md"
|
|
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-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Brain Age Concentration Training.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Super Mario 3D World + Bowsers Fury.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/The Legend of Zelda Echos of Wisdom.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|