Ejemplo n.º 1
0
def download(url,ofolder,sz=64,trimm=True,file=None):
	if file==None:
		file=location(url)
	res=file.response;readable=file.readable;fname=file.name
	file_id=file.ID;
	if not readable:
		return False
	totsize=file.size
	output=os.path.join(ofolder,fname)
	buf=int(sz*1024)
	if fname.endswith(".xci") and trimm==True:
		end=DriveTools.get_xci_rom_size(file)
		hd={"Range" : 'bytes=%s-%s' % (0,end)}
		file.sess.headers=hd	
		file.response = file.sess.get(file.url, stream=True)	
		totsize=end
	else:
		res = file.rewind()
	print("Downloading file to {}".format(output))
	t = tqdm(total=totsize, unit='B', unit_scale=True, leave=False)	
	with open(output,"wb") as o:
		for data in res.iter_content(chunk_size=buf):
			o.write(data)
			p=file.position
			t.update(len(data));file.position=p+len(data)
			if not data:
				break	
	t.close()
Ejemplo n.º 2
0
def install_xci_csv(filepath=None,remote=None,destiny="SD",cachefolder=None,override=False,keypatch=False):
	if filepath=="":
		filepath=None
	if remote=="":
		remote=None
	if remote==None:
		test=filepath.split('|');TD=None
		if len(test)<2:
			filepath=test[0]		
			lib,TD,libpath=get_library_from_path(remote_lib_file,filepath)
		else:
			filepath=test[0]	
			TD=test[1]			
			if str(TD).upper()=="NONE":
				TD=None	
		ID,name,type,size,md5,remote=DrivePrivate.get_Data(filepath,TD=TD,Print=False)
	check_connection()
	if cachefolder==None:
		cachefolder=os.path.join(ztools_dir, '_mtp_cache_')	
	files_list=DriveTools.get_files_from_head(remote,remote.name)		
	remote.rewind()		
	print(f"Installing {remote.name} by content")		
	print('- Parsing headers...')
	files=list();filesizes=list()
	fplist=list()	
	counter=0		
	for k in range(len(files_list)):
		entry=files_list[k]
		cnmtfile=entry[0]	
		if cnmtfile.endswith('.cnmt.nca'):
			counter+=1		
	print(f"- Detected {counter} content ids")				
	for i in range(len(files_list)):
		entry=files_list[i]
		cnmtfile=entry[0];
		if cnmtfile.endswith('.cnmt.nca'):
			target_cnmt=cnmtfile		
			nspname=gen_xci_parts_spec0(remote=remote,target_cnmt=target_cnmt,cachefolder=cachefolder,keypatch=keypatch)
			if (remote.name).endswith('xcz'):
				nspname=nspname[:-1]+'z'		
			files_csv=os.path.join(cachefolder, 'remote_files.csv')		
			process=subprocess.Popen([nscb_mtp,"GDInstallfromCSV","-cs",files_csv,"-nm",nspname,"-dst",destiny])		
			while process.poll()==None:
				if process.poll()!=None:
					process.terminate();	
			counter-=1		
			print('\n- Still '+str(counter)+' subitems to process')	
			if counter>0:
				print("")		
	if os.path.exists(cachefolder):			
		for f in os.listdir(cachefolder):
			fp = os.path.join(cachefolder, f)
			try:
				shutil.rmtree(fp)
			except OSError:
				os.remove(fp)		
Ejemplo n.º 3
0
def folderpicker(path, ofolder=None, TD=None, filter=None, mode='download'):
    pathlist = search_folder(path, TD, pick=True, Print=False, filter=filter)
    counter = len(pathlist)
    for path in pathlist:
        if mode == 'download':
            download(path, ofolder, TD)
        elif mode == 'get_cnmt_data':
            DriveTools.get_cnmt_data(path, TD)
        elif mode == 'decompress':
            from Drive.Decompress import decompress
            decompress(path, ofolder, TD)
        elif mode == 'supertrimm':
            from Drive.XciTools import supertrimm
            supertrimm(path, ofolder, TD)
        elif mode == 'read_cnmt':
            from Drive.Info import read_cnmt
            read_cnmt(path, TD)
        counter -= 1
        if counter > 0:
            print("Still {} files to download".format(str(counter)))
