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.

Files changed (71) hide show
  1. {pylibms-3.1.2 → pylibms-3.1.3}/PKG-INFO +1 -1
  2. {pylibms-3.1.2 → pylibms-3.1.3}/PyLibMS.egg-info/PKG-INFO +1 -1
  3. pylibms-3.1.3/lms/common/lms_fileinfo.py +11 -0
  4. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/definitions/lms_messagetext.py +4 -4
  5. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/msbt.py +35 -18
  6. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/msbtio.py +20 -19
  7. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/tag/lms_tag.py +15 -9
  8. {pylibms-3.1.2 → pylibms-3.1.3}/pyproject.toml +1 -1
  9. pylibms-3.1.2/lms/common/lms_fileinfo.py +0 -11
  10. {pylibms-3.1.2 → pylibms-3.1.3}/LICENSE +0 -0
  11. {pylibms-3.1.2 → pylibms-3.1.3}/MANIFEST.in +0 -0
  12. {pylibms-3.1.2 → pylibms-3.1.3}/PyLibMS.egg-info/SOURCES.txt +0 -0
  13. {pylibms-3.1.2 → pylibms-3.1.3}/PyLibMS.egg-info/dependency_links.txt +0 -0
  14. {pylibms-3.1.2 → pylibms-3.1.3}/PyLibMS.egg-info/requires.txt +0 -0
  15. {pylibms-3.1.2 → pylibms-3.1.3}/PyLibMS.egg-info/top_level.txt +0 -0
  16. {pylibms-3.1.2 → pylibms-3.1.3}/README.md +0 -0
  17. {pylibms-3.1.2 → pylibms-3.1.3}/lms/__init__.py +0 -0
  18. {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/__init__.py +0 -0
  19. {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/lms_datatype.py +0 -0
  20. {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/lms_exceptions.py +0 -0
  21. {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/stream/fileinfo.py +0 -0
  22. {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/stream/hashtable.py +0 -0
  23. {pylibms-3.1.2 → pylibms-3.1.3}/lms/common/stream/section.py +0 -0
  24. {pylibms-3.1.2 → pylibms-3.1.3}/lms/fileio/encoding.py +0 -0
  25. {pylibms-3.1.2 → pylibms-3.1.3}/lms/fileio/io.py +0 -0
  26. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/__init__.py +0 -0
  27. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/definitions/__init__.py +0 -0
  28. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/definitions/field/__init__.py +0 -0
  29. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/definitions/field/io.py +0 -0
  30. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/definitions/field/lms_field.py +0 -0
  31. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/msbtentry.py +0 -0
  32. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/section/__init__.py +0 -0
  33. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/section/atr1.py +0 -0
  34. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/section/tsy1.py +0 -0
  35. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/section/txt2.py +0 -0
  36. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/tag/__init__.py +0 -0
  37. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/tag/io/param_io.py +0 -0
  38. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/tag/io/tag_io.py +0 -0
  39. {pylibms-3.1.2 → pylibms-3.1.3}/lms/message/tag/lms_tagexceptions.py +0 -0
  40. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/__init__.py +0 -0
  41. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/definitions/__init__.py +0 -0
  42. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/definitions/attribute.py +0 -0
  43. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/definitions/color.py +0 -0
  44. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/definitions/style.py +0 -0
  45. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/definitions/tag.py +0 -0
  46. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/msbp.py +0 -0
  47. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/msbpread.py +0 -0
  48. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/ali2.py +0 -0
  49. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/ati2.py +0 -0
  50. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/clr1.py +0 -0
  51. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/string.py +0 -0
  52. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/syl3.py +0 -0
  53. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/tag2.py +0 -0
  54. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/tgg2.py +0 -0
  55. {pylibms-3.1.2 → pylibms-3.1.3}/lms/project/section/tgp2.py +0 -0
  56. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/__init__.py +0 -0
  57. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/config.py +0 -0
  58. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/definitions/__init__.py +0 -0
  59. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/definitions/attribute.py +0 -0
  60. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/definitions/tags.py +0 -0
  61. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/definitions/value.py +0 -0
  62. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Badge Arcade.yaml +0 -0
  63. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Brain Age Concentration Training.yaml +0 -0
  64. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Kirby Planet Robobot.yaml +0 -0
  65. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Super Mario 3D Land.yaml +0 -0
  66. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Super Mario 3D World + Bowsers Fury.yaml +0 -0
  67. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Super Mario Odyssey.yaml +0 -0
  68. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/The Legend of Zelda Echos of Wisdom.yaml +0 -0
  69. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/The Legend of Zelda a Link Between Worlds.yaml +0 -0
  70. {pylibms-3.1.2 → pylibms-3.1.3}/lms/titleconfig/presets/Tomodachi Life.yaml +0 -0
  71. {pylibms-3.1.2 → pylibms-3.1.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyLibMS
3
- Version: 3.1.2
3
+ Version: 3.1.3
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: Copyright 2025 AbdyyEee
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyLibMS
3
- Version: 3.1.2
3
+ Version: 3.1.3
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: Copyright 2025 AbdyyEee
@@ -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.match(LMS_DecodedTag.TAG_FORMAT, tag):
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.match(LMS_EncodedTag.TAG_FORMAT, tag):
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
- # List of unsupported sections mapped to their raw data
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
- if isinstance(attribute, dict):
92
- if self._attribute_config is None:
93
- raise ValueError(
94
- "The attribute config must have been provided when reading to add decoded attributes!"
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
- converted_attribute = LMS_FieldMap.from_dict(
97
- attribute, self._attribute_config.definitions
98
- )
99
- else:
100
- converted_attribute = attribute
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=converted_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.section_list.append(magic)
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, runtime_checkable
2
+ from typing import TypeGuard
3
3
 
4
- from lms.message.definitions.field.lms_field import (LMS_Field, LMS_FieldMap,
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
- """A class that represents an encoded tag.
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
- """A class that represents a decoded tag.
123
+ """
124
+ A class that represents a decoded tag.
122
125
 
123
- Example encoded tags:
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(r"\[\s*(/)?\s*(\w+)\s*:\s*(\w+)(?:\s+[^\]]*)?\s*\]")
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.2"
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"
@@ -1,11 +0,0 @@
1
- from dataclasses import dataclass
2
-
3
- from lms.fileio.encoding import FileEncoding
4
-
5
-
6
- @dataclass(frozen=True)
7
- class LMS_FileInfo:
8
- is_big_endian: bool
9
- encoding: FileEncoding
10
- version: int
11
- section_count: int
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