PyLibMS 2.1.1__tar.gz → 2.1.2__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.1 → pylibms-2.1.2}/LMS/FileIO/Stream.py +6 -0
  2. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Definitions/Field/LMS_Field.py +4 -11
  3. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Section/ATR1.py +3 -3
  4. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Tag/Stream.py +11 -13
  5. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/MSBP.py +0 -4
  6. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/MSBPRead.py +7 -6
  7. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Config.py +3 -9
  8. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Definitions/Attributes.py +1 -0
  9. {pylibms-2.1.1 → pylibms-2.1.2}/PKG-INFO +6 -5
  10. {pylibms-2.1.1 → pylibms-2.1.2}/PyLibMS.egg-info/PKG-INFO +6 -5
  11. {pylibms-2.1.1 → pylibms-2.1.2}/PyLibMS.egg-info/SOURCES.txt +1 -1
  12. {pylibms-2.1.1 → pylibms-2.1.2}/README.md +4 -3
  13. {pylibms-2.1.1 → pylibms-2.1.2}/pyproject.toml +3 -3
  14. {pylibms-2.1.1 → pylibms-2.1.2}/LICENSE +0 -0
  15. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Common/LMS_DataType.py +0 -0
  16. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Common/LMS_Exceptions.py +0 -0
  17. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Common/LMS_FileInfo.py +0 -0
  18. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Common/Stream/FileInfo.py +0 -0
  19. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Common/Stream/Hashtable.py +0 -0
  20. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Common/Stream/Section.py +0 -0
  21. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Common/__init__.py +0 -0
  22. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/FileIO/Encoding.py +0 -0
  23. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Definitions/Field/LMS_FieldMap.py +0 -0
  24. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Definitions/Field/Stream.py +0 -0
  25. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Definitions/Field/__init__.py +0 -0
  26. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Definitions/LMS_MessageText.py +0 -0
  27. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Definitions/__init__.py +0 -0
  28. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/MSBT.py +0 -0
  29. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/MSBTEntry.py +0 -0
  30. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/MSBTStream.py +0 -0
  31. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Section/TSY1.py +0 -0
  32. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Section/TXT2.py +0 -0
  33. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Section/__init__.py +0 -0
  34. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Tag/LMS_Tag.py +0 -0
  35. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Tag/LMS_TagExceptions.py +0 -0
  36. /pylibms-2.1.1/LMS/Message/Tag/System_Definition.py → /pylibms-2.1.2/LMS/Message/Tag/System_Definitions.py +0 -0
  37. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Tag/Tag_Formats.py +0 -0
  38. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/Tag/__init__.py +0 -0
  39. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Message/__init__.py +0 -0
  40. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Definitions/Attribute.py +0 -0
  41. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Definitions/Color.py +0 -0
  42. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Definitions/Style.py +0 -0
  43. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Definitions/Tag.py +0 -0
  44. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Definitions/__init__.py +0 -0
  45. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Section/ALI2.py +0 -0
  46. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Section/ATI2.py +0 -0
  47. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Section/CLR1.py +0 -0
  48. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Section/SYL3.py +0 -0
  49. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Section/String.py +0 -0
  50. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Section/TAG2.py +0 -0
  51. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Section/TGG2.py +0 -0
  52. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/Section/TGP2.py +0 -0
  53. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/Project/__init__.py +0 -0
  54. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Definitions/Tags.py +0 -0
  55. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Definitions/Value.py +0 -0
  56. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Definitions/__init__.py +0 -0
  57. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Presets/Badge Arcade.yaml +0 -0
  58. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Presets/Brain Age Concentration Training.yaml +0 -0
  59. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Presets/Kirby Planet Robobot.yaml +0 -0
  60. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Presets/Super Mario 3D Land.yaml +0 -0
  61. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Presets/The Legend of Zelda a Link Between Worlds.yaml +0 -0
  62. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/Presets/Tomodachi Life.yaml +0 -0
  63. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/TitleConfig/__init__.py +0 -0
  64. {pylibms-2.1.1 → pylibms-2.1.2}/LMS/__init__.py +0 -0
  65. {pylibms-2.1.1 → pylibms-2.1.2}/MANIFEST.in +0 -0
  66. {pylibms-2.1.1 → pylibms-2.1.2}/PyLibMS.egg-info/dependency_links.txt +0 -0
  67. {pylibms-2.1.1 → pylibms-2.1.2}/PyLibMS.egg-info/requires.txt +0 -0
  68. {pylibms-2.1.1 → pylibms-2.1.2}/PyLibMS.egg-info/top_level.txt +0 -0
  69. {pylibms-2.1.1 → pylibms-2.1.2}/setup.cfg +0 -0
