PyLibMS 2.0.0__py3-none-any.whl
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.
- LMS/Common/LMS_Exceptions.py +16 -0
- LMS/Common/LMS_FileInfo.py +11 -0
- LMS/Common/Stream/FileInfo.py +64 -0
- LMS/Common/Stream/Hashtable.py +87 -0
- LMS/Common/Stream/Section.py +60 -0
- LMS/Common/__init__.py +0 -0
- LMS/Config/Definitions/Attributes.py +14 -0
- LMS/Config/Definitions/Tags.py +52 -0
- LMS/Config/Definitions/Value.py +11 -0
- LMS/Config/Definitions/__init__.py +0 -0
- LMS/Config/TitleConfig.py +208 -0
- LMS/Config/__init__.py +0 -0
- LMS/Field/LMS_DataType.py +83 -0
- LMS/Field/LMS_Field.py +123 -0
- LMS/Field/Stream.py +56 -0
- LMS/Field/__init__.py +0 -0
- LMS/FileIO/Encoding.py +42 -0
- LMS/FileIO/Stream.py +181 -0
- LMS/Message/Definitions/LMS_FieldMap.py +5 -0
- LMS/Message/Definitions/LMS_MessageText.py +124 -0
- LMS/Message/Definitions/__init__.py +0 -0
- LMS/Message/MSBT.py +63 -0
- LMS/Message/MSBTEntry.py +33 -0
- LMS/Message/MSBTStream.py +138 -0
- LMS/Message/Section/ATR1.py +101 -0
- LMS/Message/Section/TSY1.py +13 -0
- LMS/Message/Section/TXT2.py +79 -0
- LMS/Message/Section/__init__.py +0 -0
- LMS/Message/Tag/LMS_Tag.py +169 -0
- LMS/Message/Tag/LMS_TagExceptions.py +7 -0
- LMS/Message/Tag/Stream.py +149 -0
- LMS/Message/Tag/System.yaml +47 -0
- LMS/Message/Tag/Tag_Formats.py +10 -0
- LMS/Message/Tag/__init__.py +0 -0
- LMS/Message/__init__.py +0 -0
- LMS/Project/Definitions/Attribute.py +27 -0
- LMS/Project/Definitions/Color.py +10 -0
- LMS/Project/Definitions/Style.py +14 -0
- LMS/Project/Definitions/Tag.py +58 -0
- LMS/Project/Definitions/__init__.py +0 -0
- LMS/Project/MSBP.py +64 -0
- LMS/Project/MSBPRead.py +101 -0
- LMS/Project/Section/ALI2.py +19 -0
- LMS/Project/Section/ATI2.py +17 -0
- LMS/Project/Section/CLR1.py +15 -0
- LMS/Project/Section/SYL3.py +17 -0
- LMS/Project/Section/String.py +18 -0
- LMS/Project/Section/TAG2.py +19 -0
- LMS/Project/Section/TGG2.py +20 -0
- LMS/Project/Section/TGP2.py +26 -0
- LMS/Project/__init__.py +0 -0
- LMS/__init__.py +0 -0
- pylibms-2.0.0.dist-info/METADATA +40 -0
- pylibms-2.0.0.dist-info/RECORD +57 -0
- pylibms-2.0.0.dist-info/WHEEL +5 -0
- pylibms-2.0.0.dist-info/licenses/LICENSE +7 -0
- pylibms-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
- name: Ruby
|
|
2
|
+
group_index: 0
|
|
3
|
+
tag_index: 0
|
|
4
|
+
description: 'Displays a RUBY character.'
|
|
5
|
+
parameters:
|
|
6
|
+
- name: rt
|
|
7
|
+
description: 'The ruby character.'
|
|
8
|
+
datatype: string
|
|
9
|
+
- name: Font
|
|
10
|
+
group_index: 0
|
|
11
|
+
tag_index: 1
|
|
12
|
+
description: 'Sets the font file of text.'
|
|
13
|
+
parameters:
|
|
14
|
+
- name: face
|
|
15
|
+
description: 'The font face name.'
|
|
16
|
+
datatype: string
|
|
17
|
+
- name: Size
|
|
18
|
+
group_index: 0
|
|
19
|
+
tag_index: 2
|
|
20
|
+
description: 'Alters the size of text.'
|
|
21
|
+
parameters:
|
|
22
|
+
- name: percent
|
|
23
|
+
description: 'The new size as a percent.'
|
|
24
|
+
datatype: uint16
|
|
25
|
+
- name: Color
|
|
26
|
+
group_index: 0
|
|
27
|
+
tag_index: 3
|
|
28
|
+
description: 'Sets the color of text in a message in RGBA form.'
|
|
29
|
+
parameters:
|
|
30
|
+
- name: r
|
|
31
|
+
description: 'The red color value.'
|
|
32
|
+
datatype: uint8
|
|
33
|
+
- name: g
|
|
34
|
+
description: 'The green color value.'
|
|
35
|
+
datatype: uint8
|
|
36
|
+
- name: b
|
|
37
|
+
description: 'The blue color value.'
|
|
38
|
+
datatype: uint8
|
|
39
|
+
- name: a
|
|
40
|
+
description: 'The alpha transprency value.'
|
|
41
|
+
datatype: uint8
|
|
42
|
+
- name: PageBreak
|
|
43
|
+
group_index: 0
|
|
44
|
+
tag_index: 4
|
|
45
|
+
description: 'Displays text in the same label on different pages.'
|
|
46
|
+
parameters: []
|
|
47
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Regex formats for control tags
|
|
2
|
+
|
|
3
|
+
# Regular format for any tag
|
|
4
|
+
TAG_FORMAT = r"^\[[^\]]+\]$"
|
|
5
|
+
|
|
6
|
+
DECODED_FORMAT = r"^\[(\w+):(\w+)(.*?)\]$"
|
|
7
|
+
ENCODED_FORMAT = r"^\[([A-Za-z0-9]+):([A-Za-z0-9]+)\s?([0-9A-Fa-f\-]*)?\]$"
|
|
8
|
+
|
|
9
|
+
# Key value pair regex for decoded tags
|
|
10
|
+
PARAMETER_FORMAT = r'(\w+)="([^"]*)"'
|
|
File without changes
|
LMS/Message/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from LMS.Field.LMS_DataType import LMS_DataType
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LMS_AttributeInfo:
|
|
5
|
+
def __init__(self, datatype: LMS_DataType, offset: int, list_index: int = None):
|
|
6
|
+
super().__init__()
|
|
7
|
+
|
|
8
|
+
self.name: str = None
|
|
9
|
+
|
|
10
|
+
# Set later on from the ALI2 block
|
|
11
|
+
self.list_items: list[str] = []
|
|
12
|
+
|
|
13
|
+
self._datatype = datatype
|
|
14
|
+
self._offset = offset
|
|
15
|
+
self._list_index = list_index
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def datatype(self) -> LMS_DataType:
|
|
19
|
+
return self._datatype
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def offset(self) -> int:
|
|
23
|
+
return self._offset
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def list_index(self) -> int:
|
|
27
|
+
return self._list_index
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class LMS_Style:
|
|
2
|
+
def __init__(
|
|
3
|
+
self,
|
|
4
|
+
name: str,
|
|
5
|
+
region_width: int = None,
|
|
6
|
+
line_number: int = None,
|
|
7
|
+
font_index: int = None,
|
|
8
|
+
color_index: int = None,
|
|
9
|
+
):
|
|
10
|
+
self.name = name
|
|
11
|
+
self.region_width = region_width
|
|
12
|
+
self.line_number = line_number
|
|
13
|
+
self.font_index = font_index
|
|
14
|
+
self.color_index = color_index
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from LMS.Field.LMS_DataType import LMS_DataType
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LMS_TagGroup:
|
|
7
|
+
def __init__(
|
|
8
|
+
self, name: str = None, index: int = None, tag_indexes: list[int] = None
|
|
9
|
+
):
|
|
10
|
+
self.name = name
|
|
11
|
+
|
|
12
|
+
self.tag_definitions: list[LMS_TagInfo] = []
|
|
13
|
+
|
|
14
|
+
# The index may be literally set or from the given from the stream
|
|
15
|
+
# The index from the stream does not always align with the total amount of groups
|
|
16
|
+
# Reason why is unknown (acts as an ID of some sort?)
|
|
17
|
+
self.index = index
|
|
18
|
+
|
|
19
|
+
self._tag_indexes = tag_indexes
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def tag_indexes(self) -> list[int]:
|
|
23
|
+
return self._tag_indexes
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LMS_TagInfo:
|
|
27
|
+
def __init__(self, name: str = None, param_indexes: list[int] = None):
|
|
28
|
+
self.name = name
|
|
29
|
+
self.param_info: list[LMS_TagParamInfo] = []
|
|
30
|
+
self._parameter_indexes = param_indexes
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def parameter_indexes(self) -> list[int]:
|
|
34
|
+
return self._parameter_indexes
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class LMS_TagParamInfo:
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
name: str = None,
|
|
42
|
+
list_indexes: list[int] = None,
|
|
43
|
+
datatype: LMS_DataType = None,
|
|
44
|
+
list_items: list[str] = None,
|
|
45
|
+
):
|
|
46
|
+
self.name = name
|
|
47
|
+
self.list_items = list_items or []
|
|
48
|
+
|
|
49
|
+
self._list_indexes = [] if list_indexes is None else list_indexes
|
|
50
|
+
self._type = datatype
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def datatype(self) -> LMS_DataType:
|
|
54
|
+
return self._type
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def list_indexes(self) -> list[int]:
|
|
58
|
+
return self._list_indexes
|
|
File without changes
|
LMS/Project/MSBP.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from LMS.Common.LMS_FileInfo import LMS_FileInfo
|
|
2
|
+
from LMS.Project.Definitions.Attribute import LMS_AttributeInfo
|
|
3
|
+
from LMS.Project.Definitions.Color import LMS_Color
|
|
4
|
+
from LMS.Project.Definitions.Style import LMS_Style
|
|
5
|
+
from LMS.Project.Definitions.Tag import LMS_TagGroup
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MSBP:
|
|
9
|
+
"""A class that represents a MSBP file.
|
|
10
|
+
|
|
11
|
+
https://nintendo-formats.com/libs/lms/msbp.html."""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
info: LMS_FileInfo,
|
|
16
|
+
colors: list[LMS_Color],
|
|
17
|
+
config: list[LMS_AttributeInfo],
|
|
18
|
+
tag_groups: list[LMS_TagGroup],
|
|
19
|
+
styles: list[LMS_Style],
|
|
20
|
+
source_files: list[str],
|
|
21
|
+
):
|
|
22
|
+
self.name = None
|
|
23
|
+
|
|
24
|
+
self._info = info
|
|
25
|
+
|
|
26
|
+
self._colors = colors
|
|
27
|
+
self._config = config
|
|
28
|
+
self._tag_groups = tag_groups
|
|
29
|
+
self._styles = styles
|
|
30
|
+
self._source_files = source_files
|
|
31
|
+
|
|
32
|
+
self.clb1_exists = False
|
|
33
|
+
self.alb1_exists = False
|
|
34
|
+
self.slb1_exists = False
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def info(self) -> LMS_FileInfo:
|
|
38
|
+
"""The stream info of the MSBP."""
|
|
39
|
+
return self._info
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def colors(self) -> list[LMS_Color]:
|
|
43
|
+
"""The color definitions for the project."""
|
|
44
|
+
return self._colors
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def attribute_info(self) -> list[LMS_AttributeInfo]:
|
|
48
|
+
"""The attribute definitions for the project instance."""
|
|
49
|
+
return self._config
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def tag_groups(self) -> list[LMS_TagGroup]:
|
|
53
|
+
"""The tag group definitions for the project instance."""
|
|
54
|
+
return self._tag_groups
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def style_list(self) -> list[LMS_Style]:
|
|
58
|
+
"""The style definitions for the project instance."""
|
|
59
|
+
return self._styles
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def source_files(self) -> list[str]:
|
|
63
|
+
"""The source file definitions for the project instance."""
|
|
64
|
+
return self._source_files
|
LMS/Project/MSBPRead.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import BinaryIO
|
|
3
|
+
|
|
4
|
+
from LMS.Common.Stream.FileInfo import read_file_info
|
|
5
|
+
from LMS.Common.Stream.Hashtable import read_labels
|
|
6
|
+
from LMS.Common.Stream.Section import read_section_data
|
|
7
|
+
from LMS.Field.LMS_DataType import LMS_DataType
|
|
8
|
+
from LMS.FileIO.Stream import FileReader
|
|
9
|
+
from LMS.Project.MSBP import MSBP
|
|
10
|
+
from LMS.Project.Section.ALI2 import read_ali2
|
|
11
|
+
from LMS.Project.Section.ATI2 import read_ati2
|
|
12
|
+
from LMS.Project.Section.CLR1 import read_clr1
|
|
13
|
+
from LMS.Project.Section.String import read_strings
|
|
14
|
+
from LMS.Project.Section.SYL3 import read_styles
|
|
15
|
+
from LMS.Project.Section.TAG2 import read_tag2
|
|
16
|
+
from LMS.Project.Section.TGG2 import read_tgg2
|
|
17
|
+
from LMS.Project.Section.TGP2 import read_tgp2
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def read_msbp(stream: BinaryIO | None) -> MSBP:
|
|
21
|
+
"""Reads a MSBP file from a stream.
|
|
22
|
+
|
|
23
|
+
:param stream: a stream object.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
```
|
|
27
|
+
with open(file_path, "rb") as file:
|
|
28
|
+
msbp = read_msbp(file)
|
|
29
|
+
...
|
|
30
|
+
```"""
|
|
31
|
+
if stream is None:
|
|
32
|
+
raise ValueError("Stream must be valid!")
|
|
33
|
+
|
|
34
|
+
reader = FileReader(stream)
|
|
35
|
+
file_info = read_file_info(reader, "MsgPrjBn")
|
|
36
|
+
|
|
37
|
+
exist_map = {"CLB1": False, "ALB1": False, "SLB1": False}
|
|
38
|
+
|
|
39
|
+
attribute_info_list = attribute_lists = tag_groups = tag_info_list = (
|
|
40
|
+
tag_param_list
|
|
41
|
+
) = None
|
|
42
|
+
for magic, _ in read_section_data(reader, file_info.section_count):
|
|
43
|
+
match magic:
|
|
44
|
+
case "CLB1" | "ALB1" | "SLB1":
|
|
45
|
+
labels, _ = read_labels(reader)
|
|
46
|
+
# Set the name attributes of last read item
|
|
47
|
+
for i in labels:
|
|
48
|
+
items[i].name = labels[i]
|
|
49
|
+
# Set the section exists
|
|
50
|
+
exist_map[magic] = True
|
|
51
|
+
case "CLR1":
|
|
52
|
+
colors = read_clr1(reader)
|
|
53
|
+
items = colors
|
|
54
|
+
case "ATI2":
|
|
55
|
+
attribute_info_list = read_ati2(reader)
|
|
56
|
+
items = attribute_info_list
|
|
57
|
+
case "ALI2":
|
|
58
|
+
attribute_lists = read_ali2(reader)
|
|
59
|
+
case "TGG2":
|
|
60
|
+
tag_groups = read_tgg2(reader, file_info.version)
|
|
61
|
+
case "TAG2":
|
|
62
|
+
tag_info_list = read_tag2(reader)
|
|
63
|
+
case "TGP2":
|
|
64
|
+
tag_param_list = read_tgp2(reader)
|
|
65
|
+
case "TGL2":
|
|
66
|
+
list_items = read_strings(reader, 16)
|
|
67
|
+
case "SYL3":
|
|
68
|
+
styles = read_styles(reader)
|
|
69
|
+
items = styles
|
|
70
|
+
case "CTI1":
|
|
71
|
+
source_list = read_strings(reader, 32)
|
|
72
|
+
case _:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
# Set all list items for attributes
|
|
76
|
+
if attribute_info_list:
|
|
77
|
+
for info in attribute_info_list:
|
|
78
|
+
if info.datatype is LMS_DataType.LIST:
|
|
79
|
+
info.list_items = attribute_lists[info.list_index]
|
|
80
|
+
|
|
81
|
+
# Combining tag data
|
|
82
|
+
if tag_groups:
|
|
83
|
+
for group in tag_groups:
|
|
84
|
+
group.tag_definitions = [tag_info_list[i] for i in group.tag_indexes]
|
|
85
|
+
|
|
86
|
+
for tag in group.tag_definitions:
|
|
87
|
+
tag.param_info = [tag_param_list[i] for i in tag.parameter_indexes]
|
|
88
|
+
|
|
89
|
+
for parameter in tag.param_info:
|
|
90
|
+
parameter.list_items = [
|
|
91
|
+
list_items[i] for i in parameter.list_indexes
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
file = MSBP(file_info, colors, attribute_info_list, tag_groups, styles, source_list)
|
|
95
|
+
file.name = os.path.basename(stream.name).removesuffix(".msbp")
|
|
96
|
+
|
|
97
|
+
file.clb1_exists = exist_map["CLB1"]
|
|
98
|
+
file.alb1_exists = exist_map["ALB1"]
|
|
99
|
+
file.slb1_exists = exist_map["SLB1"]
|
|
100
|
+
|
|
101
|
+
return file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from LMS.FileIO.Stream import FileReader
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def read_ali2(reader: FileReader) -> list[list[str]]:
|
|
5
|
+
attr_lists = []
|
|
6
|
+
|
|
7
|
+
count = reader.read_uint32()
|
|
8
|
+
for offset in reader.read_offset_array(count):
|
|
9
|
+
reader.seek(offset)
|
|
10
|
+
|
|
11
|
+
items = []
|
|
12
|
+
item_count = reader.read_uint32()
|
|
13
|
+
for offset in reader.read_offset_array(item_count):
|
|
14
|
+
reader.seek(offset)
|
|
15
|
+
items.append(reader.read_str_variable_encoding())
|
|
16
|
+
|
|
17
|
+
attr_lists.append(items)
|
|
18
|
+
|
|
19
|
+
return attr_lists
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from LMS.Field.LMS_DataType import LMS_DataType
|
|
2
|
+
from LMS.FileIO.Stream import FileReader
|
|
3
|
+
from LMS.Project.Definitions.Attribute import LMS_AttributeInfo
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def read_ati2(reader: FileReader) -> list[LMS_AttributeInfo]:
|
|
7
|
+
info_list = []
|
|
8
|
+
count = reader.read_uint32()
|
|
9
|
+
for _ in range(count):
|
|
10
|
+
datatype = LMS_DataType(reader.read_uint8())
|
|
11
|
+
reader.skip(1)
|
|
12
|
+
list_index = reader.read_uint16()
|
|
13
|
+
offset = reader.read_uint32()
|
|
14
|
+
|
|
15
|
+
info_list.append(LMS_AttributeInfo(datatype, offset, list_index))
|
|
16
|
+
|
|
17
|
+
return info_list
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from LMS.FileIO.Stream import FileReader
|
|
2
|
+
from LMS.Project.Definitions.Color import LMS_Color
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def read_clr1(reader: FileReader) -> list[LMS_Color]:
|
|
6
|
+
count = reader.read_uint32()
|
|
7
|
+
return [
|
|
8
|
+
LMS_Color(
|
|
9
|
+
reader.read_uint32(),
|
|
10
|
+
reader.read_uint32(),
|
|
11
|
+
reader.read_uint32(),
|
|
12
|
+
reader.read_uint32(),
|
|
13
|
+
)
|
|
14
|
+
for _ in range(count)
|
|
15
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from LMS.FileIO.Stream import FileReader
|
|
2
|
+
from LMS.Project.Definitions.Style import LMS_Style
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def read_styles(reader: FileReader) -> list[LMS_Style]:
|
|
6
|
+
style_list = []
|
|
7
|
+
|
|
8
|
+
count = reader.read_uint32()
|
|
9
|
+
for _ in range(count):
|
|
10
|
+
region_width = reader.read_uint32()
|
|
11
|
+
line_number = reader.read_uint32()
|
|
12
|
+
font_index = reader.read_uint32()
|
|
13
|
+
color_index = reader.read_uint32()
|
|
14
|
+
|
|
15
|
+
style_list.append(LMS_Style(region_width, line_number, font_index, color_index))
|
|
16
|
+
|
|
17
|
+
return style_list
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from LMS.FileIO.Stream import FileReader
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def read_strings(reader: FileReader, count_bits: int) -> list[str]:
|
|
5
|
+
string_list = []
|
|
6
|
+
|
|
7
|
+
match count_bits:
|
|
8
|
+
case 16:
|
|
9
|
+
count = reader.read_uint16()
|
|
10
|
+
reader.skip(2)
|
|
11
|
+
case 32:
|
|
12
|
+
count = reader.read_uint32()
|
|
13
|
+
|
|
14
|
+
for offset in reader.read_offset_array(count):
|
|
15
|
+
reader.seek(offset)
|
|
16
|
+
string_list.append(reader.read_str_variable_encoding())
|
|
17
|
+
|
|
18
|
+
return string_list
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from LMS.Field.LMS_DataType import LMS_DataType
|
|
2
|
+
from LMS.FileIO.Stream import FileReader
|
|
3
|
+
from LMS.Project.Definitions.Tag import LMS_TagInfo
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def read_tag2(reader: FileReader) -> list[LMS_TagInfo]:
|
|
7
|
+
info_list = []
|
|
8
|
+
|
|
9
|
+
count = reader.read_uint32()
|
|
10
|
+
for offset in reader.read_offset_array(count):
|
|
11
|
+
reader.seek(offset)
|
|
12
|
+
|
|
13
|
+
param_count = reader.read_uint16()
|
|
14
|
+
param_indexes = reader.read_uint16_array(param_count)
|
|
15
|
+
name = reader.read_str_variable_encoding()
|
|
16
|
+
|
|
17
|
+
info_list.append(LMS_TagInfo(name, param_indexes))
|
|
18
|
+
|
|
19
|
+
return info_list
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from LMS.FileIO.Stream import FileReader
|
|
2
|
+
from LMS.Project.Definitions.Tag import LMS_TagGroup
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def read_tgg2(reader: FileReader, version: int) -> list[LMS_TagGroup]:
|
|
6
|
+
group_list = []
|
|
7
|
+
|
|
8
|
+
count = reader.read_uint32()
|
|
9
|
+
for i, offset in enumerate(reader.read_offset_array(count)):
|
|
10
|
+
reader.seek(offset)
|
|
11
|
+
|
|
12
|
+
index = reader.read_uint16() if version == 4 else i
|
|
13
|
+
|
|
14
|
+
tag_count = reader.read_uint16()
|
|
15
|
+
tag_indexes = reader.read_uint16_array(tag_count)
|
|
16
|
+
|
|
17
|
+
name = reader.read_str_variable_encoding()
|
|
18
|
+
group_list.append(LMS_TagGroup(name, index, tag_indexes))
|
|
19
|
+
|
|
20
|
+
return group_list
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from LMS.Field.LMS_DataType import LMS_DataType
|
|
2
|
+
from LMS.FileIO.Stream import FileReader
|
|
3
|
+
from LMS.Project.Definitions.Tag import LMS_TagParamInfo
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def read_tgp2(reader: FileReader) -> list[LMS_TagParamInfo]:
|
|
7
|
+
parameter_info = []
|
|
8
|
+
|
|
9
|
+
count = reader.read_uint32()
|
|
10
|
+
for offset in reader.read_offset_array(count):
|
|
11
|
+
reader.seek(offset)
|
|
12
|
+
|
|
13
|
+
info_type = LMS_DataType(reader.read_uint8())
|
|
14
|
+
|
|
15
|
+
if info_type is not LMS_DataType.LIST:
|
|
16
|
+
name = reader.read_str_variable_encoding()
|
|
17
|
+
parameter_info.append(LMS_TagParamInfo(name, datatype=info_type))
|
|
18
|
+
continue
|
|
19
|
+
|
|
20
|
+
reader.skip(1)
|
|
21
|
+
list_count = reader.read_uint16()
|
|
22
|
+
list_indexes = reader.read_uint16_array(list_count)
|
|
23
|
+
name = reader.read_str_variable_encoding()
|
|
24
|
+
parameter_info.append(LMS_TagParamInfo(name, list_indexes, LMS_DataType.LIST))
|
|
25
|
+
|
|
26
|
+
return parameter_info
|
LMS/Project/__init__.py
ADDED
|
File without changes
|
LMS/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyLibMS
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Python library built for the libMessageStudio (LMS) proprietary file formats from Nintendo. Supports MSBT, MSBP, and MSBF.
|
|
5
|
+
Author: AbdyyEee
|
|
6
|
+
License: Copyright 2025 AbdyyEee
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
11
|
+
|
|
12
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
13
|
+
Project-URL: Documentation, https://github.com/AbdyyEee/PylibMS/wiki
|
|
14
|
+
Project-URL: Repository, https://github.com/AbdyyEee/PylibMS
|
|
15
|
+
Keywords: nintendo,msbf,msbt,msbp,modding,lms,libmessagestudio
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: PyYAML==6.0.1
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# PylibMS
|
|
23
|
+
PyLMS is a library built in Python3.10+ for Nintendo's LMS (libMessageStudio) file formats. Intended for games on the Nintendo 3DS and Wii U revisions of the file format.
|
|
24
|
+
> [!NOTE]
|
|
25
|
+
> PylibMS is being refactored in it's entirety, with MSBF support being added soon. Use at your own risk.
|
|
26
|
+
|
|
27
|
+
Games that work with the library, including but not limited to:
|
|
28
|
+
* Tomodachi Life
|
|
29
|
+
* Nintendo Badge Arcade
|
|
30
|
+
* The Legend of Zelda: A Link Between Worlds
|
|
31
|
+
* Animal Crossing: Amiibo Festival
|
|
32
|
+
* Super Mario 3D World
|
|
33
|
+
* Super Mario 3D Land.
|
|
34
|
+
# Features and Usage
|
|
35
|
+
See [the wiki](https://github.com/AbdyyEee/PylibMS/wiki) for explanations and examples on how to use the library.
|
|
36
|
+
# Installation
|
|
37
|
+
```
|
|
38
|
+
pip install PylibMS
|
|
39
|
+
```
|
|
40
|
+
Python version must be >= 10.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
LMS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
LMS/Common/LMS_Exceptions.py,sha256=jB2nuoPod67p5biKnJI8MWEOg0YI0iK6zs0DdfDHjiI,306
|
|
3
|
+
LMS/Common/LMS_FileInfo.py,sha256=H1JETYYR5TnkcqVP5JbknYaYCJmfFP_cUu0FDPVY9Mw,225
|
|
4
|
+
LMS/Common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
LMS/Common/Stream/FileInfo.py,sha256=6j0ikCjjqDKgmMCgM74HItIZyuxH9zzCp6QKGKR7txA,1900
|
|
6
|
+
LMS/Common/Stream/Hashtable.py,sha256=kTuqwbHMOIio5pw8fCTD-QA2Mr9PLn9GUxJmcCcYois,2841
|
|
7
|
+
LMS/Common/Stream/Section.py,sha256=iyOmyvGGUXp5mETssPHkVVSp1T85Dyxer8mXMy-pCxg,1619
|
|
8
|
+
LMS/Config/TitleConfig.py,sha256=k5AOMQbrNonzVZYCOJDf2WucFcbP_ZInuzXV8tDwx90,7670
|
|
9
|
+
LMS/Config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
LMS/Config/Definitions/Attributes.py,sha256=GOTmxOskQOiC3hoX3gZ9VkNLNfnQ_lQdVxgVwfkPRdc,405
|
|
11
|
+
LMS/Config/Definitions/Tags.py,sha256=wXycrf8POWgFEvkkv1izl0JkF3I694D_B9PpgACnzzc,1600
|
|
12
|
+
LMS/Config/Definitions/Value.py,sha256=w8IeztrRZyQGQ1zh4sFgdEglE_joQ3CpUi2jqWrTmQQ,236
|
|
13
|
+
LMS/Config/Definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
LMS/Field/LMS_DataType.py,sha256=eT4z74lJqUO5Ex-674a6QEcFAl1s7WF_3dG4gWwfQ84,2348
|
|
15
|
+
LMS/Field/LMS_Field.py,sha256=yvp20gjA7NyYgF07A9A8_nkXrblluNdQds55U1HEecI,4493
|
|
16
|
+
LMS/Field/Stream.py,sha256=f21ubq75YTC8uHFRPA0ICWe3SsGd9oTVOdmvJ1nwtlU,2162
|
|
17
|
+
LMS/Field/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
LMS/FileIO/Encoding.py,sha256=c2LQ7eJ8Y7ZmA9l7osDaOLVlUMYeOwAe9nsHoARFPko,1284
|
|
19
|
+
LMS/FileIO/Stream.py,sha256=fSmd78CnmvUdU4EXjUcuOScViyNhUzk3zfSWrf8ceBE,6085
|
|
20
|
+
LMS/Message/MSBT.py,sha256=gVRlBCmUJQP9K35YQI-ejzUuOmb5tuPvCAFl9Wp0yKQ,2025
|
|
21
|
+
LMS/Message/MSBTEntry.py,sha256=uRmd1DpVFHiLPncacUhee7brQziu4CCiZmgpjQ5EK-g,941
|
|
22
|
+
LMS/Message/MSBTStream.py,sha256=38plLke4Xumu7CHX0AERrU-9rY2Zrss35AXhBghUMxw,5137
|
|
23
|
+
LMS/Message/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
LMS/Message/Definitions/LMS_FieldMap.py,sha256=xNwM_JCLDWC1FA7iSyai6MnUhKo11NRJj3B-ZyKDQvU,258
|
|
25
|
+
LMS/Message/Definitions/LMS_MessageText.py,sha256=p5y4_-53OfqyzxOSFsLCmBMkpxGD4UGAsFBSy-A6gP4,4250
|
|
26
|
+
LMS/Message/Definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
+
LMS/Message/Section/ATR1.py,sha256=YdPmchgS2by3MYBXk1__5YSSZBzem4kERTSexy3L6X0,3213
|
|
28
|
+
LMS/Message/Section/TSY1.py,sha256=rSYjdjMevJfyJ9JFCv2WEBr1zbhuqVvOm5y_h66qOJs,401
|
|
29
|
+
LMS/Message/Section/TXT2.py,sha256=5jnnPSOGy82bYTPIK3ay8MuSkka8deMd6x4_BWvJosc,2753
|
|
30
|
+
LMS/Message/Section/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
LMS/Message/Tag/LMS_Tag.py,sha256=fKN30TUppGPe0RqkMKWtuKof01gqZxLnx6_WhaCO0UQ,5524
|
|
32
|
+
LMS/Message/Tag/LMS_TagExceptions.py,sha256=zy6WdFzUw5hTK0JqGMtLRy-BSInUNhZjKNTS2WtQqsE,147
|
|
33
|
+
LMS/Message/Tag/Stream.py,sha256=Rg4HLhVgd1QHM2FLnXrAY6LJa7WW9FwSNi3nze5HP4w,5386
|
|
34
|
+
LMS/Message/Tag/System.yaml,sha256=KdoHSc0KkwqL7hadcLVAG6cXiptnztPsY7WHUYN9Y8M,1151
|
|
35
|
+
LMS/Message/Tag/Tag_Formats.py,sha256=E8qC8aL8a7wUJ6xNrjf0danoU-LIv4v2q9YPfjLLre0,301
|
|
36
|
+
LMS/Message/Tag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
|
+
LMS/Project/MSBP.py,sha256=gS7tMRcHlOPiuaQUD9xFDwIERXuqfmTWAhbKa3Go4yM,1912
|
|
38
|
+
LMS/Project/MSBPRead.py,sha256=qA5UvYQaCwXgNCZSZaQZOT5qC1Fwo2LQjqjSKew80L8,3636
|
|
39
|
+
LMS/Project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
|
+
LMS/Project/Definitions/Attribute.py,sha256=jiVOvDs3iZpepwAnGPTWMwKoCvQ0Ns-ePcIDOEPf4O8,669
|
|
41
|
+
LMS/Project/Definitions/Color.py,sha256=tCBdLS0vKCd8sH28Wkc-cAuUjEVc9QiYjvJECimyZS4,271
|
|
42
|
+
LMS/Project/Definitions/Style.py,sha256=_cRGRVyAXOWzNMnUrg2t1luWvvXcfUVq0o8ldIYwqso,402
|
|
43
|
+
LMS/Project/Definitions/Tag.py,sha256=hGYHmqy32xhxaEDaB0N674BXnpmBoVO1BMNMr2ffAD0,1608
|
|
44
|
+
LMS/Project/Definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
+
LMS/Project/Section/ALI2.py,sha256=1alMaJ9hi6yzjZDWZmwPDFTveMZQmSarL2y3YGyLIpA,522
|
|
46
|
+
LMS/Project/Section/ATI2.py,sha256=6sc9FZnFm8hCgtCauNdOdsdXX_ALZmZMr3XFATayP0w,566
|
|
47
|
+
LMS/Project/Section/CLR1.py,sha256=Rd947097QY9TUbb8pUrYWGpi2ZomvYOyfw2NOyGc1Vw,411
|
|
48
|
+
LMS/Project/Section/SYL3.py,sha256=vzvEPu4bU8CDWMFUL_4x3nw5Ol8n41eVa6nPu2d1s0g,534
|
|
49
|
+
LMS/Project/Section/String.py,sha256=7UUxDCPFu3zrTU02nxt5zNEXU5WiB-vyM8RfB1CcNXM,484
|
|
50
|
+
LMS/Project/Section/TAG2.py,sha256=RN6lARekovT9xfniA5aTiRiq11l0js-U_v0jsei7AYQ,589
|
|
51
|
+
LMS/Project/Section/TGG2.py,sha256=2gjElRBQaSZh3kbjXCHHOaLH_DnvXTfL-8llwhlJEJ4,636
|
|
52
|
+
LMS/Project/Section/TGP2.py,sha256=zDARsPFsEH3jTQ7QJjY_uNmWQtCfYn_oOKKpYJT2Yrc,920
|
|
53
|
+
pylibms-2.0.0.dist-info/licenses/LICENSE,sha256=IGTA9rMb-XpTMPJOzC6z0rRxSzysj3DlV5IQyDd06Xo,1061
|
|
54
|
+
pylibms-2.0.0.dist-info/METADATA,sha256=ltt_e82fSMIrqK-AKNul5SLTRtknpA0j2iMEwUTlsHE,2425
|
|
55
|
+
pylibms-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
56
|
+
pylibms-2.0.0.dist-info/top_level.txt,sha256=9CNXOP8YVmfLHgkpZLI8iFLdUFr2ExrtAvTcH0GPW_o,4
|
|
57
|
+
pylibms-2.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2025 AbdyyEee
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
LMS
|