7 # TODO: -S argument for shifted ROMs
9 for arg
in sys
.argv
[1:]:
24 for path
in ['build/us/sm64.us.z64', 'build/jp/sm64.jp.z64', 'build/eu/sm64.eu.z64', 'build/sh/sm64.sh.z64']:
26 mtime
= os
.path
.getmtime(path
)
29 lang
= path
.split('/')[1]
32 print("Assuming language " + lang
)
34 baseimg
= 'baserom.' + lang
+ '.z64'
35 basemap
= 'sm64.' + lang
+ '.map'
37 myimg
= 'build/' + lang
+ '/sm64.' + lang
+ '.z64'
38 mymap
= 'build/' + lang
+ '/sm64.' + lang
+ '.map'
40 if os
.path
.isfile('expected/' + mymap
):
41 basemap
= 'expected/' + mymap
43 required_files
= [baseimg
, myimg
, mymap
]
44 if any(not os
.path
.isfile(path
) for path
in required_files
):
45 print(', '.join(required_files
[:-1]) + " and " + required_files
[-1] + " must exist.")
48 mybin
= open(myimg
, 'rb').read()
49 basebin
= open(baseimg
, 'rb').read()
51 if len(mybin
) != len(basebin
):
52 print("Modified ROM has different size...")
56 print("No differences!")
59 def search_map(rom_addr
):
63 last_fn
= '<start of rom>'
64 last_file
= '<no file>'
66 with
open(mymap
) as f
:
68 if 'load address' in line
:
69 # Example: ".boot 0x0000000004000000 0x1000 load address 0x0000000000000000"
70 if 'noload' in line
or 'noload' in prev_line
:
73 ram
= int(line
[16:16+18], 0)
74 rom
= int(line
[59:59+18], 0)
75 ram_offset
= ram
- rom
79 if ram_offset
is None or '=' in line
or '*fill*' in line
or ' 0x' not in line
:
81 ram
= int(line
[16:16+18], 0)
82 rom
= ram
- ram_offset
87 if rom
> rom_addr
or (rom_addr
& 0x80000000 and ram
> rom_addr
):
88 return 'in {} (ram 0x{:08x}, rom 0x{:x}, {})'.format(last_fn
, last_ram
, last_rom
, last_file
)
94 return 'at end of rom?'
98 cur_file
= '<no file>'
102 with
open(fname
) as f
:
104 if 'load address' in line
:
105 if 'noload' in line
or 'noload' in prev_line
:
108 ram
= int(line
[16:16+18], 0)
109 rom
= int(line
[59:59+18], 0)
110 ram_offset
= ram
- rom
114 if ram_offset
is None or '=' in line
or '*fill*' in line
or ' 0x' not in line
:
116 ram
= int(line
[16:16+18], 0)
117 rom
= ram
- ram_offset
118 fn
= line
.split()[-1]
124 syms
[fn
] = (rom
, cur_file
, prev_sym
, ram
)
129 map1
= parse_map(mymap
)
130 map2
= parse_map(basemap
)
133 for sym
, addr
in map1
.items():
136 if addr
[0] != map2
[sym
][0]:
137 if min_ram
is None or addr
[0] < min_ram
:
139 found
= (sym
, addr
[1], addr
[2])
144 print("Map appears to have shifted just before {} ({}) -- in {}?".format(found
[0], found
[1], found
[2]))
145 if found
[2] is not None and found
[2] not in map2
:
147 print("(Base map file {} out of date due to renamed symbols, so result may be imprecise.)".format(basemap
))
151 return ":".join("{:02x}".format(c
) for c
in bs
)
153 # For convenience, allow `./first-diff.py <ROM addr | RAM addr | function name>`
154 # to do a symbol <-> address lookup. This should really be split out into a
158 addr
= int(args
[0], 0)
159 print(args
[0], "is", search_map(addr
))
163 print(args
[0], "is at position", hex(m
[args
[0]][0]), "in ROM,", hex(m
[args
[0]][3]), "in RAM")
165 print("function", args
[0], "not found")
168 found_instr_diff
= None
171 for i
in range(24, len(mybin
), 4):
172 # (mybin[i:i+4] != basebin[i:i+4], but that's slightly slower in CPython...)
173 if diffs
<= shift_cap
and (mybin
[i
] != basebin
[i
] or mybin
[i
+1] != basebin
[i
+1] or mybin
[i
+2] != basebin
[i
+2] or mybin
[i
+3] != basebin
[i
+3]):
175 print("First difference at ROM addr " + hex(i
) + ", " + search_map(i
))
176 print("Bytes:", hexbytes(mybin
[i
:i
+4]), 'vs', hexbytes(basebin
[i
:i
+4]))
178 if found_instr_diff
is None and mybin
[i
] >> 2 != basebin
[i
] >> 2:
181 print("No differences!")
183 definite_shift
= (diffs
> shift_cap
)
184 if not definite_shift
:
185 print(str(diffs
) + " differing word(s).")
188 if found_instr_diff
is not None:
190 print("First instruction difference at ROM addr " + hex(i
) + ", " + search_map(i
))
191 print("Bytes:", hexbytes(mybin
[i
:i
+4]), 'vs', hexbytes(basebin
[i
:i
+4]))
193 print("Shifted ROM, as expected.")
195 if not os
.path
.isfile(basemap
):
197 print("Tons of differences, must be a shifted ROM.")
198 print("To find ROM shifts, copy a clean .map file to " + basemap
+ " and rerun this script.")
202 print("No ROM shift{}.".format(" (!?)" if definite_shift
else ""))