2015-02-19 11:15:45 -08:00
|
|
|
#!/bin/python
|
2015-03-01 20:06:21 -08:00
|
|
|
# fixes the checksums in a Majora's Mask savefile for Bizhawk
|
2015-02-19 11:15:45 -08:00
|
|
|
# note: copies are ignored and overwritten, so don't bother editing them.
|
|
|
|
|
|
|
|
import sys
|
2015-03-01 20:06:21 -08:00
|
|
|
import struct, array
|
2015-02-19 11:15:45 -08:00
|
|
|
|
|
|
|
lament = lambda *args, **kwargs: print(*args, file=sys.stderr, **kwargs)
|
|
|
|
|
2015-03-01 20:06:21 -08:00
|
|
|
R2 = lambda data: struct.unpack('>H', data)[0]
|
|
|
|
W2 = lambda data: struct.pack('>H', data)
|
2015-02-19 11:15:45 -08:00
|
|
|
|
|
|
|
save_1 = 0x20800
|
|
|
|
save_2 = 0x24800
|
|
|
|
owl_1 = 0x28800
|
|
|
|
owl_2 = 0x30800
|
|
|
|
save_1_copy = 0x22800
|
|
|
|
save_2_copy = 0x26800
|
|
|
|
owl_1_copy = 0x2C800
|
|
|
|
owl_2_copy = 0x34800
|
|
|
|
|
2015-03-01 21:00:46 -08:00
|
|
|
chksum_offset = 0x100A
|
|
|
|
save_size = 0x2000
|
|
|
|
owl_size = 0x4000
|
|
|
|
|
|
|
|
MAX16 = 0xFFFF
|
|
|
|
|
2015-02-19 11:15:45 -08:00
|
|
|
def calc_sum(data):
|
|
|
|
chksum = 0
|
2015-03-01 20:06:21 -08:00
|
|
|
for b in data:
|
|
|
|
chksum += b
|
2015-03-01 21:00:46 -08:00
|
|
|
chksum &= MAX16
|
2015-02-19 11:15:45 -08:00
|
|
|
return chksum
|
|
|
|
|
|
|
|
def fix_sum(f, addr, owl=False):
|
|
|
|
f.seek(addr)
|
2015-03-01 21:00:46 -08:00
|
|
|
data = f.read(save_size)
|
|
|
|
chksum = calc_sum(data[:chksum_offset])
|
2015-02-19 11:15:45 -08:00
|
|
|
|
2015-03-01 21:00:46 -08:00
|
|
|
if owl and data != b'\x00'*save_size:
|
2015-02-19 11:15:45 -08:00
|
|
|
chksum += 0x24 # don't know why
|
2015-03-01 21:00:46 -08:00
|
|
|
chksum &= MAX16
|
2015-02-19 11:15:45 -08:00
|
|
|
|
2015-03-01 21:00:46 -08:00
|
|
|
f.seek(addr + chksum_offset)
|
2015-03-01 20:06:21 -08:00
|
|
|
old_chksum = R2(f.read(2))
|
2015-03-01 21:00:46 -08:00
|
|
|
f.seek(addr + chksum_offset)
|
2015-03-01 20:06:21 -08:00
|
|
|
f.write(W2(chksum))
|
2015-02-19 11:15:45 -08:00
|
|
|
lament('{:04X} -> {:04X}'.format(old_chksum, chksum))
|
|
|
|
|
|
|
|
def copy_save(f, addr, addr2):
|
|
|
|
f.seek(addr)
|
2015-03-01 21:00:46 -08:00
|
|
|
# TODO: handle owl size properly
|
|
|
|
data = f.read(save_size)
|
2015-02-19 11:15:45 -08:00
|
|
|
f.seek(addr2)
|
|
|
|
f.write(data)
|
|
|
|
|
|
|
|
def delete_save(f, addr):
|
|
|
|
f.seek(addr)
|
2015-03-01 21:00:46 -08:00
|
|
|
# TODO: handle owl size properly
|
|
|
|
f.write(b'\x00'*save_size)
|
2015-02-19 11:15:45 -08:00
|
|
|
|
2015-03-01 20:06:21 -08:00
|
|
|
def swap_order(f, size='H'):
|
2015-02-19 11:15:45 -08:00
|
|
|
f.seek(0)
|
2015-03-01 20:06:21 -08:00
|
|
|
a = array.array(size, f.read())
|
|
|
|
a.byteswap()
|
2015-02-19 11:15:45 -08:00
|
|
|
f.seek(0)
|
2015-03-01 20:06:21 -08:00
|
|
|
f.write(a.tobytes())
|
2015-02-19 11:15:45 -08:00
|
|
|
|
|
|
|
def run(args):
|
|
|
|
args = args[1:]
|
|
|
|
if len(args) == 0:
|
|
|
|
lament("TODO: convert stdin to stdout")
|
|
|
|
return 0
|
|
|
|
for fn in args:
|
|
|
|
with open(fn, 'r+b') as f:
|
|
|
|
# dumb way to determine byte order
|
|
|
|
endian = 'big'
|
|
|
|
f.seek(0x10000)
|
|
|
|
if f.read(4) != b'\x03\x00\x03\x00':
|
|
|
|
endian = 'little'
|
|
|
|
|
|
|
|
if endian == 'little':
|
2015-03-01 20:06:21 -08:00
|
|
|
swap_order(f, 'L')
|
2015-02-19 11:15:45 -08:00
|
|
|
|
|
|
|
fix_sum(f, save_1)
|
|
|
|
fix_sum(f, save_2)
|
|
|
|
fix_sum(f, owl_1, owl=True)
|
|
|
|
fix_sum(f, owl_2, owl=True)
|
2015-03-01 20:06:21 -08:00
|
|
|
copy_save(f, save_1, save_1_copy)
|
|
|
|
copy_save(f, save_2, save_2_copy)
|
|
|
|
copy_save(f, owl_1, owl_1_copy)
|
|
|
|
copy_save(f, owl_2, owl_2_copy)
|
2015-02-19 11:15:45 -08:00
|
|
|
|
|
|
|
if endian == 'little':
|
2015-03-01 20:06:21 -08:00
|
|
|
swap_order(f, 'L')
|
2015-02-19 11:15:45 -08:00
|
|
|
return 0
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
ret = 0
|
|
|
|
try:
|
|
|
|
ret = run(sys.argv)
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(ret)
|