Ejemplo n.º 4
0
def download(path, ofolder, TD=None, filter=None, trimm=True):
    if path == 'pick':
        account = token_picker()
        TD = TD_picker(account)
        return
    test = path.split(".+")
    if TD == 'pick':
        TD = TD_picker(path)
    if len(test) > 1 or path.endswith('/') or path.endswith('\\'):
        type = "folder"
    else:
        ID, name, type, size, md5, remote = get_Data(path, TD=TD, Print=False)
        output = os.path.join(ofolder, name)
    if type != 'file':
        # print('Path is a folder')
        folderpicker(path, ofolder, TD, filter)
        return
    else:
        if name.endswith(".xci") and trimm == True:
            end = DriveTools.get_xci_rom_size(remote)
            hd = get_html_header(remote.access_token, off1=0, off2=end)
            remote.get_session(hd)
            size = end
        else:
            remote.get_session()
        buf = int(64 * 1024)
        print("- Downloading file to {}".format(output))
        t = tqdm(total=int(size), unit='B', unit_scale=True, leave=False)
        with open(output, "wb") as o:
            for data in remote.response.iter_content(chunk_size=buf):
                o.write(data)
                t.update(len(data))
                if not data:
                    break
        t.close()
        print("  *Finished*")
Ejemplo n.º 5
0
def gen_xci_parts_spec1(filepath=None,remote=None,target_cnmt=None,cachefolder=None,keypatch=False,files_list=None):
	if filepath=="":
		filepath=None
	if remote=="":
		remote=None
	if remote==None:
		test=filepath.split('|');TD=None
		if len(test)<2:
			filepath=test[0]		
			lib,TD,libpath=get_library_from_path(remote_lib_file,filepath)
		else:
			filepath=test[0]	
			TD=test[1]			
			if str(TD).upper()=="NONE":
				TD=None	
		ID,name,type,size,md5,remote=DrivePrivate.get_Data(filepath,TD=TD,Print=False)
	if keypatch!=False:
		try:
			keypatch=int(keypatch)
		except:	keypatch=False
	if cachefolder==None:
		cachefolder=os.path.join(ztools_dir, '_mtp_cache_')	
	if not os.path.exists(cachefolder):
		os.makedirs(cachefolder)
	else:
		for f in os.listdir(cachefolder):
			fp = os.path.join(cachefolder, f)
			try:
				shutil.rmtree(fp)
			except OSError:
				os.remove(fp)
	if files_list==None:		
		files_list=DriveTools.get_files_from_head(remote,remote.name)
	files=list();filesizes=list()
	fplist=list()
	for k in range(len(files_list)):
		entry=files_list[k]
		fplist.append(entry[0])
	if target_cnmt==None:	
		for i in range(len(files_list)):			
			entry=files_list[i]
			cnmtfile=entry[0]	
			if cnmtfile.endswith('.cnmt.nca'):
				target_cnmt=cnmtfile
				break				
	for i in range(len(files_list)):
		entry=files_list[i]
		cnmtfile=entry[0]
		if cnmtfile.endswith('.cnmt.nca') and target_cnmt==cnmtfile:
			metadict,d1,d2=DriveTools.get_cnmt_data(target=cnmtfile,file=remote)
			ncadata=metadict['ncadata']		
			for j in range(len(ncadata)):		
				row=ncadata[j]
				if row['NCAtype']!='Meta' and row['NCAtype']!='Program':
					test1=str(row['NcaId'])+'.nca';test2=str(row['NcaId'])+'.ncz'
					if test1 in fplist:
						files.append(str(row['NcaId'])+'.nca')
						filesizes.append(int(row['Size']))
					elif test2 in fplist:	
						files.append(str(row['NcaId'])+'.ncz')
						for k in range(len(files_list)):
							entry=files_list[k]
							if entry[0]==test2:				
								filesizes.append(int(entry[3]))	
								break						
			for j in range(len(ncadata)):
				row=ncadata[j]						
				if row['NCAtype']=='Meta':
					# print(str(row['NcaId'])+'.cnmt.nca')
					files.append(str(row['NcaId'])+'.cnmt.nca')
					filesizes.append(int(row['Size']))	
			for j in range(len(ncadata)):
				row=ncadata[j]
				# print(row)
				if row['NCAtype']=='Program':
					test1=str(row['NcaId'])+'.nca';test2=str(row['NcaId'])+'.ncz'
					if test1 in fplist:
						files.append(str(row['NcaId'])+'.nca')
						filesizes.append(int(row['Size']))
					elif test2 in fplist:	
						files.append(str(row['NcaId'])+'.ncz')
						for k in range(len(files_list)):
							entry=files_list[k]
							if entry[0]==test2:				
								filesizes.append(int(entry[3]))	
								break				
			break				
	remote.rewind()				
	outheader = sq_tools.gen_nsp_header(files,filesizes)	
	properheadsize=len(outheader)
	# print(properheadsize)
	# print(bucketsize)
	i=0;sum=properheadsize;
	outfile=os.path.join(cachefolder, "0")
	outf = open(outfile, 'w+b')		
	outf.write(outheader)	
	written=0
	for fi in files:
		if fi.endswith('nca') or fi.endswith('ncz') :	
			for i in range(len(files_list)):
				if str(files_list[i][0]).lower() == str(fi).lower():
					nca_name=files_list[i][0]
					off1=files_list[i][1]
					off2=files_list[i][2]
					nca_size=files_list[i][3]
					break				
			data=remote.read_at(off1,nca_size)		
			ncaHeader = NcaHeader()
			ncaHeader.open(MemoryFile(remote.read_at(off1,0x400), FsType.Crypto.XTS, uhx(Keys.get('header_key'))))	
			crypto1=ncaHeader.getCryptoType()
			crypto2=ncaHeader.getCryptoType2()		
			if crypto2>crypto1:
				masterKeyRev=crypto2
			if crypto2<=crypto1:	
				masterKeyRev=crypto1									
			crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), ncaHeader.keyIndex))
			hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))	
			gc_flag='00'*0x01					
			crypto1=ncaHeader.getCryptoType()
			crypto2=ncaHeader.getCryptoType2()					
			if ncaHeader.getRightsId() != 0:					
				ncaHeader.rewind()	
				if crypto2>crypto1:
					masterKeyRev=crypto2
				if crypto2<=crypto1:	
					masterKeyRev=crypto1								
				titleKeyDec = Keys.decryptTitleKey(titleKey, Keys.getMasterKeyIndex(int(masterKeyRev)))							
				encKeyBlock = crypto.encrypt(titleKeyDec * 4)
				if str(keypatch) != "False":
					t = tqdm(total=False, unit='B', unit_scale=False, leave=False)	
					if keypatch < ncaHeader.getCryptoType2():
						encKeyBlock,crypto1,crypto2=get_new_cryptoblock(ncaHeader,keypatch,encKeyBlock,t)	
					t.close()
			if ncaHeader.getRightsId() == 0:
				ncaHeader.rewind()											
				encKeyBlock = ncaHeader.getKeyBlock()	
				if str(keypatch) != "False":
					t = tqdm(total=False, unit='B', unit_scale=False, leave=False)								
					if keypatch < ncaHeader.getCryptoType2():
						encKeyBlock,crypto1,crypto2=get_new_cryptoblock(ncaHeader,keypatch,encKeyBlock,t)	
					t.close()									
			ncaHeader.rewind()					
			i=0				
			newheader=get_newheader(MemoryFile(remote.read_at(off1,0xC00)),encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)	
			outf.write(newheader)
			written+=len(newheader)
			break					
		else:pass					
	outf.flush()							
	outf.close()	
	tfile=os.path.join(cachefolder, "remote_files.csv")	
	with open(tfile,'w') as csvfile:	
		csvfile.write("{}|{}|{}|{}|{}|{}|{}\n".format("step","filepath","size","targetsize","off1","off2","token"))	
		csvfile.write("{}|{}|{}|{}|{}|{}|{}\n".format(0,outfile,properheadsize+written,properheadsize,0,properheadsize,"False"))	
		k=0;l=0	
		for fi in files:
			for j in files_list:
				if j[0]==fi:	
					csvfile.write("{}|{}|{}|{}|{}|{}|{}\n".format(k+1,outfile,properheadsize+written,0xC00,(properheadsize+l*0xC00),(properheadsize+(l*0xC00)+0xC00),"False"))	
					off1=j[1]+0xC00
					off2=j[2]
					targetsize=j[3]-0xC00				
					URL='https://www.googleapis.com/drive/v3/files/'+remote.ID+'?alt=media'		
					token=remote.access_token					
					csvfile.write("{}|{}|{}|{}|{}|{}|{}\n".format(k+2,URL,remote.size,targetsize,off1,off2,token))	
					break
			k+=2;l+=1	
	nspname="test.nsp"				
	try:
		g=remote.name			
		g0=[pos for pos, char in enumerate(g) if char == '[']
		g0=(g[0:g0[0]]).strip()			
		titleid=metadict['titleid']
		titleversion=metadict['version']
		ctype=metadict['ctype']
		nspname=f"{g0} [{titleid}] [v{titleversion}] [{ctype}].nsp"
	except:pass
	return nspname						
Ejemplo n.º 6
0
	def detect_xci_fw(filepath,doprint=True,remote=False):
		import sq_tools	
		import nutdb
		nutdb.check_other_file(urlconfig,'fw',nutdb=False)
		FW=None;dump={}
		if remote==False:
			xcifw=sq_tools.ret_xci_offsets_fw(filepath)
		else:
			from Drive import DriveTools
			xcifw=DriveTools.get_files_from_head_xci_fw(filepath)
		# print(xcifw)
		fwfiles=list()
		if len(xcifw)>0:
			for i in xcifw:
				entry=i
				fwfiles.append(entry[0])
		else: 
			if doprint==True:
				print('- Firmware was deleted from file')		
			return 'Deleted'
		# print(fwfiles)
		if os.path.exists(fwdb):
			with open(fwdb) as json_file:	
				data = json.load(json_file)	
				for i in data:	
					dict=data[i]
					if len(dict.items())>0:
						dump[i]=dict
					else: return False	
		for i in fwfiles:
			for j in dump:
				entry=dump[j]
				try:
					files=entry['specific']
					if i in files:
						FW=j
						if doprint==True:
							print('- Xci includes firmware: '+FW)
						return FW
				except:
					try:
						RSVcnmt=entry['RSVcnmt']
						if str(RSVcnmt).lower()==str(i).lower():
							FW=j
							if doprint==True:
								print('- Xci includes firmware: '+FW)
							return FW
					except:pass		
		fwfiles.sort()
		for j in dump:
			entry=dump[j]
			files=entry['files']
			files.sort()
			if fwfiles==files:
				FW=j
				if doprint==True:
					print('- Xci includes firmware: '+FW)
				return FW
		try:
			FW,fwver,unique=FWDB.read_fw(filepath,add=True,doprint=False)
			if doprint==True:
				print('- Xci includes firmware: '+FW)
			return FWnumber
		except:pass
		if doprint==True:
			print('- Unknown firmware')
		return 'UNKNOWN'	
