PyLibMS 2.1.2__tar.gz → 2.1.4__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 (69) hide show
  1. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Common/LMS_DataType.py +9 -4
  2. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Definitions/Field/LMS_Field.py +2 -4
  3. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Definitions/LMS_MessageText.py +28 -21
  4. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/MSBT.py +1 -1
  5. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/MSBTStream.py +1 -1
  6. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Section/TXT2.py +3 -3
  7. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Tag/LMS_Tag.py +2 -1
  8. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Tag/Stream.py +3 -3
  9. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Tag/System_Definitions.py +14 -1
  10. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Config.py +6 -15
  11. {pylibms-2.1.2 → pylibms-2.1.4}/PKG-INFO +1 -1
  12. {pylibms-2.1.2 → pylibms-2.1.4}/PyLibMS.egg-info/PKG-INFO +1 -1
  13. {pylibms-2.1.2 → pylibms-2.1.4}/pyproject.toml +1 -1
  14. {pylibms-2.1.2 → pylibms-2.1.4}/LICENSE +0 -0
  15. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Common/LMS_Exceptions.py +0 -0
  16. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Common/LMS_FileInfo.py +0 -0
  17. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Common/Stream/FileInfo.py +0 -0
  18. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Common/Stream/Hashtable.py +0 -0
  19. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Common/Stream/Section.py +0 -0
  20. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Common/__init__.py +0 -0
  21. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/FileIO/Encoding.py +0 -0
  22. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/FileIO/Stream.py +0 -0
  23. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Definitions/Field/LMS_FieldMap.py +0 -0
  24. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Definitions/Field/Stream.py +0 -0
  25. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Definitions/Field/__init__.py +0 -0
  26. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Definitions/__init__.py +0 -0
  27. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/MSBTEntry.py +0 -0
  28. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Section/ATR1.py +0 -0
  29. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Section/TSY1.py +0 -0
  30. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Section/__init__.py +0 -0
  31. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Tag/LMS_TagExceptions.py +0 -0
  32. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Tag/Tag_Formats.py +0 -0
  33. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/Tag/__init__.py +0 -0
  34. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Message/__init__.py +0 -0
  35. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Definitions/Attribute.py +0 -0
  36. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Definitions/Color.py +0 -0
  37. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Definitions/Style.py +0 -0
  38. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Definitions/Tag.py +0 -0
  39. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Definitions/__init__.py +0 -0
  40. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/MSBP.py +0 -0
  41. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/MSBPRead.py +0 -0
  42. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Section/ALI2.py +0 -0
  43. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Section/ATI2.py +0 -0
  44. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Section/CLR1.py +0 -0
  45. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Section/SYL3.py +0 -0
  46. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Section/String.py +0 -0
  47. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Section/TAG2.py +0 -0
  48. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Section/TGG2.py +0 -0
  49. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/Section/TGP2.py +0 -0
  50. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/Project/__init__.py +0 -0
  51. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Definitions/Attributes.py +0 -0
  52. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Definitions/Tags.py +0 -0
  53. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Definitions/Value.py +0 -0
  54. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Definitions/__init__.py +0 -0
  55. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Presets/Badge Arcade.yaml +0 -0
  56. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Presets/Brain Age Concentration Training.yaml +0 -0
  57. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Presets/Kirby Planet Robobot.yaml +0 -0
  58. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Presets/Super Mario 3D Land.yaml +0 -0
  59. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Presets/The Legend of Zelda a Link Between Worlds.yaml +0 -0
  60. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/Presets/Tomodachi Life.yaml +0 -0
  61. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/TitleConfig/__init__.py +0 -0
  62. {pylibms-2.1.2 → pylibms-2.1.4}/LMS/__init__.py +0 -0
  63. {pylibms-2.1.2 → pylibms-2.1.4}/MANIFEST.in +0 -0
  64. {pylibms-2.1.2 → pylibms-2.1.4}/PyLibMS.egg-info/SOURCES.txt +0 -0
  65. {pylibms-2.1.2 → pylibms-2.1.4}/PyLibMS.egg-info/dependency_links.txt +0 -0
  66. {pylibms-2.1.2 → pylibms-2.1.4}/PyLibMS.egg-info/requires.txt +0 -0
  67. {pylibms-2.1.2 → pylibms-2.1.4}/PyLibMS.egg-info/top_level.txt +0 -0
  68. {pylibms-2.1.2 → pylibms-2.1.4}/README.md +0 -0
  69. {pylibms-2.1.2 → pylibms-2.1.4}/setup.cfg +0 -0
@@ -3,7 +3,7 @@ from typing import Literal, Type
3
3
 
4
4
 
5
5
  class LMS_DataType(Enum):