@@ -166,6 +166,12 @@ class FileWriter:
166
166
  def write_string(self, string: str):
167
167
  self.write_bytes(string.encode("UTF-8"))
168
168
 
169
+ def write_len_variable_encoding_string(self, string: str) -> None:
170
+ self.write_uint16(len(string) * self.encoding.width)
171
+ self.write_variable_encoding_string.write_variable_encoding_string(
172
+ string, False
173
+ )
174
+
169
175
  def write_variable_encoding_string(self, string: str, terminate: bool = True):
170
176
  self.write_bytes(string.encode(self.encoding.to_string_format(self.big_endian)))
171
177
  if terminate:
@@ -84,7 +84,6 @@ class LMS_Field:
84
84
  return
85
85
  case LMS_DataType.FLOAT32:
86
86
  min_value, max_value = -3.4028235e38, 3.4028235e38
87
-
88
87
  # Other number types fall here
89
88
  case _:
90
89
  bits = datatype.stream_size * 8
@@ -108,16 +107,10 @@ def cast_value(value: int | str | float | bytes | bool, datatype: LMS_DataType)
108
107
  case LMS_DataType.BYTE:
109
108
  return hex(int(value))
110
109
  case LMS_DataType.BOOL:
110
+ if value not in ("false", "true"):
111
+ raise ValueError("Value must be true or false for bool type.")
111
112
  return value.strip().lower() == "true"
112
113
  case LMS_DataType.FLOAT32:
113
- try:
114
- return float(value)
115
- except ValueError:
116
- raise ValueError(f"Cannot convert '{value}' to float.")
114
+ return float(value)
117
115
  case _:
118
- if value.isdigit():
119
- return int(value)
120
- else:
121
- raise TypeError(
122
- f"The provided value for type '{datatype}' is not a digit!"
123
- )
116
+ return int(value)
@@ -88,14 +88,14 @@ def write_decoded_atr1(
88
88
  string_offset = 8 + size_per_attribute * len(attributes)
89
89
  for attr in attributes:
90
90
  for field in attr.values():
91
- if field.datatype is not LMS_DataType.STRING:
92
- write_field(writer, field)
93
- else:
91
+ if field.datatype is LMS_DataType.STRING:
94
92
  writer.write_uint32(string_offset)
95
93
  string_offset += (
96
94
  len(field.value) * writer.encoding.width
97
95
  ) + writer.encoding.width
98
96
  string_table.append(field.value)
97
+ else:
98
+ write_field(writer, field)
99
99
 
100
100
  for string in string_table:
101
101
  writer.write_variable_encoding_string(string)
@@ -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_Definition import SYSTEM_GROUP
9
+ from LMS.Message.Tag.System_Definitions import SYSTEM_GROUP
10
10
  from LMS.TitleConfig.Definitions.Tags import TagConfig, TagDefinition
11
11
 
12
12
 
@@ -34,8 +34,8 @@ def read_tag(
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.
38
- # i.e [System:Color 00-00-00-FF]
37
+ # names but read the tag as encoded. This is to account for encoded tags that group have tag names attatched.
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)
41
41
  return LMS_EncodedTag(
@@ -56,7 +56,7 @@ def read_tag(
56
56
 
57
57
  def write_tag(writer: FileWriter, tag: LMS_TagBase) -> None:
58
58
  tag_indicator = b"\x0e" + (b"\x00" * (writer.encoding.width - 1))
59
-
59
+
60
60
  if writer.big_endian:
61
61
  tag_indicator = tag_indicator[::-1]
62
62
 
@@ -121,10 +121,10 @@ def _read_decoded_parameters(
121
121
  def _write_decoded_parameters(writer: FileWriter, tag: LMS_DecodedTag) -> None:
122
122
  param_size = 0
123
123
 
124
- # Tags are padded by 0xCD if the size is not aligned to the encoding
124
+ # Tags are padded by a 0xCD byte if the size is not aligned to the encoding
125
125
  # This can occur before a string parameter, or at the end of the tag.
126
- # Set a flag in order to be able to pad the first string correctly
127
- needs_padding = False
126
+ # If a string parameter exists, then the padding will always be at the first string instance
127
+ # first_string is a flag so that the first string can be padded correctly.
128
128
  first_string = True
129
129
 
130
130
  for field in tag.parameters.values():
@@ -133,21 +133,19 @@ def _write_decoded_parameters(writer: FileWriter, tag: LMS_DecodedTag) -> None:
133
133
  else:
134
134
  param_size += field.datatype.stream_size
135
135
 
136
- if param_size % 2 == 1:
137
- needs_padding = True
136
+ if needs_padding := param_size % 2 == 1:
138
137
  param_size += 1
139
138
 
140
139
  writer.write_uint16(param_size)
141
140
  for field in tag.parameters.values():
142
141
  try:
143
142
  if field.datatype is LMS_DataType.STRING:
143
+
144
144
  if first_string and needs_padding:
145
145
  writer.write_bytes(b"\xcd")
146
- first_string = False
147
- needs_padding = False
146
+ first_string, needs_padding = False, False
148
147
 
149
- writer.write_uint16(len(field.value) * writer.encoding.width)
150
- writer.write_variable_encoding_string(field.value, False)
148
+ writer.write_len_variable_encoding_string(field.value)
151
149
  else:
152
150
  write_field(writer, field)
153
151
  except Exception as e:
@@ -29,10 +29,6 @@ class MSBP:
29
29
  self._styles = styles
30
30
  self._source_files = source_files
31
31
 
32
- self.clb1_exists = False
33
- self.alb1_exists = False
34
- self.slb1_exists = False
35
-
36
32
  @property
37
33
  def info(self) -> LMS_FileInfo:
38
34
  """The stream info of the MSBP."""
@@ -46,7 +46,6 @@ def read_msbp(stream: BinaryIO | None) -> MSBP:
46
46
  # Set the name attributes of last read item
47
47
  for i in labels:
48
48
  items[i].name = labels[i]
49
- # Set the section exists
50
49
  exist_map[magic] = True
51
50
  case "CLR1":
52
51
  colors = read_clr1(reader)
@@ -80,6 +79,13 @@ def read_msbp(stream: BinaryIO | None) -> MSBP:
80
79
 
81
80
  # Combining tag data
82
81
  if tag_groups:
82
+
83
+ # Cut out the System group from the definitions
84
+ # Since these are predefined, there is no need to keep them.
85
+ # Easier to remove here as opposed to manually skipping over them while reading
86
+ # See Tag/System_Definition for the full System definition of tags.
87
+ tag_groups = tag_groups[1:]
88
+
83
89
  for group in tag_groups:
84
90
  group.tag_definitions = [tag_info_list[i] for i in group.tag_indexes]
85
91
 
@@ -93,9 +99,4 @@ def read_msbp(stream: BinaryIO | None) -> MSBP:
93
99
 
94
100
  file = MSBP(file_info, colors, attribute_info_list, tag_groups, styles, source_list)
95
101
  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
102
  return file
@@ -105,19 +105,16 @@ class TitleConfig:
105
105
  # TODO: Add custom node definitions
106
106
 
107
107
  config = {}
108
- # Source files may be a path to a non-existent directory.
109
- # These files were generated from the source machine from the actual libMS tool
110
- # Shorten the filename with basname and replace the extension with .msbt for lookup later when reading a MSBT
108
+
111
109
  config[TAG_KEY] = {
112
110
  "groups": {
113
- i + 1: group.name for i, group in enumerate(project.tag_groups[1:])
111
+ i: group.name for i, group in enumerate(project.tag_groups, start=1)
114
112
  },
115
113
  "tags": [],
116
114
  }
117
115
 
118
- # Slice to exclude System group
119
116
  if project.tag_groups is not None:
120
- for group_i, group in enumerate(project.tag_groups[1:], start=1):
117
+ for group_i, group in enumerate(project.tag_groups, start=1):
121
118
  for tag_i, info in enumerate(group.tag_definitions):
122
119
 
123
120
  definition = {
@@ -142,10 +139,7 @@ class TitleConfig:
142
139
 
143
140
  config[TAG_KEY]["tags"].append(definition)
144
141
 
145
- # Set main attribute entries as the definitions from the MSBP
146
- # Since most games use one MSBP these act the primary definitions
147
142
  config[ATTR_KEY] = []
148
-
149
143
  if project.attribute_info is not None:
150
144
 
151
145
  attr_definitions = []
@@ -6,6 +6,7 @@ from LMS.TitleConfig.Definitions.Value import ValueDefinition
6
6
  @dataclass(frozen=True)
7
7
  class AttributeConfig:
8
8
  """Class that represents an attribute config definition"""
9
+
9
10
  name: str
10
11
  description: str = field(repr=False)
11
12
  definitions: list[ValueDefinition]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyLibMS
3
- Version: 2.1.1
3
+ Version: 2.1.2
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
@@ -13,7 +13,7 @@ License: Copyright 2025 AbdyyEee
13
13
  Project-URL: Documentation, https://github.com/AbdyyEee/PylibMS/wiki
14
14
  Project-URL: Repository, https://github.com/AbdyyEee/PylibMS
15
15
  Keywords: nintendo,msbf,msbt,msbp,modding,lms,libmessagestudio
16
- Requires-Python: >=3.10
16
+ Requires-Python: >=3.12
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: PyYAML==6.0.1
@@ -51,14 +51,15 @@ from LMS.Message.MSBTStream import write_msbt
51
51
  with open("Out.msbt", "wb") as f:
52
52
  write_msbt(f, msbt)
53
53
  ```
54
- # Adding Presets
55
- To add a Preset, you may create an issue with the relevant yaml file and the game it is for.
54
+ # Adding/Editing Presets
55
+ To add or edit Preset, you may create an issue with the relevant yaml file and the game it is for.
56
56
 
57
57
  # Installation
58
58
  ```
59
59
  pip install PylibMS
60
60
  ```
61
- Python version must be `>=3.10.`
61
+
62
+ Python version must be `>=3.12.`
62
63
  # Credits & Sources
63
64
  * [Nintendo-File-Formats](https://nintendo-formats.com) by Kinnay: For existing information on the MSBT and MSBP file formats.
64
65
  * [Trippixyz](https://github.com/Trippixyz): For helping me get started general decompilation of the formats and general help.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyLibMS
3
- Version: 2.1.1
3
+ Version: 2.1.2
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
@@ -13,7 +13,7 @@ License: Copyright 2025 AbdyyEee
13
13
  Project-URL: Documentation, https://github.com/AbdyyEee/PylibMS/wiki
14
14
  Project-URL: Repository, https://github.com/AbdyyEee/PylibMS
15
15
  Keywords: nintendo,msbf,msbt,msbp,modding,lms,libmessagestudio
16
- Requires-Python: >=3.10
16
+ Requires-Python: >=3.12
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: PyYAML==6.0.1
@@ -51,14 +51,15 @@ from LMS.Message.MSBTStream import write_msbt
51
51
  with open("Out.msbt", "wb") as f:
52
52
  write_msbt(f, msbt)
53
53
  ```
54
- # Adding Presets
55
- To add a Preset, you may create an issue with the relevant yaml file and the game it is for.
54
+ # Adding/Editing Presets
55
+ To add or edit Preset, you may create an issue with the relevant yaml file and the game it is for.
56
56
 
57
57
  # Installation
58
58
  ```
59
59
  pip install PylibMS
60
60
  ```
61
- Python version must be `>=3.10.`
61
+
62
+ Python version must be `>=3.12.`
62
63
  # Credits & Sources
63
64
  * [Nintendo-File-Formats](https://nintendo-formats.com) by Kinnay: For existing information on the MSBT and MSBP file formats.
64
65
  * [Trippixyz](https://github.com/Trippixyz): For helping me get started general decompilation of the formats and general help.
@@ -29,7 +29,7 @@ LMS/Message/Section/__init__.py
29
29
  LMS/Message/Tag/LMS_Tag.py
30
30
  LMS/Message/Tag/LMS_TagExceptions.py
31
31
  LMS/Message/Tag/Stream.py
32
- LMS/Message/Tag/System_Definition.py
32
+ LMS/Message/Tag/System_Definitions.py
33
33
  LMS/Message/Tag/Tag_Formats.py
34
34
  LMS/Message/Tag/__init__.py
35
35
  LMS/Project/MSBP.py
@@ -30,14 +30,15 @@ from LMS.Message.MSBTStream import write_msbt
30
30
  with open("Out.msbt", "wb") as f:
31
31
  write_msbt(f, msbt)
32
32
  ```
33
- # Adding Presets
34
- To add a Preset, you may create an issue with the relevant yaml file and the game it is for.
33
+ # Adding/Editing Presets
34
+ To add or edit Preset, you may create an issue with the relevant yaml file and the game it is for.
35
35
 
36
36
  # Installation
37
37
  ```
38
38
  pip install PylibMS
39
39
  ```
40
- Python version must be `>=3.10.`
40
+
41
+ Python version must be `>=3.12.`
41
42
  # Credits & Sources
42
43
  * [Nintendo-File-Formats](https://nintendo-formats.com) by Kinnay: For existing information on the MSBT and MSBP file formats.
43
44
  * [Trippixyz](https://github.com/Trippixyz): For helping me get started general decompilation of the formats and general help.
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "PyLibMS"
7
- version = "2.1.1"
8
- requires-python = ">=3.10"
7
+ version = "2.1.2"
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"
11
11
  authors = [{ name = "AbdyyEee" }]
@@ -15,4 +15,4 @@ dependencies = ["PyYAML==6.0.1"]
15
15
 
16
16
  [project.urls]
17
17
  Documentation = "https://github.com/AbdyyEee/PylibMS/wiki"
18
- Repository = "https://github.com/AbdyyEee/PylibMS"
18
+ Repository = "https://github.com/AbdyyEee/PylibMS"
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