perfect_dark/tools/inject

240 lines
6.2 KiB
Python
Executable File

#!/usr/bin/python
import sys, zlib
class Injector:
vacancies = [
(0x00ed83a0, 0x01d5ca00), # Normal files spot
(0x0002ea70, 0x00039850), # Unused space 1
(0x00157810, 0x001a15c0), # Unused space 2
]
def rarezip(self, buffer):
length = len(buffer)
header = bytes([0x11, 0x73])
lendata = bytes([length >> 16])
lendata += bytes([(length >> 8) & 0xff])
lendata += bytes([length & 0xff])
obj = zlib.compressobj(level=9, wbits=-15)
return header + lendata + obj.compress(buffer) + obj.flush()
def rareunzip(self, compressed):
return zlib.decompress(compressed[5:], wbits=-15)
def load(self, romfile):
fp = open(romfile, 'rb')
rombuffer = fp.read()
fp.close()
self.rompart1 = rombuffer[0:0x2ea70]
self.rompart2 = rombuffer[0x0004e850:0x00157810]
self.rompart3 = rombuffer[0x001a15c0:0x00ed83a0]
self.rompart4 = rombuffer[0x01d5ca00:]
fp = open('build/ntsc-final/ucode/setup.bin', 'rb')
setup = fp.read()
fp.close()
self.globaltop = setup[0:0x28080]
self.globalbot = setup[0x2a000:]
self.files = []
i = 0
while i <= 0x7dd:
romnameaddr = int.from_bytes(rombuffer[0x01d5ca00 + i * 4:0x01d5ca00 + i * 4 + 4], 'big') + 0x01d5ca00
romdataaddr = int.from_bytes(setup[0x28080 + i * 4:0x28080 + i * 4 + 4], 'big')
name = ''
while rombuffer[romnameaddr] != 0:
name = name + chr(rombuffer[romnameaddr])
romnameaddr += 1
self.files.append({
'name': name,
'romaddr': romdataaddr,
'data': None,
})
i += 1
self.files.sort(key=lambda file: file['romaddr'])
for index, file in enumerate(self.files):
start = file['romaddr']
end = self.files[index + 1]['romaddr'] if index < 0x7dd else 0x01d5ca00
file['data'] = rombuffer[start:end]
def transform(self):
# Replace file contents
for file in self.files:
if self.isFilePointless(file['name']):
file['data'] = None
continue
# Zipped file
try:
fp = open('build/ntsc-final/files/%s' % file['name'], 'rb')
contents = fp.read()
fp.close()
except:
continue
file['data'] = self.align(contents)
# Calculate new ROM addresses
vacancyid = 0
romaddr = self.vacancies[0][0]
vacend = self.vacancies[0][1]
for file in self.files:
if file['name'] == '':
continue
filelen = len(file['data'])
available = vacend - romaddr
if filelen > available:
vacancyid += 1
romaddr = self.vacancies[vacancyid][0]
vacend = self.vacancies[vacancyid][1]
available = vacend - romaddr
file['romaddr'] = romaddr
romaddr += filelen
def isFilePointless(self, name):
return False
def align(self, buffer):
length = len(buffer)
if length % 0x10 == 0:
return buffer
over = length % 0x10
pad = 0x10 - over
buffer += (0).to_bytes(1, 'big') * pad
return buffer
def compile(self):
buffer = self.rompart1
assert(len(buffer) == 0x2ea70)
buffer += self.compileVacancy(1)
assert(len(buffer) == 0x39850)
buffer += self.compileGlobals()
assert(len(buffer) == 0x4e850)
buffer += self.rompart2
assert(len(buffer) == 0x157810)
buffer += self.compileVacancy(2)
assert(len(buffer) == 0x1a15c0)
buffer += self.rompart3
assert(len(buffer) == 0xed83a0)
buffer += self.compileVacancy(0)
assert(len(buffer) == 0x01d5ca00)
buffer += self.rompart4
assert(len(buffer) == 0x2000000)
return buffer
def compileGlobals(self):
buffer = self.globaltop
for file in self.files:
buffer += file['romaddr'].to_bytes(4, 'big')
buffer += (0x01d5ca00).to_bytes(4, 'big')
buffer += (0).to_bytes(4, 'big')
buffer += self.globalbot
buffer = self.rarezip(buffer)
available = 0x15000 - len(buffer)
buffer += (0).to_bytes(1, 'big') * available
return buffer
def compileVacancy(self, vacid):
buffer = bytes()
vacstart = self.vacancies[vacid][0]
vacend = self.vacancies[vacid][1]
for file in self.files:
if file['romaddr'] >= vacstart and file['romaddr'] < vacend:
buffer += file['data']
available = vacend - vacstart - len(buffer)
buffer += (0).to_bytes(1, 'big') * available
return buffer
def ROL(self, i, b):
return ((i << b) | (i >> (32 - b))) & 0xffffffff
def R4(self, b):
return b[0]*0x1000000 + b[1]*0x10000 + b[2]*0x100 + b[3]
def crc(self, f):
seed = 0xdf26f436
t1 = t2 = t3 = t4 = t5 = t6 = seed
f.seek(0x0710 + 0x40)
lookup = f.read(0x100)
f.seek(0x1000)
for i in range(0x1000, 0x101000, 4):
d = self.R4(f.read(4))
if ((t6 + d) & 0xffffffff) < t6:
t4 += 1
t4 &= 0xffffffff
t6 += d
t6 &= 0xffffffff
t3 ^= d
r = self.ROL(d, d & 0x1F)
t5 += r
t5 &= 0xffffffff
if t2 > d:
t2 ^= r
else:
t2 ^= t6 ^ d
o = i & 0xFF
temp = self.R4(lookup[o:o + 4])
t1 += temp ^ d
t1 &= 0xffffffff
crc1 = t6 ^ t4 ^ t3
crc2 = t5 ^ t2 ^ t1
return crc1 & 0xffffffff, crc2 & 0xffffffff
injector = Injector()
injector.load(sys.argv[1])
injector.transform()
filename = 'build/ntsc-final/pd.z64'
fp = open(filename, 'wb')
fp.write(injector.compile())
fp.close()
fp = open(filename, 'rb')
crcs = injector.crc(fp)
fp.seek(0)
buffer = fp.read()
fp.close()
fp = open(filename, 'r+b')
fp.seek(0x10)
fp.write(crcs[0].to_bytes(4, 'big'))
fp.write(crcs[1].to_bytes(4, 'big'))
fp.close()