mapfile-parser 2.7.2__cp39-cp39-win32.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,24 @@
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, 7, 2)
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 File as File
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
+
22
+ from .progress_stats import ProgressStats as ProgressStats
23
+
24
+ from . import frontends as frontends
@@ -0,0 +1,34 @@
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
+
10
+ import mapfile_parser
11
+
12
+
13
+ def mapfileParserMain():
14
+ parser = argparse.ArgumentParser(description="Interface to call any of the mapfile_parser's CLI utilities", prog="mapfile_parser")
15
+
16
+ parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {mapfile_parser.__version__}")
17
+
18
+ subparsers = parser.add_subparsers(description="action", help="the action to perform", required=True)
19
+
20
+ mapfile_parser.frontends.bss_check.addSubparser(subparsers)
21
+ mapfile_parser.frontends.first_diff.addSubparser(subparsers)
22
+ mapfile_parser.frontends.jsonify.addSubparser(subparsers)
23
+ mapfile_parser.frontends.pj64_syms.addSubparser(subparsers)
24
+ mapfile_parser.frontends.progress.addSubparser(subparsers)
25
+ mapfile_parser.frontends.sym_info.addSubparser(subparsers)
26
+ mapfile_parser.frontends.symbol_sizes_csv.addSubparser(subparsers)
27
+ mapfile_parser.frontends.upload_frogress.addSubparser(subparsers)
28
+
29
+ args = parser.parse_args()
30
+ args.func(args)
31
+
32
+
33
+ if __name__ == "__main__":
34
+ mapfileParserMain()
@@ -0,0 +1,16 @@
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 pj64_syms as pj64_syms
13
+ from . import progress as progress
14
+ from . import sym_info as sym_info
15
+ from . import symbol_sizes_csv as symbol_sizes_csv
16
+ from . import upload_frogress as upload_frogress
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # SPDX-FileCopyrightText: © 2023-2024 Decompollaborate
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ from pathlib import Path
10
+ import sys
11
+
12
+ from .. import mapfile
13
+ from .. import utils
14
+
15
+
16
+ def getComparison(mapPath: Path, expectedMapPath: Path, *, reverseCheck: bool=True) -> mapfile.MapsComparisonInfo:
17
+ buildMap = mapfile.MapFile()
18
+ buildMap.readMapFile(mapPath)
19
+ buildMap = buildMap.filterBySectionType(".bss")
20
+
21
+ expectedMap = mapfile.MapFile()
22
+ expectedMap.readMapFile(expectedMapPath)
23
+ expectedMap = expectedMap.filterBySectionType(".bss")
24
+
25
+ return buildMap.compareFilesAndSymbols(expectedMap, checkOtherOnSelf=reverseCheck)
26
+
27
+ def printSymbolComparisonAsCsv(comparisonInfo: mapfile.MapsComparisonInfo, printAll: bool=False, printGoods: bool=True):
28
+ print("Symbol Name,Build Address,Build File,Expected Address,Expected File,Difference,GOOD/BAD/MISSING")
29
+
30
+ # If it's bad or missing, don't need to do anything special.
31
+ # 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.
32
+
33
+ for symbolInfo in comparisonInfo.comparedList:
34
+ buildFile = symbolInfo.buildFile
35
+ expectedFile = symbolInfo.expectedFile
36
+ buildFilePath = buildFile.filepath if buildFile is not None else None
37
+ expectedFilePath = expectedFile.filepath if expectedFile is not None else None
38
+
39
+ if symbolInfo.diff is None:
40
+ print(f"{symbolInfo.symbol.name},{symbolInfo.buildAddress:X},{buildFilePath},{symbolInfo.expectedAddress:X},{expectedFilePath},{symbolInfo.diff},MISSING")
41
+ continue
42
+
43
+ symbolState = "BAD"
44
+ if symbolInfo.diff == 0:
45
+ symbolState = "GOOD"
46
+ if not buildFile in comparisonInfo.badFiles and not expectedFile in comparisonInfo.badFiles:
47
+ if not buildFile in comparisonInfo.badFiles and not expectedFile in comparisonInfo.badFiles:
48
+ if not printAll:
49
+ continue
50
+
51
+ if not printGoods and symbolState == "GOOD":
52
+ continue
53
+
54
+ if buildFile != expectedFile:
55
+ symbolState += " MOVED"
56
+ print(f"{symbolInfo.symbol.name},{symbolInfo.buildAddress:X},{buildFilePath},{symbolInfo.expectedAddress:X},{expectedFilePath},{symbolInfo.diff:X},{symbolState}")
57
+
58
+ def printSymbolComparisonAsListing(comparisonInfo: mapfile.MapsComparisonInfo, printAll: bool=False, printGoods: bool=True):
59
+ # print("Symbol Name,Build Address,Build File,Expected Address,Expected File,Difference,GOOD/BAD/MISSING")
60
+
61
+ # If it's bad or missing, don't need to do anything special.
62
+ # 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.
63
+
64
+ for symbolInfo in comparisonInfo.comparedList:
65
+ buildFile = symbolInfo.buildFile
66
+ expectedFile = symbolInfo.expectedFile
67
+ buildFilePath = buildFile.filepath if buildFile is not None else None
68
+ expectedFilePath = expectedFile.filepath if expectedFile is not None else None
69
+
70
+ if symbolInfo.diff is None:
71
+ print(f"Symbol: {symbolInfo.symbol.name} (MISSING)")
72
+ if symbolInfo.buildAddress != -1:
73
+ print(f" Build: 0x{symbolInfo.buildAddress:08X} (file: {buildFilePath})")
74
+ if symbolInfo.expectedAddress != -1:
75
+ print(f" Expected: 0x{symbolInfo.expectedAddress:08X} (file: {expectedFilePath})")
76
+ continue
77
+
78
+ symbolState = "BAD"
79
+ if symbolInfo.diff == 0:
80
+ symbolState = "GOOD"
81
+ if not buildFile in comparisonInfo.badFiles and not expectedFile in comparisonInfo.badFiles:
82
+ if not buildFile in comparisonInfo.badFiles and not expectedFile in comparisonInfo.badFiles:
83
+ if not printAll:
84
+ continue
85
+
86
+ if not printGoods and symbolState == "GOOD":
87
+ continue
88
+
89
+ if buildFile != expectedFile:
90
+ symbolState += " MOVED"
91
+
92
+ if symbolInfo.diff < 0:
93
+ diffStr = f"-0x{-symbolInfo.diff:02X}"
94
+ else:
95
+ diffStr = f"0x{symbolInfo.diff:02X}"
96
+
97
+ print(f"Symbol: {symbolInfo.symbol.name} ({symbolState}) (diff: {diffStr})")
98
+ print(f" Build: 0x{symbolInfo.buildAddress:08X} (file: {buildFilePath})")
99
+ print(f" Expected: 0x{symbolInfo.expectedAddress:08X} (file: {expectedFilePath})")
100
+
101
+ def printSymbolComparison(comparisonInfo: mapfile.MapsComparisonInfo, printAll: bool=False, printGoods: bool=True, printingStyle: str="csv"):
102
+ if printingStyle == "csv":
103
+ printSymbolComparisonAsCsv(comparisonInfo, printAll, printGoods)
104
+ elif printingStyle == "listing":
105
+ printSymbolComparisonAsListing(comparisonInfo, printAll, printGoods)
106
+ else:
107
+ printSymbolComparisonAsListing(comparisonInfo, printAll, printGoods)
108
+
109
+ def printFileComparison(comparisonInfo: mapfile.MapsComparisonInfo):
110
+ utils.eprint("")
111
+
112
+ if len(comparisonInfo.badFiles) != 0:
113
+ utils.eprint(" BAD")
114
+
115
+ for file in comparisonInfo.badFiles:
116
+ utils.eprint(f"bss reordering in {file.filepath}")
117
+ utils.eprint("")
118
+
119
+ if len(comparisonInfo.missingFiles) != 0:
120
+ utils.eprint(" MISSING")
121
+
122
+ for file in comparisonInfo.missingFiles:
123
+ utils.eprint(f"Symbols missing from {file.filepath}")
124
+ utils.eprint("")
125
+
126
+ utils.eprint("Some files appear to be missing symbols. Have they been renamed or declared as static? You may need to remake 'expected'")
127
+
128
+
129
+ def doBssCheck(mapPath: Path, expectedMapPath: Path, *, printAll: bool=False, reverseCheck: bool=True) -> int:
130
+ if not mapPath.exists():
131
+ utils.eprint(f"{mapPath} must exist")
132
+ return 1
133
+ if not expectedMapPath.exists():
134
+ utils.eprint(f"{expectedMapPath} must exist")
135
+ return 1
136
+
137
+ comparisonInfo = getComparison(mapPath, expectedMapPath, reverseCheck=reverseCheck)
138
+ printSymbolComparison(comparisonInfo, printAll)
139
+
140
+ if len(comparisonInfo.badFiles) + len(comparisonInfo.missingFiles) != 0:
141
+ printFileComparison(comparisonInfo)
142
+ return 1
143
+
144
+ utils.eprint("")
145
+ utils.eprint(" GOOD")
146
+
147
+ return 0
148
+
149
+
150
+ def processArguments(args: argparse.Namespace):
151
+ mapPath: Path = args.mapfile
152
+ expectedMapPath: Path = args.expectedmap
153
+
154
+ printAll: bool = args.print_all
155
+ reverseCheck: bool = not args.no_reverse_check
156
+
157
+ exit(doBssCheck(mapPath, expectedMapPath, printAll=printAll, reverseCheck=reverseCheck))
158
+
159
+
160
+ def addSubparser(subparser: argparse._SubParsersAction[argparse.ArgumentParser]):
161
+ parser = subparser.add_parser("bss_check", help="Check that globally visible bss has not been reordered.")
162
+
163
+ parser.add_argument("mapfile", help="Path to a map file", type=Path)
164
+ parser.add_argument("expectedmap", help="Path to the map file in the expected dir", type=Path)
165
+
166
+ parser.add_argument("-a", "--print-all", help="Print all bss, not just non-matching.", action="store_true")
167
+ parser.add_argument("--no-reverse-check", help="Disable looking for symbols on the expected map that are missing on the built map file.", action="store_true")
168
+
169
+ parser.set_defaults(func=processArguments)
@@ -0,0 +1,168 @@
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
+ from pathlib import Path
10
+ from typing import Callable, Literal
11
+
12
+ from .. import mapfile
13
+ from .. import utils
14
+
15
+
16
+ def doFirstDiff(mapPath: Path, expectedMapPath: Path, romPath: Path, expectedRomPath: Path, diffCount: int=5, mismatchSize: bool=False, addColons: bool=True, bytesConverterCallback:Callable[[bytes, mapfile.MapFile],str|None]|None=None, endian: Literal["big", "little"] ="big") -> int:
17
+ if not mapPath.exists():
18
+ print(f"{mapPath} must exist")
19
+ return 1
20
+ if not expectedMapPath.exists():
21
+ print(f"{expectedMapPath} must exist")
22
+ return 1
23
+ if not romPath.exists():
24
+ print(f"{romPath} must exist")
25
+ return 1
26
+ if not expectedRomPath.exists():
27
+ print(f"{expectedRomPath} must exist")
28
+ return 1
29
+
30
+ builtRom = utils.readFileAsBytearray(romPath)
31
+ expectedRom = utils.readFileAsBytearray(expectedRomPath)
32
+
33
+ if len(builtRom) != len(expectedRom):
34
+ print("Modified ROM has different size...")
35
+ print(f"It should be 0x{len(expectedRom):X} but it is 0x{len(builtRom):X}")
36
+ if not mismatchSize:
37
+ return 1
38
+
39
+ if builtRom == expectedRom:
40
+ print("No differences!")
41
+ return 0
42
+
43
+ builtMapFile = mapfile.MapFile()
44
+ builtMapFile.readMapFile(mapPath)
45
+ expectedMapFile = mapfile.MapFile()
46
+ expectedMapFile.readMapFile(expectedMapPath)
47
+
48
+ endian_diff = 0
49
+ if endian == "little":
50
+ endian_diff = 3
51
+
52
+ map_search_diff: set[str] = set()
53
+ diffs = 0
54
+ shift_cap = 1000
55
+ for i in range(24, min(len(builtRom), len(expectedRom)), 4):
56
+ # (builtRom[i:i+4] != expectedRom[i:i+4], but that's slightly slower in CPython...)
57
+ if diffs <= shift_cap and (
58
+ builtRom[i] != expectedRom[i]
59
+ or builtRom[i + 1] != expectedRom[i + 1]
60
+ or builtRom[i + 2] != expectedRom[i + 2]
61
+ or builtRom[i + 3] != expectedRom[i + 3]
62
+ ):
63
+ if diffs == 0:
64
+ vromInfo, possibleFiles = builtMapFile.findSymbolByVrom(i)
65
+ extraMessage = ""
66
+ if vromInfo is not None:
67
+ extraMessage = f", {vromInfo.getAsStrPlusOffset()}"
68
+ elif len(possibleFiles) > 0:
69
+ extraMessage = f", in file {possibleFiles[0].asStr()}"
70
+ print(f"First difference at ROM addr 0x{i:X}{extraMessage}")
71
+ builtBytes = builtRom[i : i + 4]
72
+ expectedBytes = expectedRom[i : i + 4]
73
+ print(f"Bytes: {utils.hexbytes(builtBytes, addColons=addColons)} vs {utils.hexbytes(expectedBytes, addColons=addColons)}")
74
+ if bytesConverterCallback is not None:
75
+ builtConverted = bytesConverterCallback(builtBytes, builtMapFile)
76
+ expectedConverted = bytesConverterCallback(expectedBytes, expectedMapFile)
77
+ if builtConverted is not None and expectedConverted is not None:
78
+ print(f"{builtConverted} vs {expectedConverted}")
79
+ diffs += 1
80
+
81
+ if (
82
+ len(map_search_diff) < diffCount
83
+ and builtRom[i+endian_diff] >> 2 != expectedRom[i+endian_diff] >> 2
84
+ ):
85
+ vromInfo, possibleFiles = builtMapFile.findSymbolByVrom(i)
86
+ if vromInfo is not None:
87
+ vromMessage = vromInfo.getAsStr()
88
+ if vromMessage not in map_search_diff:
89
+ map_search_diff.add(vromMessage)
90
+
91
+ extraMessage = f", {vromInfo.getAsStrPlusOffset()}"
92
+ print(f"Instruction difference at ROM addr 0x{i:X}{extraMessage}")
93
+ builtBytes = builtRom[i : i + 4]
94
+ expectedBytes = expectedRom[i : i + 4]
95
+ print(f"Bytes: {utils.hexbytes(builtBytes, addColons=addColons)} vs {utils.hexbytes(expectedBytes, addColons=addColons)}")
96
+ if bytesConverterCallback is not None:
97
+ builtConverted = bytesConverterCallback(builtBytes, builtMapFile)
98
+ expectedConverted = bytesConverterCallback(expectedBytes, expectedMapFile)
99
+ if builtConverted is not None and expectedConverted is not None:
100
+ print(f"{builtConverted} vs {expectedConverted}")
101
+ elif len(possibleFiles) > 0:
102
+ extraMessage = f", in file {possibleFiles[0].asStr()}"
103
+ print(f"Instruction difference at ROM addr 0x{i:X}{extraMessage}")
104
+ builtBytes = builtRom[i : i + 4]
105
+ expectedBytes = expectedRom[i : i + 4]
106
+ print(f"Bytes: {utils.hexbytes(builtBytes, addColons=addColons)} vs {utils.hexbytes(expectedBytes, addColons=addColons)}")
107
+ if bytesConverterCallback is not None:
108
+ builtConverted = bytesConverterCallback(builtBytes, builtMapFile)
109
+ expectedConverted = bytesConverterCallback(expectedBytes, expectedMapFile)
110
+ if builtConverted is not None and expectedConverted is not None:
111
+ print(f"{builtConverted} vs {expectedConverted}")
112
+
113
+ if len(map_search_diff) >= diffCount and diffs > shift_cap:
114
+ break
115
+
116
+ if diffs == 0:
117
+ print("No differences but ROMs differ?")
118
+ return 1
119
+
120
+ print()
121
+ definite_shift = diffs > shift_cap
122
+ if definite_shift:
123
+ print(f"Over {shift_cap} differing words, must be a shifted ROM.")
124
+ else:
125
+ print(f"{diffs} differing word(s).")
126
+
127
+ if diffs > 100:
128
+ firstDifferingSym = builtMapFile.findLowestDifferingSymbol(expectedMapFile)
129
+ if firstDifferingSym is None:
130
+ print(f"No ROM shift{' (!?)' if definite_shift else ''}")
131
+ else:
132
+ sym, file, prevSym = firstDifferingSym
133
+ extraMessage = ""
134
+ if prevSym is not None:
135
+ extraMessage = f" -- in {prevSym.name}?"
136
+ print(f"Map appears to have shifted just before {sym.name} ({file.filepath}){extraMessage}")
137
+ return 1
138
+
139
+ return 0
140
+
141
+
142
+ def processArguments(args: argparse.Namespace):
143
+ mapPath: Path = args.mapfile
144
+ expectedMapPath: Path = args.expectedmap
145
+ romPath: Path = args.rompath
146
+ expectedRomPath: Path = args.expectedrom
147
+
148
+ diffCount: int = args.count
149
+ mismatchSize: bool = args.mismatch_size
150
+
151
+ endian = args.endian
152
+
153
+ exit(doFirstDiff(mapPath, expectedMapPath, romPath, expectedRomPath, diffCount, mismatchSize, endian=endian))
154
+
155
+
156
+ def addSubparser(subparser: argparse._SubParsersAction[argparse.ArgumentParser]):
157
+ parser = subparser.add_parser("first_diff", help="Find the first difference(s) between the built ROM and the base ROM.")
158
+
159
+ parser.add_argument("mapfile", help="Path to a map file", type=Path)
160
+ parser.add_argument("expectedmap", help="Path to the map file in the expected dir", type=Path)
161
+ parser.add_argument("rompath", help="Path to built ROM", type=Path)
162
+ parser.add_argument("expectedrom", help="Path to the expected ROM", type=Path)
163
+
164
+ parser.add_argument("-c", "--count", type=int, default=5, help="find up to this many instruction difference(s)")
165
+ parser.add_argument("-m", "--mismatch-size", help="Do not exit early if the ROM sizes does not match", action="store_true")
166
+ parser.add_argument("-e", "--endian", help="Specify endianness of the binary", choices=["big", "little"], default="big")
167
+
168
+ parser.set_defaults(func=processArguments)
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # SPDX-FileCopyrightText: © 2023-2024 Decompollaborate
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ import json
10
+ from pathlib import Path
11
+
12
+ from .. import mapfile
13
+
14
+
15
+ def doJsonify(mapPath: Path, outputPath: Path|None, humanReadable: bool=True, applyFixes: bool=False) -> int:
16
+ if not mapPath.exists():
17
+ print(f"Could not find mapfile at '{mapPath}'")
18
+ return 1
19
+
20
+ mapFile = mapfile.MapFile()
21
+ mapFile.readMapFile(mapPath)
22
+ if applyFixes:
23
+ mapFile = mapFile.fixupNonMatchingSymbols()
24
+
25
+ jsonStr = json.dumps(mapFile.toJson(humanReadable=humanReadable), indent=4)
26
+
27
+ if outputPath is None:
28
+ print(jsonStr)
29
+ else:
30
+ outputPath.parent.mkdir(parents=True, exist_ok=True)
31
+ outputPath.write_text(jsonStr)
32
+
33
+ return 0
34
+
35
+
36
+ def processArguments(args: argparse.Namespace):
37
+ mapPath: Path = args.mapfile
38
+ outputPath: Path|None = Path(args.output) if args.output is not None else None
39
+ machine: bool = args.machine
40
+ applyFixes: bool = args.apply_fixes
41
+
42
+ exit(doJsonify(mapPath, outputPath, humanReadable=not machine, applyFixes=applyFixes))
43
+
44
+ def addSubparser(subparser: argparse._SubParsersAction[argparse.ArgumentParser]):
45
+ parser = subparser.add_parser("jsonify", help="Converts a mapfile into a json format.")
46
+
47
+ parser.add_argument("mapfile", help="Path to a map file", type=Path)
48
+ parser.add_argument("-o", "--output", help="Output path of for the generated json. If omitted then stdout is used instead.")
49
+ parser.add_argument("-m", "--machine", help="Emit numbers as numbers instead of outputting them as pretty strings.", action="store_true")
50
+ parser.add_argument("-f", "--apply-fixes", help="Apply certain fixups, like fixing size calculation of because of the existence of fake `.NON_MATCHING` symbols.", action="store_true")
51
+
52
+ parser.set_defaults(func=processArguments)
@@ -0,0 +1,51 @@
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
+ from pathlib import Path
10
+ from typing import TextIO
11
+ import sys
12
+
13
+ from .. import mapfile
14
+
15
+
16
+ def writePj64SymsToFile(mapFile: mapfile.MapFile, outFile: TextIO):
17
+ for segment in mapFile:
18
+ for file in segment:
19
+ for sym in file:
20
+ symType = "code" if file.sectionType == ".text" else "data"
21
+ outFile.write(f"{sym.vram:08X},{symType},{sym.name}\n")
22
+
23
+ def doPj64Syms(mapPath: Path, outputPath: Path|None) -> int:
24
+ if not mapPath.exists():
25
+ print(f"Could not find mapfile at '{mapPath}'")
26
+ return 1
27
+
28
+ mapFile = mapfile.MapFile()
29
+ mapFile.readMapFile(mapPath)
30
+
31
+ if outputPath is None:
32
+ writePj64SymsToFile(mapFile, sys.stdout)
33
+ else:
34
+ with outputPath.open("w") as outFile:
35
+ writePj64SymsToFile(mapFile, outFile)
36
+
37
+ return 0
38
+
39
+ def processArguments(args: argparse.Namespace):
40
+ mapPath: Path = args.mapfile
41
+ outputPath: Path = args.output
42
+
43
+ exit(doPj64Syms(mapPath, outputPath))
44
+
45
+ def addSubparser(subparser: argparse._SubParsersAction[argparse.ArgumentParser]):
46
+ parser = subparser.add_parser("pj64_syms", help="Produce a PJ64 compatible symbol map.")
47
+
48
+ parser.add_argument("mapfile", help="Path to a map file", type=Path)
49
+ parser.add_argument("output", help="Path to output file. If omitted then output will be written to stdout", type=Path, nargs="?")
50
+
51
+ parser.set_defaults(func=processArguments)
@@ -0,0 +1,65 @@
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 json
10
+ from pathlib import Path
11
+
12
+ from .. import mapfile
13
+ from .. import progress_stats
14
+
15
+
16
+ def getProgress(mapPath: Path, asmPath: Path, nonmatchingsPath: Path, pathIndex: int=2, checkFunctionFiles: bool=True, debugging: bool=False) -> tuple[progress_stats.ProgressStats, dict[str, progress_stats.ProgressStats]]:
17
+ mapFile = mapfile.MapFile()
18
+
19
+ mapFile.debugging = debugging
20
+ mapFile.readMapFile(mapPath)
21
+
22
+ return mapFile.filterBySectionType(".text").fixupNonMatchingSymbols().getProgress(asmPath, nonmatchingsPath, pathIndex=pathIndex, checkFunctionFiles=checkFunctionFiles)
23
+
24
+ def doProgress(mapPath: Path, asmPath: Path, nonmatchingsPath: Path, pathIndex: int=2, checkFunctionFiles: bool=True, print_json: bool=False, debugging: bool=False) -> int:
25
+ if not mapPath.exists():
26
+ print(f"Could not find mapfile at '{mapPath}'")
27
+ return 1
28
+
29
+ totalStats, progressPerFolder = getProgress(mapPath, asmPath, nonmatchingsPath, pathIndex=pathIndex, checkFunctionFiles=checkFunctionFiles, debugging=debugging)
30
+
31
+ if print_json:
32
+ json_temp: dict[str, dict[str, int|float]] = {
33
+ "all": totalStats.asJsonEntry()
34
+ }
35
+ for folder, statsEntry in progressPerFolder.items():
36
+ json_temp[folder] = statsEntry.asJsonEntry()
37
+ print(json.dumps(json_temp, indent=4))
38
+ else:
39
+ progress_stats.printStats(totalStats, progressPerFolder)
40
+ return 0
41
+
42
+
43
+ def processArguments(args: argparse.Namespace):
44
+ mapPath: Path = args.mapfile
45
+ asmPath: Path = args.asmpath
46
+ nonmatchingsPath: Path = args.nonmatchingspath
47
+ pathIndex: int = args.path_index
48
+ checkFunctionFiles: bool = not args.avoid_function_files
49
+ debugging: bool = args.debugging #! @deprecated
50
+ print_json: bool = args.json
51
+
52
+ exit(doProgress(mapPath, asmPath, nonmatchingsPath, pathIndex=pathIndex, checkFunctionFiles=checkFunctionFiles, print_json=print_json, debugging=debugging))
53
+
54
+ def addSubparser(subparser: argparse._SubParsersAction[argparse.ArgumentParser]):
55
+ parser = subparser.add_parser("progress", help="Computes current progress of the matched functions. Relies on a splat (https://github.com/ethteck/splat) folder structure and matched functions not longer having a file.")
56
+
57
+ parser.add_argument("mapfile", help="Path to a map file", type=Path)
58
+ parser.add_argument("asmpath", help="Path to asm folder", type=Path)
59
+ parser.add_argument("nonmatchingspath", help="Path to nonmatchings folder", type=Path)
60
+ parser.add_argument("-i", "--path-index", help="Specify the index to start reading the file paths. Defaults to 2", type=int, default=2)
61
+ parser.add_argument("-f", "--avoid-function-files", help="Avoid checking if the assembly file for a function exists as a way to determine if the function has been matched or not", action="store_true")
62
+ parser.add_argument("-d", "--debugging", help="Enable debugging prints. This option is deprecated", action="store_true")
63
+ parser.add_argument("-j", "--json", help="Print the stats as json instead of a human readable format.", action="store_true")
64
+
65
+ parser.set_defaults(func=processArguments)
@@ -0,0 +1,75 @@
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
+ from pathlib import Path
10
+
11
+ from .. import mapfile
12
+ from .. import utils
13
+
14
+
15
+ def doSymInfo(mapPath: Path, symName: str, *, as_vram: bool=False, as_vrom: bool=False, as_name: bool=False) -> int:
16
+ if not mapPath.exists():
17
+ print(f"Could not find mapfile at '{mapPath}'")
18
+ return 1
19
+
20
+ mapFile = mapfile.MapFile()
21
+ mapFile.readMapFile(mapPath)
22
+
23
+ possibleFiles: list[mapfile.File] = []
24
+
25
+ if as_vram:
26
+ address = int(symName, 0)
27
+ info, possibleFiles = mapFile.findSymbolByVram(address)
28
+ elif as_vrom:
29
+ address = int(symName, 0)
30
+ info, possibleFiles = mapFile.findSymbolByVrom(address)
31
+ elif as_name:
32
+ info = mapFile.findSymbolByName(symName)
33
+
34
+ # Start the guessing game
35
+ elif utils.convertibleToInt(symName, 0):
36
+ address = int(symName, 0)
37
+ info, possibleFiles = mapFile.findSymbolByVram(address)
38
+ if info is None:
39
+ info, possibleFiles2 = mapFile.findSymbolByVrom(address)
40
+ possibleFiles.extend(possibleFiles2)
41
+ else:
42
+ info = mapFile.findSymbolByName(symName)
43
+
44
+ if info is not None:
45
+ print(info.getAsStrPlusOffset(symName))
46
+ return 0
47
+ print(f"'{symName}' not found in map file '{mapPath}'")
48
+ if len(possibleFiles) > 0:
49
+ print("But it may be a local symbol of either of the following files:")
50
+ for f in possibleFiles:
51
+ print(f" {f.asStr()})")
52
+ return 1
53
+
54
+
55
+ def processArguments(args: argparse.Namespace):
56
+ mapPath: Path = args.mapfile
57
+ symName: str = args.symname
58
+ as_vram: bool = args.vram
59
+ as_vrom: bool = args.vrom
60
+ as_name: bool = args.name
61
+
62
+ exit(doSymInfo(mapPath, symName, as_vram=as_vram, as_vrom=as_vrom, as_name=as_name))
63
+
64
+ def addSubparser(subparser: argparse._SubParsersAction[argparse.ArgumentParser]):
65
+ parser = subparser.add_parser("sym_info", help="Display various information about a symbol or address.")
66
+
67
+ parser.add_argument("mapfile", help="Path to a map file.", type=Path)
68
+ parser.add_argument("symname", help="Symbol name or VROM/VRAM address to lookup. How to treat this argument will be guessed.")
69
+
70
+ vram_vrom_group = parser.add_mutually_exclusive_group()
71
+ vram_vrom_group.add_argument("--vram", help="Treat the argument as a VRAM address instead of guessing.", action="store_true")
72
+ vram_vrom_group.add_argument("--vrom", help="Treat the argument as a VROM address instead of guessing.", action="store_true")
73
+ vram_vrom_group.add_argument("--name", help="Treat the argument as a symbol name instead of guessing.", action="store_true")
74
+
75
+ parser.set_defaults(func=processArguments)