def public_gdrive_install(filepath,destiny="SD",truecopy=True,outfolder=None,ch_medium=True,check_fw=True,patch_keygen=False,ch_base=False,ch_other=False,installed_list=False):
	check_connection()
	lib,TD,libpath=get_cache_lib()
	if lib==None:
		sys.exit(f"Google Drive Public Links are only supported via cache folder")	
	filename=addtodrive(filepath,truecopy=truecopy)
	ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)	
	token=remote.access_token
	name=remote.name
	sz=remote.size
	URL='https://www.googleapis.com/drive/v3/files/'+remote.ID+'?alt=media'	
	ext=name.split('.')
	ext=ext[-1]
	if not name.endswith('nsp') and not name.endswith('nsz') and not  name.endswith('xci') and not name.endswith('xcz'):
		print(f"Extension not supported for direct instalation {ext} in {name}")
		return False
	print("- Retrieving Space on device")
	SD_ds,SD_fs,NAND_ds,NAND_fs,FW,device=get_storage_info()
	print("- Calculating Installed size")	
	filesize=int(sz)
	if destiny=="SD":
		print(f"  * SD free space: {SD_fs} ({sq_tools.getSize(SD_fs)})")	
		print(f"  * File size: {filesize} ({sq_tools.getSize(filesize)})")		
		if filesize>SD_fs:
			if filesize<NAND_fs and ch_medium==True:
				print("  Not enough space on SD. Changing target to EMMC")
				print(f"  * EMMC free space: {NAND_fs} ({sq_tools.getSize(NAND_fs)})")						
				destiny="NAND"
			elif  ch_medium==False:	
				sys.exit("   NOT ENOUGH SPACE SD STORAGE")				
			else:
				sys.exit("   NOT ENOUGH SPACE ON DEVICE")				
	else:
		print(f"  * EMMC free space: {NAND_fs} ({sq_tools.getSize(NAND_fs)})")	
		print(f"  * File size: {filesize} ({sq_tools.getSize(filesize)})")		
		if filesize>NAND_fs:		
			if filesize<SD_fs and ch_medium==True:
				print("  Not enough space on EMMC. Changing target to SD")			
				print(f"  * SD free space: {SD_fs} ({sq_tools.getSize(SD_fs)})")					
				destiny="SD"
			elif  ch_medium==False:	
				sys.exit("   NOT ENOUGH SPACE EMMC STORAGE")							
			else:
				sys.exit("   NOT ENOUGH SPACE ON DEVICE")
	kgwarning=False						
	if check_fw==True:	
		try:
			cnmtdata,files_list,remote=DriveTools.get_cnmt_data(file=remote)		
			keygeneration=int(cnmtdata['keygeneration'])
			if FW!='unknown':	
				try:
					FW_RSV,RRSV=sq_tools.transform_fw_string(FW)
					FW_kg=sq_tools.kg_by_RSV(FW_RSV)
				except BaseException as e:
					Print.error('Exception: ' + str(e))
					FW='unknown'
					FW_kg='unknown'
					pass
			if FW!='unknown' and FW_kg!='unknown':			
				if int(keygeneration)>int(FW_kg):
					kgwarning=True
					tgkg=int(FW_kg)
				else:
					tgkg=keygeneration
			else:
				tgkg=keygeneration
			print(f"- Console Firmware: {FW} ({FW_RSV}) - keygen {FW_kg})")		
			print(f"- File keygeneration: {keygeneration}")				
			if kgwarning==True:
				print("File requires a higher firmware. Skipping...")
				return False
		except:	
			print("Error getting cnmtdata from file")		
	if installed_list!=False:
		try:
			fileid,fileversion,cctag,nG,nU,nD,baseid=listmanager.parsetags(name)
			fileversion=int(fileversion)
			if fileid.endswith('000') and fileversion==0 and fileid in installed_list.keys() and ch_base==True:
				print("Base game already installed. Skipping...")
				return False	
			elif fileid.endswith('000') and fileid in installed_list.keys() and ch_other==True:
				updid=fileid[:-3]+'800'
				if fileversion>((installed_list[fileid])[2]):
					print("Asking DBI to delete previous content")
					process=subprocess.Popen([nscb_mtp,"DeleteID","-ID",fileid])	
					while process.poll()==None:
						if process.poll()!=None:
							process.terminate();					
					process=subprocess.Popen([nscb_mtp,"DeleteID","-ID",updid])		
					while process.poll()==None:
						if process.poll()!=None:
							process.terminate();					
				else:
					print("The update is a previous version than the installed on device.Skipping..")
					listmanager.striplines(tfile,counter=True)
					return False		
			elif ch_other==True	and fileid in installed_list.keys():
				if fileversion>((installed_list[fileid])[2]):
					print("Asking DBI to delete previous update")
					process=subprocess.Popen([nscb_mtp,"DeleteID","-ID",fileid])					
					while process.poll()==None:
						if process.poll()!=None:
							process.terminate();
				else:
					print("The update is a previous version than the installed on device.Skipping..")
					listmanager.striplines(tfile,counter=True)
					return False	
		except:pass			
	if name.endswith('xci') or name.endswith('xcz'):
		from mtpxci_remote import install_xci_csv
		install_xci_csv(remote=remote,destiny=destiny,cachefolder=outfolder)
	else:			
		process=subprocess.Popen([nscb_mtp,"DriveInstall","-ori",URL,"-dst",destiny,"-name",name,"-size",sz,"-tk",token])
		while process.poll()==None:
			if process.poll()!=None:
				process.terminate();		
