mirror of
https://github.com/xprism1/ntool.git
synced 2025-06-18 17:45:32 -04:00
685 lines
23 KiB
Python
685 lines
23 KiB
Python
from lib.common import *
|
|
from lib.keys import NTR, TWL
|
|
from lib.ctr_cia import CIAReader, CIABuilder
|
|
from lib.ctr_cci import CCIReader, CCIBuilder
|
|
from lib.ctr_ncch import NCCHReader, NCCHBuilder
|
|
from lib.ctr_exefs import ExeFSReader, ExeFSBuilder
|
|
from lib.ctr_romfs import RomFSReader, RomFSBuilder
|
|
from lib.ctr_crr import crrReader
|
|
from lib.ctr_tmd import TMDReader, TMDBuilder
|
|
from lib.ctr_tik import tikReader, tikBuilder
|
|
from lib.ctr_cdn import CDNReader, CDNBuilder
|
|
from lib.ctr_cnt import cntReader
|
|
from lib.ntr_twl_srl import SRLReader, get_rsa_key_idx
|
|
|
|
def srl_retail2dev(path, out=''):
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
if out == '':
|
|
out = f'{name}_dev.srl'
|
|
|
|
srl = SRLReader(path, dev=0)
|
|
shutil.copyfile(path, 'tmp.nds')
|
|
|
|
if srl.media == 'Game card' and srl.secure_area_status == 'decrypted': # Encrypt NTR secure area for decrypted game card SRLs
|
|
with open(path, 'rb') as f:
|
|
f.seek(0x4000)
|
|
secure_area = f.read(2048)
|
|
secure_area_enc = srl.encrypt_secure_area(secure_area, NTR.blowfish_key)
|
|
with open('tmp.nds', 'r+b') as f:
|
|
f.seek(0x4000)
|
|
f.write(secure_area_enc)
|
|
|
|
if srl.modcrypted: # Decrypt modcrypt regions and re-encrypt with dev key
|
|
srl.decrypt_modcrypt()
|
|
f = open('decrypted.nds', 'rb')
|
|
key = bytes(srl.hdr)[:16][::-1]
|
|
for i in srl.modcrypt:
|
|
g = open('tmp.nds', 'r+b')
|
|
f.seek(i['offset'])
|
|
g.seek(i['offset'])
|
|
|
|
counter = bytearray(i['counter'])
|
|
for data in read_chunks(f, i['size']):
|
|
for j in range(len(data) // 16):
|
|
output, counter = TWL.aes_ctr_block(key, counter, data[j * 16:(j + 1) * 16])
|
|
g.write(output)
|
|
g.close()
|
|
f.close()
|
|
os.remove('decrypted.nds')
|
|
|
|
if srl.hdr.unit_code == 2 or srl.hdr.unit_code == 3 or (srl.hdr.unit_code == 0 and srl.hdr_ext.flags != 0): # Set DeveloperApp flag
|
|
srl.hdr_ext.flags |= (1 << 7)
|
|
with open('tmp.nds', 'r+b') as f:
|
|
f.seek(0x1BF)
|
|
f.write(int8tobytes(srl.hdr_ext.flags))
|
|
|
|
if not (srl.hdr.unit_code == 0 and readbe(srl.hdr_ext.sig) == 0): # Re-generate header signature
|
|
idx = get_rsa_key_idx(srl.hdr, srl.hdr_ext)
|
|
n = TWL.rsa_key_mod[idx]
|
|
d = TWL.rsa_key_priv[idx]
|
|
|
|
f = open('tmp.nds', 'rb')
|
|
sha1_calculated = Crypto.sha1(f, 0xE00)
|
|
f.close()
|
|
sha1_padded = b'\x00\x01' + b'\xff' * 105 + b'\x00' + sha1_calculated
|
|
enc = pow(readbe(sha1_padded), readbe(d[1]), readbe(n[1])).to_bytes(0x80, 'big')
|
|
with open('tmp.nds', 'r+b') as f:
|
|
f.seek(0xF80)
|
|
f.write(enc)
|
|
|
|
if srl.media == 'Game card': # Re-generate undumpable area i.e. KeyTables for game card SRLs
|
|
srl = SRLReader('tmp.nds', dev=1)
|
|
srl.regen_undumpable()
|
|
os.remove('tmp.nds')
|
|
shutil.move('new.nds', out)
|
|
else:
|
|
shutil.move('tmp.nds', out)
|
|
|
|
def cia_dev2retail(path, out=''):
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
if out == '':
|
|
out = f'{name}_retail.cia'
|
|
|
|
cia = CIAReader(path, dev=1)
|
|
cia.extract()
|
|
|
|
cf = list(cia.files.keys())
|
|
cf.remove('cia_header.bin')
|
|
cf.remove('cert.bin')
|
|
cf.remove('tik')
|
|
cf.remove('tmd')
|
|
if 'meta.bin' in cf:
|
|
meta = 1
|
|
cf.remove('meta.bin')
|
|
else:
|
|
meta = 0
|
|
|
|
for i in cf:
|
|
if i.endswith('.ncch'):
|
|
ncch = NCCHReader(i, dev=1)
|
|
ncch.extract() # NOTE: no need to resign CRR since CRR body sig will pass (all that matters)
|
|
ncch_header = 'ncch_header.bin'
|
|
if os.path.isfile('exheader.bin'):
|
|
exheader = 'exheader.bin'
|
|
else:
|
|
exheader = ''
|
|
if os.path.isfile('logo.bin'):
|
|
logo = 'logo.bin'
|
|
else:
|
|
logo = ''
|
|
if os.path.isfile('plain.bin'):
|
|
plain = 'plain.bin'
|
|
else:
|
|
plain = ''
|
|
if os.path.isfile('exefs.bin'):
|
|
exefs = 'exefs.bin'
|
|
else:
|
|
exefs = ''
|
|
if os.path.isfile('romfs.bin'):
|
|
romfs = 'romfs.bin'
|
|
else:
|
|
romfs = ''
|
|
os.remove(i)
|
|
NCCHBuilder(ncch_header=ncch_header, exheader=exheader, logo=logo, plain=plain, exefs=exefs, romfs=romfs, crypto='Secure1', regen_sig='retail', dev=0, out=i)
|
|
for j in [ncch_header, exheader, logo, plain, exefs, romfs]:
|
|
if j != '':
|
|
os.remove(j)
|
|
|
|
tmd = TMDReader('tmd', dev=1)
|
|
TMDBuilder(content_files=cf, content_files_dev=0, titleID=tmd.titleID, title_ver=tmd.hdr.title_ver, save_data_size=tmd.hdr.save_data_size, priv_save_data_size=tmd.hdr.priv_save_data_size, twl_flag=tmd.hdr.twl_flag, crypt=0, regen_sig='retail')
|
|
os.remove('tmd')
|
|
|
|
tik = tikReader('tik', dev=1)
|
|
tikBuilder(tik='tik', titlekey=hex(readbe(tik.titlekey))[2:].zfill(32), regen_sig='retail') # Use original (decrypted) titlekey
|
|
os.remove('tik')
|
|
|
|
CIABuilder(content_files=cf, tik='tik_new', tmd='tmd_new', meta=meta, dev=0, out=out)
|
|
|
|
for i in cf + ['tmd_new', 'tik_new', 'cia_header.bin', 'cert.bin', 'meta.bin']:
|
|
if os.path.isfile(i):
|
|
os.remove(i)
|
|
|
|
def cia_retail2dev(path, out=''):
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
if out == '':
|
|
out = f'{name}_dev.cia'
|
|
|
|
cia = CIAReader(path, dev=0)
|
|
cia.extract()
|
|
|
|
cf = list(cia.files.keys())
|
|
cf.remove('cia_header.bin')
|
|
cf.remove('cert.bin')
|
|
cf.remove('tik')
|
|
cf.remove('tmd')
|
|
if 'meta.bin' in cf:
|
|
meta = 1
|
|
cf.remove('meta.bin')
|
|
else:
|
|
meta = 0
|
|
|
|
for i in cf:
|
|
if i.endswith('.ncch'):
|
|
ncch = NCCHReader(i, dev=0)
|
|
ncch.extract()
|
|
ncch_header = 'ncch_header.bin'
|
|
if os.path.isfile('exheader.bin'):
|
|
exheader = 'exheader.bin'
|
|
else:
|
|
exheader = ''
|
|
if os.path.isfile('logo.bin'):
|
|
logo = 'logo.bin'
|
|
else:
|
|
logo = ''
|
|
if os.path.isfile('plain.bin'):
|
|
plain = 'plain.bin'
|
|
else:
|
|
plain = ''
|
|
if os.path.isfile('exefs.bin'):
|
|
exefs = 'exefs.bin'
|
|
else:
|
|
exefs = ''
|
|
if os.path.isfile('romfs.bin'):
|
|
romfs = 'romfs.bin'
|
|
romfs_rdr = RomFSReader('romfs.bin')
|
|
if '.crr/static.crr' in romfs_rdr.files.keys() or '.crr\\static.crr' in romfs_rdr.files.keys():
|
|
romfs_rdr.extract()
|
|
crr = crrReader('romfs/.crr/static.crr')
|
|
crr.regen_sig(dev=1)
|
|
os.remove('romfs.bin')
|
|
RomFSBuilder(romfs_dir='romfs/', out='romfs.bin')
|
|
shutil.rmtree('romfs/')
|
|
else:
|
|
romfs = ''
|
|
os.remove(i)
|
|
NCCHBuilder(ncch_header=ncch_header, exheader=exheader, logo=logo, plain=plain, exefs=exefs, romfs=romfs, crypto='Secure1', regen_sig='dev', dev=1, out=i)
|
|
for j in [ncch_header, exheader, logo, plain, exefs, romfs]:
|
|
if j != '':
|
|
os.remove(j)
|
|
|
|
tmd = TMDReader('tmd', dev=0)
|
|
TMDBuilder(content_files=cf, content_files_dev=1, titleID=tmd.titleID, title_ver=tmd.hdr.title_ver, save_data_size=tmd.hdr.save_data_size, priv_save_data_size=tmd.hdr.priv_save_data_size, twl_flag=tmd.hdr.twl_flag, crypt=1, regen_sig='dev')
|
|
os.remove('tmd')
|
|
|
|
tik = tikReader('tik', dev=0)
|
|
tikBuilder(tik='tik', titlekey=hex(readbe(tik.titlekey))[2:].zfill(32), regen_sig='dev') # Use original (decrypted) titlekey
|
|
os.remove('tik')
|
|
|
|
CIABuilder(content_files=cf, tik='tik_new', tmd='tmd_new', meta=meta, dev=1, out=out)
|
|
|
|
for i in cf + ['tmd_new', 'tik_new', 'cia_header.bin', 'cert.bin', 'meta.bin']:
|
|
if os.path.isfile(i):
|
|
os.remove(i)
|
|
|
|
def cci_dev2retail(path, out=''):
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
if out == '':
|
|
out = f'{name}_retail.3ds'
|
|
|
|
cci = CCIReader(path, dev=1)
|
|
cci.extract()
|
|
|
|
parts = list(cci.files.keys())
|
|
parts.remove('cci_header.bin')
|
|
parts.remove('card_info.bin')
|
|
parts.remove('mastering_info.bin')
|
|
parts.remove('initialdata.bin')
|
|
if 'card_device_info.bin' in parts:
|
|
parts.remove('card_device_info.bin')
|
|
|
|
for i in parts:
|
|
if i.endswith('.ncch'):
|
|
ncch = NCCHReader(i, dev=1)
|
|
ncch.extract() # NOTE: no need to resign CRR since CRR body sig will pass (all that matters)
|
|
ncch_header = 'ncch_header.bin'
|
|
if os.path.isfile('exheader.bin'):
|
|
exheader = 'exheader.bin'
|
|
else:
|
|
exheader = ''
|
|
if os.path.isfile('logo.bin'):
|
|
logo = 'logo.bin'
|
|
else:
|
|
logo = ''
|
|
if os.path.isfile('plain.bin'):
|
|
plain = 'plain.bin'
|
|
else:
|
|
plain = ''
|
|
if os.path.isfile('exefs.bin'):
|
|
exefs = 'exefs.bin'
|
|
else:
|
|
exefs = ''
|
|
if os.path.isfile('romfs.bin'):
|
|
romfs = 'romfs.bin'
|
|
else:
|
|
romfs = ''
|
|
os.remove(i)
|
|
NCCHBuilder(ncch_header=ncch_header, exheader=exheader, logo=logo, plain=plain, exefs=exefs, romfs=romfs, crypto='Secure1', regen_sig='retail', dev=0, out=i)
|
|
for j in [ncch_header, exheader, logo, plain, exefs, romfs]:
|
|
if j != '':
|
|
os.remove(j)
|
|
|
|
CCIBuilder(cci_header='cci_header.bin', card_info='card_info.bin', mastering_info='mastering_info.bin', initialdata='', card_device_info='', ncchs=parts, cardbus_crypto='Secure0', regen_sig='retail', dev=0, gen_card_device_info=0, out=out)
|
|
|
|
for i in parts + ['cci_header.bin', 'card_info.bin', 'mastering_info.bin', 'initialdata.bin', 'card_device_info.bin']:
|
|
if os.path.isfile(i):
|
|
os.remove(i)
|
|
|
|
def cci_retail2dev(path, out=''):
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
if out == '':
|
|
out = f'{name}_dev.3ds'
|
|
|
|
cci = CCIReader(path, dev=0)
|
|
cci.extract()
|
|
|
|
parts = list(cci.files.keys())
|
|
parts.remove('cci_header.bin')
|
|
parts.remove('card_info.bin')
|
|
parts.remove('mastering_info.bin')
|
|
parts.remove('initialdata.bin')
|
|
if 'card_device_info.bin' in parts:
|
|
parts.remove('card_device_info.bin')
|
|
|
|
for i in parts:
|
|
if i.endswith('.ncch'):
|
|
ncch = NCCHReader(i, dev=0)
|
|
ncch.extract()
|
|
ncch_header = 'ncch_header.bin'
|
|
if os.path.isfile('exheader.bin'):
|
|
exheader = 'exheader.bin'
|
|
else:
|
|
exheader = ''
|
|
if os.path.isfile('logo.bin'):
|
|
logo = 'logo.bin'
|
|
else:
|
|
logo = ''
|
|
if os.path.isfile('plain.bin'):
|
|
plain = 'plain.bin'
|
|
else:
|
|
plain = ''
|
|
if os.path.isfile('exefs.bin'):
|
|
exefs = 'exefs.bin'
|
|
else:
|
|
exefs = ''
|
|
if os.path.isfile('romfs.bin'):
|
|
romfs = 'romfs.bin'
|
|
romfs_rdr = RomFSReader('romfs.bin')
|
|
if '.crr/static.crr' in romfs_rdr.files.keys() or '.crr\\static.crr' in romfs_rdr.files.keys():
|
|
romfs_rdr.extract()
|
|
crr = crrReader('romfs/.crr/static.crr')
|
|
crr.regen_sig(dev=1)
|
|
os.remove('romfs.bin')
|
|
RomFSBuilder(romfs_dir='romfs/', out='romfs.bin')
|
|
shutil.rmtree('romfs/')
|
|
else:
|
|
romfs = ''
|
|
os.remove(i)
|
|
NCCHBuilder(ncch_header=ncch_header, exheader=exheader, logo=logo, plain=plain, exefs=exefs, romfs=romfs, crypto='Secure1', regen_sig='dev', dev=1, out=i)
|
|
for j in [ncch_header, exheader, logo, plain, exefs, romfs]:
|
|
if j != '':
|
|
os.remove(j)
|
|
|
|
CCIBuilder(cci_header='cci_header.bin', card_info='card_info.bin', mastering_info='mastering_info.bin', initialdata='', card_device_info='', ncchs=parts, cardbus_crypto='fixed', regen_sig='dev', dev=1, gen_card_device_info=1, out=out)
|
|
|
|
for i in parts + ['cci_header.bin', 'card_info.bin', 'mastering_info.bin', 'initialdata.bin', 'card_device_info.bin']:
|
|
if os.path.isfile(i):
|
|
os.remove(i)
|
|
|
|
def ncch_extractall(path, dev=0):
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
os.mkdir(name)
|
|
|
|
ncch = NCCHReader(path, dev)
|
|
ncch.extract()
|
|
exefs_code_compress = 0
|
|
for i in ['ncch_header.bin', 'exheader.bin', 'logo.bin', 'plain.bin', 'exefs.bin', 'romfs.bin']:
|
|
if os.path.isfile(i):
|
|
if i == 'exheader.bin':
|
|
with open(i, 'rb') as f:
|
|
f.seek(0xD)
|
|
flag = readle(f.read(1))
|
|
if flag & 1:
|
|
exefs_code_compress = 1
|
|
|
|
shutil.move(i, os.path.join(name, i))
|
|
|
|
os.chdir(name)
|
|
# Extract ExeFS
|
|
if os.path.isfile('exefs.bin'):
|
|
exefs = ExeFSReader('exefs.bin')
|
|
exefs.extract(code_compressed=exefs_code_compress)
|
|
os.mkdir('exefs')
|
|
for i in exefs.files.keys():
|
|
shutil.move(i, os.path.join('exefs', i))
|
|
if exefs_code_compress:
|
|
os.remove(os.path.join('exefs', '.code.bin'))
|
|
shutil.move('code-decompressed.bin', os.path.join('exefs', '.code.bin'))
|
|
|
|
# Extract RomFS
|
|
if os.path.isfile('romfs.bin'):
|
|
romfs = RomFSReader('romfs.bin')
|
|
romfs.extract()
|
|
|
|
os.chdir('..')
|
|
|
|
def macos_clean(path):
|
|
proc = subprocess.call(['dot_clean', path], stdout=None, stderr=None)
|
|
proc = subprocess.call(['find', path, '-type', 'f', '-name', '.DS_Store', '-exec', 'rm', '{}', ';'], stdout=None, stderr=None)
|
|
|
|
def ncch_rebuildall(path, dev=0):
|
|
os.chdir(path)
|
|
name = os.path.basename(os.getcwd())
|
|
out = f'{name}.ncch'
|
|
|
|
if os.path.isdir('exefs/'):
|
|
if platform.system() == 'Darwin':
|
|
macos_clean('exefs/')
|
|
if os.path.isfile('exefs.bin'):
|
|
os.remove('exefs.bin')
|
|
|
|
exefs_code_compress = 0
|
|
if os.path.isfile('exheader.bin'):
|
|
with open('exheader.bin', 'rb') as f:
|
|
f.seek(0xD)
|
|
flag = readle(f.read(1))
|
|
if flag & 1:
|
|
exefs_code_compress = 1
|
|
|
|
ExeFSBuilder(exefs_dir='exefs/', code_compress=exefs_code_compress)
|
|
|
|
if os.path.isdir('romfs/'):
|
|
if platform.system() == 'Darwin':
|
|
macos_clean('romfs/')
|
|
if os.path.isfile('romfs.bin'):
|
|
os.remove('romfs.bin')
|
|
RomFSBuilder(romfs_dir='romfs/')
|
|
|
|
ncch_header = 'ncch_header.bin'
|
|
if os.path.isfile('exheader.bin'):
|
|
exheader = 'exheader.bin'
|
|
else:
|
|
exheader = ''
|
|
if os.path.isfile('logo.bin'):
|
|
logo = 'logo.bin'
|
|
else:
|
|
logo = ''
|
|
if os.path.isfile('plain.bin'):
|
|
plain = 'plain.bin'
|
|
else:
|
|
plain = ''
|
|
if os.path.isfile('exefs.bin'):
|
|
exefs = 'exefs.bin'
|
|
else:
|
|
exefs = ''
|
|
if os.path.isfile('romfs.bin'):
|
|
romfs = 'romfs.bin'
|
|
else:
|
|
romfs = ''
|
|
NCCHBuilder(ncch_header=ncch_header, exheader=exheader, logo=logo, plain=plain, exefs=exefs, romfs=romfs, dev=dev, out=out)
|
|
if not os.path.isfile(f'../{out}'):
|
|
shutil.move(out, f'../{out}')
|
|
else:
|
|
shutil.move(out, f'../{name} (new).ncch')
|
|
os.chdir('..')
|
|
|
|
def cci_extractall(path, dev=0):
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
os.mkdir(name)
|
|
|
|
cci = CCIReader(path, dev)
|
|
cci.extract()
|
|
|
|
for i in cci.files.keys():
|
|
shutil.move(i, os.path.join(name, i))
|
|
|
|
if i.endswith('.ncch'):
|
|
os.chdir(name)
|
|
ncch_extractall(i)
|
|
os.chdir('..')
|
|
|
|
def cci_rebuildall(path, dev=0):
|
|
os.chdir(path)
|
|
name = os.path.basename(os.getcwd())
|
|
out = f'{name}.3ds'
|
|
|
|
ncchs = []
|
|
card_device_info = ''
|
|
if os.path.isfile('card_device_info.bin'):
|
|
card_device_info = 'card_device_info.bin'
|
|
|
|
for i in os.listdir('.'):
|
|
if os.path.isdir(i):
|
|
ncchs.append(f'{i}.ncch')
|
|
if os.path.isfile(f'{i}.ncch'):
|
|
os.remove(f'{i}.ncch')
|
|
ncch_rebuildall(i, dev)
|
|
|
|
CCIBuilder(cci_header='cci_header.bin', card_info='card_info.bin', mastering_info='mastering_info.bin', initialdata='initialdata.bin', card_device_info=card_device_info, ncchs=ncchs, dev=dev, out=out)
|
|
if not os.path.isfile(f'../{out}'):
|
|
shutil.move(out, f'../{out}')
|
|
else:
|
|
shutil.move(out, f'../{name} (new).3ds')
|
|
os.chdir('..')
|
|
|
|
def cia_extractall(path, dev=0):
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
os.mkdir(name)
|
|
|
|
cia = CIAReader(path, dev)
|
|
cia.extract()
|
|
|
|
for i in cia.files.keys():
|
|
shutil.move(i, os.path.join(name, i))
|
|
|
|
if i.endswith('.ncch'):
|
|
os.chdir(name)
|
|
ncch_extractall(i)
|
|
os.chdir('..')
|
|
|
|
def cia_rebuildall(path, dev=0):
|
|
os.chdir(path)
|
|
name = os.path.basename(os.getcwd())
|
|
out = f'{name}.cia'
|
|
|
|
cf = []
|
|
meta = 0
|
|
if os.path.isfile('meta.bin'):
|
|
meta = 1
|
|
|
|
for i in os.listdir('.'):
|
|
if os.path.isdir(i) or (os.path.isfile(i) and i.endswith('.nds')):
|
|
if os.path.isdir(i):
|
|
cf.append(f'{i}.ncch')
|
|
if os.path.isfile(f'{i}.ncch'):
|
|
os.remove(f'{i}.ncch')
|
|
ncch_rebuildall(i, dev)
|
|
else:
|
|
cf.append(i)
|
|
|
|
CIABuilder(certs='cert.bin', content_files=cf, tik='tik', tmd='tmd', meta=meta, dev=dev, out=out)
|
|
if not os.path.isfile(f'../{out}'):
|
|
shutil.move(out, f'../{out}')
|
|
else:
|
|
shutil.move(out, f'../{name} (new).cia')
|
|
os.chdir('..')
|
|
|
|
def cci2cia(path, out='', cci_dev=0, cia_dev=0):
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
if out == '':
|
|
out = f'{name}_conv.cia'
|
|
|
|
cci = CCIReader(path, cci_dev)
|
|
cci.extract()
|
|
|
|
ncchs = [i for i in cci.files.keys() if i.endswith('.ncch')]
|
|
for i in ['content6.update_n3ds.ncch', 'content7.update_o3ds.ncch']:
|
|
if i in ncchs:
|
|
ncchs.remove(i)
|
|
os.remove(i)
|
|
|
|
if cia_dev == 0:
|
|
regen_sig = 'retail'
|
|
else:
|
|
regen_sig = 'dev'
|
|
|
|
for i in ncchs:
|
|
n = NCCHReader(i, dev=cci_dev)
|
|
n.extract()
|
|
os.remove(i)
|
|
|
|
if i.startswith('content0'):
|
|
with open('exheader.bin', 'r+b') as f:
|
|
f.seek(0xD)
|
|
flag = readle(f.read(1))
|
|
f.seek(0xD)
|
|
f.write(int8tobytes(flag | 2)) # Set SDApplication bit
|
|
|
|
ncch_header = 'ncch_header.bin'
|
|
if os.path.isfile('exheader.bin'):
|
|
exheader = 'exheader.bin'
|
|
else:
|
|
exheader = ''
|
|
if os.path.isfile('logo.bin'):
|
|
logo = 'logo.bin'
|
|
else:
|
|
logo = ''
|
|
if os.path.isfile('plain.bin'):
|
|
plain = 'plain.bin'
|
|
else:
|
|
plain = ''
|
|
if os.path.isfile('exefs.bin'):
|
|
exefs = 'exefs.bin'
|
|
else:
|
|
exefs = ''
|
|
if os.path.isfile('romfs.bin'):
|
|
romfs = 'romfs.bin'
|
|
else:
|
|
romfs = ''
|
|
NCCHBuilder(ncch_header=ncch_header, exheader=exheader, logo=logo, plain=plain, exefs=exefs, romfs=romfs, regen_sig=regen_sig, dev=cia_dev, out=i)
|
|
for j in [ncch_header, exheader, logo, plain, exefs, romfs]:
|
|
if j != '':
|
|
os.remove(j)
|
|
|
|
cf = []
|
|
d = {
|
|
'content0.game.ncch': '0000.00000000.ncch',
|
|
'content1.manual.ncch': '0001.00000001.ncch',
|
|
'content2.dlp.ncch': '0002.00000002.ncch'
|
|
}
|
|
for i in ncchs:
|
|
cf.append(d[i])
|
|
shutil.move(i, d[i])
|
|
|
|
TMDBuilder(content_files=cf, content_files_dev=cia_dev, titleID=hex(readle(cci.hdr.mediaID))[2:].zfill(16), title_ver=0, crypt=0, regen_sig=regen_sig, out='tmd')
|
|
tikBuilder(titleID=hex(readle(cci.hdr.mediaID))[2:].zfill(16), title_ver=0, regen_sig=regen_sig, out='tik')
|
|
|
|
CIABuilder(content_files=cf, tik='tik', tmd='tmd', meta=1, dev=cia_dev, out=out)
|
|
|
|
for i in ['cci_header.bin', 'card_info.bin', 'mastering_info.bin', 'initialdata.bin', 'card_device_info.bin', 'tmd', 'tik'] + cf:
|
|
if os.path.exists(i):
|
|
os.remove(i)
|
|
|
|
def cdn2cia(path, out='', title_ver='', cdn_dev=0, cia_dev=0):
|
|
os.chdir(path)
|
|
name = os.path.basename(os.getcwd())
|
|
|
|
content_files = []
|
|
tmds = []
|
|
tmd = ''
|
|
tik = ''
|
|
for i in os.listdir('.'):
|
|
if i.startswith('tmd.'):
|
|
tmds.append(i)
|
|
elif i == 'cetk':
|
|
tik = i
|
|
elif i.startswith('0'):
|
|
content_files.append(i)
|
|
|
|
if len(tmds) == 1: # If only one tmd in CDN dir, use it
|
|
tmd = tmds[0]
|
|
else:
|
|
tmds.sort(key=lambda h: int(h.split('.')[1]))
|
|
if title_ver == '': # If title version not provided, use latest one
|
|
tmd = tmds[-1]
|
|
else:
|
|
tmd = f'tmd.{title_ver}'
|
|
|
|
if cia_dev == 0:
|
|
regen_sig = 'retail'
|
|
else:
|
|
regen_sig = 'dev'
|
|
|
|
t = TMDReader(tmd)
|
|
if out == '':
|
|
out = f'{name}.{t.hdr.title_ver}.cia'
|
|
|
|
cdn = CDNReader(content_files=content_files, tmd=tmd, dev=cdn_dev)
|
|
cdn.decrypt()
|
|
cf = [i for i in os.listdir('.') if i.endswith('.ncch') or i.endswith('.nds')]
|
|
|
|
if tik == '':
|
|
tikBuilder(titleID=t.titleID, title_ver=t.hdr.title_ver, titlekey=hex(readbe(cdn.titlekey))[2:].zfill(32), regen_sig=regen_sig, out='tik')
|
|
tik = 'tik'
|
|
|
|
meta = 1
|
|
if t.titleID[3:5] == '48':
|
|
meta = 0
|
|
CIABuilder(content_files=cf, tik=tik, tmd=tmd, meta=meta, dev=cia_dev, out='tmp.cia')
|
|
for i in cf:
|
|
os.remove(i)
|
|
if os.path.isfile('tik'):
|
|
os.remove('tik')
|
|
|
|
shutil.move('tmp.cia', '../tmp.cia')
|
|
os.chdir('..')
|
|
shutil.move('tmp.cia', out)
|
|
|
|
def cia2cdn(path, out='', titlekey='', cia_dev=0):
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
if out == '':
|
|
out = name
|
|
|
|
cia = CIAReader(path, cia_dev)
|
|
cia.extract()
|
|
for i in ['cia_header.bin', 'cert.bin', 'meta.bin']:
|
|
if os.path.isfile(i):
|
|
os.remove(i)
|
|
|
|
tik = 'tik'
|
|
tik_read = tikReader(tik)
|
|
if not tik_read.verify()[0][1]: # Ticket has invalid sig
|
|
tik = ''
|
|
|
|
cf = [i for i in os.listdir('.') if i.endswith('.ncch') or i.endswith('.nds')]
|
|
CDNBuilder(content_files=cf, tik=tik, tmd='tmd', titlekey=titlekey, out=out)
|
|
|
|
for i in ['tik', 'tmd'] + cf:
|
|
if os.path.isfile(i):
|
|
os.remove(i)
|
|
|
|
def csu2retailcias(path, out=''):
|
|
if out == '':
|
|
out = 'updates_retail/'
|
|
|
|
cci = CCIReader(path, dev=1)
|
|
cci.extract()
|
|
|
|
n = NCCHReader('content0.game.ncch', dev=1)
|
|
n.extract()
|
|
romfs = RomFSReader('romfs.bin')
|
|
romfs.extract()
|
|
|
|
cnt = cntReader('romfs/contents/Contents.cnt', 'romfs/contents/CupList')
|
|
cnt.extract()
|
|
|
|
for i in ['cci_header.bin', 'card_info.bin', 'mastering_info.bin', 'initialdata.bin', 'card_device_info.bin', 'content0.game.ncch', 'ncch_header.bin', 'exheader.bin', 'logo.bin', 'plain.bin', 'exefs.bin', 'romfs.bin']:
|
|
if os.path.exists(i):
|
|
os.remove(i)
|
|
shutil.rmtree('romfs/')
|
|
|
|
if not os.path.isdir(out):
|
|
os.mkdir(out)
|
|
for i in os.listdir('updates/'):
|
|
cia_dev2retail(path=os.path.join('updates/', i), out=os.path.join(out, i))
|
|
shutil.rmtree('updates/')
|