PyLibMS 3.3.9__tar.gz → 4.0.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 (89) hide show
  1. pylibms-4.0.1/PKG-INFO +94 -0
  2. pylibms-4.0.1/PyLibMS.egg-info/PKG-INFO +94 -0
  3. {pylibms-3.3.9 → pylibms-4.0.1}/PyLibMS.egg-info/SOURCES.txt +18 -7
  4. pylibms-4.0.1/README.md +79 -0
  5. {pylibms-3.3.9/lms/message/definitions → pylibms-4.0.1/lms/common}/field/io.py +5 -5
  6. {pylibms-3.3.9/lms/common → pylibms-4.0.1/lms/common/field}/lms_datatype.py +12 -13
  7. {pylibms-3.3.9/lms/message/definitions → pylibms-4.0.1/lms/common}/field/lms_field.py +25 -24
  8. pylibms-4.0.1/lms/common/lms_constants.py +9 -0
  9. {pylibms-3.3.9 → pylibms-4.0.1}/lms/common/lms_exceptions.py +3 -0
  10. {pylibms-3.3.9 → pylibms-4.0.1}/lms/common/stream/fileinfo.py +10 -8
  11. {pylibms-3.3.9 → pylibms-4.0.1}/lms/common/stream/hashtable.py +19 -10
  12. {pylibms-3.3.9 → pylibms-4.0.1}/lms/common/stream/section.py +5 -2
  13. {pylibms-3.3.9 → pylibms-4.0.1}/lms/fileio/io.py +10 -0
  14. pylibms-4.0.1/lms/flowchart/definitions/flowchart.py +103 -0
  15. pylibms-4.0.1/lms/flowchart/definitions/lms_nodeexceptions.py +7 -0
  16. pylibms-4.0.1/lms/flowchart/definitions/node.py +455 -0
  17. pylibms-4.0.1/lms/flowchart/definitions/node_type.py +97 -0
  18. pylibms-4.0.1/lms/flowchart/flw3.py +326 -0
  19. pylibms-4.0.1/lms/flowchart/msbf.py +86 -0
  20. pylibms-4.0.1/lms/flowchart/msbfio.py +138 -0
  21. {pylibms-3.3.9/lms/message/definitions → pylibms-4.0.1/lms/message}/lms_messagetext.py +1 -1
  22. {pylibms-3.3.9 → pylibms-4.0.1}/lms/message/msbtentry.py +2 -2
  23. {pylibms-3.3.9 → pylibms-4.0.1}/lms/message/msbtio.py +8 -8
  24. {pylibms-3.3.9 → pylibms-4.0.1}/lms/message/section/atr1.py +3 -3
  25. pylibms-4.0.1/lms/message/section/tsy1.py +13 -0
  26. {pylibms-3.3.9 → pylibms-4.0.1}/lms/message/section/txt2.py +1 -1
  27. {pylibms-3.3.9 → pylibms-4.0.1}/lms/message/tag/io/param_io.py +3 -3
  28. {pylibms-3.3.9 → pylibms-4.0.1}/lms/message/tag/io/tag_io.py +1 -1
  29. {pylibms-3.3.9 → pylibms-4.0.1}/lms/message/tag/lms_tag.py +1 -1
  30. pylibms-4.0.1/lms/project/definitions/__init__.py +0 -0
  31. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/definitions/attribute.py +1 -1
  32. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/definitions/tag.py +11 -11
  33. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/msbpread.py +1 -1
  34. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/section/ati2.py +1 -1
  35. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/section/tag2.py +2 -2
  36. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/section/tgp2.py +1 -1
  37. pylibms-4.0.1/lms/titleconfig/__init__.py +0 -0
  38. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/config.py +62 -4
  39. pylibms-4.0.1/lms/titleconfig/definitions/__init__.py +0 -0
  40. pylibms-4.0.1/lms/titleconfig/definitions/nodes.py +101 -0
  41. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/definitions/tags.py +3 -3
  42. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/definitions/value.py +2 -2
  43. pylibms-4.0.1/lms/titleconfig/presets/Tomodachi Life Living The Dream.yaml +3369 -0
  44. pylibms-3.3.9/lms/titleconfig/presets/Tomodachi Life.yaml → pylibms-4.0.1/lms/titleconfig/presets/Tomodachi Life NA-EU.yaml +53 -1
  45. {pylibms-3.3.9 → pylibms-4.0.1}/pyproject.toml +8 -4
  46. pylibms-3.3.9/PKG-INFO +0 -77
  47. pylibms-3.3.9/PyLibMS.egg-info/PKG-INFO +0 -77
  48. pylibms-3.3.9/README.md +0 -56
  49. pylibms-3.3.9/lms/message/section/tsy1.py +0 -13
  50. pylibms-3.3.9/lms/titleconfig/presets/Tomodachi Life Living the Dream Demo.yaml +0 -3369
  51. {pylibms-3.3.9 → pylibms-4.0.1}/LICENSE +0 -0
  52. {pylibms-3.3.9 → pylibms-4.0.1}/MANIFEST.in +0 -0
  53. {pylibms-3.3.9 → pylibms-4.0.1}/PyLibMS.egg-info/dependency_links.txt +0 -0
  54. {pylibms-3.3.9 → pylibms-4.0.1}/PyLibMS.egg-info/requires.txt +0 -0
  55. {pylibms-3.3.9 → pylibms-4.0.1}/PyLibMS.egg-info/top_level.txt +0 -0
  56. {pylibms-3.3.9 → pylibms-4.0.1}/lms/__init__.py +0 -0
  57. {pylibms-3.3.9 → pylibms-4.0.1}/lms/common/__init__.py +0 -0
  58. {pylibms-3.3.9/lms/message → pylibms-4.0.1/lms/common/field}/__init__.py +0 -0
  59. {pylibms-3.3.9 → pylibms-4.0.1}/lms/common/lms_fileinfo.py +0 -0
  60. {pylibms-3.3.9 → pylibms-4.0.1}/lms/fileio/encoding.py +0 -0
  61. {pylibms-3.3.9/lms/message/definitions → pylibms-4.0.1/lms/flowchart}/__init__.py +0 -0
  62. {pylibms-3.3.9/lms/message/definitions/field → pylibms-4.0.1/lms/flowchart/definitions}/__init__.py +0 -0
  63. {pylibms-3.3.9/lms/message/section → pylibms-4.0.1/lms/message}/__init__.py +0 -0
  64. {pylibms-3.3.9/lms/message/tag → pylibms-4.0.1/lms/message/definitions}/__init__.py +0 -0
  65. {pylibms-3.3.9/lms/project → pylibms-4.0.1/lms/message/definitions/field}/__init__.py +0 -0
  66. {pylibms-3.3.9 → pylibms-4.0.1}/lms/message/msbt.py +0 -0
  67. {pylibms-3.3.9/lms/project/definitions → pylibms-4.0.1/lms/message/section}/__init__.py +0 -0
  68. {pylibms-3.3.9 → pylibms-4.0.1}/lms/message/section/nli1.py +0 -0
  69. {pylibms-3.3.9/lms/titleconfig → pylibms-4.0.1/lms/message/tag}/__init__.py +0 -0
  70. {pylibms-3.3.9 → pylibms-4.0.1}/lms/message/tag/lms_tagexceptions.py +0 -0
  71. {pylibms-3.3.9/lms/titleconfig/definitions → pylibms-4.0.1/lms/project}/__init__.py +0 -0
  72. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/definitions/color.py +0 -0
  73. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/definitions/style.py +0 -0
  74. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/msbp.py +0 -0
  75. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/section/ali2.py +0 -0
  76. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/section/clr1.py +0 -0
  77. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/section/string.py +0 -0
  78. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/section/syl3.py +0 -0
  79. {pylibms-3.3.9 → pylibms-4.0.1}/lms/project/section/tgg2.py +0 -0
  80. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/definitions/attribute.py +0 -0
  81. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/presets/Badge Arcade.yaml +0 -0
  82. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/presets/Brain Age Concentration Training.yaml +0 -0
  83. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/presets/Kirby Planet Robobot.yaml +0 -0
  84. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/presets/Super Mario 3D Land.yaml +0 -0
  85. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/presets/Super Mario 3D World + Bowsers Fury.yaml +0 -0
  86. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/presets/Super Mario Odyssey.yaml +0 -0
  87. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/presets/The Legend of Zelda Echos of Wisdom.yaml +0 -0
  88. {pylibms-3.3.9 → pylibms-4.0.1}/lms/titleconfig/presets/The Legend of Zelda a Link Between Worlds.yaml +0 -0
  89. {pylibms-3.3.9 → pylibms-4.0.1}/setup.cfg +0 -0