def decompress_xcz(remote, ofolder):
    buf = 64 * 1024
    buffer = buf
    endname = remote.name[:-1] + 'i'
    output = os.path.join(ofolder, endname)
    files_list = DriveTools.get_files_from_head(remote, remote.name)
    remote.rewind()
    # print(files_list)
    print('Decompressing {}'.format(remote.name))
    print('- Parsing headers...')
    files = list()
    filesizes = list()
    fplist = list()
    for k in range(len(files_list)):
        entry = files_list[k]
        fplist.append(entry[0])
    for i in range(len(files_list)):
        entry = files_list[i]
        cnmtfile = entry[0]
        metadict = {}
        if cnmtfile.endswith('.cnmt.nca'):
            metadict, d1, d2 = DriveTools.get_cnmt_data(target=cnmtfile,
                                                        file=remote)
            ncadata = metadict['ncadata']
            for j in range(len(ncadata)):
                row = ncadata[j]
                # print(row)
                if row['NCAtype'] != 'Meta':
                    test1 = str(row['NcaId']) + '.nca'
                    test2 = str(row['NcaId']) + '.ncz'
                    if test1 in fplist or test2 in fplist:
                        # print(str(row['NcaId'])+'.nca')
                        files.append(str(row['NcaId']) + '.nca')
                        filesizes.append(int(row['Size']))
                else:
                    # print(str(row['NcaId'])+'.cnmt.nca')
                    files.append(str(row['NcaId']) + '.cnmt.nca')
                    filesizes.append(int(row['Size']))
            for k in range(len(files_list)):
                entry = files_list[k]
                fp = entry[0]
                sz = int(entry[3])
                if fp.endswith('xml'):
                    files.append(fp)
                    filesizes.append(sz)
            for k in range(len(files_list)):
                entry = files_list[k]
                fp = entry[0]
                sz = int(entry[3])
                if fp.endswith('.tik'):
                    files.append(fp)
                    filesizes.append(sz)
            for k in range(len(files_list)):
                entry = files_list[k]
                fp = entry[0]
                sz = int(entry[3])
                if fp.endswith('.cert'):
                    files.append(fp)
                    filesizes.append(sz)
    sec_hashlist = list()
    try:
        for target in files:
            sha, size, gamecard = DriveTools.file_hash(remote, target,
                                                       files_list)
            # print(sha)
            if sha != False:
                sec_hashlist.append(sha)
    except BaseException as e:
        Print.error('Exception: ' + str(e))
    # print(sec_hashlist)
    xci_header, game_info, sig_padding, xci_certificate, root_header, upd_header, norm_header, sec_header, rootSize, upd_multiplier, norm_multiplier, sec_multiplier = sq_tools.get_xciheader(
        files, filesizes, sec_hashlist)
    outheader = xci_header
    outheader += game_info
    outheader += sig_padding
    outheader += xci_certificate
    outheader += root_header
    outheader += upd_header
    outheader += norm_header
    outheader += sec_header
    properheadsize = len(outheader)
    totsize = properheadsize
    for s in filesizes:
        totsize += s
    # Hex.dump(outheader)
    # print(totsize)
    print(files)
    t = tqdm(total=totsize, unit='B', unit_scale=True, leave=False)
    with open(output, 'wb') as o:
        o.write(outheader)
        t.update(len(outheader))
    for file in files:
        if file.endswith('cnmt.nca'):
            for i in range(len(files_list)):
                if str(files_list[i][0]).lower() == str(file).lower():
                    nca_name = files_list[i][0]
                    off1 = files_list[i][1]
                    off2 = files_list[i][2]
                    nca_size = files_list[i][3]
                    break
            t.write('- Appending {}'.format(nca_name))
            data = remote.read_at(off1, nca_size)
            with open(output, 'ab') as o:
                o.write(data)
                t.update(len(data))
        elif file.endswith('.nca'):
            for i in range(len(files_list)):
                if str(files_list[i][0]).lower() == str(file).lower():
                    nca_name = files_list[i][0]
                    off1 = files_list[i][1]
                    off2 = files_list[i][2]
                    nca_size = files_list[i][3]
                    t.write('- Appending {}'.format(nca_name))
                    o = open(output, 'ab+')
                    remote.seek(off1, off2)
                    for data in remote.response.iter_content(chunk_size=buf):
                        o.write(data)
                        t.update(len(data))
                        o.flush()
                        if not data:
                            o.close()
                            break
                elif (str(files_list[i][0]).lower())[:-1] == (
                        str(file).lower())[:-1] and str(
                            files_list[i][0]).endswith('ncz'):
                    ncz_name = files_list[i][0]
                    off1 = files_list[i][1]
                    off2 = files_list[i][2]
                    sz = files_list[i][3]
                    t.write('- Calculating compressed sections for: ' +
                            ncz_name)
                    remote.seek(off1, off2)
                    header = remote.read(0x4000)
                    magic = readInt64(remote)
                    sectionCount = readInt64(remote)
                    sections = []
                    for i in range(sectionCount):
                        sections.append(Section(remote))
                    t.write('  Detected {} sections'.format(str(sectionCount)))
                    with open(output, 'rb+') as o:
                        o.seek(0, os.SEEK_END)
                        curr_off = o.tell()
                        t.write('- Appending decompressed {}'.format(ncz_name))
                        t.write('  Writing nca header')
                        o.write(header)
                        t.update(len(header))
                        timestamp = time.time()
                        t.write('  Writing decompressed body in plaintext')
                        count = 0
                        checkstarter = 0
                        dctx = zstandard.ZstdDecompressor()
                        hd = Private.get_html_header(remote.access_token,
                                                     remote.position, off2)
                        remote.get_session(hd)
                        reader = dctx.stream_reader(remote.response.raw,
                                                    read_size=buffer)
                        c = 0
                        spsize = 0
                        for s in sections:
                            end = s.offset + s.size
                            if s.cryptoType == 1:  #plain text
                                t.write('    * Section {} is plaintext'.format(
                                    str(c)))
                                t.write('      %x - %d bytes, Crypto type %d' %
                                        ((s.offset), s.size, s.cryptoType))
                                spsize += s.size
                                end = s.offset + s.size
                                i = s.offset
                                while i < end:
                                    chunkSz = buffer if end - i > buffer else end - i
                                    chunk = reader.read(chunkSz)
                                    if not len(chunk):
                                        break
                                    o.write(chunk)
                                    t.update(len(chunk))
                                    i += chunkSz
                            elif s.cryptoType not in (3, 4):
                                raise IOError('Unknown crypto type: %d' %
                                              s.cryptoType)
                            else:
                                t.write('    * Section {} needs decompression'.
                                        format(str(c)))
                                t.write('      %x - %d bytes, Crypto type %d' %
                                        ((s.offset), s.size, s.cryptoType))
                                t.write('      Key: %s, IV: %s' %
                                        (str(hx(s.cryptoKey)),
                                         str(hx(s.cryptoCounter))))
                                crypto = AESCTR(s.cryptoKey, s.cryptoCounter)
                                spsize += s.size
                                test = int(spsize / (buffer))
                                i = s.offset
                                while i < end:
                                    crypto.seek(i)
                                    chunkSz = buffer if end - i > buffer else end - i
                                    chunk = reader.read(chunkSz)
                                    if not len(chunk):
                                        break
                                    o.write(crypto.encrypt(chunk))
                                    t.update(len(chunk))
                                    i += chunkSz
                            c += 1
                        elapsed = time.time() - timestamp
                        minutes = elapsed / 60
                        seconds = elapsed % 60

                        speed = 0 if elapsed == 0 else (spsize / elapsed)
                        t.write(
                            '\n    Decompressed in %02d:%02d at speed: %.1f MB/s\n'
                            % (minutes, seconds, speed / 1000000.0))
        else:
            for i in range(len(files_list)):
                if str(files_list[i][0]).lower() == str(file).lower():
                    file_name = files_list[i][0]
                    off1 = files_list[i][1]
                    off2 = files_list[i][2]
                    file_size = files_list[i][3]
                    break
            t.write('- Appending {}'.format(file_name))
            data = remote.read_at(off1, file_size)
            with open(output, 'ab') as o:
                o.write(data)
                t.update(len(data))
    t.close()