PyLibMS 3.3__tar.gz → 3.3.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.
Files changed (71) hide show
  1. {pylibms-3.3 → pylibms-3.3.1}/PKG-INFO +1 -1
  2. {pylibms-3.3 → pylibms-3.3.1}/PyLibMS.egg-info/PKG-INFO +1 -1
  3. {pylibms-3.3 → pylibms-3.3.1}/lms/common/lms_datatype.py +14 -15
  4. {pylibms-3.3 → pylibms-3.3.1}/lms/common/stream/fileinfo.py +1 -1
  5. {pylibms-3.3 → pylibms-3.3.1}/lms/message/definitions/lms_messagetext.py +16 -16
  6. {pylibms-3.3 → pylibms-3.3.1}/lms/message/msbt.py +32 -13
  7. {pylibms-3.3 → pylibms-3.3.1}/lms/message/msbtio.py +4 -1
  8. {pylibms-3.3 → pylibms-3.3.1}/lms/message/section/txt2.py +6 -7
  9. {pylibms-3.3 → pylibms-3.3.1}/lms/project/msbp.py +11 -4
  10. {pylibms-3.3 → pylibms-3.3.1}/pyproject.toml +1 -1
  11. {pylibms-3.3 → pylibms-3.3.1}/LICENSE +0 -0
  12. {pylibms-3.3 → pylibms-3.3.1}/MANIFEST.in +0 -0
  13. {pylibms-3.3 → pylibms-3.3.1}/PyLibMS.egg-info/SOURCES.txt +0 -0
  14. {pylibms-3.3 → pylibms-3.3.1}/PyLibMS.egg-info/dependency_links.txt +0 -0
  15. {pylibms-3.3 → pylibms-3.3.1}/PyLibMS.egg-info/requires.txt +0 -0
  16. {pylibms-3.3 → pylibms-3.3.1}/PyLibMS.egg-info/top_level.txt +0 -0
  17. {pylibms-3.3 → pylibms-3.3.1}/README.md +0 -0
  18. {pylibms-3.3 → pylibms-3.3.1}/lms/__init__.py +0 -0
  19. {pylibms-3.3 → pylibms-3.3.1}/lms/common/__init__.py +0 -0
  20. {pylibms-3.3 → pylibms-3.3.1}/lms/common/lms_exceptions.py +0 -0
  21. {pylibms-3.3 → pylibms-3.3.1}/lms/common/lms_fileinfo.py +0 -0
  22. {pylibms-3.3 → pylibms-3.3.1}/lms/common/stream/hashtable.py +0 -0
  23. {pylibms-3.3 → pylibms-3.3.1}/lms/common/stream/section.py +0 -0
  24. {pylibms-3.3 → pylibms-3.3.1}/lms/fileio/encoding.py +0 -0
  25. {pylibms-3.3 → pylibms-3.3.1}/lms/fileio/io.py +0 -0
  26. {pylibms-3.3 → pylibms-3.3.1}/lms/message/__init__.py +0 -0
  27. {pylibms-3.3 → pylibms-3.3.1}/lms/message/definitions/__init__.py +0 -0
  28. {pylibms-3.3 → pylibms-3.3.1}/lms/message/definitions/field/__init__.py +0 -0
  29. {pylibms-3.3 → pylibms-3.3.1}/lms/message/definitions/field/io.py +0 -0
  30. {pylibms-3.3 → pylibms-3.3.1}/lms/message/definitions/field/lms_field.py +0 -0
  31. {pylibms-3.3 → pylibms-3.3.1}/lms/message/msbtentry.py +0 -0
  32. {pylibms-3.3 → pylibms-3.3.1}/lms/message/section/__init__.py +0 -0
  33. {pylibms-3.3 → pylibms-3.3.1}/lms/message/section/atr1.py +0 -0
  34. {pylibms-3.3 → pylibms-3.3.1}/lms/message/section/nli1.py +0 -0
  35. {pylibms-3.3 → pylibms-3.3.1}/lms/message/section/tsy1.py +0 -0
  36. {pylibms-3.3 → pylibms-3.3.1}/lms/message/tag/__init__.py +0 -0
  37. {pylibms-3.3 → pylibms-3.3.1}/lms/message/tag/io/param_io.py +0 -0
  38. {pylibms-3.3 → pylibms-3.3.1}/lms/message/tag/io/tag_io.py +0 -0
  39. {pylibms-3.3 → pylibms-3.3.1}/lms/message/tag/lms_tag.py +0 -0
  40. {pylibms-3.3 → pylibms-3.3.1}/lms/message/tag/lms_tagexceptions.py +0 -0
  41. {pylibms-3.3 → pylibms-3.3.1}/lms/project/__init__.py +0 -0
  42. {pylibms-3.3 → pylibms-3.3.1}/lms/project/definitions/__init__.py +0 -0
  43. {pylibms-3.3 → pylibms-3.3.1}/lms/project/definitions/attribute.py +0 -0
  44. {pylibms-3.3 → pylibms-3.3.1}/lms/project/definitions/color.py +0 -0
  45. {pylibms-3.3 → pylibms-3.3.1}/lms/project/definitions/style.py +0 -0
  46. {pylibms-3.3 → pylibms-3.3.1}/lms/project/definitions/tag.py +0 -0
  47. {pylibms-3.3 → pylibms-3.3.1}/lms/project/msbpread.py +0 -0
  48. {pylibms-3.3 → pylibms-3.3.1}/lms/project/section/ali2.py +0 -0
  49. {pylibms-3.3 → pylibms-3.3.1}/lms/project/section/ati2.py +0 -0
  50. {pylibms-3.3 → pylibms-3.3.1}/lms/project/section/clr1.py +0 -0
  51. {pylibms-3.3 → pylibms-3.3.1}/lms/project/section/string.py +0 -0
  52. {pylibms-3.3 → pylibms-3.3.1}/lms/project/section/syl3.py +0 -0
  53. {pylibms-3.3 → pylibms-3.3.1}/lms/project/section/tag2.py +0 -0
  54. {pylibms-3.3 → pylibms-3.3.1}/lms/project/section/tgg2.py +0 -0
  55. {pylibms-3.3 → pylibms-3.3.1}/lms/project/section/tgp2.py +0 -0
  56. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/__init__.py +0 -0
  57. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/config.py +0 -0
  58. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/definitions/__init__.py +0 -0
  59. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/definitions/attribute.py +0 -0
  60. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/definitions/tags.py +0 -0
  61. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/definitions/value.py +0 -0
  62. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/presets/Badge Arcade.yaml +0 -0
  63. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/presets/Brain Age Concentration Training.yaml +0 -0
  64. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/presets/Kirby Planet Robobot.yaml +0 -0
  65. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/presets/Super Mario 3D Land.yaml +0 -0
  66. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/presets/Super Mario 3D World + Bowsers Fury.yaml +0 -0
  67. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/presets/Super Mario Odyssey.yaml +0 -0
  68. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/presets/The Legend of Zelda Echos of Wisdom.yaml +0 -0
  69. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/presets/The Legend of Zelda a Link Between Worlds.yaml +0 -0
  70. {pylibms-3.3 → pylibms-3.3.1}/lms/titleconfig/presets/Tomodachi Life.yaml +0 -0
  71. {pylibms-3.3 → pylibms-3.3.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyLibMS
3
- Version: 3.3
3
+ Version: 3.3.1
4
4
  Summary: Python library built for the libMessageStudio (LMS) proprietary file formats from Nintendo. Supports MSBT, MSBP, and soon to be 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.3
3
+ Version: 3.3.1
4
4
  Summary: Python library built for the libMessageStudio (LMS) proprietary file formats from Nintendo. Supports MSBT, MSBP, and soon to be MSBF.
5
5
  Author: AbdyyEee
6
6
  License: Copyright 2025 AbdyyEee
@@ -3,6 +3,18 @@ from __future__ import annotations
3
3
  from enum import Enum
4
4
  from typing import Type, TypeGuard
5
5
 
6
+ ALIAS_MAP = {
7
+ "u8": "UINT8",
8
+ "u16": "UINT16",
9
+ "u32": "UINT32",
10
+ "i8": "INT8",
11
+ "i16": "INT16",
12
+ "i32": "INT32",
13
+ "f32": "FLOAT32",
14
+ "str": "STRING",
15
+ "bool": "BOOL",
16
+ "byte": "BYTES",
17
+ }
6
18
 
7
19
  def is_number_datatype(value: object, datatype: LMS_DataType) -> TypeGuard[int | float]:
8
20
  return datatype in (
@@ -77,7 +89,7 @@ class LMS_DataType(Enum):
77
89
  raise TypeError(f"Signed is not a valid property for '{self.to_string()}'!")
78
90
 
79
91
  @property
80
- def builtin_type(self) -> Type[int | str | float | bool | bytes]:
92
+ def builtin_type(self) -> Type[int | float | str | bool | bytes]:
81
93
  """The enum as the builtin python type."""
82
94
  return {
83
95
  LMS_DataType.UINT8: int,
@@ -116,20 +128,7 @@ class LMS_DataType(Enum):
116
128
  if member in cls.__members__:
117
129
  return cls[member]
118
130
 
119
- alias_map = {
120
- "u8": "UINT8",
121
- "u16": "UINT16",
122
- "u32": "UINT32",
123
- "i8": "INT8",
124
- "i16": "INT16",
125
- "i32": "INT32",
126
- "f32": "FLOAT32",
127
- "str": "STRING",
128
- "bool": "BOOL",
129
- "byte": "BYTES",
130
- }
131
-
132
- alias_member = alias_map.get(string.lower())
131
+ alias_member = ALIAS_MAP.get(string.lower())
133
132
  if alias_member is not None:
134
133
  return cls[alias_member]
135
134
  else:
@@ -33,7 +33,7 @@ def read_file_info(reader: FileReader, expected_magic: str) -> LMS_FileInfo:
33
33
 
34
34
  reader.seek(0, 2)
35
35
  if file_size != reader.tell():
36
- raise lms_exceptions.LMS_MisalignedSizeError(f"Filesize is misaligned!")
36
+ raise lms_exceptions.LMS_MisalignedSizeError(f"File size is misaligned!")
37
37
 
38
38
  reader.seek(DATA_START)
39
39
 
@@ -1,6 +1,6 @@
1
1
  import re
2
2
 
3
- from lms.message.definitions.field.lms_field import LMS_FieldMap
3
+ from lms.message.definitions.field.lms_field import LMS_FieldMap, FieldValue
4
4
  from lms.message.tag.lms_tag import (LMS_ControlTag, LMS_DecodedTag,
5
5
  LMS_EncodedTag, is_tag)
6
6
  from lms.message.tag.lms_tagexceptions import LMS_TagForbiddenParametersError
@@ -20,18 +20,18 @@ class LMS_MessageText:
20
20
  self._tag_config = tag_config
21
21
 
22
22
  if isinstance(message, str):
23
- self._set_parts(message)
23
+ self._set_segments(message)
24
24
  else:
25
- self._parts = message
25
+ self._segments = message
26
26
 
27
27
  def __iter__(self):
28
- return iter(self._parts)
28
+ return iter(self._segments)
29
29
 
30
30
  @property
31
31
  def text(self) -> str:
32
32
  """The raw text of the message."""
33
33
  result = []
34
- for part in self._parts:
34
+ for part in self._segments:
35
35
  if is_tag(part):
36
36
  result.append(part.to_text())
37
37
  else:
@@ -40,19 +40,19 @@ class LMS_MessageText:
40
40
 
41
41
  @text.setter
42
42
  def text(self, string: str) -> None:
43
- self._set_parts(string)
43
+ self._set_segments(string)
44
44
 
45
45
  @property
46
46
  def tags(self) -> list[LMS_ControlTag]:
47
47
  """The list of control tags in the message."""
48
- return [part for part in self._parts if is_tag(part)]
48
+ return [part for part in self._segments if is_tag(part)]
49
49
 
50
50
  @property
51
51
  def tag_positions(self) -> dict[LMS_ControlTag, tuple[int, int]]:
52
52
  """Dict of tag objects to their start and end positions in text."""
53
53
  positions = {}
54
54
  pos = 0
55
- for part in self._parts:
55
+ for part in self._segments:
56
56
  text_len = len(part)
57
57
  if is_tag(part):
58
58
  positions[part] = (pos, pos + text_len)
@@ -86,7 +86,7 @@ class LMS_MessageText:
86
86
  group_id, tag_index, None if not parameters else list(parameters)
87
87
  )
88
88
 
89
- self._parts.append(tag)
89
+ self._segments.append(tag)
90
90
  return tag
91
91
 
92
92
  def append_decoded_tag(
@@ -94,7 +94,7 @@ class LMS_MessageText:
94
94
  group_name: str,
95
95
  tag_name: str,
96
96
  is_closing: bool = False,
97
- **parameters: int | str | float | bool | bytes,
97
+ **parameters: FieldValue
98
98
  ) -> LMS_DecodedTag:
99
99
  """
100
100
  Appends a decoded tag to the current message and returns that tag.
@@ -120,7 +120,7 @@ class LMS_MessageText:
120
120
  raise LMS_TagForbiddenParametersError("There may not be parameters for closing tags!")
121
121
 
122
122
  tag = LMS_DecodedTag(definition, is_closing=True)
123
- self._parts.append(tag)
123
+ self._segments.append(tag)
124
124
  return tag
125
125
 
126
126
  if parameters:
@@ -129,7 +129,7 @@ class LMS_MessageText:
129
129
  else:
130
130
  tag = LMS_DecodedTag(definition)
131
131
 
132
- self._parts.append(tag)
132
+ self._segments.append(tag)
133
133
  return tag
134
134
 
135
135
  def append_tag_string(self, tag: str) -> LMS_ControlTag:
@@ -158,13 +158,13 @@ class LMS_MessageText:
158
158
  else:
159
159
  raise ValueError(f"Invalid format in tag '{tag}'.")
160
160
 
161
- self._parts.append(tag_obj)
161
+ self._segments.append(tag_obj)
162
162
  return tag_obj
163
163
 
164
- def _set_parts(self, text: str) -> None:
165
- self._parts = []
164
+ def _set_segments(self, text: str) -> None:
165
+ self._segments = []
166
166
  for part in self.TAG_FORMAT.split(text):
167
167
  if bool(re.match(self.TAG_FORMAT, part)):
168
168
  self.append_tag_string(part)
169
169
  else:
170
- self._parts.append(part)
170
+ self._segments.append(part)
@@ -9,20 +9,28 @@ class MSBT:
9
9
  """
10
10
  A class that represents a MSBT file.
11
11
 
12
+ ======
13
+ Usages
14
+ ======
15
+ https://github.com/AbdyyEee/PylibMS/wiki/MSBT
16
+
17
+ =========
18
+ File Info
19
+ =========
12
20
  https://nintendo-formats.com/libs/lms/msbt.html
13
21
  """
14
-
15
22
  MAGIC = "MsgStdBn"
16
23
 
17
24
  DEFAULT_SLOT_COUNT = 101
18
25
 
19
26
  ATR1_INDEX = 1
20
27
  TXT2_INDEX = 2
21
- TSY_INDEX = 3
28
+ TSY1_INDEX = 3
22
29
 
23
30
  def __init__(
24
31
  self,
25
32
  info: LMS_FileInfo | None = None,
33
+ uses_nli1: bool = False,
26
34
  section_list: list[str] | None = None,
27
35
  unsupported_section_map: dict[str, bytes] | None = None,
28
36
  attribute_config: AttributeConfig | None = None,
@@ -35,21 +43,26 @@ class MSBT:
35
43
 
36
44
  self.size_per_attribute = 0
37
45
 
38
- self.slot_count = MSBT.DEFAULT_SLOT_COUNT
46
+ self._slot_count = MSBT.DEFAULT_SLOT_COUNT
39
47
 
40
48
  self.uses_encoded_attributes = True
41
49
  self.attr_string_table: bytes | None = None
42
50
 
43
51
  self._unsupported_section_map = unsupported_section_map or {}
44
52
 
53
+ # Some MSBTs store labels with NLI1 over LBL1
54
+ # This flag creates a MSBT instance with NLI1 preferred over LBL1
55
+ self.uses_nli1 = uses_nli1
56
+
45
57
  # Store the section list so that the order of any and all sections is preserved when writing
46
- self._section_list: list[str] = section_list or ["LBL1"]
58
+ self._section_list: list[str] = section_list or ["LBL1" if not self.uses_nli1 else "NLI1"]
47
59
 
48
60
  self._attribute_config = attribute_config
49
61
  self._tag_config = tag_config
50
62
 
51
63
  @classmethod
52
64
  def new(cls,
65
+ uses_nli1: bool = False,
53
66
  attribute_config: AttributeConfig | None = None,
54
67
  tag_config: TagConfig | None = None,
55
68
  is_big_endian: bool = False,
@@ -58,6 +71,7 @@ class MSBT:
58
71
  section_count: int = 2):
59
72
  """Create a new MSBT instance.
60
73
 
74
+ :param uses_nli1: flag to determine if to use nli1 section for labels.
61
75
  :param attribute_config: the attribute config object
62
76
  :param tag_config: the tag config object
63
77
  :param is_big_endian: if the file is big endian.
@@ -65,13 +79,13 @@ class MSBT:
65
79
  :param version: the file version.
66
80
  :param section_count: the number of sections.
67
81
 
68
- ========
69
- Examples
70
- ========
82
+ ======
83
+ Usages
84
+ ======
71
85
  See https://github.com/AbdyyEee/PylibMS/wiki/MSBT#creating-a-msbt
72
86
  """
73
- return MSBT(info=LMS_FileInfo(is_big_endian, encoding, version, section_count),
74
- attribute_config=attribute_config, tag_config=tag_config)
87
+ return MSBT(LMS_FileInfo(is_big_endian, encoding, version, section_count),
88
+ uses_nli1=uses_nli1, attribute_config=attribute_config, tag_config=tag_config)
75
89
 
76
90
  def __len__(self) -> int:
77
91
  return len(self._entries)
@@ -89,6 +103,11 @@ class MSBT:
89
103
  """Tuple of all the MSBT entries."""
90
104
  return tuple(self._entries)
91
105
 
106
+ @property
107
+ def slot_count(self) -> int:
108
+ """The slot count for the MSBT instance."""
109
+ return self._slot_count
110
+
92
111
  @property
93
112
  def section_list(self) -> tuple[str, ...]:
94
113
  """The list of sections with order preserved."""
@@ -100,12 +119,12 @@ class MSBT:
100
119
  return tuple(self._unsupported_section_map.keys())
101
120
 
102
121
  @property
103
- def has_attributes(self) -> bool:
122
+ def contains_attributes(self) -> bool:
104
123
  """If the MSBT contains attributes."""
105
124
  return self.section_exists("ATR1")
106
125
 
107
126
  @property
108
- def has_styles(self) -> bool:
127
+ def contains_styles(self) -> bool:
109
128
  """If the MSBT contains style indexes."""
110
129
  return self.section_exists("TSY1")
111
130
 
@@ -141,7 +160,7 @@ class MSBT:
141
160
  raise KeyError(f"The label '{entry.name}' already exists!")
142
161
 
143
162
  # The implementation of ensuring section orders are maintained are done by constant indexes.
144
- # In most MSBT files, it goes as LBL1 -> TXT2 -> ATR1 -> TSY1.
163
+ # In most MSBT files, it goes as LBL1/NLI1 -> TXT2 -> ATR1 -> TSY1.
145
164
  # If add_entry is utilized on a new MSBT instance, then these indexes ensure the order is maintained.
146
165
  # While technically, undocumented sections (i.e. ATO1) can prefix TXT2 and other sections,
147
166
  # we do not need to account for that scenario as they aren't supported by the library
@@ -164,7 +183,7 @@ class MSBT:
164
183
  f"Entry '{entry.name}' can't be added with no style index when styles already exist!"
165
184
  )
166
185
  elif entry.style_index is not None:
167
- self._section_list.insert(self.TSY_INDEX, "TSY1")
186
+ self._section_list.insert(self.TSY1_INDEX, "TSY1")
168
187
  self._info.section_count += 1
169
188
 
170
189
  self._entries.append(entry)
@@ -79,12 +79,14 @@ def read_msbt(
79
79
  messages = atr1_data = style_indexes = None
80
80
 
81
81
  labels: dict[int, str] = {}
82
+ uses_nli1 = False
82
83
  for magic, size in read_section_data(reader, file_info.section_count):
83
84
  match magic:
84
85
  case "LBL1":
85
86
  labels, slot_count = read_labels(reader)
86
87
  case "NLI1":
87
88
  labels = read_nli1(reader)
89
+ uses_nli1 = True
88
90
  case "ATR1":
89
91
  atr1_data = read_atr1(reader, attribute_config, size)
90
92
  case "TXT2":
@@ -98,7 +100,8 @@ def read_msbt(
98
100
  section_list.append(magic)
99
101
 
100
102
  file = MSBT(
101
- file_info, section_list, unsupported_sections, attribute_config, tag_config
103
+ file_info, uses_nli1, section_list,
104
+ unsupported_sections, attribute_config, tag_config
102
105
  )
103
106
  file.slot_count = slot_count
104
107
 
@@ -19,24 +19,23 @@ def read_txt2(
19
19
  for offset in reader.read_offset_array(message_count):
20
20
  reader.seek(offset)
21
21
 
22
- text, parts = b"", []
22
+ text, message_segments = b"", []
23
23
  while (data := reader.read_bytes(encoding.width)) != encoding.terminator:
24
- is_opening_tag = data == tag_start
25
24
  is_closing_tag = data == tag_close
26
25
 
27
- if is_opening_tag or is_closing_tag:
28
- parts.append(text.decode(encoding_format))
26
+ if data == tag_start or is_closing_tag:
27
+ message_segments.append(text.decode(encoding_format))
29
28
  tag = read_tag(reader, config, is_closing_tag, suppress_tag_errors)
30
- parts.append(tag)
29
+ message_segments.append(tag)
31
30
  text = b""
32
31
  else:
33
32
  text += data
34
33
 
35
34
  # Add the remaining text in case there were no control tags
36
35
  if text:
37
- parts.append(text.decode(encoding_format))
36
+ message_segments.append(text.decode(encoding_format))
38
37
 
39
- message = LMS_MessageText(parts, config)
38
+ message = LMS_MessageText(message_segments, config)
40
39
  messages.append(message)
41
40
 
42
41
  return messages
@@ -9,7 +9,15 @@ class MSBP:
9
9
  """
10
10
  A class that represents a MSBP file.
11
11
 
12
- https://nintendo-formats.com/libs/lms/msbp.html.
12
+ ======
13
+ Usages
14
+ ======
15
+ https://github.com/AbdyyEee/PylibMS/wiki/MSBP
16
+
17
+ =========
18
+ File Info
19
+ =========
20
+ https://nintendo-formats.com/libs/lms/msbp.html
13
21
  """
14
22
 
15
23
  MAGIC = "MsgPrjBn"
@@ -23,10 +31,9 @@ class MSBP:
23
31
  styles: list[LMS_Style] | None,
24
32
  source_files: list[str] | None,
25
33
  ):
26
- self.name: str = ""
34
+ self.name = ""
27
35
 
28
36
  self._info = info
29
-
30
37
  self._colors = colors
31
38
  self._attribute_definitions = config
32
39
  self._tag_groups = tag_groups
@@ -35,7 +42,7 @@ class MSBP:
35
42
 
36
43
  @property
37
44
  def info(self) -> LMS_FileInfo:
38
- """The stream info of the MSBP."""
45
+ """The file info for the MSBP instance."""
39
46
  return self._info
40
47
 
41
48
  @property
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "PyLibMS"
7
- version = "3.3"
7
+ version = "3.3.1"
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 soon to be 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