6
-
6
+ """Enum that represents a datatype for a value entry in a MSBT/MSBP file."""
7
7
  UINT8 = 0
8
8
  UINT16 = 1
9
9
  UINT32 = 2
@@ -15,12 +15,17 @@ class LMS_DataType(Enum):
15
15
  FLOAT32 = 6
16
16
 
17
17
  # Unknown 16 bit type (value of 6) has yet to be documented
18
+ # Might be some sort of 2 byte integer, float, or may be an array.
19
+ # We wont ever know cause no game (yet that has been found) has utilized this type
20
+ # Thanks Nintendo.
18
21
  ...
19
22
 
20
23
  STRING = 8
21
24
  LIST = 9
22
-
23
- # Interface types
25
+
26
+ # These types are not offical, but allow for abstraction from the value of the actual type
27
+ # As an example, BOOL can be utilized for UInt8 values that act like a bool
28
+ # Byte types can also be used for when the type/value is unknown or if there is extra data in the tag.
24
29
  BOOL = "bool"
25
30
  BYTE = "byte"
26
31
 
@@ -59,7 +64,7 @@ class LMS_DataType(Enum):
59
64
 
60
65
  @property
61
66
  def stream_size(self) -> Literal[1, 2, 4]:
62
- """The size the datatype takes up in a strema."""
67
+ """The size the datatype takes up in a stream."""
63
68
  return {
64
69
  LMS_DataType.UINT8: 1,
65
70
  LMS_DataType.UINT16: 2,
@@ -1,4 +1,3 @@
1
- from dataclasses import dataclass, field
2
1
 
3
2
  from LMS.Common.LMS_DataType import LMS_DataType
4
3
  from LMS.TitleConfig.Definitions.Value import ValueDefinition
@@ -17,14 +16,13 @@ class LMS_Field:
17
16
  self._value = value
18
17
 
19
18
  def __repr__(self):
20
- typename = self._definition.datatype.name
21
- if typename == "LIST":
19
+ if self.datatype is LMS_DataType.LIST:
22
20
  if len(self.list_items) > 6:
23
21
  preview = self.list_items[:3] + ["..."]
24
22
  else:
25
23
  preview = self.list_items
26
24
  return f"LMS_Field(value={self._value}, options={preview})"
27
- return f"LMS_Field(value={self._value!r}, type={typename})"
25
+ return f"LMS_Field(value={self._value}, type={self.datatype.name})"
28
26
 
29
27
  @property
30
28
  def name(self) -> str:
@@ -2,7 +2,9 @@ import re
2
2
  from typing import overload
3
3
 
4
4
  from LMS.Message.Definitions.Field.LMS_Field import LMS_Field
5
+ from LMS.Message.Definitions.Field.LMS_FieldMap import LMS_FieldMap
5
6
  from LMS.Message.Tag.LMS_Tag import LMS_DecodedTag, LMS_EncodedTag, LMS_TagBase
7
+ from LMS.Message.Tag.System_Definitions import get_system_tag
6
8
  from LMS.Message.Tag.Tag_Formats import (DECODED_FORMAT, ENCODED_FORMAT,
7
9
  TAG_FORMAT)
8
10
  from LMS.TitleConfig.Config import TagConfig
@@ -44,27 +46,29 @@ class LMS_MessageText:
44
46
 
45
47
  @overload
46
48
  def append_encoded_tag(
47
- self, group: int, tag: int, parameters: list[str] | None = None
49
+ self, group: int, tag: int, *parameters: tuple[str]
48
50
  ) -> None: ...
49
51
 
50
52
  @overload
51
53
  def append_encoded_tag(
52
- self, group: str, tag: str, parameters: list[str] | None = None
54
+ self, group: str, tag: str, *parameters: tuple[str]
53
55
  ) -> None: ...
54
56
 
55
57
  def append_encoded_tag(
56
- self, group: int | str, tag: int | str, parameters: list[str] | None = None
58
+ self, group: int | str, tag: int | str, *parameters: tuple[str]
57
59
  ):
58
60
  """Appends an encoded tag to the current message.
59
61
 
60
62
  :param group: the group name or index.
61
63
  :param tag: the group tag or index:
62
64
  :param parameters: a list of hex strings.
65
+
66
+ message.append_encoded_tag(1, 2, "01", "00", "00", "CD")
63
67
  """
64
68
  if isinstance(group, int) and isinstance(tag, int):
65
69
  self._parts.append(LMS_EncodedTag(group, tag, parameters))
66
70
  return
67
-
71
+
68
72
  definition = self._config.get_definition_by_names(group, tag)
69
73
  self._parts.append(
70
74
  LMS_EncodedTag(
@@ -72,35 +76,38 @@ class LMS_MessageText:
72
76
  )
73
77
  )
74
78
 
75
- def append_decoded_tag(
76
- self,
77
- group_name: str,
78
- tag_name: str,
79
- parameters: dict[str, int | float | str | bytes | bool] | None = None,
80
- ) -> None:
79
+ def append_decoded_tag(self, group_name: str, tag_name: str, **parameters: LMS_FieldMap) -> None:
81
80
  """Appends an decoded tag to the current message.
82
81
 
83
82
  :param group_name: the group name.
84
83
  :param tag_name: the tag name.:
85
- :param parameters: a dictionary of parameters mapped to their value.
84
+ :param parameters: keyword arguments of parameters mapped to their value.
85
+
86
+ ## Usage
87
+ ```
88
+ message.append_decoded_tag("Mii", "Nickname", buffer=1, type="Voice", conversion="None")
89
+ ```
86
90
  """
87
91
 
88
- definition = self._config.get_definition_by_names(group_name, tag_name)
92
+ if group_name == "System":
93
+ definition = get_system_tag(tag_name)
94
+ else:
95
+ definition = self._config.get_definition_by_names(group_name, tag_name)
89
96
 
90
- # The provided dict must match the structure defined in the config in order for value conversion to work
97
+ # The provided kwargs must match the structure defined in the config in order for value conversion to work
91
98
  converted_params = {
92
99
  param_def.name: LMS_Field(parameters[param_def.name], param_def)
93
100
  for param_def in definition.parameters
94
101
  }
95
- self._parts.append(
96
- LMS_DecodedTag(
97
- definition.group_index,
98
- definition.tag_index,
99
- group_name,
100
- tag_name,
101
- converted_params,
102
- )
102
+
103
+ tag = LMS_DecodedTag(
104
+ definition.group_index,
105
+ definition.tag_index,
106
+ group_name,
107
+ tag_name,
108
+ converted_params,
103
109
  )
110
+ self._parts.append(tag)
104
111
 
105
112
  def append_tag_string(self, string: str) -> None:
106
113
  """Appends a tag to the current message given a string.
@@ -87,7 +87,7 @@ class MSBT:
87
87
  if self._tag_config is not None:
88
88
  message_text = LMS_MessageText(text, self._tag_config)
89
89
  else:
90
- message_text = text
90
+ message_text = LMS_MessageText(text)
91
91
  else:
92
92
  message_text = ""
93
93
 
@@ -59,7 +59,7 @@ def read_msbt(
59
59
  file.size_per_attribute = size_per_attribute
60
60
  file.attr_string_table = string_table
61
61
  case "TXT2":
62
- messages = read_txt2(reader, file_info.encoding, tag_config)
62
+ messages = read_txt2(reader, tag_config)
63
63
  case "TSY1":
64
64
  style_indexes = read_tsy1(reader, len(labels))
65
65
  case _:
@@ -6,9 +6,9 @@ from LMS.Message.Tag.Stream import read_tag, write_tag
6
6
  from LMS.TitleConfig.Definitions.Tags import TagConfig
7
7
 
8
8
 
9
- def read_txt2(
10
- reader: FileReader, encoding: FileEncoding, config: TagConfig
11
- ) -> list[LMS_MessageText]:
9
+ def read_txt2(reader: FileReader, config: TagConfig) -> list[LMS_MessageText]:
10
+ encoding = reader.encoding
11
+
12
12
  text_list = []
13
13
  count = reader.read_uint32()
14
14
 
@@ -10,6 +10,7 @@ from LMS.TitleConfig.Definitions.Tags import TagConfig
10
10
 
11
11
 
12
12
  class LMS_TagBase(ABC):
13
+ """Base class for encoded and decoded tags."""
13
14
  def __init__(
14
15
  self,
15
16
  group_index: int,
@@ -71,7 +72,7 @@ class LMS_EncodedTag(LMS_TagBase):
71
72
  self,
72
73
  group_index: int,
73
74
  tag_index: int,
74
- parameters: list[str] = None,
75
+ parameters: list[str] | tuple[str] = None,
75
76
  group_name: str = None,
76
77
  tag_name: str = None,
77
78
  ):
@@ -6,7 +6,7 @@ from LMS.Message.Definitions.Field.Stream import read_field, write_field
6
6
  from LMS.Message.Tag.LMS_Tag import LMS_DecodedTag, LMS_EncodedTag, LMS_TagBase
7
7
  from LMS.Message.Tag.LMS_TagExceptions import (LMS_TagReadingError,
8
8
  LMS_TagWritingException)
9
- from LMS.Message.Tag.System_Definitions import SYSTEM_GROUP
9
+ from LMS.Message.Tag.System_Definitions import get_system_tag
10
10
  from LMS.TitleConfig.Definitions.Tags import TagConfig, TagDefinition
11
11
 
12
12
 
@@ -29,12 +29,12 @@ def read_tag(
29
29
  return LMS_EncodedTag(group_index, tag_index, parameters)
30
30
 
31
31
  if group_index == 0:
32
- definition = SYSTEM_GROUP[tag_index]
32
+ definition = get_system_tag(tag_index)
33
33
  else:
34
34
  definition = config.get_definition_by_indexes(group_index, tag_index)
35
35
 
36
36
  # If the parameters were omitted from the definition but the tag still has defined parameters, add the decoded
37
- # names but read the tag as encoded. This is to account for encoded tags that group have tag names attatched.
37
+ # names but read the tag as encoded. This is to account for encoded tags that have group and tag names defined.
38
38
  # i.e [Group:Tag 00-00-00-FF]
39
39
  if definition.parameters is None and param_size > 0:
40
40
  parameters = _read_encoded_parameters(reader, param_size)
@@ -2,6 +2,11 @@ from LMS.Common.LMS_DataType import LMS_DataType
2
2
  from LMS.TitleConfig.Definitions.Tags import TagDefinition
3
3
  from LMS.TitleConfig.Definitions.Value import ValueDefinition
4
4
 
5
+
6
+ def get_system_tag(group: int | str) -> TagDefinition:
7
+ return SYSTEM_INT_MAP[group] if isinstance(group, int) else SYSTEM_STR_MAP[group]
8
+
9
+
5
10
  RUBY_TAG = TagDefinition(
6
11
  group_name="System",
7
12
  group_index=0,
@@ -74,10 +79,18 @@ PAGEBREAK_TAG = TagDefinition(
74
79
  description="Displays text in the same label on different pages.",
75
80
  )
76
81
 
77
- SYSTEM_GROUP = {
82
+ SYSTEM_INT_MAP = {
78
83
  0: RUBY_TAG,
79
84
  1: FONT_TAG,
80
85
  2: SIZE_TAG,
81
86
  3: COLOR_TAG,
82
87
  4: PAGEBREAK_TAG,
83
88
  }
89
+
90
+ SYSTEM_STR_MAP = {
91
+ "Ruby": RUBY_TAG,
92
+ "Font": FONT_TAG,
93
+ "Size": SIZE_TAG,
94
+ "Color": COLOR_TAG,
95
+ "Pagebreak": PAGEBREAK_TAG,
96
+ }
@@ -23,9 +23,7 @@ class TitleConfig:
23
23
  if file.name.endswith(".yaml")
24
24
  ]
25
25
 
26
- def __init__(
27
- self, attribute_configs: dict[str, AttributeConfig], tag_config: TagConfig
28
- ):
26
+ def __init__(self, attribute_configs: dict[str, AttributeConfig], tag_config: TagConfig):
29
27
  self._attribute_configs = attribute_configs
30
28
  self._tag_config = tag_config
31
29
 
@@ -61,21 +59,16 @@ class TitleConfig:
61
59
  """Loads the config of a specified game.
62
60
 
63
61
  :param content: the config content, as a string or loaded as a dictionary."""
62
+ parsed_content = content
63
+
64
64
  if isinstance(content, str):
65
65
  parsed_content = yaml.safe_load(content)
66
- else:
67
- parsed_content = content
68
66
 
69
67
  # Load the attribute definitions
70
68
  attribute_configs = {}
71
69
  for config in parsed_content[ATTR_KEY]:
72
- definitions = [
73
- ValueDefinition.from_dict(value_def)
74
- for value_def in config["definitions"]
75
- ]
76
- attribute_configs[config["name"]] = AttributeConfig(
77
- config["name"], config["description"], definitions
78
- )
70
+ definitions = [ValueDefinition.from_dict(value_def) for value_def in config["definitions"]]
71
+ attribute_configs[config["name"]] = AttributeConfig(config["name"], config["description"], definitions)
79
72
 
80
73
  # Load the tag definitions
81
74
  tag_definitions = []
@@ -107,9 +100,7 @@ class TitleConfig:
107
100
  config = {}
108
101
 
109
102
  config[TAG_KEY] = {
110
- "groups": {
111
- i: group.name for i, group in enumerate(project.tag_groups, start=1)
112
- },
103
+ "groups": {i: group.name for i, group in enumerate(project.tag_groups, start=1)},
113
104
  "tags": [],
114
105
  }
115
106
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyLibMS
3
- Version: 2.1.2
3
+ Version: 2.1.4
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: 2.1.2
3
+ Version: 2.1.4
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "PyLibMS"
7
- version = "2.1.2"
7
+ version = "2.1.4"
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