mapfile_parser 2.12.1__cp38-abi3-win_amd64.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.
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # SPDX-FileCopyrightText: © 2022-2024 Decompollaborate
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ from __future__ import annotations
7
+
8
+ __version_info__ = (2, 12, 1)
9
+ __version__ = ".".join(map(str, __version_info__)) # + "-dev0"
10
+ __author__ = "Decompollaborate"
11
+
12
+ from . import utils as utils
13
+
14
+ from .mapfile import MapFile as MapFile
15
+ from .mapfile import Symbol as Symbol
16
+ from .mapfile import Section as Section
17
+ from .mapfile import Segment as Segment
18
+ from .mapfile import FoundSymbolInfo as FoundSymbolInfo
19
+ from .mapfile import SymbolComparisonInfo as SymbolComparisonInfo
20
+ from .mapfile import MapsComparisonInfo as MapsComparisonInfo
21
+ from .mapfile import ReportCategories as ReportCategories
22
+
23
+ from .progress_stats import ProgressStats as ProgressStats
24
+
25
+ from . import frontends as frontends
26
+
27
+ # Renamed types.
28
+ # TODO: remove on version 3.0
29
+ File = Section
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # SPDX-FileCopyrightText: © 2022-2024 Decompollaborate
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ import decomp_settings
10
+
11
+ import mapfile_parser
12
+
13
+
14
+ def mapfileParserMain():
15
+ decompConfig: decomp_settings.Config | None
16
+ try:
17
+ decompConfig = decomp_settings.scan_for_config()
18
+ except Exception:
19
+ decompConfig = None
20
+
21
+ description = description = """\
22
+ Interface to call any of the mapfile_parser's CLI utilities.
23
+
24
+ All the CLI utilities support the `decomp.yaml` specification from the
25
+ [`decomp_settings`](https://github.com/ethteck/decomp_settings) project.
26
+
27
+ If a `decomp.yaml` file is detected, then every CLI argument that can be
28
+ inferred from that file will be be considered optional instead.
29
+
30
+ Most CLI utilites will also add a new optional "version" argument to allow
31
+ picking the version to process from the `decomp.yaml` file. It defaults to the
32
+ first listed version.
33
+ """
34
+ parser = argparse.ArgumentParser(
35
+ description=description,
36
+ prog="mapfile_parser",
37
+ formatter_class=argparse.RawDescriptionHelpFormatter,
38
+ )
39
+
40
+ parser.add_argument(
41
+ "-V",
42
+ "--version",
43
+ action="version",
44
+ version=f"%(prog)s {mapfile_parser.__version__}",
45
+ )
46
+
47
+ subparsers = parser.add_subparsers(
48
+ description="action", help="the action to perform", required=True
49
+ )
50
+
51
+ mapfile_parser.frontends.bss_check.addSubparser(subparsers, decompConfig)
52
+ mapfile_parser.frontends.first_diff.addSubparser(subparsers, decompConfig)
53
+ mapfile_parser.frontends.jsonify.addSubparser(subparsers, decompConfig)
54
+ mapfile_parser.frontends.objdiff_report.addSubparser(subparsers, decompConfig)
55
+ mapfile_parser.frontends.pj64_syms.addSubparser(subparsers, decompConfig)
56
+ mapfile_parser.frontends.progress.addSubparser(subparsers, decompConfig)
57
+ mapfile_parser.frontends.sym_info.addSubparser(subparsers, decompConfig)
58
+ mapfile_parser.frontends.symbol_sizes_csv.addSubparser(subparsers, decompConfig)
59
+ mapfile_parser.frontends.upload_frogress.addSubparser(subparsers, decompConfig)
60
+
61
+ args = parser.parse_args()
62
+ args.func(args, decompConfig)
63
+
64
+
65
+ if __name__ == "__main__":
66
+ mapfileParserMain()
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # SPDX-FileCopyrightText: © 2022-2024 Decompollaborate
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ from __future__ import annotations
7
+
8
+
9
+ from . import bss_check as bss_check
10
+ from . import first_diff as first_diff
11
+ from . import jsonify as jsonify
12
+ from . import objdiff_report as objdiff_report
13
+ from . import pj64_syms as pj64_syms
14
+ from . import progress as progress
15
+ from . import sym_info as sym_info
16
+ from . import symbol_sizes_csv as symbol_sizes_csv
17
+ from . import upload_frogress as upload_frogress
@@ -0,0 +1,318 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # SPDX-FileCopyrightText: © 2023-2025 Decompollaborate
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ from collections.abc import Callable
10
+ import decomp_settings
11
+ from pathlib import Path
12
+
13
+ from .. import mapfile
14
+ from .. import utils
15
+
16
+
17
+ def getComparison(
18
+ mapPath: Path,
19
+ expectedMapPath: Path,
20
+ *,
21
+ reverseCheck: bool = True,
22
+ plfResolver: Callable[[Path], Path | None] | None = None,
23
+ plfResolverExpected: Callable[[Path], Path | None] | None = None,
24
+ ) -> mapfile.MapsComparisonInfo:
25
+ buildMap = mapfile.MapFile.newFromMapFile(mapPath)
26
+ buildMap = buildMap.filterBySectionType(".bss")
27
+ if plfResolver is not None:
28
+ buildMap = buildMap.resolvePartiallyLinkedFiles(plfResolver)
29
+
30
+ expectedMap = mapfile.MapFile.newFromMapFile(expectedMapPath)
31
+ expectedMap = expectedMap.filterBySectionType(".bss")
32
+ if plfResolverExpected is not None:
33
+ expectedMap = expectedMap.resolvePartiallyLinkedFiles(plfResolverExpected)
34
+
35
+ return buildMap.compareFilesAndSymbols(expectedMap, checkOtherOnSelf=reverseCheck)
36
+
37
+
38
+ def printSymbolComparisonAsCsv(
39
+ comparisonInfo: mapfile.MapsComparisonInfo,
40
+ printAll: bool = False,
41
+ printGoods: bool = True,
42
+ ):
43
+ print(
44
+ "Symbol Name,Build Address,Build File,Expected Address,Expected File,Difference,GOOD/BAD/MISSING"
45
+ )
46
+
47
+ # If it's bad or missing, don't need to do anything special.
48
+ # If it's good, check for if it's in a file with bad or missing stuff, and check if print all is on. If none of these, print it.
49
+
50
+ for symbolInfo in comparisonInfo.comparedList:
51
+ buildFile = symbolInfo.buildFile
52
+ expectedFile = symbolInfo.expectedFile
53
+ buildFilePath = buildFile.filepath if buildFile is not None else None
54
+ expectedFilePath = expectedFile.filepath if expectedFile is not None else None
55
+
56
+ if symbolInfo.diff is None:
57
+ print(
58
+ f"{symbolInfo.symbol.name},{symbolInfo.buildAddress:X},{buildFilePath},{symbolInfo.expectedAddress:X},{expectedFilePath},{symbolInfo.diff},MISSING"
59
+ )
60
+ continue
61
+
62
+ symbolState = "BAD"
63
+ if symbolInfo.diff == 0:
64
+ symbolState = "GOOD"
65
+ if (
66
+ buildFile not in comparisonInfo.badFiles
67
+ and expectedFile not in comparisonInfo.badFiles
68
+ ):
69
+ if (
70
+ buildFile not in comparisonInfo.badFiles
71
+ and expectedFile not in comparisonInfo.badFiles
72
+ ):
73
+ if not printAll:
74
+ continue
75
+
76
+ if not printGoods and symbolState == "GOOD":
77
+ continue
78
+
79
+ if buildFile != expectedFile:
80
+ symbolState += " MOVED"
81
+ print(
82
+ f"{symbolInfo.symbol.name},{symbolInfo.buildAddress:X},{buildFilePath},{symbolInfo.expectedAddress:X},{expectedFilePath},{symbolInfo.diff:X},{symbolState}"
83
+ )
84
+
85
+
86
+ def printSymbolComparisonAsListing(
87
+ comparisonInfo: mapfile.MapsComparisonInfo,
88
+ printAll: bool = False,
89
+ printGoods: bool = True,
90
+ ):
91
+ # print("Symbol Name,Build Address,Build File,Expected Address,Expected File,Difference,GOOD/BAD/MISSING")
92
+
93
+ # If it's bad or missing, don't need to do anything special.
94
+ # If it's good, check for if it's in a file with bad or missing stuff, and check if print all is on. If none of these, print it.
95
+
96
+ for symbolInfo in comparisonInfo.comparedList:
97
+ buildFile = symbolInfo.buildFile
98
+ expectedFile = symbolInfo.expectedFile
99
+ buildFilePath = buildFile.filepath if buildFile is not None else None
100
+ expectedFilePath = expectedFile.filepath if expectedFile is not None else None
101
+
102
+ if symbolInfo.diff is None:
103
+ print(f"Symbol: {symbolInfo.symbol.name} (MISSING)")
104
+ if symbolInfo.buildAddress != -1:
105
+ print(
106
+ f" Build: 0x{symbolInfo.buildAddress:08X} (file: {buildFilePath})"
107
+ )
108
+ if symbolInfo.expectedAddress != -1:
109
+ print(
110
+ f" Expected: 0x{symbolInfo.expectedAddress:08X} (file: {expectedFilePath})"
111
+ )
112
+ continue
113
+
114
+ symbolState = "BAD"
115
+ if symbolInfo.diff == 0:
116
+ symbolState = "GOOD"
117
+ if (
118
+ buildFile not in comparisonInfo.badFiles
119
+ and expectedFile not in comparisonInfo.badFiles
120
+ ):
121
+ if (
122
+ buildFile not in comparisonInfo.badFiles
123
+ and expectedFile not in comparisonInfo.badFiles
124
+ ):
125
+ if not printAll:
126
+ continue
127
+
128
+ if not printGoods and symbolState == "GOOD":
129
+ continue
130
+
131
+ if buildFile != expectedFile:
132
+ symbolState += " MOVED"
133
+
134
+ if symbolInfo.diff < 0:
135
+ diffStr = f"-0x{-symbolInfo.diff:02X}"
136
+ else:
137
+ diffStr = f"0x{symbolInfo.diff:02X}"
138
+
139
+ print(f"Symbol: {symbolInfo.symbol.name} ({symbolState}) (diff: {diffStr})")
140
+ print(
141
+ f" Build: 0x{symbolInfo.buildAddress:08X} (file: {buildFilePath})"
142
+ )
143
+ print(
144
+ f" Expected: 0x{symbolInfo.expectedAddress:08X} (file: {expectedFilePath})"
145
+ )
146
+
147
+
148
+ def printSymbolComparison(
149
+ comparisonInfo: mapfile.MapsComparisonInfo,
150
+ printAll: bool = False,
151
+ printGoods: bool = True,
152
+ printingStyle: str = "csv",
153
+ ):
154
+ if printingStyle == "csv":
155
+ printSymbolComparisonAsCsv(comparisonInfo, printAll, printGoods)
156
+ elif printingStyle == "listing":
157
+ printSymbolComparisonAsListing(comparisonInfo, printAll, printGoods)
158
+ else:
159
+ printSymbolComparisonAsListing(comparisonInfo, printAll, printGoods)
160
+
161
+
162
+ def printFileComparison(comparisonInfo: mapfile.MapsComparisonInfo):
163
+ utils.eprint("")
164
+
165
+ if len(comparisonInfo.badFiles) != 0:
166
+ utils.eprint(" BAD")
167
+
168
+ for file in comparisonInfo.badFiles:
169
+ utils.eprint(f"bss reordering in {file.filepath}")
170
+ utils.eprint("")
171
+
172
+ if len(comparisonInfo.missingFiles) != 0:
173
+ utils.eprint(" MISSING")
174
+
175
+ for file in comparisonInfo.missingFiles:
176
+ utils.eprint(f"Symbols missing from {file.filepath}")
177
+ utils.eprint("")
178
+
179
+ utils.eprint(
180
+ "Some files appear to be missing symbols. Have they been renamed or declared as static? You may need to remake 'expected'"
181
+ )
182
+
183
+
184
+ def doBssCheck(
185
+ mapPath: Path,
186
+ expectedMapPath: Path,
187
+ *,
188
+ printAll: bool = False,
189
+ reverseCheck: bool = True,
190
+ plfResolver: Callable[[Path], Path | None] | None = None,
191
+ plfResolverExpected: Callable[[Path], Path | None] | None = None,
192
+ ) -> int:
193
+ if not mapPath.exists():
194
+ utils.eprint(f"{mapPath} must exist")
195
+ return 1
196
+ if not expectedMapPath.exists():
197
+ utils.eprint(f"{expectedMapPath} must exist")
198
+ return 1
199
+
200
+ comparisonInfo = getComparison(
201
+ mapPath,
202
+ expectedMapPath,
203
+ reverseCheck=reverseCheck,
204
+ plfResolver=plfResolver,
205
+ plfResolverExpected=plfResolverExpected,
206
+ )
207
+ printSymbolComparison(comparisonInfo, printAll)
208
+
209
+ if len(comparisonInfo.badFiles) + len(comparisonInfo.missingFiles) != 0:
210
+ printFileComparison(comparisonInfo)
211
+ return 1
212
+
213
+ utils.eprint("")
214
+ utils.eprint(" GOOD")
215
+
216
+ return 0
217
+
218
+
219
+ def processArguments(
220
+ args: argparse.Namespace, decompConfig: decomp_settings.Config | None = None
221
+ ):
222
+ if decompConfig is not None:
223
+ version = decompConfig.get_version_by_name(args.version)
224
+ assert version is not None, f"Invalid version '{args.version}' selected"
225
+
226
+ mapPath = Path(version.paths.map)
227
+
228
+ expectedDir = version.paths.expected_dir
229
+ if expectedDir is not None:
230
+ expectedMapPath = expectedDir / mapPath
231
+ else:
232
+ expectedMapPath = args.expectedmap
233
+ else:
234
+ mapPath = args.mapfile
235
+ expectedMapPath = args.expectedmap
236
+
237
+ printAll: bool = args.print_all
238
+ reverseCheck: bool = not args.no_reverse_check
239
+ plfExt: list[str] | None = args.plf_ext
240
+
241
+ plfResolver = None
242
+ if plfExt is not None:
243
+
244
+ def resolver(x: Path) -> Path | None:
245
+ if x.suffix in plfExt:
246
+ newPath = x.with_suffix(".map")
247
+ if newPath.exists():
248
+ return newPath
249
+ return None
250
+
251
+ plfResolver = resolver
252
+
253
+ exit(
254
+ doBssCheck(
255
+ mapPath,
256
+ expectedMapPath,
257
+ printAll=printAll,
258
+ reverseCheck=reverseCheck,
259
+ plfResolver=plfResolver,
260
+ )
261
+ )
262
+
263
+
264
+ def addSubparser(
265
+ subparser: argparse._SubParsersAction[argparse.ArgumentParser],
266
+ decompConfig: decomp_settings.Config | None = None,
267
+ ):
268
+ parser = subparser.add_parser(
269
+ "bss_check", help="Check that globally visible bss has not been reordered."
270
+ )
271
+
272
+ emitMapfile = True
273
+ emitExpected = True
274
+ if decompConfig is not None:
275
+ versions = []
276
+ for version in decompConfig.versions:
277
+ versions.append(version.name)
278
+
279
+ if len(versions) > 0:
280
+ parser.add_argument(
281
+ "-v",
282
+ "--version",
283
+ help="Version to process from the decomp.yaml file",
284
+ type=str,
285
+ choices=versions,
286
+ default=versions[0],
287
+ )
288
+ emitMapfile = False
289
+ if decompConfig.versions[0].paths.expected_dir is not None:
290
+ emitExpected = False
291
+
292
+ if emitMapfile:
293
+ parser.add_argument("mapfile", help="Path to a map file.", type=Path)
294
+ if emitExpected:
295
+ parser.add_argument(
296
+ "expectedmap", help="Path to the map file in the expected dir.", type=Path
297
+ )
298
+
299
+ parser.add_argument(
300
+ "-a",
301
+ "--print-all",
302
+ help="Print all bss, not just non-matching.",
303
+ action="store_true",
304
+ )
305
+ parser.add_argument(
306
+ "--no-reverse-check",
307
+ help="Disable looking for symbols on the expected map that are missing on the built map file.",
308
+ action="store_true",
309
+ )
310
+
311
+ parser.add_argument(
312
+ "-x",
313
+ "--plf-ext",
314
+ help="File extension for partially linked files (plf). Will be used to transform the `plf`s path into a mapfile path by replacing the extension. The extension must contain the leading period. This argument can be passed multiple times.",
315
+ action="append",
316
+ )
317
+
318
+ parser.set_defaults(func=processArguments)