Atmosphere/fusee/program/split_bin.py
2021-09-06 16:26:50 -07:00

190 lines
6.7 KiB
Python

#!/usr/bin/env python
import sys, lz4, hashlib
from struct import unpack as up, pack as pk
def lz4_compress(data):
try:
import lz4.block as block
except ImportError:
block = lz4.LZ4_compress
return block.compress(data, 'high_compression', store_size=False)
def read_file(fn):
with open(fn, 'rb') as f:
return f.read()
def pad(data, size):
assert len(data) <= size
return (data + '\x00' * size)[:size]
def get_overlay(program, i):
return program[0x2B000 + 0x14000 * i:0x2B000 + 0x14000 * (i+1)]
KIP_NAMES = ['Loader', 'NCM', 'ProcessManager', 'sm', 'boot', 'spl', 'ams_mitm']
def get_kips():
emummc = read_file('../../../../emummc/emummc_unpacked.kip')
loader = read_file('../../../../stratosphere/loader/loader.kip')
ncm = read_file('../../../../stratosphere/ncm/ncm.kip')
pm = read_file('../../../../stratosphere/pm/pm.kip')
sm = read_file('../../../../stratosphere/sm/sm.kip')
boot = read_file('../../../../stratosphere/boot/boot.kip')
spl = read_file('../../../../stratosphere/spl/spl.kip')
ams_mitm = read_file('../../../../stratosphere/ams_mitm/ams_mitm.kip')
return (emummc, {
'Loader' : loader,
'NCM' : ncm,
'ProcessManager' : pm,
'sm' : sm,
'boot' : boot,
'spl' : spl,
'ams_mitm' : ams_mitm,
})
def write_kip_meta(f, kip, ofs):
# Write program id
f.write(kip[0x10:0x18])
# Write offset, size
f.write(pk('<II', ofs - 0x100000, len(kip)))
# Write hash
f.write(hashlib.sha256(kip).digest())
def write_header(f, all_kips, meso_size):
# Unpack kips
emummc, kips = all_kips
# Write reserved0 (previously entrypoint) as infinite loop instruction.
f.write(pk('<I', 0xEAFFFFFE))
# Write metadata offset = 0x10
f.write(pk('<I', 0x20))
# Write flags
f.write(pk('<I', 0x00000000))
# Write meso_size
f.write(pk('<I', meso_size))
# Write num_kips
f.write(pk('<I', len(KIP_NAMES)))
# Write reserved1
f.write(b'\xCC' * 0xC)
# Write magic
f.write('FSS0')
# Write total size
f.write(pk('<I', 0x800000))
# Write reserved2
f.write(pk('<I', 0xCCCCCCCC))
# Write content_header_offset
f.write(pk('<I', 0x40))
# Write num_content_headers;
f.write(pk('<I', 8 + len(KIP_NAMES)))
# Write supported_hos_version;
f.write(pk('<I', 0xCCCCCCCC)) # TODO
# Write release_version;
f.write(pk('<I', 0xCCCCCCCC)) # TODO
# Write git_revision;
f.write(pk('<I', 0xCCCCCCCC)) # TODO
# Write content metas
f.write(pk('<IIBBBBI16s', 0x000800, 0x001800, 2, 0, 0, 0, 0xCCCCCCCC, 'warmboot'))
f.write(pk('<IIBBBBI16s', 0x002000, 0x002000, 12, 0, 0, 0, 0xCCCCCCCC, 'tsec_keygen'))
f.write(pk('<IIBBBBI16s', 0x004000, 0x01C000, 11, 0, 0, 0, 0xCCCCCCCC, 'exosphere_fatal'))
f.write(pk('<IIBBBBI16s', 0x048000, 0x00E000, 1, 0, 0, 0, 0xCCCCCCCC, 'exosphere'))
f.write(pk('<IIBBBBI16s', 0x056000, 0x0AA000, 10, 0, 0, 0, 0xCCCCCCCC, 'mesosphere'))
f.write(pk('<IIBBBBI16s', 0x7C0000, 0x020000, 0, 0, 0, 0, 0xCCCCCCCC, 'fusee'))
f.write(pk('<IIBBBBI16s', 0x7E0000, 0x001000, 3, 0, 0, 0, 0xCCCCCCCC, 'rebootstub'))
f.write(pk('<IIBBBBI16s', 0x100000, len(emummc), 8, 0, 0, 0, 0xCCCCCCCC, 'emummc'))
ofs = (0x100000 + len(emummc) + 0xF) & ~0xF
for kip_name in KIP_NAMES:
kip_data = kips[kip_name]
f.write(pk('<IIBBBBI16s', ofs, len(kip_data), 6, 0, 0, 0, 0xCCCCCCCC, kip_name))
ofs += len(kip_data)
ofs += 0xF
ofs &= ~0xF
# Pad to kip metas.
f.write(b'\xCC' * (0x400 - 0x40 - (0x20 * (8 + len(KIP_NAMES)))))
# Write emummc_meta. */
write_kip_meta(f, emummc, 0x100000)
# Write kip metas
ofs = (0x100000 + len(emummc) + 0xF) & ~0xF
for kip_name in KIP_NAMES:
kip_data = kips[kip_name]
write_kip_meta(f, kip_data, ofs)
ofs += len(kip_data)
ofs += 0xF
ofs &= ~0xF
# Pad to end of header
f.write(b'\xCC' * (0x800 - (0x400 + (1 + len(KIP_NAMES)) * 0x30)))
def write_kips(f, all_kips):
# Unpack kips
emummc, kips = all_kips
# Write emummc
f.write(emummc)
# Write kips
tot = len(emummc)
if (tot & 0xF):
f.write('\xCC' * (0x10 - (tot & 0xF)))
tot += 0xF
tot &= ~0xF
for kip_name in KIP_NAMES:
kip_data = kips[kip_name]
f.write(kip_data)
tot += len(kip_data)
if (tot & 0xF):
f.write('\xCC' * (0x10 - (tot & 0xF)))
tot += 0xF
tot &= ~0xF
# Pad to 3 MB
f.write(b'\xCC' * (0x300000 - tot))
def main(argc, argv):
if argc == 2:
target = argv[1]
if len(target) == 0:
target = ''
elif argc == 1:
target = ''
else:
print('Usage: %s target' % argv[0])
return 1
all_kips = get_kips()
with open('../../program%s.bin' % target, 'rb') as f:
data = f.read()
erista_mtc = get_overlay(data, 1)
mariko_mtc = get_overlay(data, 2)
erista_hsh = hashlib.sha256(erista_mtc[:-4]).digest()[:4]
mariko_hsh = hashlib.sha256(mariko_mtc[:-4]).digest()[:4]
fusee_program = lz4_compress(data[:0x2B000 - 8] + erista_hsh + mariko_hsh + get_overlay(data, 0)[:0x11000])
mesosphere = read_file('../../../../mesosphere/mesosphere%s.bin' % target)
with open('../../program%s.lz4' % target, 'wb') as f:
f.write(fusee_program)
with open('../../fusee-boogaloo%s.bin' % target, 'wb') as f:
# Write header
write_header(f, all_kips, len(mesosphere))
# Write warmboot
f.write(pad(read_file('../../../../exosphere/warmboot%s.bin' % target), 0x1800))
# Write TSEC Keygen
f.write(pad(read_file('../../tsec_keygen/tsec_keygen.bin'), 0x2000))
# Write Mariko Fatal
f.write(pad(read_file('../../../../exosphere/mariko_fatal%s.bin' % target), 0x1C000))
# Write Erista MTC
f.write(erista_mtc[:-4] + erista_hsh)
# Write Mariko MTC
f.write(mariko_mtc[:-4] + mariko_hsh)
# Write exosphere
f.write(pad(read_file('../../../../exosphere/exosphere%s.bin' % target), 0xE000))
# Write mesosphere
f.write(pad(read_file('../../../../mesosphere/mesosphere%s.bin' % target), 0xAA000))
# Write kips
write_kips(f, all_kips)
# Write Splash Screen
f.write(pad(read_file('../../splash_screen/splash_screen.bin'), 0x3C0000))
# Write fusee
f.write(pad(fusee_program, 0x20000))
# Write rebootstub
f.write(pad(read_file('../../../../exosphere/program/rebootstub/rebootstub%s.bin' % target), 0x1000))
# Pad to 8 MB
f.write(b'\xCC' * (0x800000 - 0x7E1000))
return 0
if __name__ == '__main__':
sys.exit(main(len(sys.argv), sys.argv))