From 5b4291ee334d83675f1fc68cd63a71e245961881 Mon Sep 17 00:00:00 2001 From: xprism1 Date: Thu, 24 Aug 2023 23:20:36 +0800 Subject: [PATCH] ctr_cnt: add support for cnt without CupList --- README.md | 4 ++-- lib/ctr_cnt.py | 63 +++++++++++++++++++++++++++++++++----------------- utils.py | 2 +- 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 8867bee..78517b9 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ python3 ntool.py cci_retail2dev (--out ) - **WARNING: Only perform this on SysNAND if you are able to use ntrboot to recover from a brick!** - First, obtain the SystemUpdaterForCTR zip file from NDP if you have a o3DS/o3DS XL/2DS. For n3DS/n3DS XL/n2DS XL, obtain the SystemUpdaterForSNAKE zip file instead - Extract the zip file, and choose the appropriate .csu file for your 3DS's region -- Run `python3 ntool.py csu2retailcias updates/` -- Place the `updates` folder in the root of your 3DS's SD +- Run `python3 ntool.py csu2retailcias ` +- Place the `updates_retail` folder in the root of your 3DS's SD and rename it to `updates` - Install [sysUpdater](https://github.com/profi200/sysUpdater), launch it and follow the on-screen instructions - You may need to enable `Set developer UNITINFO` in Luma3DS settings diff --git a/lib/ctr_cnt.py b/lib/ctr_cnt.py index cbc5358..4a5b19e 100644 --- a/lib/ctr_cnt.py +++ b/lib/ctr_cnt.py @@ -1,5 +1,6 @@ from .common import * from .keys import * +from .ctr_cia import CIAReader class cntRecord(Structure): _pack_ = 1 @@ -31,29 +32,43 @@ class cntHdr(Structure): pass class cntReader: - def __init__(self, cuplist, cnt): # files named 'CupList' and 'Contents.cnt' respectively - self.cuplist = cuplist + def __init__(self, cnt, cuplist=''): # files named 'Contents.cnt' and 'CupList' respectively self.cnt = cnt - - with open(cuplist, 'rb') as f: - cupdata = f.read() - - tidlist = [] - for i in range(0, 0x800, 8): - if cupdata[i:i + 8] == b'\x00' * 8: - break - tidlist.append(hex(readle(cupdata[i:i + 8]))[2:].zfill(16)) - self.tidlist = tidlist - - with open(cnt, 'rb') as f: - self.cnt_hdr = cntHdr(f.read(0x1400)) - + self.cuplist = cuplist files = {} - for i in range(len(tidlist)): - files[f'{tidlist[i]}.cia'] = { - 'size': self.cnt_hdr.content_records[i].offset_end - self.cnt_hdr.content_records[i].offset, - 'offset': self.cnt_hdr.content_records[i].offset + 0x1400 - 2048 - } + + if self.cuplist != '': + with open(cuplist, 'rb') as f: + cupdata = f.read() + + tidlist = [] + for i in range(0, 0x800, 8): + if cupdata[i:i + 8] == b'\x00' * 8: + break + tidlist.append(hex(readle(cupdata[i:i + 8]))[2:].zfill(16)) + self.tidlist = tidlist + + with open(cnt, 'rb') as f: + self.cnt_hdr = cntHdr(f.read(0x1400)) + + for i in range(len(tidlist)): + files[f'{tidlist[i]}.cia'] = { + 'size': self.cnt_hdr.content_records[i].offset_end - self.cnt_hdr.content_records[i].offset, + 'offset': self.cnt_hdr.content_records[i].offset + 0xC00 # Offsets are relative from the start of the content records + } + else: + with open(cnt, 'rb') as f: + f.seek(0xC00) + content_records = f.read(0x800) + for i in range(0, 0x800, 8): + offset = readle(content_records[i:i + 4]) + offset_end = readle(content_records[i + 4:i + 8]) + if offset == 0: + break + files[f'{(i // 8) + 1}.cia'] = { + 'size': offset_end - offset, + 'offset': offset + 0xC00 + } self.files = files def extract(self): @@ -68,6 +83,12 @@ class cntReader: for data in read_chunks(f, info['size']): g.write(data) g.close() + + # Rename CIAs if no cuplist + if self.cuplist == '': + cia = CIAReader(os.path.join(output_dir, name)) + titleID = cia.tmd.titleID + os.rename(os.path.join(output_dir, name), f"{os.path.join(output_dir, titleID)}.cia") f.close() print(f'Extracted to {output_dir}') \ No newline at end of file diff --git a/utils.py b/utils.py index 74aa783..df407b5 100644 --- a/utils.py +++ b/utils.py @@ -669,7 +669,7 @@ def csu2retailcias(path, out=''): romfs = RomFSReader('romfs.bin') romfs.extract() - cnt = cntReader('romfs/contents/CupList', 'romfs/contents/Contents.cnt') + 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']: