153 lines
3.8 KiB
Python
Executable File
153 lines
3.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
"""
|
|
packrom - performs code compression, writing of garbage data (required for a
|
|
matching ROM), ROM truncation to 32MB, and filling the tail end of the ROM with
|
|
0xff bytes.
|
|
|
|
Usage:
|
|
packrom <rom>
|
|
"""
|
|
|
|
def zip(binary):
|
|
filename = bdir() + '/tmp.bin';
|
|
|
|
fd = open(filename, 'wb')
|
|
fd.write(binary)
|
|
fd.close()
|
|
|
|
zipped = subprocess.check_output(['tools/rarezip', filename])
|
|
os.remove(filename)
|
|
return zipped
|
|
|
|
def bdir():
|
|
return 'build/%s' % os.environ['ROMID']
|
|
|
|
def edir():
|
|
return 'extracted/%s' % os.environ['ROMID']
|
|
|
|
def get_start(locations, segname):
|
|
return next(filter(lambda l: l['name'] == segname, locations))['addr']
|
|
|
|
def get_end(locations, start):
|
|
best = 0xffffffff
|
|
|
|
for location in locations:
|
|
if location['addr'] > start and location['addr'] < best:
|
|
best = location['addr']
|
|
|
|
return best;
|
|
|
|
def attempt(fd, locations, segname, payload, constname):
|
|
# Get location to write to
|
|
start = get_start(locations, segname + 'zip')
|
|
end = get_end(locations, start)
|
|
|
|
# Check it'll fit
|
|
allocation = end - start
|
|
|
|
if len(payload) > allocation:
|
|
print('The %s segment is too big after compression to fit the allocation of 0x%x. In ld/pd.ld, increase the value of %s to 0x%x or higher.' % (
|
|
segname, allocation, constname, len(payload)
|
|
))
|
|
exit(1)
|
|
|
|
# Write it
|
|
fd.seek(start)
|
|
fd.write(payload)
|
|
|
|
def get_segment(fd, locations, segname):
|
|
start = get_start(locations, segname)
|
|
end = get_end(locations, start)
|
|
|
|
fd.seek(start)
|
|
return fd.read(end - start)
|
|
|
|
# lib is compressed from offset 0x2000 onwards
|
|
def pack_lib(fd, locations):
|
|
lib = get_segment(fd, locations, 'lib')
|
|
zipped = lib[0:0x2000] + zip(lib[0x2000:])
|
|
attempt(fd, locations, 'lib', zipped, 'ROMALLOCATION_LIB')
|
|
|
|
def pack_data(fd, locations):
|
|
data = get_segment(fd, locations, 'data')
|
|
zipped = zip(data)
|
|
attempt(fd, locations, 'data', zipped, 'ROMALLOCATION_DATA')
|
|
|
|
def pack_game(fd, locations):
|
|
fd2 = open(bdir() + '/segments/gamezips.bin', 'rb')
|
|
zips = fd2.read()
|
|
fd2.close()
|
|
|
|
attempt(fd, locations, 'game', zips, 'ROMALLOCATION_GAME')
|
|
|
|
def get_locations():
|
|
fd = open(bdir() + '/pd.map', 'r')
|
|
ldmap = fd.read()
|
|
fd.close()
|
|
|
|
matches = re.findall(r'^\.(\S+)\s+0x[0-9a-f]+\s+0x[0-9a-f]+\s+load address\s+0x([0-9a-f]+)', ldmap, re.MULTILINE)
|
|
|
|
def make_numeric(match):
|
|
return {'addr': int(match[1], 16), 'name': match[0]}
|
|
|
|
return list(map(make_numeric, matches))
|
|
|
|
def write_garbage_part(fd, addr, filename):
|
|
fd2 = open(edir() + '/' + filename, 'rb')
|
|
binary = fd2.read()
|
|
fd2.close()
|
|
|
|
fd.seek(addr)
|
|
fd.write(binary)
|
|
|
|
def write_garbage(fd):
|
|
if os.environ['ROMID'] == 'pal-final':
|
|
write_garbage_part(fd, 0x2eb21, 'garbage1.bin')
|
|
write_garbage_part(fd, 0x158038, 'garbage2.bin')
|
|
elif os.environ['ROMID'] == 'ntsc-final':
|
|
write_garbage_part(fd, 0x2ea6c, 'garbage1.bin')
|
|
write_garbage_part(fd, 0x157800, 'garbage2.bin')
|
|
else:
|
|
write_garbage_part(fd, 0x2ea22, 'garbage1.bin')
|
|
write_garbage_part(fd, 0x1574a0, 'garbage2.bin')
|
|
|
|
def fill_tail(fd):
|
|
fd2 = open(bdir() + '/pd.map', 'r')
|
|
ldmap = fd2.read()
|
|
fd2.close()
|
|
|
|
match = re.findall(r'^\s*0x([0-9a-f]+)\s+_accessingpakSegmentRomEnd', ldmap, re.MULTILINE)
|
|
|
|
pos = int(match[0], 16)
|
|
fd.seek(pos)
|
|
|
|
while pos < 1024 * 1024 * 32:
|
|
fd.write(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff')
|
|
pos += 0x10
|
|
|
|
def main():
|
|
locations = get_locations()
|
|
|
|
fd = open(sys.argv[1], 'rb+')
|
|
|
|
write_garbage(fd)
|
|
|
|
pack_lib(fd, locations)
|
|
pack_data(fd, locations)
|
|
pack_game(fd, locations)
|
|
|
|
fill_tail(fd)
|
|
|
|
# Truncate to 32MB
|
|
fd.seek(0)
|
|
fd.truncate(1024 * 1024 * 32)
|
|
fd.close()
|
|
|
|
main()
|