pylibms-4.0.1/PKG-INFO ADDED
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyLibMS
3
+ Version: 4.0.1
4
+ Summary: Python library built for the libMessageStudio (LMS) proprietary file formats from Nintendo. Supports MSBT, MSBP, and MSBF.
5
+ Author: AbdyyEee
6
+ License-Expression: MIT
7
+ Project-URL: Documentation, https://github.com/AbdyyEee/PylibMS/wiki
8
+ Project-URL: Repository, https://github.com/AbdyyEee/PylibMS
9
+ Keywords: nintendo,msbf,msbt,msbp,modding,lms,libmessagestudio,flw3
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: PyYAML>=6.0.1
14
+ Dynamic: license-file
15
+
16
+ # PylibMS
17
+
18
+ PylibMS is a library built in Python 3.12+ for the libMessageStudio (LMS) proprietary file formats (`msbt`, `msbp`,
19
+ `msbf`) from Nintendo. It supports the following:
20
+
21
+ | File Format | Read | Write | Configuration Support |
22
+ |-------------|------|-------|-------------------------------------------------------------------------------------------|
23
+ | MSBT | ✅ | ✅ | Complete attribute decoding + tag decoding down to official `mstxt` Nintendo tool syntax. | |
24
+ | MSBF | ✅ | ✅ | Flexible node + parameter decoding through configuration definitions.
25
+ | MSBP | ✅ | ❌ | N/A |
26
+
27
+ This library is designed to support LMS revision 3.0 and above, with the associated file formats used across the
28
+ following Nintendo platforms:
29
+
30
+ * Wii (Specific titles only)
31
+ * Nintendo 3DS
32
+ * Wii U
33
+ * Mobile (Specific titles only)
34
+ * Nintendo Switch
35
+
36
+ # Features and Usage
37
+
38
+ Simple preview of the library is below. See [the wiki](https://github.com/AbdyyEee/PylibMS/wiki) for more explanations
39
+ and examples.
40
+
41
+ ## Reading
42
+
43
+ MSBT/MSBF
44
+
45
+ ```py
46
+ from lms.message.msbtio import read_msbt_path
47
+ from lms.message.msbfio import read_msbf_path
48
+
49
+ msbt = read_msbt_path("Game.msbt")
50
+ msbf = read_msbf_path("Game_Flowchart.msbf")
51
+ ```
52
+
53
+ ## Writing
54
+
55
+ ```py
56
+ from lms.message.msbtio import write_msbt_path
57
+ from lms.message.msbfio import write_msbf_path
58
+
59
+ write_msbt_path("Out_Game.msbt")
60
+ write_msbf_path("Out_Game_Flowchart.msbt")
61
+ ```
62
+
63
+ # Adding/Editing Presets
64
+
65
+ To add or edit Preset, you may create an issue with the relevant `yaml` file and the game it is for.
66
+
67
+ # Installation
68
+
69
+ ```
70
+ pip install PylibMS
71
+ ```
72
+
73
+ [Pip Page](https://pypi.org/project/PyLibMS/)
74
+
75
+ # Build Instructions
76
+
77
+ Python version must be `>=3.12.`
78
+
79
+ Clone the repository, then run `pip install` (venv recommended)
80
+
81
+ ```bash
82
+ git clone https://github.com/AbdyyEee/PylibMS.git
83
+ cd PylibMS
84
+ pip install -e
85
+ ```
86
+
87
+ # Credits & Sources
88
+
89
+ * [Nintendo-File-Formats](https://nintendo-formats.com) by Kinnay: For existing information on the MSBT and MSBP file
90
+ formats.
91
+ * [Trippixyz](https://github.com/Trippixyz): For helping me get started general decompilation of the formats and general
92
+ help.
93
+ * [AeonSake](https://github.com/AeonSake): Inspiration for some the implementation of the library and a bit of general
94
+ help.
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyLibMS
3
+ Version: 4.0.1
4
+ Summary: Python library built for the libMessageStudio (LMS) proprietary file formats from Nintendo. Supports MSBT, MSBP, and MSBF.
5
+ Author: AbdyyEee
6
+ License-Expression: MIT
7
+ Project-URL: Documentation, https://github.com/AbdyyEee/PylibMS/wiki
8
+ Project-URL: Repository, https://github.com/AbdyyEee/PylibMS
9
+ Keywords: nintendo,msbf,msbt,msbp,modding,lms,libmessagestudio,flw3
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: PyYAML>=6.0.1
14
+ Dynamic: license-file
15
+
16
+ # PylibMS
17
+
18
+ PylibMS is a library built in Python 3.12+ for the libMessageStudio (LMS) proprietary file formats (`msbt`, `msbp`,
19
+ `msbf`) from Nintendo. It supports the following:
20
+
21
+ | File Format | Read | Write | Configuration Support |
22
+ |-------------|------|-------|-------------------------------------------------------------------------------------------|
23
+ | MSBT | ✅ | ✅ | Complete attribute decoding + tag decoding down to official `mstxt` Nintendo tool syntax. | |
24
+ | MSBF | ✅ | ✅ | Flexible node + parameter decoding through configuration definitions.
25
+ | MSBP | ✅ | ❌ | N/A |
26
+
27
+ This library is designed to support LMS revision 3.0 and above, with the associated file formats used across the
28
+ following Nintendo platforms:
29
+
30
+ * Wii (Specific titles only)
31
+ * Nintendo 3DS
32
+ * Wii U
33
+ * Mobile (Specific titles only)
34
+ * Nintendo Switch
35
+
36
+ # Features and Usage
37
+
38
+ Simple preview of the library is below. See [the wiki](https://github.com/AbdyyEee/PylibMS/wiki) for more explanations
39
+ and examples.
40
+
41
+ ## Reading
42
+
43
+ MSBT/MSBF
44
+
45
+ ```py
46
+ from lms.message.msbtio import read_msbt_path
47
+ from lms.message.msbfio import read_msbf_path
48
+
49
+ msbt = read_msbt_path("Game.msbt")
50
+ msbf = read_msbf_path("Game_Flowchart.msbf")
51
+ ```
52
+
53
+ ## Writing
54
+
55
+ ```py
56
+ from lms.message.msbtio import write_msbt_path
57
+ from lms.message.msbfio import write_msbf_path
58
+
59
+ write_msbt_path("Out_Game.msbt")
60
+ write_msbf_path("Out_Game_Flowchart.msbt")
61
+ ```
62
+
63
+ # Adding/Editing Presets
64
+
65
+ To add or edit Preset, you may create an issue with the relevant `yaml` file and the game it is for.
66
+
67
+ # Installation
68
+
69
+ ```
70
+ pip install PylibMS
71
+ ```
72
+
73
+ [Pip Page](https://pypi.org/project/PyLibMS/)
74
+
75
+ # Build Instructions
76
+
77
+ Python version must be `>=3.12.`
78
+
79
+ Clone the repository, then run `pip install` (venv recommended)
80
+
81
+ ```bash
82
+ git clone https://github.com/AbdyyEee/PylibMS.git
83
+ cd PylibMS
84
+ pip install -e
85
+ ```
86
+
87
+ # Credits & Sources
88
+
89
+ * [Nintendo-File-Formats](https://nintendo-formats.com) by Kinnay: For existing information on the MSBT and MSBP file
90
+ formats.
91
+ * [Trippixyz](https://github.com/Trippixyz): For helping me get started general decompilation of the formats and general
92
+ help.
93
+ * [AeonSake](https://github.com/AeonSake): Inspiration for some the implementation of the library and a bit of general
94
+ help.
@@ -9,23 +9,34 @@ PyLibMS.egg-info/requires.txt
9
9
  PyLibMS.egg-info/top_level.txt
10
10
  lms/__init__.py
11
11
  lms/common/__init__.py
12
- lms/common/lms_datatype.py
12
+ lms/common/lms_constants.py
13
13
  lms/common/lms_exceptions.py
14
14
  lms/common/lms_fileinfo.py
15
+ lms/common/field/__init__.py
16
+ lms/common/field/io.py
17
+ lms/common/field/lms_datatype.py
18
+ lms/common/field/lms_field.py
15
19
  lms/common/stream/fileinfo.py
16
20
  lms/common/stream/hashtable.py
17
21
  lms/common/stream/section.py
18
22
  lms/fileio/encoding.py
19
23
  lms/fileio/io.py
24
+ lms/flowchart/__init__.py
25
+ lms/flowchart/flw3.py
26
+ lms/flowchart/msbf.py
27
+ lms/flowchart/msbfio.py
28
+ lms/flowchart/definitions/__init__.py
29
+ lms/flowchart/definitions/flowchart.py
30
+ lms/flowchart/definitions/lms_nodeexceptions.py
31
+ lms/flowchart/definitions/node.py
32
+ lms/flowchart/definitions/node_type.py
20
33
  lms/message/__init__.py
34
+ lms/message/lms_messagetext.py
21
35
  lms/message/msbt.py
22
36
  lms/message/msbtentry.py
23
37
  lms/message/msbtio.py
24
38
  lms/message/definitions/__init__.py
25
- lms/message/definitions/lms_messagetext.py
26
39
  lms/message/definitions/field/__init__.py
27
- lms/message/definitions/field/io.py
28
- lms/message/definitions/field/lms_field.py
29
40
  lms/message/section/__init__.py
30
41
  lms/message/section/atr1.py
31
42
  lms/message/section/nli1.py
@@ -56,6 +67,7 @@ lms/titleconfig/__init__.py
56
67
  lms/titleconfig/config.py
57
68
  lms/titleconfig/definitions/__init__.py
58
69
  lms/titleconfig/definitions/attribute.py
70
+ lms/titleconfig/definitions/nodes.py
59
71
  lms/titleconfig/definitions/tags.py
60
72
  lms/titleconfig/definitions/value.py
61
73
  lms/titleconfig/presets/Badge Arcade.yaml
@@ -66,6 +78,5 @@ lms/titleconfig/presets/Super Mario 3D World + Bowsers Fury.yaml
66
78
  lms/titleconfig/presets/Super Mario Odyssey.yaml
67
79
  lms/titleconfig/presets/The Legend of Zelda Echos of Wisdom.yaml
68
80
  lms/titleconfig/presets/The Legend of Zelda a Link Between Worlds.yaml
69
- lms/titleconfig/presets/Tomodachi Life Living The Dream Demo.yaml
70
- lms/titleconfig/presets/Tomodachi Life Living the Dream Demo.yaml
71
- lms/titleconfig/presets/Tomodachi Life.yaml
81
+ lms/titleconfig/presets/Tomodachi Life Living The Dream.yaml
82
+ lms/titleconfig/presets/Tomodachi Life NA-EU.yaml
@@ -0,0 +1,79 @@
1
+ # PylibMS
2
+
3
+ PylibMS is a library built in Python 3.12+ for the libMessageStudio (LMS) proprietary file formats (`msbt`, `msbp`,
4
+ `msbf`) from Nintendo. It supports the following:
5
+
6
+ | File Format | Read | Write | Configuration Support |
7
+ |-------------|------|-------|-------------------------------------------------------------------------------------------|
8
+ | MSBT | ✅ | ✅ | Complete attribute decoding + tag decoding down to official `mstxt` Nintendo tool syntax. | |
9
+ | MSBF | ✅ | ✅ | Flexible node + parameter decoding through configuration definitions.
10
+ | MSBP | ✅ | ❌ | N/A |
11
+
12
+ This library is designed to support LMS revision 3.0 and above, with the associated file formats used across the
13
+ following Nintendo platforms:
14
+
15
+ * Wii (Specific titles only)
16
+ * Nintendo 3DS
17
+ * Wii U
18
+ * Mobile (Specific titles only)
19
+ * Nintendo Switch
20
+
21
+ # Features and Usage
22
+
23
+ Simple preview of the library is below. See [the wiki](https://github.com/AbdyyEee/PylibMS/wiki) for more explanations
24
+ and examples.
25
+
26
+ ## Reading
27
+
28
+ MSBT/MSBF
29
+
30
+ ```py
31
+ from lms.message.msbtio import read_msbt_path
32
+ from lms.message.msbfio import read_msbf_path
33
+
34
+ msbt = read_msbt_path("Game.msbt")
35
+ msbf = read_msbf_path("Game_Flowchart.msbf")
36
+ ```
37
+
38
+ ## Writing
39
+
40
+ ```py
41
+ from lms.message.msbtio import write_msbt_path
42
+ from lms.message.msbfio import write_msbf_path
43
+
44
+ write_msbt_path("Out_Game.msbt")
45
+ write_msbf_path("Out_Game_Flowchart.msbt")
46
+ ```
47
+
48
+ # Adding/Editing Presets
49
+
50
+ To add or edit Preset, you may create an issue with the relevant `yaml` file and the game it is for.
51
+
52
+ # Installation
53
+
54
+ ```
55
+ pip install PylibMS
56
+ ```
57
+
58
+ [Pip Page](https://pypi.org/project/PyLibMS/)
59
+
60
+ # Build Instructions
61
+
62
+ Python version must be `>=3.12.`
63
+
64
+ Clone the repository, then run `pip install` (venv recommended)
65
+
66
+ ```bash
67
+ git clone https://github.com/AbdyyEee/PylibMS.git
68
+ cd PylibMS
69
+ pip install -e
70
+ ```
71
+
72
+ # Credits & Sources
73
+
74
+ * [Nintendo-File-Formats](https://nintendo-formats.com) by Kinnay: For existing information on the MSBT and MSBP file
75
+ formats.
76
+ * [Trippixyz](https://github.com/Trippixyz): For helping me get started general decompilation of the formats and general
77
+ help.
78
+ * [AeonSake](https://github.com/AeonSake): Inspiration for some the implementation of the library and a bit of general
79
+ help.
@@ -1,15 +1,15 @@
1
1
  from typing import Callable
2
2
 
3
- from lms.common.lms_datatype import (LMS_DataType, is_bool_datatype,
4
- is_bytes_datatype, is_list_datatype,
5
- is_number_datatype)
3
+ from lms.common.field.lms_datatype import (LMS_DataType, is_bool_datatype,
4
+ is_bytes_datatype, is_list_datatype,
5
+ is_number_datatype)
6
+ from lms.common.field.lms_field import LMS_Field
6
7
  from lms.fileio.io import FileReader, FileWriter
7
- from lms.message.definitions.field.lms_field import LMS_Field
8
8
  from lms.titleconfig.definitions.value import ValueDefinition
9
9
 
10
10
 
11
11
  def read_field(reader: FileReader, definition: ValueDefinition) -> LMS_Field:
12
- # String is excluded as their reading varies between tags and attributes
12
+ # String is excluded as their reading varies between MSBT/MSBF
13
13
  match definition.datatype:
14
14
  case LMS_DataType.UINT8:
15
15
  value = reader.read_uint8()
@@ -16,6 +16,7 @@ ALIAS_MAP = {
16
16
  "byte": "BYTES",
17
17
  }
18
18
 
19
+
19
20
  def is_number_datatype(value: object, datatype: LMS_DataType) -> TypeGuard[int | float]:
20
21
  return datatype in (
21
22
  LMS_DataType.UINT8,
@@ -57,16 +58,10 @@ class LMS_DataType(Enum):
57
58
 
58
59
  FLOAT32 = 6
59
60
 
60
- # Unknown 16 bit type (value of 6) has yet to be documented
61
- # Might be some sort of 2 byte integer, float, or may be an array.
62
- # We won't ever know cause no game (yet that has been found) has utilized this type
63
- # Thanks Nintendo.
64
- ...
65
-
66
61
  STRING = 8
67
62
  LIST = 9
68
63
 
69
- # Interface types
64
+ # - Interface types -
70
65
  # These types act as an abstraction for a real LMS_Datatype
71
66
  # The actual value isn't important since they are not real types, but are instantiated from a config
72
67
  BOOL = "bool"
@@ -107,8 +102,8 @@ class LMS_DataType(Enum):
107
102
 
108
103
  @property
109
104
  def stream_size(self) -> int:
110
- """The size the datatype takes up in a stream."""
111
- return {
105
+ """Size of the datatype, only for fixed integers."""
106
+ sizes = {
112
107
  LMS_DataType.UINT8: 1,
113
108
  LMS_DataType.UINT16: 2,
114
109
  LMS_DataType.UINT32: 4,
@@ -116,10 +111,14 @@ class LMS_DataType(Enum):
116
111
  LMS_DataType.INT16: 2,
117
112
  LMS_DataType.INT32: 4,
118
113
  LMS_DataType.FLOAT32: 4,
119
- LMS_DataType.LIST: 1,
120
- LMS_DataType.BOOL: 1,
121
- LMS_DataType.BYTES: 1,
122
- }[self]
114
+ }
115
+
116
+ try:
117
+ return sizes[self]
118
+ except KeyError:
119
+ raise TypeError(
120
+ f"Stream size is not defined for '{self.to_string()}'."
121
+ )
123
122
 
124
123
  @classmethod
125
124
  def from_string(cls, string: str):
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from collections.abc import Iterator
4
4
  from dataclasses import dataclass
5
5
 
6
- from lms.common.lms_datatype import LMS_DataType
6
+ from lms.common.field.lms_datatype import LMS_DataType
7
7
  from lms.titleconfig.definitions.value import ValueDefinition
8
8
 
9
9
  FLOAT32_MIN = -3.4028235e38
@@ -11,6 +11,7 @@ FLOAT32_MAX = 3.4028235e38
11
11
 
12
12
  type FieldValue = int | str | float | bool | bytes
13
13
 
14
+
14
15
  @dataclass(frozen=True)
15
16
  class LMS_FieldMap:
16
17
  """
@@ -44,9 +45,9 @@ class LMS_FieldMap:
44
45
  for definition in definitions:
45
46
  match definition.datatype:
46
47
  case LMS_DataType.LIST:
47
- field_map[definition.name] = definition.list_items[0]
48
+ field_map[definition.name] = definition.list_items[0]
48
49
  case LMS_DataType.BOOL:
49
- field_map[definition.name] = False
50
+ field_map[definition.name] = False
50
51
  case LMS_DataType.FLOAT32:
51
52
  field_map[definition.name] = 0.0
52
53
  case _:
@@ -97,15 +98,16 @@ class LMS_Field:
97
98
  """
98
99
 
99
100
  def __init__(
100
- self, value: int | str | float | bytes | bool, definition: ValueDefinition
101
+ self, value: int | str | float | bytes | bool, definition: ValueDefinition,
101
102
  ):
102
- _verify_value(value, definition)
103
+ _verify_value_from_definition(value, definition)
103
104
  self._definition = definition
104
105
  self._value = value
105
106
 
106
107
  def __repr__(self):
107
108
  if self.datatype is LMS_DataType.LIST:
108
109
  return f"LMS_Field(value={self._value!r}, list_items={self.list_items!r})"
110
+
109
111
  return f"LMS_Field(value={self._value!r}, type={self.datatype!r})"
110
112
 
111
113
  @property
@@ -135,11 +137,11 @@ class LMS_Field:
135
137
 
136
138
  @value.setter
137
139
  def value(self, new_value: int | str | float | bytes | bool):
138
- _verify_value(new_value, self._definition)
140
+ _verify_value_from_definition(new_value, self._definition)
139
141
  self._value = new_value
140
142
 
141
143
 
142
- def _verify_value(
144
+ def _verify_value_from_definition(
143
145
  value: int | str | float | bytes | bool, definition: ValueDefinition
144
146
  ) -> None:
145
147
  datatype = definition.datatype
@@ -161,33 +163,32 @@ def _verify_value(
161
163
  )
162
164
  else:
163
165
  return
164
- case LMS_DataType.FLOAT32 if isinstance(value, float):
165
- _verify_number_is_in_range(value, FLOAT32_MIN, FLOAT32_MAX, definition)
166
- return
167
166
  case _ if isinstance(value, int):
168
- bits = datatype.stream_size * 8
169
- if datatype.signed:
170
- max_value = 2 ** (bits - 1)
171
- min_value = -max_value
172
- else:
173
- min_value, max_value = 0, (2 ** bits) - 1
174
-
175
- _verify_number_is_in_range(value, min_value, max_value, definition)
167
+ _verify_number_datatype(value, definition.datatype)
176
168
  return
177
169
 
178
170
  raise TypeError(
179
- f"The value provided for '{definition.name}' type '{type(value)}' should be '{datatype.builtin_type}'."
171
+ f"The value provided for '{definition.name}' type '{type(value)}' should be '{datatype.builtin_type}' (DATATYPE {definition.datatype})."
180
172
  )
181
173
 
182
174
 
183
- def _verify_number_is_in_range(
175
+ def _verify_number_datatype(
184
176
  value: int | float,
185
- min_value: int | float,
186
- max_value: int | float,
187
- definition: ValueDefinition,
177
+ datatype: LMS_DataType,
188
178
  ):
179
+ if datatype is LMS_DataType.FLOAT32:
180
+ max_value = FLOAT32_MAX
181
+ min_value = FLOAT32_MIN
182
+ else:
183
+ bits = datatype.stream_size * 8
184
+ if datatype.signed:
185
+ max_value = 2 ** (bits - 1)
186
+ min_value = -max_value
187
+ else:
188
+ min_value, max_value = 0, (2 ** bits) - 1
189
+
189
190
  if not min_value <= value <= max_value:
190
191
  raise ValueError(
191
- f"""The value '{value}' of type '{definition.datatype}' provided for field '{definition.name}'
192
+ f"""The value '{value}' of type '{datatype.name}' provided for this field
192
193
  is out of range of ({min_value}, {max_value})"""
193
194
  )
@@ -0,0 +1,9 @@
1
+ # Common constant definitions between files
2
+ PADDING_BYTE = b"\x00"
3
+
4
+ SIZE_OFFSET = 0x12
5
+ SECTION_DATA_START = 0x20
6
+ SECTION_PADDING_BYTE = b"\xAB"
7
+
8
+ LITTLE_ENDIAN_BOM = b"\xff\xfe"
9
+ BIG_ENDIAN_BOM = b"\xfe\xff"
@@ -5,3 +5,6 @@ class LMS_UnexpectedMagicError(Exception): ...
5
5
 
6
6
 
7
7
  class LMS_MisalignedSizeError(Exception): ...
8
+
9
+
10
+ class LMS_UnsupportedSectionError(Exception): ...
@@ -1,12 +1,9 @@
1
1
  from lms.common import lms_exceptions
2
+ from lms.common.lms_constants import BIG_ENDIAN_BOM, SECTION_DATA_START, LITTLE_ENDIAN_BOM, SIZE_OFFSET
2
3
  from lms.common.lms_fileinfo import LMS_FileInfo
3
4
  from lms.fileio.encoding import FileEncoding
4
5
  from lms.fileio.io import FileReader, FileWriter
5
6
 
6
- DATA_START = 0x20
7
- LITTLE_ENDIAN_BOM = b"\xff\xfe"
8
- BIG_ENDIAN_BOM = b"\xfe\xff"
9
-
10
7
 
11
8
  def read_file_info(reader: FileReader, expected_magic: str) -> LMS_FileInfo:
12
9
  magic = reader.read_string_len(8)
@@ -32,10 +29,10 @@ def read_file_info(reader: FileReader, expected_magic: str) -> LMS_FileInfo:
32
29
  file_size = reader.read_uint32()
33
30
 
34
31
  reader.seek(0, 2)
35
- if file_size != reader.tell():
36
- raise lms_exceptions.LMS_MisalignedSizeError(f"File size is misaligned!")
32
+ if file_size != (real_size := reader.tell()):
33
+ raise lms_exceptions.LMS_MisalignedSizeError(f"File size is misaligned!` Got {file_size} expected {real_size}.")
37
34
 
38
- reader.seek(DATA_START)
35
+ reader.seek(SECTION_DATA_START)
39
36
 
40
37
  return LMS_FileInfo(
41
38
  is_big_endian,
@@ -62,4 +59,9 @@ def write_file_info(writer: FileWriter, magic: str, file_info: LMS_FileInfo) ->
62
59
  writer.write_bytes(b"\x00\x00")
63
60
  writer.write_bytes(b"\x00" * 4)
64
61
  writer.write_bytes(b"\x00" * 10)
65
- writer.seek(DATA_START)
62
+ writer.seek(SECTION_DATA_START)
63
+
64
+
65
+ def write_file_size(writer: FileWriter):
66
+ writer.seek(SIZE_OFFSET)
67
+ writer.write_uint32(writer.get_stream_size())
@@ -27,21 +27,23 @@ def read_labels(reader: FileReader) -> tuple[dict, int]:
27
27
  return sorted_labels, slot_count
28
28
 
29
29
 
30
- def write_labels(writer: FileWriter, labels: list[str], slot_count: int) -> None:
30
+ def write_labels(writer: FileWriter, labels: list[str], slot_count: int, index_map: dict[str, int] = None) -> None:
31
31
  writer.write_uint32(slot_count)
32
32
 
33
33
  hash_slots = {}
34
- index_map = {}
35
- # Add each label to each hash slot
34
+
35
+ add_indices = index_map is None
36
+
37
+ if index_map is None:
38
+ index_map = {}
39
+
36
40
  for i, label in enumerate(labels):
37
- hash = _calculate_hash(label, slot_count)
41
+ slot = _calculate_hash(label, slot_count)
38
42
 
39
- if hash not in hash_slots:
40
- hash_slots[hash] = [label]
41
- else:
42
- hash_slots[hash].append(label)
43
+ hash_slots.setdefault(slot, []).append(label)
43
44
 
44
- index_map[label] = i
45
+ if add_indices:
46
+ index_map[label] = i
45
47
 
46
48
  label_offsets = slot_count * 8 + 4
47
49
 
@@ -66,8 +68,15 @@ def write_labels(writer: FileWriter, labels: list[str], slot_count: int) -> None
66
68
  writer.write_uint32(index_map[label])
67
69
 
68
70
 
69
- # See https://nintendo-formats.com/libs/lms/overview.html#hash-tables
70
71
  def _calculate_hash(label: str, slot_count: int) -> int:
72
+ """
73
+ Calucate the hash of a label
74
+
75
+ :param label: the label to calculate the hash for.
76
+ :param slot_count: the number of slots.
77
+
78
+ Taken directly from https://nintendo-formats.com/libs/lms/overview.html#hash-tables
79
+ """
71
80
  hash = 0
72
81
  for character in label:
73
82
  hash = hash * 0x492 + ord(character)
@@ -2,6 +2,9 @@ from typing import Any, Callable, Generator
2
2
 
3
3
  from lms.fileio.io import FileReader, FileWriter
4
4
 
5
+ SECTION_PADDING_BYTE = b"\xAB"
6
+ SECTION_ALIGNMENT = 16
7
+
5
8
 
6
9
  def read_section_data(
7
10
  reader: FileReader, section_count: int
@@ -14,7 +17,7 @@ def read_section_data(
14
17
  reader.skip(8)
15
18
  end = reader.tell() + size
16
19
 
17
- yield (magic, size)
20
+ yield magic, size
18
21
 
19
22
  reader.seek(end)
20
23
  reader.align(16)
@@ -55,4 +58,4 @@ def _write_end_data(writer: FileWriter, data_start: int, size_offset: int) -> No
55
58
  writer.seek(size_offset)
56
59
  writer.write_uint32(size)
57
60
  writer.seek(end)
58
- writer.write_alignment(b"\xab", 16)
61
+ writer.write_alignment(SECTION_PADDING_BYTE, SECTION_ALIGNMENT)
@@ -6,6 +6,8 @@ from typing import BinaryIO, Generator
6
6
  from io import BytesIO, IOBase
7
7
  from lms.fileio.encoding import FileEncoding
8
8
 
9
+ PADDING_BYTE = b"\x00"
10
+
9
11
  STRUCT_TYPES = {
10
12
  "little": {
11
13
  "uint8": "<B",
@@ -90,6 +92,14 @@ class FileReader:
90
92
  def read_float32(self) -> float:
91
93
  return struct.unpack(self._get_datatype("float"), self._stream.read(4))[0]
92
94
 
95
+ def read_string_offset(self, section_start: int) -> str:
96
+ offset = self.read_uint32()
97
+ last_position = self.tell()
98
+ self.seek(section_start + offset)
99
+ string = self.read_encoded_string()
100
+ self.seek(last_position)
101
+ return string
102
+
93
103
  def read_string_len(self, length: int) -> str:
94
104
  return self._stream.read(length).decode("UTF-8")
95
105