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.
- mapfile_parser/__init__.py +24 -0
- mapfile_parser/__main__.py +34 -0
- mapfile_parser/frontends/__init__.py +16 -0
- mapfile_parser/frontends/bss_check.py +169 -0
- mapfile_parser/frontends/first_diff.py +168 -0
- mapfile_parser/frontends/jsonify.py +52 -0
- mapfile_parser/frontends/pj64_syms.py +51 -0
- mapfile_parser/frontends/progress.py +65 -0
- mapfile_parser/frontends/sym_info.py +75 -0
- mapfile_parser/frontends/symbol_sizes_csv.py +61 -0
- mapfile_parser/frontends/upload_frogress.py +96 -0
- mapfile_parser/mapfile.py +988 -0
- mapfile_parser/mapfile_parser.cp39-win32.pyd +0 -0
- mapfile_parser/mapfile_parser.pyi +266 -0
- mapfile_parser/mapfile_rs.py +115 -0
- mapfile_parser/progress_stats.py +69 -0
- mapfile_parser/progress_stats_rs.py +16 -0
- mapfile_parser/utils.py +55 -0
- mapfile_parser-2.7.2.dist-info/METADATA +139 -0
- mapfile_parser-2.7.2.dist-info/RECORD +22 -0
- mapfile_parser-2.7.2.dist-info/WHEEL +4 -0
- mapfile_parser-2.7.2.dist-info/licenses/LICENSE +21 -0
|
@@ -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)
|