diff --git a/py/Fs/ChromeNacp.py b/py/Fs/ChromeNacp.py
deleted file mode 100644
index 326313c3..00000000
--- a/py/Fs/ChromeNacp.py
+++ /dev/null
@@ -1,1449 +0,0 @@
-from Fs.File import File
-import Fs.Type
-from binascii import hexlify as hx, unhexlify as uhx
-from enum import IntEnum
-import Print
-
-import Keys
-import re
-import pykakasi
-import io
-indent = 1
-tabs = '\t' * indent
-
-class NacpLanguageType(IntEnum):
- AmericanEnglish = 0
- BritishEnglish = 1
- Japanese = 2
- French = 3
- German = 4
- LatinAmericanSpanish = 5
- Spanish = 6
- Italian = 7
- Dutch = 8
- CanadianFrench = 9
- Portuguese = 10
- Russian = 11
- Korean = 12
- TraditionalChinese = 13
- SimplifiedChinese = 14
-
-class NacpLanguage:
- def __init__(self):
- self.name = None
- self.publisher = None
-
-
-class OrganizationType(IntEnum):
- CERO = 0
- GRACGCRB = 1
- GSRMR = 2
- ESRB = 3
- ClassInd = 4
- USK = 5
- PEGI = 6
- PEGIPortugal = 7
- PEGIBBFC = 8
- Russian = 9
- ACB = 10
- OFLC = 11
-
-class RatingAge:
- def __init__(self):
- self.age = None
-
-
-class ChromeNacp(File):
- def __init__(self, path = None, mode = None, cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
- super(ChromeNacp, self).__init__(path, mode, cryptoType, cryptoKey, cryptoCounter)
-
-
- self.languages = []
-
- for i in range(15):
- self.languages.append(NacpLanguage())
-
- self.isbn = None
- self.startupUserAccount = None
- self.userAccountSwitchLock = None
- self.addOnContentRegistrationType = None
- self.attribute = None
- self.parentalControl = None
- self.screenshot = None
- self.videoCapture = None
- self.dataLossConfirmation = None
- self.playLogPolicy = None
- self.presenceGroupId = None
-
- self.ages = []
-
- for i in range(12):
- self.ages.append(RatingAge())
-
- self.displayVersion = None
- self.addOnContentBaseId = None
- self.saveDataOwnerId = None
- self.userAccountSaveDataSize = None
- self.userAccountSaveDataJournalSize = None
- self.deviceSaveDataSize = None
- self.deviceSaveDataJournalSize = None
- self.bcatDeliveryCacheStorageSize = None
- self.applicationErrorCodeCategory = None
- self.localCommunicationId = None
- self.logoType = None
- self.logoHandling = None
- self.runtimeAddOnContentInstall = None
- self.crashReport = None
- self.hdcp = None
- self.seedForPseudoDeviceId = None
- self.bcatPassphrase = None
- self.userAccountSaveDataSizeMax = None
- self.userAccountSaveDataJournalSizeMax = None
- self.deviceSaveDataSizeMax = None
- self.deviceSaveDataJournalSizeMax = None
- self.temporaryStorageSize = None
- self.cacheStorageSize = None
- self.cacheStorageJournalSize = None
- self.cacheStorageDataAndJournalSizeMax = None
- self.cacheStorageIndexMax = None
- self.playLogQueryableApplicationId = None
- self.playLogQueryCapability = None
- self.repair = None
- self.programIndex = None
- self.requiredNetworkServiceLicenseOnLaunch = None
-
- def html_feed(self,feed='',style=1,message=''):
- if style==1:
- feed+='
{}
'.format(message)
- return feed
- if style==2:
- feed+='{}
'.format(message)
- return feed
- if style==3:
- feed+='{} {} '.format(message[0],message[1])
- return feed
- if style==4:
- feed+='{} {}. {} {} '.format(message[0],message[1],message[2],message[3])
- return feed
- if style==5:
- feed+='{}
'.format(message)
- return feed
- if style==6:
- feed+='{}
'.format(message)
- return feed
-
- def getName(self, i):
- self.seek(i * 0x300)
- self.languages[i].name = self.read(0x200)
- self.languages[i].name = self.languages[i].name.split(b'\0', 1)[0].decode('utf-8')
- return self.languages[i].name
-
- def getPublisher(self, i):
- self.seek(i * 0x300 + 0x200)
- self.languages[i].publisher = self.read(0x100)
- self.languages[i].publisher = self.languages[i].publisher.split(b'\0', 1)[0].decode('utf-8')
- return self.languages[i].publisher
-
- def par_getNameandPub(self, data, feed ='',gui=False,roma=True):
- Langue = list()
- Langue = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
- for i in Langue:
- off1=i*0x300;off2=off1+0x200;off3=off2+0x100
- title=data[off1:off2]
- title = title.split(b'\0', 1)[0].decode('utf-8')
- title = (re.sub(r'[\/\\]+', ' ', title))
- title = title.strip()
- if title == '':
- continue
- else:
- editor=data[off2:off3]
- editor = editor.split(b'\0', 1)[0].decode('utf-8')
- editor = (re.sub(r'[\/\\]+', ' ', editor))
- editor = editor.strip()
- if roma == True:
- if i == 2:
- kakasi = pykakasi.kakasi()
- kakasi.setMode("H", "a")
- kakasi.setMode("K", "a")
- kakasi.setMode("J", "a")
- kakasi.setMode("s", True)
- kakasi.setMode("E", "a")
- kakasi.setMode("a", None)
- kakasi.setMode("C", False)
- converter = kakasi.getConverter()
- title=converter.do(title)
- title=title[0].upper()+title[1:]
- editor=converter.do(editor)
- editor=editor[0].upper()+editor[1:]
- if i == 14 or i == 13 or i==12:
- kakasi = pykakasi.kakasi()
- kakasi.setMode("H", "a")
- kakasi.setMode("K", "a")
- kakasi.setMode("J", "a")
- kakasi.setMode("s", True)
- kakasi.setMode("E", "a")
- kakasi.setMode("a", None)
- kakasi.setMode("C", False)
-
- message=("Language: "+str(NacpLanguageType(i)).replace('NacpLanguageType.', ''));feed=self.html_feed(feed,6,message)
-
- feed+=''
- message=["Name:",title];feed=self.html_feed(feed,3,message)
- message=["Publisher:",editor];feed=self.html_feed(feed,3,message)
- feed+=' '
- return feed
-
-
- def getIsbn(self):
- self.seek(0x3000)
- self.isbn = self.read(0x24).split(b'\0', 1)[0].decode('utf-8')
- return self.isbn
-
- def par_Isbn(self, data, feed =''):
- isbn=data.split(b'\0', 1)[0].decode('utf-8')
- if isbn != '':
- message=["Isbn:", str(isbn)];feed=self.html_feed(feed,3,message)
- return feed
-
- def par_getStartupUserAccount(self, data, feed =''):
- b=data
- if b == 0:
- StartupUserAccount = 'None'
- elif b == 1:
- StartupUserAccount = 'Required'
- elif b == 2:
- StartupUserAccount = 'RequiredWithNetworkServiceAccountAvailable'
- else:
- StartupUserAccount = 'Unknown'
- message=["StartupUserAccount:", str(StartupUserAccount)];feed=self.html_feed(feed,3,message)
- return feed
-
- def getStartupUserAccount(self):
- self.seek(0x3025)
- b = self.readInt8('little')
- if b == 0:
- self.startupUserAccount = 'None'
- elif b == 1:
- self.startupUserAccount = 'Required'
- elif b == 2:
- self.startupUserAccount = 'RequiredWithNetworkServiceAccountAvailable'
- else:
- self.startupUserAccount = 'Unknown'
- return self.startupUserAccount
-
- def par_getUserAccountSwitchLock(self, data, feed =''):
- b=data
- if b == 0:
- userAccountSwitchLock = 'Disable'
- elif b == 1:
- userAccountSwitchLock = 'Enable'
- else:
- userAccountSwitchLock = 'Unknown'
- message=["UserAccountSwitchLock:", str(userAccountSwitchLock)];feed=self.html_feed(feed,3,message)
- return feed
-
- def getUserAccountSwitchLock(self):
- self.seek(0x3026)
- b = self.readInt8('little')
- if b == 0:
- self.userAccountSwitchLock = 'Disable'
- elif b == 1:
- self.userAccountSwitchLock = 'Enable'
- else:
- self.userAccountSwitchLock = 'Unknown'
- return self.userAccountSwitchLock
-
- def par_getAddOnContentRegistrationType(self, data, feed =''):
- b=data
- if b == 0:
- addOnContentRegistrationType = 'AllOnLaunch'
- elif b == 1:
- addOnContentRegistrationType = 'OnDemand'
- else:
- addOnContentRegistrationType = 'Unknown'
- message=["AddOnContentRegistrationType:", str(addOnContentRegistrationType)];feed=self.html_feed(feed,3,message)
- return feed
-
-
- def getAddOnContentRegistrationType(self):
- self.seek(0x3027)
- b = self.readInt8('little')
- if b == 0:
- self.addOnContentRegistrationType = 'AllOnLaunch'
- elif b == 1:
- self.addOnContentRegistrationType = 'OnDemand'
- else:
- self.addOnContentRegistrationType = 'Unknown'
- return self.addOnContentRegistrationType
-
- def par_getContentType(self, data, feed =''):
- b=data
- if b == 0:
- content_type = 'False'
- elif b == 1:
- content_type = 'True'
- elif b == 2:
- content_type = 'RetailInteractiveDisplay'
- else:
- content_type = 'Unknown'
- message=["IsDemo:", str(content_type)];feed=self.html_feed(feed,3,message)
- return feed
-
-
- def getAttribute(self):
- self.seek(0x3028)
- b = self.readInt8('little')
- if b == 0:
- self.attribute = 'None'
- elif b == 1:
- self.attribute = 'Demo'
- elif b == 2:
- self.attribute = 'RetailInteractiveDisplay'
- else:
- self.attribute = 'Unknown'
- return self.attribute
-
- def par_getParentalControl(self, data, feed =''):
- b=data
- if b == 0:
- parentalControl = 'None'
- elif b == 1:
- parentalControl = 'FreeCommunication'
- else:
- parentalControl = 'Unknown'
- message=["ParentalControl:", str(parentalControl)];feed=self.html_feed(feed,3,message)
- return feed
-
-
- def getParentalControl(self):
- self.seek(0x3030)
- b = self.readInt8('little')
- if b == 0:
- self.parentalControl = 'None'
- elif b == 1:
- self.parentalControl = 'FreeCommunication'
- else:
- self.parentalControl = 'Unknown'
- return self.parentalControl
-
- def par_getScreenshot(self, data, feed =''):
- b=data
- if b == 0:
- screenshot = 'Allowed'
- elif b == 1:
- screenshot = 'Denied'
- else:
- screenshot = 'Unknown'
- message=["Screenshots:", str(screenshot)];feed=self.html_feed(feed,3,message)
- return feed
-
-
- def getScreenshot(self):
- self.seek(0x3034)
- b = self.readInt8('little')
- if b == 0:
- self.screenshot = 'Allow'
- elif b == 1:
- self.screenshot = 'Deny'
- else:
- self.screenshot = 'Unknown'
- return self.screenshot
-
- def par_getVideoCapture(self, data, feed =''):
- b=data
- if b == 0:
- videoCapture = 'Disabled'
- elif b == 1:
- videoCapture = 'Manual'
- elif b == 2:
- videoCapture = 'Enabled'
- else:
- videoCapture = 'Unknown'
- message=["VideoCapture:", str(videoCapture)];feed=self.html_feed(feed,3,message)
- return feed
-
- def getVideoCapture(self):
- self.seek(0x3035)
- b = self.readInt8('little')
- if b == 0:
- self.videoCapture = 'Disable'
- elif b == 1:
- self.videoCapture = 'Manual'
- elif b == 2:
- self.videoCapture = 'Enable'
- else:
- self.videoCapture = 'Unknown'
- return self.videoCapture
-
- def par_dataLossConfirmation(self, data, feed =''):
- b=data
- if b == 0:
- dataLossConfirmation = 'None'
- elif b == 1:
- dataLossConfirmation = 'Required'
- else:
- dataLossConfirmation = 'Unknown'
- message=["DataLossConfirmation:", str(dataLossConfirmation)];feed=self.html_feed(feed,3,message)
- return feed
-
- def getDataLossConfirmation(self):
- self.seek(0x3036)
- b = self.readInt8('little')
- if b == 0:
- self.dataLossConfirmation = 'None'
- elif b == 1:
- self.dataLossConfirmation = 'Required'
- else:
- self.dataLossConfirmation = 'Unknown'
- return self.dataLossConfirmation
-
-
- def par_getPlayLogPolicy(self, data, feed =''):
- b=data
- if b == 0:
- playLogPolicy = 'All'
- elif b == 1:
- playLogPolicy = 'LogOnly'
- elif b == 2:
- playLogPolicy = 'None'
- else:
- playLogPolicy = 'Unknown'
- message=["PlayLogPolicy:", str(playLogPolicy)];feed=self.html_feed(feed,3,message)
- return feed
-
- def getPlayLogPolicy(self):
- self.seek(0x3037)
- b = self.readInt8('little')
- if b == 0:
- self.playLogPolicy = 'All'
- elif b == 1:
- self.playLogPolicy = 'LogOnly'
- elif b == 2:
- self.playLogPolicy = 'None'
- else:
- self.playLogPolicy = 'Unknown'
- return self.playLogPolicy
-
- def par_getPresenceGroupId(self, data, feed =''):
- b=data
- message=["PresenceGroupId:", ('0x' + hex(data).replace('0x', '').zfill(16))];feed=self.html_feed(feed,3,message)
- return feed
-
- def getPresenceGroupId(self):
- self.seek(0x3038)
- self.presenceGroupId = self.readInt64('little')
- return self.presenceGroupId
-
- def par_getRatingAge(self,data,i, feed =''):
- b=data
- if b == 0:
- age = '0'
- elif b == 3:
- age = '3'
- elif b == 4:
- age = '4'
- elif b == 6:
- age = '6'
- elif b == 7:
- age = '7'
- elif b == 8:
- age = '8'
- elif b == 10:
- age = '10'
- elif b == 12:
- age = '12'
- elif b == 13:
- age = '13'
- elif b == 14:
- age = '14'
- elif b == 15:
- age = '15'
- elif b == 16:
- age = '16'
- elif b == 17:
- age = '17'
- elif b == 18:
- age = '18'
- else:
- age = 'Unknown'
- if age != 'Unknown':
- message=[(str(OrganizationType(i)).replace('OrganizationType.', '')+ ' Rating'),str(age)];feed=self.html_feed(feed,3,message)
- return feed
-
- def getRatingAge(self, i):
- self.seek(i + 0x3040)
- b = self.readInt8('little')
- if b == 0:
- self.ages[i].age = '0'
- elif b == 3:
- self.ages[i].age = '3'
- elif b == 4:
- self.ages[i].age = '4'
- elif b == 6:
- self.ages[i].age = '6'
- elif b == 7:
- self.ages[i].age = '7'
- elif b == 8:
- self.ages[i].age = '8'
- elif b == 10:
- self.ages[i].age = '10'
- elif b == 12:
- self.ages[i].age = '12'
- elif b == 13:
- self.ages[i].age = '13'
- elif b == 14:
- self.ages[i].age = '14'
- elif b == 15:
- self.ages[i].age = '15'
- elif b == 16:
- self.ages[i].age = '16'
- elif b == 17:
- self.ages[i].age = '17'
- elif b == 18:
- self.ages[i].age = '18'
- else:
- self.ages[i].age = 'Unknown'
- return self.ages[i].age
-
- def par_getDisplayVersion(self, data, feed =''):
- b=data
- message=["Build Number:", (data.split(b'\0', 1)[0].decode('utf-8'))];feed=self.html_feed(feed,3,message)
- return feed
-
- def getDisplayVersion(self):
- self.seek(0x3060)
- self.displayVersion = self.read(0xF)
- self.displayVersion = self.displayVersion.split(b'\0', 1)[0].decode('utf-8')
- return self.displayVersion
-
- def par_getAddOnContentBaseId(self, data, feed =''):
- b=data
- message=["AddOnContentBaseId:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getAddOnContentBaseId(self):
- self.seek(0x3070)
- self.addOnContentBaseId = self.readInt64('little')
- return self.addOnContentBaseId
-
- def par_getSaveDataOwnerId(self, data, feed =''):
- b=data
- message=["SaveDataOwnerId:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getSaveDataOwnerId(self):
- self.seek(0x3078)
- self.saveDataOwnerId = self.readInt64('little')
- return self.saveDataOwnerId
-
- def par_getUserAccountSaveDataSize(self, data, feed =''):
- b=data
- message=["UserAccountSaveDataSize:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getUserAccountSaveDataSize(self):
- self.seek(0x3080)
- self.userAccountSaveDataSize = self.readInt64('little')
- return self.userAccountSaveDataSize
-
- def par_getUserAccountSaveDataJournalSize(self, data, feed =''):
- b=data
- message=["UserAccountSaveDataJournalSize:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getUserAccountSaveDataJournalSize(self):
- self.seek(0x3088)
- self.userAccountSaveDataJournalSize = self.readInt64('little')
- return self.userAccountSaveDataJournalSize
-
- def par_getDeviceSaveDataSize(self, data, feed =''):
- b=data
- if data==0:
- pass
- else:
- message=["DeviceSaveDataSize:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getDeviceSaveDataSize(self):
- self.seek(0x3090)
- self.deviceSaveDataSize = self.readInt64('little')
- return self.deviceSaveDataSize
-
- def par_getDeviceSaveDataJournalSize(self, data, feed =''):
- b=data
- if data==0:
- pass
- else:
- message=["DeviceSaveDataJournalSize:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getDeviceSaveDataJournalSize(self):
- self.seek(0x3098)
- self.deviceSaveDataJournalSize = self.readInt64('little')
- return self.deviceSaveDataJournalSize
-
- def par_getBcatDeliveryCacheStorageSize(self, data, feed =''):
- b=data
- if data==0:
- pass
- else:
- message=["BcatDeliveryCacheStorageSize:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getBcatDeliveryCacheStorageSize(self):
- self.seek(0x30A0)
- self.bcatDeliveryCacheStorageSize = self.readInt64('little')
- return self.bcatDeliveryCacheStorageSize
-
- def par_getApplicationErrorCodeCategory(self, data, feed =''):
- b=data
- applicationErrorCodeCategory = data.split(b'\0', 1)[0].decode('utf-8')
- if applicationErrorCodeCategory == '':
- pass
- else:
- message=["ApplicationErrorCodeCategory:", applicationErrorCodeCategory];feed=self.html_feed(feed,3,message)
- return feed
-
- def getApplicationErrorCodeCategory(self):
- self.seek(0x30A8)
- self.applicationErrorCodeCategory = self.read(0x7).split(b'\0', 1)[0].decode('utf-8')
- return self.applicationErrorCodeCategory
-
- def par_getLocalCommunicationId(self, data, feed =''):
- b=data
- message=["LocalCommunicationId:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getLocalCommunicationId(self):
- self.seek(0x30B0)
- self.localCommunicationId = self.readInt64('little')
- return self.localCommunicationId
-
- def par_getLogoType(self, data, feed =''):
- b=data
- if b == 0:
- logoType = 'LicensedByNintendo'
- elif b == 2:
- logoType = 'Nintendo'
- else:
- logoType = 'Unknown'
- message=["LogoType:", logoType];feed=self.html_feed(feed,3,message)
- return feed
-
- def getLogoType(self):
- self.seek(0x30F0)
- b = self.readInt8('little')
- if b == 0:
- self.logoType = 'LicensedByNintendo'
- elif b == 2:
- self.logoType = 'Nintendo'
- else:
- self.logoType = 'Unknown'
- return self.logoType
-
- def par_getLogoHandling(self, data, feed =''):
- b=data
- if b == 0:
- logoHandling = 'Auto'
- elif b == 1:
- logoHandling = 'Manual'
- else:
- logoHandling = 'Unknown'
- message=["LogoHandling:", logoHandling];feed=self.html_feed(feed,3,message)
- return feed
-
- def getLogoHandling(self):
- self.seek(0x30F1)
- b = self.readInt8('little')
- if b == 0:
- self.logoHandling = 'Auto'
- elif b == 1:
- self.logoHandling = 'Manual'
- else:
- self.logoHandling = 'Unknown'
- return self.logoHandling
-
- def par_getRuntimeAddOnContentInstall(self, data, feed =''):
- b=data
- if b == 0:
- runtimeAddOnContentInstall = 'Deny'
- elif b == 1:
- runtimeAddOnContentInstall = 'AllowAppend'
- else:
- runtimeAddOnContentInstall = 'Unknown'
- message=["RuntimeAddOnContentInstall:", runtimeAddOnContentInstall];feed=self.html_feed(feed,3,message)
- return feed
-
- def getRuntimeAddOnContentInstall(self):
- self.seek(0x30F2)
- b = self.readInt8('little')
- if b == 0:
- self.runtimeAddOnContentInstall = 'Deny'
- elif b == 1:
- self.runtimeAddOnContentInstall = 'AllowAppend'
- else:
- self.runtimeAddOnContentInstall = 'Unknown'
- return self.runtimeAddOnContentInstall
-
- def par_getCrashReport(self, data, feed =''):
- b=data
- if b == 0:
- crashReport = 'Deny'
- elif b == 1:
- crashReport = 'Allow'
- else:
- crashReport = 'Unknown'
- message=["CrashReport:", crashReport];feed=self.html_feed(feed,3,message)
- return feed
-
- def getCrashReport(self):
- self.seek(0x30F6)
- b = self.readInt8('little')
- if b == 0:
- self.crashReport = 'Deny'
- elif b == 1:
- self.crashReport = 'Allow'
- else:
- self.crashReport = 'Unknown'
- return self.crashReport
-
- def par_getHdcp(self, data, feed =''):
- b=data
- if b == 0:
- hdcp = 'None'
- elif b == 1:
- hdcp = 'Required'
- else:
- hdcp = 'Unknown'
- message=["HDCP:", hdcp];feed=self.html_feed(feed,3,message)
- return feed
-
- def getHdcp(self):
- self.seek(0x30F7)
- b = self.readInt8('little')
- if b == 0:
- self.hdcp = 'None'
- elif b == 1:
- self.hdcp = 'Required'
- else:
- self.hdcp = 'Unknown'
- return self.hdcp
-
- def par_getSeedForPseudoDeviceId(self, data, feed =''):
- message=["SeedForPseudoDeviceId:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getSeedForPseudoDeviceId(self):
- self.seek(0x30F8)
- self.seedForPseudoDeviceId = self.readInt64('little')
- return self.seedForPseudoDeviceId
-
- def par_getBcatPassphrase(self, data, feed =''):
- bcatPassphrase = data.split(b'\0', 1)[0].decode('utf-8')
- if bcatPassphrase == 0:
- pass
- elif str(bcatPassphrase) == '':
- pass
- else:
- message=["BcatPassphrase:", str(bcatPassphrase)];feed=self.html_feed(feed,3,message)
- return feed
-
- def getBcatPassphrase(self):
- self.seek(0x3100)
- self.bcatPassphrase = self.read(0x40).split(b'\0', 1)[0].decode('utf-8')
- return self.bcatPassphrase
-
- def par_UserAccountSaveDataSizeMax(self, data, feed =''):
- if data == 0:
- pass
- else:
- message=["UserAccountSaveDataSizeMax:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getUserAccountSaveDataSizeMax(self):
- self.seek(0x3148)
- self.userAccountSaveDataSizeMax = self.readInt64('little')
- return self.userAccountSaveDataSizeMax
-
- def par_UserAccountSaveDataJournalSizeMax(self, data, feed =''):
- if data == 0:
- pass
- else:
- message=["UserAccountSaveDataJournalSizeMax:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getUserAccountSaveDataJournalSizeMax(self):
- self.seek(0x3150)
- self.userAccountSaveDataJournalSizeMax = self.readInt64('little')
- return self.userAccountSaveDataJournalSizeMax
-
- def par_getDeviceSaveDataSizeMax(self, data, feed =''):
- if data == 0:
- pass
- else:
- message=["DeviceSaveDataSizeMax:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getDeviceSaveDataSizeMax(self):
- self.seek(0x3158)
- self.deviceSaveDataSizeMax = self.readInt64('little')
- return self.deviceSaveDataSizeMax
-
- def par_getDeviceSaveDataJournalSizeMax(self, data, feed =''):
- if data == 0:
- pass
- else:
- message=["DeviceSaveDataJournalSizeMax:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getDeviceSaveDataJournalSizeMax(self):
- self.seek(0x3160)
- self.deviceSaveDataJournalSizeMax = self.readInt64('little')
- return self.deviceSaveDataJournalSizeMax
-
- def par_getTemporaryStorageSize(self, data, feed =''):
- if data == 0:
- pass
- else:
- message=["TemporaryStorageSize:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getTemporaryStorageSize(self):
- self.seek(0x3168)
- self.temporaryStorageSize = self.readInt64('little')
- return self.temporaryStorageSize
-
- def par_getCacheStorageSize(self, data, feed =''):
- if data == 0:
- pass
- else:
- message=["CacheStorageSize:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getCacheStorageSize(self):
- self.seek(0x3170)
- self.cacheStorageSize = self.readInt64('little')
- return self.cacheStorageSize
-
- def par_getCacheStorageJournalSize(self, data, feed =''):
- if data == 0:
- pass
- else:
- message=["CacheStorageJournalSize:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getCacheStorageJournalSize(self):
- self.seek(0x3178)
- self.cacheStorageJournalSize = self.readInt64('little')
- return self.cacheStorageJournalSize
-
- def par_getCacheStorageDataAndJournalSizeMax(self, data, feed =''):
- if data == 0:
- pass
- else:
- message=["CacheStorageDataAndJournalSizeMax:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
-
- def getCacheStorageDataAndJournalSizeMax(self):
- self.seek(0x3180)
- self.cacheStorageDataAndJournalSizeMax = self.readInt32('little')
- return self.cacheStorageDataAndJournalSizeMax
-
- def par_getCacheStorageIndexMax(self, data, feed =''):
- if data == 0:
- pass
- else:
- message=["CacheStorageIndexMax:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
- def getCacheStorageIndexMax(self):
- self.seek(0x3188)
- self.cacheStorageIndexMax = self.readInt16('little')
- return self.cacheStorageIndexMax
-
- def par_getPlayLogQueryableApplicationId(self, data, feed =''):
- if data == 0:
- pass
- else:
- message=["PlayLogQueryableApplicationId:", ('0x' + (hex(data).replace('0x', '').zfill(16)).upper())];feed=self.html_feed(feed,3,message)
- return feed
-
-
- def getPlayLogQueryableApplicationId(self):
- self.seek(0x3190)
- self.playLogQueryableApplicationId = self.readInt64('little')
- return self.playLogQueryableApplicationId
-
- def par_getPlayLogQueryCapability(self, data, feed =''):
- b = data;playLogQueryCapability = 'Unknown'
- if b == 0:
- playLogQueryCapability = 'None'
- elif b == 1:
- playLogQueryCapability = 'WhiteList'
- elif b == 2:
- playLogQueryCapability = 'All'
- else:
- playLogQueryCapability = 'Unknown'
- message=["PlayLogQueryCapability:", playLogQueryCapability];feed=self.html_feed(feed,3,message)
- return feed
-
- def getPlayLogQueryCapability(self):
- self.seek(0x3210)
- b = self.readInt8('little')
- if b == 0:
- self.playLogQueryCapability = 'None'
- elif b == 1:
- self.playLogQueryCapability = 'WhiteList'
- elif b == 2:
- self.playLogQueryCapability = 'All'
- else:
- self.playLogQueryCapability = 'Unknown'
- return self.playLogQueryCapability
-
- def par_getRepair(self, data, feed =''):
- b = data
- if b == 0:
- repair = 'None'
- elif b == 1:
- repair = 'SuppressGameCardAccess'
- else:
- repair = 'Unknown'
- message=["Repair:", repair];feed=self.html_feed(feed,3,message)
- return feed
-
-
- def getRepair(self):
- self.seek(0x3211)
- b = self.readInt8('little')
- if b == 0:
- self.repair = 'None'
- elif b == 1:
- self.repair = 'SuppressGameCardAccess'
- else:
- self.repair = 'Unknown'
- return self.repair
-
- def par_getProgramIndex(self, data, feed =''):
- message=["ProgramIndex:", str(data)];feed=self.html_feed(feed,3,message)
- return feed
-
- def getProgramIndex(self):
- self.seek(0x3212)
- self.programIndex = self.readInt8('little')
- return self.programIndex
-
- def par_getRequiredNetworkServiceLicenseOnLaunch(self, data, feed =''):
- b = data
- if b == 0:
- requiredNetworkServiceLicenseOnLaunch = 'None'
- elif b == 1:
- requiredNetworkServiceLicenseOnLaunch = 'Common'
- else:
- requiredNetworkServiceLicenseOnLaunch = 'Unknown'
- message=["RequiredNetworkServiceLicenseOnLaunch:", requiredNetworkServiceLicenseOnLaunch];feed=self.html_feed(feed,3,message)
- return feed
-
- def getRequiredNetworkServiceLicenseOnLaunch(self):
- self.seek(0x3213)
- b = self.readInt8('little')
- if b == 0:
- self.requiredNetworkServiceLicenseOnLaunch = 'None'
- elif b == 1:
- self.requiredNetworkServiceLicenseOnLaunch = 'Common'
- else:
- self.requiredNetworkServiceLicenseOnLaunch = 'Unknown'
- return self.requiredNetworkServiceLicenseOnLaunch
-
-
- def printInfo(self, indent = 0):
- tabs = '\t' * indent
- Print.info('\n%sNintendo Application Control Property (NACP)\n' % (tabs))
- super(ChromeNacp, self).printInfo(indent)
-
- for i in range(15):
- if self.getName(i) == '':
- pass
- else:
- Print.info('Title:')
- Print.info(' Language: ' + str(NacpLanguageType(i)).replace('NacpLanguageType.', ''))
- Print.info(' Name: ' + self.getName(i))
- Print.info(' Publisher: ' + self.getPublisher(i))
-
- if str(self.getIsbn()) == '':
- pass
- else:
- Print.info('Isbn: ' + str(self.getIsbn()))
-
- Print.info('AddOnContentRegistrationType: ' + str(self.getAddOnContentRegistrationType()))
- Print.info('StartupUserAccount: ' + str(self.getStartupUserAccount()))
- Print.info('UserAccountSwitchLock: ' + str(self.getUserAccountSwitchLock()))
- Print.info('Attribute: ' + str(self.getAttribute()))
- Print.info('ParentalControl: ' + str(self.getParentalControl()))
- Print.info('Screenshot: ' + str(self.getScreenshot()))
- Print.info('VideoCapture: ' + str(self.getVideoCapture()))
- Print.info('DataLossConfirmation: ' + str(self.getDataLossConfirmation()))
- Print.info('PlayLogPolicy: ' + str(self.getPlayLogPolicy()))
- Print.info('PresenceGroupId: ' + '0x' + hex(self.getPresenceGroupId()).replace('0x', '').zfill(16))
-
- for i in range(12):
- if str(self.getRatingAge(i)) == 'Unknown':
- pass
- else:
- Print.info('Rating:')
- Print.info(' Organization: ' + str(OrganizationType(i)).replace('OrganizationType.', ''))
- Print.info(' Age: ' + str(self.getRatingAge(i)))
-
- Print.info('DisplayVersion: ' + str(self.getDisplayVersion()))
- Print.info('AddOnContentBaseId: ' + '0x' + hex(self.getAddOnContentBaseId()).replace('0x', '').zfill(16))
- Print.info('SaveDataOwnerId: ' + '0x' + hex(self.getSaveDataOwnerId()).replace('0x', '').zfill(16))
- Print.info('UserAccountSaveDataSize: ' + '0x' + hex(self.getUserAccountSaveDataSize()).replace('0x', '').zfill(16))
- Print.info('UserAccountSaveDataJournalSize: ' + '0x' + hex(self.getUserAccountSaveDataJournalSize()).replace('0x', '').zfill(16))
- Print.info('DeviceSaveDataSize: ' + '0x' + hex(self.getDeviceSaveDataSize()).replace('0x', '').zfill(16))
- Print.info('DeviceSaveDataJournalSize: ' + '0x' + hex(self.getDeviceSaveDataJournalSize()).replace('0x', '').zfill(16))
-
- if hex(self.getBcatDeliveryCacheStorageSize()).replace('0x', '').zfill(16) == '0000000000000000':
- pass
- else:
- Print.info('BcatDeliveryCacheStorageSize: ' + '0x' + hex(self.getBcatDeliveryCacheStorageSize()).replace('0x', '').zfill(16))
-
- if str(self.getApplicationErrorCodeCategory()) == '':
- pass
- else:
- Print.info('ApplicationErrorCodeCategory: ' + str(self.getApplicationErrorCodeCategory()))
-
- Print.info('LocalCommunicationId: ' + '0x' + hex(self.getLocalCommunicationId()).replace('0x', '').zfill(16))
- Print.info('LogoType: ' + str(self.getLogoType()))
- Print.info('LogoHandling: ' + str(self.getLogoHandling()))
- Print.info('RuntimeAddOnContentInstall: ' + str(self.getRuntimeAddOnContentInstall()))
- Print.info('CrashReport: ' + str(self.getCrashReport()))
- Print.info('Hdcp: ' + str(self.getHdcp()))
- Print.info('SeedForPseudoDeviceId: ' + '0x' + hex(self.getSeedForPseudoDeviceId()).replace('0x', '').zfill(16))
-
- if str(self.getBcatPassphrase()) == '0000000000000000000000000000000000000000000000000000000000000000':
- pass
- elif str(self.getBcatPassphrase()) == '':
- pass
- else:
- Print.info('BcatPassphrase: ' + str(self.getBcatPassphrase()))
-
- Print.info('UserAccountSaveDataSizeMax: ' + '0x' + hex(self.getUserAccountSaveDataSizeMax()).replace('0x', '').zfill(16))
- Print.info('UserAccountSaveDataJournalSizeMax: ' + '0x' + hex(self.getUserAccountSaveDataJournalSizeMax()).replace('0x', '').zfill(16))
- Print.info('DeviceSaveDataSizeMax: ' + '0x' + hex(self.getDeviceSaveDataSizeMax()).replace('0x', '').zfill(16))
- Print.info('DeviceSaveDataJournalSizeMax: ' + '0x' + hex(self.getDeviceSaveDataJournalSizeMax()).replace('0x', '').zfill(16))
- Print.info('TemporaryStorageSize: ' + '0x' + hex(self.getTemporaryStorageSize()).replace('0x', '').zfill(16))
- Print.info('CacheStorageSize: ' + '0x' + hex(self.getCacheStorageSize()).replace('0x', '').zfill(16))
- Print.info('CacheStorageJournalSize: ' + '0x' + hex(self.getCacheStorageJournalSize()).replace('0x', '').zfill(16))
- Print.info('CacheStorageDataAndJournalSizeMax: ' + '0x' + hex(self.getCacheStorageDataAndJournalSizeMax()).replace('0x', '').zfill(8))
- Print.info('CacheStorageIndexMax: ' + '0x' + hex(self.getCacheStorageIndexMax()).replace('0x', '').zfill(4))
- Print.info('PlayLogQueryableApplicationId: ' + '0x' + hex(self.getPlayLogQueryableApplicationId()).replace('0x', '').zfill(16))
- Print.info('PlayLogQueryCapability: ' + str(self.getPlayLogQueryCapability()))
- Print.info('Repair: ' + str(self.getRepair()))
- Print.info('ProgramIndex: ' + str(self.getProgramIndex()))
- Print.info('RequiredNetworkServiceLicenseOnLaunch: ' + str(self.getRequiredNetworkServiceLicenseOnLaunch()))
-
-############
-# RETURNS
-############
- def get_NameandPub(self, data):
- Langue = list()
- Langue = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
- lstring={}
- editstring={}
- for i in Langue:
- off1=i*0x300;off2=off1+0x200;off3=off2+0x100
- title=data[off1:off2]
- title = title.split(b'\0', 1)[0].decode('utf-8')
- title = (re.sub(r'[\/\\]+', ' ', title))
- title = title.strip()
- if title == '':
- continue
- else:
- editor=data[off2:off3]
- editor = editor.split(b'\0', 1)[0].decode('utf-8')
- editor = (re.sub(r'[\/\\]+', ' ', editor))
- editor = editor.strip()
- Language=str(NacpLanguageType(i)).replace('NacpLanguageType.', '')
- lstring[Language]=title
- editstring[Language]=editor
- return lstring,editstring
-
- def get_Isbn(self, data):
- isbn=data.split(b'\0', 1)[0].decode('utf-8')
- if isbn == '':
- isbn=None
- return str(isbn)
-
- def get_StartupUserAccount(self, data):
- b=data
- if b == 0:
- StartupUserAccount = 'None'
- elif b == 1:
- StartupUserAccount = 'Required'
- elif b == 2:
- StartupUserAccount = 'RequiredWithNetworkServiceAccountAvailable'
- else:
- StartupUserAccount = 'Unknown'
- return str(StartupUserAccount)
-
- def get_UserAccountSwitchLock(self, data):
- b=data
- if b == 0:
- userAccountSwitchLock = 'Disable'
- elif b == 1:
- userAccountSwitchLock = 'Enable'
- else:
- userAccountSwitchLock = 'Unknown'
- return str(userAccountSwitchLock)
-
- def get_AddOnContentRegistrationType(self, data):
- b=data
- if b == 0:
- addOnContentRegistrationType = 'AllOnLaunch'
- elif b == 1:
- addOnContentRegistrationType = 'OnDemand'
- else:
- addOnContentRegistrationType = 'Unknown'
- return str(addOnContentRegistrationType)
-
-
- def get_ContentType(self, data):
- b=data
- if b == 0:
- content_type = 'False'
- elif b == 1:
- content_type = 'True'
- elif b == 2:
- content_type = 'RetailInteractiveDisplay'
- else:
- content_type = 'Unknown'
- return str(content_type)
-
- def get_ParentalControl(self, data):
- b=data
- if b == 0:
- parentalControl = 'None'
- elif b == 1:
- parentalControl = 'FreeCommunication'
- else:
- parentalControl = 'Unknown'
- return str(parentalControl)
-
- def get_Screenshot(self, data):
- b=data
- if b == 0:
- screenshot = 'Allowed'
- elif b == 1:
- screenshot = 'Denied'
- else:
- screenshot = 'Unknown'
- return str(screenshot)
-
- def get_VideoCapture(self, data):
- b=data
- if b == 0:
- videoCapture = 'Disabled'
- elif b == 1:
- videoCapture = 'Manual'
- elif b == 2:
- videoCapture = 'Enabled'
- else:
- videoCapture = 'Unknown'
- return str(videoCapture)
-
- def get_dataLossConfirmation(self, data):
- b=data
- if b == 0:
- dataLossConfirmation = 'None'
- elif b == 1:
- dataLossConfirmation = 'Required'
- else:
- dataLossConfirmation = 'Unknown'
- return str(dataLossConfirmation)
-
- def get_PlayLogPolicy(self, data):
- b=data
- if b == 0:
- playLogPolicy = 'All'
- elif b == 1:
- playLogPolicy = 'LogOnly'
- elif b == 2:
- playLogPolicy = 'None'
- else:
- playLogPolicy = 'Unknown'
- return str(playLogPolicy)
-
- def get_PresenceGroupId(self, data):
- b=data
- PresenceGroupId='0x' + hex(data).replace('0x', '').zfill(16)
- return PresenceGroupId
-
- def get_RatingAge(self,data):
- inmemoryfile = io.BytesIO(data)
- AgeRatings={}
- for i in range(12):
- b=int(inmemoryfile.read(1)[0])
- if b == 0:
- age = '0'
- elif b == 3:
- age = '3'
- elif b == 4:
- age = '4'
- elif b == 6:
- age = '6'
- elif b == 7:
- age = '7'
- elif b == 8:
- age = '8'
- elif b == 10:
- age = '10'
- elif b == 12:
- age = '12'
- elif b == 13:
- age = '13'
- elif b == 14:
- age = '14'
- elif b == 15:
- age = '15'
- elif b == 16:
- age = '16'
- elif b == 17:
- age = '17'
- elif b == 18:
- age = '18'
- else:
- age = 'Unknown'
- if age != 'Unknown':
- Org=str(OrganizationType(i)).replace('OrganizationType.', '')
- AgeRatings[Org] =str(age)
- #print(AgeRatings)
- return AgeRatings
-
- def get_DisplayVersion(self, data):
- b=data
- buildnumber=data.split(b'\0', 1)[0].decode('utf-8')
- return buildnumber
-
- def get_AddOnContentBaseId(self, data):
- b=data
- AddOnContentBaseId= '0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return AddOnContentBaseId
-
- def get_SaveDataOwnerId(self, data):
- b=data
- SaveDataOwnerId='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return SaveDataOwnerId
-
- def get_UserAccountSaveDataSize(self, data):
- b=data
- UserAccountSaveDataSize='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return UserAccountSaveDataSize
-
- def get_UserAccountSaveDataJournalSize(self, data):
- b=data
- UserAccountSaveDataJournalSize='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return UserAccountSaveDataJournalSize
-
- def get_DeviceSaveDataSize(self, data):
- b=data
- if data==0:
- DeviceSaveDataSize='None'
- else:
- DeviceSaveDataSize='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return DeviceSaveDataSize
-
- def get_DeviceSaveDataJournalSize(self, data):
- b=data
- if data==0:
- DeviceSaveDataJournalSize='None'
- else:
- DeviceSaveDataJournalSize='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return DeviceSaveDataJournalSize
-
- def get_BcatDeliveryCacheStorageSize(self, data):
- b=data
- if data==0:
- BcatDeliveryCacheStorageSize='None'
- else:
- BcatDeliveryCacheStorageSize='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return BcatDeliveryCacheStorageSize
-
- def get_ApplicationErrorCodeCategory(self, data):
- b=data
- applicationErrorCodeCategory = data.split(b'\0', 1)[0].decode('utf-8')
- if applicationErrorCodeCategory == '':
- applicationErrorCodeCategory='None'
- return applicationErrorCodeCategory
-
- def get_LocalCommunicationId(self, data):
- b=data
- LocalCommunicationId='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return LocalCommunicationId
-
- def get_LogoType(self, data):
- b=data
- if b == 0:
- logoType = 'LicensedByNintendo'
- elif b == 2:
- logoType = 'Nintendo'
- else:
- logoType = 'Unknown'
- return logoType
-
- def get_LogoHandling(self, data):
- b=data
- if b == 0:
- logoHandling = 'Auto'
- elif b == 1:
- logoHandling = 'Manual'
- else:
- logoHandling = 'Unknown'
- return logoHandling
-
- def get_RuntimeAddOnContentInstall(self, data):
- b=data
- if b == 0:
- runtimeAddOnContentInstall = 'Deny'
- elif b == 1:
- runtimeAddOnContentInstall = 'AllowAppend'
- else:
- runtimeAddOnContentInstall = 'Unknown'
- return runtimeAddOnContentInstall
-
- def get_CrashReport(self, data):
- b=data
- if b == 0:
- crashReport = 'Deny'
- elif b == 1:
- crashReport = 'Allow'
- else:
- crashReport = 'Unknown'
- return crashReport
-
- def get_Hdcp(self, data):
- b=data
- if b == 0:
- hdcp = 'None'
- elif b == 1:
- hdcp = 'Required'
- else:
- hdcp = 'Unknown'
- return hdcp
-
- def get_SeedForPseudoDeviceId(self, data):
- SeedForPseudoDeviceId='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return SeedForPseudoDeviceId
-
- def get_BcatPassphrase(self, data):
- bcatPassphrase = data.split(b'\0', 1)[0].decode('utf-8')
- if bcatPassphrase == 0:
- bcatPassphrase='None'
- elif str(bcatPassphrase) == '':
- bcatPassphrase='None'
- return bcatPassphrase
-
- def get_UserAccountSaveDataSizeMax(self, data):
- if data == 0:
- UserAccountSaveDataSizeMax='None'
- else:
- UserAccountSaveDataSizeMax='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return UserAccountSaveDataSizeMax
-
- def get_UserAccountSaveDataJournalSizeMax(self, data):
- if data == 0:
- UserAccountSaveDataJournalSizeMax='None'
- else:
- UserAccountSaveDataJournalSizeMax='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return UserAccountSaveDataJournalSizeMax
-
- def get_DeviceSaveDataSizeMax(self, data):
- if data == 0:
- DeviceSaveDataSizeMax='None'
- else:
- DeviceSaveDataSizeMax='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return DeviceSaveDataSizeMax
-
- def get_DeviceSaveDataJournalSizeMax(self, data):
- if data == 0:
- DeviceSaveDataJournalSizeMax='None'
- else:
- DeviceSaveDataJournalSizeMax='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return DeviceSaveDataJournalSizeMax
-
- def get_TemporaryStorageSize(self, data):
- if data == 0:
- TemporaryStorageSize='None'
- else:
- TemporaryStorageSize='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return TemporaryStorageSize
-
- def get_CacheStorageSize(self, data):
- if data == 0:
- CacheStorageSize='None'
- else:
- CacheStorageSize='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return CacheStorageSize
-
- def get_CacheStorageJournalSize(self, data):
- if data == 0:
- CacheStorageJournalSize='None'
- else:
- CacheStorageJournalSize='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return CacheStorageJournalSize
-
- def get_CacheStorageDataAndJournalSizeMax(self, data):
- if data == 0:
- CacheStorageDataAndJournalSizeMax='None'
- else:
- CacheStorageDataAndJournalSizeMax='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return CacheStorageDataAndJournalSizeMax
-
- def get_CacheStorageIndexMax(self, data):
- if data == 0:
- CacheStorageIndexMax='None'
- else:
- CacheStorageIndexMax='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return CacheStorageIndexMax
-
- def get_PlayLogQueryableApplicationId(self, data):
- if data == 0:
- PlayLogQueryableApplicationId='None'
- else:
- PlayLogQueryableApplicationId='0x' + (hex(data).replace('0x', '').zfill(16)).upper()
- return PlayLogQueryableApplicationId
-
- def get_PlayLogQueryCapability(self, data):
- b = data
- if b == 0:
- playLogQueryCapability = 'None'
- elif b == 1:
- playLogQueryCapability = 'WhiteList'
- elif b == 2:
- playLogQueryCapability = 'All'
- else:
- playLogQueryCapability = 'Unknown'
- return playLogQueryCapability
-
- def get_Repair(self, data):
- b = data
- if b == 0:
- repair = 'None'
- elif b == 1:
- repair = 'SuppressGameCardAccess'
- else:
- repair = 'Unknown'
- return repair
-
- def get_ProgramIndex(self, data):
- ProgramIndex=str(data)
- return ProgramIndex
-
- def get_RequiredNetworkServiceLicenseOnLaunch(self, data):
- b = data
- if b == 0:
- requiredNetworkServiceLicenseOnLaunch = 'None'
- elif b == 1:
- requiredNetworkServiceLicenseOnLaunch = 'Common'
- else:
- requiredNetworkServiceLicenseOnLaunch = 'Unknown'
- return requiredNetworkServiceLicenseOnLaunch
diff --git a/py/Fs/ChromeNca.py b/py/Fs/ChromeNca.py
deleted file mode 100644
index cb3432c0..00000000
--- a/py/Fs/ChromeNca.py
+++ /dev/null
@@ -1,2670 +0,0 @@
-import aes128
-import Title
-import Titles
-import Hex
-import math
-from binascii import hexlify as hx, unhexlify as uhx
-from struct import pack as pk, unpack as upk
-from hashlib import sha256
-import Fs.Type
-import os
-import re
-import pathlib
-
-import Keys
-import Config
-import Print
-import Nsps
-from tqdm import tqdm
-import Fs
-from Fs import Type
-from Fs.File import File
-from Fs.File import MemoryFile
-from Fs.Rom import Rom
-from Fs.Pfs0 import Pfs0
-from Fs.BaseFs import BaseFs
-from Fs.Ticket import Ticket
-from Fs.Nacp import Nacp
-import sq_tools
-import pykakasi
-from Fs.pyNCA3 import NCA3
-import io
-from googletrans import Translator
-
-import sq_settings
-
-MEDIA_SIZE = 0x200
-RSA_PUBLIC_EXPONENT = 0x10001
-FS_HEADER_LENGTH = 0x200
-if sq_settings.key_system =="production":
- nca_header_fixed_key_modulus_00 = 0xBFBE406CF4A780E9F07D0C99611D772F96BC4B9E58381B03ABB175499F2B4D5834B005A37522BE1A3F0373AC7068D116B904465EB707912F078B26DEF60007B2B451F80D0A5E58ADEBBC9AD649B964EFA782B5CF6D7013B00F85F6A908AA4D676687FA89FF7590181E6B3DE98A68C92604D980CE3F5E92CE01FF063BF2C1A90CCE026F16BC92420A4164CD52B6344DAEC02EDEA4DF27683CC1A060AD43F3FC86C13E6C46F77C299FFAFDF0E3CE64E735F2F656566F6DF1E242B08340A5C3202BCC9AAECAED4D7030A8701C70FD1363290279EAD2A7AF3528321C7BE62F1AAA407E328C2742FE8278EC0DEBE6834B6D8104401A9E9A67F67229FA04F09DE4F403
- nca_header_fixed_key_modulus_01 = 0xADE3E1FA0435E5B6DD49EA8929B1FFB643DFCA96A04A13DF43D9949796436548705833A27D357B96745E0B5C32181424C258B36C227AA1B7CB90A7A3F97D4516A5C8ED8FAD395E9E4B51687DF80C35C63F91AE44A592300D46F840FFD0FF06D21C7F9618DCB71D663ED173BC158A2F94F300C183F1CDD78188ABDF8CEF97DD1B175F58F69AE9E8C22F3815F52107F837905D2E024024150D25B7265D09CC4CF4F21B94705A9EEEED7777D45199F5DC761EE36C8CD112D457D1B683E4E4FEDAE9B43B33E5378ADFB57F89F19B9EB015B23AFEEA61845B7D4B23120B8312F2226BB922964B260B635E965752A3676422CAD0563E74B5981F0DF8B334E698685AAD
-
- acid_fixed_key_modulus_00 = 0xDDC8DDF24E6DF0CA9EC75DC77BADFE7D238969B6F206A20288E15591ABCB4D502EFC9D9476D64CD8FF10FA5E930AB457AC51C71666F41A54C2C5043D1BFE30208AAC6F6FF5C7B668B8C9406B42AD1121E78BE9750186E4489B0A0AF87FE887F28201E6A30FE466AE833F4E9F5E0130A400B99AAE5F03CC1860E5EF3B5E1516FE1C8278B52F477C0666885D35A2672010E76C4368D3E45A682A5AE26D73B031531C200944F51A9D22BE12A17711E2A1CD409AA28B609BEFA0D34863A2F8A32C0856522E6019675AA79FDC3F3F692B316AB7884A148480333C9D44B73F4CE175EA37EAE81E7C77B7C61AA2F09F1061CD7B5B324C37EFB17168530AED517D3522FD
- acid_fixed_key_modulus_01 = 0xE7AA25C801A5146B01603ED9965ABF90ACA7FD9B5BBD8A26B0CB20289A7212F52065B3B984581F27BC7CA2C99E1895CFC2732E748C66E59E792BB8070CB04E8EAB852142C4C56D889CDB15953F80DB7A9A7D4156251718424D8CACA57BDB425D5935455D8A02B570C0723546D01D60014ACC1C46D3D63552D6E1F83B5DEADDB8FE7D50CB3523678BB6E474D260FCFD43BF910881C54F5D169AC49AC6F6F3E1F65C07AA716C13A4B1B366BF904C3DA2C40BB83D7A8C19FAFF6BB91F02CCB6D30C7D191F47F9C74001FA46EA0BD402E03D309A1A0FEAA76655F7CB28E2BB99E483C34303EEDC1F0223DDD12D39A4657503EF379C06D6FAA115F0DB1747264F4903
-else:
- nca_header_fixed_key_modulus_00 = 0xD8F118EF32724CA7474CB9EAB304A8A4AC99080804BF6857B843942BC7B9664985E58A9BC1009A6A8DD0EFCEFF86C85C5DE9537B192AA8C022D1F3220A50F22B65051B9EEC61B563A36F3BBA633A53F4492FCF03CCD750821B294F08DE1B6D474FA8B66A26A0833F1AAF838F0E173FFE441C56942E49838303E9B6ADD5DEE32DA1D966205D1F5E965D5B550DD4B4776EAE1B69F3A6610E51623928637576BFB0D222EF98250205C0D76A062CA5D85A9D7AA421559FF93EBF16F607C2B96E879EB51CBE97FA827EED30D4663FDED81B4B15D9FB2F50F09D1D524C1C4D8DAE851EEA7F86F30B7B8781982380634F2FB062CC6ED24613652BD6443359B58FB94AA9
- nca_header_fixed_key_modulus_01 = 0x9ABC88BD0ABED70C9B427565385ED101CD12AEEAE94BDBB45E361096DA3D2E66D399138ABE6741C893D93E42CE34CE96FA0B23CC2CDF073F3B244B12673A2936A3AA06F065A585BAFD12ECF16067F08FD35B011B1E84A35C6536F9237EF326386498BAE419914C02CFC96D86EC1D4169DD56EA5CA32A58B439CC4031FDFB4274F8ECEA00F0D928EAFA2D00E14353C632F4A207D45FD4CBACCAFFDF84D286143CDE2275A573FF68074AF97C2CCCDE45B6548290361F2C5196C50A535BF08B4AAA3B689719171F01B8EDB99A5E08C5201E6A09F0E973A3BE100602E9FB85FA5F01AC60E0ED7DB949A89E987D914005CFF91AFC4022A8965BB0DC7AF5B7E9914C49
-
- acid_fixed_key_modulus_00 = 0xD634A5786C68CE5AC23717F38245C689E12D0667BFB40619556B27660CA4B5878125F430BC530868A248498C3F38409CC426F479E2A185F55C7F58BAA61CA08B8416146F85D97CE13C67221EFBD8A7A59ABFEC0ECF967E85C21D495D5426CB327CF6BB5803802B5DF7FBD19DC7C62E53C06F392C1FA992F24D7D4E74FFE4EFE47C3D342A71A49759FF4FA2F46678D8BA99E3E6DB54B9E954A170FC051F11674B268C0C3E03D2A3555C7DC05D9DFF132FFD19BFED44C38CA728CBE5E0B1A79C338DB86EDE87182260C4AEF2879FCE095CB599A59F49F2D758FAF9C0257DD6CBF3D86CA269916873B1946FA3F3B97DF8E0729E937B7AA25760B75BA984AE648869
- acid_fixed_key_modulus_01 = 0xBCA56A7EEA383462A610183CE1637BF0D3088CF5C5C4C793E9D9E632F3A0F66E8A9876473347650270DC865F3D615A70BC5ACACA50AD617EC9EC27FFE864429AEEBEC3D10BC0E9BF838DC00CD8005B7690D24B3084358B1E20B7E4DC63E5DFCD005F815F67C58BDFFCE1375F07D9DE4FE67BF1FBA15A7140FEBA1EAE1322D2FE37A2B68BABEB84814E7C1E02D1FBD75D118464D24DBB50006754E27789BA0BE705579A225AEC761CFDE8A81816416503FAC4A6315C1A7FAB11C84A99B9E6CF6221A67247DBBA96264E2ED48C46D6A71A6C32A7DF851C03C36DA9E968F4171EB2702AA1E5E1F38F6F63ACEB720B4C4A363C60919F6E1C71EAD07878A02EC6326B
-
-
-indent = 1
-tabs = '\t' * indent
-
-from Crypto.Hash import SHA256
-from Crypto.Cipher import AES
-from Crypto.Util import Counter
-from Crypto.PublicKey import RSA
-from Crypto.Signature import PKCS1_v1_5, PKCS1_PSS
-
-
-class SectionTableEntry:
- def __init__(self, d):
- self.mediaOffset = int.from_bytes(d[0x0:0x4], byteorder='little', signed=False)
- self.mediaEndOffset = int.from_bytes(d[0x4:0x8], byteorder='little', signed=False)
-
- self.offset = self.mediaOffset * MEDIA_SIZE
- self.endOffset = self.mediaEndOffset * MEDIA_SIZE
-
- self.unknown1 = int.from_bytes(d[0x8:0xc], byteorder='little', signed=False)
- self.unknown2 = int.from_bytes(d[0xc:0x10], byteorder='little', signed=False)
- self.sha1 = None
-
-
-def GetSectionFilesystem(buffer, cryptoKey):
- fsType = buffer[0x3]
- if fsType == Fs.Type.Fs.PFS0:
- return Fs.Pfs0(buffer, cryptoKey = cryptoKey)
-
- if fsType == Fs.Type.Fs.ROMFS:
- return Fs.Rom(buffer, cryptoKey = cryptoKey)
-
- return BaseFs(buffer, cryptoKey = cryptoKey)
-
-class NcaHeader(File):
- def __init__(self, path = None, mode = None, cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
- self.signature1 = None
- self.signature2 = None
- self.magic = None
- self.isGameCard = None
- self.contentType = None
- self.cryptoType = None
- self.keyIndex = None
- self.size = None
- self.titleId = None
- self.sdkVersion1 = None
- self.sdkVersion2 = None
- self.sdkVersion3 = None
- self.sdkVersion4 = None
- self.cryptoType2 = None
- self.sigKeyGen = None
- self.rightsId = None
- self.titleKeyDec = None
- self.masterKey = None
- self.sectionTables = []
- self.keys = []
-
- super(NcaHeader, self).__init__(path, mode, cryptoType, cryptoKey, cryptoCounter)
-
- def open(self, file = None, mode = 'rb', cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
- super(NcaHeader, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.rewind()
- self.signature1 = self.read(0x100)
- self.signature2 = self.read(0x100)
- self.magic = self.read(0x4)
- self.isGameCard = self.readInt8()
- self.contentType = self.readInt8()
-
- try:
- self.contentType = Fs.Type.Content(self.contentType)
- except:
- pass
-
- self.cryptoType = self.readInt8()
- self.keyIndex = self.readInt8()
- self.size = self.readInt64()
- self.titleId = hx(self.read(8)[::-1]).decode('utf-8').upper()
-
- self.readInt32() # padding
-
-
- self.sdkVersion1 = self.readInt8()
- self.sdkVersion2 = self.readInt8()
- self.sdkVersion3 = self.readInt8()
- self.sdkVersion4 = self.readInt8()
- self.cryptoType2 = self.readInt8()
- self.sigKeyGen = self.readInt8()
-
- self.read(0xE) # padding
-
- self.rightsId = hx(self.read(0x10))
-
- if self.magic not in [b'NCA3', b'NCA2']:
- raise Exception('Failed to decrypt NCA header: ' + str(self.magic))
-
- self.sectionHashes = []
-
- for i in range(4):
- self.sectionTables.append(SectionTableEntry(self.read(0x10)))
-
- for i in range(4):
- self.sectionHashes.append(self.sectionTables[i])
-
- self.masterKey = (self.cryptoType if self.cryptoType > self.cryptoType2 else self.cryptoType2)-1
-
- if self.masterKey < 0:
- self.masterKey = 0
-
-
- self.encKeyBlock = self.getKeyBlock()
- #for i in range(4):
- # offset = i * 0x10
- # key = encKeyBlock[offset:offset+0x10]
- # Print.info('enc %d: %s' % (i, hx(key)))
-
- if Keys.keyAreaKey(self.masterKey, self.keyIndex):
- crypto = aes128.AESECB(Keys.keyAreaKey(self.masterKey, self.keyIndex))
- self.keyBlock = crypto.decrypt(self.encKeyBlock)
- self.keys = []
- for i in range(4):
- offset = i * 0x10
- key = self.keyBlock[offset:offset+0x10]
- #Print.info('dec %d: %s' % (i, hx(key)))
- self.keys.append(key)
- else:
- self.keys = [None, None, None, None, None, None, None]
-
-
- if self.hasTitleRights():
- if self.titleId.upper() in Titles.keys() and Titles.get(self.titleId.upper()).key:
- self.titleKeyDec = Keys.decryptTitleKey(uhx(Titles.get(self.titleId.upper()).key), self.masterKey)
- else:
- pass
- #Print.info('could not find title key!')
- else:
- self.titleKeyDec = self.key()
-
- def key(self):
- return self.keys[2]
- return self.keys[self.cryptoType]
-
- def hasTitleRights(self):
- return self.rightsId != (b'0' * 32)
-
- def getTitleID(self):
- self.seek(0x210)
- return self.read(8)[::-1]
-
- def setTitleID(self,tid):
- self.seek(0x210)
- tid=bytes.fromhex(tid)[::-1]
- return self.write(tid)
-
- def getKeyBlock(self):
- self.seek(0x300)
- return self.read(0x40)
-
- def getKB1L(self):
- self.seek(0x300)
- return self.read(0x10)
-
- def setKeyBlock(self, value):
- if len(value) != 0x40:
- raise IOError('invalid keyblock size')
- self.seek(0x300)
- return self.write(value)
-
- def getCryptoType(self):
- self.seek(0x206)
- return self.readInt8()
-
- def setCryptoType(self, value):
- self.seek(0x206)
- self.writeInt8(value)
-
- def setgamecard(self, value):
- self.seek(0x204)
- self.writeInt8(value)
-
- def getgamecard(self):
- self.seek(0x204)
- return self.readInt8()
-
- def getCryptoType2(self):
- self.seek(0x220)
- return self.readInt8()
-
- def setCryptoType2(self, value):
- self.seek(0x220)
- self.writeInt8(value)
-
- def getSigKeyGen(self):
- self.seek(0x221)
- return self.readInt8()
-
- def setSigKeyGen(self, value):
- self.seek(0x221)
- self.writeInt8(value)
-
- def getRightsId(self):
- self.seek(0x230)
- return self.readInt128('big')
-
- def setRightsId(self, value):
- self.seek(0x230)
- self.writeInt128(value, 'big')
-
- def get_hblock_hash(self):
- self.seek(0x280)
- return self.read(0x20)
-
- def set_hblock_hash(self, value):
- self.seek(0x280)
- return self.write(value)
-
- def calculate_hblock_hash(self):
- indent = 2
- tabs = '\t' * indent
- self.seek(0x400)
- hblock = self.read(0x200)
- sha=sha256(hblock).hexdigest()
- sha_hash= bytes.fromhex(sha)
- #Print.info(tabs + 'calculated header block hash: ' + str(hx(sha_hash)))
- return sha_hash
-
- def get_hblock_version(self):
- self.seek(0x400)
- return self.read(0x02)
-
- def get_hblock_filesystem(self):
- self.seek(0x403)
- return self.read(0x01)
-
- def get_hblock_hash_type(self):
- self.seek(0x404)
- return self.read(0x01)
-
- def get_hblock_crypto_type(self):
- self.seek(0x405)
- return self.read(0x01)
-
- def get_htable_hash(self):
- self.seek(0x408)
- return self.read(0x20)
-
- def set_htable_hash(self, value):
- self.seek(0x408)
- return self.write(value)
-
- def get_hblock_block_size(self):
- self.seek(0x428)
- return self.readInt32()
-
- def get_hblock_uk1(self):
- self.seek(0x42C)
- return self.read(0x04)
-
- def get_htable_offset(self):
- self.seek(0x430)
- return self.readInt64()
-
- def get_htable_size(self):
- self.seek(0x438)
- return self.readInt64()
-
- def get_pfs0_offset(self):
- self.seek(0x440)
- return self.readInt64()
-
- def get_pfs0_size(self):
- self.seek(0x448)
- return self.readInt64()
-
-
-class Nca(File):
- def __init__(self, path = None, mode = 'rb', cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
- self.header = None
- self.sectionFilesystems = []
- super(Nca, self).__init__(path, mode, cryptoType, cryptoKey, cryptoCounter)
-
- def __iter__(self):
- return self.sectionFilesystems.__iter__()
-
- def __getitem__(self, key):
- return self.sectionFilesystems[key]
-
- def open(self, file = None, mode = 'rb', cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
-
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
-
- self.header = NcaHeader()
- self.partition(0x0, 0xC00, self.header, Fs.Type.Crypto.XTS, uhx(Keys.get('header_key')))
- #Print.info('partition complete, seeking')
-
- self.header.seek(0x400)
- #Print.info('reading')
- #Hex.dump(self.header.read(0x200))
- #exit()
-
- for i in range(4):
- fs = GetSectionFilesystem(self.header.read(0x200), cryptoKey = self.header.titleKeyDec)
- #Print.info('fs type = ' + hex(fs.fsType))
- #Print.info('fs crypto = ' + hex(fs.cryptoType))
- #Print.info('st end offset = ' + str(self.header.sectionTables[i].endOffset - self.header.sectionTables[i].offset))
- #Print.info('fs offset = ' + hex(self.header.sectionTables[i].offset))
- #Print.info('fs section start = ' + hex(fs.sectionStart))
- #Print.info('titleKey = ' + str(hx(self.header.titleKeyDec)))
- try:
- self.partition(self.header.sectionTables[i].offset + fs.sectionStart, self.header.sectionTables[i].endOffset - self.header.sectionTables[i].offset, fs, cryptoKey = self.header.titleKeyDec)
- except BaseException as e:
- pass
- #Print.info(e)
- #raise
-
- if fs.fsType:
- self.sectionFilesystems.append(fs)
-
-
- self.titleKeyDec = None
- self.masterKey = None
-
-
- def html_feed(self,feed='',style=1,message=''):
- if style==1:
- feed+='{}
'.format(message)
- return feed
- if style==2:
- feed+='{}
'.format(message)
- return feed
- if style==3:
- feed+='{} {} '.format(message[0],message[1])
- return feed
- if style==4:
- feed+='{} {}. {} {} '.format(message[0],message[1],message[2],message[3])
- return feed
- if style==5:
- feed+='{}
'.format(message)
- return feed
- if style==6:
- feed+='{}
'.format(message)
- return feed
- if style==7:
- feed+='{} {}
'.format(message[0],message[1])
- return feed
-
- def get_hblock(self):
- version = self.header.get_hblock_version()
- Print.info('version: ' + str(int.from_bytes(version, byteorder='little')))
- filesystem = self.header.get_hblock_filesystem()
- Print.info('filesystem: ' + str(int.from_bytes(filesystem, byteorder='little')))
- hash_type = self.header.get_hblock_hash_type()
- Print.info('hash type: ' + str(int.from_bytes(hash_type, byteorder='little')))
- crypto_type = self.header.get_hblock_crypto_type()
- Print.info('crypto type: ' + str(int.from_bytes(crypto_type, byteorder='little')))
- hash_from_htable = self.header.get_htable_hash()
- Print.info('hash from hash table: ' + str(hx(hash_from_htable)))
- block_size = self.header.get_hblock_block_size()
- Print.info('block size in bytes: ' + str(hx(block_size.to_bytes(8, byteorder='big'))))
- v_unkn1 = self.header.get_hblock_uk1()
- htable_offset = self.header.get_htable_offset()
- Print.info('hash table offset: ' + str(hx(htable_offset.to_bytes(8, byteorder='big'))))
- htable_size = self.header.get_htable_size()
- Print.info('Size of hash-table: ' + str(hx(htable_size.to_bytes(8, byteorder='big'))))
- pfs0_offset = self.header.get_pfs0_offset()
- Print.info('Pfs0 offset: ' + str(hx(pfs0_offset.to_bytes(8, byteorder='big'))))
- pfs0_size = self.header.get_pfs0_size()
- Print.info('Pfs0 size: ' + str(hx(pfs0_size.to_bytes(8, byteorder='big'))))
-
-
- def get_pfs0_hash_data(self):
- block_size = self.header.get_hblock_block_size()
- #Print.info('block size in bytes: ' + str(hx(block_size.to_bytes(8, byteorder='big'))))
- pfs0_size = self.header.get_pfs0_size()
- #Print.info('Pfs0 size: ' + str(hx(pfs0_size.to_bytes(8, byteorder='big'))))
- multiplier=pfs0_size/block_size
- multiplier=math.ceil(multiplier)
- #Print.info('Multiplier: ' + str(multiplier))
- return pfs0_size,block_size,multiplier
-
- def pfs0_MULT(self):
- block_size = self.header.get_hblock_block_size()
- #Print.info('block size in bytes: ' + str(hx(block_size.to_bytes(8, byteorder='big'))))
- pfs0_size = self.header.get_pfs0_size()
- #Print.info('Pfs0 size: ' + str(hx(pfs0_size.to_bytes(8, byteorder='big'))))
- multiplier=pfs0_size/block_size
- multiplier=math.ceil(multiplier)
- #Print.info('Multiplier: ' + str(multiplier))
- return multiplier
-
- def get_pfs0_hash(self, file = None, mode = 'rb', cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
- mult=self.pfs0_MULT()
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(0xC00+self.header.get_htable_offset())
- hash_from_pfs0=self.read(0x20*mult)
- return hash_from_pfs0
-
- def extract(self,ofolder,buffer):
- ncaname = str(self._path)[:-4]+'_nca'
- ncafolder = os.path.join(ofolder,ncaname)
- if not os.path.exists(ncafolder):
- os.makedirs(ncafolder)
- nca3 = NCA3(open(str(self._path), 'rb'))
- nca3.extract_conts(ncafolder, disp=True)
-
- def calc_htable_hash(self):
- indent = 2
- tabs = '\t' * indent
- htable = self.get_pfs0_hash()
- sha=sha256(htable).hexdigest()
- sha_hash= bytes.fromhex(sha)
- #Print.info(tabs + 'calculated table hash: ' + str(hx(sha_hash)))
- return sha_hash
-
- def calc_pfs0_hash(self, file = None, mode = 'rb'):
- mult=self.pfs0_MULT()
- indent = 2
- tabs = '\t' * indent
- for f in self:
- cryptoType2=f.get_cryptoType()
- cryptoKey2=f.get_cryptoKey()
- cryptoCounter2=f.get_cryptoCounter()
- super(Nca, self).open(file, mode, cryptoType2, cryptoKey2, cryptoCounter2)
- pfs0_offset = self.header.get_pfs0_offset()
- pfs0_size = self.header.get_pfs0_size()
- block_size = self.header.get_hblock_block_size()
- self.seek(0xC00+self.header.get_htable_offset()+pfs0_offset)
- if mult>1:
- pfs0=self.read(block_size)
- else:
- pfs0=self.read(pfs0_size)
- sha=sha256(pfs0).hexdigest()
- #Print.info('caculated hash from pfs0: ' + sha)
- sha_signature = bytes.fromhex(sha)
- #Print.info(tabs + 'calculated hash from pfs0: ' + str(hx(sha_signature)))
- return sha_signature
-
- def set_pfs0_hash(self,value):
- file = None
- mode = 'r+b'
- for f in self:
- cryptoType2=f.get_cryptoType()
- cryptoKey2=f.get_cryptoKey()
- cryptoCounter2=f.get_cryptoCounter()
- super(Nca, self).open(file, mode, cryptoType2, cryptoKey2, cryptoCounter2)
- self.seek(0xC00+self.header.get_htable_offset())
- self.write(value)
-
- def test(self,titleKeyDec, file = None, mode = 'rb'):
- indent = 1
- tabs = '\t' * indent
- self.header = NcaHeader()
- self.partition(0x0, 0xC00, self.header, Fs.Type.Crypto.XTS, uhx(Keys.get('header_key')))
- Print.info('partition complete, seeking')
- self.rewind()
- self.header.seek(0x400)
- Print.info('reading')
- Hex.dump(self.header.read(0x200))
- #exit()
-
- for i in range(4):
- fs = GetSectionFilesystem(self.header.read(0x200), cryptoKey = titleKeyDec)
- Print.info('fs type = ' + hex(fs.fsType))
- Print.info('fs crypto = ' + hex(fs.cryptoType))
- Print.info('st end offset = ' + str(self.header.sectionTables[i].endOffset - self.header.sectionTables[i].offset))
- Print.info('fs offset = ' + hex(self.header.sectionTables[i].offset))
- Print.info('fs section start = ' + hex(fs.sectionStart))
- Print.info('titleKey = ' + str(hx(titleKeyDec)))
- try:
- self.partition(self.header.sectionTables[i].offset + fs.sectionStart, self.header.sectionTables[i].endOffset - self.header.sectionTables[i].offset, fs, cryptoKey = titleKeyDec)
- except BaseException as e:
- pass
- #Print.info(e)
- #raise
-
- if fs.fsType:
- self.sectionFilesystems.append(fs)
- for f in self:
- print(type(f))
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- for g in f:
- print(type(g))
- if type(g) == File:
- print(str(g._path))
- print(g.read(0x10))
-
- def pr_noenc_check(self, file = None, mode = 'rb'):
- indent = 1
- tabs = '\t' * indent
- check = False
- for f in self:
- #print(type(f))
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- for g in f:
- if type(g) == File:
- if (str(g._path)) == 'main.npdm':
- check = True
- break
- return check
-
- def pr_noenc_check_dlc(self, file = None, mode = 'rb'):
- crypto1=self.header.getCryptoType()
- crypto2=self.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- masterKeyRev=crypto1
- else:
- masterKeyRev=crypto2
- else:
- masterKeyRev=crypto2
- decKey = Keys.decryptTitleKey(self.header.titleKeyDec, Keys.getMasterKeyIndex(masterKeyRev))
- for f in self.sectionFilesystems:
- #print(f.fsType);print(f.cryptoType)
- if f.fsType == Type.Fs.ROMFS and f.cryptoType == Type.Crypto.CTR:
- ncaHeader = NcaHeader()
- self.header.rewind()
- ncaHeader = self.header.read(0x400)
- #Hex.dump(ncaHeader)
- pfs0=f
- #Hex.dump(pfs0.read())
- sectionHeaderBlock = f.buffer
-
- levelOffset = int.from_bytes(sectionHeaderBlock[0x18:0x20], byteorder='little', signed=False)
- levelSize = int.from_bytes(sectionHeaderBlock[0x20:0x28], byteorder='little', signed=False)
-
- pfs0Header = pfs0.read(levelSize)
- if sectionHeaderBlock[8:12] == b'IVFC':
- data = pfs0Header;
- #Hex.dump(pfs0Header)
- if hx(sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8') == str(sha256(data).hexdigest()):
- return True
- else:
- return False
- else:
- data = pfs0Header;
- #Hex.dump(pfs0Header)
- magic = pfs0Header[0:4]
- if magic != b'PFS0':
- return False
- else:
- return True
-
- def get_cnmt_titleid(self, file = None, mode = 'rb'):
- indent = 1
- tabs = '\t' * indent
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset)
- titleid=self.read(8)[::-1]
- return titleid
-
- def write_cnmt_titleid(self, value,file = None, mode = 'rb'):
- indent = 1
- tabs = '\t' * indent
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset)
- titleid=bytes.fromhex(value)[::-1]
- self.write(titleid)
- return(titleid)
-
-
-
-
- def get_req_system(self, file = None, mode = 'rb'):
- indent = 1
- tabs = '\t' * indent
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset+0x28)
- min_sversion=self.readInt32()
- #Print.info(tabs + 'RequiredSystemVersion = ' + str(min_sversion))
- return min_sversion
-
- def write_req_system(self, verNumber):
- indent = 1
- tabs = '\t' * indent
- file = None
- mode = 'r+b'
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset+0x28)
- min_sversion=self.readInt32()
- #Print.info('Original RequiredSystemVersion = ' + str(min_sversion))
- self.seek(cmt_offset+0x28)
- self.writeInt32(verNumber)
- self.seek(cmt_offset+0x28)
- min_sversion=self.readInt32()
- #Print.info(tabs + 'New RequiredSystemVersion = ' + str(min_sversion))
- return min_sversion
-
- def write_version(self, verNumber):
- indent = 1
- tabs = '\t' * indent
- file = None
- mode = 'r+b'
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset)
- titleid=self.readInt64()
- tnumber = verNumber.to_bytes(0x04, byteorder='little')
- titleversion = self.write(tnumber)
- self.seek(cmt_offset)
- titleid=self.readInt64()
- titleversion = self.read(0x4)
- Print.info('version = ' + str(int.from_bytes(titleversion, byteorder='little')))
- return titleversion
-
- def removeTitleRightsnca(self, masterKeyRev, titleKeyDec):
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- Print.info('writing masterKeyRev for %s, %d' % (str(self._path), masterKeyRev))
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), self.header.keyIndex))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- self.header.setRightsId(0)
- self.header.setKeyBlock(encKeyBlock)
- Hex.dump(encKeyBlock)
-
- def printtitleId(self, indent = 0):
- Print.info(str(self.header.titleId))
-
- def print_nca_type(self, indent = 0):
- Print.info(str(self.header.contentType))
-
- def cardstate(self, indent = 0):
- Print.info(hex(self.header.isGameCard))
-
- def read_pfs0_header(self, file = None, mode = 'rb'):
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset)
- pfs0_magic = self.read(4)
- pfs0_nfiles=self.readInt32()
- pfs0_table_size=self.readInt32()
- pfs0_reserved=self.read(0x4)
- Print.info('PFS0 Magic = ' + str(pfs0_magic))
- Print.info('PFS0 number of files = ' + str(pfs0_nfiles))
- Print.info('PFS0 string table size = ' + str(hx(pfs0_table_size.to_bytes(4, byteorder='big'))))
- for i in range(pfs0_nfiles):
- Print.info('........................')
- Print.info('PFS0 Content number ' + str(i+1))
- Print.info('........................')
- f_offset = self.readInt64()
- Print.info('offset = ' + str(hx(f_offset.to_bytes(8, byteorder='big'))))
- f_size = self.readInt32()
- Print.info('Size =\t' + str(hx(pfs0_table_size.to_bytes(4, byteorder='big'))))
- filename_offset = self.readInt32()
- Print.info('offset of filename = ' + str(hx(f_offset.to_bytes(8, byteorder='big'))))
- f_reserved= self.read(0x4)
-
- def read_cnmt(self, file = None, mode = 'rb'):
- feed=''
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- pfs0_size = self.header.get_pfs0_size()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset)
- titleid=self.readInt64()
- titleversion = self.read(0x4)
- type_n = self.read(0x1)
- self.seek(cmt_offset+0xE)
- offset=self.readInt16()
- content_entries=self.readInt16()
- meta_entries=self.readInt16()
- self.seek(cmt_offset+0x20)
- original_ID=self.readInt64()
- self.seek(cmt_offset+0x28)
- min_sversion=self.readInt32()
- length_of_emeta=self.readInt32()
- feed=self.html_feed(feed,6,message=str(os.path.basename(os.path.abspath(self._path))))
- message=["Titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Version:",(str(int.from_bytes(titleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Cnmt Type:",(sq_tools.cnmt_type(type_n))];feed=self.html_feed(feed,3,message)
- message=["Table offset:",(str(hx((offset+0x20).to_bytes(2, byteorder='big'))))];feed=self.html_feed(feed,3,message)
- message=["Number of content:",(str(content_entries))];feed=self.html_feed(feed,3,message)
- message=["Number of meta entries:",(str(meta_entries))];feed=self.html_feed(feed,3,message)
- message=["Application id\Patch id:",((str(hx(original_ID.to_bytes(8, byteorder='big')))[2:-1]).upper())];feed=self.html_feed(feed,3,message)
- message=["RequiredVersion:",str(min_sversion)];feed=self.html_feed(feed,3,message)
- message=["Length of exmeta:",str(length_of_emeta)];feed=self.html_feed(feed,3,message)
-
- self.seek(cmt_offset+offset+0x20)
- for i in range(content_entries):
- feed=self.html_feed(feed,2,message=str('Content number ' + str(i+1)))
- vhash = self.read(0x20)
- message=["Hash:",(str(hx(vhash))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- NcaId = self.read(0x10)
- message=["NcaId:",(str(hx(NcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- size = self.read(0x6)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- ncatype = self.read(0x1)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- IdOffset = self.read(0x1)
- message=["IdOffset:",(str(int.from_bytes(IdOffset, byteorder='little', signed=True)))];feed=self.html_feed(feed,3,message)
-
- self.seek(pfs0_offset+pfs0_size-0x20)
- digest = self.read(0x20)
- feed=self.html_feed(feed,7,message=['Digest= ',(str((str(hx(digest))[2:-1]).upper()))])
- self.seek(cmt_offset+offset+0x20+content_entries*0x38)
- if length_of_emeta>0:
- feed=self.html_feed(feed,2,message=str('Extended meta:'))
- num_prev_cnmt=self.read(0x4)
- num_prev_delta=self.read(0x4)
- num_delta_info=self.read(0x4)
- num_delta_application =self.read(0x4)
- num_previous_content=self.read(0x4)
- num_delta_content=self.read(0x4)
- self.read(0x4)
- message=["Number of previous cnmt entries:",(str(int.from_bytes(num_prev_cnmt, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Number of previous delta entries:",(str(int.from_bytes(num_prev_delta, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Number of previous content entries:",(str(int.from_bytes(num_previous_content, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Number of delta content entries:",(str(int.from_bytes(num_delta_content, byteorder='little')))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_prev_cnmt, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Previous cnmt records: '+ str(i+1)))
- titleid=self.readInt64()
- titleversion = self.read(0x4)
- type_n = self.read(0x1)
- unknown1=self.read(0x3)
- vhash = self.read(0x20)
- unknown2=self.read(0x2)
- unknown3=self.read(0x2)
- unknown4=self.read(0x4)
- message=["Titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["Version:",(str(int.from_bytes(titleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Content Type:",(sq_tools.cnmt_type(type_n))];feed=self.html_feed(feed,3,message)
- message=["Hash:",(str(hx(vhash))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(unknown2, byteorder='little'))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_prev_delta, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Previous delta records: '+ str(i+1)))
- oldtitleid=self.readInt64()
- newtitleid=self.readInt64()
- oldtitleversion = self.read(0x4)
- newtitleversion = self.read(0x4)
- size = self.read(0x8)
- unknown1=self.read(0x8)
- message=["Old titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["New titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["Old version:",(str(int.from_bytes(oldtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["New version:",(str(int.from_bytes(newtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_delta_info, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Delta info: '+ str(i+1)))
- oldtitleid=self.readInt64()
- newtitleid=self.readInt64()
- oldtitleversion = self.read(0x4)
- newtitleversion = self.read(0x4)
- index1=self.readInt64()
- index2=self.readInt64()
- message=["Old titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["New titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["Old version:",(str(int.from_bytes(oldtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["New version:",(str(int.from_bytes(newtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Index1:",(str(hx(index1.to_bytes(8, byteorder='big'))))];feed=self.html_feed(feed,3,message)
- message=["Index2:",(str(hx(index2.to_bytes(8, byteorder='big'))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_delta_application, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Delta application info: '+ str(i+1)))
- OldNcaId = self.read(0x10)
- NewNcaId = self.read(0x10)
- old_size = self.read(0x6)
- up2bytes = self.read(0x2)
- low4bytes = self.read(0x4)
- unknown1 = self.read(0x2)
- ncatype = self.read(0x1)
- installable = self.read(0x1)
- unknown2 = self.read(0x4)
- message=["OldNcaId:",(str(hx(OldNcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["NewNcaId:",(str(hx(NewNcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Old size:",(str(sq_tools.getSize(int.from_bytes(old_size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- message=["Unknown1:",(str(int.from_bytes(unknown1, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- message=["Installable:",(str(int.from_bytes(installable, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Upper 2 bytes of the new size:",(str(hx(up2bytes)))];feed=self.html_feed(feed,3,message)
- message=["Lower 4 bytes of the new size:",(str(hx(low4bytes)))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_previous_content, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Previous content records: '+ str(i+1)))
- NcaId = self.read(0x10)
- size = self.read(0x6)
- ncatype = self.read(0x1)
- unknown1 = self.read(0x1)
- message=["NcaId:",(str(hx(NcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_delta_content, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Delta content entry: '+ str(i+1)))
- vhash = self.read(0x20)
- message=["Hash:",(str(hx(vhash))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- NcaId = self.read(0x10)
- message=["NcaId:",(str(hx(NcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- size = self.read(0x6)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- ncatype = self.read(0x1)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- IdOffset = self.read(0x1)
- message=["IdOffset:",(str(int.from_bytes(IdOffset, byteorder='little', signed=True)))];feed=self.html_feed(feed,3,message)
- return feed
-
- def xml_gen(self,ofolder,nsha):
- file = None
- mode = 'rb'
- crypto1=self.header.getCryptoType()
- crypto2=self.header.getCryptoType2()
- if crypto2>crypto1:
- keygeneration=crypto2
- if crypto2<=crypto1:
- keygeneration=crypto1
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- pfs0_size = self.header.get_pfs0_size()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset)
- titleid=self.readInt64()
- titleversion = self.read(0x4)
- type_n = self.read(0x1)
- self.seek(cmt_offset+0xE)
- offset=self.readInt16()
- content_entries=self.readInt16()
- meta_entries=self.readInt16()
- self.seek(cmt_offset+0x18)
- RDSV=self.readInt64()
- self.seek(cmt_offset+0x20)
- original_ID=self.readInt64()
- self.seek(cmt_offset+0x28)
- min_sversion=self.readInt32()
- length_of_emeta=self.readInt32()
- self.seek(cmt_offset+offset+0x20)
-
- if str(hx(type_n)) == "b'1'":
- type='SystemProgram'
- if str(hx(type_n)) == "b'2'":
- type='SystemData'
- if str(hx(type_n)) == "b'3'":
- type='SystemUpdate'
- if str(hx(type_n)) == "b'4'":
- type='BootImagePackage'
- if str(hx(type_n)) == "b'5'":
- type='BootImagePackageSafe'
- if str(hx(type_n)) == "b'80'":
- type='Application'
- if str(hx(type_n)) == "b'81'":
- type='Patch'
- if str(hx(type_n)) == "b'82'":
- type='AddOnContent'
- if str(hx(type_n)) == "b'83'":
- type='Delta'
-
- titleid=str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid='0x'+titleid[2:-1]
- version = str(int.from_bytes(titleversion, byteorder='little'))
- RDSV=str(RDSV)
- xmlname = str(self._path)
- xmlname = xmlname[:-4] + '.xml'
- outfolder = str(ofolder)+'\\'
- textpath = os.path.join(outfolder, xmlname)
- with open(textpath, 'w+') as tfile:
- tfile.write('' + '\n')
- tfile.write('' + '\n')
- tfile.write(' '+ type +' ' + '\n')
- tfile.write(' '+ titleid +' ' + '\n')
- tfile.write(' '+ version +' ' + '\n')
- tfile.write(' '+ RDSV +' ' + '\n')
-
- for i in range(content_entries):
- vhash = self.read(0x20)
- NcaId = self.read(0x10)
- size = self.read(0x6)
- ncatype = self.readInt8()
- unknown = self.read(0x1)
- if ncatype==0:
- type='Meta'
- if str(ncatype)=="1":
- type='Program'
- if ncatype==2:
- type='Data'
- if ncatype==3:
- type='Control'
- if ncatype==4:
- type='HtmlDocument'
- if ncatype==5:
- type='LegalInformation'
- if ncatype==6:
- type='DeltaFragment'
-
- NcaId=str(hx(NcaId))
- NcaId=NcaId[2:-1]
- size=str(int.from_bytes(size, byteorder='little'))
- vhash=str(hx(vhash))
- vhash=vhash[2:-1]
-
- with open(textpath, 'a') as tfile:
- tfile.write(' ' + '\n')
- tfile.write(' '+ type +' ' + '\n')
- tfile.write(' '+ NcaId +' ' + '\n')
- tfile.write(' '+ size +' ' + '\n')
- tfile.write(' '+ vhash +' ' + '\n')
- tfile.write(' '+ str(self.header.cryptoType2) +' ' + '\n')
- tfile.write(' ' + '\n')
-
- self.seek(pfs0_offset+pfs0_size-0x20)
- digest = str(hx(self.read(0x20)))
- digest=digest[2:-1]
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID='0x'+original_ID[2:-1]
- metaname=os.path.basename(os.path.abspath(self._path))
- metaname = metaname[:-9]
- size=str(os.path.getsize(self._path))
- with open(textpath, 'a') as tfile:
- tfile.write(' ' + '\n')
- tfile.write(' '+ 'Meta' +' ' + '\n')
- tfile.write(' '+ metaname +' ' + '\n')
- tfile.write(' '+ size +' ' + '\n')
- tfile.write(' '+ nsha +' ' + '\n')
- tfile.write(' '+ str(keygeneration) +' ' + '\n')
- tfile.write(' ' + '\n')
- tfile.write(' '+ digest +' ' + '\n')
- tfile.write(' '+ str(keygeneration) +' ' + '\n')
- tfile.write(' '+ str(min_sversion) +' ' + '\n')
- tfile.write(' '+ original_ID +' ' + '\n')
- tfile.write(' ')
- return textpath
-
- def xml_gen_mod(self,ofolder,nsha,keygeneration):
- file = None
- mode = 'rb'
- crypto1=self.header.getCryptoType()
- crypto2=self.header.getCryptoType2()
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- pfs0_size = self.header.get_pfs0_size()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset)
- titleid=self.readInt64()
- titleversion = self.read(0x4)
- type_n = self.read(0x1)
- self.seek(cmt_offset+0xE)
- offset=self.readInt16()
- content_entries=self.readInt16()
- meta_entries=self.readInt16()
- self.seek(cmt_offset+0x18)
- RDSV=self.readInt64()
- self.seek(cmt_offset+0x20)
- original_ID=self.readInt64()
- self.seek(cmt_offset+0x28)
- min_sversion=self.readInt32()
- length_of_emeta=self.readInt32()
- self.seek(cmt_offset+offset+0x20)
-
- if str(hx(type_n)) == "b'1'":
- type='SystemProgram'
- if str(hx(type_n)) == "b'2'":
- type='SystemData'
- if str(hx(type_n)) == "b'3'":
- type='SystemUpdate'
- if str(hx(type_n)) == "b'4'":
- type='BootImagePackage'
- if str(hx(type_n)) == "b'5'":
- type='BootImagePackageSafe'
- if str(hx(type_n)) == "b'80'":
- type='Application'
- if str(hx(type_n)) == "b'81'":
- type='Patch'
- if str(hx(type_n)) == "b'82'":
- type='AddOnContent'
- if str(hx(type_n)) == "b'83'":
- type='Delta'
-
- titleid=str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid='0x'+titleid[2:-1]
- version = str(int.from_bytes(titleversion, byteorder='little'))
- RDSV=str(RDSV)
- xmlname = str(self._path)
- xmlname = xmlname[:-4] + '.xml'
- outfolder = str(ofolder)+'\\'
- textpath = os.path.join(outfolder, xmlname)
- with open(textpath, 'w+') as tfile:
- tfile.write('' + '\n')
- tfile.write('' + '\n')
- tfile.write(' '+ type +' ' + '\n')
- tfile.write(' '+ titleid +' ' + '\n')
- tfile.write(' '+ version +' ' + '\n')
- tfile.write(' '+ RDSV +' ' + '\n')
-
- for i in range(content_entries):
- vhash = self.read(0x20)
- NcaId = self.read(0x10)
- size = self.read(0x6)
- ncatype = self.readInt8()
- unknown = self.read(0x1)
- if ncatype==0:
- type='Meta'
- if str(ncatype)=="1":
- type='Program'
- if ncatype==2:
- type='Data'
- if ncatype==3:
- type='Control'
- if ncatype==4:
- type='HtmlDocument'
- if ncatype==5:
- type='LegalInformation'
- if ncatype==6:
- type='DeltaFragment'
-
- NcaId=str(hx(NcaId))
- NcaId=NcaId[2:-1]
- size=str(int.from_bytes(size, byteorder='little'))
- vhash=str(hx(vhash))
- vhash=vhash[2:-1]
-
- with open(textpath, 'a') as tfile:
- tfile.write(' ' + '\n')
- tfile.write(' '+ type +' ' + '\n')
- tfile.write(' '+ NcaId +' ' + '\n')
- tfile.write(' '+ size +' ' + '\n')
- tfile.write(' '+ vhash +' ' + '\n')
- tfile.write(' '+ str(self.header.cryptoType2) +' ' + '\n')
- tfile.write(' ' + '\n')
-
- self.seek(pfs0_offset+pfs0_size-0x20)
- digest = str(hx(self.read(0x20)))
- digest=digest[2:-1]
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID='0x'+original_ID[2:-1]
- metaname=os.path.basename(os.path.abspath(self._path))
- metaname = metaname[:-9]
- size=str(os.path.getsize(self._path))
- with open(textpath, 'a') as tfile:
- tfile.write(' ' + '\n')
- tfile.write(' '+ 'Meta' +' ' + '\n')
- tfile.write(' '+ metaname +' ' + '\n')
- tfile.write(' '+ size +' ' + '\n')
- tfile.write(' '+ nsha +' ' + '\n')
- tfile.write(' '+ str(keygeneration) +' ' + '\n')
- tfile.write(' ' + '\n')
- tfile.write(' '+ digest +' ' + '\n')
- tfile.write(' '+ str(keygeneration) +' ' + '\n')
- min_sversion=sq_tools.getMinRSV(keygeneration,min_sversion)
- tfile.write(' '+ str(min_sversion) +' ' + '\n')
- tfile.write(' '+ original_ID +' ' + '\n')
- tfile.write(' ')
- return textpath
-
-
-
-
- def ret_xml(self):
- file = None
- mode = 'rb'
- crypto1=self.header.getCryptoType()
- crypto2=self.header.getCryptoType2()
- if crypto2>crypto1:
- keygeneration=crypto2
- if crypto2<=crypto1:
- keygeneration=crypto1
- xmlname = str(self._path)
- xmlname = xmlname[:-4] + '.xml'
- metaname=str(self._path)
- metaname = metaname[:-9]
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- pfs0_size = self.header.get_pfs0_size()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset)
- titleid=self.readInt64()
- titleversion = self.read(0x4)
- type_n = self.read(0x1)
- self.seek(cmt_offset+0xE)
- offset=self.readInt16()
- content_entries=self.readInt16()
- meta_entries=self.readInt16()
- self.seek(cmt_offset+0x18)
- RDSV=self.readInt64()
- self.seek(cmt_offset+0x20)
- original_ID=self.readInt64()
- self.seek(cmt_offset+0x28)
- min_sversion=self.readInt32()
- length_of_emeta=self.readInt32()
- self.seek(cmt_offset+offset+0x20)
-
- if str(hx(type_n)) == "b'1'":
- type='SystemProgram'
- if str(hx(type_n)) == "b'2'":
- type='SystemData'
- if str(hx(type_n)) == "b'3'":
- type='SystemUpdate'
- if str(hx(type_n)) == "b'4'":
- type='BootImagePackage'
- if str(hx(type_n)) == "b'5'":
- type='BootImagePackageSafe'
- if str(hx(type_n)) == "b'80'":
- type='Application'
- if str(hx(type_n)) == "b'81'":
- type='Patch'
- if str(hx(type_n)) == "b'82'":
- type='AddOnContent'
- if str(hx(type_n)) == "b'83'":
- type='Delta'
-
- titleid=str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid='0x'+titleid[2:-1]
- version = str(int.from_bytes(titleversion, byteorder='little'))
- RDSV=str(RDSV)
-
- xml_string = '' + '\n'
- xml_string += '' + '\n'
- xml_string +=' '+ type +' ' + '\n'
- xml_string +=(' '+ titleid +' ' + '\n')
- xml_string +=(' '+ version +' ' + '\n')
- xml_string +=(' '+ RDSV +' ' + '\n')
-
- for i in range(content_entries):
- vhash = self.read(0x20)
- NcaId = self.read(0x10)
- size = self.read(0x6)
- ncatype = self.readInt8()
- unknown = self.read(0x1)
- if ncatype==0:
- type='Meta'
- if str(ncatype)=="1":
- type='Program'
- if ncatype==2:
- type='Data'
- if ncatype==3:
- type='Control'
- if ncatype==4:
- type='HtmlDocument'
- if ncatype==5:
- type='LegalInformation'
- if ncatype==6:
- type='DeltaFragment'
-
- NcaId=str(hx(NcaId))
- NcaId=NcaId[2:-1]
- size=str(int.from_bytes(size, byteorder='little'))
- vhash=str(hx(vhash))
- vhash=vhash[2:-1]
-
- xml_string +=(' ' + '\n')
- xml_string +=(' '+ type +' ' + '\n')
- xml_string +=(' '+ NcaId +' ' + '\n')
- xml_string +=(' '+ size +' ' + '\n')
- xml_string +=(' '+ vhash +' ' + '\n')
- xml_string +=(' '+ str(keygeneration) +' ' + '\n')
- xml_string +=(' ' + '\n')
-
- self.seek(pfs0_offset+pfs0_size-0x20)
- digest = str(hx(self.read(0x20)))
- digest=digest[2:-1]
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID='0x'+original_ID[2:-1]
- size=str(os.path.getsize(self._path))
-
- xml_string +=(' ' + '\n')
- xml_string +=(' '+ 'Meta' +' ' + '\n')
- xml_string +=(' '+ metaname +' ' + '\n')
- xml_string +=(' '+ size +' ' + '\n')
- xml_string +=(' '+ nsha +' ' + '\n')
- xml_string +=(' '+ str(keygeneration) +' ' + '\n')
- xml_string +=(' ' + '\n')
- xml_string +=(' '+ digest +' ' + '\n')
- xml_string +=(' '+ str(keygeneration) +' ' + '\n')
- xml_string +=(' '+ str(min_sversion) +' ' + '\n')
- xml_string +=(' '+ original_ID +' ' + '\n')
- xml_string +=(' ')
-
- #print(xml_string)
-
- size=len(xml_string)
-
- return xmlname,size,xml_string
-
-
- def printInfo(self, indent = 0):
- tabs = '\t' * indent
- Print.info('\n%sNCA Archive\n' % (tabs))
- super(Nca, self).printInfo(indent)
-# Print.info(tabs + 'Header Block Hash: ' + str(hx(self.header.get_hblock_hash())))
-# self.header.calculate_hblock_hash()
-# self.get_hblock()
-# self.calc_htable_hash()
-# Print.info('hash from pfs0: ' + str(hx(self.get_pfs0_hash())))
-# self.calc_pfs0_hash()
-# self.get_req_system()
-# Print.info(tabs + 'RSA-2048 signature 1 = ' + str(hx(self.header.signature1)))
-# Print.info(tabs + 'RSA-2048 signature 2 = ' + str(hx(self.header.signature2)))
- Print.info(tabs + 'magic = ' + str(self.header.magic))
- Print.info(tabs + 'titleId = ' + str(self.header.titleId))
- Print.info(tabs + 'rightsId = ' + str(self.header.rightsId))
- Print.info(tabs + 'isGameCard = ' + hex(self.header.isGameCard))
- Print.info(tabs + 'contentType = ' + str(self.header.contentType))
- #Print.info(tabs + 'cryptoType = ' + str(self.header.getCryptoType()))
- Print.info(tabs + 'SDK version = ' + self.get_sdkversion())
- Print.info(tabs + 'Size: ' + str(self.header.size))
- Print.info(tabs + 'Crypto-Type1: ' + str(self.header.cryptoType))
- Print.info(tabs + 'Crypto-Type2: ' + str(self.header.cryptoType2))
- Print.info(tabs + 'key Index: ' + str(self.header.keyIndex))
- #Print.info(tabs + 'key Block: ' + str(self.header.getKeyBlock()))
- for key in self.header.keys:
- Print.info(tabs + 'key Block: ' + str(hx(key)))
-
- Print.info('\n%sPartitions:' % (tabs))
-
- for s in self:
- s.printInfo(indent+1)
-
- #self.read_pfs0_header()
- #self.read_cnmt()
-
-
- def ncalist_bycnmt(self, file = None, mode = 'rb'):
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- pfs0_size = self.header.get_pfs0_size()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset)
- titleid=self.readInt64()
- titleversion = self.read(0x4)
- type_n = self.read(0x1)
- self.seek(cmt_offset+0xE)
- offset=self.readInt16()
- content_entries=self.readInt16()
- meta_entries=self.readInt16()
- self.seek(cmt_offset+0x20)
- original_ID=self.readInt64()
- self.seek(cmt_offset+0x28)
- min_sversion=self.readInt32()
- length_of_emeta=self.readInt32()
- self.seek(cmt_offset+offset+0x20)
- ncalist=list()
- for i in range(content_entries):
- vhash = self.read(0x20)
- NcaId = self.read(0x10)
- size = self.read(0x6)
- ncatype = self.read(0x1)
- unknown = self.read(0x1)
- nca2append=str(hx(NcaId))
- nca2append=nca2append[2:-1]+'.nca'
- ncalist.append(nca2append)
- nca_meta=str(self._path)
- ncalist.append(nca_meta)
- return ncalist
-
- def return_cnmt(self,file = None, mode = 'rb'):
- for f in self:
- cryptoType=f.get_cryptoType()
- cryptoKey=f.get_cryptoKey()
- cryptoCounter=f.get_cryptoCounter()
- pfs0_offset=0xC00+self.header.get_htable_offset()+self.header.get_pfs0_offset()
- super(Nca, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.seek(pfs0_offset+0x8)
- pfs0_table_size=self.readInt32()
- cmt_offset=pfs0_offset+0x28+pfs0_table_size
- self.seek(cmt_offset)
- return self.read()
-
- def copy_files(self,buffer,ofolder=False,filepath=False,io=0,eo=False):
- i=0
- if ofolder == False:
- outfolder = 'ofolder'
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- for f in self:
- if filepath==False:
- filename = str(i)
- i+=1
- filepath = os.path.join(outfolder, filename)
- fp = open(filepath, 'w+b')
- self.rewind();f.seek(io)
- for data in iter(lambda: f.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- fp.close()
- break
-
- def get_nacp_offset(self):
- for f in self:
- self.rewind()
- f.rewind()
- Langue = list()
- Langue = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
- SupLg=list()
- regionstr=""
- offset=0x14200
- for i in Langue:
- try:
- f.seek(offset+i*0x300)
- test=f.read(0x200)
- test = test.split(b'\0', 1)[0].decode('utf-8')
- test = (re.sub(r'[\/\\]+', ' ', test))
- test = test.strip()
- test2=f.read(0x100)
- test2 = test2.split(b'\0', 1)[0].decode('utf-8')
- test2 = (re.sub(r'[\/\\]+', ' ', test2))
- test2 = test2.strip()
- if test == "" or test2 == "":
- offset=0x14400
- f.seek(offset+i*0x300)
- test=f.read(0x200)
- test = test.split(b'\0', 1)[0].decode('utf-8')
- test = (re.sub(r'[\/\\]+', ' ', test))
- test = test.strip()
- test2=f.read(0x100)
- test2 = test2.split(b'\0', 1)[0].decode('utf-8')
- test2 = (re.sub(r'[\/\\]+', ' ', test2))
- test2 = test2.strip()
- if test == "" or test2 == "":
- offset=0x14000
- while offset<=(0x14200+i*0x300):
- offset=offset+0x100
- f.seek(offset+i*0x300)
- test=f.read(0x200)
- test = test.split(b'\0', 1)[0].decode('utf-8')
- test = (re.sub(r'[\/\\]+', ' ', test))
- test = test.strip()
- test2=f.read(0x100)
- test2 = test2.split(b'\0', 1)[0].decode('utf-8')
- test2 = (re.sub(r'[\/\\]+', ' ', test2))
- test2 = test2.strip()
- if test != "" and test2 != "" :
- offset=offset
- break
- if test != "":
- offset=offset
- break
- if test != "":
- offset=offset
- break
- else:
- break
- except:
- pass
- try:
- f.seek(offset+0x3060)
- ediver = f.read(0x10)
- #print('here2')
- #print(hx(ediver))
- try:
- ediver = ediver.split(b'\0', 1)[0].decode('utf-8')
- ediver = (re.sub(r'[\/\\]+', ' ', ediver))
- ediver = ediver.strip()
- except:
- offset=0x16900-0x300*14
- f.seek(offset+0x3060)
- ediver = f.read(0x10)
- ediver = ediver.split(b'\0', 1)[0].decode('utf-8')
- ediver = (re.sub(r'[\/\\]+', ' ', ediver))
- ediver = ediver.strip()
- try:
- int(ediver[0])+1
- except:
- ediver="-"
- if ediver == '-':
- for i in Langue:
- try:
- i=i+1
- offset2=offset-0x300*i
- f.seek(offset2+0x3060)
- ediver = f.read(0x10)
- ediver = ediver.split(b'\0', 1)[0].decode('utf-8')
- ediver = (re.sub(r'[\/\\]+', ' ', ediver))
- ediver = ediver.strip()
- try:
- int(ediver[0])+1
- offset=offset2
- break
- except:
- ediver="-"
- except:
- pass
- if ediver == '-':
- try:
- while (offset2+0x3060)<=0x18600:
- offset2+=0x100
- f.seek(offset2+0x3060)
- ediver = f.read(0x10)
- ediver = ediver.split(b'\0', 1)[0].decode('utf-8')
- ediver = (re.sub(r'[\/\\]+', ' ', ediver))
- ediver = ediver.strip()
- if ediver != '':
- if str(ediver[0])!='v' and str(ediver[0])!='V':
- try:
- int(ediver[0])+1
- offset=offset2
- break
- except:
- ediver="-"
- break
- except:
- ediver="-"
- except:
- pass
- f.seek(offset)
- #data=f.read()
- return offset
-
-
- def get_langueblock(self,title,roman=True,trans=False):
- for f in self:
- self.rewind()
- f.rewind()
- Langue = list()
- Langue = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
- SupLg=list()
- regionstr=""
- offset=0x14200
- for i in Langue:
- try:
- f.seek(offset+i*0x300)
- test=f.read(0x200)
- test = test.split(b'\0', 1)[0].decode('utf-8')
- test = (re.sub(r'[\/\\]+', ' ', test))
- test = test.strip()
- test2=f.read(0x100)
- test2 = test2.split(b'\0', 1)[0].decode('utf-8')
- test2 = (re.sub(r'[\/\\]+', ' ', test2))
- test2 = test2.strip()
- if test == "" or test2 == "":
- offset=0x14400
- f.seek(offset+i*0x300)
- test=f.read(0x200)
- test = test.split(b'\0', 1)[0].decode('utf-8')
- test = (re.sub(r'[\/\\]+', ' ', test))
- test = test.strip()
- test2=f.read(0x100)
- test2 = test2.split(b'\0', 1)[0].decode('utf-8')
- test2 = (re.sub(r'[\/\\]+', ' ', test2))
- test2 = test2.strip()
- if test == "" or test2 == "":
- offset=0x14000
- while offset<=(0x14200+i*0x300):
- offset=offset+0x100
- f.seek(offset+i*0x300)
- test=f.read(0x200)
- test = test.split(b'\0', 1)[0].decode('utf-8')
- test = (re.sub(r'[\/\\]+', ' ', test))
- test = test.strip()
- test2=f.read(0x100)
- test2 = test2.split(b'\0', 1)[0].decode('utf-8')
- test2 = (re.sub(r'[\/\\]+', ' ', test2))
- test2 = test2.strip()
- if test != "" and test2 != "" :
- offset=offset
- break
- if test != "":
- offset=offset
- break
- if test != "":
- offset=offset
- break
- else:
- break
- except:
- pass
- try:
- f.seek(offset+0x3060)
- ediver = f.read(0x10)
- #print('here2')
- #print(hx(ediver))
- try:
- ediver = ediver.split(b'\0', 1)[0].decode('utf-8')
- ediver = (re.sub(r'[\/\\]+', ' ', ediver))
- ediver = ediver.strip()
- except:
- offset=0x16900-0x300*14
- f.seek(offset+0x3060)
- ediver = f.read(0x10)
- ediver = ediver.split(b'\0', 1)[0].decode('utf-8')
- ediver = (re.sub(r'[\/\\]+', ' ', ediver))
- ediver = ediver.strip()
- try:
- int(ediver[0])+1
- except:
- ediver="-"
- if ediver == '-':
- for i in Langue:
- try:
- i=i+1
- offset2=offset-0x300*i
- f.seek(offset2+0x3060)
- ediver = f.read(0x10)
- ediver = ediver.split(b'\0', 1)[0].decode('utf-8')
- ediver = (re.sub(r'[\/\\]+', ' ', ediver))
- ediver = ediver.strip()
- try:
- int(ediver[0])+1
- offset=offset2
- break
- except:
- ediver="-"
- except:
- pass
- if ediver == '-':
- try:
- while (offset2+0x3060)<=0x18600:
- offset2+=0x100
- f.seek(offset2+0x3060)
- ediver = f.read(0x10)
- ediver = ediver.split(b'\0', 1)[0].decode('utf-8')
- ediver = (re.sub(r'[\/\\]+', ' ', ediver))
- ediver = ediver.strip()
- if ediver != '':
- if str(ediver[0])!='v' and str(ediver[0])!='V':
- try:
- int(ediver[0])+1
- offset=offset2
- break
- except:
- ediver="-"
- break
- except:
- ediver="-"
-
- except:
- pass
- Langue = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
- for i in Langue:
- try:
- f.seek(offset+i*0x300)
- title = f.read(0x200)
- title = title.split(b'\0', 1)[0].decode('utf-8')
- title = (re.sub(r'[\/\\]+', ' ', title))
- title = title.strip()
- if title != "":
- if i==0:
- SupLg.append("US (eng)")
- regionstr+='1|'
- if i==1:
- SupLg.append("UK (eng)")
- regionstr+='1|'
- if i==2:
- SupLg.append("JP")
- regionstr+='1|'
- if i==3:
- SupLg.append("FR")
- regionstr+='1|'
- if i==4:
- SupLg.append("DE")
- regionstr+='1|'
- if i==5:
- SupLg.append("LAT (spa)")
- regionstr+='1|'
- if i==6:
- SupLg.append("SPA")
- regionstr+='1|'
- if i==7:
- SupLg.append("IT")
- regionstr+='1|'
- if i==8:
- SupLg.append("DU")
- regionstr+='1|'
- if i==9:
- SupLg.append("CAD (fr)")
- regionstr+='1|'
- if i==10:
- SupLg.append("POR")
- regionstr+='1|'
- if i==11:
- SupLg.append("RU")
- regionstr+='1|'
- if i==12:
- SupLg.append("KOR")
- regionstr+='1|'
- if i==13:
- SupLg.append("TW (ch)")
- regionstr+='1|'
- if i==14:
- SupLg.append("CH")
- regionstr+='1|'
- else:
- regionstr+='0|'
- except:
- pass
- Langue = [0,1,6,5,7,10,3,4,9,8,2,11,12,13,14]
- for i in Langue:
- try:
- f.seek(offset+i*0x300)
- title = f.read(0x200)
- editor = f.read(0x100)
- title = title.split(b'\0', 1)[0].decode('utf-8')
- title = (re.sub(r'[\/\\]+', ' ', title))
- title = title.strip()
- editor = editor.split(b'\0', 1)[0].decode('utf-8')
- editor = (re.sub(r'[\/\\]+', ' ', editor))
- editor = editor.strip()
- if title == "":
- title = 'DLC'
- if title != 'DLC':
- title = title
- f.seek(offset+0x3060)
- ediver = f.read(0x10)
- ediver = ediver.split(b'\0', 1)[0].decode('utf-8')
- ediver = (re.sub(r'[\/\\]+', ' ', ediver))
- ediver = ediver.strip()
- if ediver == '':
- try:
- while (offset+0x3060)<=0x18600:
- offset+=0x100
- f.seek(offset+0x3060)
- ediver = f.read(0x10)
- ediver = ediver.split(b'\0', 1)[0].decode('utf-8')
- ediver = (re.sub(r'[\/\\]+', ' ', ediver))
- ediver = ediver.strip()
- if ediver != '':
- if str(ediver[0])!='v' and str(ediver[0])!='V':
- try:
- int(ediver[0])+1
- break
- except:
- ediver="-"
- break
- except:
- ediver="-"
- f.seek(offset+0x3028)
- isdemo = f.readInt8('little')
- if ediver !='-':
- if isdemo == 0:
- isdemo = 0
- elif isdemo == 1:
- isdemo = 1
- elif isdemo == 2:
- isdemo = 2
- else:
- isdemo = 0
- else:
- isdemo = 0
- if i == 2:
- if roman == True:
- kakasi = pykakasi.kakasi()
- kakasi.setMode("H", "a")
- kakasi.setMode("K", "a")
- kakasi.setMode("J", "a")
- kakasi.setMode("s", True)
- kakasi.setMode("E", "a")
- kakasi.setMode("a", None)
- kakasi.setMode("C", False)
- converter = kakasi.getConverter()
- title=converter.do(title)
- title=title[0].upper()+title[1:]
- editor=converter.do(editor)
- editor=editor[0].upper()+editor[1:]
- if trans==True:
- try:
- translator = Translator()
- translation=translator.translate(title,src='ja',dest='en')
- title=translation.text
- translation=translator.translate(editor,src='ja',dest='en')
- editor=translation.text
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- else:pass
- elif i == 14 or i == 13 or i==12:
- if roman == True:
- kakasi = pykakasi.kakasi()
- kakasi.setMode("H", "a")
- kakasi.setMode("K", "a")
- kakasi.setMode("J", "a")
- kakasi.setMode("s", True)
- kakasi.setMode("E", "a")
- kakasi.setMode("a", None)
- kakasi.setMode("C", False)
- converter = kakasi.getConverter()
- title=converter.do(title)
- title=title[0].upper()+title[1:]
- editor=converter.do(editor)
- editor=editor[0].upper()+editor[1:]
- if trans==True:
- try:
- translator = Translator()
- translation=translator.translate(title,src='ja',dest='en')
- title=translation.text
- translation=translator.translate(editor,src='ja',dest='en')
- editor=translation.text
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- else:pass
- else:
- if roman == True:
- kakasi = pykakasi.kakasi()
- kakasi.setMode("H", "a")
- kakasi.setMode("K", "a")
- kakasi.setMode("J", "a")
- kakasi.setMode("s", True)
- kakasi.setMode("E", "a")
- kakasi.setMode("a", None)
- kakasi.setMode("C", False)
- converter = kakasi.getConverter()
- ogname=title
- ogeditor=editor
- title=converter.do(title)
- title=title[0].upper()+title[1:]
- editor=converter.do(editor)
- editor=editor[0].upper()+editor[1:]
- title=self.choose_name(title,ogname)
- editor=self.choose_name(editor,ogeditor)
- else:pass
- title=re.sub(' +', ' ',title)
- editor=re.sub(' +', ' ',editor)
- return(title,editor,ediver,SupLg,regionstr[:-1],isdemo)
- except:
- pass
- regionstr="0|0|0|0|0|0|0|0|0|0|0|0|0|0"
- ediver='-'
- return(title,"",ediver,"",regionstr,"")
-
- def choose_name(self,name,ogname):
- from difflib import SequenceMatcher
- _name=name;_ogname=ogname
- name = re.sub(r'[àâá@äå]', 'a', name);name = re.sub(r'[ÀÂÁÄÅ]', 'A', name)
- name = re.sub(r'[èêéë]', 'e', name);name = re.sub(r'[ÈÊÉË]', 'E', name)
- name = re.sub(r'[ìîíï]', 'i', name);name = re.sub(r'[ÌÎÍÏ]', 'I', name)
- name = re.sub(r'[òôóöø]', 'o', name);name = re.sub(r'[ÒÔÓÖØ]', 'O', name)
- name = re.sub(r'[ùûúü]', 'u', name);name = re.sub(r'[ÙÛÚÜ]', 'U', name)
- name=name.lower()
- name = list([val for val in name if val.isalnum()])
- name = "".join(name)
-
- ogname = re.sub(r'[àâá@äå]', 'a', ogname);ogname = re.sub(r'[ÀÂÁÄÅ]', 'A', ogname)
- ogname = re.sub(r'[èêéë]', 'e', ogname);ogname = re.sub(r'[ÈÊÉË]', 'E', ogname)
- ogname = re.sub(r'[ìîíï]', 'i', ogname);ogname = re.sub(r'[ÌÎÍÏ]', 'I', ogname)
- ogname = re.sub(r'[òôóöø]', 'o', ogname);ogname = re.sub(r'[ÒÔÓÖØ]', 'O', ogname)
- ogname = re.sub(r'[ùûúü]', 'u', ogname);ogname = re.sub(r'[ÙÛÚÜ]', 'U', ogname)
- ogname=ogname.lower()
- ogname = list([val for val in ogname if val.isalnum()])
- ogname = "".join(ogname)
-
- ratio=SequenceMatcher(None, name, ogname).ratio()
- if ratio==1.0:
- return _ogname
- return _name
-
- def get_sdkversion(self):
- sdkversion=str(self.header.sdkVersion4)+'.'+str(self.header.sdkVersion3)+'.'+str(self.header.sdkVersion2)+'.'+str(self.header.sdkVersion1)
- return sdkversion
-
- def verify(self,feed,targetkg=False,endcheck=False,progress=False,bar=False):
- if feed == False:
- feed=''
- indent=' > '
- if self._path.endswith('cnmt.nca'):
- arrow=' -> '
- else:
- arrow=tabs+' -> '
- self.rewind()
- #print('Signature 1:')
- sign1 = self.header.signature1
- #Hex.dump(sign1)
- #print('')
- #print('Header data:')
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- self.header.rewind()
- orig_header= self.header.read(0xC00)
- self.header.seek(0x200)
- headdata = self.header.read(0x200)
- #print(hx(orig_header))
- #Hex.dump(headdata)
- if self.header.getSigKeyGen() == 0:
- pubkey=RSA.RsaKey(n=nca_header_fixed_key_modulus_00, e=RSA_PUBLIC_EXPONENT)
- else:
- pubkey=RSA.RsaKey(n=nca_header_fixed_key_modulus_01, e=RSA_PUBLIC_EXPONENT)
- rsapss = PKCS1_PSS.new(pubkey)
- digest = SHA256.new(headdata)
- verification=rsapss.verify(digest, sign1)
- crypto1=self.header.getCryptoType()
- crypto2=self.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- currkg=masterKeyRev
- if os.path.exists(self._path):
- printname=str(os.path.basename(os.path.abspath(self._path)))
- else:
- printname=str(self._path)
- if verification == True:
- try:
- bar.close()
- except:pass
- message=(indent+printname+arrow+'is PROPER');print(message);feed+=message+'\n'
- #print(hx(headdata))
- return True,False,self._path,feed,currkg,False,False,self.header.getgamecard()
- else:
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), self.header.keyIndex))
- KB1L=self.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) != 0:
- #print(hx(headdata))
- checkrights,kgchg,titlekey,tr,headdata,orkg=self.restorehead_tr()
- if headdata != False:
- orig_header=orig_header[0x00:0x200]+headdata+orig_header[0x400:]
- #print(hx(orig_header))
- orig_header=hcrypto.encrypt(orig_header)
- else:
- orig_header = False
- if checkrights == True:
- message=(indent+printname+arrow+'is PROPER');print(message);feed+=message+'\n'
- message=(tabs+'* '+"TITLERIGHTS WERE REMOVED");print(message);feed+=message+'\n'
- if kgchg == False:
- message=(tabs+'* '+"Original titlerights id is : "+(str(hx(tr)).upper())[2:-1]);print(message);feed+=message+'\n'
- message=(tabs+'* '+"Original titlekey is : "+(str(hx(titlekey)).upper())[2:-1]);print(message);feed+=message+'\n'
- tcheck=(str(hx(titlekey)).upper())[2:-1]
- if tcheck == '00000000000000000000000000000000':
- message=(tabs+'* '+"WARNING: sum(titlekey)=0 -> S.C. conversion may be incorrect and come from nsx file");print(message);feed+=message+'\n'
- elif kgchg == True:
- message=(tabs+'* '+"KEYGENERATION WAS CHANGED FROM "+str(orkg)+" TO "+str(currkg));print(message);feed+=message+'\n'
- message=(tabs+'* '+"Original titlerights id is -> "+(str(hx(tr)).upper())[2:-1]);print(message);feed+=message+'\n'
- message=(tabs+'* '+"Original titlekey is -> "+(str(hx(titlekey)).upper())[2:-1]);print(message);feed+=message+'\n'
- tcheck=(str(hx(titlekey)).upper())[2:-1]
- if tcheck == '00000000000000000000000000000000':
- message=(tabs+'* '+"WARNING: sum(titlekey)=0 -> S.C. conversion may be incorrect and come from nsx file");print(message);feed+=message+'\n'
- return True,orig_header,self._path,feed,orkg,tr,titlekey,self.header.getgamecard()
- else:
- message=(indent+self._path+arrow+'was MODIFIED');print(message);feed+=message+'\n'
- message=(tabs+'* '+"NOT VERIFIABLE COULD'VE BEEN TAMPERED WITH");print(message);feed+=message+'\n'
- return False,False,self._path,feed,False,False,False,self.header.getgamecard()
- else:
- if targetkg != False:
- if self.header.contentType == Type.Content.META:
- ver,kgchg,cardchange,headdata,orkg=self.verify_cnmt_withkg(targetkg)
- else:
- ver,kgchg,cardchange,headdata,orkg=self.restorehead_ntr()
- if headdata != False:
- orig_header=orig_header[0x00:0x200]+headdata+orig_header[0x400:]
- #print(hx(orig_header))
- orig_header=hcrypto.encrypt(orig_header)
- else:
- orig_header = False
- if ver == True:
- OGGC=self.header.getgamecard()
- chkkg=currkg
- if targetkg == False:
- message=(indent+printname+arrow+'is PROPER');print(message);feed+=message+'\n'
- else:
- if progress != False:
- message=(tabs+'* '+"ORIGIN OF CNMT FILE IS PROPER");bar.write(message);feed+=message+'\n'
- else:
- message=(tabs+'* '+"ORIGIN OF CNMT FILE IS PROPER");print(message);feed+=message+'\n'
- if kgchg == True:
- if progress != False:
- message=(tabs+'* '+"KEYGENERATION WAS CHANGED FROM "+str(orkg)+" TO "+str(currkg));bar.write(message);feed+=message+'\n'
- else:
- message=(tabs+'* '+"KEYGENERATION WAS CHANGED FROM "+str(orkg)+" TO "+str(currkg));print(message);feed+=message+'\n'
- chkkg=orkg
- if cardchange == True:
- if self.header.getgamecard() != 0:
- OGGC=0
- if progress != False:
- message=(tabs+'* '+"ISGAMECARD WAS CHANGED FROM 0 TO 1");bar.write(message);feed+=message+'\n'
- else:
- message=(tabs+'* '+"ISGAMECARD WAS CHANGED FROM 0 TO 1");print(message);feed+=message+'\n'
- else:
- OGGC=1
- if progress != False:
- message=(tabs+'* '+"ISGAMECARD WAS CHANGED FROM 1 TO 0");bar.write(message);feed+=message+'\n'
- else:
- message=(tabs+'* '+"ISGAMECARD WAS CHANGED FROM 1 TO 0");print(message);feed+=message+'\n'
- return True,orig_header,self._path,feed,chkkg,False,False,OGGC
- else:
- if self.header.contentType == Type.Content.META:
- if targetkg == False:
- if progress != False:
- pass
- else:
- message=(indent+printname+arrow+'needs RSV check');print(message);feed+=message+'\n'
- message=(tabs+'* '+"CHECKING INTERNAL HASHES");print(message);feed+=message+'\n'
- if progress == False:
- feed,correct=self.check_cnmt_hashes(feed)
- if correct == True:
- if progress != False:
- message=(tabs+'* '+"INTERNAL HASHES MATCH");bar.write(message);feed+=message+'\n'
- else:
- message=(tabs+'* '+"INTERNAL HASHES MATCH");print(message);feed+=message+'\n'
- if correct == False:
- if progress != False:
- message=(tabs+'* '+"INTERNAL HASH MISSMATCH");bar.write(message);feed+=message+'\n'
- message=(tabs+'* '+"BAD CNMT FILE!!!");bar.write(message);feed+=message+'\n'
- else:
- message=(tabs+'* '+"INTERNAL HASH MISSMATCH");print(message);feed+=message+'\n'
- message=(tabs+'* '+"BAD CNMT FILE!!!");print(message);feed+=message+'\n'
- return 'BADCNMT',False,self._path,feed,False,False,False,self.header.getgamecard()
- else:
- if endcheck == False:
- pass
- elif endcheck == True:
- if progress != False:
- message=(indent+printname+arrow+'was MODIFIED');bar.write(message);feed+=message+'\n'
- message=(tabs+'* '+"NOT VERIFIABLE!!!");bar.write(message);feed+=message+'\n'
- else:
- message=(indent+printname+arrow+'was MODIFIED');print(message);feed+=message+'\n'
- message=(tabs+'* '+"NOT VERIFIABLE!!!");print(message);feed+=message+'\n'
- return False,False,self._path,feed,False,False,False,self.header.getgamecard()
- else:
- if os.path.exists(self._path):
- printname=str(os.path.basename(os.path.abspath(self._path)))
- else:
- printname=str(self._path)
- if progress != False:
- message=(indent+printname+arrow+'was MODIFIED');bar.write(message);feed+=message+'\n'
- message=(tabs+'* '+"NOT VERIFIABLE!!!");bar.write(message);feed+=message+'\n'
- else:
- message=(indent+printname+arrow+'was MODIFIED');print(message);feed+=message+'\n'
- message=(tabs+'* '+"NOT VERIFIABLE!!!");print(message);feed+=message+'\n'
- return False,False,self._path,feed,False,False,False,self.header.getgamecard()
- if progress != False:
- message=(indent+printname+arrow+'was MODIFIED');bar.write(message);feed+=message+'\n'
- message=(tabs+'* '+"NOT VERIFIABLE!!!");bar.write(message);feed+=message+'\n'
- else:
- message=(indent+printname+arrow+'was MODIFIED');print(message);feed+=message+'\n'
- message=(tabs+'* '+"NOT VERIFIABLE!!!");print(message);feed+=message+'\n'
- return False,False,self._path,feed,False,False,False,self.header.getgamecard()
-
- def verify_cnmt_withkg(self,targetkg):
- sign1 = self.header.signature1
- self.header.seek(0x200)
- headdata = self.header.read(0x200)
- card='01';card=bytes.fromhex(card)
- eshop='00';eshop=bytes.fromhex(eshop)
- #print(hx(headdata))
- #Hex.dump(headdata)
- if self.header.getSigKeyGen() == 0:
- pubkey=RSA.RsaKey(n=nca_header_fixed_key_modulus_00, e=RSA_PUBLIC_EXPONENT)
- else:
- pubkey=RSA.RsaKey(n=nca_header_fixed_key_modulus_01, e=RSA_PUBLIC_EXPONENT)
- rsapss = PKCS1_PSS.new(pubkey)
- digest = SHA256.new(headdata)
- verification=rsapss.verify(digest, sign1)
- crypto1=self.header.getCryptoType()
- crypto2=self.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- if self.header.getgamecard() == 0:
- headdata2 = b''
- headdata2=headdata[0x00:0x04]+eshop+headdata[0x05:]
- digest2 = SHA256.new(headdata2)
- verification2=rsapss.verify(digest2, sign1)
- if verification2 == True:
- return True,False,False,headdata2,masterKeyRev
- else:
- headdata2 = b''
- headdata2=headdata[0x00:0x04]+card+headdata[0x05:]
- digest2 = SHA256.new(headdata2)
- verification2=rsapss.verify(digest2, sign1)
- if verification2 == True:
- return True,False,True,headdata2,masterKeyRev
- else:
- headdata2 = b''
- headdata2=headdata[0x00:0x04]+eshop+headdata[0x05:]
- digest2 = SHA256.new(headdata2)
- verification2=rsapss.verify(digest2, sign1)
- if verification2 == True:
- return True,False,True,headdata2,masterKeyRev
- else:
- headdata2 = b''
- headdata2=headdata[0x00:0x04]+card+headdata[0x05:]
- digest2 = SHA256.new(headdata2)
- verification2=rsapss.verify(digest2, sign1)
- if verification2 == True:
- return True,False,False,headdata2,masterKeyRev
-
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), self.header.keyIndex)
- crypto = aes128.AESECB(key)
- encKeyBlock = self.header.getKeyBlock()
- decKeyBlock = crypto.decrypt(encKeyBlock)
- newMasterKeyRev=targetkg
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(newMasterKeyRev), self.header.keyIndex)
- crypto = aes128.AESECB(key)
- reEncKeyBlock = crypto.encrypt(decKeyBlock)
- i=targetkg
- cr2=str(hex(i))[2:]
- if i<3:
- crypto1='0'+str(i)
- crypto2='00'
- else:
- cr2=str(hex(i))[2:]
- if len(str(cr2))==1:
- crypto1='02'
- crypto2='0'+str(cr2)
- elif len(str(cr2))==2:
- crypto1='02'
- crypto2=str(cr2)
- crypto1=bytes.fromhex(crypto1);crypto2=bytes.fromhex(crypto2)
- headdata1 = b''
- headdata1=headdata[0x00:0x04]+card+headdata[0x05:0x06]+crypto1+headdata[0x07:0x20]+crypto2+headdata[0x21:0x100]+reEncKeyBlock+headdata[0x140:]
- #print(hx(headdata1))
- headdata2 = b''
- headdata2=headdata[0x00:0x04]+eshop+headdata1[0x05:]
- #print(hx(headdata2))
- digest1 = SHA256.new(headdata1)
- digest2 = SHA256.new(headdata2)
- verification1=rsapss.verify(digest1, sign1)
- verification2=rsapss.verify(digest2, sign1)
- if verification1 == True:
- if self.header.getgamecard() == 0:
- return True,True,True,headdata1,newMasterKeyRev
- else:
- return True,True,False,headdata1,newMasterKeyRev
- if verification2 == True:
- if self.header.getgamecard() == 0:
- return True,True,False,headdata2,newMasterKeyRev
- else:
- return True,True,True,headdata2,newMasterKeyRev
- return False,False,False,False,masterKeyRev
-
- def check_cnmt_hashes(self,feed):
- sha=self.calc_pfs0_hash()
- sha_get=self.calc_pfs0_hash()
- correct=True
- if sha == sha_get:
- message=(tabs+' '+" - PFS0 hash is CORRECT");print(message);feed+=message+'\n'
- #print(hx(sha))
- #print(hx(sha_get))
- else:
- message=(tabs+' '+" - PFS0 hash is INCORRECT!!!");print(message);feed+=message+'\n'
- #print(hx(sha))
- #print(hx(sha_get))
- correct=False
- sha2=self.calc_htable_hash()
- sha2_get=self.calc_htable_hash()
- if sha2 == sha2_get:
- message=(tabs+' '+" - HASH TABLE hash is CORRECT");print(message);feed+=message+'\n'
- #print(hx(sha2))
- #print(hx(sha2_get))
- else:
- message=(tabs+' '+" - HASH TABLE hash is INCORRECT!!!");print(message);feed+=message+'\n'
- #print(hx(sha2))
- #print(hx(sha2_get))
- correct=False
- sha3=self.header.calculate_hblock_hash()
- sha3_get=self.header.calculate_hblock_hash()
- if sha3 == sha3_get:
- message=(tabs+' '+" - HEADER BLOCK hash is CORRECT");print(message);feed+=message+'\n'
- #print(hx(sha3))
- #print(hx(sha3_get))
- else:
- message=(tabs+' '+" - HEADER BLOCK hash is INCORRECT!!!");print(message);feed+=message+'\n'
- #print(hx(sha3))
- #print(hx(sha3_get))
- correct=False
- return feed,correct
-
- def restorehead_tr(self):
- sign1 = self.header.signature1
- crypto1=self.header.getCryptoType()
- crypto2=self.header.getCryptoType2()
- nca_id=self.header.titleId
- cr2=str(hex(crypto2))[2:]
- if len(str(cr2))==1:
- tr=nca_id+'000000000000000'+str(cr2)
- elif len(str(cr2))==2:
- tr=nca_id+'00000000000000'+str(cr2)
- tr=bytes.fromhex(tr)
- if crypto1>crypto2:
- masterKeyRev = crypto1
- else:
- masterKeyRev = crypto2
-
- encKeyBlock = self.header.getKeyBlock()
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), self.header.keyIndex)
- crypto = aes128.AESECB(key)
- decKeyBlock = crypto.decrypt(encKeyBlock[:16])
- titleKeyEnc = Keys.encryptTitleKey(decKeyBlock, Keys.getMasterKeyIndex(masterKeyRev))
-
- currdecKeyBlock=decKeyBlock
-
- self.header.seek(0x200)
- headdata = b''
- headdata += self.header.read(0x30)
- headdata += tr
- self.header.read(0x10)
- headdata += self.header.read(0x100-0x40)
- headdata += bytes.fromhex('00'*0x10*4)
- self.header.seek(0x340)
- headdata += self.header.read(0x100-0x40)
- #print(hx(headdata))
- #Hex.dump(headdata)
- if self.header.getSigKeyGen() == 0:
- pubkey=RSA.RsaKey(n=nca_header_fixed_key_modulus_00, e=RSA_PUBLIC_EXPONENT)
- else:
- pubkey=RSA.RsaKey(n=nca_header_fixed_key_modulus_01, e=RSA_PUBLIC_EXPONENT)
- rsapss = PKCS1_PSS.new(pubkey)
- digest = SHA256.new(headdata)
- verification=rsapss.verify(digest, sign1)
- if verification == True:
- return True,False,titleKeyEnc,tr,headdata,masterKeyRev
- else:
- cr2=str(hex(crypto2))[2:]
- if len(str(cr2))==1:
- tr2=nca_id[:-3]+'800000000000000000'+str(cr2)
- elif len(str(cr2))==2:
- tr2=nca_id[:-3]+'80000000000000000'+str(cr2)
- tr2=bytes.fromhex(tr2)
- headdata2 = b''
- headdata2=headdata[0x00:0x30]+tr2+headdata[0x40:]
- digest2 = SHA256.new(headdata2)
- verification=rsapss.verify(digest2, sign1)
- if verification == True:
- return True,False,titleKeyEnc,tr2,headdata2,masterKeyRev
- else:
- nlist=list()
- for i in range(12):
- nlist.append(i)
- nlist=sorted(nlist, key=int, reverse=True)
- for i in nlist:
- if i<3:
- crypto1='0'+str(i)
- crypto2='00'
- else:
- cr2=str(hex(i))[2:]
- if len(str(cr2))==1:
- crypto1='02'
- crypto2='0'+str(cr2)
- elif len(str(cr2))==2:
- crypto1='02'
- crypto2=str(cr2)
- masterKeyRev = i
- encKeyBlock = self.header.getKeyBlock()
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), self.header.keyIndex)
- crypto = aes128.AESECB(key)
- decKeyBlock = currdecKeyBlock
- titleKeyEnc = Keys.encryptTitleKey(decKeyBlock, Keys.getMasterKeyIndex(masterKeyRev))
-
- tr1=nca_id+'000000000000000'+str(crypto2[1])
- tr2=nca_id[:-3]+'800000000000000000'+str(crypto2[1])
- tr1=bytes.fromhex(tr1);tr2=bytes.fromhex(tr2)
- crypto1=bytes.fromhex(crypto1);crypto2=bytes.fromhex(crypto2)
- headdata1 = b''
- headdata1=headdata[0x00:0x06]+crypto1+headdata[0x07:0x20]+crypto2+headdata[0x21:0x30]+tr1+headdata[0x40:]
- headdata2 = b''
- headdata2=headdata1[0x00:0x30]+tr2+headdata[0x40:]
- digest1 = SHA256.new(headdata1)
- digest2 = SHA256.new(headdata2)
- verification1=rsapss.verify(digest1, sign1)
- verification2=rsapss.verify(digest2, sign1)
- if verification1 == True:
- return True,True,titleKeyEnc,tr1,headdata1,masterKeyRev
- if verification2 == True:
- return True,True,titleKeyEnc,tr2,headdata2,masterKeyRev
- return False,False,False,False,False,masterKeyRev
-
- def restorehead_ntr(self):
- sign1 = self.header.signature1
- self.header.seek(0x200)
- headdata = self.header.read(0x200)
- card='01';card=bytes.fromhex(card)
- eshop='00';eshop=bytes.fromhex(eshop)
- #print(hx(headdata))
- #Hex.dump(headdata)
- if self.header.getSigKeyGen() == 0:
- pubkey=RSA.RsaKey(n=nca_header_fixed_key_modulus_00, e=RSA_PUBLIC_EXPONENT)
- else:
- pubkey=RSA.RsaKey(n=nca_header_fixed_key_modulus_01, e=RSA_PUBLIC_EXPONENT)
- rsapss = PKCS1_PSS.new(pubkey)
- digest = SHA256.new(headdata)
- verification=rsapss.verify(digest, sign1)
- crypto1=self.header.getCryptoType()
- crypto2=self.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- if self.header.getgamecard() == 0:
- headdata2 = b''
- headdata2=headdata[0x00:0x04]+eshop+headdata[0x05:]
- digest2 = SHA256.new(headdata2)
- verification2=rsapss.verify(digest2, sign1)
- if verification2 == True:
- return True,False,False,headdata2,masterKeyRev
- else:
- headdata2 = b''
- headdata2=headdata[0x00:0x04]+card+headdata[0x05:]
- digest2 = SHA256.new(headdata2)
- verification2=rsapss.verify(digest2, sign1)
- if verification2 == True:
- return True,False,True,headdata2,masterKeyRev
- else:
- headdata2 = b''
- headdata2=headdata[0x00:0x04]+eshop+headdata[0x05:]
- digest2 = SHA256.new(headdata2)
- verification2=rsapss.verify(digest2, sign1)
- if verification2 == True:
- return True,False,True,headdata2,masterKeyRev
- else:
- headdata2 = b''
- headdata2=headdata[0x00:0x04]+card+headdata[0x05:]
- digest2 = SHA256.new(headdata2)
- verification2=rsapss.verify(digest2, sign1)
- if verification2 == True:
- return True,False,False,headdata2,masterKeyRev
- else:
- pass
- if self.header.contentType == Type.Content.META:
- return False,False,False,False,masterKeyRev
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), self.header.keyIndex)
- crypto = aes128.AESECB(key)
- encKeyBlock = self.header.getKeyBlock()
- decKeyBlock = crypto.decrypt(encKeyBlock)
- nlist=list()
- for i in range(12):
- nlist.append(i)
- nlist=sorted(nlist, key=int, reverse=True)
- for i in nlist:
- if i<3:
- crypto1='0'+str(i)
- crypto2='00'
- else:
- cr2=str(hex(i))[2:]
- if len(str(cr2))==1:
- crypto1='02'
- crypto2='0'+str(cr2)
- elif len(str(cr2))==2:
- crypto1='02'
- crypto2=str(cr2)
- newMasterKeyRev=i
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(newMasterKeyRev), self.header.keyIndex)
- crypto = aes128.AESECB(key)
- reEncKeyBlock = crypto.encrypt(decKeyBlock)
- crypto1=bytes.fromhex(crypto1);crypto2=bytes.fromhex(crypto2)
- headdata1 = b''
- headdata1=headdata[0x00:0x04]+card+headdata[0x05:0x06]+crypto1+headdata[0x07:0x20]+crypto2+headdata[0x21:0x100]+reEncKeyBlock+headdata[0x140:]
- #print(hx(headdata1))
- headdata2 = b''
- headdata2=headdata[0x00:0x04]+eshop+headdata1[0x05:]
- #print(hx(headdata2))
- if self.header.contentType != Type.Content.META:
- digest1 = SHA256.new(headdata1)
- digest2 = SHA256.new(headdata2)
- verification1=rsapss.verify(digest1, sign1)
- verification2=rsapss.verify(digest2, sign1)
- if verification1 == True:
- if self.header.getgamecard() == 0:
- return True,True,True,headdata1,newMasterKeyRev
- else:
- return True,True,False,headdata1,newMasterKeyRev
- if verification2 == True:
- if self.header.getgamecard() == 0:
- return True,True,False,headdata2,newMasterKeyRev
- else:
- return True,True,True,headdata2,newMasterKeyRev
- return False,False,False,False,masterKeyRev
-
- def ret_nacp(self):
- if str(self.header.contentType) == 'Content.CONTROL':
- offset=self.get_nacp_offset()
- for f in self:
- f.seek(offset)
- return f.read()
-
-#READ NACP FILE WITHOUT EXTRACTION
- def read_nacp(self,feed=''):
- if str(self.header.contentType) == 'Content.CONTROL':
- offset=self.get_nacp_offset()
- for f in self:
- f.seek(offset)
- nacp = Nacp()
- feed=nacp.par_getNameandPub(f.read(0x300*15),feed)
- message='...............................';print(message);feed+=message+'\n'
- message='NACP FLAGS';print(message);feed+=message+'\n'
- message='...............................';print(message);feed+=message+'\n'
- f.seek(offset+0x3000)
- feed=nacp.par_Isbn(f.read(0x24),feed)
- f.seek(offset+0x3025)
- feed=nacp.par_getStartupUserAccount(f.readInt8('little'),feed)
- feed=nacp.par_getUserAccountSwitchLock(f.readInt8('little'),feed)
- feed=nacp.par_getAddOnContentRegistrationType(f.readInt8('little'),feed)
- feed=nacp.par_getContentType(f.readInt8('little'),feed)
- f.seek(offset+0x3030)
- feed=nacp.par_getParentalControl(f.readInt8('little'),feed)
- f.seek(offset+0x3034)
- feed=nacp.par_getScreenshot(f.readInt8('little'),feed)
- feed=nacp.par_getVideoCapture(f.readInt8('little'),feed)
- feed=nacp.par_dataLossConfirmation(f.readInt8('little'),feed)
- feed=nacp.par_getPlayLogPolicy(f.readInt8('little'),feed)
- f.seek(offset+0x3038)
- feed=nacp.par_getPresenceGroupId(f.readInt64('little'),feed)
- f.seek(offset+0x3040)
- listages=list()
- message='...............................';print(message);feed+=message+'\n'
- message='Age Ratings';print(message);feed+=message+'\n'
- message='...............................';print(message);feed+=message+'\n'
- for i in range(12):
- feed=nacp.par_getRatingAge(f.readInt8('little'),i,feed)
- f.seek(offset+0x3060)
- message='...............................';print(message);feed+=message+'\n'
- message='NACP ATTRIBUTES';print(message);feed+=message+'\n'
- message='...............................';print(message);feed+=message+'\n'
- try:
- feed=nacp.par_getDisplayVersion(f.read(0xF),feed)
- f.seek(offset+0x3070)
- feed=nacp.par_getAddOnContentBaseId(f.readInt64('little'),feed)
- f.seek(offset+0x3078)
- feed=nacp.par_getSaveDataOwnerId(f.readInt64('little'),feed)
- f.seek(offset+0x3080)
- feed=nacp.par_getUserAccountSaveDataSize(f.readInt64('little'),feed)
- f.seek(offset+0x3088)
- feed=nacp.par_getUserAccountSaveDataJournalSize(f.readInt64('little'),feed)
- f.seek(offset+0x3090)
- feed=nacp.par_getDeviceSaveDataSize(f.readInt64('little'),feed)
- f.seek(offset+0x3098)
- feed=nacp.par_getDeviceSaveDataJournalSize(f.readInt64('little'),feed)
- f.seek(offset+0x30A0)
- feed=nacp.par_getBcatDeliveryCacheStorageSize(f.readInt64('little'),feed)
- f.seek(offset+0x30A8)
- feed=nacp.par_getApplicationErrorCodeCategory(f.read(0x07),feed)
- f.seek(offset+0x30B0)
- feed=nacp.par_getLocalCommunicationId(f.readInt64('little'),feed)
- f.seek(offset+0x30F0)
- feed=nacp.par_getLogoType(f.readInt8('little'),feed)
- feed=nacp.par_getLogoHandling(f.readInt8('little'),feed)
- feed=nacp.par_getRuntimeAddOnContentInstall(f.readInt8('little'),feed)
- f.seek(offset+0x30F6)
- feed=nacp.par_getCrashReport(f.readInt8('little'),feed)
- feed=nacp.par_getHdcp(f.readInt8('little'),feed)
- feed=nacp.par_getSeedForPseudoDeviceId(f.readInt64('little'),feed)
- f.seek(offset+0x3100)
- feed=nacp.par_getBcatPassphrase(f.read(0x40),feed)
- f.seek(offset+0x3148)
- feed=nacp.par_UserAccountSaveDataSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3150)
- feed=nacp.par_UserAccountSaveDataJournalSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3158)
- feed=nacp.par_getDeviceSaveDataSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3160)
- feed=nacp.par_getDeviceSaveDataJournalSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3168)
- feed=nacp.par_getTemporaryStorageSize(f.readInt64('little'),feed)
- feed=nacp.par_getCacheStorageSize(f.readInt64('little'),feed)
- f.seek(offset+0x3178)
- feed=nacp.par_getCacheStorageJournalSize(f.readInt64('little'),feed)
- feed=nacp.par_getCacheStorageDataAndJournalSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3188)
- feed=nacp.par_getCacheStorageIndexMax(f.readInt64('little'),feed)
- f.seek(offset+0x3188)
- feed=nacp.par_getPlayLogQueryableApplicationId(f.readInt64('little'),feed)
- f.seek(offset+0x3210)
- feed=nacp.par_getPlayLogQueryCapability(f.readInt8('little'),feed)
- feed=nacp.par_getRepair(f.readInt8('little'),feed)
- feed=nacp.par_getProgramIndex(f.readInt8('little'),feed)
- feed=nacp.par_getRequiredNetworkServiceLicenseOnLaunch(f.readInt8('little'),feed)
- except:continue
- return feed
-
-#PATCH NETWORK LICENSE
- def patch_netlicense(self):
- if str(self.header.contentType) == 'Content.CONTROL':
- offset=self.get_nacp_offset()
- for f in self:
- nacp = Nacp()
- print('CURRENT VALUES:')
- f.seek(offset+0x3025)
- startup_acc=f.readInt8('little')
- netlicense=f.readInt8('little')
- f.seek(offset+0x3213)
- netlicense=f.readInt8('little')
- nacp.par_getStartupUserAccount(startup_acc)
- nacp.par_getRequiredNetworkServiceLicenseOnLaunch(netlicense)
- if netlicense==0 and startup_acc<2:
- print(str(self._path)+" doesn't need a linked account")
- return False
- else:
- print(' -> '+str(self._path)+" needs a linked account. Patching...")
- print('NEW VALUES:')
- if startup_acc==2:
- f.seek(offset+0x3025)
- f.writeInt8(1)
- if netlicense==1:
- f.seek(offset+0x3213)
- f.writeInt8(0)
- f.seek(offset+0x3025)
- nacp.par_getStartupUserAccount(f.readInt8('little'))
- f.seek(offset+0x3213)
- nacp.par_getRequiredNetworkServiceLicenseOnLaunch(f.readInt8('little'))
- return True
- def redo_lvhashes(self):
- if str(self.header.contentType) == 'Content.CONTROL':
- #offset=self.get_nacp_offset()
- for fs in self.sectionFilesystems:
- pfs0=fs
- sectionHeaderBlock = fs.buffer
- inmemoryfile = io.BytesIO(sectionHeaderBlock)
- self.seek(fs.offset)
- pfs0Offset=fs.offset
- leveldata,hash,masterhashsize,superhashoffset=self.prIVFCData(inmemoryfile)
- return leveldata,superhashoffset
-
- def set_lv_hash(self,j,leveldata):
- if str(self.header.contentType) == 'Content.CONTROL':
- for fs in self.sectionFilesystems:
- levelnumb=leveldata[j][0]
- lvoffs=leveldata[j][1]
- levelsize=leveldata[j][2]
- lvbsize=leveldata[j][3]
- fs.seek(lvoffs)
- data = fs.read(lvbsize)
- newhash=(str(sha256(data).hexdigest()))
- fs.seek((j-1)*0x4000)
- hashlv=(hx(fs.read(32))).decode('utf-8')
- if str(hashlv) != str(newhash):
- fs.seek((j-1)*0x4000)
- sha=bytes.fromhex(newhash)
- fs.write(sha)
- print('Old lv'+str(j)+' hash: '+str(hashlv))
- print('New lv'+str(j)+' hash: '+str(newhash))
-
- def set_lvsuperhash(self,leveldata,superhashoffset):
- if str(self.header.contentType) == 'Content.CONTROL':
- for fs in self.sectionFilesystems:
- memlv0 = io.BytesIO(fs.read((leveldata[0][2])*(len(leveldata)-1)))
- memlv0.seek(0);newlvdata=memlv0.read()
- memlv0.seek(0);ndat=memlv0.read(0x4000)
- superhash=(str(sha256(ndat).hexdigest()))
- self.header.seek(0x400+superhashoffset)
- test = hx((self.header.read(32))).decode('utf-8');print('-OLD IVFC_Hash: '+str(test))
- self.header.seek(0x400+superhashoffset)
- self.header.write(bytes.fromhex(superhash))
- self.header.seek(0x400+superhashoffset)
- newivfchash = hx((self.header.read(32))).decode('utf-8');print('-NEW IVFC_Hash: '+str(newivfchash))
- fs.seek(0)
- fs.write(newlvdata)
-
- def prIVFCData(self,inmemoryfile):
- #Hex.dump(inmemoryfile.read())
- inmemoryfile.seek(0)
- version=int.from_bytes(inmemoryfile.read(0x2), byteorder='little', signed=True);print('-Version: '+str(version))
- fstype=int.from_bytes(inmemoryfile.read(0x1), byteorder='little', signed=True);print('-FileSystemtype: '+str(fstype))
- hashtype=int.from_bytes(inmemoryfile.read(0x1), byteorder='little', signed=True);print('-HashType: '+str(hashtype))
- enctype=int.from_bytes(inmemoryfile.read(0x1), byteorder='little', signed=True);print('-EncType: '+str(enctype))
- nulldata=inmemoryfile.read(0x3)
- magic=inmemoryfile.read(0x4);print('-Magic: '+str(magic))
- magicnumber=int.from_bytes(inmemoryfile.read(0x4), byteorder='little', signed=True);print('-MagicNumber: '+str(magicnumber))
- masterhashsize=int.from_bytes(inmemoryfile.read(0x4), byteorder='little', signed=True)*0x200;print('-MasterHashSize: '+str(masterhashsize))
- numberLevels=int.from_bytes(inmemoryfile.read(0x4), byteorder='little', signed=True);print('-Number: '+str(numberLevels))
- leveldata=list();c=24
- for i in range(numberLevels-1):
- lvoffs=int.from_bytes(inmemoryfile.read(0x8), byteorder='little', signed=True);print('-level'+str(i)+' offs: '+str(lvoffs))
- lvsize=int.from_bytes(inmemoryfile.read(0x8), byteorder='little', signed=True);print('-level'+str(i)+' size: '+str(lvsize))
- lvbsize=2**int.from_bytes(inmemoryfile.read(0x4), byteorder='little', signed=True);print('-level'+str(i)+' block size: '+str(lvbsize))
- treserved=int.from_bytes(inmemoryfile.read(0x4), byteorder='little', signed=True);print('-level'+str(i)+' Reserved: '+str(treserved))
- leveldata.append([i,lvoffs,lvsize,lvbsize])
- c=c+24
- inmemoryfile.read(32);c=c+32
- hash = hx((inmemoryfile.read(32))).decode('utf-8');print('-IVFC_Hash: '+str(hash))
- return leveldata,hash,masterhashsize,c
-
- def pr_ivfcsuperhash(self, file = None, mode = 'rb'):
- crypto1=self.header.getCryptoType()
- crypto2=self.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- masterKeyRev=crypto1
- else:
- masterKeyRev=crypto2
- else:
- masterKeyRev=crypto2
- decKey = Keys.decryptTitleKey(self.header.titleKeyDec, Keys.getMasterKeyIndex(masterKeyRev))
- for f in self.sectionFilesystems:
- #print(f.fsType);print(f.cryptoType)
- if f.fsType == Type.Fs.ROMFS and f.cryptoType == Type.Crypto.CTR:
- ncaHeader = NcaHeader()
- self.header.rewind()
- ncaHeader = self.header.read(0x400)
- #Hex.dump(ncaHeader)
- pfs0=f
- #Hex.dump(pfs0.read())
- sectionHeaderBlock = f.buffer
-
- levelOffset = int.from_bytes(sectionHeaderBlock[0x18:0x20], byteorder='little', signed=False)
- levelSize = int.from_bytes(sectionHeaderBlock[0x20:0x28], byteorder='little', signed=False)
-
- pfs0Header = pfs0.read(levelSize)
- if sectionHeaderBlock[8:12] == b'IVFC':
- data = pfs0Header;
- Hex.dump(pfs0Header)
- print(str('1: ')+hx(sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8'))
- print(str('2: ')+str(sha256(data).hexdigest()))
- superhash=str(sha256(data).hexdigest())
- return superhash
-
-
- def verify_hash_nca(self,buffer,origheader,didverify,feed):
- verdict=True; basename=str(os.path.basename(os.path.abspath(self._path)))
- if feed == False:
- feed=''
- message='***************';print(message);feed+=message+'\n'
- message=('HASH TEST');print(message);feed+=message+'\n'
- message='***************';print(message);feed+=message+'\n'
-
- message=(str(self.header.titleId)+' - '+str(self.header.contentType));print(message);feed+=message+'\n'
- ncasize=self.header.size
- t = tqdm(total=ncasize, unit='B', unit_scale=True, leave=False)
- i=0
- self.rewind();
- rawheader=self.read(0xC00)
- self.rewind()
- for data in iter(lambda: self.read(int(buffer)), ""):
- if i==0:
- sha=sha256()
- self.seek(0xC00)
- sha.update(rawheader)
- if origheader != False:
- sha0=sha256()
- sha0.update(origheader)
- i+=1
- t.update(len(data))
- self.flush()
- else:
- sha.update(data)
- if origheader != False:
- sha0.update(data)
- t.update(len(data))
- self.flush()
- if not data:
- break
- t.close()
- sha=sha.hexdigest()
- if origheader != False:
- sha0=sha0.hexdigest()
- message=(' - File name: '+basename);print(message);feed+=message+'\n'
- message=(' - SHA256: '+sha);print(message);feed+=message+'\n'
- if origheader != False:
- message=(' - ORIG_SHA256: '+sha0);print(message);feed+=message+'\n'
- if str(basename)[:16] == str(sha)[:16]:
- message=(' > FILE IS CORRECT');print(message);feed+=message+'\n'
- elif origheader != False:
- if str(basename)[:16] == str(sha0)[:16]:
- message=(' > FILE IS CORRECT');print(message);feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');print(message);feed+=message+'\n'
- verdict = False
- elif self.header.contentType == Type.Content.META and didverify == True:
- message=(' > RSV WAS CHANGED');print(message);feed+=message+'\n'
- #print(' > CHECKING INTERNAL HASHES')
- message=(' * FILE IS CORRECT');print(message);feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');print(message);feed+=message+'\n'
- verdict = False
- message=('');print(message);feed+=message+'\n'
- if verdict == False:
- message=("VERDICT: NCA FILE IS CORRUPT");print(message);feed+=message+'\n'
- if verdict == True:
- message=('VERDICT: NCA FILE IS CORRECT');print(message);feed+=message+'\n'
- return verdict,feed
diff --git a/py/Fs/ChromeNsp.py b/py/Fs/ChromeNsp.py
deleted file mode 100644
index c0878c67..00000000
--- a/py/Fs/ChromeNsp.py
+++ /dev/null
@@ -1,9195 +0,0 @@
-import aes128
-import Title
-import Titles
-import Hex
-from binascii import hexlify as hx, unhexlify as uhx
-from struct import pack as pk, unpack as upk
-from Fs.File import File
-from hashlib import sha256,sha1
-import Fs.Type
-from Fs import Type
-import os
-import re
-import pathlib
-
-import Keys
-import Config
-import Print
-import Nsps
-import sq_tools
-from tqdm import tqdm
-from Fs.Pfs0 import Pfs0
-from Fs.Ticket import Ticket
-from Fs.Nca import Nca
-from Fs.Nacp import Nacp
-from Fs.ChromeNacp import ChromeNacp
-from Fs.Nca import NcaHeader
-from Fs.File import MemoryFile
-from Fs.pyNCA3 import NCA3
-from Fs.pyNPDM import NPDM
-import math
-import sys
-import shutil
-import DBmodule
-if sys.platform == 'win32':
- import win32con, win32api
-from operator import itemgetter, attrgetter, methodcaller
-from Crypto.Cipher import AES
-import io
-import nutdb
-import textwrap
-from PIL import Image
-import zstandard
-from Crypto.Cipher import AES
-from Crypto.Util import Counter
-import time
-
-#from Cryptodome.Signature import pss
-#from Cryptodome.PublicKey import RSA
-#from Cryptodome import Random
-#from Cryptodome.Hash import SHA256 as newsha
-
-MEDIA_SIZE = 0x200
-indent = 1
-tabs = '\t' * indent
-htmlspace=' '
-
-def readInt64(f, byteorder='little', signed = False):
- return int.from_bytes(f.read(8), byteorder=byteorder, signed=signed)
-
-def readInt128(f, byteorder='little', signed = False):
- return int.from_bytes(f.read(16), byteorder=byteorder, signed=signed)
-
-class AESCTR:
- def __init__(self, key, nonce, offset = 0):
- self.key = key
- self.nonce = nonce
- self.seek(offset)
-
- def encrypt(self, data, ctr=None):
- if ctr is None:
- ctr = self.ctr
- return self.aes.encrypt(data)
-
- def decrypt(self, data, ctr=None):
- return self.encrypt(data, ctr)
-
- def seek(self, offset):
- self.ctr = Counter.new(64, prefix=self.nonce[0:8], initial_value=(offset >> 4))
- self.aes = AES.new(self.key, AES.MODE_CTR, counter=self.ctr)
-
-class Section:
- def __init__(self, f):
- self.f = f
- self.offset = readInt64(f)
- self.size = readInt64(f)
- self.cryptoType = readInt64(f)
- readInt64(f) # padding
- self.cryptoKey = f.read(16)
- self.cryptoCounter = f.read(16)
-
-class ChromeNsp(Pfs0):
-
- def __init__(self, path = None, mode = 'rb'):
- self.path = None
- self.titleId = None
- self.hasValidTicket = None
- self.timestamp = None
- self.version = None
-
- super(ChromeNsp, self).__init__(None, path, mode)
-
- if path:
- self.setPath(path)
- #if files:
- # self.pack(files)
-
- if self.titleId and self.isUnlockable():
- Print.info('unlockable title found ' + self.path)
- # self.unlock()
-
- def loadCsv(self, line, map = ['id', 'path', 'version', 'timestamp', 'hasValidTicket']):
- split = line.split('|')
- for i, value in enumerate(split):
- if i >= len(map):
- Print.info('invalid map index: ' + str(i) + ', ' + str(len(map)))
- continue
-
- i = str(map[i])
- methodName = 'set' + i[0].capitalize() + i[1:]
- method = getattr(self, methodName, lambda x: None)
- method(value.strip())
-
- def serialize(self, map = ['id', 'path', 'version', 'timestamp', 'hasValidTicket']):
- r = []
- for i in map:
-
- methodName = 'get' + i[0].capitalize() + i[1:]
- method = getattr(self, methodName, lambda: methodName)
- r.append(str(method()))
- return '|'.join(r)
-
- def __lt__(self, other):
- return str(self.path) < str(other.path)
-
- def __iter__(self):
- return self.files.__iter__()
-
- def title(self):
-
-
- if self.titleId in Titles.keys():
- return Titles.get(self.titleId)
-
- t = Title.Title()
- t.setId(self.titleId)
- Titles.data()[self.titleId] = t
- return t
-
- def getUpdateFile(self):
- title = self.title()
-
- if title.isUpdate or title.isDLC or not title.updateId:
- return None
-
- for i, nsp in Nsps.files.items():
- if nsp.titleId == title.updateId:
- return nsp
-
- return None
-
- def isUpdateAvailable(self):
- title = self.title()
-
- if self.titleId and title.version != None and self.version < title.version and str(title.version) != '0':
- return {'id': title.id, 'baseId': title.baseId, 'currentVersion': self.version, 'newVersion': title.version}
-
- if not title.isUpdate and not title.isDLC and Titles.contains(title.updateId):
- updateFile = self.getUpdateFile()
-
- if updateFile:
- return updateFile.isUpdateAvailable()
-
- updateTitle = Titles.get(title.updateId)
-
- if updateTitle.version and str(updateTitle.version) != '0':
- return {'id': updateTitle.id, 'baseId': title.baseId, 'currentVersion': None, 'newVersion': updateTitle.version}
-
- return None
-
- def readMeta(self):
- self.open()
- try:
- #a = self.application()
- #if a.header.titleId:
- # self.titleId = a.header.titleId
- # self.title().setRightsId(a.header.rightsId)
-
- t = self.ticket()
- rightsId = hx(t.getRightsId().to_bytes(0x10, byteorder='big')).decode('utf-8').upper()
- self.titleId = rightsId[0:16]
- self.title().setRightsId(rightsId)
- Print.debug('rightsId = ' + rightsId)
- Print.debug(self.titleId + ' key = ' + str(t.getTitleKeyBlock()))
- self.setHasValidTicket(t.getTitleKeyBlock() != 0)
- except BaseException as e:
- Print.info('readMeta filed ' + self.path + ", " + str(e))
- raise
- self.close()
-
- def unpack(self, path):
- os.makedirs(path, exist_ok=True)
-
- for nspF in self:
- filePath = os.path.abspath(path + '/' + nspF._path)
- f = open(filePath, 'wb')
- nspF.rewind()
- i = 0
-
- pageSize = 0x10000
-
- while True:
- buf = nspF.read(pageSize)
- if len(buf) == 0:
- break
- i += len(buf)
- f.write(buf)
- f.close()
- Print.info(filePath)
-
- def setHasValidTicket(self, value):
- if self.title().isUpdate:
- self.hasValidTicket = True
- return
-
- try:
- self.hasValidTicket = (True if value and int(value) != 0 else False) or self.title().isUpdate
- except:
- pass
-
- def getHasValidTicket(self):
- if self.title().isUpdate:
- return 1
- return (1 if self.hasValidTicket and self.hasValidTicket == True else 0)
-
- def setId(self, id):
- if re.match('[A-F0-9]{16}', id, re.I):
- self.titleId = id
-
- def getId(self):
- return self.titleId or ('0' * 16)
-
- def setTimestamp(self, timestamp):
- try:
- self.timestamp = int(str(timestamp), 10)
- except:
- pass
-
- def getTimestamp(self):
- return str(self.timestamp or '')
-
- def setVersion(self, version):
- if version and len(version) > 0:
- self.version = version
-
- def getVersion(self):
- return self.version or ''
-
- def setPath(self, path):
- self.path = path
- self.version = '0'
-
- z = re.match('.*\[([a-zA-Z0-9]{16})\].*', path, re.I)
- if z:
- self.titleId = z.groups()[0].upper()
- else:
- self.titleId = None
-
- z = re.match('.*\[v([0-9]+)\].*', path, re.I)
- if z:
- self.version = z.groups()[0]
-
- ext = pathlib.Path(path).suffix
- if ext == '.nsp':
- if self.hasValidTicket == None:
- self.setHasValidTicket(True)
- elif ext == '.nsx':
- if self.hasValidTicket == None:
- self.setHasValidTicket(False)
- else:
- return
-
- def getPath(self):
- return self.path or ''
-
- def open(self, path = None, mode = 'rb', cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
- super(ChromeNsp, self).open(path or self.path, mode, cryptoType, cryptoKey, cryptoCounter)
-
- def move(self):
- if not self.path:
- Print.error('no path set')
- return False
-
- if not self.fileName():
- Print.error('could not get filename for ' + self.path)
- return False
-
- if os.path.abspath(self.fileName()).lower() == os.path.abspath(self.path).lower():
- return False
- if os.path.isfile(self.fileName()) and os.path.abspath(self.path) == os.path.abspath(self.fileName()):
- Print.info('duplicate title: ')
- Print.info(os.path.abspath(self.path))
- Print.info(os.path.abspath(self.fileName()))
- return False
-
- try:
- Print.info(self.path + ' -> ' + self.fileName())
- os.makedirs(os.path.dirname(self.fileName()), exist_ok=True)
- newPath = self.fileName()
- os.rename(self.path, newPath)
- self.path = newPath
- except BaseException as e:
- Print.info('failed to rename file! %s -> %s : %s' % (self.path, self.fileName(), e))
-
- return True
-
- def cleanFilename(self, s):
- #s = re.sub('\s+\Demo\s*', ' ', s, re.I)
- s = re.sub('\s*\[DLC\]\s*', '', s, re.I)
- s = re.sub(r'[\/\\\:\*\?\"\<\>\|\.\s™©®()\~]+', ' ', s)
- return s.strip()
-
- def dict(self):
- return {"titleId": self.titleId, "hasValidTicket": self.hasValidTicket, 'version': self.version, 'timestamp': self.timestamp, 'path': self.path }
-
- def fileName(self):
- bt = None
- if not self.titleId in Titles.keys():
- if not Title.getBaseId(self.titleId) in Titles.keys():
- Print.info('could not find title key for ' + self.titleId + ' or ' + Title.getBaseId(self.titleId))
- return None
- bt = Titles.get(Title.getBaseId(self.titleId))
- t = Title()
- t.loadCsv(self.titleId + '0000000000000000|0000000000000000|' + bt.name)
- else:
- t = Titles.get(self.titleId)
-
- if not t.baseId in Titles.keys():
- Print.info('could not find baseId for ' + self.path)
- return None
- bt = Titles.get(t.baseId)
-
- if t.isDLC:
- format = Config.paths.getTitleDLC(not self.hasValidTicket)
- elif t.isDemo:
- if t.idExt != 0:
- format = Config.paths.getTitleDemoUpdate(not self.hasValidTicket)
- else:
- format = Config.paths.getTitleDemo(not self.hasValidTicket)
- elif t.idExt != 0:
- format = Config.paths.getTitleUpdate(not self.hasValidTicket)
- else:
- format = Config.paths.getTitleBase(not self.hasValidTicket)
-
- format = format.replace('{id}', self.cleanFilename(t.id))
- format = format.replace('{region}', self.cleanFilename(t.region or ''))
- format = format.replace('{name}', self.cleanFilename(t.name or ''))
- format = format.replace('{version}', str(self.version or 0))
- format = format.replace('{baseId}', self.cleanFilename(bt.id))
- format = format.replace('{baseName}', self.cleanFilename(bt.name or ''))
-
- '''
- if self.hasValidTicket:
- format = os.path.splitext(format)[0] + '.nsp'
- else:
- format = os.path.splitext(format)[0] + '.nsx'
- '''
-
- return format
-
- def ticket(self):
- for f in (f for f in self if type(f) == Ticket):
- return f
- raise IOError('no ticket in NSP')
-
- def cnmt(self):
- for f in (f for f in self if f._path.endswith('.cnmt.nca')):
- return f
- raise IOError('no cnmt in NSP')
-
- def xml(self):
- for f in (f for f in self if f._path.endswith('.xml')):
- return f
- raise IOError('no XML in NSP')
-
- def hasDeltas(self):
- return b'DeltaFragment' in self.xml().read()
-
- def application(self):
- for f in (f for f in self if f._path.endswith('.nca') and not f._path.endswith('.cnmt.nca')):
- return f
- raise IOError('no application in NSP')
-
- def isUnlockable(self):
- return (not self.hasValidTicket) and self.titleId and Titles.contains(self.titleId) and Titles.get(self.titleId).key
-
- def unlock(self):
- #if not self.isOpen():
- # self.open('r+b')
-
- if not Titles.contains(self.titleId):
- raise IOError('No title key found in database!')
-
- self.ticket().setTitleKeyBlock(int(Titles.get(self.titleId).key, 16))
- Print.info('setting title key to ' + Titles.get(self.titleId).key)
- self.ticket().flush()
- self.close()
- self.hasValidTicket = True
- self.move()
-
-
- # ...................................................
- # Patch requrements for network account
- # ...................................................
- def gen_ctrl_list(self):
- print('- Seeking control nca files...')
- ctrl_list=list()
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- ctrl_list.append(nca._path)
- else:
- pass
- return ctrl_list
-
- def patch_netlicense(self,item=False):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if item == False or nca._path == item:
- check=nca.patch_netlicense()
- return check
-
- def reb_lv_hashes(self,item=False):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if item == False or nca._path == item:
- print('-------------------------------------------------')
- print('Get Current IVFC level data:')
- print('-------------------------------------------------')
- leveldata,superhashoffset=nca.redo_lvhashes()
- return leveldata,superhashoffset
-
- def set_lv_hash(self,j,leveldata,item=False):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if item == False or nca._path == item:
- print('-------------------------------------------------')
- print('Rebuild hashes for IVFC level '+str(j)+':')
- print('-------------------------------------------------')
- nca.set_lv_hash(j,leveldata)
-
- def set_lvsuperhash(self,leveldata,superhashoffset,item=False):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if item == False or nca._path == item:
- print('-------------------------------------------------')
- print('Rebuild IVFC superhash:')
- print('-------------------------------------------------')
- nca.set_lvsuperhash(leveldata,superhashoffset)
-
- def ctrl_upd_hblock_hash(self,item=False):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if item == False or nca._path == item:
- print('-------------------------------------------------')
- print('Rebuild nca hash-table:')
- print('-------------------------------------------------')
- oldhash=nca.header.get_hblock_hash();print('- Old nca sblock hash: '+str(hx(oldhash)))
- newhash=nca.header.calculate_hblock_hash();print('- New nca sblock hash: '+str(hx(newhash)))
- nca.header.set_hblock_hash(newhash)
-
- # ...................................................
-
- def setMasterKeyRev(self, newMasterKeyRev):
- if not Titles.contains(self.titleId):
- raise IOError('No title key found in database! ' + self.titleId)
-
- ticket = self.ticket()
- masterKeyRev = ticket.getMasterKeyRevision()
- titleKey = ticket.getTitleKeyBlock()
- newTitleKey = Keys.changeTitleKeyMasterKey(titleKey.to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev), Keys.getMasterKeyIndex(newMasterKeyRev))
- rightsId = ticket.getRightsId()
-
- if rightsId != 0:
- raise IOError('please remove titlerights first')
-
- if (newMasterKeyRev == None and rightsId == 0) or masterKeyRev == newMasterKeyRev:
- Print.info('Nothing to do')
- return
-
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKey =\t' + str(hx(titleKey.to_bytes(16, byteorder='big'))))
- Print.info('newTitleKey =\t' + str(hx(newTitleKey)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
-
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(nca._path)} - {nca.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
-
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.getCryptoType2() == 0:
- if nca.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
-
- ticket.setMasterKeyRevision(newMasterKeyRev)
- ticket.setRightsId((ticket.getRightsId() & 0xFFFFFFFFFFFFFFFF0000000000000000) + newMasterKeyRev)
- ticket.setTitleKeyBlock(int.from_bytes(newTitleKey, 'big'))
-
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getCryptoType2() != newMasterKeyRev:
- Print.info('writing masterKeyRev for %s, %d -> %s' % (str(nca._path), nca.header.getCryptoType2(), str(newMasterKeyRev)))
-
- encKeyBlock = nca.header.getKeyBlock()
-
- if sum(encKeyBlock) != 0:
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)
- Print.info('decrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- crypto = aes128.AESECB(key)
- decKeyBlock = crypto.decrypt(encKeyBlock)
-
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex)
- Print.info('encrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex))
- crypto = aes128.AESECB(key)
-
- reEncKeyBlock = crypto.encrypt(decKeyBlock)
- nca.header.setKeyBlock(reEncKeyBlock)
-
-
- if newMasterKeyRev >= 3:
- nca.header.setCryptoType(2)
- nca.header.setCryptoType2(newMasterKeyRev)
- if newMasterKeyRev == 2:
- nca.header.setCryptoType(2)
- nca.header.setCryptoType2(0)
- else:
- nca.header.setCryptoType(newMasterKeyRev)
- nca.header.setCryptoType2(0)
-
- def nsptype(self):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'Patch':
- return 'UPDATE'
- if content_type_cnmt == 'AddOnContent':
- return 'DLC'
- if content_type_cnmt == 'Application':
- return 'BASE'
-
- def removeTitleRights(self):
- if not Titles.contains(self.titleId):
- raise IOError('No title key found in database! ' + self.titleId)
-
- ticket = self.ticket()
- masterKeyRev = ticket.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = ticket.getRightsId()
-
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
-
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(nca._path)} - {nca.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
-
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.getCryptoType2() == 0:
- if nca.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
-
- ticket.setRightsId(0)
-
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() == 0:
- continue
-
- Print.info('writing masterKeyRev for %s, %d' % (str(nca._path), masterKeyRev))
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
-
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- nca.header.setRightsId(0)
- nca.header.setKeyBlock(encKeyBlock)
- Hex.dump(encKeyBlock)
-
-#Extract all files
- def extract_all(self,ofolder,buffer):
- indent = 1
- tabs = '\t' * indent
- print("Processing: "+str(self._path))
- for file in self:
- file.rewind()
- filename = str(file._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- file.rewind()
- t = tqdm(total=file.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: file.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
-
- def copy_ticket(self,ofolder):
- for ticket in self:
- if type(ticket) == Ticket:
- ticket.rewind()
- data = ticket.read()
- filename = str(ticket._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(str(filepath), 'w+b')
- fp.write(data)
- fp.flush()
- fp.close()
-
- def copy_nca_control(self,ofolder,buffer):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- fp.close()
- break
-
- def copy_nca_meta(self,ofolder,buffer):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- fp.close()
- break
-
- def copy_pfs0_meta(self,ofolder,buffer):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- nca.rewind()
- f.rewind()
- filename = 'PFS0'
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- f.rewind()
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- fp.close()
- break
-
- def copy_cnmt(self,ofolder,buffer):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for file in f:
- nca.rewind()
- f.rewind()
- file.rewind()
- filename = str(file._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- f.rewind()
- file.rewind()
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- fp.close()
- break
-
- def copy_nacp(self,ofolder,buffer=32768):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- ncaname = str(nca._path)[:-4]+'_nca'
- ncafolder = os.path.join(ofolder,ncaname)
- filename = 'control.nacp'
- filepath = os.path.join(ncafolder,filename)
- if not os.path.exists(ncafolder):
- os.makedirs(ncafolder)
- offset=nca.get_nacp_offset()
- for f in nca:
- totSize=f.size-offset
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write('- Writing control.nacp...')
- f.seek(offset)
- fp = open(filepath, 'w+b')
- for data in iter(lambda: f.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- t.update(len(data))
- if not data:
- fp.close()
- t.close()
- break
- break
- print(' DONE')
-
- def copy_as_plaintext(self,ofolder,files_list,buffer=32768):
- for nca in self:
- tk=None;skip=False
- if type(nca) == Nca:
- for fs in nca.sectionFilesystems:
- if fs.cryptoType == Type.Crypto.BKTR:
- skip=True
- break
- if nca.header.getRightsId() != 0:
- correct, tkey = self.verify_nca_key(str(nca._path))
- if correct == True:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- tk = Keys.decryptTitleKey(tkey, Keys.getMasterKeyIndex(int(masterKeyRev)))
- else:
- tk=nca.header.titleKeyDec
- if skip == False:
- ncaname = str(nca._path)
- PN = os.path.join(ofolder,ncaname)
- if not os.path.exists(ofolder):
- os.makedirs(ofolder)
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- #print(offset)
- break
- #print(nca.size)
- #print(str(nca._path)[-9:])
- lon=0;test=str(nca._path)[-9:]
- if test=='.cnmt.nca':
- ext='.plain.cnmt.nca'
- else:
- ext='.plain.nca'
- lon=(-1)*len(ext)
- try:
- fp=open(str(self._path), 'rb')
- nca3=NCA3(fp,int(offset),str(nca._path),tk)
- nca3.decrypt_to_plaintext(PN.replace(str(nca._path)[lon:], ext))
- fp.close();
- except BaseException as e:
- #Print.error('Exception: ' + str(e))
- if nca.size'
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- #print(offset)
- break
- decKey=nca.header.titleKeyDec
- try:
- fp=open(str(self._path), 'rb')
- nca3=NCA3(fp,int(offset),str(nca._path),decKey)
- feed+=nca3.print_npdm()
- fp.close();
- feed+=' '
- return feed
- except BaseException as e:
- #Print.error('Exception: ' + str(e))
- nca.rewind()
- for f in nca:
- for g in f:
- if str(g._path)=='main.npdm':
- inmemoryfile = io.BytesIO(g.read())
- npdm = NPDM(inmemoryfile)
- n=npdm.__str__()
- feed+=n
- feed+=''
- return feed
- if type(nca) == Fs.Nca and nca.header.getRightsId() != 0:
- if str(nca.header.contentType) == 'Content.PROGRAM':
- titleid2=nca.header.titleId
- feed=self.html_feed(feed,1,message=str('TITLEID: ' + str(titleid2).upper()))
- feed+=''
- correct, tkey = self.verify_nca_key(str(nca._path))
- if correct == True:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- decKey = Keys.decryptTitleKey(tkey, Keys.getMasterKeyIndex(int(masterKeyRev)))
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- #print(offset)
- break
- try:
- fp=open(str(self._path), 'rb')
- nca3=NCA3(fp,int(offset),str(nca._path),decKey)
- feed+=nca3.print_npdm()
- fp.close();
- feed+='
'
- return feed
- except BaseException as e:
- #Print.error('Exception: ' + str(e))
- nca.rewind()
- for fs in nca.sectionFilesystems:
- #print(fs.fsType)
- #print(fs.cryptoType)
- if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR:
- nca.seek(0)
- ncaHeader = NcaHeader()
- ncaHeader.open(MemoryFile(nca.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key'))))
- ncaHeader.seek(0)
- fs.rewind()
- pfs0=fs
- sectionHeaderBlock = fs.buffer
- nca.seek(fs.offset)
- pfs0Offset=fs.offset
- pfs0Header = nca.read(0x10*30)
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- head=data[0:4]
- n_files=(data[4:8])
- n_files=int.from_bytes(n_files, byteorder='little')
- st_size=(data[8:12])
- st_size=int.from_bytes(st_size, byteorder='little')
- junk=(data[12:16])
- offset=(0x10 + n_files * 0x18)
- stringTable=(data[offset:offset+st_size])
- stringEndOffset = st_size
- headerSize = 0x10 + 0x18 * n_files + st_size
- #print(head)
- if head!=b'PFS0':
- feed=self.html_feed(feed,2,message=str('- Error decrypting npdm'))
- continue
- #print(str(n_files))
- #print(str(st_size))
- #print(str((stringTable)))
- files_list=list()
- for i in range(n_files):
- i = n_files - i - 1
- pos=0x10 + i * 0x18
- offset = data[pos:pos+8]
- offset=int.from_bytes(offset, byteorder='little')
- size = data[pos+8:pos+16]
- size=int.from_bytes(size, byteorder='little')
- nameOffset = data[pos+16:pos+20] # just the offset
- nameOffset=int.from_bytes(nameOffset, byteorder='little')
- name = stringTable[nameOffset:stringEndOffset].decode('utf-8').rstrip(' \t\r\n\0')
- stringEndOffset = nameOffset
- junk2 = data[pos+20:pos+24] # junk data
- #print(name)
- #print(offset)
- #print(size)
- files_list.append([name,offset,size])
- files_list.reverse()
- #print(files_list)
- for i in range(len(files_list)):
- if files_list[i][0] == 'main.npdm':
- off1=files_list[i][1]+pfs0Offset+headerSize
- nca.seek(off1)
- np=nca.read(files_list[i][2])
- mem = MemoryFile(np, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = off1)
- data = mem.read();
- #Hex.dump(data)
- inmemoryfile = io.BytesIO(data)
- npdm = NPDM(inmemoryfile)
- n=npdm.__str__()
- feed+=n
- feed+=''
- return feed
- break
- return feed
- except:
- feed=self.html_feed(feed,2,message=str('- Error decrypting npdm'))
- return feed
-
- def read_buildid(self,target=None):
- iscorrect=False;
- ModuleId='';BuildID8='';BuildID16=''
- files_list=sq_tools.ret_nsp_offsets(self._path)
- # print(files_list)
- for nca in self:
- if type(nca) == Fs.Nca:
- if str(nca.header.contentType) == 'Content.PROGRAM':
- if target==None:
- target=str(nca._path)
- if str(nca._path)==target:
- if nca.header.getRightsId() == 0:
- decKey=nca.header.titleKeyDec
- if nca.header.getRightsId() != 0:
- correct, tkey = self.verify_nca_key(str(nca._path))
- if correct == True:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- decKey = Keys.decryptTitleKey(tkey, Keys.getMasterKeyIndex(int(masterKeyRev)))
- else:
- decKey=nca.header.titleKeyDec
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- # print(offset)
- break
- nca.rewind()
- for fs in nca.sectionFilesystems:
- #print(fs.fsType)
- #print(fs.cryptoType)
- if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR:
- nca.seek(0)
- ncaHeader = NcaHeader()
- ncaHeader.open(MemoryFile(nca.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key'))))
- ncaHeader.seek(0)
- fs.rewind()
- pfs0=fs
- sectionHeaderBlock = fs.buffer
- nca.seek(fs.offset)
- pfs0Offset=fs.offset
- pfs0Header = nca.read(0x10*30)
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- head=data[0:4]
- n_files=(data[4:8])
- n_files=int.from_bytes(n_files, byteorder='little')
- st_size=(data[8:12])
- st_size=int.from_bytes(st_size, byteorder='little')
- junk=(data[12:16])
- offset=(0x10 + n_files * 0x18)
- stringTable=(data[offset:offset+st_size])
- stringEndOffset = st_size
- headerSize = 0x10 + 0x18 * n_files + st_size
- #print(head)
- if head!=b'PFS0':
- continue
- #print(str(n_files))
- #print(str(st_size))
- #print(str((stringTable)))
- files_list=list()
- for i in range(n_files):
- i = n_files - i - 1
- pos=0x10 + i * 0x18
- offset = data[pos:pos+8]
- offset=int.from_bytes(offset, byteorder='little')
- size = data[pos+8:pos+16]
- size=int.from_bytes(size, byteorder='little')
- nameOffset = data[pos+16:pos+20] # just the offset
- nameOffset=int.from_bytes(nameOffset, byteorder='little')
- name = stringTable[nameOffset:stringEndOffset].decode('utf-8').rstrip(' \t\r\n\0')
- stringEndOffset = nameOffset
- junk2 = data[pos+20:pos+24] # junk data
- #print(name)
- #print(offset)
- #print(size)
- files_list.append([name,offset,size])
- files_list.reverse()
- #print(files_list)
- for i in range(len(files_list)):
- if files_list[i][0] == 'main':
- off1=files_list[i][1]+pfs0Offset+headerSize
- nca.seek(off1)
- np=nca.read(0x60)
- mem = MemoryFile(np, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = off1)
- magic=mem.read(0x4)
- if magic==b'NSO0':
- mem.seek(0x40)
- data = mem.read(0x20);
- ModuleId=(str(hx(data)).upper())[2:-1]
- BuildID8=(str(hx(data[:8])).upper())[2:-1]
- BuildID16=(str(hx(data[:16])).upper())[2:-1]
- iscorrect=True;
- break
- break
- if iscorrect==False:
- try:
- from nutFS.Nca import Nca as nca3type
- for nca in self:
- if type(nca) == Fs.Nca:
- if str(nca.header.contentType) == 'Content.PROGRAM':
- if target==None or str(nca._path)==target:
- nca3type=Nca(nca)
- nca3type._path=nca._path
- ModuleId=str(nca3type.buildId)
- BuildID8=ModuleId[:8]
- BuildID16=ModuleId[:16]
- except:
- ModuleId='';BuildID8='';BuildID16='';
- return ModuleId,BuildID8,BuildID16
-
-
- def extract_nca(self,ofolder,files_list,buffer=32768):
- for nca in self:
- tk=None;skip=False
- if type(nca) == Nca:
- for fs in nca.sectionFilesystems:
- if fs.cryptoType == Type.Crypto.BKTR:
- skip=True
- break
- if nca.header.getRightsId() != 0:
- correct, tkey = self.verify_nca_key(str(nca._path))
- if correct == True:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- tk = Keys.decryptTitleKey(tkey, Keys.getMasterKeyIndex(int(masterKeyRev)))
- else:
- tk=nca.header.titleKeyDec
- if skip == False:
- if type(nca) == Nca:
- ncaname = str(nca._path)[:-4]+'_nca'
- ncafolder = os.path.join(ofolder,ncaname)
- ncaname2 = str(nca._path)
- PN = os.path.join(ofolder,ncaname2)
- if not os.path.exists(ncafolder):
- os.makedirs(ncafolder)
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- break
- #t = tqdm(total=nca.size, unit='B', unit_scale=True, leave=False)
- try:
- fp=open(str(self._path), 'rb')
- nca3=NCA3(fp,int(offset),str(nca._path),tk,buffer)
- nca3.extract_conts(ncafolder, disp=True)
- fp.close()
- except:
- #Print.error('Exception: ' + str(e))
- if nca.size crypto2:
- masterKeyRev=nca.header.getCryptoType()
- else:
- masterKeyRev=nca.header.getCryptoType2()
- else:
- masterKeyRev=nca.header.getCryptoType2()
- if nca.header.getRightsId() != 0:
- break
-
-
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = ticket.getRightsId()
- titleKey = ticket.getTitleKeyBlock().to_bytes(16, byteorder='big')
-
- return titleKeyDec
-
-
- def pack(self, files,buffer,fat,fx):
- if not self.path:
- return False
- indent = 1
- tabs = '\t' * indent
-
- hd = self.generateHeader(files)
-
- totSize = len(hd) + sum(os.path.getsize(file) for file in files)
- if os.path.exists(self.path) and os.path.getsize(self.path) == totSize:
- Print.info('\t\tRepack %s is already complete!' % self.path)
- return
- outfile=self.path
- if totSize <= 4294901760:
- fat="exfat"
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294901760)
- index=0
- outfile=outfile[:-1]+str(index)
- if fx=="folder" and fat=="fat32":
- output_folder ="archfolder"
- dir_path = os.path.dirname(os.path.realpath(self.path))
- output_folder = os.path.join(dir_path, output_folder)
- outfile = os.path.join(output_folder, "00")
- if not os.path.exists(output_folder):
- os.makedirs(output_folder)
- c=0
- Print.info('Generating nsp:')
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing header...')
-
- outf = open(outfile, 'wb')
- outf.write(hd)
- t.update(len(hd))
- c=c+len(hd)
- block=4294901760
- for file in files:
- if file.endswith('.nca'):
- nca =Fs.Nca(file, 'r+b')
- nca.rewind()
- if nca.header.getgamecard() == 1:
- nca.header.setgamecard(0)
- nca.flush()
- nca.close()
- t.write(tabs+'- Appending %s' % os.path.basename(file))
- with open(file, 'rb') as inf:
- while True:
- data = inf.read(int(buffer))
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- dat2=inf.read(int(n2))
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- if not data:
- break
- t.close()
- outf.close()
-
- def generateHeader(self, files):
- filesNb = len(files)
- stringTable = '\x00'.join(os.path.basename(file) for file in files)
- headerSize = 0x10 + (filesNb)*0x18 + len(stringTable)
- remainder = 0x10 - headerSize%0x10
- headerSize += remainder
-
- fileSizes = [os.path.getsize(file) for file in files]
- fileOffsets = [sum(fileSizes[:n]) for n in range(filesNb)]
-
- fileNamesLengths = [len(os.path.basename(file))+1 for file in files] # +1 for the \x00
- stringTableOffsets = [sum(fileNamesLengths[:n]) for n in range(filesNb)]
-
- header = b''
- header += b'PFS0'
- header += pk('{} '.format(message)
- return feed
- if style==2:
- feed+='{}
'.format(message)
- return feed
- if style==3:
- feed+='{} {} '.format(message[0],message[1])
- return feed
- if style==4:
- feed+='{} {}. {} {} '.format(message[0],message[1],message[2],message[3])
- return feed
- if style==5:
- feed+='{}
'.format(message)
- return feed
- if style==6:
- feed+='{}
'.format(message)
- return feed
- if style==7:
- feed+='{} {}
'.format(message[0],message[1])
- if style==8:
- feed+='[{}] {} {}. {} {} '.format(message[3],message[0],message[1],message[4],message[5])
- return feed
-
-#ADVANCED FILE-LIST
- def adv_file_list(self):
- contentlist=list()
- feed='';ncadb={};size_pr=0
- for nca in self:
- size1=0;size2=0;size3=0
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- target=str(nca._path)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'Patch':
- content_type='Update'
- reqtag='RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo Update'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay Update'
- if content_type_cnmt == 'AddOnContent':
- content_type='DLC'
- reqtag='RequiredUpdateNumber: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if content_type_cnmt == 'Application':
- content_type='Game or Application'
- reqtag='RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay'
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- original_ID2 = str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID2 = original_ID2[2:-1]
- version=str(int.from_bytes(titleversion, byteorder='little'))
- v_number=int(int(version)/65536)
- RS_number=int(min_sversion/65536)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- programSDKversion,dataSDKversion=self.getsdkvertit(titleid2)
- sdkversion=nca.get_sdkversion()
- MinRSV=sq_tools.getMinRSV(keygen,min_sversion)
- FW_rq=sq_tools.getFWRangeKG(keygen)
- RSV_rq=sq_tools.getFWRangeRSV(min_sversion)
- RSV_rq_min=sq_tools.getFWRangeRSV(MinRSV)
- feed=self.html_feed(feed,1,message=str('TITLEID: ' + str(titleid2).upper()))
- feed=self.html_feed(feed,2,message=str("- Titleinfo:"))
- feed+=''
- if content_type_cnmt != 'AddOnContent':
- message=["Name:",tit_name];feed=self.html_feed(feed,3,message)
- message=["Editor:",editor];feed=self.html_feed(feed,3,message)
- message=["Display Version:",str(ediver)];feed=self.html_feed(feed,3,message)
- message=["Meta SDK version:",sdkversion];feed=self.html_feed(feed,3,message)
- message=["Program SDK version:",programSDKversion];feed=self.html_feed(feed,3,message)
- suplangue=str((', '.join(SupLg)))
- message=["Supported Languages:",suplangue];feed=self.html_feed(feed,3,message)
- message=["Content type:",content_type];feed=self.html_feed(feed,3,message)
- v_number=str(v_number)
- data='{} -> {} ({})'.format(version,content_type_cnmt,v_number)
- message=["Version:",data];feed=self.html_feed(feed,3,message=message);
- if content_type_cnmt == 'AddOnContent':
- if tit_name != "DLC":
- message=["Name:",tit_name];feed=self.html_feed(feed,3,message)
- message=["Editor:",editor];feed=self.html_feed(feed,3,message)
- message=["Content type:","DLC"];feed=self.html_feed(feed,3,message)
- DLCnumb=str(titleid2)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- message=["DLC number:",str(str(DLCnumb)+' -> '+"AddOnContent"+' ('+str(DLCnumb)+')')];feed=self.html_feed(feed,3,message)
- message=["DLC version Number:",str( version+' -> '+"Version"+' ('+str(v_number)+')')];feed=self.html_feed(feed,3,message)
- message=["Meta SDK version:",sdkversion];feed=self.html_feed(feed,3,message)
- message=["Data SDK version:",dataSDKversion];feed=self.html_feed(feed,3,message)
- if SupLg !='':
- suplangue=str((', '.join(SupLg)))
- message=["Supported Languages:",suplangue];feed=self.html_feed(feed,3,message)
- feed+=' '
- feed=self.html_feed(feed,2,message=str("- Required Firmware:"))
- incl_Firm=DBmodule.FWDB.detect_xci_fw(self._path,False)
- feed+=''
- message=["Included Firmware:",str(str(incl_Firm))];feed=self.html_feed(feed,3,message)
- if content_type_cnmt == 'AddOnContent':
- if v_number == 0:
- message=["Required game version:",str(str(min_sversion)+' -> '+"Application"+' ('+str(RS_number)+')')];feed=self.html_feed(feed,3,message)
- message=["Required game version:",str(str(min_sversion)+' -> '+"Application"+' ('+str(RS_number)+')')];feed=self.html_feed(feed,3,message)
- if v_number > 0:
- message=["Required game version:",str(str(min_sversion)+' -> '+"Patch"+' ('+str(RS_number)+')')];feed=self.html_feed(feed,3,message)
-
- else:
- message=[reqtag,(str(min_sversion)+" -> " +RSV_rq)];feed=self.html_feed(feed,3,message)
- message=['Encryption (keygeneration):',(str(keygen)+" -> " +FW_rq)];feed=self.html_feed(feed,3,message)
- if content_type_cnmt != 'AddOnContent':
- message=['Patchable to:',(str(MinRSV)+" -> " + RSV_rq_min)];feed=self.html_feed(feed,3,message)
-
- else:
- message=['Patchable to:',('DLC -> no RSV to patch')];feed=self.html_feed(feed,3,message)
- feed+=' '
- ncalist = list()
- ncasize = 0
- feed=self.html_feed(feed,2,message=str("- Nca files (Non Deltas):"))
- feed+=''
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- ncatype = int.from_bytes(ncatype, byteorder='little')
- IdOffset = cnmt.read(0x1)
- IdOffset = int.from_bytes(IdOffset, byteorder='little', signed=True)
- #Print.info(str(ncatype))
- if ncatype != 6:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if titleid2.endswith('800'):
- showID=str(original_ID2).upper()
- else:
- showID=str(titleid2).upper()
- if IdOffset>0:
- showID=showID[:-1]+str(IdOffset)
- ncadb[nca_name]=[showID,version,v_number]
- showID=showID+' v'+version
- s1=0;s1,feed=self.print_nca_by_title(nca_name,ncatype,showID,feed)
- ncasize=ncasize+s1
- ncalist.append(nca_name[:-4])
- contentlist.append(nca_name)
- if ncatype == 6:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- ncalist.append(nca_name[:-4])
- contentlist.append(nca_name)
- nca_meta=str(nca._path)
- ncalist.append(nca_meta[:-4])
- contentlist.append(nca_meta)
- showID=str(titleid2).upper()
- ncadb[nca_name]=[showID,version,v_number]
- showID=showID+' v'+version
- s1=0;s1,feed=self.print_nca_by_title(nca_meta,0,showID,feed)
- ncasize=ncasize+s1
- size1=ncasize
- size_pr=sq_tools.getSize(ncasize)
- feed+=' '
- feed=self.html_feed(feed,5,message=('TOTAL SIZE: '+size_pr))
- if self.actually_has_deltas(ncalist)=="true":
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- ncatype = int.from_bytes(ncatype, byteorder='little')
- unknown = cnmt.read(0x1)
- if ncatype == 6:
- feed=self.html_feed(feed,2,message=('- Nca files (Deltas):'))
- feed+=''
- break
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- ncasize = 0
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- ncatype = int.from_bytes(ncatype, byteorder='little')
- IdOffset = cnmt.read(0x1)
- IdOffset = int.from_bytes(IdOffset, byteorder='little', signed=True)
- if ncatype == 6:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if titleid2.endswith('800'):
- showID=str(original_ID2).upper()
- else:
- showID=str(titleid2).upper()
- if IdOffset>0:
- showID=showID[:-1]+str(IdOffset)
- ncadb[nca_name]=[showID,version,v_number]
- showID=showID+' v'+version
- s1=0;s1,feed=self.print_nca_by_title(nca_name,ncatype,showID,feed)
- ncasize=ncasize+s1
- size2=ncasize
- size_pr=sq_tools.getSize(ncasize)
- feed+=' '
- feed=self.html_feed(feed,5,message=('TOTAL SIZE: '+size_pr))
- if self.actually_has_other(titleid2,ncalist)=="true":
- feed=self.html_feed(feed,2,message=('- Other types of files:'))
- feed+=''
- othersize=0;os1=0;os2=0;os3=0
- os1,feed=self.print_xml_by_title(ncalist,contentlist,feed)
- os2,feed=self.print_tac_by_title(titleid2,contentlist,feed)
- os3,feed=self.print_jpg_by_title(ncalist,contentlist,feed)
- othersize=othersize+os1+os2+os3
- size3=othersize
- size_pr=sq_tools.getSize(othersize)
- feed+=' '
- feed=self.html_feed(feed,5,message=('TOTAL SIZE: '+size_pr))
- finalsize=size1+size2+size3
- size_pr=sq_tools.getSize(finalsize)
- feed=self.html_feed(feed,2,message=('FULL CONTENT TOTAL SIZE: '+size_pr))
- feed=self.printnonlisted(contentlist,feed)
- feed=self.print_BuildIDs(ncadb,feed)
- return feed
-
-
- def print_nca_by_title(self,nca_name,ncatype,showID,feed):
- tab="\t";
- size=0
- ncz_name=nca_name[:-1]+'z'
- for nca in self:
- filename = str(nca._path)
- if type(nca) == Nca:
- if filename == nca_name:
- size=nca.header.size
- size_pr=sq_tools.getSize(size)
- content=str(nca.header.contentType)
- content=content[8:]+": "
- ncatype=sq_tools.getTypeFromCNMT(ncatype)
- if ncatype != "Meta: ":
- message=[ncatype,str(filename),'TitleID:',showID,'Size',size_pr];feed=self.html_feed(feed,8,message)
- else:
- message=[ncatype,str(filename),'TitleID:',showID,'Size',size_pr];feed=self.html_feed(feed,8,message)
- return size,feed
- elif filename == ncz_name:
- ncztype=Nca(nca)
- ncztype._path=nca._path
- size=ncztype.header.size
- size_pr=sq_tools.getSize(size)
- content=str(ncztype.header.contentType)
- content=content[8:]+": "
- ncatype=sq_tools.getTypeFromCNMT(ncatype)
- if ncatype != "Meta: ":
- message=[ncatype,str(filename),'TitleID:',showID,'Size',size_pr];feed=self.html_feed(feed,8,message)
- else:
- message=[ncatype,str(filename),'TitleID:',showID,'Size',size_pr];feed=self.html_feed(feed,8,message)
- return size,feed
- return size,feed
- def print_xml_by_title(self,ncalist,contentlist,feed=''):
- tab="\t";
- size2return=0
- for file in self:
- if file._path.endswith('.xml'):
- size=file.size
- size_pr=sq_tools.getSize(size)
- filename = str(file._path)
- xml=filename[:-4]
- if xml in ncalist:
- message=['XML:',str(filename),'Size:',size_pr];feed=self.html_feed(feed,4,message)
- contentlist.append(filename)
- size2return=size+size2return
- return size2return,feed
- def print_tac_by_title(self,titleid,contentlist,feed=''):
- tab="\t";
- size2return=0
- for ticket in self:
- if type(ticket) == Ticket:
- size=ticket.size
- size_pr=sq_tools.getSize(size)
- filename = str(ticket._path)
- tik=filename[:-20]
- if tik == titleid:
- message=['Ticket:',str(filename),'Size:',size_pr];feed=self.html_feed(feed,4,message)
- contentlist.append(filename)
- size2return=size+size2return
- for cert in self:
- if cert._path.endswith('.cert'):
- size=cert.size
- size_pr=sq_tools.getSize(size)
- filename = str(cert._path)
- cert_id =filename[:-21]
- if cert_id == titleid:
- message=['Cert:',str(filename),'Size:',size_pr];feed=self.html_feed(feed,4,message)
- contentlist.append(filename)
- size2return=size+size2return
- return size2return,feed
- def print_jpg_by_title(self,ncalist,contentlist,feed=''):
- size2return=0
- tab="\t";
- for file in self:
- if file._path.endswith('.jpg'):
- size=file.size
- size_pr=sq_tools.getSize(size)
- filename = str(file._path)
- jpg=filename[:32]
- if jpg in ncalist:
- message=['JPG:',str(filename),'Size:',size_pr];feed=self.html_feed(feed,4,message)
- contentlist.append(filename)
- size2return=size+size2return
- return size2return,feed
- def actually_has_deltas(self,ncalist):
- vfragment="false"
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.DATA':
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- if nca._path[:-4] in ncalist:
- vfragment="true"
- break
- return vfragment
- def actually_has_other(self,titleid,ncalist):
- vother="false"
- for file in self:
- if file._path.endswith('.xml'):
- filename = str(file._path)
- xml=filename[:-4]
- if xml in ncalist:
- vother="true"
- break
- if type(file) == Ticket:
- filename = str(file._path)
- tik=filename[:-20]
- if tik == titleid:
- vother="true"
- break
- if file._path.endswith('.cert'):
- filename = str(file._path)
- cert_id =filename[:-21]
- if cert_id == titleid:
- vother="true"
- break
- if file._path.endswith('.jpg'):
- filename = str(file._path)
- jpg=filename[:32]
- if jpg in ncalist:
- vother="true"
- break
- return vother
- def printnonlisted(self,contentlist,feed=''):
- tab="\t";
- list_nonlisted="false"
- for file in self:
- filename = str(file._path)
- if not filename in contentlist:
- list_nonlisted="true"
- if list_nonlisted == "true":
- feed=self.html_feed(feed,2,'- Files not linked to content in nsp:')
- totsnl=0
- for file in self:
- filename = str(file._path)
- nczname= str(file._path)[:-1]+'z'
- if not filename in contentlist and not nczname in contentlist:
- totsnl=totsnl+file.size
- size_pr=sq_tools.getSize(file.size)
- message=['OTHER:',str(filename),'Size:',size_pr];feed=self.html_feed(feed,4,message)
- size_pr=sq_tools.getSize(totsnl)
- feed=self.html_feed(feed,5,message=('TOTAL SIZE: '+size_pr))
- return feed
-
- def print_BuildIDs(self,ncadb,feed=''):
- c=0
- for nca in self:
- size1=0;size2=0;size3=0
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.PROGRAM':
- target=str(nca._path)
- tit=str(nca.header.titleId).upper()
- entry=ncadb[target]
- ModuleId,BuildID8,BuildID16=self.read_buildid(target)
- ModuleId=sq_tools.trimm_module_id(ModuleId)
- if ModuleId!="":
- if c==0:
- feed=self.html_feed(feed,2,'EXEFS DATA:')
- c+=1
- feed=self.html_feed(feed,2,f'[Title: {tit} v{entry[1]}]')
- message=[f'BuildID8:',str(BuildID8)];feed=self.html_feed(feed,3,message)
- message=[f'BuildID:',str(ModuleId)];feed=self.html_feed(feed,3,message)
- return feed
-
-#ADVANCED FILE-LIST
- def adv_content_list(self):
- feed=''
- applist=list(); applist_ID=list()
- patchlist=list(); patchlist_ID=list()
- dlclist=list(); dlclist_ID=list()
- for nca in self:
- size1=0;size2=0;size3=0
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid = titleid[2:-1]
- titleversion = cnmt.read(0x4)
- version=str(int.from_bytes(titleversion, byteorder='little'))
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- original_ID = str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- target=str(nca._path)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'Application':
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- applist.append([target,titleid,version,tit_name,editor])
- if content_type_cnmt == 'Patch':
- patchlist.append([target,original_ID,version,titleid])
- patchlist_ID.append(target)
- if content_type_cnmt == 'AddOnContent':
- DLCnumb=str(titleid)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- dlclist.append([target,original_ID,version,titleid,DLCnumb])
-
- applist=sorted(applist, key=itemgetter(1))
- patchlist=sorted(patchlist, key=itemgetter(1))
- dlclist=sorted(dlclist, key=itemgetter(4))
- patch_called=list()
- dlc_called=list()
- if len(applist) != 0:
- for i in range(len(applist)):
- tid=applist[i][1]
- message='------------------------------------------------';print(message);feed+=message+'\n'
- message='BASE CONTENT ID: ' + str(tid);print(message);feed+=message+'\n'
- message='------------------------------------------------';print(message);feed+=message+'\n'
- message='Name: '+applist[i][3];print(message);feed+=message+'\n'
- message='Editor: '+applist[i][4];print(message);feed+=message+'\n'
- message='------------------------------------------------';print(message);feed+=message+'\n'
- message=applist[i][1]+" [BASE]"+" v"+applist[i][2];print(message);feed+=message+'\n'
- cupd=0
- for j in range(len(patchlist)):
- if tid == patchlist[j][1]:
- v=patchlist[j][2]
- v_number=str(int(int(v)/65536))
- message=patchlist[j][3]+" [UPD]"+" v"+patchlist[j][2]+" -> Patch("+v_number+")";print(message);feed+=message+'\n'
- cupd+=1
- patch_called.append(patchlist[j])
- cdlc=0
- for k in range(len(dlclist)):
- if tid == dlclist[k][1]:
- message=dlclist[k][3]+" [DLC "+str(dlclist[k][4])+"]"+" v"+dlclist[k][2];print(message);feed+=message+'\n'
- cdlc+=1
- dlc_called.append(dlclist[k])
- message='------------------------------------------------';print(message);feed+=message+'\n'
- message='CONTENT INCLUDES: 1 BASEGAME '+str(cupd)+' UPDATES '+str(cdlc)+' DLCS';print(message);feed+=message+'\n'
- message='------------------------------------------------';print(message);feed+=message+'\n'
- if len(patchlist) != len(patch_called):
- message='------------------------------------------------';print(message);feed+=message+'\n'
- message='ORPHANED UPDATES:';print(message);feed+=message+'\n'
- message='------------------------------------------------';print(message);feed+=message+'\n'
- for j in range(len(patchlist)):
- if patchlist[j] not in patch_called:
- v=patchlist[j][2]
- v_number=str(int(int(v)/65536))
- message=patchlist[j][3]+" [UPD]"+" v"+patchlist[j][2]+" -> Patch("+v_number+")";print(message);feed+=message+'\n'
- if len(dlclist) != len(dlc_called):
- message='------------------------------------------------';print(message);feed+=message+'\n'
- message='ORPHANED DLCS:';print(message);feed+=message+'\n'
- message='------------------------------------------------';print(message);feed+=message+'\n'
- for k in range(len(dlclist)):
- if dlclist[k] not in dlc_called:
- message=dlclist[k][3]+" [DLC "+str(dlclist[k][4])+"]"+" v"+dlclist[k][2];print(message);feed+=message+'\n'
- else:
- message='This option is currently meant for multicontent, that includes at least a base game';print(message);feed+=message+'\n'
- return feed
-
-#READ NACP FILE WITHOUT EXTRACTION
- def read_nacp(self,feed='',gui=False):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- titleid2=nca.header.titleId
- feed=self.html_feed(feed,1,message=str('TITLEID: ' + str(titleid2).upper()))
- offset=nca.get_nacp_offset()
- for f in nca:
- f.seek(offset)
- nacp = ChromeNacp()
- feed=nacp.par_getNameandPub(f.read(0x300*15),feed,gui)
- feed=self.html_feed(feed,2,message=str("Nacp Flags:"))
- feed+=''
- f.seek(offset+0x3000)
- feed=nacp.par_Isbn(f.read(0x24),feed)
- f.seek(offset+0x3025)
- feed=nacp.par_getStartupUserAccount(f.readInt8('little'),feed)
- feed=nacp.par_getUserAccountSwitchLock(f.readInt8('little'),feed)
- feed=nacp.par_getAddOnContentRegistrationType(f.readInt8('little'),feed)
- feed=nacp.par_getContentType(f.readInt8('little'),feed)
- f.seek(offset+0x3030)
- feed=nacp.par_getParentalControl(f.readInt8('little'),feed)
- f.seek(offset+0x3034)
- feed=nacp.par_getScreenshot(f.readInt8('little'),feed)
- feed=nacp.par_getVideoCapture(f.readInt8('little'),feed)
- feed=nacp.par_dataLossConfirmation(f.readInt8('little'),feed)
- feed=nacp.par_getPlayLogPolicy(f.readInt8('little'),feed)
- f.seek(offset+0x3038)
- feed=nacp.par_getPresenceGroupId(f.readInt64('little'),feed)
- feed+=' '
- f.seek(offset+0x3040)
- listages=list()
- feed=self.html_feed(feed,2,message=str("Age Ratings:"))
- feed+=''
- for i in range(12):
- feed=nacp.par_getRatingAge(f.readInt8('little'),i,feed)
- feed+=' '
- f.seek(offset+0x3060)
- try:
- feed=self.html_feed(feed,2,message=str("Nacp Atributes:"))
- feed+=''
- feed=nacp.par_getDisplayVersion(f.read(0xF),feed)
- f.seek(offset+0x3070)
- feed=nacp.par_getAddOnContentBaseId(f.readInt64('little'),feed)
- f.seek(offset+0x3078)
- feed=nacp.par_getSaveDataOwnerId(f.readInt64('little'),feed)
- f.seek(offset+0x3080)
- feed=nacp.par_getUserAccountSaveDataSize(f.readInt64('little'),feed)
- f.seek(offset+0x3088)
- feed=nacp.par_getUserAccountSaveDataJournalSize(f.readInt64('little'),feed)
- f.seek(offset+0x3090)
- feed=nacp.par_getDeviceSaveDataSize(f.readInt64('little'),feed)
- f.seek(offset+0x3098)
- feed=nacp.par_getDeviceSaveDataJournalSize(f.readInt64('little'),feed)
- f.seek(offset+0x30A0)
- feed=nacp.par_getBcatDeliveryCacheStorageSize(f.readInt64('little'),feed)
- f.seek(offset+0x30A8)
- feed=nacp.par_getApplicationErrorCodeCategory(f.read(0x07),feed)
- f.seek(offset+0x30B0)
- feed=nacp.par_getLocalCommunicationId(f.readInt64('little'),feed)
- f.seek(offset+0x30F0)
- feed=nacp.par_getLogoType(f.readInt8('little'),feed)
- feed=nacp.par_getLogoHandling(f.readInt8('little'),feed)
- feed=nacp.par_getRuntimeAddOnContentInstall(f.readInt8('little'),feed)
- f.seek(offset+0x30F6)
- feed=nacp.par_getCrashReport(f.readInt8('little'),feed)
- feed=nacp.par_getHdcp(f.readInt8('little'),feed)
- feed=nacp.par_getSeedForPseudoDeviceId(f.readInt64('little'),feed)
- f.seek(offset+0x3100)
- feed=nacp.par_getBcatPassphrase(f.read(0x40),feed)
- f.seek(offset+0x3148)
- feed=nacp.par_UserAccountSaveDataSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3150)
- feed=nacp.par_UserAccountSaveDataJournalSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3158)
- feed=nacp.par_getDeviceSaveDataSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3160)
- feed=nacp.par_getDeviceSaveDataJournalSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3168)
- feed=nacp.par_getTemporaryStorageSize(f.readInt64('little'),feed)
- feed=nacp.par_getCacheStorageSize(f.readInt64('little'),feed)
- f.seek(offset+0x3178)
- feed=nacp.par_getCacheStorageJournalSize(f.readInt64('little'),feed)
- feed=nacp.par_getCacheStorageDataAndJournalSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3188)
- feed=nacp.par_getCacheStorageIndexMax(f.readInt64('little'),feed)
- f.seek(offset+0x3188)
- feed=nacp.par_getPlayLogQueryableApplicationId(f.readInt64('little'),feed)
- f.seek(offset+0x3210)
- feed=nacp.par_getPlayLogQueryCapability(f.readInt8('little'),feed)
- feed=nacp.par_getRepair(f.readInt8('little'),feed)
- feed=nacp.par_getProgramIndex(f.readInt8('little'),feed)
- feed=nacp.par_getRequiredNetworkServiceLicenseOnLaunch(f.readInt8('little'),feed)
- feed+=' '
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- feed+=''
- continue
- return feed
-
-
-#READ CNMT FILE WITHOUT EXTRACTION
- def read_cnmt(self):
- feed=''
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- titleid2=nca.header.titleId
- feed=self.html_feed(feed,1,message=str('TITLEID: ' + str(titleid2).upper()))
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- type_n = cnmt.read(0x1)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- #cnmt.rewind()
- #cnmt.seek(0x28)
- #cnmt.writeInt64(336592896)
- cnmt.rewind()
- cnmt.seek(0x28)
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- feed=self.html_feed(feed,6,message=str(str(cnmt._path)))
- message=["Titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Version:",(str(int.from_bytes(titleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Cnmt Type:",(sq_tools.cnmt_type(type_n))];feed=self.html_feed(feed,3,message)
- message=["Table offset:",(str(hx((offset+0x20).to_bytes(2, byteorder='big'))))];feed=self.html_feed(feed,3,message)
- message=["Number of content:",(str(content_entries))];feed=self.html_feed(feed,3,message)
- message=["Number of meta entries:",(str(meta_entries))];feed=self.html_feed(feed,3,message)
- message=["Application id\Patch id:",((str(hx(original_ID.to_bytes(8, byteorder='big')))[2:-1]).upper())];feed=self.html_feed(feed,3,message)
-
- content_name=str(cnmt._path)
- content_name=content_name[:-22]
- if content_name == 'AddOnContent':
- message=["RequiredUpdateNumber:",(str(min_sversion))];feed=self.html_feed(feed,3,message)
- if content_name != 'AddOnContent':
- message=["RequiredSystemVersion:",(str(min_sversion))];feed=self.html_feed(feed,3,message)
- message=["Length of exmeta:",(str(min_sversion))];feed=self.html_feed(feed,3,message)
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- feed=self.html_feed(feed,2,message=str('Content number ' + str(i+1)))
- vhash = cnmt.read(0x20)
- message=["Hash:",(str(hx(vhash))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- NcaId = cnmt.read(0x10)
- message=["NcaId:",(str(hx(NcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- size = cnmt.read(0x6)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- ncatype = cnmt.read(0x1)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- IdOffset = cnmt.read(0x1)
- message=["IdOffset:",(str(int.from_bytes(IdOffset, byteorder='little', signed=True)))];feed=self.html_feed(feed,3,message)
- cnmt.seek(0x20+offset+content_entries*0x38+length_of_emeta)
- digest = cnmt.read(0x20)
- feed=self.html_feed(feed,7,message=['Digest= ',(str((str(hx(digest))[2:-1]).upper()))])
- cnmt.seek(0x20+offset+content_entries*0x38)
- if length_of_emeta>0:
- feed=self.html_feed(feed,2,message=str('Extended meta:'))
- num_prev_cnmt=cnmt.read(0x4)
- num_prev_delta=cnmt.read(0x4)
- num_delta_info=cnmt.read(0x4)
- num_delta_application =cnmt.read(0x4)
- num_previous_content=cnmt.read(0x4)
- num_delta_content=cnmt.read(0x4)
- cnmt.read(0x4)
- message=["Number of previous cnmt entries:",(str(int.from_bytes(num_prev_cnmt, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Number of previous delta entries:",(str(int.from_bytes(num_prev_delta, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Number of previous content entries:",(str(int.from_bytes(num_previous_content, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Number of delta content entries:",(str(int.from_bytes(num_delta_content, byteorder='little')))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_prev_cnmt, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Previous cnmt records: '+ str(i+1)))
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- type_n = cnmt.read(0x1)
- unknown1=cnmt.read(0x3)
- vhash = cnmt.read(0x20)
- unknown2=cnmt.read(0x2)
- unknown3=cnmt.read(0x2)
- unknown4=cnmt.read(0x4)
- message=["Titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["Version:",(str(int.from_bytes(titleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Content Type:",(sq_tools.cnmt_type(type_n))];feed=self.html_feed(feed,3,message)
- message=["Hash:",(str(hx(vhash))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(unknown2, byteorder='little'))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_prev_delta, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Previous delta records: '+ str(i+1)))
- oldtitleid=cnmt.readInt64()
- newtitleid=cnmt.readInt64()
- oldtitleversion = cnmt.read(0x4)
- newtitleversion = cnmt.read(0x4)
- size = cnmt.read(0x8)
- unknown1=cnmt.read(0x8)
- message=["Old titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["New titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["Old version:",(str(int.from_bytes(oldtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["New version:",(str(int.from_bytes(newtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_delta_info, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Delta info: '+ str(i+1)))
- oldtitleid=cnmt.readInt64()
- newtitleid=cnmt.readInt64()
- oldtitleversion = cnmt.read(0x4)
- newtitleversion = cnmt.read(0x4)
- index1=cnmt.readInt64()
- index2=cnmt.readInt64()
- message=["Old titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["New titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["Old version:",(str(int.from_bytes(oldtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["New version:",(str(int.from_bytes(newtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Index1:",(str(hx(index1.to_bytes(8, byteorder='big'))))];feed=self.html_feed(feed,3,message)
- message=["Index2:",(str(hx(index2.to_bytes(8, byteorder='big'))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_delta_application, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Delta application info: '+ str(i+1)))
- OldNcaId = cnmt.read(0x10)
- NewNcaId = cnmt.read(0x10)
- old_size = cnmt.read(0x6)
- up2bytes = cnmt.read(0x2)
- low4bytes = cnmt.read(0x4)
- unknown1 = cnmt.read(0x2)
- ncatype = cnmt.read(0x1)
- installable = cnmt.read(0x1)
- unknown2 = cnmt.read(0x4)
- message=["OldNcaId:",(str(hx(OldNcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["NewNcaId:",(str(hx(NewNcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Old size:",(str(sq_tools.getSize(int.from_bytes(old_size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- message=["Unknown1:",(str(int.from_bytes(unknown1, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- message=["Installable:",(str(int.from_bytes(installable, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Upper 2 bytes of the new size:",(str(hx(up2bytes)))];feed=self.html_feed(feed,3,message)
- message=["Lower 4 bytes of the new size:",(str(hx(low4bytes)))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_previous_content, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Previous content records: '+ str(i+1)))
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown1 = cnmt.read(0x1)
- message=["NcaId:",(str(hx(NcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_delta_content, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Delta content entry: '+ str(i+1)))
- vhash = cnmt.read(0x20)
- message=["Hash:",(str(hx(vhash))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- NcaId = cnmt.read(0x10)
- message=["NcaId:",(str(hx(NcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- size = cnmt.read(0x6)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- ncatype = cnmt.read(0x1)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- IdOffset = cnmt.read(0x1)
- message=["IdOffset:",(str(int.from_bytes(IdOffset, byteorder='little', signed=True)))];feed=self.html_feed(feed,3,message)
- return feed
-
-#READ CNMT FILE WITHOUT EXTRACTION
- def ret_xml(self,nca):
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- keygeneration=crypto2
- if crypto2<=crypto1:
- keygeneration=crypto1
- xmlname = str(nca._path)
- xmlname = xmlname[:-4] + '.xml'
- metaname=str(nca._path)
- metaname = metaname[:-9]
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- type_n = cnmt.read(0x1)
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.seek(0x18)
- RDSV=cnmt.readInt64()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- cnmt.seek(0x28)
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- cnmt.seek(0x20+offset)
- if str(hx(type_n)) == "b'1'":
- type='SystemProgram'
- if str(hx(type_n)) == "b'2'":
- type='SystemData'
- if str(hx(type_n)) == "b'3'":
- type='SystemUpdate'
- if str(hx(type_n)) == "b'4'":
- type='BootImagePackage'
- if str(hx(type_n)) == "b'5'":
- type='BootImagePackageSafe'
- if str(hx(type_n)) == "b'80'":
- type='Application'
- if str(hx(type_n)) == "b'81'":
- type='Patch'
- if str(hx(type_n)) == "b'82'":
- type='AddOnContent'
- if str(hx(type_n)) == "b'83'":
- type='Delta'
-
- titleid=str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid='0x'+titleid[2:-1]
- version = str(int.from_bytes(titleversion, byteorder='little'))
- RDSV=str(RDSV)
-
- xml_string = '' + '\n'
- xml_string += '' + '\n'
- xml_string +=' '+ type +' ' + '\n'
- xml_string +=(' '+ titleid +' ' + '\n')
- xml_string +=(' '+ version +' ' + '\n')
- xml_string +=(' '+ RDSV +' ' + '\n')
-
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.readInt8()
- unknown = cnmt.read(0x1)
- if ncatype==0:
- type='Meta'
- if str(ncatype)=="1":
- type='Program'
- if ncatype==2:
- type='Data'
- if ncatype==3:
- type='Control'
- if ncatype==4:
- type='HtmlDocument'
- if ncatype==5:
- type='LegalInformation'
- if ncatype==6:
- type='DeltaFragment'
-
- NcaId=str(hx(NcaId))
- NcaId=NcaId[2:-1]
- size=str(int.from_bytes(size, byteorder='little'))
- vhash=str(hx(vhash))
- vhash=vhash[2:-1]
-
- xml_string +=(' ' + '\n')
- xml_string +=(' '+ type +' ' + '\n')
- xml_string +=(' '+ NcaId +' ' + '\n')
- xml_string +=(' '+ size +' ' + '\n')
- xml_string +=(' '+ vhash +' ' + '\n')
- xml_string +=(' '+ str(keygeneration) +' ' + '\n')
- xml_string +=(' ' + '\n')
-
- cnmt.seek(0x20+offset+content_entries*0x38+length_of_emeta)
- digest = str(hx(cnmt.read(0x20)))
- digest=digest[2:-1]
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID='0x'+original_ID[2:-1]
- size=str(nca.header.size)
- block = nca.read()
- nsha=sha256(block).hexdigest()
- xml_string +=(' ' + '\n')
- xml_string +=(' '+ 'Meta' +' ' + '\n')
- xml_string +=(' '+ metaname +' ' + '\n')
- xml_string +=(' '+ size +' ' + '\n')
- xml_string +=(' '+ str(nsha) +' ' + '\n')
- xml_string +=(' '+ str(keygeneration) +' ' + '\n')
- xml_string +=(' ' + '\n')
- xml_string +=(' '+ digest +' ' + '\n')
- xml_string +=(' '+ str(keygeneration) +' ' + '\n')
- xml_string +=(' '+ str(min_sversion) +' ' + '\n')
- xml_string +=(' '+ original_ID +' ' + '\n')
- xml_string +=(' ')
-
- xmlsize=len(xml_string)
- return xmlname,xmlsize
-
-
-#GET VERSION NUMBER FROM CNMT
- def get_cnmt_verID(self):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- cnmt.seek(0x8)
- titleversion = cnmt.read(0x4)
- Print.info(str(int.from_bytes(titleversion, byteorder='little')))
-
-
-#COPY AND CLEAN NCA FILES AND PATCH NEEDED SYSTEM VERSION
- def cr_tr_nca(self,ofolder,buffer,metapatch, keypatch,RSV_cap):
- if keypatch != 'false':
- keypatch = int(keypatch)
- indent = 1
- tabs = '\t' * indent
- text_file='newcontent_'+self.getnspid()+'.dat'
- ticket = self.ticket()
- masterKeyRev = ticket.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = ticket.getRightsId()
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
-
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(nca._path)} - {nca.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
-
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.getCryptoType2() == 0:
- if nca.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
-
- for nca in self:
- if type(nca) == Nca:
- Print.info('Copying files: ')
- if nca.header.getRightsId() != 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- textpath = os.path.join(outfolder, text_file)
- with open(textpath, 'a') as tfile:
- tfile.write(str(nca.header.contentType)+ ': ' + tabs + str(nca._path) + '\n')
- fp = open(filepath, 'w+b')
- nca.rewind()
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- Print.info(tabs + 'Removing titlerights for ' + str(filename))
- Print.info(tabs + 'Writing masterKeyRev for %s, %d' % (str(nca._path), masterKeyRev))
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- target.header.setRightsId(0)
- target.header.setKeyBlock(encKeyBlock)
- Hex.dump(encKeyBlock)
- target.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- Print.info(tabs + 'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if metapatch == 'true':
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs +'DLC -> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- else:
- self.patch_meta(filepath,outfolder,RSV_cap)
-
-
-#COPY AND CLEAN NCA FILES SKIPPING DELTAS AND PATCH NEEDED SYSTEM VERSION
- def cr_tr_nca_nd(self,ofolder,buffer,metapatch, keypatch,RSV_cap):
- if keypatch != 'false':
- keypatch = int(keypatch)
- indent = 1
- tabs = '\t' * indent
- text_file='newcontent_'+self.getnspid()+'.dat'
- ticket = self.ticket()
- masterKeyRev = ticket.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = ticket.getRightsId()
-
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(nca._path)} - {nca.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
-
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.getCryptoType2() == 0:
- if nca.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
-
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- Print.info('Copying files: ')
-
- for nca in self:
- vfragment="false"
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.DATA':
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- Print.info(tabs + 'Skipping delta fragment: ' + str(nca._path))
- continue
- else:
- if nca.header.getRightsId() != 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- textpath = os.path.join(outfolder, text_file)
- with open(textpath, 'a') as tfile:
- tfile.write(str(nca.header.contentType)+ ': ' + tabs + str(nca._path)+'\n')
- fp = open(filepath, 'w+b')
- nca.rewind()
- Print.info(tabs)
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs + '-> Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- Print.info(tabs + 'Removing titlerights for ' + str(filename))
- Print.info(tabs + 'Writing masterKeyRev for %s, %d' % (str(nca._path), masterKeyRev))
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- target.header.setRightsId(0)
- target.header.setKeyBlock(encKeyBlock)
- Hex.dump(encKeyBlock)
- Print.info('')
- target.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- Print.info(tabs)
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs + '-> Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- #///////////////////////////////////
- if metapatch == 'true':
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs +'DLC -> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- else:
- self.patch_meta(filepath,outfolder,RSV_cap)
-
-#Copy nca files
- def copy_nca(self,ofolder,buffer,metapatch,keypatch,RSV_cap):
- if keypatch != 'false':
- keypatch = int(keypatch)
- indent = 1
- tabs = '\t' * indent
- text_file='newcontent_'+self.getnspid()+'.dat'
- for nca in self:
- if type(nca) == Nca:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if metapatch == 'true':
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs +'DLC -> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- else:
- self.patch_meta(filepath,outfolder,RSV_cap)
- t.close()
-
-
-#Copy nca files skipping deltas
- def copy_nca_nd(self,ofolder,buffer,metapatch, keypatch,RSV_cap):
- if keypatch != 'false':
- keypatch = int(keypatch)
- indent = 1
- tabs = '\t' * indent
- text_file='newcontent_'+self.getnspid()+'.dat'
- Print.info('Copying files: ')
- for nca in self:
- vfragment="false"
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.DATA':
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- Print.info('Skipping delta fragment: ' + str(nca._path))
- continue
- else:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if metapatch == 'true':
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs +'DLC -> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- else:
- self.patch_meta(filepath,outfolder,RSV_cap)
-
-#///////////////////////////////////////////////////
-#SPLIT MULTI-CONTENT NSP IN FOLDERS
-#///////////////////////////////////////////////////
- def splitter_read(self,ofolder,buffer,pathend):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=self.readInt32()
- end_of_emeta=self.readInt32()
- target=str(nca._path)
- contentname = self.splitter_get_title(target,offset,content_entries,original_ID)
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- Print.info('-------------------------------------')
- Print.info('Detected content: ' + str(titleid2))
- Print.info('-------------------------------------')
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- version='[v'+version+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- ofolder2 = ofolder+ '/'+contentname+' '+ titleid3+' '+version+'/'+pathend
- self.splitter_copy(ofolder2,buffer,nca_name)
- nca_meta=str(nca._path)
- self.splitter_copy(ofolder2,buffer,nca_meta)
- self.splitter_tyc(ofolder2,titleid2)
- dirlist=os.listdir(ofolder)
- textpath = os.path.join(ofolder, 'dirlist.txt')
- with open(textpath, 'a') as tfile:
- for folder in dirlist:
- item = os.path.join(ofolder, folder)
- tfile.write(item + '\n')
-
-
- def splitter_copy(self,ofolder,buffer,nca_name):
- indent = 1
- tabs = '\t' * indent
- for nca in self:
- if type(nca) == Nca:
- if nca_name == str(nca._path):
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
-
- def splitter_tyc(self,ofolder,titleid):
- indent = 1
- tabs = '\t' * indent
- for ticket in self:
- if type(ticket) == Ticket:
- tik_id = str(ticket._path)
- tik_id =tik_id[:-20]
- if titleid == tik_id:
- ticket.rewind()
- data = ticket.read()
- filename = str(ticket._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(str(filepath), 'w+b')
- Print.info(tabs + 'Copying: ' + str(filename))
- fp.write(data)
- fp.flush()
- fp.close()
- for cert in self:
- if cert._path.endswith('.cert'):
- cert_id = str(cert._path)
- cert_id =cert_id[:-21]
- if titleid == cert_id:
- cert.rewind()
- data = cert.read()
- filename = str(cert._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(str(filepath), 'w+b')
- Print.info(tabs + 'Copying: ' + str(filename))
- fp.write(data)
- fp.flush()
- fp.close()
-
- def splitter_get_title(self,target,offset,content_entries,original_ID):
- content_type=''
- for nca in self:
- if type(nca) == Nca:
- if target == str(nca._path):
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- nca_name='false'
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- ncatype2 = int.from_bytes(ncatype, byteorder='little')
- if ncatype2 == 3:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- content_name=str(cnmt._path)
- content_name=content_name[:-22]
- if content_name == 'Patch':
- content_type=' [UPD]'
- if nca_name=='false':
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- cnmt.rewind()
- testID=cnmt.readInt64()
- if testID == original_ID:
- nca.rewind()
- f.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=self.readInt32()
- end_of_emeta=self.readInt32()
- target=str(nca._path)
- contentname = self.splitter_get_title(target,offset,content_entries,original_ID)
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- ncatype2 = int.from_bytes(ncatype, byteorder='little')
- if ncatype2 == 3:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- content_type=' [DLC]'
- title='DLC'
- for nca in self:
- if type(nca) == Nca:
- if nca_name == str(nca._path):
- for f in nca:
- nca.rewind()
- f.rewind()
- Langue = list()
- Langue = [0,1,6,5,7,10,3,4,9,8,2,11,12,13,14]
- for i in Langue:
- f.seek(0x14200+i*0x300)
- title = f.read(0x200)
- title = title.split(b'\0', 1)[0].decode('utf-8')
- title = (re.sub(r'[\/\\\:\*\?\!\"\<\>\|\.\s™©®()\~]+', ' ', title))
- title = title.strip()
- if title == "":
- title = 'DLC'
- if title != 'DLC':
- title = title + content_type
- return(title)
- return(title)
-
-#///////////////////////////////////////////////////
-#INFO ABOUT UPD REQUIREMENTS
-#///////////////////////////////////////////////////
- def getsdkvertit(self,titid):
- programSDKversion=''
- dataSDKversion=''
- for nca in self:
- if type(nca) == Nca:
- nca_id=nca.header.titleId
- if str(titid[:-3]).upper() == str(nca_id[:-3]).upper():
- if str(nca.header.contentType) == 'Content.PROGRAM':
- programSDKversion=nca.get_sdkversion()
- break
- if programSDKversion=='':
- for nca in self:
- if type(nca) == Nca:
- nca_id=nca.header.titleId
- if str(titid[:-3]).upper() == str(nca_id[:-3]).upper():
- if str(nca.header.contentType) == 'Content.CONTROL':
- programSDKversion=nca.get_sdkversion()
- break
- if programSDKversion=='':
- for nca in self:
- if type(nca) == Nca:
- nca_id=nca.header.titleId
- if str(titid[:-3]).upper() == str(nca_id[:-3]).upper():
- if str(nca.header.contentType) == 'Content.PUBLIC_DATA':
- dataSDKversion = nca.get_sdkversion()
- break
- return programSDKversion,dataSDKversion
-
- def print_fw_req(self,trans=True):
- feed=''
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- RSversion=cnmt.readInt32()
- Emeta=cnmt.readInt32()
- target=str(nca._path)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'Patch':
- content_type='Update'
- reqtag='- RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo Update'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay Update'
- if content_type_cnmt == 'AddOnContent':
- content_type='DLC'
- reqtag='- RequiredUpdateNumber: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if content_type_cnmt == 'Application':
- content_type='Base Game or Application'
- reqtag='- RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay'
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- version=str(int.from_bytes(titleversion, byteorder='little'))
- v_number=int(int(version)/65536)
- RS_number=int(RSversion/65536)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- MinRSV=sq_tools.getMinRSV(keygen,RSversion)
- FW_rq=sq_tools.getFWRangeKG(keygen)
- RSV_rq=sq_tools.getFWRangeRSV(RSversion)
- RSV_rq_min=sq_tools.getFWRangeRSV(MinRSV)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'Patch':
- content_type='Update'
- reqtag='- RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo Update'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay Update'
- if content_type_cnmt == 'AddOnContent':
- content_type='DLC'
- reqtag='- RequiredUpdateNumber: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if content_type_cnmt == 'Application':
- content_type='Base Game or Application'
- reqtag='- RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay'
- programSDKversion,dataSDKversion=self.getsdkvertit(titleid2)
- nsuId,releaseDate,category,ratingContent,numberOfPlayers,intro,description,iconUrl,screenshots,bannerUrl,region,rating,developer,productCode,OnlinePlay,SaveDataCloud,playmodes,video,shopurl=nutdb.get_content_data(titleid2,trans)
- sdkversion=nca.get_sdkversion()
- message=('-----------------------------');print(message);feed+=message+'\n'
- message=('CONTENT ID: ' + str(titleid2));print(message);feed+=message+'\n'
- message=('-----------------------------');print(message);feed+=message+'\n'
- if content_type_cnmt != 'AddOnContent':
- message=("Titleinfo:");print(message);feed+=message+'\n'
- message=("- Name: " + tit_name);print(message);feed+=message+'\n'
- message=("- Editor: " + editor);print(message);feed+=message+'\n'
- message=("- Display Version: " + str(ediver));print(message);feed+=message+'\n'
- message=("- Meta SDK version: " + sdkversion);print(message);feed+=message+'\n'
- message=("- Program SDK version: " + programSDKversion);print(message);feed+=message+'\n'
- suplangue=str((', '.join(SupLg)))
- message=("- Supported Languages: "+suplangue);
- par = textwrap.dedent(message).strip()
- message=(textwrap.fill(par,width=80,initial_indent='', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));print(message);feed+=message+'\n'
- message=("- Content type: "+content_type);print(message);feed+=message+'\n'
- message=("- Version: " + version+' -> '+content_type_cnmt+' ('+str(v_number)+')');print(message);feed+=message+'\n'
- if content_type_cnmt == 'AddOnContent':
- nsuId=nutdb.get_dlcnsuId(titleid2)
- message=("Titleinfo:");print(message);feed+=message+'\n'
- if tit_name != "DLC":
- message=("- Name: " + tit_name);print(message);feed+=message+'\n'
- message=("- Editor: " + editor);print(message);feed+=message+'\n'
- message=("- Content type: "+"DLC");print(message);feed+=message+'\n'
- DLCnumb=str(titleid2)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- message=("- DLC number: "+str(DLCnumb)+' -> '+"AddOnContent"+' ('+str(DLCnumb)+')');print(message);feed+=message+'\n'
- message=("- DLC version Number: " + version+' -> '+"Version"+' ('+str(v_number)+')');print(message);feed+=message+'\n'
- message=("- Meta SDK version: " + sdkversion);print(message);feed+=message+'\n'
- message=("- Data SDK version: " + dataSDKversion);print(message);feed+=message+'\n'
- if SupLg !='':
- suplangue=str((', '.join(SupLg)))
- message=("- Supported Languages: "+suplangue);
- par = textwrap.dedent(message).strip()
- message=(textwrap.fill(par,width=80,initial_indent='', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));print(message);feed+=message+'\n'
- message=("\nRequired Firmware:");print(message);feed+=message+'\n'
- if content_type_cnmt == 'AddOnContent':
- if v_number == 0:
- message=("- Required game version: " + str(RSversion)+' -> '+"Application"+' ('+str(RS_number)+')');print(message);feed+=message+'\n'
- if v_number > 0:
- message=("- Required game version: " + str(RSversion)+' -> '+"Patch"+' ('+str(RS_number)+')');print(message);feed+=message+'\n'
- else:
- message=(reqtag + str(RSversion)+" -> " +RSV_rq);print(message);feed+=message+'\n'
- message=('- Encryption (keygeneration): ' + str(keygen)+" -> " +FW_rq);print(message);feed+=message+'\n'
- if content_type_cnmt != 'AddOnContent':
- message=('- Patchable to: ' + str(MinRSV)+" -> " + RSV_rq_min+'\n');print(message);feed+=message+'\n'
- else:
- message=('- Patchable to: DLC -> no RSV to patch\n');print(message);feed+=message+'\n'
- try:
- if content_type_cnmt != 'AddOnContent':
- message=('ExeFS Data:');print(message);feed+=message+'\n'
- ModuleId,BuildID8,BuildID16=self.read_buildid()
- message=('- BuildID8: '+ BuildID8);print(message);feed+=message+'\n'
- message=('- BuildID16: '+ BuildID16);print(message);feed+=message+'\n'
- message=('- BuildID32: '+ ModuleId +'\n');print(message);feed+=message+'\n'
- except:pass
- if nsuId!=False or numberOfPlayers!=False or releaseDate!=False or category!=False or ratingContent!=False:
- message=('Eshop Data:');print(message);feed+=message+'\n'
- if nsuId!=False:
- message=("- nsuId: " + nsuId);print(message);feed+=message+'\n'
- if region!=False:
- message=('- Data from Region: ' + region);print(message);feed+=message+'\n'
- if numberOfPlayers!=False:
- message=("- Number of Players: " + numberOfPlayers);print(message);feed+=message+'\n'
- if releaseDate!=False:
- message=("- Release Date: " + releaseDate);print(message);feed+=message+'\n'
- if category!=False:
- category=str((', '.join(category)))
- message=("- Genres: " + category);
- par = textwrap.dedent(message).strip()
- message=(textwrap.fill(par,width=80,initial_indent='', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));print(message);feed+=message+'\n'
- if rating!=False:
- message=("- AgeRating: " + rating);print(message);feed+=message+'\n'
- if ratingContent!=False:
- ratingContent=str((', '.join(ratingContent)))
- message=("- Rating tags: " + ratingContent);
- par = textwrap.dedent(message).strip()
- message=(textwrap.fill(par,width=80,initial_indent='', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));print(message);feed+=message+'\n'
- if intro!=False or description!=False:
- message=('\nDescription:');print(message);feed+=message+'\n'
- if intro!=False:
- par = textwrap.dedent(intro).strip().upper()
- message=('-----------------------------------------------------------------------------');print(message);feed+=message+'\n'
- message=(textwrap.fill(par,width=80,initial_indent=' ', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));print(message);feed+=message+'\n'
- message=('-----------------------------------------------------------------------------');print(message);feed+=message+'\n'
- if description!=False:
- if "•" in description:
- data=description.split("• ")
- token='• '
- elif "★" in description:
- data=description.split("★ ")
- token='* '
- elif "*" in description:
- data=description.split("* ")
- token='* '
- elif "■" in description:
- data=description.split("* ")
- token='• '
- elif "●" in description:
- data=description.split("● ")
- token='• '
- elif "-" in description:
- data=description.split("- ")
- token='- '
- else:
- data=description.split(" ")
- token=''
- i=0
- for d in data:
- if i>0:
- d=token+d
- i+=1
- par = textwrap.dedent(d).strip()
- message=(textwrap.fill(par,width=80,initial_indent=' ', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));print(message);feed+=message+'\n'
- return feed
-
- def inf_get_title(self,target,offset,content_entries,original_ID,roman=True):
- content_type=''
- for nca in self:
- if type(nca) == Nca:
- if target == str(nca._path):
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- nca_name='false'
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- ncatype2 = int.from_bytes(ncatype, byteorder='little')
- if ncatype2 == 3:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name=='false':
- title = 'DLC'
- title,editor=nutdb.get_dlcData(titleid2)
- if editor==False:
- editor=""
- if title==False:
- title = 'DLC'
- else:
- SupLg=nutdb.get_content_langue(titleid2)
- if SupLg==False:
- SupLg=""
- #return(title,editor,ediver,SupLg,regionstr,isdemo)
- regionstr="0|0|0|0|0|0|0|0|0|0|0|0|0|0"
- return(title,editor,"",SupLg,regionstr,"")
- if nca_name=='false' and title == 'DLC' :
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- cnmt.rewind()
- testID=cnmt.readInt64()
- if testID == original_ID:
- nca.rewind()
- f.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=self.readInt32()
- end_of_emeta=self.readInt32()
- target=str(nca._path)
- contentname,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- DLCnumb=str(titleid2)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- name = contentname+' '+'['+str(DLCnumb)+']'
- return name,editor,"","",regionstr,""
- title = 'DLC'
- for nca in self:
- if type(nca) == Nca:
- if nca_name == str(nca._path):
- if str(nca.header.contentType) == 'Content.CONTROL':
- title,editor,ediver,SupLg,regionstr,isdemo=nca.get_langueblock(title,roman)
- return(title,editor,ediver,SupLg,regionstr,isdemo)
- regionstr="0|0|0|0|0|0|0|0|0|0|0|0|0|0"
- return(title,"","","",regionstr,"")
-
-
-#///////////////////////////////////////////////////
-#PREPARE BASE CONTENT TO UPDATE IT
-#///////////////////////////////////////////////////
- def updbase_read(self,ofolder,buffer,cskip,metapatch, keypatch,RSV_cap):
- indent = 1
- rightsId = 0
- tabs = '\t' * indent
- titleKeyDec=0x00*10
-
- if keypatch != 'false':
- try:
- keypatch = int(keypatch)
- except:
- print("New keygeneration is no valid integer")
- for file in self:
- if type(file) == Ticket:
- masterKeyRev = file.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = file.getRightsId()
- for file in self:
- if type(file) == Nca:
- if file.header.getRightsId() != 0:
- if file.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(file._path)} - {file.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
- for file in self:
- if type(file) == Nca:
- if file.header.getRightsId() != 0:
- if file.header.getCryptoType2() == 0:
- if file.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
- Print.info('Reading Base NSP:')
- if rightsId !=0:
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- print("")
-
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- content_name=str(cnmt._path)
- content_name=content_name[:-22]
- if content_name == 'Patch':
- if cskip == 'upd':
- continue
- if cskip == 'both':
- continue
- if content_name == 'AddOnContent':
- if cskip == 'dlc':
- continue
- if cskip == 'both':
- continue
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=cnmt.readInt32()
- Emeta=cnmt.readInt32()
- target=str(nca._path)
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- Print.info('-------------------------------------')
- Print.info('Copying content: ' + str(titleid2))
- Print.info('-------------------------------------')
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- version='[v'+version+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- self.updbase_copy(ofolder,buffer,nca_name,metapatch, keypatch,RSV_cap,titleKeyDec)
- nca_meta=str(nca._path)
- self.updbase_copy(ofolder,buffer,nca_meta,metapatch, keypatch,RSV_cap,titleKeyDec)
- #self.updbase_tyc(ofolder,titleid2)
-
- def updbase_copy(self,ofolder,buffer,nca_name,metapatch, keypatch,RSV_cap,titleKeyDec):
- indent = 1
- tabs = '\t' * indent
-
- for nca in self:
- if type(nca) == Nca:
- if nca_name == str(nca._path):
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- if nca.header.getRightsId() != 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- Print.info(tabs)
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs + '-> Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- Print.info(tabs + 'Removing titlerights for ' + str(filename))
- Print.info(tabs + 'Writing masterKeyRev for %s, %d' % (str(nca._path), masterKeyRev))
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- target.header.setRightsId(0)
- target.header.setKeyBlock(encKeyBlock)
- Hex.dump(encKeyBlock)
- Print.info('')
- target.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- Print.info(tabs)
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs + '-> Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- #///////////////////////////////////
- if metapatch == 'true':
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs +'DLC -> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- else:
- self.patch_meta(filepath,outfolder,RSV_cap)
-
- def updbase_tyc(self,ofolder,titleid):
- indent = 1
- tabs = '\t' * indent
- for ticket in self:
- if type(ticket) == Ticket:
- tik_id = str(ticket._path)
- tik_id =tik_id[:-20]
- if titleid == tik_id:
- ticket.rewind()
- data = ticket.read()
- filename = str(ticket._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(str(filepath), 'w+b')
- Print.info(tabs + 'Copying: ' + str(filename))
- fp.write(data)
- fp.flush()
- fp.close()
- for cert in self:
- if cert._path.endswith('.cert'):
- cert_id = str(cert._path)
- cert_id =cert_id[:-21]
- if titleid == cert_id:
- cert.rewind()
- data = cert.read()
- filename = str(cert._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(str(filepath), 'w+b')
- Print.info(tabs + 'Copying: ' + str(filename))
- fp.write(data)
- fp.flush()
- fp.close()
-
-#///////////////////////////////////////////////////
-# Change MKREV_NCA
-#///////////////////////////////////////////////////
-
- def change_mkrev_nca(self, nca, newMasterKeyRev,silent=False):
-
- indent = 2
- tabs = '\t' * indent
- indent2 = 3
- tabs2 = '\t' * indent2
-
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
-
- if type(nca) == Nca:
- if nca.header.getCryptoType2() != newMasterKeyRev:
- if silent == False:
- Print.info(tabs + '-----------------------------------')
- Print.info(tabs + 'Changing keygeneration from %d to %s' % ( nca.header.getCryptoType2(), str(newMasterKeyRev)))
- Print.info(tabs + '-----------------------------------')
- encKeyBlock = nca.header.getKeyBlock()
- if sum(encKeyBlock) != 0:
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)
- if silent == False:
- Print.info(tabs2 + '+ decrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- crypto = aes128.AESECB(key)
- decKeyBlock = crypto.decrypt(encKeyBlock)
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex)
- if silent == False:
- Print.info(tabs2 + '+ encrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex))
- crypto = aes128.AESECB(key)
- reEncKeyBlock = crypto.encrypt(decKeyBlock)
- nca.header.setKeyBlock(reEncKeyBlock)
- if newMasterKeyRev >= 3:
- nca.header.setCryptoType(2)
- nca.header.setCryptoType2(newMasterKeyRev)
- if newMasterKeyRev == 2:
- nca.header.setCryptoType(2)
- nca.header.setCryptoType2(0)
- if newMasterKeyRev < 2:
- nca.header.setCryptoType(newMasterKeyRev)
- nca.header.setCryptoType2(0)
- if silent == False:
- Print.info(tabs2 + 'DONE')
-
-#///////////////////////////////////////////////////
-#PATCH META FUNCTION
-#///////////////////////////////////////////////////
- def patch_meta(self,filepath,outfolder,RSV_cap):
- RSV_cap=int(RSV_cap)
- indent = 1
- tabs = '\t' * indent
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs + 'Checking meta: ')
- meta_nca = Fs.Nca(filepath, 'r+b')
- crypto1=meta_nca.header.getCryptoType()
- crypto2=meta_nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=meta_nca.header.getCryptoType()
- else:
- keygen=meta_nca.header.getCryptoType2()
- else:
- keygen=meta_nca.header.getCryptoType2()
- RSV=meta_nca.get_req_system()
- RSVmin=sq_tools.getMinRSV(keygen,RSV)
- RSVmax=sq_tools.getTopRSV(keygen,RSV)
- if RSV > RSVmin:
- if RSVmin >= RSV_cap:
- meta_nca.write_req_system(RSVmin)
- else:
- if keygen < 4:
- if RSV > RSVmax:
- meta_nca.write_req_system(RSV_cap)
- else:
- meta_nca.write_req_system(RSV_cap)
- meta_nca.flush()
- meta_nca.close()
- Print.info(tabs + 'Updating cnmt hashes: ')
- ############################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha=meta_nca.calc_pfs0_hash()
- Print.info(tabs + '- Calculated hash from pfs0: ')
- Print.info(tabs +' + '+ str(hx(sha)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.set_pfs0_hash(sha)
- meta_nca.flush()
- meta_nca.close()
- ############################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha2=meta_nca.calc_htable_hash()
- Print.info(tabs + '- Calculated table hash: ')
- Print.info(tabs +' + '+ str(hx(sha2)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.header.set_htable_hash(sha2)
- meta_nca.flush()
- meta_nca.close()
- ########################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha3=meta_nca.header.calculate_hblock_hash()
- Print.info(tabs + '- Calculated header block hash: ')
- Print.info(tabs +' + '+ str(hx(sha3)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.header.set_hblock_hash(sha3)
- meta_nca.flush()
- meta_nca.close()
- ########################
- '''
- with open(filepath, 'r+b') as file:
- nsha=sha256(file.read()).hexdigest()
- newname=nsha[:32] + '.cnmt.nca'
- Print.info(tabs +'New name: ' + newname )
- dir=os.path.dirname(os.path.abspath(filepath))
- newpath=dir+ '/' + newname
- os.rename(filepath, newpath)
- Print.info(tabs + '-------------------------------------')
- else:
- Print.info(tabs +'-> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- '''
-
-
- def get_title(self,baseid,roman=True,tag=False):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- if baseid != titleid2:
- continue
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- target=str(nca._path)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'AddOnContent':
- if tag==False:
- nutdbname=nutdb.get_dlcname(titleid2)
- else:
- nutdbname=False
- if nutdbname!=False:
- title=nutdbname
- else:
- DLCnumb=str(titleid2)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- title = 'DLC number '+str(DLCnumb)
- return(title)
- title='DLC'
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- title,editor,ediver,SupLg,regionstr,isdemo=nca.get_langueblock(title,roman)
- return(title)
-
- def get_lang_tag(self,baseid):
- languetag=False
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- if baseid != titleid2:
- continue
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- target=str(nca._path)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'AddOnContent':
- return(False)
- title='DLC'
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- title,editor,ediver,SupLg,regionstr,isdemo=nca.get_langueblock(title)
- languetag='('
- if ("US (eng)" in SupLg) or ("UK (eng)" in SupLg):
- languetag=languetag+'En,'
- if "JP" in SupLg:
- languetag=languetag+'Jp,'
- if ("CAD (fr)" in SupLg) or ("FR" in SupLg):
- languetag=languetag+'Fr,'
- elif ("CAD (fr)" in SupLg):
- languetag=languetag+'CADFr,'
- elif ("FR") in SupLg:
- languetag=languetag+'Fr,'
- if "DE" in SupLg:
- languetag=languetag+'De,'
- if ("LAT (spa)" in SupLg) and ("SPA" in SupLg):
- languetag=languetag+'Es,'
- elif "LAT (spa)" in SupLg:
- languetag=languetag+'LatEs,'
- elif "SPA" in SupLg:
- languetag=languetag+'Es,'
- if "IT" in SupLg:
- languetag=languetag+'It,'
- if "DU" in SupLg:
- languetag=languetag+'Du,'
- if "POR" in SupLg:
- languetag=languetag+'Por,'
- if "RU" in SupLg:
- languetag=languetag+'Ru,'
- if "KOR" in SupLg:
- languetag=languetag+'Kor,'
- if "TAI" in SupLg:
- languetag=languetag+'Tw,'
- if "CH" in SupLg:
- languetag=languetag+'Ch,'
- languetag=languetag[:-1]
- languetag=languetag+')'
- return(languetag)
-
- def gen_nsp_head(self,files,delta,inc_xml,ofolder):
-
- filesNb = len(files)
- stringTable = '\x00'.join(str(nca) for nca in files)
- headerSize = 0x10 + (filesNb)*0x18 + len(stringTable)
- remainder = 0x10 - headerSize%0x10
- headerSize += remainder
-
- fileSizes = list()
- for nca in self:
- vfragment="false"
- if type(nca) == Nca:
- if (delta == False) and (str(nca.header.contentType) == 'Content.DATA'):
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- continue
- fileSizes.append(nca.header.size)
- if str(nca.header.contentType) == 'Content.META' and inc_xml==True:
- xmlname=nca._path
- xmlname=xmlname[:-3]+'xml'
- xmlpath = os.path.join(ofolder, xmlname)
- size=os.path.getsize(xmlpath)
- fileSizes.append(int(size))
-
- fileOffsets = [sum(fileSizes[:n]) for n in range(filesNb)]
-
- fileNamesLengths = [len(str(nca))+1 for nca in files] # +1 for the \x00
- stringTableOffsets = [sum(fileNamesLengths[:n]) for n in range(filesNb)]
-
- header = b''
- header += b'PFS0'
- header += pk('crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- filename = str(nca._path)
- outfolder = str(ofolder)
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- encKeyBlock = target.header.getKeyBlock()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- newheader=self.get_newheader(target,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- target.rewind()
- target.write(newheader)
- target.close()
- if metapatch == 'true':
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if str(target.header.contentType) == 'Content.META':
- for pfs0 in target:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- target.close()
- else:
- target.close()
- self.patcher_meta(filepath,RSV_cap,t)
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- block = target.read()
- nsha=sha256(block).hexdigest()
- target.rewind()
- xml_file=target.xml_gen(ofolder,nsha)
- target.close()
-
- t.close()
-
- contentlist=list()
- for nca in self:
- vfragment="false"
- if type(nca) == Nca:
- if (delta == False) and (str(nca.header.contentType) == 'Content.DATA'):
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- continue
- contentlist.append(nca._path)
- if str(nca.header.contentType) == 'Content.META':
- xmlname=nca._path
- xmlname=xmlname[:-3]+'xml'
- contentlist.append(xmlname)
- hd = self.gen_nsp_head(contentlist,delta,True,ofolder)
-
- totSize = len(hd)
- for nca in self:
- vfragment="false"
- if type(nca) == Nca:
- if (delta == False) and (str(nca.header.contentType) == 'Content.DATA'):
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- continue
- totSize=totSize+nca.header.size
- if str(nca.header.contentType) == 'Content.META':
- xmlname=nca._path
- xmlname=xmlname[:-3]+'xml'
- xmlpath = os.path.join(ofolder, xmlname)
- totSize=totSize+os.path.getsize(xmlpath)
- if os.path.exists(outfile) and os.path.getsize(outfile) == totSize:
- Print.info('\t\tRepack %s is already complete!' % outfile)
- return
-
- indent = 1
- rightsId = 0
- tabs = '\t' * indent
-
- if not os.path.exists(ofolder):
- os.makedirs(ofolder)
-
- if totSize <= 4294901760:
- fat="exfat"
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294901760)
- index=0
- outfile=outfile[:-1]+str(index)
- if fx=="folder" and fat=="fat32":
- output_folder ="archfolder"
- output_folder = os.path.join(ofolder, output_folder)
- outfile = os.path.join(output_folder, "00")
- if not os.path.exists(output_folder):
- os.makedirs(output_folder)
- c=0
-
- Print.info('Generating NSP:')
- if rightsId !=0:
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- print("")
-
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing header...')
- outf = open(outfile, 'w+b')
- outf.write(hd)
- t.update(len(hd))
- c=c+len(hd)
- block=4294901760
- for nca in self:
- vfragment="false"
- if type(nca) == Nca and (str(nca.header.contentType) != 'Content.META'):
- if (delta == False) and (str(nca.header.contentType) == 'Content.DATA'):
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- t.write(tabs+'- Skipping delta fragment: ' + str(nca._path))
- continue
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- if nca.header.getRightsId() != 0:
- nca.rewind()
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- if keypatch != 'false':
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- if nca.header.getRightsId() == 0:
- nca.rewind()
- encKeyBlock = nca.header.getKeyBlock()
- if keypatch != 'false':
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- t.write('')
- t.write(tabs+'- Appending: ' + str(nca._path))
- nca.rewind()
- i=0
- for data in iter(lambda: nca.read(int(buffer)), ""):
- if i==0:
- newheader=self.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- nca.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- if type(nca) == Nca and str(nca.header.contentType) == 'Content.META':
- filename = str(nca._path)
- filepath = os.path.join(outfolder, filename)
- xml_file=filepath[:-3]+'xml'
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- size=os.path.getsize(filepath)
- t.write(tabs+'- Appending: ' + str(nca._path))
- for data in iter(lambda: target.read(int(size)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- target.close()
- break
- try:
- os.remove(filepath)
- except:
- pass
- with open(xml_file, 'r+b') as xmlf:
- size=os.path.getsize(xml_file)
- xmlname=str(nca._path)
- xmlname=xmlname[:-3]+'xml'
- t.write(tabs+'- Appending: ' + xmlname)
- xmlf.seek(0x00)
- for data in iter(lambda: xmlf.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- xmlf.close()
- break
- try:
- os.remove(xml_file)
- except:
- pass
- t.close()
- print("")
- print("Closing file. Please wait")
- outf.close()
-
-#///////////////////////////////////////////////////
-#ADD TO DATABASE
-#///////////////////////////////////////////////////
- def addtodb(self,ofile,dbtype,roman=True):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- titleversion = cnmt.read(0x4)
- version=str(int.from_bytes(titleversion, byteorder='little'))
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- cnmt.readInt64()
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- min_sversion=cnmt.readInt32()
- RS_number=int(min_sversion/65536)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- sdkversion=nca.get_sdkversion()
- programSDKversion,dataSDKversion=self.getsdkvertit(titleid2)
- MinRSV=sq_tools.getMinRSV(keygen,min_sversion)
- RSV_rq=sq_tools.getFWRangeRSV(min_sversion)
- length_of_emeta=cnmt.readInt32()
- target=str(nca._path)
- titlerights,ckey=self.getdbtr(original_ID,content_type,titleid2)
- if ckey == '0':
- ckey=self.getdbkey(titlerights)
- target=str(nca._path)
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID,roman)
- if tit_name=='DLC' and (str(titlerights).endswith('000') or str(titlerights).endswith('800')):
- tit_name='-'
- editor='-'
- if dbtype == 'extended' or dbtype == 'all':
- dbstring=self.getdbstr(titleid2,titlerights,ckey,content_type,tit_name,version,crypto1,crypto2,keygen,min_sversion,RSV_rq[1:-1],RS_number,MinRSV,regionstr,ediver,editor,isdemo,sdkversion,programSDKversion,dataSDKversion)
- if dbtype == 'keyless' or dbtype == 'all':
- kdbstring=self.getkeylessdbstr(titleid2,titlerights,ckey,content_type,tit_name,version,crypto1,crypto2,keygen,min_sversion,RSV_rq[1:-1],RS_number,MinRSV,regionstr,ediver,editor,isdemo,sdkversion,programSDKversion,dataSDKversion)
- if dbtype == 'nutdb' or dbtype == 'all':
- ndbstring=self.getnutdbstr(titleid2,titlerights,ckey,content_type,tit_name,version,regionstr,isdemo)
- if dbtype == 'simple' or dbtype == 'all':
- simplbstr=self.simplbstr(titlerights,ckey,tit_name)
- print ("- Processing: "+ str(self._path))
- print("")
- if dbtype == 'extended':
- print("EXTENDED DB STRING:")
- self.appendtodb(dbstring,ofile,dbtype)
- if dbtype == 'keyless':
- print("EXTENDED KEYLESS DB STRING:")
- self.appendtodb(kdbstring,ofile,dbtype)
- if dbtype == 'nutdb':
- print("NUTDB DB STRING:")
- self.appendtodb(ndbstring,ofile,dbtype)
- if dbtype == 'simple':
- print("SIMPLE DB STRING:")
- self.appendtodb(simplbstr,ofile,dbtype)
- if dbtype == 'all':
- dir=os.path.dirname(os.path.abspath(ofile))
- edbf='extended_DB.txt'
- edbf = os.path.join(dir, edbf)
- ndbf='nutdb_DB.txt'
- ndbf = os.path.join(dir, ndbf)
- kdbfile='keyless_DB.txt'
- kdbfile = os.path.join(dir, kdbfile)
- simpbfile='simple_DB.txt'
- simpbfile = os.path.join(dir, simpbfile)
- print("EXTENDED DB STRING:")
- self.appendtodb(dbstring,edbf,"extended")
- print("")
- print("EXTENDED KEYLESS DB STRING:")
- self.appendtodb(kdbstring,kdbfile,"keyless")
- print("")
- print("NUTDB DB STRING:")
- self.appendtodb(ndbstring,ndbf,"nutdb")
- print("")
- print("SIMPLE DB STRING:")
- self.appendtodb(simplbstr,simpbfile,"simple")
- print("")
-
-
- def getdbtr(self,original_ID,content_type,cnmt_id):
- titleKeyDec='0'
- nca_id=''
- self.rewind()
- tr=''
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- nca_id=nca.header.titleId
- nca_id=nca_id.lower()
- if original_ID==nca_id:
- tr=str(nca.header.rightsId)
- tr = tr[2:-1]
- check=tr[:16]
- if check.endswith('000') and content_type=='Application':
- return tr,titleKeyDec
- if check.endswith('800') and content_type=='Patch':
- return tr,titleKeyDec
- if not check.endswith('000') and not tr.endswith('800') and content_type=='AddOnContent':
- return tr,titleKeyDec
- else:
- tr=str(nca.header.rightsId)
- tr = tr[2:-1]
- return tr,titleKeyDec
- if nca.header.getRightsId() == 0:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- nca_id=nca.header.titleId
- nca_id=nca_id.lower()
- if original_ID==nca_id:
- if nca.header.getgamecard() == 0:
- if crypto1>crypto2:
- masterKeyRev = crypto1
- else:
- masterKeyRev = crypto2
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)
- crypto = aes128.AESECB(key)
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) != 0:
- encKeyBlock = nca.header.getKeyBlock()
- decKeyBlock = crypto.decrypt(encKeyBlock[:16])
- titleKeyDec = hx(Keys.encryptTitleKey(decKeyBlock, Keys.getMasterKeyIndex(masterKeyRev)))
- titleKeyDec=str(titleKeyDec)[2:-1]
- tr=cnmt_id+'000000000000000'+str(crypto2)
- check=tr[:16]
- if check.endswith('000') and content_type=='Application':
- return tr,titleKeyDec
- if check.endswith('800') and content_type=='Patch':
- return tr,titleKeyDec
- elif cnmt_id==nca_id:
- if nca.header.getgamecard() == 0:
- if crypto1>crypto2:
- masterKeyRev = crypto1
- else:
- masterKeyRev = crypto2
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)
- crypto = aes128.AESECB(key)
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) != 0:
- encKeyBlock = nca.header.getKeyBlock()
- decKeyBlock = crypto.decrypt(encKeyBlock[:16])
- titleKeyDec = hx(Keys.encryptTitleKey(decKeyBlock, Keys.getMasterKeyIndex(masterKeyRev)))
- titleKeyDec=str(titleKeyDec)[2:-1]
- tr=cnmt_id+'000000000000000'+str(crypto2)
- check=tr[:16]
- if not check.endswith('000') and not tr.endswith('800') and content_type=='AddOnContent':
- return tr,titleKeyDec
- else:
- tr=cnmt_id+'000000000000000'+str(crypto2)
- else:
- tr=cnmt_id+'000000000000000'+str(crypto2)
- if tr=='':
- tr=cnmt_id+'000000000000000'+str(crypto2)
- titleKeyDec='00000000000000000000000000000000'
- return tr,titleKeyDec
-
- def getdbkey(self,titlerights):
- for ticket in self:
- if type(ticket) == Ticket:
- tikrights = hex(ticket.getRightsId())
- tikrights = '0'+tikrights[2:]
- if titlerights == tikrights:
- titleKey = ticket.getTitleKeyBlock()
- titleKey=str(hx(titleKey.to_bytes(16, byteorder='big')))
- titleKey=titleKey[2:-1]
- return str(titleKey)
-
- def getdbstr(self,titleid,titlerights,ckey,content_type,tit_name,version,crypto1,crypto2,keygen,min_sversion,RSV_rq,RS_number,MinRSV,regionstr,ediver,editor,isdemo,sdkversion,programSDKversion,dataSDKversion):
- dbstr=str()
- dbstr+=str(titleid).upper()+'|'
- dbstr+=str(titlerights).upper()+'|'
- dbstr+=str(keygen)+'|'
- if content_type == 'AddOnContent':
- RSV_rq=sq_tools.getFWRangeRSV(MinRSV)
- RSV_rq=RSV_rq[1:-1]
- ediver='-'
- editor='-'
- dbstr+=str(RSV_rq)+'|'
- if content_type == 'AddOnContent':
- RGV_rq=RS_number
- else:
- RGV_rq="0"
- dbstr+=str(RGV_rq)+'|'
- dbstr+=str(ckey).upper()+'|'
- if content_type == 'Application' and isdemo==0:
- if tit_name == 'DLC':
- tit_name = '-'
- dbstr+='GAME|'
- elif content_type=='Patch' and isdemo==0:
- dbstr+='UPD|'
- elif content_type=='AddOnContent':
- dbstr+='DLC|'
- if tit_name == 'DLC':
- DLCnumb=str(titleid)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- tit_name="DLC Numb. "+str(DLCnumb)
- elif content_type == 'Application' and isdemo!=0:
- if isdemo == 2:
- dbstr+='INT DISPLAY|'
- else:
- dbstr+='DEMO|'
- elif content_type == 'Patch' and isdemo!=0:
- if isdemo == 2:
- dbstr+='UPD INT DISPLAY|'
- else:
- dbstr+='UPD DEMO|'
- elif content_type == 'Application':
- dbstr+='DEMO|'
- elif content_type == 'Patch':
- dbstr+='UPD DEMO|'
- else:
- dbstr+='-|'
- dbstr+=str(tit_name)+'|'
- dbstr+=str(editor)+'|'
- dbstr+=str(version)+'|'
- dbstr+=str(ediver)+'|'
- dbstr+=sdkversion+'|'
- if content_type!='AddOnContent':
- dbstr+=programSDKversion+'|'
- else:
- dbstr+=dataSDKversion+'|'
- dbstr+=regionstr
- return dbstr
-
- def getkeylessdbstr(self,titleid,titlerights,ckey,content_type,tit_name,version,crypto1,crypto2,keygen,min_sversion,RSV_rq,RS_number,MinRSV,regionstr,ediver,editor,isdemo,sdkversion,programSDKversion,dataSDKversion):
- dbstr=str()
- dbstr+=str(titleid).upper()+'|'
- dbstr+=str(titlerights).upper()+'|'
- dbstr+=str(keygen)+'|'
- if content_type == 'AddOnContent':
- RSV_rq=sq_tools.getFWRangeRSV(MinRSV)
- RSV_rq=RSV_rq[1:-1]
- ediver='-'
- editor='-'
- dbstr+=str(RSV_rq)+'|'
- if content_type == 'AddOnContent':
- RGV_rq=RS_number
- else:
- RGV_rq="0"
- dbstr+=str(RGV_rq)+'|'
- if content_type == 'Application' and isdemo==0:
- if tit_name == 'DLC':
- tit_name = '-'
- dbstr+='GAME|'
- elif content_type=='Patch' and isdemo==0:
- dbstr+='UPD|'
- elif content_type=='AddOnContent':
- dbstr+='DLC|'
- if tit_name == 'DLC':
- DLCnumb=str(titleid)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- tit_name="DLC Numb. "+str(DLCnumb)
- elif content_type == 'Application' and isdemo!=0:
- if isdemo == 2:
- dbstr+='INT DISPLAY|'
- else:
- dbstr+='DEMO|'
- elif content_type == 'Patch' and isdemo!=0:
- if isdemo == 2:
- dbstr+='UPD INT DISPLAY|'
- else:
- dbstr+='UPD DEMO|'
- elif content_type == 'Application':
- dbstr+='DEMO|'
- elif content_type == 'Patch':
- dbstr+='UPD DEMO|'
- else:
- dbstr+='-|'
- dbstr+=str(tit_name)+'|'
- dbstr+=str(editor)+'|'
- dbstr+=str(version)+'|'
- dbstr+=str(ediver)+'|'
- dbstr+=sdkversion+'|'
- if content_type!='AddOnContent':
- dbstr+=programSDKversion+'|'
- else:
- dbstr+=dataSDKversion+'|'
- dbstr+=regionstr
- return dbstr
-
- def getnutdbstr(self,titleid,titlerights,ckey,content_type,tit_name,version,regionstr,isdemo,):
- dbstr=str()
- dbstr+=str(titleid).upper()+'|'
- dbstr+=str(titlerights).upper()+'|'
- dbstr+=str(ckey).upper()+'|'
- if content_type == 'Application' and isdemo==0:
- if tit_name == 'DLC':
- tit_name = '-'
- dbstr+='0|0|0|'
- elif content_type=='Patch' and isdemo==0:
- dbstr+='1|0|0|'
- elif content_type=='AddOnContent':
- dbstr+='0|1|0|'
- if tit_name == 'DLC':
- DLCnumb=str(titleid)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- tit_name="DLC Numb. "+str(DLCnumb)
- elif content_type == 'Application' and isdemo!=0:
- dbstr+='0|0|1|'
- elif content_type == 'Patch' and isdemo!=0:
- dbstr+='1|0|1|'
- elif content_type == 'Application':
- dbstr+='0|0|1|'
- elif content_type == 'Patch':
- dbstr+='1|0|1|'
- else:
- dbstr+='-|'
- dbstr+=str(tit_name)+'|'
- dbstr+=str(tit_name)+'|'
- dbstr+=str(version)+'|'
- if regionstr[0]=="1" and regionstr[2]=="0":
- Regionvar="US"
- elif regionstr[0]=="1" and regionstr[2]=="1":
- Regionvar="WORLD"
- elif regionstr[0]=="0" and (regionstr[2]=="1" or regionstr[6]=="1" or regionstr[8]=="1" or regionstr[12]=="1" or regionstr[14]=="1" or regionstr[16]=="1" or regionstr[20]=="1"):
- Regionvar="EUR"
- elif regionstr[4]=="1" and (regionstr[24]=="1" or regionstr[26]=="1" or regionstr[28]=="1"):
- Regionvar="AS"
- elif regionstr[4]=="1" and (regionstr[0]=="0" or regionstr[2]=="0"):
- Regionvar="JAP"
- else:
- Regionvar="-"
- dbstr+=Regionvar
- return dbstr
-
- def simplbstr(self,titlerights,ckey,tit_name):
- dbstr=str()
- dbstr+=str(titlerights).upper()+'|'
- dbstr+=str(ckey).upper()+'|'
- dbstr+=str(tit_name)
- return dbstr
-
- def appendtodb(self,dbstring,ofile,dbtype):
- if dbtype == 'extended':
- initdb='id|rightsId|keygeneration|RSV|RGV|key|ContentType|baseName|editor|version|cversion|metasdkversion|exesdkversion|us|uk|jp|fr|de|lat|spa|it|du|cad|por|ru|kor|tai|ch'
- if dbtype == 'keyless':
- initdb='id|rightsId|keygeneration|RSV|RGV|ContentType|baseName|editor|version|cversion|metasdkversion|exesdkversion|us|uk|jp|fr|de|lat|spa|it|du|cad|por|ru|kor|tai|ch'
- if dbtype == 'nutdb':
- initdb='id|rightsId|key|isUpdate|isDLC|isDemo|baseName|name|version|region'
- if dbtype == 'simple':
- initdb='rightsId|key|name'
- if not os.path.exists(ofile):
- with open(ofile, 'a') as dbfile:
- dbfile.write(initdb+ '\n')
- with open(ofile, 'ab') as dbfile:
- print(dbstring)
- dbfile.write(dbstring.encode('utf-8'))
- with open(ofile, 'a') as dbfile:
- dbfile.write('\n')
-
- def c_xci_direct(self,buffer,outfile,ofolder,fat,delta,metapatch,RSV_cap,keypatch):
- if keypatch != 'false':
- try:
- keypatch = int(keypatch)
- except:
- print("New keygeneration is no valid integer")
-
- indent = 1
- rightsId = 0
- tabs = '\t' * indent
- xci_header,game_info,sig_padding,xci_certificate,root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier=self.get_xciheader(delta)
-
- totSize=len(xci_header)+len(game_info)+len(sig_padding)+len(xci_certificate)+rootSize
-
- if os.path.exists(outfile) and os.path.getsize(outfile) == totSize:
- Print.info('\t\tRepack %s is already complete!' % outfile)
- return
-
- for file in self:
- if type(file) == Ticket:
- masterKeyRev = file.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = file.getRightsId()
- for file in self:
- if type(file) == Nca:
- if file.header.getRightsId() != 0:
- if file.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(file._path)} - {file.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
- for file in self:
- if type(file) == Nca:
- if file.header.getRightsId() != 0:
- if file.header.getCryptoType2() == 0:
- if file.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
- contTR=0
- contGC=0
- iscartridge=False
- for nca in self:
- if type(nca) == Nca:
- contTR+=1
- if nca.header.getgamecard() == 0:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0:
- contGC+=1
- else:
- contGC+=0
- else:
- contGC+=1
- if contTR == contGC and contTR>0 and contGC>0:
- iscartridge=True
-
- Print.info('Generating XCI:')
- if rightsId !=0:
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- print("")
-
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294934528)
- index=0
- outfile=outfile[:-1]+str(index)
- c=0
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing XCI header...')
- if not os.path.exists(ofolder):
- os.makedirs(ofolder)
- outf = open(outfile, 'w+b')
- outf.write(xci_header)
- t.update(len(xci_header))
- c=c+len(xci_header)
- t.write(tabs+'- Writing XCI game info...')
- outf.write(game_info)
- t.update(len(game_info))
- c=c+len(game_info)
- t.write(tabs+'- Generating padding...')
- outf.write(sig_padding)
- t.update(len(sig_padding))
- c=c+len(sig_padding)
- t.write(tabs+'- Writing XCI certificate...')
- outf.write(xci_certificate)
- t.update(len(xci_certificate))
- c=c+len(xci_certificate)
- t.write(tabs+'- Writing ROOT HFS0 header...')
- outf.write(root_header)
- t.update(len(root_header))
- c=c+len(root_header)
- t.write(tabs+'- Writing UPDATE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(upd_multiplier))
- outf.write(upd_header)
- t.update(len(upd_header))
- c=c+len(upd_header)
- t.write(tabs+'- Writing NORMAL partition header...')
- t.write(tabs+' Calculated multiplier: '+str(norm_multiplier))
- outf.write(norm_header)
- t.update(len(norm_header))
- c=c+len(norm_header)
- t.write(tabs+'- Writing SECURE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(sec_multiplier))
- outf.write(sec_header)
- t.update(len(sec_header))
- c=c+len(sec_header)
-
- block=4294934528
-
- for nca in self:
- vfragment="false"
- if type(nca) == Nca and (str(nca.header.contentType) != 'Content.META'):
- if (delta == False) and (str(nca.header.contentType) == 'Content.DATA'):
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if vfragment=="true":
- t.write(tabs+'- Skipping delta fragment: ' + str(nca._path))
- continue
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- if nca.header.getgamecard() == 0:
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0 and iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- else:
- if iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- if nca.header.getRightsId() != 0:
- nca.rewind()
- t.write('')
- t.write(tabs+'* Appending: ' + str(nca._path))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- if keypatch != 'false':
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- if nca.header.getRightsId() == 0:
- nca.rewind()
- t.write('')
- t.write(tabs+'* Appending: ' + str(nca._path))
- encKeyBlock = nca.header.getKeyBlock()
- if keypatch != 'false':
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- nca.rewind()
- i=0
- for data in iter(lambda: nca.read(int(buffer)), ""):
- if i==0:
- newheader=self.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- nca.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- if type(nca) == Nca and str(nca.header.contentType) == 'Content.META':
- nca.rewind()
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- if nca.header.getgamecard() == 0:
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0 and iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- else:
- if iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- t.write('')
- t.write(tabs+'* Getting: ' + str(nca._path))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- encKeyBlock = target.header.getKeyBlock()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- newheader=self.get_newheader(target,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- target.rewind()
- target.write(newheader)
- target.close()
- if metapatch == 'true':
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if str(target.header.contentType) == 'Content.META':
- for pfs0 in target:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- t.write(tabs + '-------------------------------------')
- t.write(tabs +'DLC -> No need to patch the meta' )
- t.write(tabs + '-------------------------------------')
- else:
- target.close()
- self.patcher_meta(filepath,RSV_cap,t)
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- size=os.path.getsize(filepath)
- t.write(tabs+'- Appending: ' + str(nca._path))
- for data in iter(lambda: target.read(int(size)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- target.close()
- break
- try:
- os.remove(filepath)
- except:
- pass
- t.close()
- print("")
- print("Closing file. Please wait")
- outf.close()
-
-
-
- def patcher_meta(self,filepath,RSV_cap,t):
- indent = 1
- RSV_cap=int(RSV_cap)
- tabs = '\t' * indent
- t.write(tabs + '-------------------------------------')
- t.write(tabs + '')
- t.write(tabs + 'Checking meta: ')
- meta_nca = Fs.Nca(filepath, 'r+b')
- crypto1=meta_nca.header.getCryptoType()
- crypto2=meta_nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=meta_nca.header.getCryptoType()
- else:
- keygen=meta_nca.header.getCryptoType2()
- else:
- keygen=meta_nca.header.getCryptoType2()
- RSV=meta_nca.get_req_system()
- t.write(tabs + '- RequiredSystemVersion = ' + str(RSV))
- RSVmin=sq_tools.getMinRSV(keygen,RSV)
- RSVmax=sq_tools.getTopRSV(keygen,RSV)
- if RSV > RSVmin:
- if RSVmin >= RSV_cap:
- min_sversion=meta_nca.write_req_system(RSVmin)
- t.write(tabs + '- New RequiredSystemVersion = '+ str(min_sversion))
- else:
- if keygen < 4:
- if RSV > RSVmax:
- meta_nca.write_req_system(RSV_cap)
- else:
- meta_nca.write_req_system(RSV_cap)
- meta_nca.flush()
- meta_nca.close()
- t.write(tabs + '')
- t.write(tabs + 'Updating cnmt hashes: ')
- ############################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha=meta_nca.calc_pfs0_hash()
- t.write(tabs + '- Calculated hash from pfs0:')
- t.write(tabs + ' + ' + str(hx(sha)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.set_pfs0_hash(sha)
- meta_nca.flush()
- meta_nca.close()
- ############################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha2=meta_nca.calc_htable_hash()
- t.write(tabs + '- Calculated table hash: ')
- t.write(tabs + ' + ' + str(hx(sha2)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.header.set_htable_hash(sha2)
- meta_nca.flush()
- meta_nca.close()
- ########################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha3=meta_nca.header.calculate_hblock_hash()
- t.write(tabs + '- Calculated header block hash: ')
- t.write(tabs + ' + ' + str(hx(sha3)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.header.set_hblock_hash(sha3)
- meta_nca.flush()
- meta_nca.close()
- t.write(tabs + '-------------------------------------')
- else:
- t.write(tabs +'-> No need to patch the meta' )
- t.write(tabs + '-------------------------------------')
- meta_nca.close()
-
-
-
- def metapatcher(self,number):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- filename=nca
- try:
- f = Fs.Nca(filename, 'r+b')
- f.write_req_system(number)
- f.flush()
- f.close()
- ############################
- f = Fs.Nca(filename, 'r+b')
- sha=f.calc_pfs0_hash()
- f.flush()
- f.close()
- f = Fs.Nca(filename, 'r+b')
- f.set_pfs0_hash(sha)
- f.flush()
- f.close()
- ############################
- f = Fs.Nca(filename, 'r+b')
- sha2=f.calc_htable_hash()
- f.flush()
- f.close()
- f = Fs.Nca(filename, 'r+b')
- f.header.set_htable_hash(sha2)
- f.flush()
- f.close()
- ########################
- f = Fs.Nca(filename, 'r+b')
- sha3=f.header.calculate_hblock_hash()
- f.flush()
- f.close()
- f = Fs.Nca(filename, 'r+b')
- f.header.set_hblock_hash(sha3)
- f.flush()
- f.close()
- except BaseException as e:
- Print.error('Exception: ' + str(e))
-
-
- def get_xciheader(self,delta):
- upd_list=list()
- upd_fileSizes = list()
- norm_list=list()
- norm_fileSizes = list()
- sec_list=list()
- sec_fileSizes = list()
- sec_shalist = list()
- for file in self:
- vfragment="false"
- if type(file) == Nca:
- if (delta == False) and (str(file.header.contentType) == 'Content.DATA'):
- for f in file:
- for fn in f:
- filename = str(fn._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- continue
- sec_list.append(file._path)
- sec_fileSizes.append(file.header.size)
- file.rewind()
- hblock = file.read(0x200)
- sha=sha256(hblock).hexdigest()
- sec_shalist.append(sha)
-
- hfs0 = Fs.Hfs0(None, None)
- root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier=hfs0.gen_rhfs0_head(upd_list,norm_list,sec_list,sec_fileSizes,sec_shalist)
- #print (hx(root_header))
- tot_size=0xF000+rootSize
-
- signature=sq_tools.randhex(0x100)
- signature= bytes.fromhex(signature)
-
- sec_offset=root_header[0x90:0x90+0x8]
- sec_offset=int.from_bytes(sec_offset, byteorder='little')
- sec_offset=int((sec_offset+0xF000+0x200)/0x200)
- sec_offset=sec_offset.to_bytes(4, byteorder='little')
- back_offset=(0xFFFFFFFF).to_bytes(4, byteorder='little')
- kek=(0x00).to_bytes(1, byteorder='big')
- cardsize,access_freq=sq_tools.getGCsize(tot_size)
- cardsize=cardsize.to_bytes(1, byteorder='big')
- GC_ver=(0x00).to_bytes(1, byteorder='big')
- GC_flag=(0x00).to_bytes(1, byteorder='big')
- pack_id=(0x8750F4C0A9C5A966).to_bytes(8, byteorder='big')
- valid_data=int(((tot_size-0x1)/0x200))
- valid_data=valid_data.to_bytes(8, byteorder='little')
-
- try:
- Keys.get('xci_header_key')
- key= Keys.get('xci_header_key')
- key= bytes.fromhex(key)
- IV=sq_tools.randhex(0x10)
- IV= bytes.fromhex(IV)
- xkey=True
- #print(hx(IV))
- except:
- IV=(0x5B408B145E277E81E5BF677C94888D7B).to_bytes(16, byteorder='big')
- xkey=False
-
- HFS0_offset=(0xF000).to_bytes(8, byteorder='little')
- len_rHFS0=(len(root_header)).to_bytes(8, byteorder='little')
- sha_rheader=sha256(root_header[0x00:0x200]).hexdigest()
- sha_rheader=bytes.fromhex(sha_rheader)
- sha_ini_data=bytes.fromhex('1AB7C7B263E74E44CD3C68E40F7EF4A4D6571551D043FCA8ECF5C489F2C66E7E')
- SM_flag=(0x01).to_bytes(4, byteorder='little')
- TK_flag=(0x02).to_bytes(4, byteorder='little')
- K_flag=(0x0).to_bytes(4, byteorder='little')
- end_norm = sec_offset
-
- header = b''
- header += signature
- header += b'HEAD'
- header += sec_offset
- header += back_offset
- header += kek
- header += cardsize
- header += GC_ver
- header += GC_flag
- header += pack_id
- header += valid_data
- header += IV
- header += HFS0_offset
- header += len_rHFS0
- header += sha_rheader
- header += sha_ini_data
- header += SM_flag
- header += TK_flag
- header += K_flag
- header += end_norm
-
- #Game_info
- if xkey==True:
- firm_ver='0100000000000000'
- access_freq=access_freq
- Read_Wait_Time='88130000'
- Read_Wait_Time2='00000000'
- Write_Wait_Time='00000000'
- Write_Wait_Time2='00000000'
- Firmware_Mode='00110C00'
- CUP_Version='5a000200'
- Empty1='00000000'
- Upd_Hash='9bfb03ddbb7c5fca'
- CUP_Id='1608000000000001'
- Empty2='00'*0x38
- #print(hx(Empty2))
-
- firm_ver=bytes.fromhex(firm_ver)
- access_freq=bytes.fromhex(access_freq)
- Read_Wait_Time=bytes.fromhex(Read_Wait_Time)
- Read_Wait_Time2=bytes.fromhex(Read_Wait_Time2)
- Write_Wait_Time=bytes.fromhex(Write_Wait_Time)
- Write_Wait_Time2=bytes.fromhex(Write_Wait_Time2)
- Firmware_Mode=bytes.fromhex(Firmware_Mode)
- CUP_Version=bytes.fromhex(CUP_Version)
- Empty1=bytes.fromhex(Empty1)
- Upd_Hash=bytes.fromhex(Upd_Hash)
- CUP_Id=bytes.fromhex(CUP_Id)
- Empty2=bytes.fromhex(Empty2)
-
- Game_info = b''
- Game_info += firm_ver
- Game_info += access_freq
- Game_info += Read_Wait_Time
- Game_info += Read_Wait_Time2
- Game_info += Write_Wait_Time
- Game_info += Write_Wait_Time2
- Game_info += Firmware_Mode
- Game_info += CUP_Version
- Game_info += Empty1
- Game_info += Upd_Hash
- Game_info += CUP_Id
- Game_info += Empty2
-
- gamecardInfoIV=IV[::-1]
- crypto = aes128.AESCBC(key, gamecardInfoIV)
- enc_info=crypto.encrypt(Game_info)
- if xkey==False:
- enc_info=sq_tools.get_enc_gameinfo(tot_size)
-
- #print (hx(enc_info))
-
- #Padding
- sig_padding='00'*0x6E00
- sig_padding=bytes.fromhex(sig_padding)
- #print (hx(sig_padding))
-
- #CERT
- fake_CERT='FF'*0x8000
- fake_CERT=bytes.fromhex(fake_CERT)
- #print (hx(fake_CERT))
-
-
- return header,enc_info,sig_padding,fake_CERT,root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier
-
-#///////////////////////////////////////////////////
-# Change MKREV_NCA
-#///////////////////////////////////////////////////
-
- def get_new_cryptoblock(self, nca, newMasterKeyRev,encKeyBlock,t):
- indent = 1
- tabs = '\t' * indent
- indent2 = 2
- tabs2 = '\t' * indent2
-
- masterKeyRev = nca.header.getCryptoType2()
-
- if type(nca) == Nca:
- if nca.header.getCryptoType2() != newMasterKeyRev:
- t.write(tabs + '-----------------------------------')
- t.write(tabs + 'Changing keygeneration from %d to %s' % ( nca.header.getCryptoType2(), str(newMasterKeyRev)))
- t.write(tabs + '-----------------------------------')
- #encKeyBlock = nca.header.getKeyBlock()
- if sum(encKeyBlock) != 0:
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)
- t.write(tabs2 + '+ decrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- crypto = aes128.AESECB(key)
- decKeyBlock = crypto.decrypt(encKeyBlock)
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex)
- t.write(tabs2 + '+ encrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex))
- crypto = aes128.AESECB(key)
- reEncKeyBlock = crypto.encrypt(decKeyBlock)
- encKeyBlock = reEncKeyBlock
- if newMasterKeyRev >= 3:
- crypto1=2
- crypto2=newMasterKeyRev
- if newMasterKeyRev == 2:
- crypto1=2
- crypto2=0
- if newMasterKeyRev < 2:
- crypto1=newMasterKeyRev
- crypto2=0
- return encKeyBlock,crypto1,crypto2
- return encKeyBlock,nca.header.getCryptoType(),nca.header.getCryptoType2()
-
- def get_newheader(self,target,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag):
- target.rewind()
- rawhead=target.read(0xC00)
- rawhead=hcrypto.decrypt(rawhead)
- header = b''
- header += rawhead[0x00:0x00+0x204]
- #isgamecard 0x204
- GC=bytes.fromhex(gc_flag)
- header += GC
- #contentType 0x205
- header += rawhead[0x205:0x206]
- #crypto 1 0x206
- c1=crypto1.to_bytes(1, byteorder='big')
- header += c1
- #########
- header += rawhead[0x207:0x220]
- #crypto 1 0x220
- c2=crypto2.to_bytes(1, byteorder='big')
- header += c2
- #########
- header += rawhead[0x221:0x230]
- tr='00'*0x10
- tr=bytes.fromhex(tr)
- header += tr
- header += rawhead[0x240:0x240+0xC0]
- header += encKeyBlock
- header += rawhead[0x340:]
- newheader=hcrypto.encrypt(header)
- return newheader
-
- def rebuild(self,buffer,filepath,delta,ndskip,xml_gen):
- ofolder=os.path.dirname(os.path.abspath(filepath))
- if ndskip==True:
- vfragment=False
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.DATA':
- try:
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment=True
- break
- except:
- pass
- if vfragment != True:
- print("> File doesn't have deltas. Skipping")
- return
- contentlist=list()
- ncalist=list()
- completefilelist=list()
- xmlsize=0
- self.rewind()
- for file in self:
- completefilelist.append(str(file._path))
- self.rewind()
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=''
- CTYPE='BASE'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [UPD]'
- CTYPE='UPDATE'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [DLC]'
- CTYPE='DLC'
- else:
- print(titleid2+' is not an update. Skipping')
- continue
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.readInt8()
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- version='[v'+version+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name in completefilelist:
- if ncatype==6 and delta==False:
- print('Skipping delta fragment '+nca_name)
- continue
- else:
- contentlist.append(nca_name)
- nca_meta=str(nca._path)
- if nca_meta in completefilelist:
- contentlist.append(nca_meta)
- if xml_gen == True:
- target = Fs.Nca(nca, 'r+b')
- target.rewind()
- outf= os.path.join(ofolder, str(nca._path))
- fp = open(outf, 'w+b')
- for data in iter(lambda: target.read(int(32768)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- fp.close()
- break
- target = Fs.Nca(outf, 'r+b')
- block = target.read()
- nsha=sha256(block).hexdigest()
- target.rewind()
- xml=target.xml_gen(ofolder,nsha)
- xmlname=nca_meta[:-3]+'xml'
- xmlsize=xmlsize+os.path.getsize(xml)
- contentlist.append(xmlname)
- target.close()
- try:
- os.remove(outf)
- except:
- pass
- for f in completefilelist:
- if f.endswith('.tik') or f.endswith('.cert'):
- test=f[0:16]
- if titleid2==test:
- contentlist.append(f)
-
- filesNb = len(contentlist)
- stringTable = '\x00'.join(str(file) for file in contentlist)
- headerSize = 0x10 + (filesNb)*0x18 + len(stringTable)
- remainder = 0x10 - headerSize%0x10
- headerSize += remainder
-
- fileSizes = list()
- self.rewind()
- for f in contentlist:
- if not f.endswith('.xml'):
- for file in self:
- if file._path==f:
- fileSizes.append(file.size)
- elif f.endswith('.xml'):
- xmlfile= os.path.join(ofolder, f)
- fileSizes.append(os.path.getsize(xmlfile))
- fileOffsets = [sum(fileSizes[:n]) for n in range(filesNb)]
- fileNamesLengths = [len(str(file))+1 for file in contentlist] # +1 for the \x00
- stringTableOffsets = [sum(fileNamesLengths[:n]) for n in range(filesNb)]
-
- hd = b''
- hd += b'PFS0'
- hd += pk(' File doesn't have deltas. Skipping")
- return
- contentlist=list()
- ncalist=list()
- completefilelist=list()
- for file in self:
- completefilelist.append(str(file._path))
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- ncalist=list()
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=''
- CTYPE='BASE'
- if incxml != "special":
- print(titleid2+' is not an update. Skipping')
- continue
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [UPD]'
- CTYPE='UPDATE'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [DLC]'
- CTYPE='DLC'
- if incxml != "special":
- print(titleid2+' is not an update. Skipping')
- continue
- else:
- print(titleid2+' is not an update. Skipping')
- continue
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.readInt8()
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- version='[v'+version+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name in completefilelist:
- if ncatype==6:
- print('Skipping delta fragment '+nca_name)
- continue
- else:
- ncalist.append(nca_name)
- nca_meta=str(nca._path)
- if nca_meta in completefilelist:
- ncalist.append(nca_meta)
- target=str(nca._path)
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- tit_name = (re.sub(r'[\/\\\:\*\?\!\"\<\>\|\.\s™©®()\~]+', ' ', tit_name))
- tit_name = tit_name.strip()
- if tit_name=='DLC' and (str(titleid2).endswith('000') or str(titleid2).endswith('800')):
- tit_name='-'
- editor='-'
- tid='['+titleid2+']'
- tid=tid.upper()
- filename=tit_name+' '+tid+' '+version
- titlerights=titleid2+str('0'*15)+str(crypto2)
- contentlist.append([filename,titleid2,titlerights,keygen,ncalist,CTYPE])
-
- for file in self:
- if type(file) == Ticket or file._path.endswith('.cert'):
- test=file._path
- test=test[0:32]
- for i in contentlist:
- if i[2]==test:
- i[4].append(file._path)
- elif file._path.endswith('.xml') and incxml==True:
- test=file._path
- test=test[0:-4]+'.nca'
- for i in contentlist:
- if test in i[4]:
- i[4].append(file._path)
-
- '''
- for i in contentlist:
- print("")
- print('Filename: '+i[0])
- print('TitleID: '+i[1])
- print('TitleRights: '+i[2])
- print('Keygen: '+str(i[3]))
- for j in i[4]:
- print (j)
- '''
-
- for i in contentlist:
- self.cd_spl_nsp_ND(buffer,i[0],ofolder,i[4],fat,fx)
-
- def cd_spl_nsp_ND(self,buffer,ofile,ofolder,filelist,fat,fx):
- self.rewind()
- outfile=ofile+'.nsp'
- filepath = os.path.join(ofolder, outfile)
- if os.path.exists(filepath) and os.path.getsize(filepath) == totSize:
- Print.info('\t\tRepack %s is already complete!' % outfile)
- return
- if not os.path.exists(ofolder):
- os.makedirs(ofolder)
- self.rewind()
-
- contentlist=list()
- for file in self:
- if file._path in filelist:
- contentlist.append(file._path)
-
- hd = self.cd_spl_gen_nsph(contentlist)
- totSize = len(hd)
- for file in self:
- if file._path in contentlist:
- totSize=totSize+file.size
-
- indent = 1
- rightsId = 0
- tabs = '\t' * indent
-
- if totSize <= 4294901760:
- fat="exfat"
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294901760)
- index=0
- filepath=filepath[:-1]+str(index)
- if fx=="folder" and fat=="fat32":
- output_folder ="archfolder"
- output_folder = os.path.join(ofolder, output_folder)
- filepath = os.path.join(output_folder, "00")
- if not os.path.exists(output_folder):
- os.makedirs(output_folder)
- c=0
-
- Print.info("")
- Print.info('Generating NSP:')
- Print.info('Filename: '+outfile)
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing header...')
- outf = open(str(filepath), 'w+b')
- outf.write(hd)
- t.update(len(hd))
- c=c+len(hd)
- block=4294901760
- for file in self:
- if file._path in contentlist:
- file.rewind()
- t.write(tabs+'- Appending: ' + str(file._path))
- for data in iter(lambda: file.read(int(buffer)), ""):
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- dat2=file.read(int(n2))
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- if totSize>(4294934528+int(buffer)):
- dat2=file.read(int(buffer))
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- if not data:
- break
- t.close()
- print("Closing file. Please wait")
- outf.close()
-
-
- def get_content(self,ofolder,vkeypatch,delta):
- indent = 1
- tabs = '\t' * indent
- if vkeypatch=='false':
- vkeypatch=False
- contentlist=list()
- ncalist=list()
- completefilelist=list()
- for file in self:
- completefilelist.append(str(file._path))
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- ncalist=list()
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- cnmt.readInt64()
- ttag=''
- CTYPE='BASE'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [UPD]'
- CTYPE='UPDATE'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [DLC]'
- CTYPE='DLC'
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- size=int.from_bytes(size, byteorder='little')
- ncatype = cnmt.readInt8()
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- ver=version
- ver='[v'+ver+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name in completefilelist:
- if delta==False and ncatype==6:
- print(tabs+'- Excluding delta fragment '+nca_name)
- continue
- else:
- ncalist.append([nca_name,size])
- nca_meta=str(nca._path)
- if nca_meta in completefilelist:
- ncalist.append([nca_meta,nca.size])
- if ofolder != False:
- target = Fs.Nca(nca, 'r+b')
- target.rewind()
- outf= os.path.join(ofolder, str(nca._path))
- fp = open(outf, 'w+b')
- for data in iter(lambda: target.read(int(32768)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- fp.close()
- break
- target = Fs.Nca(outf, 'r+b')
- block = target.read()
- nsha=sha256(block).hexdigest()
- target.rewind()
- if vkeypatch == False:
- xml=target.xml_gen(ofolder,nsha)
- else:
- xml=target.xml_gen_mod(ofolder,nsha,vkeypatch)
- xmlname=nca_meta[:-3]+'xml'
- xmlsize=os.path.getsize(xml)
- ncalist.append([xmlname,xmlsize])
- target.close()
- try:
- os.remove(outf)
- except:
- pass
- titlerights=titleid2+str('0'*15)+str(crypto2)
- contentlist.append([str(self._path),titleid2,titlerights,keygen,ncalist,CTYPE,version])
-
- for file in self:
- if type(file) == Ticket or file._path.endswith('.cert'):
- test=file._path
- test=test[0:32]
- for i in contentlist:
- if i[2]==test:
- i[4].append([file._path,file.size])
- '''
- for i in contentlist:
- print('Filename: '+i[0])
- print('TitleID: '+i[1])
- print('TitleRights: '+i[2])
- print('Version: '+str(i[6]))
- print('Keygen: '+str(i[3]))
- print('Content type: '+str(i[5]))
- for j in i[4]:
- print (j)
- print("")
- '''
-
- return contentlist
-
-
- def get_content_placeholder(self,ofolder):
- contentlist=list()
- ncalist=list()
- completefilelist=list()
- for file in self:
- if type(file) == Nca:
- if str(file.header.contentType) != 'Content.META' and str(file.header.contentType) != 'Content.CONTROL':
- continue
- else:
- completefilelist.append(str(file._path))
- elif str(file._path).endswith('.xml'):
- pass
- else:
- completefilelist.append(str(file._path))
- print (completefilelist)
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- ncalist=list()
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- cnmt.readInt64()
- ttag=''
- CTYPE='BASE'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [UPD]'
- CTYPE='UPDATE'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [DLC]'
- CTYPE='DLC'
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- size=int.from_bytes(size, byteorder='little')
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- ver=version
- ver='[v'+ver+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name in completefilelist:
- ncalist.append([nca_name,size])
- nca_meta=str(nca._path)
- if nca_meta in completefilelist:
- ncalist.append([nca_meta,nca.size])
- if ofolder != False:
- target = Fs.Nca(nca, 'r+b')
- target.rewind()
- outf= os.path.join(ofolder, str(nca._path))
- fp = open(outf, 'w+b')
- for data in iter(lambda: target.read(int(32768)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- fp.close()
- break
- target = Fs.Nca(outf, 'r+b')
- block = target.read()
- nsha=sha256(block).hexdigest()
- target.rewind()
- xml=target.xml_gen(ofolder,nsha)
- xmlname=nca_meta[:-3]+'xml'
- xmlsize=os.path.getsize(xml)
- ncalist.append([xmlname,xmlsize])
- target.close()
- try:
- os.remove(outf)
- except:
- pass
- titlerights=titleid2+str('0'*15)+str(crypto2)
- contentlist.append([str(self._path),titleid2,titlerights,keygen,ncalist,CTYPE,version])
-
- for file in self:
- if type(file) == Ticket or file._path.endswith('.cert'):
- test=file._path
- test=test[0:32]
- for i in contentlist:
- if i[2]==test:
- i[4].append([file._path,file.size])
- '''
- for i in contentlist:
- print('Filename: '+i[0])
- print('TitleID: '+i[1])
- print('TitleRights: '+i[2])
- print('Version: '+str(i[6]))
- print('Keygen: '+str(i[3]))
- print('Content type: '+str(i[5]))
- for j in i[4]:
- print (j)
- print("")
- '''
-
- return contentlist
-
- def append_content(self,outf,target,buffer,t,fat='exfat',fx='files',c=0,index=0):
- block=4294934528
- indent = 1
- tabs = '\t' * indent
- for file in self:
- if str(file._path) == target:
- if type(file) == Nca:
- fp = open(outf, 'a+b')
- file.rewind()
- t.write(tabs+'- Appending: ' + str(file._path))
- for data in iter(lambda: file.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- fp.flush()
- if not data:
- break
- if str(file.header.contentType) == 'Content.META':
- target=str(file._path)
- xmlname=target[:-3]+'xml'
- t.write(tabs+'- Appending: ' + xmlname)
- dir=os.path.dirname(os.path.abspath(outf))
- xmlfile= os.path.join(dir, xmlname)
- xml = open(xmlfile, 'rb')
- data=xml.read()
- xml.close()
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- fp.flush()
- try:
- os.remove(outf)
- except:
- pass
- fp.close()
- else:
- fp = open(outf, 'a+b')
- file.rewind()
- t.write(tabs+'- Appending: ' + str(file._path))
- for data in iter(lambda: file.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- fp.flush()
- if not data:
- break
- fp.close()
- return outf,index,c
-
- def append_clean_content(self,outf,target,buffer,t,gamecard,keypatch,metapatch,RSV_cap,fat,fx,c,index):
- block=4294934528
- indent = 1
- tabs = '\t' * indent
- ticketlist=list()
- if keypatch != 'false':
- try:
- keypatch = int(keypatch)
- except:
- print("New keygeneration is no valid integer")
- for file in self:
- if type(file) == Ticket:
- masterKeyRev = file.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = str(hx(file.getRightsId().to_bytes(16, byteorder='big')))
- encryptedkey=file.getTitleKeyBlock().to_bytes(16, byteorder='big')
- ticketlist.append([masterKeyRev,rightsId,encryptedkey])
- for file in self:
- if str(file._path) == target:
- if type(file) == Nca:
- gc_flag=file.header.getgamecard()
- if gc_flag != 0:
- if gamecard==False:
- gc_flag='00'*0x01
- else:
- gc_flag='01'*0x01
- elif gc_flag == 0:
- if gamecard==True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- else:
- gc_flag='00'*0x01
- file.rewind()
- crypto1=file.header.getCryptoType()
- crypto2=file.header.getCryptoType2()
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- if file.header.getRightsId() != 0:
- for i in range(len(ticketlist)):
- #print(str(file.header.rightsId))
- #print(ticketlist[i][1])
- if str(file.header.rightsId) == ticketlist[i][1]:
- encryptedkey=ticketlist[i][2]
- break
- titleKeyDec = Keys.decryptTitleKey(encryptedkey, Keys.getMasterKeyIndex(masterKeyRev))
- t.write("")
- t.write(tabs+'rightsId =\t' + str(file.header.rightsId))
- t.write(tabs+'titleKeyDec =\t' + str(hx(titleKeyDec)))
- t.write(tabs+'masterKeyRev =\t' + hex(masterKeyRev))
- t.write("")
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), file.header.keyIndex))
- file.rewind()
- t.write(tabs+'* Appending: ' + str(file._path))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- if keypatch != 'false':
- if keypatch < file.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(file, keypatch,encKeyBlock,t)
- #t.write(str(hx(encKeyBlock)))
- newheader=self.get_newheader(file,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- #t.write(str(hx(newheader)))
- if file.header.getRightsId() == 0:
- t.write(tabs+'* Appending: ' + str(file._path))
- file.rewind()
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), file.header.keyIndex))
- encKeyBlock = file.header.getKeyBlock()
- if keypatch != 'false':
- if keypatch < file.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(file, keypatch,encKeyBlock,t)
- newheader=self.get_newheader(file,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- if str(file.header.contentType) != 'Content.META':
- i=0
- sha=sha256()
- fp = open(outf, 'ab')
- file.rewind()
- for data in iter(lambda: file.read(int(buffer)), ""):
- if i==0:
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- sha.update(newheader)
- file.seek(0xC00)
- else:
- fp.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- sha.update(newheader)
- file.seek(0xC00)
- i+=1
- fp.flush()
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- sha.update(data)
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- fp.flush()
- sha.update(data)
- if not data:
- break
- sha=sha.hexdigest()
- '''
- if str(file._path).endswith('.cnmt.nca'):
- newname=sha[:32]+'.cnmt.nca'
- else:
- newname=sha[:32]+'.nca'
- '''
- #t.write(tabs+'new hash: '+sha)
- #t.write(tabs+'new name: '+newname)
- fp.close()
- elif str(file.header.contentType) == 'Content.META':
- metaname = str(file._path)
- dir=os.path.dirname(os.path.abspath(outf))
- metafile=os.path.join(dir, metaname)
- fp = open(metafile, 'w+b')
- file.rewind()
- i=0
- sha=sha256()
- for data in iter(lambda: file.read(int(buffer)), ""):
- if i==0:
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- fp.flush()
- sha.update(newheader)
- else:
- fp.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- sha.update(newheader)
- file.seek(0xC00)
- i+=1
- fp.flush()
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- sha.update(data)
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- sha.update(data)
- fp.flush()
- if not data:
- break
- fp.close()
- if metapatch == 'true' or keypatch != 'false':
- target = Fs.Nca(metafile, 'r+b')
- target.rewind()
- if str(target.header.contentType) == 'Content.META':
- for pfs0 in target:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- t.write(tabs+' > DLC. No RSV to patch')
- target.close()
- else:
- target.close()
- minRSV=sq_tools.getMinRSV(keypatch,RSV_cap)
- if int(minRSV)>int(RSV_cap):
- RSV_cap=minRSV
- self.patcher_meta(metafile,RSV_cap,t)
- target = Fs.Nca(metafile, 'r+b')
- target.rewind()
- fp = open(outf, 'ab')
- file.rewind()
- for data in iter(lambda: target.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- sha.update(data)
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- sha.update(data)
- fp.flush()
- if not data:
- target.close()
- break
- try:
- os.remove(metafile)
- except:
- pass
- if not outf.endswith('xci'):
- test=outf[0:-1]
- if not test.endswith('xc'):
- target=str(file._path)
- xmlname=target[:-3]+'xml'
- t.write(tabs+'* Appending: ' + xmlname)
- dir=os.path.dirname(os.path.abspath(outf))
- xmlfile= os.path.join(dir, xmlname)
- xml = open(xmlfile, 'rb')
- data=xml.read()
- xml.close()
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- fp.flush()
- try:
- os.remove(outf)
- except:
- pass
- fp.close()
- else:
- fp = open(outf, 'ab')
- file.rewind()
- t.write(tabs+'* Appending: ' + str(file._path))
- for data in iter(lambda: file.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- fp.flush()
- if not data:
- break
- fp.close()
- try:
- fp.close()
- except:pass
- return outf,index,c
-
- def sp_groupncabyid(self,buffer,ofolder,fat,fx,export):
- contentlist=list()
- ncalist=list()
- completefilelist=list()
- for file in self:
- completefilelist.append(str(file._path))
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- ncalist=list()
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- cnmt.readInt64()
- ttag=''
- CTYPE='BASE'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [UPD]'
- CTYPE='UPDATE'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [DLC]'
- CTYPE='DLC'
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- version='[v'+version+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name in completefilelist:
- ncalist.append(nca_name)
- nca_meta=str(nca._path)
- if nca_meta in completefilelist:
- ncalist.append(nca_meta)
- target=str(nca._path)
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- tit_name = (re.sub(r'[\/\\\:\*\?\!\"\<\>\|\.\s™©®()\~]+', ' ', tit_name))
- tit_name = tit_name.strip()
- if tit_name=='DLC' and (str(titleid2).endswith('000') or str(titleid2).endswith('800')):
- tit_name='-'
- editor='-'
- filename=tit_name+' '+titleid3+' '+version
- titlerights=titleid2+str('0'*15)+str(crypto2)
- contentlist.append([filename,titleid2,titlerights,keygen,ncalist,CTYPE])
-
- for file in self:
- if type(file) == Ticket or file._path.endswith('.cert'):
- test=file._path
- test=test[0:32]
- for i in contentlist:
- if i[2]==test:
- i[4].append(file._path)
- elif file._path.endswith('.xml'):
- test=file._path
- test=test[0:-4]+'.nca'
- for i in contentlist:
- if test in i[4]:
- i[4].append(file._path)
-
-
- '''
- for i in contentlist:
- print("")
- print('Filename: '+i[0])
- print('TitleID: '+i[1])
- print('TitleRights: '+i[2])
- print('Keygen: '+str(i[3]))
- for j in i[4]:
- print (j)
- '''
-
-
- for i in contentlist:
- if export == 'both' and i[5] != 'BASE':
- export='nsp'
- if export == 'nsp':
- self.cd_spl_nsp(buffer,i[0],ofolder,i[4],fat,fx)
- if export == 'xci':
- self.cd_spl_xci(buffer,i[0],ofolder,i[4],fat,fx)
- if export == 'both':
- self.cd_spl_nsp(buffer,i[0],ofolder,i[4],fat,fx)
- self.cd_spl_xci(buffer,i[0],ofolder,i[4],fat,fx)
-
- def cd_spl_nsp(self,buffer,ofile,ofolder,filelist,fat,fx):
- self.rewind()
- outfile=ofile+'.nsp'
- filepath = os.path.join(ofolder, outfile)
- if os.path.exists(filepath) and os.path.getsize(filepath) == totSize:
- Print.info('\t\tRepack %s is already complete!' % outfile)
- return
- if not os.path.exists(ofolder):
- os.makedirs(ofolder)
- self.rewind()
-
- contentlist=list()
- for file in self:
- if file._path in filelist:
- contentlist.append(file._path)
-
- hd = self.cd_spl_gen_nsph(contentlist)
- totSize = len(hd)
- for file in self:
- if file._path in contentlist:
- totSize=totSize+file.size
-
- indent = 1
- rightsId = 0
- tabs = '\t' * indent
-
- if totSize <= 4294901760:
- fat="exfat"
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294901760)
- index=0
- filepath=filepath[:-1]+str(index)
-
- if fx=="folder" and fat=="fat32":
- output_folder ="archfolder"
- output_folder = os.path.join(ofolder, output_folder)
- filepath = os.path.join(output_folder, "00")
- if not os.path.exists(output_folder):
- os.makedirs(output_folder)
- c=0
-
- Print.info("")
- Print.info('Generating NSP:')
- Print.info('Filename: '+outfile)
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing header...')
- outf = open(str(filepath), 'w+b')
- outfile=str(filepath)
- outf.write(hd)
- t.update(len(hd))
- c=c+len(hd)
- block=4294901760
- for file in self:
- if type(file) == Nca and file._path in contentlist:
- crypto1=file.header.getCryptoType()
- crypto2=file.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), file.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- file.rewind()
- encKeyBlock = file.header.getKeyBlock()
- t.write(tabs+'- Appending: ' + str(file._path))
- file.rewind()
- i=0
- for data in iter(lambda: file.read(int(buffer)), ""):
- if i==0:
- newheader=self.get_newheader(file,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- file.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- if type(file) != Nca and file._path in contentlist:
- file.rewind()
- t.write(tabs+'- Appending: ' + str(file._path))
- for data in iter(lambda: file.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- t.close()
- print("Closing file. Please wait")
- outf.close()
-
- def cd_spl_gen_nsph(self,filelist):
- filesNb = len(filelist)
- stringTable = '\x00'.join(str(file) for file in filelist)
- headerSize = 0x10 + (filesNb)*0x18 + len(stringTable)
- remainder = 0x10 - headerSize%0x10
- headerSize += remainder
-
- fileSizes = list()
- self.rewind()
- for file in self:
- if file._path in filelist:
- fileSizes.append(file.size)
-
- fileOffsets = [sum(fileSizes[:n]) for n in range(filesNb)]
- fileNamesLengths = [len(str(file))+1 for file in filelist] # +1 for the \x00
- stringTableOffsets = [sum(fileNamesLengths[:n]) for n in range(filesNb)]
-
- header = b''
- header += b'PFS0'
- header += pk('crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0:
- contGC+=1
- else:
- contGC+=0
- else:
- contGC+=1
- if contTR == contGC and contTR>0 and contGC>0:
- iscartridge=True
-
- Print.info("")
- Print.info('Generating XCI:')
- Print.info('Filename: '+outfile)
-
- if rightsId !=0:
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- print("")
-
- if totSize <= 4294934528:
- fat="exfat"
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294934528)
- index=0
- filepath=filepath[:-1]+str(index)
-
- outfile=str(filepath)
- c=0
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing XCI header...')
- outf = open(filepath, 'w+b')
- outf.write(xci_header)
- t.update(len(xci_header))
- c=c+len(xci_header)
- t.write(tabs+'- Writing XCI game info...')
- outf.write(game_info)
- t.update(len(game_info))
- c=c+len(game_info)
- t.write(tabs+'- Generating padding...')
- outf.write(sig_padding)
- t.update(len(sig_padding))
- c=c+len(sig_padding)
- t.write(tabs+'- Writing XCI certificate...')
- outf.write(xci_certificate)
- t.update(len(xci_certificate))
- c=c+len(xci_certificate)
- t.write(tabs+'- Writing ROOT HFS0 header...')
- outf.write(root_header)
- t.update(len(root_header))
- c=c+len(root_header)
- t.write(tabs+'- Writing UPDATE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(upd_multiplier))
- outf.write(upd_header)
- t.update(len(upd_header))
- c=c+len(upd_header)
- t.write(tabs+'- Writing NORMAL partition header...')
- t.write(tabs+' Calculated multiplier: '+str(norm_multiplier))
- outf.write(norm_header)
- t.update(len(norm_header))
- c=c+len(norm_header)
- t.write(tabs+'- Writing SECURE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(sec_multiplier))
- outf.write(sec_header)
- t.update(len(sec_header))
- c=c+len(sec_header)
-
- block=4294934528
- for nca in self:
- if type(nca) == Nca and nca._path in contentlist:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- if nca.header.getgamecard() == 0:
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0 and iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- else:
- if iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- if nca.header.getRightsId() != 0:
- nca.rewind()
- t.write('')
- t.write(tabs+'* Appending: ' + str(nca._path))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- if nca.header.getRightsId() == 0:
- nca.rewind()
- t.write('')
- t.write(tabs+'* Appending: ' + str(nca._path))
- encKeyBlock = nca.header.getKeyBlock()
- nca.rewind()
- i=0
- for data in iter(lambda: nca.read(int(buffer)), ""):
- if i==0:
- newheader=self.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- nca.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- t.close()
- print("")
- print("Closing file. Please wait")
- outf.close()
-
- def cd_spl_gen_xcih(self,filelist):
- upd_list=list()
- upd_fileSizes = list()
- norm_list=list()
- norm_fileSizes = list()
- sec_list=list()
- sec_fileSizes = list()
- sec_shalist = list()
- for file in self:
- if file._path in filelist:
- sec_list.append(file._path)
- sec_fileSizes.append(file.size)
- file.rewind()
- hblock = file.read(0x200)
- sha=sha256(hblock).hexdigest()
- sec_shalist.append(sha)
-
- hfs0 = Fs.Hfs0(None, None)
- root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier=hfs0.gen_rhfs0_head(upd_list,norm_list,sec_list,sec_fileSizes,sec_shalist)
- #print (hx(root_header))
- tot_size=0xF000+rootSize
-
- signature=sq_tools.randhex(0x100)
- signature= bytes.fromhex(signature)
-
- sec_offset=root_header[0x90:0x90+0x8]
- sec_offset=int.from_bytes(sec_offset, byteorder='little')
- sec_offset=int((sec_offset+0xF000+0x200)/0x200)
- sec_offset=sec_offset.to_bytes(4, byteorder='little')
- back_offset=(0xFFFFFFFF).to_bytes(4, byteorder='little')
- kek=(0x00).to_bytes(1, byteorder='big')
- cardsize,access_freq=sq_tools.getGCsize(tot_size)
- cardsize=cardsize.to_bytes(1, byteorder='big')
- GC_ver=(0x00).to_bytes(1, byteorder='big')
- GC_flag=(0x00).to_bytes(1, byteorder='big')
- pack_id=(0x8750F4C0A9C5A966).to_bytes(8, byteorder='big')
- valid_data=int(((tot_size-0x1)/0x200))
- valid_data=valid_data.to_bytes(8, byteorder='little')
-
- try:
- Keys.get('xci_header_key')
- key= Keys.get('xci_header_key')
- key= bytes.fromhex(key)
- IV=sq_tools.randhex(0x10)
- IV= bytes.fromhex(IV)
- xkey=True
- #print(hx(IV))
- #print("i'm here")
- except:
- IV=(0x5B408B145E277E81E5BF677C94888D7B).to_bytes(16, byteorder='big')
- xkey=False
- #print("i'm here 2")
-
- HFS0_offset=(0xF000).to_bytes(8, byteorder='little')
- len_rHFS0=(len(root_header)).to_bytes(8, byteorder='little')
- sha_rheader=sha256(root_header[0x00:0x200]).hexdigest()
- sha_rheader=bytes.fromhex(sha_rheader)
- sha_ini_data=bytes.fromhex('1AB7C7B263E74E44CD3C68E40F7EF4A4D6571551D043FCA8ECF5C489F2C66E7E')
- SM_flag=(0x01).to_bytes(4, byteorder='little')
- TK_flag=(0x02).to_bytes(4, byteorder='little')
- K_flag=(0x0).to_bytes(4, byteorder='little')
- end_norm = sec_offset
-
- header = b''
- header += signature
- header += b'HEAD'
- header += sec_offset
- header += back_offset
- header += kek
- header += cardsize
- header += GC_ver
- header += GC_flag
- header += pack_id
- header += valid_data
- header += IV
- header += HFS0_offset
- header += len_rHFS0
- header += sha_rheader
- header += sha_ini_data
- header += SM_flag
- header += TK_flag
- header += K_flag
- header += end_norm
-
- #Game_info
- if xkey==True:
- firm_ver='0100000000000000'
- access_freq=access_freq
- Read_Wait_Time='88130000'
- Read_Wait_Time2='00000000'
- Write_Wait_Time='00000000'
- Write_Wait_Time2='00000000'
- Firmware_Mode='00110C00'
- CUP_Version='5a000200'
- Empty1='00000000'
- Upd_Hash='9bfb03ddbb7c5fca'
- CUP_Id='1608000000000001'
- Empty2='00'*0x38
- #print(hx(Empty2))
-
- firm_ver=bytes.fromhex(firm_ver)
- access_freq=bytes.fromhex(access_freq)
- Read_Wait_Time=bytes.fromhex(Read_Wait_Time)
- Read_Wait_Time2=bytes.fromhex(Read_Wait_Time2)
- Write_Wait_Time=bytes.fromhex(Write_Wait_Time)
- Write_Wait_Time2=bytes.fromhex(Write_Wait_Time2)
- Firmware_Mode=bytes.fromhex(Firmware_Mode)
- CUP_Version=bytes.fromhex(CUP_Version)
- Empty1=bytes.fromhex(Empty1)
- Upd_Hash=bytes.fromhex(Upd_Hash)
- CUP_Id=bytes.fromhex(CUP_Id)
- Empty2=bytes.fromhex(Empty2)
-
- Game_info = b''
- Game_info += firm_ver
- Game_info += access_freq
- Game_info += Read_Wait_Time
- Game_info += Read_Wait_Time2
- Game_info += Write_Wait_Time
- Game_info += Write_Wait_Time2
- Game_info += Firmware_Mode
- Game_info += CUP_Version
- Game_info += Empty1
- Game_info += Upd_Hash
- Game_info += CUP_Id
- Game_info += Empty2
-
- gamecardInfoIV=IV[::-1]
- crypto = aes128.AESCBC(key, gamecardInfoIV)
- enc_info=crypto.encrypt(Game_info)
- if xkey==False:
- enc_info=sq_tools.get_enc_gameinfo(tot_size)
-
- #print (hx(enc_info))
-
- #Padding
- sig_padding='00'*0x6E00
- sig_padding=bytes.fromhex(sig_padding)
- #print (hx(sig_padding))
-
- #CERT
- fake_CERT='FF'*0x8000
- fake_CERT=bytes.fromhex(fake_CERT)
- #print (hx(fake_CERT))
-
-
- return header,enc_info,sig_padding,fake_CERT,root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier
-
-
- def file_hash(self,target):
- indent = 1
- gamecard = False
- tabs = '\t' * indent
- sha=False;sizef=False
- for file in self:
- docheck = False
- if str(file._path) == target:
- file.rewind()
- hblock = file.read(0x200)
- sha=sha256(hblock).hexdigest()
- sizef=file.size
- #print(str(sizef))
- #return sha,sizef
- if type(file) == Nca and gamecard==False:
- if file.header.getgamecard() == 1:
- gamecard=True
- else:
- nca_id=file.header.titleId
- if nca_id.endswith('000') or nca_id.endswith('800'):
- if str(file.header.contentType) == 'Content.PROGRAM':
- docheck=True
- else:
- if str(file.header.contentType) == 'Content.DATA':
- docheck=True
- if docheck == True:
- crypto1=file.header.getCryptoType()
- crypto2=file.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), file.header.keyIndex))
- KB1L=file.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0:
- gamecard=True
- return sha,sizef,gamecard
-
- def verify(self):
- contentlist=list()
- validfiles=list()
- listed_files=list()
- contentlist=list()
- feed=''
- delta = False
- verdict = True
- checktik=False
- feed=self.html_feed(feed,2,message=('DECRYPTION TEST:'))
- for file in self:
- if str(file._path).endswith('.nca'):
- listed_files.append(str(file._path))
- if type(file) == Nca:
- validfiles.append(str(file._path))
- if str(file._path).endswith('.ncz'):
- listed_files.append(str(file._path))
- validfiles.append(str(file._path))
- for file in self:
- if str(file._path).endswith('.tik'):
- listed_files.append(str(file._path))
- if type(file) == Ticket:
- validfiles.append(str(file._path))
-
- for file in listed_files:
- correct=False;baddec=False
- if file in validfiles:
- if file.endswith('cnmt.nca'):
- for f in self:
- if str(f._path) == file:
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));feed+=message+'\n'
- for nf in f:
- nf.rewind()
- test=nf.read(0x4)
- #print(test)
- if str(test) == "b'PFS0'":
- correct=True
- break
- if correct == True:
- correct = self.verify_enforcer(file)
- elif file.endswith('.nca'):
- for f in self:
- if str(f._path) == file:
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));feed+=message+'\n'
- if str(f.header.contentType) != 'Content.PROGRAM':
- correct = self.verify_enforcer(file)
- if correct == True:
- if str(f.header.contentType) == 'Content.PUBLIC_DATA' and f.header.getRightsId() == 0:
- correct = f.pr_noenc_check_dlc()
- if correct == False:
- baddec=True
- else:
- for nf in f:
- try:
- nf.rewind()
- test=nf.read(0x4)
- if str(test) == "b'PFS0'":
- correct=True
- break
- except:
- print(f"{tabs} Error reading {nf}" )
- pass
- f.rewind()
- if correct == True:
- correct = self.verify_enforcer(file)
- if correct == False and f.header.getRightsId() == 0:
- correct = f.pr_noenc_check()
- if correct == False and f.header.getRightsId() != 0:
- correct,tk = self.verify_nca_key(file)
- if correct == True and f.header.getRightsId() == 0:
- correct = f.pr_noenc_check()
- if correct == False:
- baddec=True
- elif file.endswith('.ncz'):
- for f in self:
- if str(f._path)[:-1] == file[:-1]:
- ncztype=Nca(f)
- ncztype._path=f._path
- message=(str(ncztype.header.titleId)+' - '+str(ncztype.header.contentType));feed+=message+'\n'
- correct=self.verify_ncz(file)
- break
- elif file.endswith('.tik'):
- tikfile=str(file)
- checktik == False
- for f in self:
- if str(f._path).endswith('.nca'):
- if checktik == False and f.header.getRightsId() != 0:
- checktik = self.verify_key(str(f._path),tikfile)
- if checktik == True:
- break
- if checktik==False and str(self._path).endswith('.nsz'):
- checktik='nsz'
- else:
- message=('Content.TICKET');feed+=message+'\n'
- correct = checktik
- else:
- correct=False
- if correct==True:
- if file.endswith('cnmt.nca'):
- message=(tabs+file+' -> is CORRECT');feed+=message+'\n'
- else:
- message=(tabs+file+tabs+' -> is CORRECT');feed+=message+'\n'
- elif correct=='ncz':
- message=(tabs+file+tabs+' -> ncz file needs HASH check');feed+=message+'\n'
- elif correct=='nsz':
- pass
- else:
- verdict=False
- if file.endswith('cnmt.nca'):
- message=(tabs+file+' -> is CORRUPT <<<-');feed+=message+'\n'
- elif file.endswith('nca'):
- message=(tabs+file+tabs+' -> is CORRUPT <<<-');feed+=message+'\n'
- if baddec == True:
- message=(tabs+' * NOTE: S.C. CONVERSION WAS PERFORMED WITH BAD KEY');feed+=message+'\n'
- elif file.endswith('tik') and not str(self._path).endswith('.nsz'):
- message=(tabs+file+tabs+' -> titlekey is INCORRECT <<<-');feed+=message+'\n'
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- cnmt.readInt64()
- ttag=''
- CTYPE='BASE'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [UPD]'
- CTYPE='UPDATE'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [DLC]'
- CTYPE='DLC'
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- size=int.from_bytes(size, byteorder='little')
- ncatype = cnmt.readInt8()
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- ver=version
- ver='[v'+ver+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- ncz_name=nca_name[:-4]+'.ncz'
- if (nca_name not in listed_files and ncatype!=6) or (nca_name not in validfiles and ncatype!=6):
- if ncz_name not in listed_files:
- verdict = False
- message=('\n- Missing file from '+titleid2+': '+nca_name);feed+=message+'\n'
-
- ticketlist=list()
- for ticket in self:
- if type(ticket) == Ticket:
- ticketlist.append(str(ticket._path))
- titlerights=list()
- for nca in self:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- rightsId = hx(nca.header.getRightsId().to_bytes(0x10, byteorder='big')).decode('utf-8').lower()
- if rightsId not in titlerights:
- titlerights.append(rightsId)
- mtick=rightsId+'.tik'
- if mtick not in ticketlist:
- message=('\n- File has titlerights!!! Missing ticket: '+mtick);feed+=message+'\n'
- verdict = False
-
- if str(self.path).endswith('.nsz'):
- token='NSZ'
- elif str(self.path).endswith('.nsx'):
- token='NSX'
- elif str(self.path).endswith('.nsp'):
- token='NSP'
- else:
- token='NSP'
- if verdict == False:
- message='\nVERDICT: {} FILE IS CORRUPT OR MISSES FILES\n'.format(token);feed+=message+'\n'
- if verdict == True:
- message='\nVERDICT: {} FILE IS CORRECT\n'.format(token);feed+=message
- return verdict,feed
-
- def verify_sig(self,feed,tmpfolder):
- hlisthash=False
- if feed == False:
- feed=''
- verdict=True
- headerlist=list()
- keygenerationlist=list()
- feed=self.html_feed(feed,2,message=('\nSIGNATURE 1 TEST:'))
- for f in self:
- if type(f) == Nca and f.header.contentType != Type.Content.META:
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));feed+=message+'\n'
- verify,origheader,ncaname,feed,origkg,tr,tkey,iGC=f.verify(feed)
- # headerlist.append([ncaname,origheader,hlisthash])
- headerlist.append([ncaname,origheader,hlisthash,tr,tkey,iGC])
- keygenerationlist.append([ncaname,origkg])
- if verdict == True:
- verdict=verify
- message='';feed+=message+'\n'
- if str(f._path).endswith('.ncz'):
- ncz=Nca(f)
- ncz._path=f._path
- message=(str(ncz.header.titleId)+' - '+str(ncz.header.contentType));feed+=message+'\n'
- verify,origheader,ncaname,feed,origkg,tr,tkey,iGC=ncz.verify(feed)
- # headerlist.append([ncaname,origheader,hlisthash])
- headerlist.append([ncaname,origheader,hlisthash,tr,tkey,iGC])
- keygenerationlist.append([ncaname,origkg])
- if verdict == True:
- verdict=verify
- message='';feed+=message+'\n'
- for f in self:
- if type(f) == Nca and f.header.contentType == Type.Content.META:
- meta_nca=f._path
- f.rewind();meta_dat=f.read()
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));feed+=message+'\n'
- targetkg,minrsv=self.find_addecuatekg(meta_nca,keygenerationlist)
- verify,origheader,ncaname,feed,origkg,tr,tkey,iGC=f.verify(feed)
- #print(targetkg)
- if verify == False:
- tempfile=os.path.join(tmpfolder,meta_nca)
- if not os.path.exists(tmpfolder):
- os.makedirs(tmpfolder)
- fp = open(tempfile, 'w+b')
- fp.write(meta_dat);fp.flush();fp.close()
- kglist=sq_tools.kgstring()
- numb=0;topkg=len(kglist)
- for kg in kglist:
- topkg-=1
- if topkg >= origkg:
- for verNumber in kg:
- numb+=1
- cnmtdidverify=False
- t = tqdm(total=numb, unit='RSV', unit_scale=True, leave=False)
- topkg=len(kglist)
- for kg in kglist:
- if cnmtdidverify == True:
- break
- topkg-=1
- #print(topkg)
- if topkg >= origkg:
- c=0;rsv_endcheck=False
- for verNumber in kg:
- c+=1
- if topkg == origkg:
- if c == len(kg):
- rsv_endcheck=True
- #print(verNumber)
- fp = Fs.Nca(tempfile, 'r+b')
- fp.write_req_system(verNumber)
- fp.flush()
- fp.close()
- ############################
- fp = Fs.Nca(tempfile, 'r+b')
- sha=fp.calc_pfs0_hash()
- fp.flush()
- fp.close()
- fp = Fs.Nca(tempfile, 'r+b')
- fp.set_pfs0_hash(sha)
- fp.flush()
- fp.close()
- ############################
- fp = Fs.Nca(tempfile, 'r+b')
- sha2=fp.calc_htable_hash()
- fp.flush()
- fp.close()
- fp = Fs.Nca(tempfile, 'r+b')
- fp.header.set_htable_hash(sha2)
- fp.flush()
- fp.close()
- ########################
- fp = Fs.Nca(tempfile, 'r+b')
- sha3=fp.header.calculate_hblock_hash()
- fp.flush()
- fp.close()
- fp = Fs.Nca(tempfile, 'r+b')
- fp.header.set_hblock_hash(sha3)
- fp.flush()
- fp.close()
- fp = Fs.Nca(tempfile, 'r+b')
- progress=True
- verify,origheader,ncapath,feed,origkg,tr,tkey,iGC=fp.verify(feed,targetkg,rsv_endcheck,progress,t)
- fp.close()
- t.update(1)
- if verify == True:
- t.close()
- message=(tabs+'* '+"RSV WAS CHANGED FROM "+str(verNumber)+" TO "+str(minrsv));feed+=message+'\n'
- message=(tabs+'* '+"THE CNMT FILE IS CORRECT");feed+=message+'\n'
- if origheader != False:
- hlisthash=True;i=0
- fp = Fs.Nca(tempfile, 'r+b')
- for data in iter(lambda: fp.read(int(32768)), ""):
- if i==0:
- sha0=sha256()
- sha0.update(origheader)
- i+=1
- fp.flush()
- fp.seek(0xC00)
- else:
- sha0.update(data)
- fp.flush()
- if not data:
- fp.close()
- cnmtdidverify=True
- break
- break
- else:break
- try:
- t.close()
- except:pass
- if hlisthash == True:
- sha0=sha0.hexdigest()
- hlisthash=sha0
- headerlist.append([ncaname,origheader,hlisthash,tr,tkey,iGC])
- message='';feed+=message+'\n'
- try:
- shutil.rmtree(tmpfolder)
- except:pass
- if str(self.path).endswith('.nsz'):
- token='NSZ'
- elif str(self.path).endswith('.nsx'):
- token='NSX'
- elif str(self.path).endswith('.nsp'):
- token='NSP'
- else:
- token='NSP'
- if verdict == False:
- message=("VERDICT: {} FILE COULD'VE BEEN TAMPERED WITH").format(token);feed+=message+'\n'
- if verdict == True:
- message=('VERDICT: {} FILE IS SAFE').format(token);feed+=message+'\n'
- return verdict,headerlist,feed
-
-
- def find_addecuatekg(self,ncameta,keygenerationlist):
- ncalist=list()
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- if nca._path == ncameta:
- for f in nca:
- for cnmt in f:
- nca.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=cnmt.readInt32()
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- ncaname=(str(hx(NcaId)))[2:-1]+'.nca'
- for i in range(len(keygenerationlist)):
- if ncaname == keygenerationlist[i][0]:
- if keygenerationlist[i][1] != False:
- return keygenerationlist[i][1],min_sversion
- else:
- size = cnmt.read(0x6)
- size=int.from_bytes(size, byteorder='little')
- ncatype = cnmt.readInt8()
- unknown = cnmt.read(0x1)
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- if nca._path == ncameta:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- keygeneration=crypto2
- if crypto2<=crypto1:
- keygeneration=crypto1
- return keygeneration,min_sversion
- return False,False
-
-
- def verify_hash_nca(self,buffer,headerlist,didverify,feed):
- verdict=True
- if feed == False:
- feed=''
- message='\n***************';print(message);feed+=message+'\n'
- message=('HASH TEST');print(message);feed+=message+'\n'
- message='***************';print(message);feed+=message+'\n'
- for f in self:
- if type(f) == Nca:
- origheader=False
- for i in range(len(headerlist)):
- if str(f._path)==headerlist[i][0]:
- origheader=headerlist[i][1]
- listedhash=headerlist[i][2]
- break
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));print(message);feed+=message+'\n'
- ncasize=f.header.size
- t = tqdm(total=ncasize, unit='B', unit_scale=True, leave=False)
- i=0
- f.rewind();
- rawheader=f.read(0xC00)
- f.rewind()
- for data in iter(lambda: f.read(int(buffer)), ""):
- if i==0:
- sha=sha256()
- f.seek(0xC00)
- sha.update(rawheader)
- if origheader != False and listedhash == False:
- sha0=sha256()
- sha0.update(origheader)
- i+=1
- t.update(len(data))
- f.flush()
- else:
- sha.update(data)
- if origheader != False and listedhash == False:
- sha0.update(data)
- t.update(len(data))
- f.flush()
- if not data:
- break
- t.close()
- sha=sha.hexdigest()
- if listedhash != False:
- sha0=listedhash
- elif origheader != False:
- sha0=sha0.hexdigest()
- message=(' - File name: '+f._path);print(message);feed+=message+'\n'
- message=(' - SHA256: '+sha);print(message);feed+=message+'\n'
- if origheader != False:
- message=(' - ORIG_SHA256: '+sha0);print(message);feed+=message+'\n'
- if str(f._path)[:16] == str(sha)[:16]:
- message=(' > FILE IS CORRECT');print(message);feed+=message+'\n'
- elif origheader != False:
- if str(f._path)[:16] == str(sha0)[:16]:
- message=(' > FILE IS CORRECT');print(message);feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');print(message);feed+=message+'\n'
- verdict = False
- elif f.header.contentType == Type.Content.META and didverify == True:
- message=(' > RSV WAS CHANGED');print(message);feed+=message+'\n'
- #print(' > CHECKING INTERNAL HASHES')
- message=(' * FILE IS CORRECT');print(message);feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');print(message);feed+=message+'\n'
- verdict = False
- message=('');print(message);feed+=message+'\n'
- if verdict == False:
- message=("VERDICT: NSP FILE IS CORRUPT");print(message);feed+=message+'\n'
- if verdict == True:
- message=('VERDICT: NSP FILE IS CORRECT');print(message);feed+=message+'\n'
- return verdict,feed
-
- def verify_enforcer(self,nca):
-
- for f in self:
- if str(f._path) == nca:
- if type(f) == Fs.Nca and f.header.contentType == Type.Content.PROGRAM:
- for fs in f.sectionFilesystems:
- if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR:
- f.seek(0)
- ncaHeader = f.read(0x400)
-
- sectionHeaderBlock = fs.buffer
-
- f.seek(fs.offset)
- pfs0Header = f.read(0x10)
- return True
- else:
- return False
- if type(f) == Fs.Nca and f.header.contentType == Type.Content.META:
- for fs in f.sectionFilesystems:
- if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR:
- f.seek(0)
- ncaHeader = f.read(0x400)
-
- sectionHeaderBlock = fs.buffer
-
- f.seek(fs.offset)
- pfs0Header = f.read(0x10)
- return True
- else:
- return False
-
- if type(f) == Fs.Nca:
- for fs in f.sectionFilesystems:
- if fs.fsType == Type.Fs.ROMFS and fs.cryptoType == Type.Crypto.CTR or f.header.contentType == Type.Content.MANUAL or f.header.contentType == Type.Content.DATA:
- f.seek(0)
- ncaHeader = f.read(0x400)
-
- sectionHeaderBlock = fs.buffer
-
- levelOffset = int.from_bytes(sectionHeaderBlock[0x18:0x20], byteorder='little', signed=False)
- levelSize = int.from_bytes(sectionHeaderBlock[0x20:0x28], byteorder='little', signed=False)
-
- offset = fs.offset + levelOffset
-
- f.seek(offset)
- pfs0Header = f.read(levelSize)
- return True
- else:
- return False
-
- def verify_nca_key(self,nca):
- check=False;titleKey=0
- for file in self:
- if (file._path).endswith('.tik'):
- titleKey = file.getTitleKeyBlock().to_bytes(16, byteorder='big')
- check=self.verify_key(nca,str(file._path))
- if check==True:
- break
- return check,titleKey
-
- def verify_key(self,nca,ticket):
- verticket=ticket
- for file in self:
- if type(file) == Nca:
- crypto1=file.header.getCryptoType()
- crypto2=file.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- masterKeyRev=file.header.getCryptoType()
- else:
- masterKeyRev=file.header.getCryptoType2()
- else:
- masterKeyRev=file.header.getCryptoType2()
- if str(file._path) == nca:
- break
-
- for file in self:
- if type(file) == Ticket:
- if ticket != False:
- if str(file._path) == ticket:
- titleKeyDec = Keys.decryptTitleKey(file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = file.getRightsId()
- break
- else:
- ticket = str(file._path)
- titleKeyDec = Keys.decryptTitleKey(file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = file.getRightsId()
-
- decKey = titleKeyDec
-
- for f in self:
- if str(f._path) == nca:
- if type(f) == Fs.Nca and f.header.getRightsId() != 0:
- for fs in f.sectionFilesystems:
- #print(fs.fsType)
- #print(fs.cryptoType)
- if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR:
- f.seek(0)
- ncaHeader = NcaHeader()
- ncaHeader.open(MemoryFile(f.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key'))))
- pfs0=fs
- sectionHeaderBlock = fs.buffer
- f.seek(fs.offset)
- pfs0Offset=fs.offset
- pfs0Header = f.read(0x10)
- #print(sectionHeaderBlock[8:12] == b'IVFC')
- if sectionHeaderBlock[8:12] == b'IVFC':
- #Hex.dump(self.sectionHeaderBlock)
- #Print.info(hx(self.sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8'))
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- #print('hash = %s' % str(sha256(data).hexdigest()))
- if hx(sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8') == str(sha256(data).hexdigest()):
- return True
- else:
- return False
- else:
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- magic = mem.read()[0:4]
- #print(magic)
- if magic != b'PFS0':
- pass
- else:
- return True
-
- if fs.fsType == Type.Fs.ROMFS and fs.cryptoType == Type.Crypto.CTR:
- f.seek(0)
- ncaHeader = NcaHeader()
- ncaHeader.open(MemoryFile(f.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key'))))
- ncaHeader = f.read(0x400)
- pfs0=fs
- sectionHeaderBlock = fs.buffer
-
- levelOffset = int.from_bytes(sectionHeaderBlock[0x18:0x20], byteorder='little', signed=False)
- levelSize = int.from_bytes(sectionHeaderBlock[0x20:0x28], byteorder='little', signed=False)
-
- pfs0Offset = fs.offset + levelOffset
- f.seek(pfs0Offset)
- pfs0Header = f.read(levelSize)
- #print(sectionHeaderBlock[8:12] == b'IVFC')
- if sectionHeaderBlock[8:12] == b'IVFC':
- #Hex.dump(sectionHeaderBlock)
- #Print.info(hx(sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8'))
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- #print('hash = %s' % str(sha256(data).hexdigest()))
- if hx(sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8') == str(sha256(data).hexdigest()):
- return True
- else:
- return False
- else:
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- magic = mem.read()[0:4]
- #print(magic)
- if magic != b'PFS0':
- return False
- else:
- return True
-
- if fs.fsType == Type.Fs.ROMFS and fs.cryptoType == Type.Crypto.BKTR and str(f.header.contentType) == 'Content.PROGRAM':
- f.seek(0)
- ncaHeader = NcaHeader()
- ncaHeader.open(MemoryFile(f.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key'))))
- ncaHeader = f.read(0x400)
- pfs0=fs
- sectionHeaderBlock = fs.buffer
-
- levelOffset = int.from_bytes(sectionHeaderBlock[0x18:0x20], byteorder='little', signed=False)
- levelSize = int.from_bytes(sectionHeaderBlock[0x20:0x28], byteorder='little', signed=False)
-
- pfs0Offset = fs.offset + levelOffset
- f.seek(pfs0Offset)
- pfs0Header = f.read(levelSize)
- if sectionHeaderBlock[8:12] == b'IVFC':
- for i in range(10):
- ini=0x100+(i*0x10)
- fin=0x110+(i*4)
- test=sectionHeaderBlock[ini:fin]
- if test==b'BKTR':
- return True
- return False
-
-
-
- def cnmt_get_baseids(self):
- ctype='addon'
- idlist=list()
- counter=0
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- if counter>0 and not str(titleid2).endswith('000'):
- counter+=1
- break
- else:
- counter+=1
- titleversion = cnmt.read(0x4);cnmt.seek(0xE)
- offset=cnmt.readInt16();content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16();content_type=str(cnmt._path);content_type=content_type[:-22]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- if original_ID not in idlist:
- idlist.append(original_ID)
- ctype='base'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- if original_ID not in idlist:
- idlist.append(original_ID)
- ctype='update'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- if original_ID not in idlist:
- idlist.append(original_ID)
- ctype='addon'
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- if original_ID not in idlist:
- idlist.append(original_ID)
- ctype='addon'
- break
-
- '''
- for i in idlist:
- print(i)
- '''
- return ctype,idlist
-
-##################
-#FILE RESTORATION
-##################
-
-
-
-
-##################
-#DB DATA
-##################
- def Incorporate_to_permaDB(self,dbfile,trans=True):
- Datashelve = DBmodule.Dict(dbfile)
- cnmtnames=list()
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- cnmtnames.append(str(nca._path))
- for file in cnmtnames:
- DBdict=self.getDBdict(file,trans)
- dbkey=(str(DBdict['id'])+'_v'+str(DBdict['version'])).lower()
- if not 'fields' in Datashelve:
- DBmodule.MainDB.initializeDB(dbfile)
- if not dbkey in Datashelve:
- Datashelve[dbkey]=DBdict
- Datashelve.close()
-
- def return_DBdict(self):
- cnmtnames=list()
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- cnmtnames.append(str(nca._path))
- content_number=len(cnmtnames)
- if content_number>1:
- file,mcstring,mGame=self.choosecnmt(cnmtnames)
- DBdict=self.getDBdict(file,content_number=content_number,mcstring=mcstring,mGame=mGame)
- else:
- DBdict=self.getDBdict(cnmtnames[0],content_number=content_number)
- return DBdict
-
- def choosecnmt(self,cnmtnames):
- file=False;titleid=False;nG=0;nU=0;nD=0;mcstring="";mGame=False
- for nca in self:
- if str(nca._path) in cnmtnames:
- if type(nca) == Nca:
- if titleid==False:
- file=str(nca._path)
- titleid=str(nca.header.titleId)
- if str(nca.header.titleId).endswith('000'):
- nG+=1
- elif str(nca.header.titleId).endswith('800'):
- if nU==0 and nG<2:
- file=str(nca._path)
- nU+=1
- else:
- nD+=1
- if nG>0:
- mcstring+='{} Game'.format(str(nG))
- if nG>1:
- mcstring+='s'
- mGame=True
- if nU>0:
- if nG>0:
- mcstring+=', '
- mcstring+='{} Update'.format(str(nU))
- if nU>1:
- mcstring+='s'
- if nD>0:
- if nG>0 or nU>0:
- mcstring+=', '
- mcstring+='{} DLC'.format(str(nD))
- if nD>1:
- mcstring+='s'
- return file,mcstring,mGame
-
-
- def getDBdict(self,cnmtname,json=False,trans=True,content_number=False,mcstring=False,mGame=False):
- DBdict={}
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=self.get_data_from_cnmt(cnmtname)
- try:
- sqname,sqeditor,SupLg,ctype=self.DB_get_names(ctype,ncadata)
- except:
- sqname,sqeditor,SupLg=self.DB_get_names_from_nutdb(titleid)
- if ctype != 'DLC':
- nacpdict=self.get_data_from_nacp(ncadata)
- titlekey,dectkey=self.db_get_titlekey(rightsId,keygeneration)
- #cnmt flags
- DBdict['id']=titleid
- DBdict['baseid']=base_ID
- DBdict['rightsId']=rightsId
- DBdict['Type']=ctype
- DBdict['version']=titleversion
- DBdict['keygeneration']=keygeneration
- DBdict['RSV']=RSV[1:-1]
- DBdict['RGV']=RGV
- DBdict['metasdkversion']=metasdkversion
- DBdict['exesdkversion']=exesdkversion
- DBdict['InstalledSize']=Installedsize
- DBdict['deltasize']=DeltaSize
- DBdict['HtmlManual']=hasHtmlManual
- DBdict['ncasizes']=ncadata
-
- #nacpt flags
- if ctype != 'DLC':
- DBdict['baseName']=sqname
- DBdict['contentname']='-'
- else:
- DBdict['baseName']='-'
- DBdict['contentname']=sqname
- DBdict['editor']=sqeditor
- DBdict['languages']=SupLg
- if ctype != 'DLC':
- try:
- if nacpdict['StartupUserAccount']=='RequiredWithNetworkServiceAccountAvailable' or nacpdict['RequiredNetworkServiceLicenseOnLaunch']!='None':
- DBdict['linkedAccRequired']='True'
- else:
- DBdict['linkedAccRequired']='False'
- DBdict['dispversion']=nacpdict['BuildNumber']
- except:pass
-
- #ticket flags
- if titlekey==False:
- titlekey='-'
- dectkey='-'
- DBdict['key']=titlekey
- DBdict['deckey']=dectkey
-
- #Distribution type flags
- if ctype=='GAME':
- DBdict['DistEshop']='True'
- DBdict['DistCard']='-'
- else:
- DBdict['DistEshop']='-'
- DBdict['DistCard']='-'
-
- #Check if multicontent
- if content_number==False:
- content_number=sq_tools.count_content(self._path)
- # print(str(content_number))
- DBdict['ContentNumber']=str(content_number)
- if mcstring !=False:
- DBdict['ContentString']=mcstring
- if content_number!=False and content_number>1:
- if mGame==True:
- DBdict['Type']="MULTIGAME"
- else:
- DBdict['Type']="MULTICONTENT"
- DBdict['InstalledSize']=sq_tools.get_mc_isize(self._path)
-
- DBdict['nsuId']='-'
- DBdict['genretags']='-'
- DBdict['ratingtags']='-'
- DBdict['worldreleasedate']='-'
- DBdict['numberOfPlayers']='-'
- DBdict['iconUrl']='-'
- DBdict['screenshots']='-'
- DBdict['bannerUrl']='-'
- DBdict['intro']='-'
- DBdict['description']='-'
- if ctype=='GAME' or ctype=='DLC' or ctype=='DEMO':
- nsuId,worldreleasedate,genretags,ratingtags,numberOfPlayers,intro,description,iconUrl,screenshots,bannerUrl,region,rating,developer,productCode,OnlinePlay,SaveDataCloud,playmodes,video,shopurl=nutdb.get_content_data(titleid,trans)
- regions=nutdb.get_contenregions(titleid)
- else:
- nsuId,worldreleasedate,genretags,ratingtags,numberOfPlayers,intro,description,iconUrl,screenshots,bannerUrl,region,rating,developer,productCode,OnlinePlay,SaveDataCloud,playmodes,video,shopurl=nutdb.get_content_data(base_ID,trans)
- regions=nutdb.get_contenregions(base_ID)
- if nsuId!=False:
- DBdict['nsuId']=nsuId
- if genretags!=False:
- DBdict['genretags']=genretags
- if ratingtags!=False:
- DBdict['ratingtags']=ratingtags
- if worldreleasedate!=False:
- DBdict['worldreleasedate']=worldreleasedate
- if numberOfPlayers!=False:
- DBdict['numberOfPlayers']=numberOfPlayers
- if iconUrl!=False:
- DBdict['iconUrl']=iconUrl
- if screenshots!=False:
- DBdict['screenshots']=screenshots
- if bannerUrl!=False:
- DBdict['bannerUrl']=bannerUrl
- if intro!=False:
- DBdict['intro']=intro
- if description!=False:
- DBdict['description']=description
- if rating!=False:
- DBdict['eshoprating']=rating
- if developer!=False:
- DBdict['developer']=developer
- if productCode!=False:
- DBdict['productCode']=productCode
- if OnlinePlay!=False:
- DBdict['OnlinePlay']=OnlinePlay
- if SaveDataCloud!=False:
- DBdict['SaveDataCloud']=SaveDataCloud
- if playmodes!=False:
- DBdict['playmodes']=playmodes
- if video!=False:
- DBdict['video']=video
- if shopurl!=False:
- DBdict['shopurl']=shopurl
- if len(regions)>0:
- DBdict['regions']=regions
- metascore,userscore,openscore=nutdb.get_metascores(titleid)
- DBdict['metascore']='-'
- DBdict['userscore']='-'
- DBdict['openscore']='-'
- if metascore!=False:
- DBdict['metascore']=metascore
- if userscore!=False:
- DBdict['userscore']=userscore
- if openscore!=False:
- DBdict['openscore']=openscore
- return DBdict
-
- def DB_get_names_from_nutdb(self,titleid):
- try:
- sqname,sqeditor=nutdb.get_dlcData(titleid)
- SupLg=nutdb.get_content_langue(titleid)
- return sqname,sqeditor,SupLg
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return "-","-","-"
-
- def get_data_from_cnmt(self,cnmtname):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- if str(nca._path)==cnmtname:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- keygeneration=crypto2
- if crypto2<=crypto1:
- keygeneration=crypto1
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- titleid=(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1]).upper()
- titleversion = cnmt.read(0x4)
- titleversion=str(int.from_bytes(titleversion, byteorder='little'))
- type_n = cnmt.read(0x1)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- base_ID=cnmt.readInt64()
- base_ID=(str(hx(base_ID.to_bytes(8, byteorder='big')))[2:-1]).upper()
- cnmt.rewind()
- cnmt.seek(0x28)
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt != 'AddOnContent':
- RSV=str(min_sversion)
- RSV=sq_tools.getFWRangeRSV(int(RSV))
- RGV=0
- if content_type_cnmt == 'AddOnContent':
- RSV_rq_min=sq_tools.getMinRSV(keygeneration, 0)
- RSV=sq_tools.getFWRangeRSV(int(RSV_rq_min))
- RGV=str(min_sversion)
- if str(hx(type_n)) == "b'1'":
- ctype='SystemProgram'
- if str(hx(type_n)) == "b'2'":
- ctype='SystemData'
- if str(hx(type_n)) == "b'3'":
- ctype='SystemUpdate'
- if str(hx(type_n)) == "b'4'":
- ctype='BootImagePackage'
- if str(hx(type_n)) == "b'5'":
- ctype='BootImagePackageSafe'
- if str(hx(type_n)) == "b'80'":
- ctype='GAME'
- if str(hx(type_n)) == "b'81'":
- ctype='UPDATE'
- if str(hx(type_n)) == "b'82'":
- ctype='DLC'
- if str(hx(type_n)) == "b'83'":
- ctype='Delta'
- metasdkversion=nca.get_sdkversion()
- programSDKversion,dataSDKversion=self.getsdkvertit(titleid2)
- if content_type_cnmt == 'AddOnContent':
- exesdkversion=dataSDKversion
- if content_type_cnmt != 'AddOnContent':
- exesdkversion=programSDKversion
- ncadata=list()
- hasHtmlManual=False
- Installedsize=int(nca.header.size);DeltaSize=0
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- data={}
- vhash = cnmt.read(0x20)
- vhash=str(hx(vhash))
- NcaId = cnmt.read(0x10)
- NcaId=str(hx(NcaId))
- size = cnmt.read(0x6)
- size=str(int.from_bytes(size, byteorder='little', signed=True))
- ncatype = cnmt.read(0x1)
- ncatype=str(int.from_bytes(ncatype, byteorder='little', signed=True))
- ncatype=sq_tools.getmetacontenttype(ncatype)
- IdOffset = cnmt.read(0x1)
- IdOffset=str(int.from_bytes(IdOffset, byteorder='little', signed=True))
- data['NcaId']=NcaId[2:-1]
- data['NCAtype']=ncatype
- data['Size']=size
- data['Hash']=vhash[2:-1]
- data['IdOffset']=IdOffset
- if int(IdOffset)>0:
- if ctype=='MultiProgram' or ctype=='MultiProgram UPD':
- pass
- elif ctype != 'UPDATE':
- ctype='MultiProgram'
- else:
- ctype='MultiProgram UPD'
- if ncatype != "DeltaFragment":
- Installedsize=Installedsize+int(size)
- else:
- DeltaSize=DeltaSize+int(size)
- if ncatype == "HtmlDocument":
- hasHtmlManual=True
- ncadata.append(data)
- cnmtdata={}
- metaname=str(nca._path);metaname = metaname[:-9]
- nca.rewind();block = nca.read();nsha=sha256(block).hexdigest()
- cnmtdata['NcaId']=metaname
- cnmtdata['NCAtype']='Meta'
- cnmtdata['Size']=str(nca.header.size)
- cnmtdata['Hash']=str(nsha)
- ncadata.append(cnmtdata)
- rightsId=titleid+'000000000000000'+str(crypto2)
- return titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata
-
- def get_data_from_nacp(self,ncadata):
- for entry in ncadata:
- if entry['NCAtype'].lower()=='control':
- target=entry['NcaId']+'.nca'
- dict={}
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if target==str(nca._path):
- offset=nca.get_nacp_offset()
- for f in nca:
- nacp = Nacp()
- f.seek(offset+0x3025)
- dict['StartupUserAccount']=nacp.get_StartupUserAccount(f.readInt8('little'))
- f.seek(offset+0x3060)
- try:
- dict['BuildNumber']=nacp.get_DisplayVersion(f.read(0xF))
- f.seek(offset+0x3213)
- dict['RequiredNetworkServiceLicenseOnLaunch']=nacp.get_RequiredNetworkServiceLicenseOnLaunch(f.readInt8('little'))
- except:continue
- return dict
-
- def DB_get_names(self,ctype,ncadata):
- for entry in ncadata:
- if str(entry['NCAtype']).lower()=='control':
- target=str(entry['NcaId'])+'.nca'
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if target==str(nca._path):
- title,editor,ediver,SupLg,regionstr,isdemo=nca.get_langueblock('DLC',roman=True)
- if ctype=='GAME':
- if isdemo==1:
- ctype='DEMO'
- if isdemo==2:
- ctype='RETAILINTERACTIVEDISPLAY'
- elif ctype=='UPDATE':
- if isdemo==1:
- ctype='DEMO UPDATE'
- if isdemo==2:
- ctype='RETAILINTERACTIVEDISPLAY UPDATE'
- else:
- ctype=ctype
- return title,editor,SupLg,ctype
-
- def db_get_titlekey(self,rightsId,keygeneration):
- #print(rightsId)
- #print(keygeneration)
- for ticket in self:
- if type(ticket) == Ticket:
- tikrights = hex(ticket.getRightsId())
- tikrights = '0'+tikrights[2:]
- if str(rightsId).lower() == str(tikrights).lower():
- titleKey = ticket.getTitleKeyBlock()
- titleKey=str(hx(titleKey.to_bytes(16, byteorder='big')))
- titleKey=titleKey[2:-1].upper()
- deckey = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(int(keygeneration)))
- deckey=(str(hx(deckey))[2:-1]).upper()
- #print(titleKey)
- #print(deckey)
- return str(titleKey),str(deckey)
- return False,False
-
- def icon_info(self,files_list,buffer = 65536):
- for nca in self:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- tk=nca.header.titleKeyDec
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- #print(offset)
- break
- checksums = list()
- try:
- fp=open(str(self._path), 'rb')
- nca3=NCA3(fp,int(offset),str(nca._path),tk)
- for dat in self.open_all_dat(nca3):
- checksum = sha1(dat.read()).digest()
- if checksum not in checksums:
- checksums.append(checksum)
- a=self.print_dat(dat)
- return a
- break
- fp.close()
- except BaseException as e:
- # Print.error('Exception: ' + str(e))
- try:
- with open(str(self._path), 'rb') as f:
- f.seek(offset)
- inmemoryfile = io.BytesIO(f.read(files_list[i][3]))
- nca3=NCA3(inmemoryfile,int(0),str(nca._path),tk,buffer)
- for dat in self.open_all_dat(nca3):
- checksum = sha1(dat.read()).digest()
- if checksum not in checksums:
- checksums.append(checksum)
- a=self.print_dat(dat)
- return a
- break
- fp.close()
- except BaseException as e:
- #Print.error('Exception: ' + str(e))
- pass
- def open_all_dat(self,nca):
- # Iterators that yields all the icon file handles inside an NCA
- for _, sec in enumerate(nca.sections):
- if sec.fs_type == 'PFS0':
- continue
- cur_file = sec.fs.first_file
- while cur_file is not None:
- if cur_file.name.endswith('.dat'):
- yield sec.fs.open(cur_file)
- cur_file = cur_file.next
-
- def print_dat(self,dat):
- dat.seek(0)
- a=dat.read()
- # print(a)
- # img = Image.open(dat)
- # img.show()
- return a
-
- def decompress(self,output,buffer = 65536):
- print('Decompressing {}'.format(self._path))
- files_list=sq_tools.ret_nsp_offsets(self._path)
- 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]
- filepath=entry[0]
- if filepath.endswith('.cnmt.nca'):
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=self.get_data_from_cnmt(filepath)
- 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)
- nspheader=sq_tools.gen_nsp_header(files,filesizes)
- totsize=0
- for s in filesizes:
- totsize+=s
- for i in range(len(files_list)):
- entry=files_list[i]
- fp=entry[0]
- if fp.endswith('.ncz'):
- for j in range(len(files)):
- fp2=files[j]
- if str(fp2[:-1])==str(fp[:-1]):
- totsize+=filesizes[j]
- t = tqdm(total=totsize, unit='B', unit_scale=True, leave=False)
- # Hex.dump(nspheader)
- with open(output, 'wb') as o:
- o.write(nspheader)
- t.update(len(nspheader))
- for file in files:
- if file.endswith('cnmt.nca'):
- for nca in self:
- if str(nca._path)==file:
- t.write('- Appending {}'.format(str(nca._path)))
- nca.rewind()
- data=nca.read()
- with open(output, 'ab') as o:
- o.write(data)
- t.update(len(data))
- elif file.endswith('nca'):
- for nca in self:
- if str(nca._path)[:-1]==file[:-1] and not str(nca._path).endswith('.ncz'):
- t.write('- Appending {}'.format(str(nca._path)))
- o = open(output, 'ab+')
- nca.rewind()
- for data in iter(lambda: nca.read(int(buffer)), ""):
- o.write(data)
- t.update(len(data))
- o.flush()
- if not data:
- o.close()
- break
- elif str(nca._path)[:-1]==file[:-1] and str(nca._path).endswith('ncz'):
- # print(nca._path)
- nca.rewind()
- header = nca.read(0x4000)
- magic = readInt64(nca)
- sectionCount = readInt64(nca)
- sections = []
- for i in range(sectionCount):
- sections.append(Section(nca))
- # print(sections)
- dctx = zstandard.ZstdDecompressor()
- reader = dctx.stream_reader(nca)
- with open(output, 'rb+') as o:
- o.seek(0, os.SEEK_END)
- curr_off= o.tell()
- t.write('- Appending decompressed {}'.format(str(nca._path)))
- t.write(' Writing nca header')
- o.write(header)
- t.update(len(header))
- timestamp = time.time()
- t.write(' Writing decompressed body in plaintext')
- while True:
- chunk = reader.read(16384)
- if not chunk:
- break
- o.write(chunk)
- t.update(len(chunk))
- c=0;spsize=0
- t.write(' + Restoring crypto in sections')
- for s in sections:
- c+=1
- 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))
- t.update(s.size);spsize+=s.size
- continue
- if s.cryptoType not in (3, 4):
- raise IOError('Unknown crypto type: %d' % s.cryptoType)
- 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))))
- i = s.offset
- crypto = AESCTR(s.cryptoKey, s.cryptoCounter)
- end = s.offset + s.size
- spsize+=s.size
- while i < end:
- o.seek(i+curr_off)
- crypto.seek(i)
- chunkSz = 0x10000 if end - i > 0x10000 else end - i
- buf = o.read(chunkSz)
- if not len(buf):
- break
- o.seek(i+curr_off)
- o.write(crypto.encrypt(buf))
- t.update(len(buf))
- i += chunkSz
- 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 ot in self:
- if str(ot._path)==file:
- t.write('- Appending {}'.format(str(ot._path)))
- ot.rewind()
- data=ot.read()
- with open(output, 'ab') as o:
- o.write(data)
- t.update(len(data))
-
- def decompress_direct(self,output,buffer = 65536):
- print('Decompressing {}'.format(self._path))
- files_list=sq_tools.ret_nsp_offsets(self._path)
- 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]
- filepath=entry[0]
- if filepath.endswith('.cnmt.nca'):
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=self.get_data_from_cnmt(filepath)
- 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)
- nspheader=sq_tools.gen_nsp_header(files,filesizes)
- totsize=0
- for s in filesizes:
- totsize+=s
- t = tqdm(total=totsize, unit='B', unit_scale=True, leave=False)
- # Hex.dump(nspheader)
- with open(output, 'wb') as o:
- o.write(nspheader)
- t.update(len(nspheader))
- for file in files:
- if file.endswith('cnmt.nca'):
- for nca in self:
- if str(nca._path)==file:
- t.write('- Appending {}'.format(str(nca._path)))
- nca.rewind()
- data=nca.read()
- with open(output, 'ab') as o:
- o.write(data)
- t.update(len(data))
- elif file.endswith('nca'):
- for nca in self:
- if str(nca._path)[:-1]==file[:-1] and not str(nca._path).endswith('.ncz'):
- t.write('- Appending {}'.format(str(nca._path)))
- o = open(output, 'ab+')
- nca.rewind()
- for data in iter(lambda: nca.read(int(buffer)), ""):
- o.write(data)
- t.update(len(data))
- o.flush()
- if not data:
- o.close()
- break
- elif str(nca._path)[:-1]==file[:-1] and str(nca._path).endswith('ncz'):
- # print(nca._path)
- nca.rewind()
- header = nca.read(0x4000)
- magic = readInt64(nca)
- sectionCount = readInt64(nca)
- sections = []
- for i in range(sectionCount):
- sections.append(Section(nca))
- # print(sections)
- dctx = zstandard.ZstdDecompressor()
- reader = dctx.stream_reader(nca)
- with open(output, 'rb+') as o:
- o.seek(0, os.SEEK_END)
- curr_off= o.tell()
- t.write('- Appending decompressed {}'.format(str(nca._path)))
- 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()
- reader = dctx.stream_reader(nca)
- 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
-
- 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 ot in self:
- if str(ot._path)==file:
- t.write('- Appending {}'.format(str(ot._path)))
- ot.rewind()
- data=ot.read()
- with open(output, 'ab') as o:
- o.write(data)
- t.update(len(data))
-
-
- def verify_ncz(self,target):
- files_list=sq_tools.ret_nsp_offsets(self._path)
- files=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]
- filepath=entry[0]
- if filepath.endswith('.cnmt.nca'):
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=self.get_data_from_cnmt(filepath)
- 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')
- if str(row['NcaId'])==target[:-4]:
- files.append(str(row['NcaId'])+'.nca')
- break
- for file in files:
- if file.endswith('nca'):
- for nca in self:
- if str(nca._path)[:-1]==file[:-1] and str(nca._path).endswith('ncz'):
- # print(nca._path)
- nca.rewind()
- header = nca.read(0x4000)
- magic = readInt64(nca)
- sectionCount = readInt64(nca)
- sections = []
- for i in range(sectionCount):
- sections.append(Section(nca))
- count=0;checkstarter=0
- if titleid.endswith('000'):
- for s in sections:
- count+=1
- if count==2:
- break
- # print(s.cryptoType)
- # print(s.size)
- checkstarter+=s.size
- # print(s.size)
- dctx = zstandard.ZstdDecompressor()
- reader = dctx.stream_reader(nca)
- test=int(checkstarter/(16384))
- for i in (range(test+1)):
- reader.seek(16384,1)
- chunk = reader.read(16384)
- b1=chunk[:32];# Hex.dump(b1)
- b2=chunk[32:64];# Hex.dump(b2)
- if sum(b1)!=0 and sum(b2)==0:
- return True
- else:
- return 'ncz'
- if not titleid.endswith('800'):
- dctx = zstandard.ZstdDecompressor()
- reader = dctx.stream_reader(nca)
- chunk = reader.read(16384)
- b1=chunk[:32];# Hex.dump(b1)
- b2=chunk[32:64];# Hex.dump(b2)
- if sum(b1)!=0 and sum(b2)==0:
- return True
- else:
- return 'ncz'
- elif titleid.endswith('800'):
- dctx = zstandard.ZstdDecompressor()
- reader = dctx.stream_reader(nca)
- chunk = reader.read(16384)
- b1=chunk[:32];# Hex.dump(b1)
- b2=chunk[32:64];# Hex.dump(b2)
- if sum(b1)!=0 and sum(b2)==0:
- return True
- else:
- return 'ncz'
-
-
- def nsz_hasher(self,buffer,headerlist,didverify,feed):
- buffer=int(buffer)
- verdict=True
- if feed == False:
- feed=''
- message='\n***************';print(message);feed+=message+'\n'
- message=('HASH TEST');print(message);feed+=message+'\n'
- message='***************';print(message);feed+=message+'\n'
- for f in self:
- if type(f) == Nca:
- origheader=False
- for i in range(len(headerlist)):
- if str(f._path)==headerlist[i][0]:
- origheader=headerlist[i][1]
- listedhash=headerlist[i][2]
- break
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));print(message);feed+=message+'\n'
- ncasize=f.header.size
- t = tqdm(total=ncasize, unit='B', unit_scale=True, leave=False)
- i=0
- f.rewind();
- rawheader=f.read(0xC00)
- f.rewind()
- for data in iter(lambda: f.read(int(buffer)), ""):
- if i==0:
- sha=sha256()
- f.seek(0xC00)
- sha.update(rawheader)
- if origheader != False and listedhash == False:
- sha0=sha256()
- sha0.update(origheader)
- i+=1
- t.update(len(data))
- f.flush()
- else:
- sha.update(data)
- if origheader != False and listedhash == False:
- sha0.update(data)
- t.update(len(data))
- f.flush()
- if not data:
- break
- t.close()
- sha=sha.hexdigest()
- if listedhash != False:
- sha0=listedhash
- elif origheader != False:
- sha0=sha0.hexdigest()
- message=(' - File name: '+f._path);print(message);feed+=message+'\n'
- message=(' - SHA256: '+sha);print(message);feed+=message+'\n'
- if origheader != False:
- message=(' - ORIG_SHA256: '+sha0);print(message);feed+=message+'\n'
- if str(f._path)[:16] == str(sha)[:16]:
- message=(' > FILE IS CORRECT');print(message);feed+=message+'\n'
- elif origheader != False:
- if str(f._path)[:16] == str(sha0)[:16]:
- message=(' > FILE IS CORRECT');print(message);feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');print(message);feed+=message+'\n'
- verdict = False
- elif f.header.contentType == Type.Content.META and didverify == True:
- message=(' > RSV WAS CHANGED');print(message);feed+=message+'\n'
- #print(' > CHECKING INTERNAL HASHES')
- message=(' * FILE IS CORRECT');print(message);feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');print(message);feed+=message+'\n'
- verdict = False
- message=('');print(message);feed+=message+'\n'
- if (f._path).endswith('ncz'):
- ncz=Nca(f)
- ncz._path=f._path
- origheader=False
- for i in range(len(headerlist)):
- if str(f._path)==headerlist[i][0]:
- origheader=headerlist[i][1]
- listedhash=headerlist[i][2]
- break
- message=(str(ncz.header.titleId)+' - '+str(ncz.header.contentType));print(message);feed+=message+'\n'
- ncasize=ncz.header.size
- t = tqdm(total=ncasize, unit='B', unit_scale=True, leave=False)
- i=0
- f.rewind();
- rawheader=f.read(0xC00)
- f.rewind()
- sha=sha256()
- f.seek(0xC00)
- sha.update(rawheader)
- if origheader != False and listedhash == False:
- sha0=sha256()
- sha0.update(origheader)
- i+=1
- t.update(len(rawheader))
- f.flush()
- size=0x4000-0xC00
- dif = f.read(size)
- sha.update(dif)
- if origheader != False and listedhash == False:
- sha0.update(dif)
- t.update(len(dif))
- f.flush()
- f.seek(0x4000)
- magic = readInt64(f)
- sectionCount = readInt64(f)
- sections = []
- for i in range(sectionCount):
- sections.append(Section(f))
- # print(sections)
- count=0;checkstarter=0
- dctx = zstandard.ZstdDecompressor()
- reader = dctx.stream_reader(f)
- c=0;spsize=0
- for s in sections:
- end = s.offset + s.size
- if s.cryptoType == 1: #plain text
- 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
- sha.update(chunk)
- if origheader != False and listedhash == False:
- sha0.update(chunk)
- t.update(len(chunk))
- i += chunkSz
- elif s.cryptoType not in (3, 4):
- raise IOError('Unknown crypto type: %d' % s.cryptoType)
- else:
- 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
- crpt=crypto.encrypt(chunk)
- sha.update(crpt)
- if origheader != False and listedhash == False:
- sha0.update(crpt)
- t.update(len(chunk))
- i += chunkSz
- t.close()
- sha=sha.hexdigest()
- if listedhash != False:
- sha0=listedhash
- elif origheader != False:
- sha0=sha0.hexdigest()
- message=(' - File name: '+ncz._path);print(message);feed+=message+'\n'
- message=(' - SHA256: '+sha);print(message);feed+=message+'\n'
- if origheader != False:
- message=(' - ORIG_SHA256: '+sha0);print(message);feed+=message+'\n'
- if str(ncz._path)[:16] == str(sha)[:16]:
- message=(' > FILE IS CORRECT');print(message);feed+=message+'\n'
- elif origheader != False:
- if str(ncz._path)[:16] == str(sha0)[:16]:
- message=(' > FILE IS CORRECT');print(message);feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');print(message);feed+=message+'\n'
- verdict = False
- elif ncz.header.contentType == Type.Content.META and didverify == True:
- message=(' > RSV WAS CHANGED');print(message);feed+=message+'\n'
- #print(' > CHECKING INTERNAL HASHES')
- message=(' * FILE IS CORRECT');print(message);feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');print(message);feed+=message+'\n'
- verdict = False
- message=('');print(message);feed+=message+'\n'
- if str(self.path).endswith('.nsz'):
- token='NSZ'
- elif str(self.path).endswith('.nsx'):
- token='NSX'
- elif str(self.path).endswith('.nsp'):
- token='NSP'
- else:
- token='NSP'
- if verdict == False:
- message=("VERDICT: {} FILE IS CORRUPT").format(token);print(message);feed+=message+'\n'
- if verdict == True:
- message=('VERDICT: {} FILE IS CORRECT').format(token);print(message);feed+=message+'\n'
- return verdict,feed
diff --git a/py/Fs/ChromeXci.py b/py/Fs/ChromeXci.py
deleted file mode 100644
index c639767c..00000000
--- a/py/Fs/ChromeXci.py
+++ /dev/null
@@ -1,8886 +0,0 @@
-from binascii import hexlify as hx, unhexlify as uhx
-from Fs.Hfs0 import Hfs0
-from Fs.Ticket import Ticket
-from Fs.Nca import Nca
-from Fs.File import File
-from Fs.Nca import NcaHeader
-from Fs.File import MemoryFile
-from Fs.pyNCA3 import NCA3
-from Fs.pyNPDM import NPDM
-import Fs.Type
-from Fs import Type
-from Fs.Nacp import Nacp
-from Fs.ChromeNacp import ChromeNacp
-import os
-import Print
-
-import Keys
-import aes128
-import Title
-import Titles
-import Hex
-import sq_tools
-from struct import pack as pk, unpack as upk
-from hashlib import sha256,sha1
-import re
-import pathlib
-import Config
-import Print
-from tqdm import tqdm
-import math
-from operator import itemgetter, attrgetter, methodcaller
-import shutil
-import DBmodule
-import io
-import nutdb
-import textwrap
-from PIL import Image
-import zstandard
-from Crypto.Cipher import AES
-from Crypto.Util import Counter
-import time
-
-MEDIA_SIZE = 0x200
-RSA_PUBLIC_EXPONENT = 65537
-HFS0_START = 0xf000
-XCI_SIGNATURE_SIZE = 0x100
-XCI_IV_SIZE = 0x10
-XCI_HASH_SIZE = 0x20
-XCI_GAMECARD_INFO_LENGTH = 0x70
-XCI_GAMECARD_INFO_PADDING_LENGTH = 0x38
-indent = 1
-tabs = '\t' * indent
-
-def readInt64(f, byteorder='little', signed = False):
- return int.from_bytes(f.read(8), byteorder=byteorder, signed=signed)
-
-def readInt128(f, byteorder='little', signed = False):
- return int.from_bytes(f.read(16), byteorder=byteorder, signed=signed)
-
-class AESCTR:
- def __init__(self, key, nonce, offset = 0):
- self.key = key
- self.nonce = nonce
- self.seek(offset)
-
- def encrypt(self, data, ctr=None):
- if ctr is None:
- ctr = self.ctr
- return self.aes.encrypt(data)
-
- def decrypt(self, data, ctr=None):
- return self.encrypt(data, ctr)
-
- def seek(self, offset):
- self.ctr = Counter.new(64, prefix=self.nonce[0:8], initial_value=(offset >> 4))
- self.aes = AES.new(self.key, AES.MODE_CTR, counter=self.ctr)
-
-class Section:
- def __init__(self, f):
- self.f = f
- self.offset = readInt64(f)
- self.size = readInt64(f)
- self.cryptoType = readInt64(f)
- readInt64(f) # padding
- self.cryptoKey = f.read(16)
- self.cryptoCounter = f.read(16)
-
-class GamecardInfo(File):
- def __init__(self, file = None):
- super(GamecardInfo, self).__init__()
- if file:
- self.open(file)
-
- def open(self, file, mode='rb', cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
- super(GamecardInfo, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.rewind()
- self.firmwareVersion = self.readInt64()
- self.accessControlFlags = self.readInt32()
- self.readWaitTime = self.readInt32()
- self.readWaitTime2 = self.readInt32()
- self.writeWaitTime = self.readInt32()
- self.writeWaitTime2 = self.readInt32()
- self.firmwareMode = self.readInt32()
- self.cupVersion = self.readInt32()
- self.empty1 = self.readInt32()
- self.updatePartitionHash = self.readInt64()
- self.cupId = self.readInt64()
- self.empty2 = self.read(0x38)
-
-class GamecardCertificate(File):
- def __init__(self, file = None):
- super(GamecardCertificate, self).__init__()
- self.signature = None
- self.magic = None
- self.unknown1 = None
- self.KekIndex = None
- self.unknown2 = None
- self.DeviceID = None
- self.unknown3 = None
- self.data = None
-
- if file:
- self.open(file)
-
- def open(self, file, mode = 'rb', cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
- super(GamecardCertificate, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
- self.rewind()
- self.signature = self.read(0x100)
- self.magic = self.read(0x4)
- self.unknown1 = self.read(0x4)
- self.KekIndex = self.read(0x1)
- self.unknown2 = self.read(0x7)
- self.DeviceID = self.read(0x10)
- self.unknown3 = self.read(0x10)
- self.data = self.read(0xD0)
-
-class ChromeXci(File):
- def __init__(self, file = None):
- super(ChromeXci, self).__init__()
- self.header = None
- self.signature = None
- self.magic = None
- self.secureOffset = None
- self.backupOffset = None
- self.titleKekIndex = None
- self.gamecardSize = None
- self.gamecardHeaderVersion = None
- self.gamecardFlags = None
- self.packageId = None
- self.validDataEndOffset = None
- self.gamecardInfo = None
- self.gamecardInfoIV = None
-
- self.hfs0Offset = None
- self.hfs0HeaderSize = None
- self.hfs0HeaderHash = None
- self.hfs0InitialDataHash = None
- self.secureMode = None
-
- self.titleKeyFlag = None
- self.keyFlag = None
- self.normalAreaEndOffset = None
-
- self.gamecardInfo = None
- self.gamecardCert = None
- self.hfs0 = None
-
- if file:
- self.open(file)
- self.path=file
-
- def readHeader(self):
-
- self.signature = self.read(0x100)
- self.magic = self.read(0x4)
- self.secureOffset = self.readInt32()
- self.backupOffset = self.readInt32()
- self.titleKekIndex = self.readInt8()
- self.gamecardSize = self.read(0x1)
- self.gamecardHeaderVersion = self.readInt8()
- self.gamecardFlags = self.readInt8()
- self.packageId = self.readInt64()
- self.validDataEndOffset = self.readInt64()
- self.gamecardInfo = self.read(0x10)
- self.gamecardInfoIV = self.gamecardInfo
-
- self.hfs0Offset = self.readInt64()
- self.hfs0HeaderSize = self.readInt64()
- self.hfs0HeaderHash = self.read(0x20)
- self.hfs0InitialDataHash = self.read(0x20)
- self.secureMode = self.readInt32()
-
- self.titleKeyFlag = self.readInt32()
- self.keyFlag = self.readInt32()
- self.normalAreaEndOffset = self.readInt32()
-
- self.gamecardInfo = GamecardInfo(self.partition(self.tell(), 0x70))
- self.gamecardCert = GamecardCertificate(self.partition(0x7000, 0x200))
-
-
- def open(self, path = None, mode = 'rb', cryptoType = -1, cryptoKey = -1, cryptoCounter = -1):
- r = super(ChromeXci, self).open(path, mode, cryptoType, cryptoKey, cryptoCounter)
- self.readHeader()
- self.seek(0xF000)
- self.hfs0 = Hfs0(None, cryptoKey = None)
- self.partition(0xf000, None, self.hfs0, cryptoKey = None)
-
- def unpack(self, path):
- os.makedirs(path, exist_ok=True)
-
- for nspF in self.hfs0:
- filePath = os.path.abspath(path + '/' + nspF._path)
- f = open(filePath, 'wb')
- nspF.rewind()
- i = 0
-
- pageSize = 0x10000
-
- while True:
- buf = nspF.read(pageSize)
- if len(buf) == 0:
- break
- i += len(buf)
- f.write(buf)
- f.close()
- Print.info(filePath)
-
- def printInfo(self):
- maxDepth = 3
- indent = 0
- tabs = '\t' * indent
- Print.info('\n%sXCI Archive\n' % (tabs))
- super(ChromeXci, self).printInfo(maxDepth, indent)
-
- Print.info(tabs + 'magic = ' + str(self.magic))
- Print.info(tabs + 'titleKekIndex = ' + str(self.titleKekIndex))
-
- Print.info(tabs + 'gamecardCert = ' + str(hx(self.gamecardCert.magic + self.gamecardCert.unknown1 +self.gamecardCert.KekIndex + self.gamecardCert.unknown2 +self.gamecardCert.DeviceID +self.gamecardCert.unknown3 + self.gamecardCert.data)))
-
- self.hfs0.printInfo( indent)
-
- def getxciid(self):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- titleid=str(nca.header.titleId)
- return titleid
-
- def copy_hfs0(self,ofolder,buffer,token):
- indent = 1
- tabs = '\t' * indent
- if token == "all":
- for nspF in self.hfs0:
- nspF.rewind()
- filename = str(nspF._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nspF.rewind()
- Print.info(tabs + 'Copying: ' + str(filename))
- for data in iter(lambda: nspF.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
- else:
- for nspF in self.hfs0:
- if token == str(nspF._path):
- nspF.rewind()
- filename = str(nspF._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nspF.rewind()
- Print.info(tabs + 'Copying: ' + str(filename))
- for data in iter(lambda: nspF.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
-
- def copy_ticket(self,ofolder,buffer,token):
- indent = 1
- tabs = '\t' * indent
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for ticket in nspF:
- if type(ticket) == Ticket:
- ticket.rewind()
- data = ticket.read()
- filename = str(ticket._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(str(filepath), 'w+b')
- fp.write(data)
- fp.flush()
-
- def copy_cnmt(self,ofolder,buffer):
- for nspF in self.hfs0:
- if "secure" == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for file in f:
- nca.rewind()
- f.rewind()
- file.rewind()
- filename = str(file._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- f.rewind()
- file.rewind()
- for data in iter(lambda:file.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
-
-
- def copy_other(self,ofolder,buffer,token):
- indent = 1
- tabs = '\t' * indent
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for file in self:
- if type(file) == File:
- file.rewind()
- filename = str(file._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- file.rewind()
- for data in iter(lambda: file.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
-
- def copy_root_hfs0(self,ofolder,buffer):
- indent = 1
- tabs = '\t' * indent
- target=self.hfs0
- target.rewind()
- filename = 'root.hfs0'
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- target.rewind()
- Print.info(tabs + 'Copying: ' + str(filename))
- for data in iter(lambda: target.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
-
-#Extract all files
- def extract_all(self,ofolder,buffer):
- indent = 1
- tabs = '\t' * indent
- print("Processing: "+str(self._path))
- for nspF in self.hfs0:
- if 'secure' == str(nspF._path):
- for file in nspF:
- file.rewind()
- filename = str(file._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- file.rewind()
- t = tqdm(total=file.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: file.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
-
-#Copy nca files from secure
- def copy_nca(self,ofolder,buffer,token,metapatch,keypatch,RSV_cap):
- if keypatch != 'false':
- keypatch = int(keypatch)
- indent = 1
- tabs = '\t' * indent
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if metapatch == 'true':
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs +'DLC -> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- else:
- self.patch_meta(filepath,outfolder,RSV_cap)
-
-
-
-#Copy nca files from secure skipping deltas
- def copy_nca_nd(self,ofolder,buffer,metapatch,keypatch,RSV_cap):
- if keypatch != 'false':
- keypatch = int(keypatch)
- indent = 1
- tabs = '\t' * indent
- Print.info('Copying files: ')
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- vfragment="false"
- if type(nca) == Nca:
- vfragment="false"
- if str(nca.header.contentType) == 'Content.DATA':
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- Print.info('Skipping delta fragment: ' + str(nca._path))
- continue
- else:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if metapatch == 'true':
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs +'DLC -> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- else:
- self.patch_meta(filepath,outfolder,RSV_cap)
-
-
-#COPY AND CLEAN NCA FILES FROM SECURE AND PATCH NEEDED SYSTEM VERSION
- def cr_tr_nca(self,ofolder,buffer,metapatch,keypatch,RSV_cap):
- if keypatch != 'false':
- keypatch = int(keypatch)
- indent = 1
- tabs = '\t' * indent
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for ticket in nspF:
- if type(ticket) == Ticket:
- masterKeyRev = ticket.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = ticket.getRightsId()
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- tik=ticket
- for nca in nspF:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(nca._path)} - {nca.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
- for nca in nspF:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.getCryptoType2() == 0:
- if nca.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(tik.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
-
- for nca in nspF:
- vfragment="false"
- if type(nca) == Nca:
- Print.info('Copying files: ')
- if nca.header.getRightsId() != 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- target = Nca(filepath, 'r+b')
- target.rewind()
- Print.info(tabs + 'Removing titlerights for ' + str(filename))
- Print.info(tabs + 'Writing masterKeyRev for %s, %d' % (str(nca._path), masterKeyRev))
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- target.header.setRightsId(0)
- target.header.setKeyBlock(encKeyBlock)
- Hex.dump(encKeyBlock)
- target.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if metapatch == 'true':
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs +'DLC -> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- else:
- self.patch_meta(filepath,outfolder,RSV_cap)
-
-
-
-#COPY AND CLEAN NCA FILES FROM SECURE SKIPPING DELTAS AND PATCH NEEDED SYSTEM VERSION
- def cr_tr_nca_nd(self,ofolder,buffer,metapatch,keypatch,RSV_cap):
- if keypatch != 'false':
- keypatch = int(keypatch)
- indent = 1
- tabs = '\t' * indent
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for ticket in nspF:
- if type(ticket) == Ticket:
- masterKeyRev = ticket.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = ticket.getRightsId()
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- tik=ticket
- for nca in nspF:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(nca._path)} - {nca.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
- for nca in nspF:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.getCryptoType2() == 0:
- if nca.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(tik.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
-
- for nca in nspF:
- vfragment="false"
- if type(nca) == Nca:
- vfragment="false"
- if str(nca.header.contentType) == 'Content.DATA':
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- Print.info('Skipping delta fragment: ' + str(nca._path))
- continue
- else:
- Print.info('Copying files: ')
- if nca.header.getRightsId() != 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- Print.info(tabs + 'Copying: ' + str(filename))
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- target = Nca(filepath, 'r+b')
- target.rewind()
- Print.info(tabs + 'Removing titlerights for ' + str(filename))
- Print.info(tabs + 'Writing masterKeyRev for %s, %d' % (str(nca._path), masterKeyRev))
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- target.header.setRightsId(0)
- target.header.setKeyBlock(encKeyBlock)
- Hex.dump(encKeyBlock)
- target.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- Print.info(tabs + 'Copying: ' + str(filename))
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if metapatch == 'true':
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs +'DLC -> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- else:
- self.patch_meta(filepath,outfolder,RSV_cap)
-
-#///////////////////////////////////////////////////
-# Change MKREV_NCA
-#///////////////////////////////////////////////////
-
- def change_mkrev_nca(self, nca, newMasterKeyRev):
-
- indent = 2
- tabs = '\t' * indent
- indent2 = 3
- tabs2 = '\t' * indent2
-
- masterKeyRev = nca.header.getCryptoType2()
-
- if type(nca) == Nca:
- if nca.header.getCryptoType2() != newMasterKeyRev:
- Print.info(tabs + '-----------------------------------')
- Print.info(tabs + 'Changing keygeneration from %d to %s' % ( nca.header.getCryptoType2(), str(newMasterKeyRev)))
- Print.info(tabs + '-----------------------------------')
- encKeyBlock = nca.header.getKeyBlock()
- if sum(encKeyBlock) != 0:
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)
- Print.info(tabs2 + '+ decrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- crypto = aes128.AESECB(key)
- decKeyBlock = crypto.decrypt(encKeyBlock)
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex)
- Print.info(tabs2 + '+ encrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex))
- crypto = aes128.AESECB(key)
- reEncKeyBlock = crypto.encrypt(decKeyBlock)
- nca.header.setKeyBlock(reEncKeyBlock)
- if newMasterKeyRev >= 3:
- nca.header.setCryptoType(2)
- nca.header.setCryptoType2(newMasterKeyRev)
- if newMasterKeyRev == 2:
- nca.header.setCryptoType(2)
- nca.header.setCryptoType2(0)
- else:
- nca.header.setCryptoType(newMasterKeyRev)
- nca.header.setCryptoType2(0)
- Print.info(tabs2 + 'DONE')
-
-#///////////////////////////////////////////////////
-#PATCH META FUNCTION
-#///////////////////////////////////////////////////
- def patch_meta(self,filepath,outfolder,RSV_cap):
- RSV_cap=int(RSV_cap)
- indent = 1
- tabs = '\t' * indent
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs + 'Checking meta: ')
- meta_nca = Fs.Nca(filepath, 'r+b')
- crypto1=meta_nca.header.getCryptoType()
- crypto2=meta_nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=meta_nca.header.getCryptoType()
- else:
- keygen=meta_nca.header.getCryptoType2()
- else:
- keygen=meta_nca.header.getCryptoType2()
- RSV=meta_nca.get_req_system()
- RSVmin=sq_tools.getMinRSV(keygen,RSV)
- RSVmax=sq_tools.getTopRSV(keygen,RSV)
- if RSV > RSVmin:
- if RSVmin >= RSV_cap:
- meta_nca.write_req_system(RSVmin)
- else:
- if keygen < 4:
- if RSV > RSVmax:
- meta_nca.write_req_system(RSV_cap)
- else:
- meta_nca.write_req_system(RSV_cap)
- meta_nca.flush()
- meta_nca.close()
- Print.info(tabs + 'Updating cnmt hashes: ')
- ############################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha=meta_nca.calc_pfs0_hash()
- Print.info(tabs + '- Calculated hash from pfs0: ')
- Print.info(tabs +' + '+ str(hx(sha)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.set_pfs0_hash(sha)
- meta_nca.flush()
- meta_nca.close()
- ############################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha2=meta_nca.calc_htable_hash()
- Print.info(tabs + '- Calculated table hash: ')
- Print.info(tabs +' + '+ str(hx(sha2)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.header.set_htable_hash(sha2)
- meta_nca.flush()
- meta_nca.close()
- ########################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha3=meta_nca.header.calculate_hblock_hash()
- Print.info(tabs + '- Calculated header block hash: ')
- Print.info(tabs +' + '+ str(hx(sha2)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.header.set_hblock_hash(sha3)
- meta_nca.flush()
- meta_nca.close()
- ########################
- '''
- with open(filepath, 'r+b') as file:
- nsha=sha256(file.read()).hexdigest()
- newname=nsha[:32] + '.cnmt.nca'
- Print.info(tabs +'New name: ' + newname )
- dir=os.path.dirname(os.path.abspath(filepath))
- newpath=dir+ '/' + newname
- os.rename(filepath, newpath)
- Print.info(tabs + '-------------------------------------')
- else:
- Print.info(tabs +'-> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- '''
-
-#Check if titlerights
- def trights_set(self):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- return 'TRUE'
- return 'FALSE'
-
-#Check if exists ticket
- def exist_ticket(self):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for ticket in nspF:
- if type(ticket) == Ticket:
- return 'TRUE'
- return 'FALSE'
-
-#READ NACP FILE WITHOUT EXTRACTION
- def read_nacp(self,feed='',gui=False):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- titleid2=nca.header.titleId
- feed=self.html_feed(feed,1,message=str('TITLEID: ' + str(titleid2).upper()))
- offset=nca.get_nacp_offset()
- for f in nca:
- f.seek(offset)
- nacp = ChromeNacp()
- feed=nacp.par_getNameandPub(f.read(0x300*15),feed,gui)
- feed=self.html_feed(feed,2,message=str("Nacp Flags:"))
- feed+=''
- f.seek(offset+0x3000)
- feed=nacp.par_Isbn(f.read(0x24),feed)
- f.seek(offset+0x3025)
- feed=nacp.par_getStartupUserAccount(f.readInt8('little'),feed)
- feed=nacp.par_getUserAccountSwitchLock(f.readInt8('little'),feed)
- feed=nacp.par_getAddOnContentRegistrationType(f.readInt8('little'),feed)
- feed=nacp.par_getContentType(f.readInt8('little'),feed)
- f.seek(offset+0x3030)
- feed=nacp.par_getParentalControl(f.readInt8('little'),feed)
- f.seek(offset+0x3034)
- feed=nacp.par_getScreenshot(f.readInt8('little'),feed)
- feed=nacp.par_getVideoCapture(f.readInt8('little'),feed)
- feed=nacp.par_dataLossConfirmation(f.readInt8('little'),feed)
- feed=nacp.par_getPlayLogPolicy(f.readInt8('little'),feed)
- f.seek(offset+0x3038)
- feed=nacp.par_getPresenceGroupId(f.readInt64('little'),feed)
- feed+=' '
- f.seek(offset+0x3040)
- listages=list()
- feed=self.html_feed(feed,2,message=str("Age Ratings:"))
- feed+=''
- for i in range(12):
- feed=nacp.par_getRatingAge(f.readInt8('little'),i,feed)
- feed+=' '
- f.seek(offset+0x3060)
- try:
- feed=self.html_feed(feed,2,message=str("Nacp Atributes:"))
- feed+=''
- feed=nacp.par_getDisplayVersion(f.read(0xF),feed)
- f.seek(offset+0x3070)
- feed=nacp.par_getAddOnContentBaseId(f.readInt64('little'),feed)
- f.seek(offset+0x3078)
- feed=nacp.par_getSaveDataOwnerId(f.readInt64('little'),feed)
- f.seek(offset+0x3080)
- feed=nacp.par_getUserAccountSaveDataSize(f.readInt64('little'),feed)
- f.seek(offset+0x3088)
- feed=nacp.par_getUserAccountSaveDataJournalSize(f.readInt64('little'),feed)
- f.seek(offset+0x3090)
- feed=nacp.par_getDeviceSaveDataSize(f.readInt64('little'),feed)
- f.seek(offset+0x3098)
- feed=nacp.par_getDeviceSaveDataJournalSize(f.readInt64('little'),feed)
- f.seek(offset+0x30A0)
- feed=nacp.par_getBcatDeliveryCacheStorageSize(f.readInt64('little'),feed)
- f.seek(offset+0x30A8)
- feed=nacp.par_getApplicationErrorCodeCategory(f.read(0x07),feed)
- f.seek(offset+0x30B0)
- feed=nacp.par_getLocalCommunicationId(f.readInt64('little'),feed)
- f.seek(offset+0x30F0)
- feed=nacp.par_getLogoType(f.readInt8('little'),feed)
- feed=nacp.par_getLogoHandling(f.readInt8('little'),feed)
- feed=nacp.par_getRuntimeAddOnContentInstall(f.readInt8('little'),feed)
- f.seek(offset+0x30F6)
- feed=nacp.par_getCrashReport(f.readInt8('little'),feed)
- feed=nacp.par_getHdcp(f.readInt8('little'),feed)
- feed=nacp.par_getSeedForPseudoDeviceId(f.readInt64('little'),feed)
- f.seek(offset+0x3100)
- feed=nacp.par_getBcatPassphrase(f.read(0x40),feed)
- f.seek(offset+0x3148)
- feed=nacp.par_UserAccountSaveDataSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3150)
- feed=nacp.par_UserAccountSaveDataJournalSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3158)
- feed=nacp.par_getDeviceSaveDataSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3160)
- feed=nacp.par_getDeviceSaveDataJournalSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3168)
- feed=nacp.par_getTemporaryStorageSize(f.readInt64('little'),feed)
- feed=nacp.par_getCacheStorageSize(f.readInt64('little'),feed)
- f.seek(offset+0x3178)
- feed=nacp.par_getCacheStorageJournalSize(f.readInt64('little'),feed)
- feed=nacp.par_getCacheStorageDataAndJournalSizeMax(f.readInt64('little'),feed)
- f.seek(offset+0x3188)
- feed=nacp.par_getCacheStorageIndexMax(f.readInt64('little'),feed)
- f.seek(offset+0x3188)
- feed=nacp.par_getPlayLogQueryableApplicationId(f.readInt64('little'),feed)
- f.seek(offset+0x3210)
- feed=nacp.par_getPlayLogQueryCapability(f.readInt8('little'),feed)
- feed=nacp.par_getRepair(f.readInt8('little'),feed)
- feed=nacp.par_getProgramIndex(f.readInt8('little'),feed)
- feed=nacp.par_getRequiredNetworkServiceLicenseOnLaunch(f.readInt8('little'),feed)
- feed+=' '
- except:
- feed+=''
- continue
- return feed
-
- def read_npdm(self,files_list,buffer=32768):
- feed=''
- try:
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Fs.Nca and nca.header.getRightsId() == 0:
- if str(nca.header.contentType) == 'Content.PROGRAM':
- titleid2=nca.header.titleId
- feed=self.html_feed(feed,1,message=str('TITLEID: ' + str(titleid2).upper()))
- feed+=' '
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- #print(offset)
- break
- decKey=nca.header.titleKeyDec
- try:
- fp=open(str(self._path), 'rb')
- nca3=NCA3(fp,int(offset),str(nca._path),decKey)
- feed+=nca3.print_npdm()
- fp.close();
- feed+='
'
- return feed
- except BaseException as e:
- #Print.error('Exception: ' + str(e))
- nca.rewind()
- for f in nca:
- for g in f:
- if str(g._path)=='main.npdm':
- inmemoryfile = io.BytesIO(g.read())
- npdm = NPDM(inmemoryfile)
- n=npdm.__str__()
- feed+=n
- feed+=''
- return feed
- if type(nca) == Fs.Nca and nca.header.getRightsId() != 0:
- if str(nca.header.contentType) == 'Content.PROGRAM':
- titleid2=nca.header.titleId
- feed=self.html_feed(feed,1,message=str('TITLEID: ' + str(titleid2).upper()))
- feed+=''
- correct, tkey = self.verify_nca_key(str(nca._path))
- if correct == True:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- decKey = Keys.decryptTitleKey(tkey, Keys.getMasterKeyIndex(int(masterKeyRev)))
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- #print(offset)
- break
- try:
- fp=open(str(self._path), 'rb')
- nca3=NCA3(fp,int(offset),str(nca._path),decKey)
- feed+=nca3.print_npdm()
- fp.close();
- feed+='
'
- return feed
- except BaseException as e:
- #Print.error('Exception: ' + str(e))
- nca.rewind()
- for fs in nca.sectionFilesystems:
- #print(fs.fsType)
- #print(fs.cryptoType)
- if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR:
- nca.seek(0)
- ncaHeader = NcaHeader()
- ncaHeader.open(MemoryFile(nca.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key'))))
- ncaHeader.seek(0)
- fs.rewind()
- pfs0=fs
- sectionHeaderBlock = fs.buffer
- nca.seek(fs.offset)
- pfs0Offset=fs.offset
- pfs0Header = nca.read(0x10*30)
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- head=data[0:4]
- n_files=(data[4:8])
- n_files=int.from_bytes(n_files, byteorder='little')
- st_size=(data[8:12])
- st_size=int.from_bytes(st_size, byteorder='little')
- junk=(data[12:16])
- offset=(0x10 + n_files * 0x18)
- stringTable=(data[offset:offset+st_size])
- stringEndOffset = st_size
- headerSize = 0x10 + 0x18 * n_files + st_size
- #print(head)
- if head!=b'PFS0':
- feed=self.html_feed(feed,2,message=str('- Error decrypting npdm'))
- continue
- #print(str(n_files))
- #print(str(st_size))
- #print(str((stringTable)))
- files_list=list()
- for i in range(n_files):
- i = n_files - i - 1
- pos=0x10 + i * 0x18
- offset = data[pos:pos+8]
- offset=int.from_bytes(offset, byteorder='little')
- size = data[pos+8:pos+16]
- size=int.from_bytes(size, byteorder='little')
- nameOffset = data[pos+16:pos+20] # just the offset
- nameOffset=int.from_bytes(nameOffset, byteorder='little')
- name = stringTable[nameOffset:stringEndOffset].decode('utf-8').rstrip(' \t\r\n\0')
- stringEndOffset = nameOffset
- junk2 = data[pos+20:pos+24] # junk data
- #print(name)
- #print(offset)
- #print(size)
- files_list.append([name,offset,size])
- files_list.reverse()
- #print(files_list)
- for i in range(len(files_list)):
- if files_list[i][0] == 'main.npdm':
- off1=files_list[i][1]+pfs0Offset+headerSize
- nca.seek(off1)
- np=nca.read(files_list[i][2])
- mem = MemoryFile(np, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = off1)
- data = mem.read();
- #Hex.dump(data)
- inmemoryfile = io.BytesIO(data)
- npdm = NPDM(inmemoryfile)
- n=npdm.__str__()
- feed+=n
- feed+=''
- return feed
- break
- return feed
- except:
- feed=self.html_feed(feed,2,message=str('- Error decrypting npdm'))
- return feed
-
- def read_buildid(self,target=None):
- iscorrect=False
- ModuleId='';BuildID8='';BuildID16=''
- files_list=sq_tools.ret_xci_offsets(self._path)
- # print(files_list)
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Fs.Nca:
- if str(nca.header.contentType) == 'Content.PROGRAM':
- if target==None:
- target=str(nca._path)
- if str(nca._path)==target:
- if nca.header.getRightsId() == 0:
- decKey=nca.header.titleKeyDec
- if nca.header.getRightsId() != 0:
- correct, tkey = self.verify_nca_key(str(nca._path))
- if correct == True:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- decKey = Keys.decryptTitleKey(tkey, Keys.getMasterKeyIndex(int(masterKeyRev)))
- else:
- decKey=nca.header.titleKeyDec
-
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- # print(offset)
- break
- nca.rewind()
- for fs in nca.sectionFilesystems:
- #print(fs.fsType)
- #print(fs.cryptoType)
- if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR:
- nca.seek(0)
- ncaHeader = NcaHeader()
- ncaHeader.open(MemoryFile(nca.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key'))))
- ncaHeader.seek(0)
- fs.rewind()
- pfs0=fs
- sectionHeaderBlock = fs.buffer
- nca.seek(fs.offset)
- pfs0Offset=fs.offset
- pfs0Header = nca.read(0x10*30)
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- head=data[0:4]
- n_files=(data[4:8])
- n_files=int.from_bytes(n_files, byteorder='little')
- st_size=(data[8:12])
- st_size=int.from_bytes(st_size, byteorder='little')
- junk=(data[12:16])
- offset=(0x10 + n_files * 0x18)
- stringTable=(data[offset:offset+st_size])
- stringEndOffset = st_size
- headerSize = 0x10 + 0x18 * n_files + st_size
- #print(head)
- if head!=b'PFS0':
- continue
- #print(str(n_files))
- #print(str(st_size))
- #print(str((stringTable)))
- files_list=list()
- for i in range(n_files):
- i = n_files - i - 1
- pos=0x10 + i * 0x18
- offset = data[pos:pos+8]
- offset=int.from_bytes(offset, byteorder='little')
- size = data[pos+8:pos+16]
- size=int.from_bytes(size, byteorder='little')
- nameOffset = data[pos+16:pos+20] # just the offset
- nameOffset=int.from_bytes(nameOffset, byteorder='little')
- name = stringTable[nameOffset:stringEndOffset].decode('utf-8').rstrip(' \t\r\n\0')
- stringEndOffset = nameOffset
- junk2 = data[pos+20:pos+24] # junk data
- #print(name)
- #print(offset)
- #print(size)
- files_list.append([name,offset,size])
- files_list.reverse()
- # print(files_list)
- for i in range(len(files_list)):
- if files_list[i][0] == 'main':
- off1=files_list[i][1]+pfs0Offset+headerSize
- nca.seek(off1)
- np=nca.read(0x60)
- mem = MemoryFile(np, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = off1)
- magic=mem.read(0x4)
- mem.rewind()
- Hex.dump(mem.read())
- if magic==b'NSO0':
- mem.seek(0x40)
- data = mem.read(0x20);
- ModuleId=(str(hx(data)).upper())[2:-1]
- BuildID8=(str(hx(data[:8])).upper())[2:-1]
- BuildID16=(str(hx(data[:16])).upper())[2:-1]
- iscorrect=True;
- break
- break
- if iscorrect==False:
- try:
- from nutFS.Nca import Nca as nca3type
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Fs.Nca:
- if str(nca.header.contentType) == 'Content.PROGRAM':
- if target==None or str(nca._path)==target:
- nca3type=Nca(nca)
- nca3type._path=nca._path
- ModuleId=str(nca3type.buildId)
- BuildID8=ModuleId[:8]
- BuildID16=ModuleId[:16]
- except:
- ModuleId='';BuildID8='';BuildID16='';
- return ModuleId,BuildID8,BuildID16
-
- def copy_as_plaintext(self,ofolder,files_list,buffer=32768):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- tk=None;skip=False
- if type(nca) == Nca:
- for fs in nca.sectionFilesystems:
- if fs.cryptoType == Type.Crypto.BKTR:
- skip=True
- break
- if nca.header.getRightsId() != 0:
- correct, tkey = self.verify_nca_key(str(nca._path))
- if correct == True:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- tk = Keys.decryptTitleKey(tkey, Keys.getMasterKeyIndex(int(masterKeyRev)))
- else:
- tk=nca.header.titleKeyDec
- if skip == False:
- ncaname = str(nca._path)
- PN = os.path.join(ofolder,ncaname)
- if not os.path.exists(ofolder):
- os.makedirs(ofolder)
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- #print(offset)
- break
- #print(nca.size)
- #print(str(nca._path)[-9:])
- lon=0;test=str(nca._path)[-9:]
- if test=='.cnmt.nca':
- ext='.plain.cnmt.nca'
- else:
- ext='.plain.nca'
- lon=(-1)*len(ext)
- try:
- fp=open(str(self._path), 'rb')
- nca3=NCA3(fp,int(offset),str(nca._path),tk)
- nca3.decrypt_to_plaintext(PN.replace(str(nca._path)[lon:], ext))
- fp.close();
- except BaseException as e:
- #Print.error('Exception: ' + str(e))
- if nca.sizecrypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- tk = Keys.decryptTitleKey(tkey, Keys.getMasterKeyIndex(int(masterKeyRev)))
- else:
- tk=nca.header.titleKeyDec
- if skip == False:
- if type(nca) == Nca:
- ncaname = str(nca._path)[:-4]+'_nca'
- ncafolder = os.path.join(ofolder,ncaname)
- ncaname2 = str(nca._path)
- PN = os.path.join(ofolder,ncaname2)
- if not os.path.exists(ncafolder):
- os.makedirs(ncafolder)
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- break
- #t = tqdm(total=nca.size, unit='B', unit_scale=True, leave=False)
- try:
- fp=open(str(self._path), 'rb')
- nca3=NCA3(fp,int(offset),str(nca._path),tk,buffer)
- nca3.extract_conts(ncafolder, disp=True)
- fp.close()
- except:
- #Print.error('Exception: ' + str(e))
- if nca.size0:
- feed=self.html_feed(feed,2,message=str('Extended meta:'))
- num_prev_cnmt=cnmt.read(0x4)
- num_prev_delta=cnmt.read(0x4)
- num_delta_info=cnmt.read(0x4)
- num_delta_application =cnmt.read(0x4)
- num_previous_content=cnmt.read(0x4)
- num_delta_content=cnmt.read(0x4)
- cnmt.read(0x4)
- message=["Number of previous cnmt entries:",(str(int.from_bytes(num_prev_cnmt, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Number of previous delta entries:",(str(int.from_bytes(num_prev_delta, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Number of previous content entries:",(str(int.from_bytes(num_previous_content, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Number of delta content entries:",(str(int.from_bytes(num_delta_content, byteorder='little')))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_prev_cnmt, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Previous cnmt records: '+ str(i+1)))
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- type_n = cnmt.read(0x1)
- unknown1=cnmt.read(0x3)
- vhash = cnmt.read(0x20)
- unknown2=cnmt.read(0x2)
- unknown3=cnmt.read(0x2)
- unknown4=cnmt.read(0x4)
- message=["Titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["Version:",(str(int.from_bytes(titleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Content Type:",(sq_tools.cnmt_type(type_n))];feed=self.html_feed(feed,3,message)
- message=["Hash:",(str(hx(vhash))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(unknown2, byteorder='little'))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_prev_delta, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Previous delta records: '+ str(i+1)))
- oldtitleid=cnmt.readInt64()
- newtitleid=cnmt.readInt64()
- oldtitleversion = cnmt.read(0x4)
- newtitleversion = cnmt.read(0x4)
- size = cnmt.read(0x8)
- unknown1=cnmt.read(0x8)
- message=["Old titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["New titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["Old version:",(str(int.from_bytes(oldtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["New version:",(str(int.from_bytes(newtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_delta_info, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Delta info: '+ str(i+1)))
- oldtitleid=cnmt.readInt64()
- newtitleid=cnmt.readInt64()
- oldtitleversion = cnmt.read(0x4)
- newtitleversion = cnmt.read(0x4)
- index1=cnmt.readInt64()
- index2=cnmt.readInt64()
- message=["Old titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["New titleid:",(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1].upper())];feed=self.html_feed(feed,3,message)
- message=["Old version:",(str(int.from_bytes(oldtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["New version:",(str(int.from_bytes(newtitleversion, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Index1:",(str(hx(index1.to_bytes(8, byteorder='big'))))];feed=self.html_feed(feed,3,message)
- message=["Index2:",(str(hx(index2.to_bytes(8, byteorder='big'))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_delta_application, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Delta application info: '+ str(i+1)))
- OldNcaId = cnmt.read(0x10)
- NewNcaId = cnmt.read(0x10)
- old_size = cnmt.read(0x6)
- up2bytes = cnmt.read(0x2)
- low4bytes = cnmt.read(0x4)
- unknown1 = cnmt.read(0x2)
- ncatype = cnmt.read(0x1)
- installable = cnmt.read(0x1)
- unknown2 = cnmt.read(0x4)
- message=["OldNcaId:",(str(hx(OldNcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["NewNcaId:",(str(hx(NewNcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Old size:",(str(sq_tools.getSize(int.from_bytes(old_size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- message=["Unknown1:",(str(int.from_bytes(unknown1, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- message=["Installable:",(str(int.from_bytes(installable, byteorder='little')))];feed=self.html_feed(feed,3,message)
- message=["Upper 2 bytes of the new size:",(str(hx(up2bytes)))];feed=self.html_feed(feed,3,message)
- message=["Lower 4 bytes of the new size:",(str(hx(low4bytes)))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_previous_content, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Previous content records: '+ str(i+1)))
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown1 = cnmt.read(0x1)
- message=["NcaId:",(str(hx(NcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- for i in range(int.from_bytes(num_delta_content, byteorder='little')):
- feed=self.html_feed(feed,2,message=str('Delta content entry: '+ str(i+1)))
- vhash = cnmt.read(0x20)
- message=["Hash:",(str(hx(vhash))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- NcaId = cnmt.read(0x10)
- message=["NcaId:",(str(hx(NcaId))[2:-1]).upper()];feed=self.html_feed(feed,3,message)
- size = cnmt.read(0x6)
- message=["Size:",(str(sq_tools.getSize(int.from_bytes(size, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- ncatype = cnmt.read(0x1)
- message=["Ncatype:",(sq_tools.getmetacontenttype(str(int.from_bytes(ncatype, byteorder='little', signed=True))))];feed=self.html_feed(feed,3,message)
- IdOffset = cnmt.read(0x1)
- message=["IdOffset:",(str(int.from_bytes(IdOffset, byteorder='little', signed=True)))];feed=self.html_feed(feed,3,message)
- return feed
-
-
-#///////////////////////////////////////////////////
-#SPLIT MULTI-CONTENT XCI IN FOLDERS
-#///////////////////////////////////////////////////
- def splitter_read(self,ofolder,buffer,pathend):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=self.readInt32()
- end_of_emeta=self.readInt32()
- target=str(nca._path)
- contentname = self.splitter_get_title(target,offset,content_entries,original_ID)
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- Print.info('-------------------------------------')
- Print.info('Detected content: ' + str(titleid2))
- Print.info('-------------------------------------')
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- version='[v'+version+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- ofolder2 = ofolder+ '/'+contentname+' '+ titleid3+' '+version+'/'+pathend
- self.splitter_copy(ofolder2,buffer,nca_name)
- nca_meta=str(nca._path)
- self.splitter_copy(ofolder2,buffer,nca_meta)
- self.splitter_tyc(ofolder2,titleid2)
- dirlist=os.listdir(ofolder)
- textpath = os.path.join(ofolder, 'dirlist.txt')
- with open(textpath, 'a') as tfile:
- for folder in dirlist:
- item = os.path.join(ofolder, folder)
- tfile.write(item + '\n')
-
-
- indent = 1
- tabs = '\t' * indent
- token='secure'
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- if nca_name == str(nca._path):
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- Print.info(tabs + 'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
-
- def splitter_copy(self,ofolder,buffer,nca_name):
- indent = 1
- tabs = '\t' * indent
- token='secure'
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- if nca_name == str(nca._path):
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
-
- def splitter_tyc(self,ofolder,titleid):
- indent = 1
- tabs = '\t' * indent
- token='secure'
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for ticket in nspF:
- if type(ticket) == Ticket:
- tik_id = str(ticket._path)
- tik_id =tik_id[:-20]
- if titleid == tik_id:
- ticket.rewind()
- data = ticket.read()
- filename = str(ticket._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(str(filepath), 'w+b')
- Print.info(tabs + 'Copying: ' + str(filename))
- fp.write(data)
- fp.flush()
- fp.close()
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for cert in nspF:
- if cert._path.endswith('.cert'):
- cert_id = str(cert._path)
- cert_id =cert_id[:-21]
- if titleid == cert_id:
- cert.rewind()
- data = cert.read()
- filename = str(cert._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(str(filepath), 'w+b')
- Print.info(tabs + 'Copying: ' + str(filename))
- fp.write(data)
- fp.flush()
- fp.close()
-
- def splitter_get_title(self,target,offset,content_entries,original_ID):
- content_type=''
- token='secure'
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- if target == str(nca._path):
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- nca_name='false'
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- ncatype2 = int.from_bytes(ncatype, byteorder='little')
- if ncatype2 == 3:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- content_name=str(cnmt._path)
- content_name=content_name[:-22]
- if content_name == 'Patch':
- content_type=' [UPD]'
- if nca_name=='false':
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- cnmt.rewind()
- testID=cnmt.readInt64()
- if testID == original_ID:
- nca.rewind()
- f.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=self.readInt32()
- end_of_emeta=self.readInt32()
- target=str(nca._path)
- contentname = self.splitter_get_title(target,offset,content_entries,original_ID)
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- ncatype2 = int.from_bytes(ncatype, byteorder='little')
- if ncatype2 == 3:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- content_type=' [DLC]'
- title='DLC'
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- if nca_name == str(nca._path):
- for f in nca:
- nca.rewind()
- f.rewind()
- Langue = list()
- Langue = [0,1,6,5,7,10,3,4,9,8,2,11,12,13,14]
- for i in Langue:
- f.seek(0x14200+i*0x300)
- title = f.read(0x200)
- title = title.split(b'\0', 1)[0].decode('utf-8')
- title = (re.sub(r'[\/\\\:\*\?\!\"\<\>\|\.\s™©®()\~]+', ' ', title))
- title = title.strip()
- if title == "":
- title = 'DLC'
- if title != 'DLC':
- title = title + content_type
- return(title)
- return(title)
-
-
-
-#///////////////////////////////////////////////////
-#PREPARE BASE CONTENT TO UPDATE IT
-#///////////////////////////////////////////////////
- def updbase_read(self,ofolder,buffer,cskip,metapatch, keypatch,RSV_cap):
- indent = 1
- rightsId = 0
- tabs = '\t' * indent
- titleKeyDec=0x00*10
-
- if keypatch != 'false':
- try:
- keypatch = int(keypatch)
- except:
- print("New keygeneration is no valid integer")
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Ticket:
- masterKeyRev = file.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = file.getRightsId()
- ticket=file
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Nca:
- if file.header.getRightsId() != 0:
- if file.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(file._path)} - {file.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Nca:
- if file.header.getRightsId() != 0:
- if file.header.getCryptoType2() == 0:
- if file.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
-
- Print.info('Reading Base XCI:')
- if rightsId !=0:
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- print("")
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- content_name=str(cnmt._path)
- content_name=content_name[:-22]
- if content_name == 'Patch':
- if cskip == 'upd':
- continue
- if cskip == 'both':
- continue
- if content_name == 'AddOnContent':
- if cskip == 'dlc':
- continue
- if cskip == 'both':
- continue
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=self.readInt32()
- end_of_emeta=self.readInt32()
- target=str(nca._path)
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- Print.info('-------------------------------------')
- Print.info('Copying content: ' + str(titleid2))
- Print.info('-------------------------------------')
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- version='[v'+version+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- self.updbase_copy(ofolder,buffer,nca_name,metapatch, keypatch,RSV_cap,titleKeyDec)
- nca_meta=str(nca._path)
- self.updbase_copy(ofolder,buffer,nca_meta,metapatch, keypatch,RSV_cap,titleKeyDec)
- #self.updbase_tyc(ofolder,titleid2)
-
- def updbase_copy(self,ofolder,buffer,nca_name,metapatch, keypatch,RSV_cap,titleKeyDec):
- indent = 1
- tabs = '\t' * indent
- token='secure'
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- if nca_name == str(nca._path):
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- if nca.header.getRightsId() != 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- Print.info(tabs)
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs + '-> Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- Print.info(tabs + 'Removing titlerights for ' + str(filename))
- Print.info(tabs + 'Writing masterKeyRev for %s, %d' % (str(nca._path), masterKeyRev))
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- target.header.setRightsId(0)
- target.header.setKeyBlock(encKeyBlock)
- Hex.dump(encKeyBlock)
- Print.info('')
- target.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- filename = str(nca._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- Print.info(tabs)
- t = tqdm(total=nca.header.size, unit='B', unit_scale=True, leave=False)
- t.write(tabs + '-> Copying: ' + str(filename))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- t.close()
- break
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- self.change_mkrev_nca(target, keypatch)
- target.close()
- #///////////////////////////////////
- if metapatch == 'true':
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- Print.info(tabs + '-------------------------------------')
- Print.info(tabs +'DLC -> No need to patch the meta' )
- Print.info(tabs + '-------------------------------------')
- else:
- self.patch_meta(filepath,outfolder,RSV_cap)
-
- def updbase_tyc(self,ofolder,titleid):
- indent = 1
- tabs = '\t' * indent
- token='secure'
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for ticket in nspF:
- if type(ticket) == Ticket:
- tik_id = str(ticket._path)
- tik_id =tik_id[:-20]
- if titleid == tik_id:
- ticket.rewind()
- data = ticket.read()
- filename = str(ticket._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(str(filepath), 'w+b')
- Print.info(tabs + 'Copying: ' + str(filename))
- fp.write(data)
- fp.flush()
- fp.close()
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for cert in nspF:
- if cert._path.endswith('.cert'):
- cert_id = str(cert._path)
- cert_id =cert_id[:-21]
- if titleid == cert_id:
- cert.rewind()
- data = cert.read()
- filename = str(cert._path)
- outfolder = str(ofolder)+'/'
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(str(filepath), 'w+b')
- Print.info(tabs + 'Copying: ' + str(filename))
- fp.write(data)
- fp.flush()
- fp.close()
-
-#GET VERSION NUMBER FROM CNMT
- def get_cnmt_verID(self):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- cnmt.seek(0x8)
- titleversion = cnmt.read(0x4)
- Print.info(str(int.from_bytes(titleversion, byteorder='little')))
-
-#SIMPLE FILE-LIST
- def print_file_list(self):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if nca._path.endswith('.cnmt.nca'):
- continue
- filename = str(nca._path)
- Print.info(str(filename))
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if nca._path.endswith('.cnmt.nca'):
- filename = str(nca._path)
- Print.info(str(filename))
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) != Nca:
- filename = str(file._path)
- Print.info(str(filename))
-
- def html_feed(self,feed='',style=1,message=''):
- if feed==None:
- feed=''
- if message==None:
- message=''
- if style==1:
- feed+='{}
'.format(message)
- return feed
- if style==2:
- feed+='{}
'.format(message)
- return feed
- if style==3:
- feed+='{} {} '.format(message[0],message[1])
- return feed
- if style==4:
- feed+='{} {}. {} {} '.format(message[0],message[1],message[2],message[3])
- return feed
- if style==5:
- feed+='{}
'.format(message)
- return feed
- if style==6:
- feed+='{}
'.format(message)
- return feed
- if style==7:
- feed+='{} {}
'.format(message[0],message[1])
- return feed
- if style==8:
- feed+='[{}] {} {}. {} {} '.format(message[3],message[0],message[1],message[4],message[5])
- return feed
-
-#ADVANCED FILE-LIST
- def adv_file_list(self):
- contentlist=list()
- feed='';ncadb={};finalsize=0
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- size1=0;size2=0;size3=0
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- target=str(nca._path)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'Patch':
- content_type='Update'
- reqtag='RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo Update'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay Update'
- if content_type_cnmt == 'AddOnContent':
- content_type='DLC'
- reqtag='RequiredUpdateNumber: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if content_type_cnmt == 'Application':
- content_type='Game or Application'
- reqtag='RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay'
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- original_ID2 = str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID2 = original_ID2[2:-1]
- version=str(int.from_bytes(titleversion, byteorder='little'))
- v_number=int(int(version)/65536)
- RS_number=int(min_sversion/65536)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- programSDKversion,dataSDKversion=self.getsdkvertit(titleid2)
- sdkversion=nca.get_sdkversion()
- MinRSV=sq_tools.getMinRSV(keygen,min_sversion)
- FW_rq=sq_tools.getFWRangeKG(keygen)
- RSV_rq=sq_tools.getFWRangeRSV(min_sversion)
- RSV_rq_min=sq_tools.getFWRangeRSV(MinRSV)
- feed=self.html_feed(feed,1,message=str('TITLEID: ' + str(titleid2).upper()))
- feed=self.html_feed(feed,2,message=str("- Titleinfo:"))
- feed+=''
- if content_type_cnmt != 'AddOnContent':
- message=["Name:",tit_name];feed=self.html_feed(feed,3,message)
- message=["Editor:",editor];feed=self.html_feed(feed,3,message)
- message=["Display Version:",str(ediver)];feed=self.html_feed(feed,3,message)
- message=["Meta SDK version:",sdkversion];feed=self.html_feed(feed,3,message)
- message=["Program SDK version:",programSDKversion];feed=self.html_feed(feed,3,message)
- suplangue=str((', '.join(SupLg)))
- message=["Supported Languages:",suplangue];feed=self.html_feed(feed,3,message)
- message=["Content type:",content_type];feed=self.html_feed(feed,3,message)
- v_number=str(v_number)
- data='{} -> {} ({})'.format(version,content_type_cnmt,v_number)
- message=["Version:",data];feed=self.html_feed(feed,3,message=message);
- if content_type_cnmt == 'AddOnContent':
- if tit_name != "DLC":
- message=["Name:",tit_name];feed=self.html_feed(feed,3,message)
- message=["Editor:",editor];feed=self.html_feed(feed,3,message)
- message=["Content type:","DLC"];feed=self.html_feed(feed,3,message)
- DLCnumb=str(titleid2)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- message=["DLC number:",str(str(DLCnumb)+' -> '+"AddOnContent"+' ('+str(DLCnumb)+')')];feed=self.html_feed(feed,3,message)
- message=["DLC version Number:",str( version+' -> '+"Version"+' ('+str(v_number)+')')];feed=self.html_feed(feed,3,message)
- message=["Meta SDK version:",sdkversion];feed=self.html_feed(feed,3,message)
- message=["Data SDK version:",dataSDKversion];feed=self.html_feed(feed,3,message)
- if SupLg !='':
- suplangue=str((', '.join(SupLg)))
- message=["Supported Languages:",suplangue];feed=self.html_feed(feed,3,message)
- feed+=' '
- feed=self.html_feed(feed,2,message=str("- Required Firmware:"))
- incl_Firm=DBmodule.FWDB.detect_xci_fw(self._path,False)
- feed+=''
- message=["Included Firmware:",str(str(incl_Firm))];feed=self.html_feed(feed,3,message)
- if content_type_cnmt == 'AddOnContent':
- if v_number == 0:
- message=["Required game version:",str(str(min_sversion)+' -> '+"Application"+' ('+str(RS_number)+')')];feed=self.html_feed(feed,3,message)
- message=["Required game version:",str(str(min_sversion)+' -> '+"Application"+' ('+str(RS_number)+')')];feed=self.html_feed(feed,3,message)
- if v_number > 0:
- message=["Required game version:",str(str(min_sversion)+' -> '+"Patch"+' ('+str(RS_number)+')')];feed=self.html_feed(feed,3,message)
-
- else:
- message=[reqtag,(str(min_sversion)+" -> " +RSV_rq)];feed=self.html_feed(feed,3,message)
- message=['Encryption (keygeneration):',(str(keygen)+" -> " +FW_rq)];feed=self.html_feed(feed,3,message)
- if content_type_cnmt != 'AddOnContent':
- message=['Patchable to:',(str(MinRSV)+" -> " + RSV_rq_min)];feed=self.html_feed(feed,3,message)
-
- else:
- message=['Patchable to:',('DLC -> no RSV to patch')];feed=self.html_feed(feed,3,message)
- feed+=' '
- ncalist = list()
- ncasize = 0
- feed=self.html_feed(feed,2,message=str("- Nca files (Non Deltas):"))
- feed+=''
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- ncatype = int.from_bytes(ncatype, byteorder='little')
- IdOffset = cnmt.read(0x1)
- IdOffset = int.from_bytes(IdOffset, byteorder='little', signed=True)
- #Print.info(str(ncatype))
- if ncatype != 6:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if titleid2.endswith('800'):
- showID=str(original_ID2).upper()
- else:
- showID=str(titleid2).upper()
- if IdOffset>0:
- showID=showID[:-1]+str(IdOffset)
- ncadb[nca_name]=[showID,version,v_number]
- showID=showID+' v'+version
- s1=0;s1,feed=self.print_nca_by_title(nca_name,ncatype,showID,feed)
- ncasize=ncasize+s1
- ncalist.append(nca_name[:-4])
- contentlist.append(nca_name)
- if ncatype == 6:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- ncalist.append(nca_name[:-4])
- contentlist.append(nca_name)
- nca_meta=str(nca._path)
- ncalist.append(nca_meta[:-4])
- contentlist.append(nca_meta)
- showID=str(titleid2).upper()
- ncadb[nca_name]=[showID,version,v_number]
- showID=showID+' v'+version
- s1=0;s1,feed=self.print_nca_by_title(nca_meta,0,showID,feed)
- ncasize=ncasize+s1
- size1=ncasize
- size_pr=sq_tools.getSize(ncasize)
- feed+=' '
- feed=self.html_feed(feed,5,message=('TOTAL SIZE: '+size_pr))
- if self.actually_has_deltas(ncalist)=="true":
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- ncatype = int.from_bytes(ncatype, byteorder='little')
- unknown = cnmt.read(0x1)
- if ncatype == 6:
- feed=self.html_feed(feed,2,message=('- Nca files (Deltas):'))
- feed+=''
- break
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- ncasize = 0
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- ncatype = int.from_bytes(ncatype, byteorder='little')
- IdOffset = cnmt.read(0x1)
- IdOffset = int.from_bytes(IdOffset, byteorder='little', signed=True)
- if ncatype == 6:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if titleid2.endswith('800'):
- showID=str(original_ID2).upper()
- else:
- showID=str(titleid2).upper()
- if IdOffset>0:
- showID=showID[:-1]+str(IdOffset)
- ncadb[nca_name]=[showID,version,v_number]
- showID=showID+' v'+version
- s1=0;s1,feed=self.print_nca_by_title(nca_name,ncatype,showID,feed)
- ncasize=ncasize+s1
- size2=ncasize
- size_pr=sq_tools.getSize(ncasize)
- feed+=' '
- feed=self.html_feed(feed,5,message=('TOTAL SIZE: '+size_pr))
- if self.actually_has_other(titleid2,ncalist)=="true":
- feed=self.html_feed(feed,2,message=('- Other types of files:'))
- feed+=''
- othersize=0;os1=0;os2=0;os3=0
- os1,feed=self.print_xml_by_title(ncalist,contentlist,feed)
- os2,feed=self.print_tac_by_title(titleid2,contentlist,feed)
- os3,feed=self.print_jpg_by_title(ncalist,contentlist,feed)
- othersize=othersize+os1+os2+os3
- size3=othersize
- size_pr=sq_tools.getSize(othersize)
- feed+=' '
- feed=self.html_feed(feed,5,message=('TOTAL SIZE: '+size_pr))
- finalsize+=size1+size2+size3
- size_pr=sq_tools.getSize(finalsize)
- feed=self.html_feed(feed,2,message=('FULL CONTENT TOTAL SIZE: '+size_pr))
- feed=self.printnonlisted(contentlist,feed)
- feed=self.print_BuildIDs(ncadb,feed)
- return feed
-
- def print_nca_by_title(self,nca_name,ncatype,showID,feed):
- tab="\t"
- size=0
- ncz_name=nca_name[:-1]+'z'
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- filename = str(nca._path)
- if type(nca) == Nca:
- if filename == nca_name:
- size=nca.header.size
- size_pr=sq_tools.getSize(size)
- content=str(nca.header.contentType)
- content=content[8:]+": "
- ncatype=sq_tools.getTypeFromCNMT(ncatype)
- if ncatype != "Meta: ":
- message=[ncatype,str(filename),'TitleID:',showID,'Size',size_pr];feed=self.html_feed(feed,8,message)
- else:
- message=[ncatype,str(filename),'TitleID:',showID,'Size',size_pr];feed=self.html_feed(feed,8,message)
- return size,feed
- elif filename == ncz_name:
- ncztype=Nca(nca)
- ncztype._path=nca._path
- size=ncztype.header.size
- size_pr=sq_tools.getSize(size)
- content=str(ncztype.header.contentType)
- content=content[8:]+": "
- ncatype=sq_tools.getTypeFromCNMT(ncatype)
- if ncatype != "Meta: ":
- message=[ncatype,str(filename),'TitleID:',showID,'Size',size_pr];feed=self.html_feed(feed,8,message)
- else:
- message=[ncatype,str(filename),'TitleID:',showID,'Size',size_pr];feed=self.html_feed(feed,8,message)
- return size,feed
- return size,feed
- def print_xml_by_title(self,ncalist,contentlist,feed):
- tab="\t"
- size2return=0
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if file._path.endswith('.xml'):
- size=file.size
- size_pr=sq_tools.getSize(size)
- filename = str(file._path)
- xml=filename[:-4]
- if xml in ncalist:
- message=['XML:',str(filename),'Size:',size_pr];feed=self.html_feed(feed,4,message)
- contentlist.append(filename)
- size2return=size+size2return
- return size2return,feed
- def print_tac_by_title(self,titleid,contentlist,feed):
- tab="\t"
- size2return=0
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for ticket in nspF:
- if type(ticket) == Ticket:
- size=ticket.size
- size_pr=sq_tools.getSize(size)
- filename = str(ticket._path)
- tik=filename[:-20]
- if tik == titleid:
- message=['Ticket:',str(filename),'Size:',size_pr];feed=self.html_feed(feed,4,message)
- contentlist.append(filename)
- size2return=size+size2return
- for cert in nspF:
- if cert._path.endswith('.cert'):
- size=cert.size
- size_pr=sq_tools.getSize(size)
- filename = str(cert._path)
- cert_id =filename[:-21]
- if cert_id == titleid:
- message=['Cert:',str(filename),'Size:',size_pr];feed=self.html_feed(feed,4,message)
- contentlist.append(filename)
- size2return=size+size2return
- return size2return,feed
- def print_jpg_by_title(self,ncalist,contentlist,feed):
- size2return=0
- tab="\t"
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if file._path.endswith('.jpg'):
- size=file.size
- size_pr=sq_tools.getSize(size)
- filename = str(file._path)
- jpg=filename[:32]
- if jpg in ncalist:
- message=['JPG:',str(filename),'Size:',size_pr];feed=self.html_feed(feed,4,message)
- contentlist.append(filename)
- size2return=size+size2return
- return size2return,feed
- def actually_has_deltas(self,ncalist):
- vfragment="false"
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.DATA':
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- if nca._path[:-4] in ncalist:
- vfragment="true"
- break
- return vfragment
- def actually_has_other(self,titleid,ncalist):
- vother="false"
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if file._path.endswith('.xml'):
- filename = str(file._path)
- xml=filename[:-4]
- if xml in ncalist:
- vother="true"
- break
- if type(file) == Ticket:
- filename = str(file._path)
- tik=filename[:-20]
- if tik == titleid:
- vother="true"
- break
- if file._path.endswith('.cert'):
- filename = str(file._path)
- cert_id =filename[:-21]
- if cert_id == titleid:
- vother="true"
- break
- if file._path.endswith('.jpg'):
- filename = str(file._path)
- jpg=filename[:32]
- if jpg in ncalist:
- vother="true"
- break
- return vother
- def printnonlisted(self,contentlist,feed=''):
- tab="\t"
- list_nonlisted="false"
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- filename = str(file._path)
- if not filename in contentlist:
- list_nonlisted="true"
- if list_nonlisted == "true":
- feed=self.html_feed(feed,2,'Files not linked to content in xci:')
- totsnl=0
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- filename = str(file._path)
- nczname= str(file._path)[:-1]+'z'
- if not filename in contentlist and not nczname in contentlist:
- totsnl=totsnl+file.size
- size_pr=sq_tools.getSize(file.size)
- message=['OTHER:',str(filename),'Size:',size_pr];feed=self.html_feed(feed,4,message)
- bigtab="\t"*7
- size_pr=sq_tools.getSize(totsnl)
- feed=self.html_feed(feed,5,message=('TOTAL SIZE: '+size_pr))
- return feed
-
- def print_BuildIDs(self,ncadb,feed=''):
- c=0
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- size1=0;size2=0;size3=0
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.PROGRAM':
- target=str(nca._path)
- tit=str(nca.header.titleId).upper()
- entry=ncadb[target]
- ModuleId,BuildID8,BuildID16=self.read_buildid(target)
- if ModuleId!="":
- if c==0:
- feed=self.html_feed(feed,2,'EXEFS DATA:')
- c+=1
- feed=self.html_feed(feed,2,f'[Title: {tit} v{entry[1]}]')
- ModuleId=sq_tools.trimm_module_id(ModuleId)
- message=[f'BuildID8:',str(BuildID8)];feed=self.html_feed(feed,3,message)
- message=[f'BuildID:',str(ModuleId)];feed=self.html_feed(feed,3,message)
- return feed
-#ADVANCED FILE-LIST
- def adv_content_list(self):
- feed=''
- applist=list(); applist_ID=list()
- patchlist=list(); patchlist_ID=list()
- dlclist=list(); dlclist_ID=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- size1=0;size2=0;size3=0
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid = titleid[2:-1]
- titleversion = cnmt.read(0x4)
- version=str(int.from_bytes(titleversion, byteorder='little'))
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- original_ID = str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- target=str(nca._path)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'Application':
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- applist.append([target,titleid,version,tit_name,editor])
- if content_type_cnmt == 'Patch':
- patchlist.append([target,original_ID,version,titleid])
- patchlist_ID.append(target)
- if content_type_cnmt == 'AddOnContent':
- DLCnumb=str(titleid)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- dlclist.append([target,original_ID,version,titleid,DLCnumb])
-
- applist=sorted(applist, key=itemgetter(1))
- patchlist=sorted(patchlist, key=itemgetter(1))
- dlclist=sorted(dlclist, key=itemgetter(4))
- patch_called=list()
- dlc_called=list()
- if len(applist) != 0:
- for i in range(len(applist)):
- tid=applist[i][1]
- message='------------------------------------------------';feed+=message+'\n'
- message='BASE CONTENT ID: ' + str(tid);feed+=message+'\n'
- message='------------------------------------------------';feed+=message+'\n'
- message='Name: '+applist[i][3];feed+=message+'\n'
- message='Editor: '+applist[i][4];feed+=message+'\n'
- message='------------------------------------------------';feed+=message+'\n'
- message=applist[i][1]+" [BASE]"+" v"+applist[i][2];feed+=message+'\n'
- cupd=0
- for j in range(len(patchlist)):
- if tid == patchlist[j][1]:
- v=patchlist[j][2]
- v_number=str(int(int(v)/65536))
- message=patchlist[j][3]+" [UPD]"+" v"+patchlist[j][2]+" -> Patch("+v_number+")";feed+=message+'\n'
- cupd+=1
- patch_called.append(patchlist[j])
- cdlc=0
- for k in range(len(dlclist)):
- if tid == dlclist[k][1]:
- message=dlclist[k][3]+" [DLC "+str(dlclist[k][4])+"]"+" v"+dlclist[k][2];feed+=message+'\n'
- cdlc+=1
- dlc_called.append(dlclist[k])
- message='------------------------------------------------';feed+=message+'\n'
- message='CONTENT INCLUDES: 1 BASEGAME '+str(cupd)+' UPDATES '+str(cdlc)+' DLCS';feed+=message+'\n'
- message='------------------------------------------------';feed+=message+'\n'
- if len(patchlist) != len(patch_called):
- message='------------------------------------------------';feed+=message+'\n'
- message='ORPHANED UPDATES:';feed+=message+'\n'
- message='------------------------------------------------';feed+=message+'\n'
- for j in range(len(patchlist)):
- if patchlist[j] not in patch_called:
- v=patchlist[j][2]
- v_number=str(int(int(v)/65536))
- message=patchlist[j][3]+" [UPD]"+" v"+patchlist[j][2]+" -> Patch("+v_number+")";feed+=message+'\n'
- if len(dlclist) != len(dlc_called):
- message='------------------------------------------------';feed+=message+'\n'
- message='ORPHANED DLCS:';feed+=message+'\n'
- message='------------------------------------------------';feed+=message+'\n'
- for k in range(len(dlclist)):
- if dlclist[k] not in dlc_called:
- message=dlclist[k][3]+" [DLC "+str(dlclist[k][4])+"]"+" v"+dlclist[k][2];feed+=message+'\n'
- else:
- message='This option is currently meant for multicontent, that includes at least a base game';feed+=message+'\n'
- return feed
-
-
-#///////////////////////////////////////////////////
-#INFO ABOUT UPD REQUIREMENTS
-#///////////////////////////////////////////////////
- def getsdkvertit(self,titid):
- programSDKversion=''
- dataSDKversion=''
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- nca_id=nca.header.titleId
- if str(titid[:-3]).upper() == str(nca_id[:-3]).upper():
- if str(nca.header.contentType) == 'Content.PROGRAM':
- programSDKversion=nca.get_sdkversion()
- break
- if programSDKversion=='':
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- nca_id=nca.header.titleId
- if str(titid[:-3]).upper() == str(nca_id[:-3]).upper():
- if str(nca.header.contentType) == 'Content.CONTROL':
- programSDKversion=nca.get_sdkversion()
- break
- if programSDKversion=='':
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- nca_id=nca.header.titleId
- if str(titid[:-3]).upper() == str(nca_id[:-3]).upper():
- if str(nca.header.contentType) == 'Content.PUBLIC_DATA':
- dataSDKversion = nca.get_sdkversion()
- break
- return programSDKversion,dataSDKversion
-
- def print_fw_req(self,trans=True):
- feed=''
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- RSversion=cnmt.readInt32()
- Emeta=cnmt.readInt32()
- target=str(nca._path)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'Patch':
- content_type='Update'
- reqtag='- RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo Update'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay Update'
- if content_type_cnmt == 'AddOnContent':
- content_type='DLC'
- reqtag='- RequiredUpdateNumber: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if content_type_cnmt == 'Application':
- content_type='Base Game or Application'
- reqtag='- RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay'
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- version=str(int.from_bytes(titleversion, byteorder='little'))
- v_number=int(int(version)/65536)
- RS_number=int(RSversion/65536)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- MinRSV=sq_tools.getMinRSV(keygen,RSversion)
- FW_rq=sq_tools.getFWRangeKG(keygen)
- RSV_rq=sq_tools.getFWRangeRSV(RSversion)
- RSV_rq_min=sq_tools.getFWRangeRSV(MinRSV)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'Patch':
- content_type='Update'
- reqtag='- RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo Update'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay Update'
- if content_type_cnmt == 'AddOnContent':
- content_type='DLC'
- reqtag='- RequiredUpdateNumber: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if content_type_cnmt == 'Application':
- content_type='Base Game or Application'
- reqtag='- RequiredSystemVersion: '
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- if tit_name=='DLC':
- tit_name='-'
- editor='-'
- if isdemo == 1:
- content_type='Demo'
- if isdemo == 2:
- content_type='RetailInteractiveDisplay'
- programSDKversion,dataSDKversion=self.getsdkvertit(titleid2)
- nsuId,releaseDate,category,ratingContent,numberOfPlayers,intro,description,iconUrl,screenshots,bannerUrl,region,rating,developer,productCode,OnlinePlay,SaveDataCloud,playmodes,video,shopurl=nutdb.get_content_data(titleid2,trans)
- sdkversion=nca.get_sdkversion()
- message=('-----------------------------');feed+=message+'\n'
- message=('CONTENT ID: ' + str(titleid2));feed+=message+'\n'
- message=('-----------------------------');feed+=message+'\n'
- if content_type_cnmt != 'AddOnContent':
- message=("Titleinfo:");feed+=message+'\n'
- message=("- Name: " + tit_name);feed+=message+'\n'
- message=("- Editor: " + editor);feed+=message+'\n'
- message=("- Display Version: " + str(ediver));feed+=message+'\n'
- message=("- Meta SDK version: " + sdkversion);feed+=message+'\n'
- message=("- Program SDK version: " + programSDKversion);feed+=message+'\n'
- suplangue=str((', '.join(SupLg)))
- message=("- Supported Languages: "+suplangue);
- par = textwrap.dedent(message).strip()
- message=(textwrap.fill(par,width=80,initial_indent='', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));feed+=message+'\n'
- message=("- Content type: "+content_type);feed+=message+'\n'
- message=("- Version: " + version+' -> '+content_type_cnmt+' ('+str(v_number)+')');feed+=message+'\n'
- if content_type_cnmt == 'AddOnContent':
- nsuId=nutdb.get_dlcnsuId(titleid2)
- message=("Titleinfo:");feed+=message+'\n'
- if tit_name != "DLC":
- message=("- Name: " + tit_name);feed+=message+'\n'
- message=("- Editor: " + editor);feed+=message+'\n'
- message=("- Content type: "+"DLC");feed+=message+'\n'
- DLCnumb=str(titleid2)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- message=("- DLC number: "+str(DLCnumb)+' -> '+"AddOnContent"+' ('+str(DLCnumb)+')');feed+=message+'\n'
- message=("- DLC version Number: " + version+' -> '+"Version"+' ('+str(v_number)+')');feed+=message+'\n'
- message=("- Meta SDK version: " + sdkversion);feed+=message+'\n'
- message=("- Data SDK version: " + dataSDKversion);feed+=message+'\n'
- if SupLg !='':
- suplangue=str((', '.join(SupLg)))
- message=("- Supported Languages: "+suplangue);
- par = textwrap.dedent(message).strip()
- message=(textwrap.fill(par,width=80,initial_indent='', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));feed+=message+'\n'
- message=("\nRequired Firmware:");feed+=message+'\n'
- if content_type_cnmt == 'AddOnContent':
- if v_number == 0:
- message=("- Required game version: " + str(RSversion)+' -> '+"Application"+' ('+str(RS_number)+')');feed+=message+'\n'
- if v_number > 0:
- message=("- Required game version: " + str(RSversion)+' -> '+"Patch"+' ('+str(RS_number)+')');feed+=message+'\n'
- else:
- message=(reqtag + str(RSversion)+" -> " +RSV_rq);feed+=message+'\n'
- message=('- Encryption (keygeneration): ' + str(keygen)+" -> " +FW_rq);feed+=message+'\n'
- if content_type_cnmt != 'AddOnContent':
- message=('- Patchable to: ' + str(MinRSV)+" -> " + RSV_rq_min+'\n');feed+=message+'\n'
- else:
- message=('- Patchable to: DLC -> no RSV to patch\n');feed+=message+'\n'
- try:
- if content_type_cnmt != 'AddOnContent':
- message=('ExeFS Data:');feed+=message+'\n'
- ModuleId,BuildID8,BuildID16=self.read_buildid()
- message=('- BuildID8: '+ BuildID8);feed+=message+'\n'
- message=('- BuildID16: '+ BuildID16);feed+=message+'\n'
- message=('- BuildID32: '+ ModuleId +'\n');feed+=message+'\n'
- except:pass
- if nsuId!=False or numberOfPlayers!=False or releaseDate!=False or category!=False or ratingContent!=False:
- message=('Eshop Data:');feed+=message+'\n'
- if nsuId!=False:
- message=("- nsuId: " + nsuId);feed+=message+'\n'
- if region!=False:
- message=('- Data from Region: ' + region);feed+=message+'\n'
- if numberOfPlayers!=False:
- message=("- Number of Players: " + numberOfPlayers);feed+=message+'\n'
- if releaseDate!=False:
- message=("- Release Date: " + releaseDate);feed+=message+'\n'
- if category!=False:
- category=str((', '.join(category)))
- message=("- Genres: " + category);
- par = textwrap.dedent(message).strip()
- message=(textwrap.fill(par,width=80,initial_indent='', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));feed+=message+'\n'
- if rating!=False:
- message=("- AgeRating: " + rating);feed+=message+'\n'
- if ratingContent!=False:
- ratingContent=str((', '.join(ratingContent)))
- message=("- Rating tags: " + ratingContent);
- par = textwrap.dedent(message).strip()
- message=(textwrap.fill(par,width=80,initial_indent='', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));feed+=message+'\n'
- if intro!=False or description!=False:
- message=('\nDescription:');feed+=message+'\n'
- if intro!=False:
- par = textwrap.dedent(intro).strip().upper()
- message=('-----------------------------------------------------------------------------');feed+=message+'\n'
- message=(textwrap.fill(par,width=80,initial_indent=' ', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));feed+=message+'\n'
- message=('-----------------------------------------------------------------------------');feed+=message+'\n'
- if description!=False:
- if "•" in description:
- data=description.split("• ")
- token='• '
- elif "★" in description:
- data=description.split("★ ")
- token='* '
- elif "*" in description:
- data=description.split("* ")
- token='* '
- elif "■" in description:
- data=description.split("* ")
- token='• '
- elif "●" in description:
- data=description.split("● ")
- token='• '
- elif "-" in description:
- data=description.split("- ")
- token='- '
- else:
- data=description.split(" ")
- token=''
- i=0
- for d in data:
- if i>0:
- d=token+d
- i+=1
- par = textwrap.dedent(d).strip()
- message=(textwrap.fill(par,width=80,initial_indent=' ', subsequent_indent=' ',replace_whitespace=True,fix_sentence_endings=True));feed+=message+'\n'
- return feed
-
- def inf_get_title(self,target,offset,content_entries,original_ID,roman=True):
- content_type=''
- token='secure'
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- if target == str(nca._path):
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- nca_name='false'
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- ncatype2 = int.from_bytes(ncatype, byteorder='little')
- if ncatype2 == 3:
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name=='false':
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- cnmt.rewind()
- testID=cnmt.readInt64()
- if testID == original_ID:
- nca.rewind()
- f.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=self.readInt32()
- end_of_emeta=self.readInt32()
- target=str(nca._path)
- contentname,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- title = 'DLC'
- for nspF in self.hfs0:
- if token == str(nspF._path):
- for nca in nspF:
- if type(nca) == Nca:
- if nca_name == str(nca._path):
- if str(nca.header.contentType) == 'Content.CONTROL':
- title,editor,ediver,SupLg,regionstr,isdemo=nca.get_langueblock(title,roman)
- return(title,editor,ediver,SupLg,regionstr,isdemo)
- regionstr="0|0|0|0|0|0|0|0|0|0|0|0|0|0"
- return(title,"","","",regionstr,"")
-
-
-
- def pack(self,upd_list,norm_list,sec_list,buffer,fat):
- if not self.path:
- return False
- indent = 1
- tabs = '\t' * indent
- hfs0 = Fs.Hfs0(None, None)
- root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier=hfs0.genRHeader(upd_list,norm_list,sec_list)
- #print(hx(root_header))
- xci_header,game_info,sig_padding,xci_certificate=self.genheader(root_header,rootSize)
- totSize=len(xci_header)+len(game_info)+len(sig_padding)+len(xci_certificate)+rootSize
-
- if os.path.exists(self.path) and os.path.getsize(self.path) == totSize:
- Print.info('\t\tRepack %s is already complete!' % self.path)
- return
- outfile=self.path
- Print.info('Generating XCI:')
- print("")
- if totSize <= 4294934528:
- fat="exfat"
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294934528)
- index=0
- outfile=outfile[:-1]+str(index)
- c=0
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing XCI header...')
- outf = open(outfile, 'wb')
- outf.write(xci_header)
- t.update(len(xci_header))
- c=c+len(xci_header)
- t.write(tabs+'- Writing XCI game info...')
- outf.write(game_info)
- t.update(len(game_info))
- c=c+len(game_info)
- t.write(tabs+'- Generating padding...')
- outf.write(sig_padding)
- t.update(len(sig_padding))
- c=c+len(sig_padding)
- t.write(tabs+'- Writing XCI certificate...')
- outf.write(xci_certificate)
- t.update(len(xci_certificate))
- c=c+len(xci_certificate)
- t.write(tabs+'- Writing ROOT HFS0 header...')
- outf.write(root_header)
- t.update(len(root_header))
- c=c+len(root_header)
- t.write(tabs+'- Writing UPDATE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(upd_multiplier))
- outf.write(upd_header)
- t.update(len(upd_header))
- c=c+len(upd_header)
- for file in upd_list:
- t.write(tabs+'- Appending %s' % os.path.basename(file))
- with open(file, 'rb') as inf:
- while True:
- buf = inf.read(int(buffer))
- if not buf:
- break
- outf.write(buf)
- t.update(len(buf))
- c=c+len(buf)
- t.write(tabs+'- Writing NORMAL partition header...')
- t.write(tabs+' Calculated multiplier: '+str(norm_multiplier))
- outf.write(norm_header)
- t.update(len(norm_header))
- c=c+len(norm_header)
- for file in norm_list:
- t.write(tabs+'- Appending %s' % os.path.basename(file))
- with open(file, 'rb') as inf:
- while True:
- buf = inf.read(int(buffer))
- if not buf:
- break
- outf.write(buf)
- t.update(len(buf))
- c=c+len(buf)
- t.write(tabs+'- Writing SECURE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(sec_multiplier))
- outf.write(sec_header)
- t.update(len(sec_header))
- c=c+len(sec_header)
- block=4294934528
- for file in sec_list:
- t.write(tabs+' > Appending %s' % os.path.basename(file))
- if file.endswith('.nca'):
- nca =Fs.Nca(file, 'r+b')
- nca.rewind()
- if nca.header.getgamecard() == 0:
- masterKeyRev = nca.header.getCryptoType2()
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)
- crypto = aes128.AESECB(key)
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0:
- nca.header.setgamecard(1)
- nca.flush()
- nca.close()
- with open(file, 'rb') as inf:
- while True:
- data = inf.read(int(buffer))
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- dat2=inf.read(int(n2))
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- if not data:
- break
- t.close()
- print("")
- print("Closing file. Please wait")
- outf.close()
-
- def genheader(self,root_header,rootSize):
- signature=sq_tools.randhex(0x100)
- signature= bytes.fromhex(signature)
- sec_offset=root_header[0x90:0x90+0x8]
- sec_offset=int.from_bytes(sec_offset, byteorder='little')
- sec_offset=int((sec_offset+0xF000+0x200)/0x200)
- sec_offset=sec_offset.to_bytes(4, byteorder='little')
- back_offset=(0xFFFFFFFF).to_bytes(4, byteorder='little')
- kek=(0x00).to_bytes(1, byteorder='big')
- tot_size=0xF000+rootSize
- cardsize,access_freq=sq_tools.getGCsize(tot_size)
- cardsize=cardsize.to_bytes(1, byteorder='big')
- GC_ver=(0x00).to_bytes(1, byteorder='big')
- GC_flag=(0x00).to_bytes(1, byteorder='big')
- pack_id=(0x8750F4C0A9C5A966).to_bytes(8, byteorder='big')
- valid_data=int(((tot_size-0x1)/0x200))
- valid_data=valid_data.to_bytes(8, byteorder='little')
-
- try:
- key= Keys.get('xci_header_key')
- key= bytes.fromhex(key)
- IV=sq_tools.randhex(0x10)
- IV= bytes.fromhex(IV)
- xkey=True
- #print(hx(IV))
- #print("i'm here")
- except:
- IV=(0x5B408B145E277E81E5BF677C94888D7B).to_bytes(16, byteorder='big')
- xkey=False
- #print("i'm here 2")
-
- HFS0_offset=(0xF000).to_bytes(8, byteorder='little')
- len_rHFS0=(len(root_header)).to_bytes(8, byteorder='little')
- sha_rheader=sha256(root_header[0x00:0x200]).hexdigest()
- sha_rheader=bytes.fromhex(sha_rheader)
- sha_ini_data=bytes.fromhex('1AB7C7B263E74E44CD3C68E40F7EF4A4D6571551D043FCA8ECF5C489F2C66E7E')
- SM_flag=(0x01).to_bytes(4, byteorder='little')
- TK_flag=(0x02).to_bytes(4, byteorder='little')
- K_flag=(0x0).to_bytes(4, byteorder='little')
- end_norm = sec_offset
-
- header = b''
- header += signature
- header += b'HEAD'
- header += sec_offset
- header += back_offset
- header += kek
- header += cardsize
- header += GC_ver
- header += GC_flag
- header += pack_id
- header += valid_data
- header += IV
- header += HFS0_offset
- header += len_rHFS0
- header += sha_rheader
- header += sha_ini_data
- header += SM_flag
- header += TK_flag
- header += K_flag
- header += end_norm
-
- #Game_info
- if xkey==True:
- firm_ver='0100000000000000'
- access_freq=access_freq
- Read_Wait_Time='88130000'
- Read_Wait_Time2='00000000'
- Write_Wait_Time='00000000'
- Write_Wait_Time2='00000000'
- Firmware_Mode='00110C00'
- CUP_Version='5a000200'
- Empty1='00000000'
- Upd_Hash='9bfb03ddbb7c5fca'
- CUP_Id='1608000000000001'
- Empty2='00'*0x38
- #print(hx(Empty2))
-
- firm_ver=bytes.fromhex(firm_ver)
- access_freq=bytes.fromhex(access_freq)
- Read_Wait_Time=bytes.fromhex(Read_Wait_Time)
- Read_Wait_Time2=bytes.fromhex(Read_Wait_Time2)
- Write_Wait_Time=bytes.fromhex(Write_Wait_Time)
- Write_Wait_Time2=bytes.fromhex(Write_Wait_Time2)
- Firmware_Mode=bytes.fromhex(Firmware_Mode)
- CUP_Version=bytes.fromhex(CUP_Version)
- Empty1=bytes.fromhex(Empty1)
- Upd_Hash=bytes.fromhex(Upd_Hash)
- CUP_Id=bytes.fromhex(CUP_Id)
- Empty2=bytes.fromhex(Empty2)
-
- Game_info = b''
- Game_info += firm_ver
- Game_info += access_freq
- Game_info += Read_Wait_Time
- Game_info += Read_Wait_Time2
- Game_info += Write_Wait_Time
- Game_info += Write_Wait_Time2
- Game_info += Firmware_Mode
- Game_info += CUP_Version
- Game_info += Empty1
- Game_info += Upd_Hash
- Game_info += CUP_Id
- Game_info += Empty2
-
- gamecardInfoIV=IV[::-1]
- crypto = aes128.AESCBC(key, gamecardInfoIV)
- enc_info=crypto.encrypt(Game_info)
- if xkey==False:
- enc_info=sq_tools.get_enc_gameinfo(tot_size)
-
- #print (hx(enc_info))
-
- #Padding
- sig_padding='00'*0x6E00
- sig_padding=bytes.fromhex(sig_padding)
- #print (hx(sig_padding))
-
- #CERT
- fake_CERT='FF'*0x8000
- fake_CERT=bytes.fromhex(fake_CERT)
- #print (hx(fake_CERT))
- return header,enc_info,sig_padding,fake_CERT
-
- def print_head(self):
-
- gamecardInfoIV=self.gamecardInfoIV[::-1]
- #print (str(hx(gamecardInfoIV)))
- try:
- key= Keys.get('xci_header_key')
- key= bytes.fromhex(key)
- except:
- pass
- #print(hx(key))
- crypto = aes128.AESCBC(key, gamecardInfoIV)
- #print (self.gamecardInfo.firmwareVersion)
- self.seek(0x190)
-
- encrypted_info=self.read(0x70)
-
- firm_ver= encrypted_info[0x00:0x00+0x8]
- access_freq= encrypted_info[0x08:0x08+0x4]
- Read_Wait_Time= encrypted_info[0xC:0xC+0x4]
- Read_Wait_Time2= encrypted_info[0x10:0x10+0x4]
- Write_Wait_Time= encrypted_info[0x14:0x14+0x4]
- Write_Wait_Time2= encrypted_info[0x18:0x18+0x4]
- Firmware_Mode = encrypted_info[0x1C:0x1C+0x4]
- CUP_Version = encrypted_info[0x20:0x20+0x4]
- Empty1=encrypted_info[0x24:0x24+0x4]
- Update_Partition_Hash=encrypted_info[0x28:0x28+0x8]
- CUP_Id =encrypted_info[0x30:0x30+0x8]
- Empty2=encrypted_info[0x38:0x38+0x38]
-
- print ('firmware version: '+str(hx(firm_ver)))
- print ('access freq: '+str(hx(access_freq)))
- print ('Read_Wait_Time: '+str(hx(Read_Wait_Time)))
- print ('Read_Wait_Time2: '+str(hx(Read_Wait_Time2)))
- print ('Write_Wait_Time: '+str(hx(Write_Wait_Time)))
- print ('Write_Wait_Time2: '+str(hx(Write_Wait_Time2)))
- print ('Firmware_Mode: '+str(hx(Firmware_Mode)))
- print ('CUP_Version: '+str(hx(CUP_Version)))
- print ('Empty1: '+str(hx(Empty1)))
- print ('Update_Partition_Hash: '+str(hx(Update_Partition_Hash)))
- print ('CUP_Id: '+str(hx(CUP_Id)))
- print ('Empty2: '+str(hx(Empty2)))
-
- #upd_hash= dec_info[0x28:0x28+0x8]
- #print (hx(upd_hash))
- #print (hx(encrypted_info))
- #print (hx(dec_info))
-
- def supertrim(self,buffer,outfile,ofolder,fat,keepupd=False):
- indent = 1
- rightsId = 0
- tabs = '\t' * indent
- completefilelist=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- completefilelist.append(str(file._path))
- updlist=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Patch' and keepupd==False:
- pass
- else:
- break
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- #**************************************************************
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name in completefilelist:
- updlist.append(nca_name)
- nca_meta=str(nca._path)
- if nca_meta in completefilelist:
- updlist.append(nca_meta)
-
- xci_header,game_info,sig_padding,xci_certificate,root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier =self.get_header_supertrimmer(updlist)
-
- totSize=len(xci_header)+len(game_info)+len(sig_padding)+len(xci_certificate)+rootSize
-
- if os.path.exists(outfile) and os.path.getsize(outfile) == totSize:
- Print.info('\t\tRepack %s is already complete!' % outfile)
- return
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for ticket in nspF:
- if type(ticket) == Ticket:
- masterKeyRev = ticket.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = ticket.getRightsId()
- tik=ticket
- for nca in nspF:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(nca._path)} - {nca.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
- for nca in nspF:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- if nca.header.getCryptoType2() == 0:
- if nca.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(tik.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
-
- Print.info('Generating XCI:')
- '''
- if rightsId !=0:
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- print("")
- '''
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294934528)
- index=0
- outfile=outfile[:-1]+str(index)
- outfile=outfile.replace(' [Trimmed]','');outfile=outfile.replace(' (Trimmed)','')
- outfile=outfile.replace(' [TM]','');outfile=outfile.replace(' (TM)','')
- outfile=outfile.replace('[Trimmed]','');outfile=outfile.replace('(Trimmed)','')
- outfile=outfile.replace('[TM]','');outfile=outfile.replace('(TM)','')
- outfile=outfile.replace(' [STR].xci','.xci');outfile=outfile.replace('[STR].xci','.xci')
- outfile=outfile.replace('.xci',' [STR].xci')
- c=0
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing XCI header...')
- if not os.path.exists(ofolder):
- os.makedirs(ofolder)
- outf = open(outfile, 'wb')
- outf.write(xci_header)
- t.update(len(xci_header))
- c=c+len(xci_header)
- t.write(tabs+'- Writing XCI game info...')
- outf.write(game_info)
- t.update(len(game_info))
- c=c+len(game_info)
- t.write(tabs+'- Generating padding...')
- outf.write(sig_padding)
- t.update(len(sig_padding))
- c=c+len(sig_padding)
- t.write(tabs+'- Writing XCI certificate...')
- outf.write(xci_certificate)
- t.update(len(xci_certificate))
- c=c+len(xci_certificate)
- t.write(tabs+'- Writing ROOT HFS0 header...')
- outf.write(root_header)
- t.update(len(root_header))
- c=c+len(root_header)
- t.write(tabs+'- Writing UPDATE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(upd_multiplier))
- outf.write(upd_header)
- t.update(len(upd_header))
- c=c+len(upd_header)
- t.write(tabs+'- Writing NORMAL partition header...')
- t.write(tabs+' Calculated multiplier: '+str(norm_multiplier))
- outf.write(norm_header)
- t.update(len(norm_header))
- c=c+len(norm_header)
- t.write(tabs+'- Writing SECURE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(sec_multiplier))
- outf.write(sec_header)
- t.update(len(sec_header))
- c=c+len(sec_header)
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- block=4294934528
- for nca in nspF:
- if str(nca._path) in updlist:
- t.write(tabs+'- Skipping update nca: ' + str(nca._path))
- continue
- #if type(file) == Nca or type(file) == Ticket or file._path.endswith('.cert'):
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- nca.rewind()
- t.write(tabs+'- Appending: ' + str(nca._path))
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- nca.rewind()
- i=0
- for data in iter(lambda: nca.read(int(buffer)), ""):
- if i==0:
- nca.rewind()
- rawhead=nca.read(0xC00)
- rawhead=hcrypto.decrypt(rawhead)
- header = b''
- header += rawhead[0x00:0x00+0x230]
- tr='00'*0x10
- tr=bytes.fromhex(tr)
- header += tr
- header += rawhead[0x240:0x240+0xC0]
- header += encKeyBlock
- header += rawhead[0x340:]
- newheader=hcrypto.encrypt(header)
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- dat2=newheader[0x00:0x00+int(n2)]
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- dat2=newheader[0x00+int(n2)+1:]
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- nca.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- if nca.header.getRightsId() == 0:
- nca.rewind()
- #fp.seek(pos)
- t.write(tabs+'- Appending: ' + str(nca._path))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- t.close()
- print("")
- print("Closing file. Please wait")
- outf.close()
-
-
- def trim(self,buffer,outfile,ofolder,fat):
- block=4294934528
- #print(str(self.gamecardSize))
- #print(str(self.validDataEndOffset))
- valid_data=int(((self.validDataEndOffset+0x1)*0x200))
- totSize=valid_data
- #print(str(valid_data))
- if int(self.size) == totSize:
- print("File is already trimmed!!!")
- return
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- self.rewind()
- wrdata=0;c=0
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294934528)
- index=0
- outfile=outfile[:-1]+str(index)
- outfile=outfile.replace(' [Trimmed]','');outfile=outfile.replace(' (Trimmed)','')
- outfile=outfile.replace(' [TM]','');outfile=outfile.replace(' (TM)','')
- outfile=outfile.replace('[Trimmed]','');outfile=outfile.replace('(Trimmed)','')
- outfile=outfile.replace('[TM]','');outfile=outfile.replace('(TM)','')
- outfile=outfile.replace(' [STR].xci','.xci');outfile=outfile.replace('[STR].xci','.xci')
- outfile=outfile.replace('.xci',' [TR].xci')
- outf = open(outfile, 'wb')
- for data in iter(lambda: self.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- outf.flush()
- wrdata=wrdata+len(data)
- else:
- if (wrdata+len(data))>totSize:
- n2=totSize-wrdata
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- c=c+len(dat2)
- wrdata=wrdata+len(dat2)
- break
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- wrdata=wrdata+len(data)
- outf.flush()
- if not data:
- break
- t.close()
- print("")
- print("Closing file. Please wait")
- outf.close()
-
- def untrim(self,buffer,outfile,ofolder,fat):
- block=4294934528
- GCFlag=(str(hx(self.gamecardSize))[2:-1]).upper()
- #print(GCFlag)
- #print(str(self.validDataEndOffset))
- valid_data=int(((self.validDataEndOffset+0x1)*0x200))
- GCSize=sq_tools.getGCsizeinbytes(GCFlag)
- #print(str(GCSize))
- totSize=int(GCSize)
- #print(str(valid_data))
- if int(self.size) == totSize:
- print("File is already untrimmed!!!")
- return
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- self.rewind()
- wrdata=0;c=0
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294934528)
- index=0
- outfile=outfile[:-1]+str(index)
- outfile=outfile.replace(' [Trimmed]','');outfile=outfile.replace(' (Trimmed)','')
- outfile=outfile.replace(' [TM]','');outfile=outfile.replace(' (TM)','')
- outfile=outfile.replace('[Trimmed]','');outfile=outfile.replace('(Trimmed)','')
- outfile=outfile.replace('[TM]','');outfile=outfile.replace('(TM)','')
- outfile=outfile.replace(' [STR].xci','.xci');outfile=outfile.replace('[STR].xci','.xci')
- outfile=outfile.replace(' [TR].xci','.xci');outfile=outfile.replace('[TR].xci','.xci')
- outf = open(outfile, 'wb')
- for data in iter(lambda: self.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- outf.flush()
- wrdata=wrdata+len(data)
- else:
- if (wrdata+len(data))>valid_data:
- n2=valid_data-wrdata
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- c=c+len(dat2)
- wrdata=wrdata+len(dat2)
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- wrdata=wrdata+len(data)
- outf.flush()
- if not data or wrdata==valid_data or wrdata>valid_data:
- break
- if wrdatablock:
- n2=block-c
- outf.write(bytes.fromhex('FF'*n2))
- outf.flush()
- outf.close()
- t.update(n2)
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- n3=totSize-valid_data-n2
- outf.write(bytes.fromhex('FF'*n3))
- t.update(n3)
- outf.flush()
- else:
- bytes_tofill=int(round((int(totSize-valid_data)/buffer),0))
- #print(str(bytes_tofill))
- count=0
- while countcrypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- filename = str(nca._path)
- outfolder = str(ofolder)
- filepath = os.path.join(outfolder, filename)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- fp = open(filepath, 'w+b')
- nca.rewind()
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- encKeyBlock = target.header.getKeyBlock()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- newheader=self.get_newheader(target,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- target.rewind()
- target.write(newheader)
- target.close()
- if metapatch == 'true':
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if str(target.header.contentType) == 'Content.META':
- for pfs0 in target:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- target.close()
- else:
- target.close()
- self.patcher_meta(filepath,RSV_cap,t)
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- block = target.read()
- nsha=sha256(block).hexdigest()
- target.rewind()
- xml_file=target.xml_gen(ofolder,nsha)
- target.close()
-
- t.close()
-
- contentlist=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- vfragment="false"
- if type(nca) == Nca:
- if (delta == False) and (str(nca.header.contentType) == 'Content.DATA'):
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- continue
- contentlist.append(nca._path)
- if str(nca.header.contentType) == 'Content.META':
- xmlname=nca._path
- xmlname=xmlname[:-3]+'xml'
- contentlist.append(xmlname)
- hd = self.gen_nsp_head(contentlist,delta,True,ofolder)
-
- totSize = len(hd)
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- vfragment="false"
- if type(nca) == Nca:
- if (delta == False) and (str(nca.header.contentType) == 'Content.DATA'):
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- continue
- totSize=totSize+nca.header.size
- if str(nca.header.contentType) == 'Content.META':
- xmlname=nca._path
- xmlname=xmlname[:-3]+'xml'
- xmlpath = os.path.join(ofolder, xmlname)
- totSize=totSize+os.path.getsize(xmlpath)
-
- if os.path.exists(outfile) and os.path.getsize(outfile) == totSize:
- Print.info('\t\tRepack %s is already complete!' % outfile)
- return
-
- indent = 1
- rightsId = 0
- tabs = '\t' * indent
-
- if not os.path.exists(ofolder):
- os.makedirs(ofolder)
-
- if totSize <= 4294901760:
- fat="exfat"
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294901760)
- index=0
- outfile=outfile[:-1]+str(index)
- if fx=="folder" and fat=="fat32":
- output_folder ="archfolder"
- output_folder = os.path.join(ofolder, output_folder)
- outfile = os.path.join(output_folder, "00")
- if not os.path.exists(output_folder):
- os.makedirs(output_folder)
- c=0
-
- Print.info('Generating NSP:')
- if rightsId !=0:
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- print("")
-
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing header...')
- outf = open(outfile, 'w+b')
- outf.write(hd)
- t.update(len(hd))
- c=c+len(hd)
- block=4294901760
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- vfragment="false"
- if type(nca) == Nca and (str(nca.header.contentType) != 'Content.META'):
- if (delta == False) and (str(nca.header.contentType) == 'Content.DATA'):
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- t.write(tabs+'- Skipping delta fragment: ' + str(nca._path))
- continue
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if nca.header.getRightsId() != 0:
- nca.rewind()
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- if keypatch != 'false':
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- if nca.header.getRightsId() == 0:
- nca.rewind()
- encKeyBlock = nca.header.getKeyBlock()
- if keypatch != 'false':
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- t.write('')
- t.write(tabs+'- Appending: ' + str(nca._path))
- nca.rewind()
- i=0
- for data in iter(lambda: nca.read(int(buffer)), ""):
- if i==0:
- newheader=self.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- nca.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- nca.close()
- break
- if type(nca) == Nca and str(nca.header.contentType) == 'Content.META':
- filename = str(nca._path)
- filepath = os.path.join(outfolder, filename)
- xml_file=filepath[:-3]+'xml'
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- size=os.path.getsize(filepath)
- t.write(tabs+'- Appending: ' + str(nca._path))
- for data in iter(lambda: target.read(int(size)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- target.close()
- break
- try:
- os.remove(filepath)
- except:
- pass
- with open(xml_file, 'r+b') as xmlf:
- size=os.path.getsize(xml_file)
- xmlname=str(nca._path)
- xmlname=xmlname[:-3]+'xml'
- t.write(tabs+'- Appending: ' + xmlname)
- xmlf.seek(0x00)
- for data in iter(lambda: xmlf.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- xmlf.close()
- break
- try:
- os.remove(xml_file)
- except:
- pass
- t.close()
- print("")
- print("Closing file. Please wait")
- outf.close()
-
-#///////////////////////////////////////////////////
-#DIRECT XCI TO XCI
-#///////////////////////////////////////////////////
-
- def c_xci_direct(self,buffer,outfile,ofolder,fat,delta,metapatch,RSV_cap,keypatch):
- if keypatch != 'false':
- try:
- keypatch = int(keypatch)
- except:
- print("New keygeneration is no valid integer")
- indent = 1
- rightsId = 0
- tabs = '\t' * indent
- xci_header,game_info,sig_padding,xci_certificate,root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier=self.get_xciheader(delta)
-
- totSize=len(xci_header)+len(game_info)+len(sig_padding)+len(xci_certificate)+rootSize
-
- if os.path.exists(outfile) and os.path.getsize(outfile) == totSize:
- Print.info('\t\tRepack %s is already complete!' % outfile)
- return
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Ticket:
- masterKeyRev = file.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = file.getRightsId()
- ticket=file
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Nca:
- if file.header.getRightsId() != 0:
- if file.header.masterKeyRev != masterKeyRev:
- print('WARNING!!! Mismatched masterKeyRevs!')
- print(f"{str(file._path)} - {file.header.masterKeyRev}")
- print(f"{str(ticket._path)} - {masterKeyRev}")
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Nca:
- if file.header.getRightsId() != 0:
- if file.header.getCryptoType2() == 0:
- if file.header.getCryptoType() == 2:
- masterKeyRev = 2
- titleKeyDec = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- break
-
- contTR=0
- contGC=0
- iscartridge=False
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- contTR+=1
- if nca.header.getgamecard() == 0:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0:
- contGC+=1
- else:
- contGC+=0
- else:
- contGC+=1
- if contTR == contGC and contTR>0 and contGC>0:
- iscartridge=True
-
- Print.info('Generating XCI:')
- if rightsId !=0:
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- print("")
-
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294934528)
- index=0
- outfile=outfile[:-1]+str(index)
- c=0
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing XCI header...')
- if not os.path.exists(ofolder):
- os.makedirs(ofolder)
- outf = open(outfile, 'w+b')
- outf.write(xci_header)
- t.update(len(xci_header))
- c=c+len(xci_header)
- t.write(tabs+'- Writing XCI game info...')
- outf.write(game_info)
- t.update(len(game_info))
- c=c+len(game_info)
- t.write(tabs+'- Generating padding...')
- outf.write(sig_padding)
- t.update(len(sig_padding))
- c=c+len(sig_padding)
- t.write(tabs+'- Writing XCI certificate...')
- outf.write(xci_certificate)
- t.update(len(xci_certificate))
- c=c+len(xci_certificate)
- t.write(tabs+'- Writing ROOT HFS0 header...')
- outf.write(root_header)
- t.update(len(root_header))
- c=c+len(root_header)
- t.write(tabs+'- Writing UPDATE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(upd_multiplier))
- outf.write(upd_header)
- t.update(len(upd_header))
- c=c+len(upd_header)
- t.write(tabs+'- Writing NORMAL partition header...')
- t.write(tabs+' Calculated multiplier: '+str(norm_multiplier))
- outf.write(norm_header)
- t.update(len(norm_header))
- c=c+len(norm_header)
- t.write(tabs+'- Writing SECURE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(sec_multiplier))
- outf.write(sec_header)
- t.update(len(sec_header))
- c=c+len(sec_header)
-
- block=4294934528
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- vfragment="false"
- if type(nca) == Nca and (str(nca.header.contentType) != 'Content.META'):
- if (delta == False) and (str(nca.header.contentType) == 'Content.DATA'):
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if vfragment=="true":
- t.write(tabs+'- Skipping delta fragment: ' + str(nca._path))
- continue
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- if nca.header.getgamecard() == 0:
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0 and iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- else:
- if iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- if nca.header.getRightsId() != 0:
- nca.rewind()
- t.write('')
- t.write(tabs+'* Appending: ' + str(nca._path))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- if keypatch != 'false':
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- if nca.header.getRightsId() == 0:
- nca.rewind()
- t.write('')
- t.write(tabs+'* Appending: ' + str(nca._path))
- encKeyBlock = nca.header.getKeyBlock()
- if keypatch != 'false':
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- nca.rewind()
- i=0
- for data in iter(lambda: nca.read(int(buffer)), ""):
- if i==0:
- newheader=self.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- nca.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- if type(nca) == Nca and str(nca.header.contentType) == 'Content.META':
- nca.rewind()
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- if nca.header.getgamecard() == 0:
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0 and iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- else:
- if iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- filename = str(nca._path)
- outfolder = str(ofolder)
- filepath = os.path.join(outfolder, filename)
- fp = open(filepath, 'w+b')
- nca.rewind()
- t.write('')
- t.write(tabs+'* Getting: ' + str(nca._path))
- for data in iter(lambda: nca.read(int(buffer)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- break
- fp.close()
- #///////////////////////////////////
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- encKeyBlock = target.header.getKeyBlock()
- if keypatch != 'false':
- if keypatch < target.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(nca, keypatch,encKeyBlock,t)
- newheader=self.get_newheader(target,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- target.rewind()
- target.write(newheader)
- target.close()
- if metapatch == 'true':
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- if str(target.header.contentType) == 'Content.META':
- for pfs0 in target:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- t.write(tabs + '-------------------------------------')
- t.write(tabs +'DLC -> No need to patch the meta' )
- t.write(tabs + '-------------------------------------')
- target.close()
- else:
- target.close()
- self.patcher_meta(filepath,RSV_cap,t)
- target = Fs.Nca(filepath, 'r+b')
- target.rewind()
- size=os.path.getsize(filepath)
- t.write(tabs+'- Appending: ' + str(nca._path))
- for data in iter(lambda: target.read(int(size)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- target.close()
- try:
- os.remove(filepath)
- except:
- pass
- t.close()
- print("")
- print("Closing file. Please wait")
- outf.close()
-
-
- def patcher_meta(self,filepath,RSV_cap,t):
- indent = 1
- RSV_cap=int(RSV_cap)
- tabs = '\t' * indent
- t.write(tabs + '-------------------------------------')
- t.write(tabs + '')
- t.write(tabs + 'Checking meta: ')
- meta_nca = Fs.Nca(filepath, 'r+b')
- crypto1=meta_nca.header.getCryptoType()
- crypto2=meta_nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=meta_nca.header.getCryptoType()
- else:
- keygen=meta_nca.header.getCryptoType2()
- else:
- keygen=meta_nca.header.getCryptoType2()
- RSV=meta_nca.get_req_system()
- t.write(tabs + '- RequiredSystemVersion = ' + str(RSV))
- RSVmin=sq_tools.getMinRSV(keygen,RSV)
- RSVmax=sq_tools.getTopRSV(keygen,RSV)
- if RSV > RSVmin:
- if RSVmin >= RSV_cap:
- min_sversion=meta_nca.write_req_system(RSVmin)
- t.write(tabs + '- New RequiredSystemVersion = '+ str(min_sversion))
- else:
- if keygen < 4:
- if RSV > RSVmax:
- meta_nca.write_req_system(RSV_cap)
- else:
- meta_nca.write_req_system(RSV_cap)
- meta_nca.flush()
- meta_nca.close()
- t.write(tabs + '')
- t.write(tabs + 'Updating cnmt hashes: ')
- ############################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha=meta_nca.calc_pfs0_hash()
- t.write(tabs + '- Calculated hash from pfs0:')
- t.write(tabs + ' + ' + str(hx(sha)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.set_pfs0_hash(sha)
- meta_nca.flush()
- meta_nca.close()
- ############################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha2=meta_nca.calc_htable_hash()
- t.write(tabs + '- Calculated table hash: ')
- t.write(tabs + ' + ' + str(hx(sha2)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.header.set_htable_hash(sha2)
- meta_nca.flush()
- meta_nca.close()
- ########################
- meta_nca = Fs.Nca(filepath, 'r+b')
- sha3=meta_nca.header.calculate_hblock_hash()
- t.write(tabs + '- Calculated header block hash: ')
- t.write(tabs + ' + ' + str(hx(sha3)))
- meta_nca.flush()
- meta_nca.close()
- meta_nca = Fs.Nca(filepath, 'r+b')
- meta_nca.header.set_hblock_hash(sha3)
- meta_nca.flush()
- meta_nca.close()
- t.write(tabs + '-------------------------------------')
- else:
- t.write(tabs +'-> No need to patch the meta' )
- t.write(tabs + '-------------------------------------')
- meta_nca.close()
-
- def get_xciheader(self,delta):
- upd_list=list()
- upd_fileSizes = list()
- norm_list=list()
- norm_fileSizes = list()
- sec_list=list()
- sec_fileSizes = list()
- sec_shalist = list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- vfragment="false"
- if type(file) == Nca:
- if (delta == False) and (str(file.header.contentType) == 'Content.DATA'):
- for f in file:
- for fn in f:
- filename = str(fn._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- continue
- sec_list.append(file._path)
- sec_fileSizes.append(file.header.size)
- file.rewind()
- hblock = file.read(0x200)
- sha=sha256(hblock).hexdigest()
- sec_shalist.append(sha)
-
- hfs0 = Fs.Hfs0(None, None)
- root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier=hfs0.gen_rhfs0_head(upd_list,norm_list,sec_list,sec_fileSizes,sec_shalist)
- #print (hx(root_header))
- tot_size=0xF000+rootSize
-
- signature=sq_tools.randhex(0x100)
- signature= bytes.fromhex(signature)
-
- sec_offset=root_header[0x90:0x90+0x8]
- sec_offset=int.from_bytes(sec_offset, byteorder='little')
- sec_offset=int((sec_offset+0xF000+0x200)/0x200)
- sec_offset=sec_offset.to_bytes(4, byteorder='little')
- back_offset=(0xFFFFFFFF).to_bytes(4, byteorder='little')
- kek=(0x00).to_bytes(1, byteorder='big')
- cardsize,access_freq=sq_tools.getGCsize(tot_size)
- cardsize=cardsize.to_bytes(1, byteorder='big')
- GC_ver=(0x00).to_bytes(1, byteorder='big')
- GC_flag=(0x00).to_bytes(1, byteorder='big')
- pack_id=(0x8750F4C0A9C5A966).to_bytes(8, byteorder='big')
- valid_data=int(((tot_size-0x1)/0x200))
- valid_data=valid_data.to_bytes(8, byteorder='little')
-
- try:
- key= Keys.get('xci_header_key')
- key= bytes.fromhex(key)
- IV=sq_tools.randhex(0x10)
- IV= bytes.fromhex(IV)
- xkey=True
- #print(hx(IV))
- #print("i'm here")
- except:
- IV=(0x5B408B145E277E81E5BF677C94888D7B).to_bytes(16, byteorder='big')
- xkey=False
- #print("i'm here 2")
-
- HFS0_offset=(0xF000).to_bytes(8, byteorder='little')
- len_rHFS0=(len(root_header)).to_bytes(8, byteorder='little')
- sha_rheader=sha256(root_header[0x00:0x200]).hexdigest()
- sha_rheader=bytes.fromhex(sha_rheader)
- sha_ini_data=bytes.fromhex('1AB7C7B263E74E44CD3C68E40F7EF4A4D6571551D043FCA8ECF5C489F2C66E7E')
- SM_flag=(0x01).to_bytes(4, byteorder='little')
- TK_flag=(0x02).to_bytes(4, byteorder='little')
- K_flag=(0x0).to_bytes(4, byteorder='little')
- end_norm = sec_offset
-
- header = b''
- header += signature
- header += b'HEAD'
- header += sec_offset
- header += back_offset
- header += kek
- header += cardsize
- header += GC_ver
- header += GC_flag
- header += pack_id
- header += valid_data
- header += IV
- header += HFS0_offset
- header += len_rHFS0
- header += sha_rheader
- header += sha_ini_data
- header += SM_flag
- header += TK_flag
- header += K_flag
- header += end_norm
-
- #Game_info
- if xkey==True:
- firm_ver='0100000000000000'
- access_freq=access_freq
- Read_Wait_Time='88130000'
- Read_Wait_Time2='00000000'
- Write_Wait_Time='00000000'
- Write_Wait_Time2='00000000'
- Firmware_Mode='00110C00'
- CUP_Version='5a000200'
- Empty1='00000000'
- Upd_Hash='9bfb03ddbb7c5fca'
- CUP_Id='1608000000000001'
- Empty2='00'*0x38
- #print(hx(Empty2))
-
- firm_ver=bytes.fromhex(firm_ver)
- access_freq=bytes.fromhex(access_freq)
- Read_Wait_Time=bytes.fromhex(Read_Wait_Time)
- Read_Wait_Time2=bytes.fromhex(Read_Wait_Time2)
- Write_Wait_Time=bytes.fromhex(Write_Wait_Time)
- Write_Wait_Time2=bytes.fromhex(Write_Wait_Time2)
- Firmware_Mode=bytes.fromhex(Firmware_Mode)
- CUP_Version=bytes.fromhex(CUP_Version)
- Empty1=bytes.fromhex(Empty1)
- Upd_Hash=bytes.fromhex(Upd_Hash)
- CUP_Id=bytes.fromhex(CUP_Id)
- Empty2=bytes.fromhex(Empty2)
-
- Game_info = b''
- Game_info += firm_ver
- Game_info += access_freq
- Game_info += Read_Wait_Time
- Game_info += Read_Wait_Time2
- Game_info += Write_Wait_Time
- Game_info += Write_Wait_Time2
- Game_info += Firmware_Mode
- Game_info += CUP_Version
- Game_info += Empty1
- Game_info += Upd_Hash
- Game_info += CUP_Id
- Game_info += Empty2
-
- gamecardInfoIV=IV[::-1]
- crypto = aes128.AESCBC(key, gamecardInfoIV)
- enc_info=crypto.encrypt(Game_info)
- if xkey==False:
- enc_info=sq_tools.get_enc_gameinfo(tot_size)
-
- #print (hx(enc_info))
-
- #Padding
- sig_padding='00'*0x6E00
- sig_padding=bytes.fromhex(sig_padding)
- #print (hx(sig_padding))
-
- #CERT
- fake_CERT='FF'*0x8000
- fake_CERT=bytes.fromhex(fake_CERT)
- #print (hx(fake_CERT))
- return header,enc_info,sig_padding,fake_CERT,root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier
-
- def get_new_cryptoblock(self, nca, newMasterKeyRev,encKeyBlock,t):
- indent = 1
- tabs = '\t' * indent
- indent2 = 2
- tabs2 = '\t' * indent2
-
- masterKeyRev = nca.header.getCryptoType2()
-
- if type(nca) == Nca:
- if nca.header.getCryptoType2() != newMasterKeyRev:
- t.write(tabs + '-----------------------------------')
- t.write(tabs + 'Changing keygeneration from %d to %s' % ( nca.header.getCryptoType2(), str(newMasterKeyRev)))
- t.write(tabs + '-----------------------------------')
- #encKeyBlock = nca.header.getKeyBlock()
- if sum(encKeyBlock) != 0:
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)
- t.write(tabs2 + '+ decrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- crypto = aes128.AESECB(key)
- decKeyBlock = crypto.decrypt(encKeyBlock)
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex)
- t.write(tabs2 + '+ encrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(newMasterKeyRev), nca.header.keyIndex))
- crypto = aes128.AESECB(key)
- reEncKeyBlock = crypto.encrypt(decKeyBlock)
- encKeyBlock = reEncKeyBlock
- if newMasterKeyRev >= 3:
- crypto1=2
- crypto2=newMasterKeyRev
- if newMasterKeyRev == 2:
- crypto1=2
- crypto2=0
- if newMasterKeyRev < 2:
- crypto1=newMasterKeyRev
- crypto2=0
- return encKeyBlock,crypto1,crypto2
- return encKeyBlock,nca.header.getCryptoType(),nca.header.getCryptoType2()
-
- def get_newheader(self,target,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag):
- target.rewind()
- rawhead=target.read(0xC00)
- rawhead=hcrypto.decrypt(rawhead)
- header = b''
- header += rawhead[0x00:0x00+0x204]
- #isgamecard 0x204
- GC=bytes.fromhex(gc_flag)
- header += GC
- #contentType 0x205
- header += rawhead[0x205:0x206]
- #crypto 1 0x206
- c1=crypto1.to_bytes(1, byteorder='big')
- header += c1
- #########
- header += rawhead[0x207:0x220]
- #crypto 1 0x220
- c2=crypto2.to_bytes(1, byteorder='big')
- header += c2
- #########
- header += rawhead[0x221:0x230]
- tr='00'*0x10
- tr=bytes.fromhex(tr)
- header += tr
- header += rawhead[0x240:0x240+0xC0]
- header += encKeyBlock
- header += rawhead[0x340:]
- newheader=hcrypto.encrypt(header)
- return newheader
-
-
- def gen_nsp_head(self,files,delta,inc_xml,ofolder):
-
- filesNb = len(files)
- stringTable = '\x00'.join(str(nca) for nca in files)
- headerSize = 0x10 + (filesNb)*0x18 + len(stringTable)
- remainder = 0x10 - headerSize%0x10
- headerSize += remainder
-
- fileSizes = list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- vfragment="false"
- if type(nca) == Nca:
- if (delta == False) and (str(nca.header.contentType) == 'Content.DATA'):
- for f in nca:
- for file in f:
- filename = str(file._path)
- if filename=="fragment":
- vfragment="true"
- if str(vfragment)=="true":
- continue
- fileSizes.append(nca.header.size)
- if str(nca.header.contentType) == 'Content.META' and inc_xml==True:
- xmlname=nca._path
- xmlname=xmlname[:-3]+'xml'
- xmlpath = os.path.join(ofolder, xmlname)
- size=os.path.getsize(xmlpath)
- fileSizes.append(int(size))
-
- fileOffsets = [sum(fileSizes[:n]) for n in range(filesNb)]
-
- fileNamesLengths = [len(str(nca))+1 for nca in files] # +1 for the \x00
- stringTableOffsets = [sum(fileNamesLengths[:n]) for n in range(filesNb)]
-
- header = b''
- header += b'PFS0'
- header += pk(' crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- ncalist=list()
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- cnmt.readInt64()
- ttag=''
- CTYPE='BASE'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [UPD]'
- CTYPE='UPDATE'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [DLC]'
- CTYPE='DLC'
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- version='[v'+version+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name in completefilelist:
- ncalist.append(nca_name)
- nca_meta=str(nca._path)
- if nca_meta in completefilelist:
- ncalist.append(nca_meta)
- target=str(nca._path)
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID)
- tit_name = (re.sub(r'[\/\\\:\*\?\!\"\<\>\|\.\s™©®()\~]+', ' ', tit_name))
- tit_name = tit_name.strip()
- if tit_name=='DLC' and (str(titleid2).endswith('000') or str(titleid2).endswith('800')):
- tit_name='-'
- editor='-'
- tid='['+titleid2+']'
- filename=tit_name+' '+tid+' '+version
- titlerights=titleid2+str('0'*15)+str(crypto2)
- contentlist.append([filename,titleid2,titlerights,keygen,ncalist,CTYPE])
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Ticket or file._path.endswith('.cert'):
- test=file._path
- test=test[0:32]
- for i in contentlist:
- if i[2]==test:
- i[4].append(file._path)
- elif file._path.endswith('.xml'):
- test=file._path
- test=test[0:-4]+'.nca'
- for i in contentlist:
- if test in i[4]:
- i[4].append(file._path)
-
- '''
- for i in contentlist:
- print("")
- print('Filename: '+i[0])
- print('TitleID: '+i[1])
- print('TitleRights: '+i[2])
- print('Keygen: '+str(i[3]))
- for j in i[4]:
- print (j)
- '''
- for i in contentlist:
- if export == 'nsp':
- self.cd_spl_nsp(buffer,i[0],ofolder,i[4],fat,fx)
- if export == 'xci':
- if i[5] != 'BASE':
- self.cd_spl_nsp(buffer,i[0],ofolder,i[4],fat,fx)
- else:
- self.cd_spl_xci(buffer,i[0],ofolder,i[4],fat,fx)
- if export == 'both':
- if i[5] != 'BASE':
- self.cd_spl_nsp(buffer,i[0],ofolder,i[4],fat,fx)
- else:
- self.cd_spl_nsp(buffer,i[0],ofolder,i[4],fat,fx)
- self.cd_spl_xci(buffer,i[0],ofolder,i[4],fat,fx)
-
- def cd_spl_nsp(self,buffer,outfile,ofolder,filelist,fat,fx):
- outfile=outfile+'.nsp'
- filepath = os.path.join(ofolder, outfile)
- if os.path.exists(filepath) and os.path.getsize(filepath) == totSize:
- Print.info('\t\tRepack %s is already complete!' % outfile)
- return
- if not os.path.exists(ofolder):
- os.makedirs(ofolder)
-
- contentlist=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if file._path in filelist:
- contentlist.append(file._path)
-
- hd = self.cd_spl_gen_nsph(contentlist)
- totSize = len(hd)
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if file._path in contentlist:
- totSize=totSize+file.size
-
- indent = 1
- rightsId = 0
- tabs = '\t' * indent
-
- if totSize <= 4294901760:
- fat="exfat"
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294901760)
- index=0
- filepath=filepath[:-1]+str(index)
-
- if fx=="folder" and fat=="fat32":
- output_folder ="archfolder"
- output_folder = os.path.join(ofolder, output_folder)
- filepath = os.path.join(output_folder, "00")
- if not os.path.exists(output_folder):
- os.makedirs(output_folder)
- c=0
-
- Print.info("")
- Print.info('Generating NSP:')
- Print.info('Filename: '+outfile)
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing header...')
- outf = open(str(filepath), 'w+b')
- outfile=str(filepath)
- outf.write(hd)
- t.update(len(hd))
- c=c+len(hd)
- block=4294901760
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Nca and file._path in contentlist:
- crypto1=file.header.getCryptoType()
- crypto2=file.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), file.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- file.rewind()
- encKeyBlock = file.header.getKeyBlock()
- t.write(tabs+'- Appending: ' + str(file._path))
- file.rewind()
- i=0
- for data in iter(lambda: file.read(int(buffer)), ""):
- if i==0:
- newheader=self.get_newheader(file,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- file.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- if type(file) != Nca and file._path in contentlist:
- file.rewind()
- t.write(tabs+'- Appending: ' + str(file._path))
- for data in iter(lambda: file.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- t.close()
- print("Closing file. Please wait")
- outf.close()
-
- def cd_spl_gen_nsph(self,filelist):
- filesNb = len(filelist)
- stringTable = '\x00'.join(str(file) for file in filelist)
- headerSize = 0x10 + (filesNb)*0x18 + len(stringTable)
- remainder = 0x10 - headerSize%0x10
- headerSize += remainder
-
- fileSizes = list()
- self.rewind()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if file._path in filelist:
- fileSizes.append(file.size)
-
- fileOffsets = [sum(fileSizes[:n]) for n in range(filesNb)]
- fileNamesLengths = [len(str(file))+1 for file in filelist] # +1 for the \x00
- stringTableOffsets = [sum(fileNamesLengths[:n]) for n in range(filesNb)]
-
- header = b''
- header += b'PFS0'
- header += pk('crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0:
- contGC+=1
- else:
- contGC+=0
- else:
- contGC+=1
- if contTR == contGC and contTR>0 and contGC>0:
- iscartridge=True
-
- Print.info("")
- Print.info('Generating XCI:')
- Print.info('Filename: '+outfile)
-
- if rightsId !=0:
- Print.info('rightsId =\t' + hex(rightsId))
- Print.info('titleKeyDec =\t' + str(hx(titleKeyDec)))
- Print.info('masterKeyRev =\t' + hex(masterKeyRev))
- print("")
-
- if totSize <= 4294934528:
- fat="exfat"
- if fat=="fat32":
- splitnumb=math.ceil(totSize/4294934528)
- index=0
- filepath=filepath[:-1]+str(index)
-
- c=0
- t = tqdm(total=totSize, unit='B', unit_scale=True, leave=False)
- t.write(tabs+'- Writing XCI header...')
- outf = open(filepath, 'w+b')
- outf.write(xci_header)
- t.update(len(xci_header))
- c=c+len(xci_header)
- t.write(tabs+'- Writing XCI game info...')
- outf.write(game_info)
- t.update(len(game_info))
- c=c+len(game_info)
- t.write(tabs+'- Generating padding...')
- outf.write(sig_padding)
- t.update(len(sig_padding))
- c=c+len(sig_padding)
- t.write(tabs+'- Writing XCI certificate...')
- outf.write(xci_certificate)
- t.update(len(xci_certificate))
- c=c+len(xci_certificate)
- t.write(tabs+'- Writing ROOT HFS0 header...')
- outf.write(root_header)
- t.update(len(root_header))
- c=c+len(root_header)
- t.write(tabs+'- Writing UPDATE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(upd_multiplier))
- outf.write(upd_header)
- t.update(len(upd_header))
- c=c+len(upd_header)
- t.write(tabs+'- Writing NORMAL partition header...')
- t.write(tabs+' Calculated multiplier: '+str(norm_multiplier))
- outf.write(norm_header)
- t.update(len(norm_header))
- c=c+len(norm_header)
- t.write(tabs+'- Writing SECURE partition header...')
- t.write(tabs+' Calculated multiplier: '+str(sec_multiplier))
- outf.write(sec_header)
- t.update(len(sec_header))
- c=c+len(sec_header)
- outfile=str(filepath)
- block=4294934528
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca and nca._path in contentlist:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- if nca.header.getgamecard() == 0:
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0 and iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- else:
- if iscartridge == True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- if nca.header.getRightsId() != 0:
- nca.rewind()
- t.write('')
- t.write(tabs+'* Appending: ' + str(nca._path))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- if nca.header.getRightsId() == 0:
- nca.rewind()
- t.write('')
- t.write(tabs+'* Appending: ' + str(nca._path))
- encKeyBlock = nca.header.getKeyBlock()
- nca.rewind()
- i=0
- for data in iter(lambda: nca.read(int(buffer)), ""):
- if i==0:
- newheader=self.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- nca.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- outf.write(dat2)
- outf.flush()
- outf.close()
- t.update(len(dat2))
- index=index+1
- outfile=outfile[0:-1]
- outfile=outfile+str(index)
- outf = open(outfile, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- outf.write(dat2)
- t.update(len(dat2))
- outf.flush()
- else:
- outf.write(data)
- t.update(len(data))
- c=c+len(data)
- outf.flush()
- if not data:
- break
- t.close()
- print("")
- print("Closing file. Please wait")
- outf.close()
-
- def cd_spl_gen_xcih(self,filelist):
- upd_list=list()
- upd_fileSizes = list()
- norm_list=list()
- norm_fileSizes = list()
- sec_list=list()
- sec_fileSizes = list()
- sec_shalist = list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if file._path in filelist:
- sec_list.append(file._path)
- sec_fileSizes.append(file.size)
- file.rewind()
- hblock = file.read(0x200)
- sha=sha256(hblock).hexdigest()
- sec_shalist.append(sha)
-
- hfs0 = Fs.Hfs0(None, None)
- root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier=hfs0.gen_rhfs0_head(upd_list,norm_list,sec_list,sec_fileSizes,sec_shalist)
- #print (hx(root_header))
- tot_size=0xF000+rootSize
-
- signature=sq_tools.randhex(0x100)
- signature= bytes.fromhex(signature)
-
- sec_offset=root_header[0x90:0x90+0x8]
- sec_offset=int.from_bytes(sec_offset, byteorder='little')
- sec_offset=int((sec_offset+0xF000+0x200)/0x200)
- sec_offset=sec_offset.to_bytes(4, byteorder='little')
- back_offset=(0xFFFFFFFF).to_bytes(4, byteorder='little')
- kek=(0x00).to_bytes(1, byteorder='big')
- cardsize,access_freq=sq_tools.getGCsize(tot_size)
- cardsize=cardsize.to_bytes(1, byteorder='big')
- GC_ver=(0x00).to_bytes(1, byteorder='big')
- GC_flag=(0x00).to_bytes(1, byteorder='big')
- pack_id=(0x8750F4C0A9C5A966).to_bytes(8, byteorder='big')
- valid_data=int(((tot_size-0x1)/0x200))
- valid_data=valid_data.to_bytes(8, byteorder='little')
-
- try:
- Keys.get('xci_header_key')
- key= Keys.get('xci_header_key')
- key= bytes.fromhex(key)
- IV=sq_tools.randhex(0x10)
- IV= bytes.fromhex(IV)
- xkey=True
- #print(hx(IV))
- #print("i'm here")
- except:
- IV=(0x5B408B145E277E81E5BF677C94888D7B).to_bytes(16, byteorder='big')
- xkey=False
- #print("i'm here 2")
-
- HFS0_offset=(0xF000).to_bytes(8, byteorder='little')
- len_rHFS0=(len(root_header)).to_bytes(8, byteorder='little')
- sha_rheader=sha256(root_header[0x00:0x200]).hexdigest()
- sha_rheader=bytes.fromhex(sha_rheader)
- sha_ini_data=bytes.fromhex('1AB7C7B263E74E44CD3C68E40F7EF4A4D6571551D043FCA8ECF5C489F2C66E7E')
- SM_flag=(0x01).to_bytes(4, byteorder='little')
- TK_flag=(0x02).to_bytes(4, byteorder='little')
- K_flag=(0x0).to_bytes(4, byteorder='little')
- end_norm = sec_offset
-
- header = b''
- header += signature
- header += b'HEAD'
- header += sec_offset
- header += back_offset
- header += kek
- header += cardsize
- header += GC_ver
- header += GC_flag
- header += pack_id
- header += valid_data
- header += IV
- header += HFS0_offset
- header += len_rHFS0
- header += sha_rheader
- header += sha_ini_data
- header += SM_flag
- header += TK_flag
- header += K_flag
- header += end_norm
-
- #Game_info
- if xkey==True:
- firm_ver='0100000000000000'
- access_freq=access_freq
- Read_Wait_Time='88130000'
- Read_Wait_Time2='00000000'
- Write_Wait_Time='00000000'
- Write_Wait_Time2='00000000'
- Firmware_Mode='00110C00'
- CUP_Version='5a000200'
- Empty1='00000000'
- Upd_Hash='9bfb03ddbb7c5fca'
- CUP_Id='1608000000000001'
- Empty2='00'*0x38
- #print(hx(Empty2))
-
- firm_ver=bytes.fromhex(firm_ver)
- access_freq=bytes.fromhex(access_freq)
- Read_Wait_Time=bytes.fromhex(Read_Wait_Time)
- Read_Wait_Time2=bytes.fromhex(Read_Wait_Time2)
- Write_Wait_Time=bytes.fromhex(Write_Wait_Time)
- Write_Wait_Time2=bytes.fromhex(Write_Wait_Time2)
- Firmware_Mode=bytes.fromhex(Firmware_Mode)
- CUP_Version=bytes.fromhex(CUP_Version)
- Empty1=bytes.fromhex(Empty1)
- Upd_Hash=bytes.fromhex(Upd_Hash)
- CUP_Id=bytes.fromhex(CUP_Id)
- Empty2=bytes.fromhex(Empty2)
-
- Game_info = b''
- Game_info += firm_ver
- Game_info += access_freq
- Game_info += Read_Wait_Time
- Game_info += Read_Wait_Time2
- Game_info += Write_Wait_Time
- Game_info += Write_Wait_Time2
- Game_info += Firmware_Mode
- Game_info += CUP_Version
- Game_info += Empty1
- Game_info += Upd_Hash
- Game_info += CUP_Id
- Game_info += Empty2
-
- gamecardInfoIV=IV[::-1]
- crypto = aes128.AESCBC(key, gamecardInfoIV)
- enc_info=crypto.encrypt(Game_info)
- if xkey==False:
- enc_info=sq_tools.get_enc_gameinfo(tot_size)
-
- #print (hx(enc_info))
-
- #Padding
- sig_padding='00'*0x6E00
- sig_padding=bytes.fromhex(sig_padding)
- #print (hx(sig_padding))
-
- #CERT
- fake_CERT='FF'*0x8000
- fake_CERT=bytes.fromhex(fake_CERT)
- #print (hx(fake_CERT))
-
-
- return header,enc_info,sig_padding,fake_CERT,root_header,upd_header,norm_header,sec_header,rootSize,upd_multiplier,norm_multiplier,sec_multiplier
-
-
- def get_content(self,ofolder,vkeypatch,delta):
- if vkeypatch=='false':
- vkeypatch=False
- contentlist=list()
- ncalist=list()
- completefilelist=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- completefilelist.append(str(file._path))
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- ncalist=list()
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- cnmt.readInt64()
- ttag=''
- CTYPE='BASE'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [UPD]'
- CTYPE='UPDATE'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [DLC]'
- CTYPE='DLC'
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- size=int.from_bytes(size, byteorder='little')
- ncatype = cnmt.readInt8()
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- ver=version
- ver='[v'+ver+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name in completefilelist:
- if delta==False and ncatype==6:
- print(tabs+'- Excluding delta fragment '+nca_name)
- continue
- else:
- ncalist.append([nca_name,size])
- nca_meta=str(nca._path)
- if nca_meta in completefilelist:
- ncalist.append([nca_meta,nca.size])
- if ofolder != False:
- target = Fs.Nca(nca, 'r+b')
- target.rewind()
- outf= os.path.join(ofolder, str(nca._path))
- fp = open(outf, 'w+b')
- for data in iter(lambda: target.read(int(32768)), ""):
- fp.write(data)
- fp.flush()
- if not data:
- fp.close()
- break
- target = Fs.Nca(outf, 'r+b')
- block = target.read()
- nsha=sha256(block).hexdigest()
- target.rewind()
- if vkeypatch == False:
- xml=target.xml_gen(ofolder,nsha)
- else:
- xml=target.xml_gen_mod(ofolder,nsha,vkeypatch)
- xmlname=nca_meta[:-3]+'xml'
- xmlsize=os.path.getsize(xml)
- ncalist.append([xmlname,xmlsize])
- target.close()
- try:
- os.remove(outf)
- except:
- pass
- titlerights=titleid2+str('0'*15)+str(crypto2)
- contentlist.append([str(self._path),titleid2,titlerights,keygen,ncalist,CTYPE,version])
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Ticket or file._path.endswith('.cert'):
- test=file._path
- test=test[0:32]
- for i in contentlist:
- if i[2]==test:
- i[4].append([file._path,file.size])
- '''
- for i in contentlist:
- print('Filename: '+i[0])
- print('TitleID: '+i[1])
- print('TitleRights: '+i[2])
- print('Version: '+str(i[6]))
- print('Keygen: '+str(i[3]))
- print('Content type: '+str(i[5]))
- for j in i[4]:
- print (j)
- print("")
- '''
- return contentlist
-
- def get_content_placeholder(self,ofolder):
- contentlist=list()
- ncalist=list()
- completefilelist=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Nca:
- if str(file.header.contentType) != 'Content.META' and str(file.header.contentType) != 'Content.CONTROL':
- continue
- else:
- completefilelist.append(str(file._path))
- elif str(file._path).endswith('.xml'):
- pass
- else:
- completefilelist.append(str(file._path))
- print (completefilelist)
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- ncalist=list()
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- cnmt.readInt64()
- ttag=''
- CTYPE='BASE'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [UPD]'
- CTYPE='UPDATE'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [DLC]'
- CTYPE='DLC'
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- size=int.from_bytes(size, byteorder='little')
- ncatype = cnmt.read(0x1)
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- ver=version
- ver='[v'+ver+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- if nca_name in completefilelist:
- ncalist.append([nca_name,size])
- nca_meta=str(nca._path)
- if nca_meta in completefilelist:
- ncalist.append([nca_meta,nca.size])
- titlerights=titleid2+str('0'*15)+str(crypto2)
- contentlist.append([str(self._path),titleid2,titlerights,keygen,ncalist,CTYPE,version])
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Ticket or file._path.endswith('.cert'):
- test=file._path
- test=test[0:32]
- for i in contentlist:
- if i[2]==test:
- i[4].append([file._path,file.size])
- '''
- for i in contentlist:
- print('Filename: '+i[0])
- print('TitleID: '+i[1])
- print('TitleRights: '+i[2])
- print('Version: '+str(i[6]))
- print('Keygen: '+str(i[3]))
- print('Content type: '+str(i[5]))
- for j in i[4]:
- print (j)
- print("")
- '''
-
- return contentlist
-
-
- def get_title(self,baseid,roman=True,tag=False):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- if baseid != titleid2:
- continue
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- target=str(nca._path)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'AddOnContent':
- if tag==False:
- nutdbname=nutdb.get_dlcname(titleid2)
- else:
- nutdbname=False
- if nutdbname!=False:
- title=nutdbname
- else:
- DLCnumb=str(titleid2)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- title = 'DLC number '+str(DLCnumb)
- return(title)
- title='DLC'
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- title,editor,ediver,SupLg,regionstr,isdemo=nca.get_langueblock(title,roman)
- return(title)
-
- def get_lang_tag(self,baseid):
- languetag=False
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- if baseid != titleid2:
- continue
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- target=str(nca._path)
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt == 'AddOnContent':
- return(False)
- title='DLC'
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- title,editor,ediver,SupLg,regionstr,isdemo=nca.get_langueblock(title)
- languetag='('
- if ("US (eng)" in SupLg) or ("UK (eng)" in SupLg):
- languetag=languetag+'En,'
- if "JP" in SupLg:
- languetag=languetag+'Jp,'
- if ("CAD (fr)" in SupLg) or ("FR" in SupLg):
- languetag=languetag+'Fr,'
- elif ("CAD (fr)" in SupLg):
- languetag=languetag+'CADFr,'
- elif ("FR") in SupLg:
- languetag=languetag+'Fr,'
- if "DE" in SupLg:
- languetag=languetag+'De,'
- if ("LAT (spa)" in SupLg) and ("SPA" in SupLg):
- languetag=languetag+'Es,'
- elif "LAT (spa)" in SupLg:
- languetag=languetag+'LatEs,'
- elif "SPA" in SupLg:
- languetag=languetag+'Es,'
- if "IT" in SupLg:
- languetag=languetag+'It,'
- if "DU" in SupLg:
- languetag=languetag+'Du,'
- if "POR" in SupLg:
- languetag=languetag+'Por,'
- if "RU" in SupLg:
- languetag=languetag+'Ru,'
- if "KOR" in SupLg:
- languetag=languetag+'Kor,'
- if "TAI" in SupLg:
- languetag=languetag+'Tw,'
- if "CH" in SupLg:
- languetag=languetag+'Ch,'
- languetag=languetag[:-1]
- languetag=languetag+')'
- return(languetag)
-
- def file_hash(self,target):
- indent = 1
- gamecard = False
- tabs = '\t' * indent
- sha=False;sizef=False
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- docheck = False
- if str(file._path) == target:
- file.rewind()
- hblock = file.read(0x200)
- sha=sha256(hblock).hexdigest()
- sizef=file.size
- #print(str(sizef))
- #return sha,sizef
- if type(file) == Nca and gamecard==False:
- if file.header.getgamecard() == 1:
- gamecard=True
- else:
- nca_id=file.header.titleId
- if nca_id.endswith('000') or nca_id.endswith('800'):
- if str(file.header.contentType) == 'Content.PROGRAM':
- docheck=True
- else:
- if str(file.header.contentType) == 'Content.DATA':
- docheck=True
- if docheck == True:
- crypto1=file.header.getCryptoType()
- crypto2=file.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), file.header.keyIndex))
- KB1L=file.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) == 0:
- gamecard=True
- return sha,sizef,gamecard
-
- def append_content(self,outf,target,buffer,t,includexml=True):
- indent = 1
- tabs = '\t' * indent
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if str(file._path) == target:
- if type(file) == Nca:
- fp = open(outf, 'a+b')
- file.rewind()
- t.write(tabs+'- Appending: ' + str(file._path))
- for data in iter(lambda: file.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- break
- if str(file.header.contentType) == 'Content.META' and includexml==True:
- target=str(file._path)
- xmlname=target[:-3]+'xml'
- t.write(tabs+'- Appending: ' + xmlname)
- dir=os.path.dirname(os.path.abspath(outf))
- xmlfile= os.path.join(dir, xmlname)
- xml = open(xmlfile, 'rb')
- data=xml.read()
- xml.close()
- fp.write(data)
- t.update(len(data))
- fp.flush()
- try:
- os.remove(outf)
- except:
- pass
- fp.close()
- else:
- fp = open(outf, 'a+b')
- file.rewind()
- t.write(tabs+'- Appending: ' + str(file._path))
- for data in iter(lambda: file.read(int(buffer)), ""):
- fp.write(data)
- t.update(len(data))
- fp.flush()
- if not data:
- fp.close()
- break
-
- def append_clean_content(self,outf,target,buffer,t,gamecard,keypatch,metapatch,RSV_cap,fat,fx,c,index):
- block=4294934528
- indent = 1
- tabs = '\t' * indent
- ticketlist=list()
- if keypatch != 'false':
- try:
- keypatch = int(keypatch)
- except:
- print("New keygeneration is no valid integer")
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Ticket:
- masterKeyRev = file.getMasterKeyRevision()
- titleKeyDec = Keys.decryptTitleKey(file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = str(hx(file.getRightsId().to_bytes(16, byteorder='big')))
- encryptedkey=file.getTitleKeyBlock().to_bytes(16, byteorder='big')
- ticketlist.append([masterKeyRev,rightsId,encryptedkey])
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if str(file._path) == target:
- if type(file) == Nca:
- gc_flag=file.header.getgamecard()
- if gc_flag != 0:
- if gamecard==False:
- gc_flag='00'*0x01
- else:
- gc_flag='01'*0x01
- elif gc_flag == 0:
- if gamecard==True:
- gc_flag='01'*0x01
- else:
- gc_flag='00'*0x01
- else:
- gc_flag='00'*0x01
- file.rewind()
- crypto1=file.header.getCryptoType()
- crypto2=file.header.getCryptoType2()
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- if file.header.getRightsId() != 0:
- for i in range(len(ticketlist)):
- #print(str(file.header.rightsId))
- #print(ticketlist[i][1])
- if str(file.header.rightsId) == ticketlist[i][1]:
- encryptedkey=ticketlist[i][2]
- break
- titleKeyDec = Keys.decryptTitleKey(encryptedkey, Keys.getMasterKeyIndex(masterKeyRev))
- t.write("")
- t.write(tabs+'rightsId =\t' + str(file.header.rightsId))
- t.write(tabs+'titleKeyDec =\t' + str(hx(titleKeyDec)))
- t.write(tabs+'masterKeyRev =\t' + hex(masterKeyRev))
- t.write("")
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), file.header.keyIndex))
- file.rewind()
- t.write(tabs+'* Appending: ' + str(file._path))
- encKeyBlock = crypto.encrypt(titleKeyDec * 4)
- if keypatch != 'false':
- if keypatch < file.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(file, keypatch,encKeyBlock,t)
- #t.write(str(hx(encKeyBlock)))
- newheader=self.get_newheader(file,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- #t.write(str(hx(newheader)))
- if file.header.getRightsId() == 0:
- t.write(tabs+'* Appending: ' + str(file._path))
- file.rewind()
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), file.header.keyIndex))
- encKeyBlock = file.header.getKeyBlock()
- if keypatch != 'false':
- if keypatch < file.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=self.get_new_cryptoblock(file, keypatch,encKeyBlock,t)
- newheader=self.get_newheader(file,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- if str(file.header.contentType) != 'Content.META':
- i=0
- sha=sha256()
- fp = open(outf, 'ab')
- file.rewind()
- for data in iter(lambda: file.read(int(buffer)), ""):
- if i==0:
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- sha.update(newheader)
- else:
- fp.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- sha.update(newheader)
- file.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- sha.update(data)
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- fp.flush()
- sha.update(data)
- if not data:
- break
- sha=sha.hexdigest()
- '''
- if str(file._path).endswith('.cnmt.nca'):
- newname=sha[:32]+'.cnmt.nca'
- else:
- newname=sha[:32]+'.nca'
- '''
- #t.write(tabs+'new hash: '+sha)
- #t.write(tabs+'new name: '+newname)
- fp.close()
- elif str(file.header.contentType) == 'Content.META':
- metaname = str(file._path)
- dir=os.path.dirname(os.path.abspath(outf))
- metafile=os.path.join(dir, metaname)
- fp = open(metafile, 'w+b')
- file.rewind()
- i=0
- sha=sha256()
- for data in iter(lambda: file.read(int(buffer)), ""):
- if i==0:
- if fat=="fat32" and (c+len(newheader))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(newheader)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(newheader)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- fp.flush()
- sha.update(newheader)
- else:
- fp.write(newheader)
- t.update(len(newheader))
- c=c+len(newheader)
- sha.update(newheader)
- file.seek(0xC00)
- i+=1
- else:
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- sha.update(data)
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- sha.update(data)
- fp.flush()
- if not data:
- break
- fp.close()
- if metapatch == 'true' or keypatch != 'false':
- target = Fs.Nca(metafile, 'r+b')
- target.rewind()
- if str(target.header.contentType) == 'Content.META':
- for pfs0 in target:
- for cnmt in pfs0:
- check=str(cnmt._path)
- check=check[:-22]
- if check == 'AddOnContent':
- t.write(tabs+' > DLC. No RSV to patch')
- target.close()
- else:
- target.close()
- minRSV=sq_tools.getMinRSV(keypatch,RSV_cap)
- if int(minRSV)>int(RSV_cap):
- RSV_cap=minRSV
- self.patcher_meta(metafile,RSV_cap,t)
- target = Fs.Nca(metafile, 'r+b')
- target.rewind()
- fp = open(outf, 'ab')
- file.rewind()
- for data in iter(lambda: target.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- sha.update(data)
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- sha.update(data)
- fp.flush()
- if not data:
- target.close()
- break
- try:
- os.remove(metafile)
- except:
- pass
- if not outf.endswith('xci'):
- test=outf[0:-1]
- if not test.endswith('xc'):
- target=str(file._path)
- xmlname=target[:-3]+'xml'
- t.write(tabs+'* Appending: ' + xmlname)
- dir=os.path.dirname(os.path.abspath(outf))
- xmlfile= os.path.join(dir, xmlname)
- xml = open(xmlfile, 'rb')
- data=xml.read()
- xml.close()
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- fp.flush()
- try:
- os.remove(outf)
- except:
- pass
- fp.close()
- else:
- fp = open(outf, 'ab')
- file.rewind()
- t.write(tabs+'* Appending: ' + str(file._path))
- for data in iter(lambda: file.read(int(buffer)), ""):
- if fat=="fat32" and (c+len(data))>block:
- n2=block-c
- c=0
- inmemoryfile = io.BytesIO()
- inmemoryfile.write(data)
- inmemoryfile.seek(0)
- dat2=inmemoryfile.read(n2)
- fp.write(dat2)
- fp.flush()
- fp.close()
- t.update(len(dat2))
- index=index+1
- outf=outf[0:-1]
- outf=outf+str(index)
- fp = open(outf, 'wb')
- inmemoryfile.seek(n2)
- dat2=inmemoryfile.read(len(data)-n2)
- inmemoryfile.close()
- fp.write(dat2)
- t.update(len(dat2))
- c=c+len(dat2)
- fp.flush()
- else:
- fp.write(data)
- t.update(len(data))
- c=c+len(data)
- fp.flush()
- if not data:
- break
- fp.close()
- return outf,index,c
-
- def cnmt_get_baseids(self):
- ctype='addon'
- idlist=list()
- counter=0
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for pfs0 in nca:
- for cnmt in pfs0:
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- if counter>0 and not str(titleid2).endswith('000'):
- counter+=1
- break
- else:
- counter+=1
- titleversion = cnmt.read(0x4);cnmt.seek(0xE)
- offset=cnmt.readInt16();content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16();content_type=str(cnmt._path);content_type=content_type[:-22]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- if original_ID not in idlist:
- idlist.append(original_ID)
- ctype='base'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- if original_ID not in idlist:
- idlist.append(original_ID)
- ctype='update'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- if original_ID not in idlist:
- idlist.append(original_ID)
- ctype='addon'
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- if original_ID not in idlist:
- idlist.append(original_ID)
- ctype='addon'
- break
- break
- '''
- for i in idlist:
- print(i)
- '''
- return ctype,idlist
-
-
-#///////////////////////////////////////////////////
-#ADD TO DATABASE
-#///////////////////////////////////////////////////
- def addtodb(self,ofile,dbtype,roman=True):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- titleversion = cnmt.read(0x4)
- version=str(int.from_bytes(titleversion, byteorder='little'))
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- cnmt.readInt64()
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- min_sversion=cnmt.readInt32()
- RS_number=int(min_sversion/65536)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- keygen=nca.header.getCryptoType()
- else:
- keygen=nca.header.getCryptoType2()
- else:
- keygen=nca.header.getCryptoType2()
- sdkversion=nca.get_sdkversion()
- programSDKversion,dataSDKversion=self.getsdkvertit(titleid2)
- MinRSV=sq_tools.getMinRSV(keygen,min_sversion)
- RSV_rq=sq_tools.getFWRangeRSV(min_sversion)
- length_of_emeta=cnmt.readInt32()
- target=str(nca._path)
- titlerights,ckey=self.getdbtr(original_ID,content_type,titleid2)
- if ckey == '0':
- ckey=self.getdbkey(titlerights)
- target=str(nca._path)
- tit_name,editor,ediver,SupLg,regionstr,isdemo = self.inf_get_title(target,offset,content_entries,original_ID,roman)
- if tit_name=='DLC' and (str(titleid2).endswith('000') or str(titleid2).endswith('800')):
- tit_name='-'
- editor='-'
- if dbtype == 'extended' or dbtype == 'all':
- dbstring=self.getdbstr(titleid2,titlerights,ckey,content_type,tit_name,version,crypto1,crypto2,keygen,min_sversion,RSV_rq[1:-1],RS_number,MinRSV,regionstr,ediver,editor,isdemo,sdkversion,programSDKversion,dataSDKversion)
- if dbtype == 'keyless' or dbtype == 'all':
- kdbstring=self.getkeylessdbstr(titleid2,titlerights,ckey,content_type,tit_name,version,crypto1,crypto2,keygen,min_sversion,RSV_rq[1:-1],RS_number,MinRSV,regionstr,ediver,editor,isdemo,sdkversion,programSDKversion,dataSDKversion)
- if dbtype == 'nutdb' or dbtype == 'all':
- ndbstring=self.getnutdbstr(titleid2,titlerights,ckey,content_type,tit_name,version,regionstr,isdemo)
- if dbtype == 'simple' or dbtype == 'all':
- simplbstr=self.simplbstr(titlerights,ckey,tit_name)
- print ("- Processing: "+ str(self._path))
- print("")
- if dbtype == 'extended':
- print("EXTENDED DB STRING:")
- self.appendtodb(dbstring,ofile,dbtype)
- if dbtype == 'keyless':
- print("EXTENDED KEYLESS DB STRING:")
- self.appendtodb(kdbstring,ofile,dbtype)
- if dbtype == 'nutdb':
- print("NUTDB DB STRING:")
- self.appendtodb(ndbstring,ofile,dbtype)
- if dbtype == 'simple':
- print("SIMPLE DB STRING:")
- self.appendtodb(simplbstr,ofile,dbtype)
- if dbtype == 'all':
- dir=os.path.dirname(os.path.abspath(ofile))
- edbf='extended_DB.txt'
- edbf = os.path.join(dir, edbf)
- ndbf='nutdb_DB.txt'
- ndbf = os.path.join(dir, ndbf)
- kdbfile='keyless_DB.txt'
- kdbfile = os.path.join(dir, kdbfile)
- simpbfile='simple_DB.txt'
- simpbfile = os.path.join(dir, simpbfile)
- print("EXTENDED DB STRING:")
- self.appendtodb(dbstring,edbf,"extended")
- print("")
- print("EXTENDED KEYLESS DB STRING:")
- self.appendtodb(kdbstring,kdbfile,"keyless")
- print("")
- print("NUTDB DB STRING:")
- self.appendtodb(ndbstring,ndbf,"nutdb")
- print("")
- print("SIMPLE DB STRING:")
- self.appendtodb(simplbstr,simpbfile,"simple")
- print("")
-
-
- def getdbtr(self,original_ID,content_type,cnmt_id):
- titleKeyDec=''
- nca_id=''
- self.rewind()
- tr=''
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- nca_id=nca.header.titleId
- nca_id=nca_id.lower()
- if original_ID==nca_id:
- tr=str(nca.header.rightsId)
- tr = tr[2:-1]
- check=tr[:16]
- if check.endswith('000') and content_type=='Application':
- return tr,titleKeyDec
- if check.endswith('800') and content_type=='Patch':
- return tr,titleKeyDec
- if not check.endswith('000') and not tr.endswith('800') and content_type=='AddOnContent':
- return tr,titleKeyDec
- else:
- tr=str(nca.header.rightsId)
- tr = tr[2:-1]
- return tr,titleKeyDec
- if nca.header.getRightsId() == 0:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- nca_id=nca.header.titleId
- nca_id=nca_id.lower()
- if original_ID==nca_id:
- if nca.header.getgamecard() == 0:
- if crypto1>crypto2:
- masterKeyRev = crypto1
- else:
- masterKeyRev = crypto2
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)
- crypto = aes128.AESECB(key)
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) != 0:
- encKeyBlock = nca.header.getKeyBlock()
- decKeyBlock = crypto.decrypt(encKeyBlock[:16])
- titleKeyDec = hx(Keys.encryptTitleKey(decKeyBlock, Keys.getMasterKeyIndex(masterKeyRev)))
- titleKeyDec=str(titleKeyDec)[2:-1]
- tr=cnmt_id+'000000000000000'+str(crypto2)
- check=tr[:16]
- if check.endswith('000') and content_type=='Application':
- return tr,titleKeyDec
- if check.endswith('800') and content_type=='Patch':
- return tr,titleKeyDec
- elif cnmt_id==nca_id:
- if nca.header.getgamecard() == 0:
- if crypto1>crypto2:
- masterKeyRev = crypto1
- else:
- masterKeyRev = crypto2
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex)
- crypto = aes128.AESECB(key)
- KB1L=nca.header.getKB1L()
- KB1L = crypto.decrypt(KB1L)
- if sum(KB1L) != 0:
- encKeyBlock = nca.header.getKeyBlock()
- decKeyBlock = crypto.decrypt(encKeyBlock[:16])
- titleKeyDec = hx(Keys.encryptTitleKey(decKeyBlock, Keys.getMasterKeyIndex(masterKeyRev)))
- titleKeyDec=str(titleKeyDec)[2:-1]
- tr=cnmt_id+'000000000000000'+str(crypto2)
- check=tr[:16]
- if not check.endswith('000') and not tr.endswith('800') and content_type=='AddOnContent':
- return tr,titleKeyDec
- else:
- tr=cnmt_id+'000000000000000'+str(crypto2)
- else:
- tr=cnmt_id+'000000000000000'+str(crypto2)
- if tr=='':
- tr=cnmt_id+'000000000000000'+str(crypto2)
- if titleKeyDec=='':
- titleKeyDec='00000000000000000000000000000000'
- return tr,titleKeyDec
-
- def getdbkey(self,titlerights):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for ticket in nspF:
- if type(ticket) == Ticket:
- tikrights = hex(ticket.getRightsId())
- tikrights = '0'+tikrights[2:]
- if titlerights == tikrights:
- titleKey = ticket.getTitleKeyBlock()
- titleKey=str(hx(titleKey.to_bytes(16, byteorder='big')))
- titleKey=titleKey[2:-1]
- return str(titleKey)
-
- def getdbstr(self,titleid,titlerights,ckey,content_type,tit_name,version,crypto1,crypto2,keygen,min_sversion,RSV_rq,RS_number,MinRSV,regionstr,ediver,editor,isdemo,sdkversion,programSDKversion,dataSDKversion):
- dbstr=str()
- dbstr+=str(titleid).upper()+'|'
- dbstr+=str(titlerights).upper()+'|'
- dbstr+=str(keygen)+'|'
- if content_type == 'AddOnContent':
- RSV_rq=sq_tools.getFWRangeRSV(MinRSV)
- RSV_rq=RSV_rq[1:-1]
- ediver='-'
- editor='-'
- dbstr+=str(RSV_rq)+'|'
- if content_type == 'AddOnContent':
- RGV_rq=RS_number
- else:
- RGV_rq="0"
- dbstr+=str(RGV_rq)+'|'
- dbstr+=str(ckey).upper()+'|'
- if content_type == 'Application' and isdemo==0:
- if tit_name == 'DLC':
- tit_name = '-'
- dbstr+='GAME|'
- elif content_type=='Patch' and isdemo==0:
- dbstr+='UPD|'
- elif content_type=='AddOnContent':
- dbstr+='DLC|'
- DLCnumb=str(titleid)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- tit_name="DLC Numb. "+str(DLCnumb)
- elif content_type == 'Application' and isdemo!=0:
- if isdemo == 2:
- dbstr+='INT DISPLAY|'
- else:
- dbstr+='DEMO|'
- elif content_type == 'Patch' and isdemo!=0:
- if isdemo == 2:
- dbstr+='UPD INT DISPLAY|'
- else:
- dbstr+='UPD DEMO|'
- elif content_type == 'Application':
- dbstr+='DEMO|'
- elif content_type == 'Patch':
- dbstr+='UPD DEMO|'
- else:
- dbstr+='-|'
- dbstr+=str(tit_name)+'|'
- dbstr+=str(editor)+'|'
- dbstr+=str(version)+'|'
- dbstr+=str(ediver)+'|'
- dbstr+=sdkversion+'|'
- if content_type!='AddOnContent':
- dbstr+=programSDKversion+'|'
- else:
- dbstr+=dataSDKversion+'|'
- dbstr+=regionstr
- return dbstr
-
- def getkeylessdbstr(self,titleid,titlerights,ckey,content_type,tit_name,version,crypto1,crypto2,keygen,min_sversion,RSV_rq,RS_number,MinRSV,regionstr,ediver,editor,isdemo,sdkversion,programSDKversion,dataSDKversion):
- dbstr=str()
- dbstr+=str(titleid).upper()+'|'
- dbstr+=str(titlerights).upper()+'|'
- dbstr+=str(keygen)+'|'
- if content_type == 'AddOnContent':
- RSV_rq=sq_tools.getFWRangeRSV(MinRSV)
- RSV_rq=RSV_rq[1:-1]
- ediver='-'
- editor='-'
- dbstr+=str(RSV_rq)+'|'
- if content_type == 'AddOnContent':
- RGV_rq=RS_number
- else:
- RGV_rq="0"
- dbstr+=str(RGV_rq)+'|'
- if content_type == 'Application' and isdemo==0:
- if tit_name == 'DLC':
- tit_name = '-'
- dbstr+='GAME|'
- elif content_type=='Patch' and isdemo==0:
- dbstr+='UPD|'
- elif content_type=='AddOnContent':
- dbstr+='DLC|'
- DLCnumb=str(titleid)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- tit_name="DLC Numb. "+str(DLCnumb)
- elif content_type == 'Application' and isdemo!=0:
- if isdemo == 2:
- dbstr+='INT DISPLAY|'
- else:
- dbstr+='DEMO|'
- elif content_type == 'Patch' and isdemo!=0:
- if isdemo == 2:
- dbstr+='UPD INT DISPLAY|'
- else:
- dbstr+='UPD DEMO|'
- elif content_type == 'Application':
- dbstr+='DEMO|'
- elif content_type == 'Patch':
- dbstr+='UPD DEMO|'
- else:
- dbstr+='-|'
- dbstr+=str(tit_name)+'|'
- dbstr+=str(editor)+'|'
- dbstr+=str(version)+'|'
- dbstr+=str(ediver)+'|'
- dbstr+=sdkversion+'|'
- if content_type!='AddOnContent':
- dbstr+=programSDKversion+'|'
- else:
- dbstr+=dataSDKversion+'|'
- dbstr+=regionstr
- return dbstr
-
- def getnutdbstr(self,titleid,titlerights,ckey,content_type,tit_name,version,regionstr,isdemo):
- dbstr=str()
- dbstr+=str(titleid).upper()+'|'
- dbstr+=str(titlerights).upper()+'|'
- dbstr+=str(ckey).upper()+'|'
- if content_type == 'Application' and isdemo==0:
- if tit_name == 'DLC':
- tit_name = '-'
- dbstr+='0|0|0|'
- elif content_type=='Patch' and isdemo==0:
- dbstr+='1|0|0|'
- elif content_type=='AddOnContent':
- dbstr+='0|1|0|'
- DLCnumb=str(titleid)
- DLCnumb="0000000000000"+DLCnumb[-3:]
- DLCnumb=bytes.fromhex(DLCnumb)
- DLCnumb=str(int.from_bytes(DLCnumb, byteorder='big'))
- DLCnumb=int(DLCnumb)
- tit_name="DLC Numb. "+str(DLCnumb)
- elif content_type == 'Application' and isdemo!=0:
- dbstr+='0|0|1|'
- elif content_type == 'Patch' and isdemo!=0:
- dbstr+='1|0|1|'
- elif content_type == 'Application':
- dbstr+='0|0|1|'
- elif content_type == 'Patch':
- dbstr+='1|0|1|'
- else:
- dbstr+='-|'
- dbstr+=str(tit_name)+'|'
- dbstr+=str(tit_name)+'|'
- dbstr+=str(version)+'|'
- if regionstr[0]=="1" and regionstr[2]=="0":
- Regionvar="US"
- elif regionstr[0]=="1" and regionstr[2]=="1":
- Regionvar="WORLD"
- elif regionstr[0]=="0" and (regionstr[2]=="1" or regionstr[6]=="1" or regionstr[8]=="1" or regionstr[12]=="1" or regionstr[14]=="1" or regionstr[16]=="1" or regionstr[20]=="1"):
- Regionvar="EUR"
- elif regionstr[4]=="1" and (regionstr[24]=="1" or regionstr[26]=="1" or regionstr[28]=="1"):
- Regionvar="AS"
- elif regionstr[4]=="1" and (regionstr[0]=="0" or regionstr[2]=="0"):
- Regionvar="JAP"
- else:
- Regionvar="-"
- dbstr+=Regionvar
- return dbstr
-
- def simplbstr(self,titlerights,ckey,tit_name):
- dbstr=str()
- dbstr+=str(titlerights).upper()+'|'
- dbstr+=str(ckey).upper()+'|'
- dbstr+=str(tit_name)
- return dbstr
-
- def appendtodb(self,dbstring,ofile,dbtype):
- if dbtype == 'extended':
- initdb='id|rightsId|keygeneration|RSV|RGV|key|ContentType|baseName|editor|version|cversion|metasdkversion|exesdkversion|us|uk|jp|fr|de|lat|spa|it|du|cad|por|ru|kor|tai|ch'
- if dbtype == 'keyless':
- initdb='id|rightsId|keygeneration|RSV|RGV|ContentType|baseName|editor|version|cversion|metasdkversion|exesdkversion|us|uk|jp|fr|de|lat|spa|it|du|cad|por|ru|kor|tai|ch'
- if dbtype == 'nutdb':
- initdb='id|rightsId|key|isUpdate|isDLC|isDemo|baseName|name|version|region'
- if dbtype == 'simple':
- initdb='rightsId|key|name'
- if not os.path.exists(ofile):
- with open(ofile, 'a') as dbfile:
- dbfile.write(initdb+ '\n')
- with open(ofile, 'ab') as dbfile:
- print(dbstring)
- dbfile.write(dbstring.encode('utf-8'))
- with open(ofile, 'a') as dbfile:
- dbfile.write('\n')
-
- def verify(self):
- contentlist=list()
- validfiles=list()
- listed_files=list()
- contentlist=list()
- feed=''
- delta = False
- verdict = True
- checktik=False
-
- feed=self.html_feed(feed,2,message=('DECRYPTION TEST:'))
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if str(file._path).endswith('.nca'):
- listed_files.append(str(file._path))
- if type(file) == Nca:
- validfiles.append(str(file._path))
- if str(file._path).endswith('.ncz'):
- listed_files.append(str(file._path))
- validfiles.append(str(file._path))
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if str(file._path).endswith('.tik'):
- listed_files.append(str(file._path))
- if type(file) == Ticket:
- validfiles.append(str(file._path))
-
- for file in listed_files:
- correct=False;baddec=False
- if file in validfiles:
- if file.endswith('cnmt.nca'):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for f in nspF:
- if str(f._path) == file:
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));feed+=message+'\n'
- for nf in f:
- nf.rewind()
- test=nf.read(0x4)
- #print(test)
- if str(test) == "b'PFS0'":
- correct=True
- break
- if correct == True:
- correct = self.verify_enforcer(file)
- elif file.endswith('.nca'):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for f in nspF:
- if str(f._path) == file:
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));feed+=message+'\n'
- if str(f.header.contentType) != 'Content.PROGRAM':
- correct = self.verify_enforcer(file)
- if correct == True:
- if str(f.header.contentType) == 'Content.PUBLIC_DATA' and f.header.getRightsId() == 0:
- correct = f.pr_noenc_check_dlc()
- if correct == False:
- baddec=True
- else:
- for nf in f:
- try:
- nf.rewind()
- test=nf.read(0x4)
- if str(test) == "b'PFS0'":
- correct=True
- break
- except:
- print(f"{tabs} Error reading {nf}" )
- pass
- f.rewind()
- if correct == True:
- correct = self.verify_enforcer(file)
- if correct == False and f.header.getRightsId() == 0:
- correct = f.pr_noenc_check()
- if correct == False and f.header.getRightsId() != 0:
- correct,tk = self.verify_nca_key(file)
- if correct == True and f.header.getRightsId() == 0:
- correct = f.pr_noenc_check()
- if correct == False:
- baddec=True
- elif file.endswith('.ncz'):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for f in nspF:
- if str(f._path)[:-1] == file[:-1]:
- ncztype=Nca(f)
- ncztype._path=f._path
- message=(str(ncztype.header.titleId)+' - '+str(ncztype.header.contentType));feed+=message+'\n'
- correct=self.verify_ncz(file)
- break
- elif file.endswith('.tik'):
- tikfile=str(file)
- checktik == False
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for f in nspF:
- if str(f._path).endswith('.nca'):
- if checktik == False and f.header.getRightsId() != 0:
- checktik = self.verify_key(str(f._path),tikfile)
- if checktik == True:
- break
- if checktik==False and str(self._path).endswith('.xcz'):
- checktik='xcz'
- else:
- message=('Content.TICKET');feed+=message+'\n'
- correct = checktik
- else:
- correct=False
-
- if correct==True:
- if file.endswith('cnmt.nca'):
- message=(tabs+file+' -> is CORRECT');feed+=message+'\n'
- else:
- message=(tabs+file+tabs+' -> is CORRECT');feed+=message+'\n'
- elif correct=='ncz':
- message=(tabs+file+tabs+' -> ncz file needs HASH check');feed+=message+'\n'
- elif correct=='xcz':
- pass
- else:
- verdict=False
- if file.endswith('cnmt.nca'):
- message=(tabs+file+' -> is CORRUPT <<<-');feed+=message+'\n'
- elif file.endswith('nca'):
- message=(tabs+file+tabs+' -> is CORRUPT <<<-');feed+=message+'\n'
- if baddec == True:
- print(tabs+'* NOTE: S.C. CONVERSION WAS PERFORMED WITH BAD KEY')
- elif file.endswith('tik'):
- message=(tabs+file+tabs+' -> titlekey is INCORRECT <<<-');feed+=message+'\n'
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- for f in nca:
- for cnmt in f:
- nca.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- content_type=str(cnmt._path)
- content_type=content_type[:-22]
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- cnmt.seek(0x20)
- if content_type=='Application':
- original_ID=titleid2
- cnmt.readInt64()
- ttag=''
- CTYPE='BASE'
- elif content_type=='Patch':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [UPD]'
- CTYPE='UPDATE'
- elif content_type=='AddOnContent':
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- ttag=' [DLC]'
- CTYPE='DLC'
- else:
- original_ID=cnmt.readInt64()
- original_ID=str(hx(original_ID.to_bytes(8, byteorder='big')))
- original_ID = original_ID[2:-1]
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- size = cnmt.read(0x6)
- size=int.from_bytes(size, byteorder='little')
- ncatype = cnmt.readInt8()
- unknown = cnmt.read(0x1)
- #**************************************************************
- version=str(int.from_bytes(titleversion, byteorder='little'))
- ver=version
- ver='[v'+ver+']'
- titleid3 ='['+ titleid2+']'
- nca_name=str(hx(NcaId))
- nca_name=nca_name[2:-1]+'.nca'
- ncz_name=nca_name[:-4]+'.ncz'
- if (nca_name not in listed_files and ncatype!=6) or (nca_name not in validfiles and ncatype!=6):
- if ncz_name not in listed_files:
- verdict = False
- message=('\n- Missing file from '+titleid2+': '+nca_name);feed+=message+'\n'
- ticketlist=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for ticket in nspF:
- if type(ticket) == Ticket:
- ticketlist.append(ticket._path)
- titlerights=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if nca.header.getRightsId() != 0:
- rightsId = hx(nca.header.getRightsId().to_bytes(0x10, byteorder='big')).decode('utf-8').lower()
- if rightsId not in titlerights:
- titlerights.append(rightsId)
- mtick=rightsId+'.tik'
- if mtick not in ticketlist:
- message=('\n- File has titlerights!!! Missing ticket: '+mtick);feed+=message+'\n'
- verdict = False
- if str(self.path).endswith('.xcz'):
- token='XCZ'
- elif str(self.path).endswith('.xci'):
- token='XCI'
- else:
- token='XCI'
- if verdict == False:
- message='\nVERDICT: {} FILE IS CORRUPT OR MISSES FILES\n'.format(token);feed+=message+'\n'
- if verdict == True:
- message='\nVERDICT: {} FILE IS CORRECT\n'.format(token);feed+=message
- return verdict,feed
-
- def verify_sig(self,feed,tmpfolder):
- hlisthash=False
- if feed == False:
- feed=''
- verdict=True
- headerlist=list()
- keygenerationlist=list()
- feed=self.html_feed(feed,2,message=('\nSIGNATURE 1 TEST:'))
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for f in nspF:
- if type(f) == Nca and f.header.contentType != Type.Content.META:
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));feed+=message+'\n'
- verify,origheader,ncaname,feed,origkg,tr,tkey,iGC=f.verify(feed)
- headerlist.append([ncaname,origheader,hlisthash,tr,tkey,iGC])
- keygenerationlist.append([ncaname,origkg])
- if verdict == True:
- verdict=verify
- message='';feed+=message+'\n'
- if str(f._path).endswith('.ncz'):
- ncz=Nca(f)
- ncz._path=f._path
- message=(str(ncz.header.titleId)+' - '+str(ncz.header.contentType));feed+=message+'\n'
- verify,origheader,ncaname,feed,origkg,tr,tkey,iGC=ncz.verify(feed)
- # headerlist.append([ncaname,origheader,hlisthash])
- headerlist.append([ncaname,origheader,hlisthash,tr,tkey,iGC])
- keygenerationlist.append([ncaname,origkg])
- if verdict == True:
- verdict=verify
- message='';feed+=message+'\n'
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for f in nspF:
- if type(f) == Nca and f.header.contentType == Type.Content.META:
- meta_nca=f._path
- f.rewind();meta_dat=f.read()
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));feed+=message+'\n'
- targetkg,minrsv=self.find_addecuatekg(meta_nca,keygenerationlist)
- verify,origheader,ncaname,feed,origkg,tr,tkey,iGC=f.verify(feed)
- #print(targetkg)
- if verify == False:
- tempfile=os.path.join(tmpfolder,meta_nca)
- if not os.path.exists(tmpfolder):
- os.makedirs(tmpfolder)
- fp = open(tempfile, 'w+b')
- fp.write(meta_dat);fp.flush();fp.close()
- kglist=sq_tools.kgstring()
- numb=0;topkg=len(kglist)
- for kg in kglist:
- topkg-=1
- if topkg >= origkg:
- for verNumber in kg:
- numb+=1
- cnmtdidverify=False
- t = tqdm(total=numb, unit='RSV', unit_scale=True, leave=False)
- topkg=len(kglist)
- for kg in kglist:
- if cnmtdidverify == True:
- break
- topkg-=1
- #print(topkg)
- if topkg >= origkg:
- c=0;rsv_endcheck=False
- for verNumber in kg:
- c+=1
- if topkg == origkg:
- if c == len(kg):
- rsv_endcheck=True
- #print(verNumber)
- fp = Fs.Nca(tempfile, 'r+b')
- fp.write_req_system(verNumber)
- fp.flush()
- fp.close()
- ############################
- fp = Fs.Nca(tempfile, 'r+b')
- sha=fp.calc_pfs0_hash()
- fp.flush()
- fp.close()
- fp = Fs.Nca(tempfile, 'r+b')
- fp.set_pfs0_hash(sha)
- fp.flush()
- fp.close()
- ############################
- fp = Fs.Nca(tempfile, 'r+b')
- sha2=fp.calc_htable_hash()
- fp.flush()
- fp.close()
- fp = Fs.Nca(tempfile, 'r+b')
- fp.header.set_htable_hash(sha2)
- fp.flush()
- fp.close()
- ########################
- fp = Fs.Nca(tempfile, 'r+b')
- sha3=fp.header.calculate_hblock_hash()
- fp.flush()
- fp.close()
- fp = Fs.Nca(tempfile, 'r+b')
- fp.header.set_hblock_hash(sha3)
- fp.flush()
- fp.close()
- fp = Fs.Nca(tempfile, 'r+b')
- progress=True
- verify,origheader,ncapath,feed,origkg,tr,tkey,iGC=fp.verify(feed,targetkg,rsv_endcheck,progress,t)
- fp.close()
- t.update(1)
- if verify == True:
- t.close()
- message=(tabs+'* '+"RSV WAS CHANGED FROM "+str(verNumber)+" TO "+str(minrsv));feed+=message+'\n'
- message=(tabs+'* '+"THE CNMT FILE IS CORRECT");feed+=message+'\n'
- if origheader != False:
- hlisthash=True;i=0
- fp = Fs.Nca(tempfile, 'r+b')
- for data in iter(lambda: fp.read(int(32768)), ""):
- if i==0:
- sha0=sha256()
- sha0.update(origheader)
- i+=1
- fp.flush()
- fp.seek(0xC00)
- else:
- sha0.update(data)
- fp.flush()
- if not data:
- fp.close()
- cnmtdidverify=True
- break
- break
- else:break
- if hlisthash == True:
- sha0=sha0.hexdigest()
- hlisthash=sha0
- headerlist.append([ncaname,origheader,hlisthash,tr,tkey,iGC])
- message='';feed+=message+'\n'
- try:
- shutil.rmtree(tmpfolder)
- except:pass
- if str(self.path).endswith('.xcz'):
- token='XCZ'
- elif str(self.path).endswith('.xci'):
- token='XCI'
- else:
- token='XCI'
- if verdict == False:
- message=("VERDICT: {} FILE COULD'VE BEEN TAMPERED WITH").format(token);feed+=message+'\n'
- if verdict == True:
- message=('VERDICT: {} FILE IS SAFE').format(token);feed+=message+'\n'
- return verdict,headerlist,feed
-
- def find_addecuatekg(self,ncameta,keygenerationlist):
- ncalist=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- if nca._path == ncameta:
- for f in nca:
- for cnmt in f:
- nca.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleversion = cnmt.read(0x4)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- cnmt.seek(0x20)
- original_ID=cnmt.readInt64()
- min_sversion=cnmt.readInt32()
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- vhash = cnmt.read(0x20)
- NcaId = cnmt.read(0x10)
- ncaname=(str(hx(NcaId)))[2:-1]+'.nca'
- for i in range(len(keygenerationlist)):
- if ncaname == keygenerationlist[i][0]:
- if keygenerationlist[i][1] != False:
- return keygenerationlist[i][1],min_sversion
- else:
- size = cnmt.read(0x6)
- size=int.from_bytes(size, byteorder='little')
- ncatype = cnmt.readInt8()
- unknown = cnmt.read(0x1)
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- if nca._path == ncameta:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- keygeneration=crypto2
- if crypto2<=crypto1:
- keygeneration=crypto1
- return keygeneration,min_sversion
- return False,False
-
- def verify_hash_nca(self,buffer,headerlist,didverify,feed):
- verdict=True
- if feed == False:
- feed=''
- message='\n***************';feed+=message+'\n'
- message=('HASH TEST');feed+=message+'\n'
- message='***************';feed+=message+'\n'
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for f in nspF:
- if type(f) == Nca:
- origheader=False
- for i in range(len(headerlist)):
- if str(f._path)==headerlist[i][0]:
- origheader=headerlist[i][1]
- break
- #print(origheader)
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));feed+=message+'\n'
- ncasize=f.header.size
- t = tqdm(total=ncasize, unit='B', unit_scale=True, leave=False)
- i=0
- f.rewind();
- rawheader=f.read(0xC00)
- f.rewind()
- for data in iter(lambda: f.read(int(buffer)), ""):
- if i==0:
- sha=sha256()
- f.seek(0xC00)
- sha.update(rawheader)
- if origheader != False:
- sha0=sha256()
- sha0.update(origheader)
- i+=1
- t.update(len(data))
- f.flush()
- else:
- sha.update(data)
- if origheader != False:
- sha0.update(data)
- t.update(len(data))
- f.flush()
- if not data:
- break
- t.close()
- sha=sha.hexdigest()
- if origheader != False:
- sha0=sha0.hexdigest()
- message=(' - File name: '+f._path);feed+=message+'\n'
- message=(' - SHA256: '+sha);feed+=message+'\n'
- if origheader != False:
- message=(' - ORIG_SHA256: '+sha0);feed+=message+'\n'
- if str(f._path)[:16] == str(sha)[:16]:
- message=(' > FILE IS CORRECT');feed+=message+'\n'
- elif origheader != False:
- if str(f._path)[:16] == str(sha0)[:16]:
- message=(' > FILE IS CORRECT');feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');feed+=message+'\n'
- verdict = False
- elif f.header.contentType == Type.Content.META and didverify == True:
- message=(' > RSV WAS CHANGED');feed+=message+'\n'
- #print(' > CHECKING INTERNAL HASHES')
- message=(' * FILE IS CORRECT');feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');feed+=message+'\n'
- verdict = False
- message=('');feed+=message+'\n'
- if verdict == False:
- message=("VERDICT: XCI FILE IS CORRUPT");feed+=message+'\n'
- if verdict == True:
- message=('VERDICT: XCI FILE IS CORRECT');feed+=message+'\n'
- return verdict,feed
-
- def verify_enforcer(self,nca):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for f in nspF:
- if str(f._path) == nca:
- if type(f) == Fs.Nca and f.header.contentType == Type.Content.PROGRAM:
- for fs in f.sectionFilesystems:
- if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR:
- f.seek(0)
- ncaHeader = f.read(0x400)
-
- sectionHeaderBlock = fs.buffer
-
- f.seek(fs.offset)
- pfs0Header = f.read(0x10)
-
- return True
- else:
- return False
- if type(f) == Fs.Nca and f.header.contentType == Type.Content.META:
- for fs in f.sectionFilesystems:
- if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR:
- f.seek(0)
- ncaHeader = f.read(0x400)
-
- sectionHeaderBlock = fs.buffer
-
- f.seek(fs.offset)
- pfs0Header = f.read(0x10)
-
- return True
- else:
- return False
-
- if type(f) == Fs.Nca:
- for fs in f.sectionFilesystems:
- if fs.fsType == Type.Fs.ROMFS and fs.cryptoType == Type.Crypto.CTR or f.header.contentType == Type.Content.MANUAL or f.header.contentType == Type.Content.DATA:
- f.seek(0)
- ncaHeader = f.read(0x400)
-
- sectionHeaderBlock = fs.buffer
-
- levelOffset = int.from_bytes(sectionHeaderBlock[0x18:0x20], byteorder='little', signed=False)
- levelSize = int.from_bytes(sectionHeaderBlock[0x20:0x28], byteorder='little', signed=False)
-
- offset = fs.offset + levelOffset
-
- f.seek(offset)
- pfs0Header = f.read(levelSize)
- return True
- else:
- return False
- def verify_nca_key(self,nca):
- check=False;titleKey=0
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if (file._path).endswith('.tik'):
- titleKey = file.getTitleKeyBlock().to_bytes(16, byteorder='big')
- check=self.verify_key(nca,str(file._path))
- if check==True:
- break
- return check,titleKey
-
- def verify_key(self,nca,ticket):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Nca:
- crypto1=file.header.getCryptoType()
- crypto2=file.header.getCryptoType2()
- if crypto1 == 2:
- if crypto1 > crypto2:
- masterKeyRev=file.header.getCryptoType()
- else:
- masterKeyRev=file.header.getCryptoType2()
- else:
- masterKeyRev=file.header.getCryptoType2()
- break
-
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if type(file) == Ticket:
- if ticket != False:
- if str(file._path) == ticket:
- titleKeyDec = Keys.decryptTitleKey(file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = file.getRightsId()
- break
- else:
- ticket = str(file._path)
- titleKeyDec = Keys.decryptTitleKey(file.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(masterKeyRev))
- rightsId = file.getRightsId()
-
- decKey = titleKeyDec
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for f in nspF:
- if str(f._path) == nca:
- if type(f) == Fs.Nca and f.header.getRightsId() != 0:
- for fs in f.sectionFilesystems:
- #print(fs.fsType)
- #print(fs.cryptoType)
- if fs.fsType == Type.Fs.PFS0 and fs.cryptoType == Type.Crypto.CTR:
- f.seek(0)
- ncaHeader = NcaHeader()
- ncaHeader.open(MemoryFile(f.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key'))))
- pfs0=fs
- sectionHeaderBlock = fs.buffer
- f.seek(fs.offset)
- pfs0Offset=fs.offset
- pfs0Header = f.read(0x10)
- #print(hx(sectionHeaderBlock[8:12]))
- #Hex.dump(sectionHeaderBlock)
- if sectionHeaderBlock[8:12] == b'IVFC':
- #Hex.dump(sectionHeaderBlock)
- #Print.info(hx(self.sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8'))
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- #print('hash = %s' % str(sha256(data).hexdigest()))
- if hx(sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8') == str(sha256(data).hexdigest()):
- return True
- else:
- return False
- else:
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- magic = mem.read()[0:4]
- #print(magic)
- if magic != b'PFS0':
- pass
- else:
- return True
-
- if fs.fsType == Type.Fs.ROMFS and fs.cryptoType == Type.Crypto.CTR:
- f.seek(0)
- ncaHeader = NcaHeader()
- ncaHeader.open(MemoryFile(f.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key'))))
- ncaHeader = f.read(0x400)
- pfs0=fs
- sectionHeaderBlock = fs.buffer
-
- levelOffset = int.from_bytes(sectionHeaderBlock[0x18:0x20], byteorder='little', signed=False)
- levelSize = int.from_bytes(sectionHeaderBlock[0x20:0x28], byteorder='little', signed=False)
-
- pfs0Offset = fs.offset + levelOffset
- f.seek(pfs0Offset)
- pfs0Header = f.read(levelSize)
- #print(sectionHeaderBlock[8:12] == b'IVFC')
- if sectionHeaderBlock[8:12] == b'IVFC':
- #Hex.dump(self.sectionHeaderBlock)
- #Print.info(hx(sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8'))
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- #print('hash = %s' % str(sha256(data).hexdigest()))
- if hx(sectionHeaderBlock[0xc8:0xc8+0x20]).decode('utf-8') == str(sha256(data).hexdigest()):
- return True
- else:
- return False
- else:
- mem = MemoryFile(pfs0Header, Type.Crypto.CTR, decKey, pfs0.cryptoCounter, offset = pfs0Offset)
- data = mem.read();
- #Hex.dump(data)
- magic = mem.read()[0:4]
- #print(magic)
- if magic != b'PFS0':
- return False
- else:
- return True
-
- if fs.fsType == Type.Fs.ROMFS and fs.cryptoType == Type.Crypto.BKTR and str(f.header.contentType) == 'Content.PROGRAM':
- f.seek(0)
- ncaHeader = NcaHeader()
- ncaHeader.open(MemoryFile(f.read(0x400), Type.Crypto.XTS, uhx(Keys.get('header_key'))))
- ncaHeader = f.read(0x400)
- pfs0=fs
- sectionHeaderBlock = fs.buffer
-
- levelOffset = int.from_bytes(sectionHeaderBlock[0x18:0x20], byteorder='little', signed=False)
- levelSize = int.from_bytes(sectionHeaderBlock[0x20:0x28], byteorder='little', signed=False)
-
- pfs0Offset = fs.offset + levelOffset
- f.seek(pfs0Offset)
- pfs0Header = f.read(levelSize)
- if sectionHeaderBlock[8:12] == b'IVFC':
- for i in range(10):
- ini=0x100+(i*0x10)
- fin=0x110+(i*4)
- test=sectionHeaderBlock[ini:fin]
- if test==b'BKTR':
- return True
- return False
-
- # ...................................................
- # Patch requrements for network account
- # ...................................................
- def gen_ctrl_list(self):
- print('- Seeking control nca files... ')
- ctrl_list=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- ctrl_list.append(nca._path)
- else:
- pass
- return ctrl_list
-
- def patch_netlicense(self,item=False):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if item == False or nca._path == item:
- check=nca.patch_netlicense()
- return check
-
- def reb_lv_hashes(self,item=False):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if item == False or nca._path == item:
- print('-------------------------------------------------')
- print('Get Current IVFC level data:')
- print('-------------------------------------------------')
- leveldata,superhashoffset=nca.redo_lvhashes()
- return leveldata,superhashoffset
-
- def set_lv_hash(self,j,leveldata,item=False):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if item == False or nca._path == item:
- print('-------------------------------------------------')
- print('Rebuild hashes for IVFC level '+str(j)+':')
- print('-------------------------------------------------')
- nca.set_lv_hash(j,leveldata)
-
- def set_lvsuperhash(self,leveldata,superhashoffset,item=False):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if item == False or nca._path == item:
- print('-------------------------------------------------')
- print('Rebuild IVFC superhash:')
- print('-------------------------------------------------')
- nca.set_lvsuperhash(leveldata,superhashoffset)
-
- def ctrl_upd_hblock_hash(self,item=False):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if item == False or nca._path == item:
- print('-------------------------------------------------')
- print('Rebuild nca hash-table:')
- print('-------------------------------------------------')
- oldhash=nca.header.get_hblock_hash();print('- Old nca sblock hash: '+str(hx(oldhash)))
- newhash=nca.header.calculate_hblock_hash();print('- New nca sblock hash: '+str(hx(newhash)))
- nca.header.set_hblock_hash(newhash)
-
- # ...................................................
-
-
-##################
-#FILE RESTORATION
-##################
-
- def restore_ncas(self,buffer,headerlist,didverify,ofile,feed='',output_type='nsp'):
- from Fs.Ticket import PublicCert
- from Fs.Ticket import PublicTik
- files_list=sq_tools.ret_xci_offsets(self._path)
- 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]
- filepath=entry[0]
- if filepath.endswith('.cnmt.nca'):
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=self.get_data_from_cnmt(filepath)
- 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)
- rightslist=list();ticketlist=list();certlist=list()
- for i in range(len(headerlist)):
- entry=headerlist[i]
- import Hex
- if entry[1]!=False and entry[4]!=False :
- rights=str(hx(entry[3]))[2:-1]
- key=str(hx(entry[4]))[2:-1]
- key=key.upper()
- if rights not in rightslist:
- rightslist.append(rights)
- tik=PublicTik();cert=PublicCert()
- gencert=cert.generate()
- gentik=tik.generate(rights[:16],key,rights[-2:])
- ticketlist.append(gentik);certlist.append(gencert)
- tikname=str(rights.lower())+'.tik'
- certname=str(rights.lower())+'.cert'
- files.append(tikname);files.append(certname)
- filesizes.append(len(gentik));filesizes.append(len(gencert))
- if output_type=='nsp':
- outheader=sq_tools.gen_nsp_header(files,filesizes)
- ofile=ofile[:-3]+'nsp'
- else:
- files_aux=list();filesizes_aux=list()
- for item in range(len(files)):
- if not (files[item]).endswith('.xml'):
- files_aux.append(files[item])
- filesizes_aux.append(filesizes[item])
- files=files_aux
- filesizes=filesizes_aux
- sec_hashlist=list()
- try:
- for file in files:
- sha,size,gamecard=self.file_hash(file)
- # print(sha)
- if sha != False:
- sec_hashlist.append(sha)
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- 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
- ofile=ofile[:-3]+'xci'
- # files_list=sq_tools.ret_xci_offsets(self._path)
- totsize=0
- for s in filesizes:
- totsize+=s
- t = tqdm(total=totsize, unit='B', unit_scale=True, leave=False)
- with open(ofile, 'wb+') as o:
- o.write(outheader)
- t.update(len(outheader))
- with open(ofile, 'rb+') as o:
- o.seek(0, os.SEEK_END)
- for file in files:
- if file.endswith('cnmt.nca'):
- for i in range(len(files_list)):
- if files_list[i][0]==file:
- o.seek(0, os.SEEK_END)
- head_off= o.tell()
- ncahead=False
- for k in range(len(headerlist)):
- entry=headerlist[k]
- if entry[0]==file and entry[1]!=False:
- ncahead=entry[1]
- off1=files_list[i][1]
- off2=files_list[i][2]
- t.write('- Appending {}'.format(files_list[i][0]))
- s=files_list[i][3]
- if int(buffer)>s:
- buf=s
- else:
- buf=buffer
- with open(self._path, 'r+b') as f:
- f.seek(off1);c=0
- for data in iter(lambda: f.read(int(buf)), ""):
- o.write(data)
- o.flush()
- c=len(data)+c
- t.update(len(data))
- if c+int(buf)>s:
- if (s-c)<0:
- t.close()
- o.close()
- break
- data=f.read(s-c)
- o.write(data)
- t.update(len(data))
- break
- if not data:
- break
- if ncahead!=False:
- o.seek(head_off)
- o.write(ncahead)
- o.seek(0, os.SEEK_END)
- elif file.endswith('.nca'):
- for i in range(len(files_list)):
- if files_list[i][0]==file:
- o.seek(0, os.SEEK_END)
- head_off= o.tell()
- ncahead=False
- for k in range(len(headerlist)):
- entry=headerlist[k]
- if entry[0]==file and entry[1]!=False:
- ncahead=entry[1]
- off1=files_list[i][1]
- off2=files_list[i][2]
- t.write('- Appending {}'.format(files_list[i][0]))
- s=files_list[i][3]
- if int(buffer)>s:
- buf=s
- else:
- buf=buffer
- with open(self._path, 'r+b') as f:
- f.seek(off1);c=0
- for data in iter(lambda: f.read(int(buf)), ""):
- o.write(data)
- o.flush()
- c=len(data)+c
- t.update(len(data))
- if c+int(buf)>s:
- if (s-c)<0:
- t.close()
- o.close()
- break
- data=f.read(s-c)
- o.write(data)
- t.update(len(data))
- break
- if not data:
- break
- if ncahead!=False:
- o.seek(head_off)
- o.write(ncahead)
- o.seek(0, os.SEEK_END)
- elif file.endswith('.xml'):
- for i in range(len(files_list)):
- if files_list[i][0]==file:
- off1=files_list[i][1]
- off2=files_list[i][2]
- t.write('- Appending {}'.format(files_list[i][0]))
- s=files_list[i][3]
- if int(buffer)>s:
- buf=s
- else:
- buf=buffer
- with open(self._path, 'r+b') as f:
- f.seek(off1);c=0
- for data in iter(lambda: f.read(int(buf)), ""):
- o.write(data)
- o.flush()
- c=len(data)+c
- t.update(len(data))
- if c+int(buf)>s:
- if (s-c)<0:
- t.close()
- o.close()
- break
- data=f.read(s-c)
- o.write(data)
- t.update(len(data))
- break
- if not data:
- break
- elif file.endswith('.tik')or file.endswith('.cert'):
- pass
-
- for i in range(len(ticketlist)):
- t.write('- Appending tickets and certs')
- o.write(ticketlist[i])
- t.update(len(ticketlist[i]))
- o.flush()
- o.write(certlist[i])
- t.update(len(certlist[i]))
- o.flush()
- t.close()
-
-##################
-#DB DATA
-##################
- def Incorporate_to_permaDB(self,dbfile,trans=True):
- Datashelve = DBmodule.Dict(dbfile)
- cnmtnames=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- cnmtnames.append(str(nca._path))
- for file in cnmtnames:
- DBdict=self.getDBdict(file,trans)
- dbkey=(str(DBdict['id'])+'_v'+str(DBdict['version'])+'_xci').lower()
- if not 'fields' in Datashelve:
- DBmodule.MainDB.initializeDB(dbfile)
- if not dbkey in Datashelve:
- Datashelve[dbkey]=DBdict
- Datashelve.close()
-
- def return_DBdict(self):
- cnmtnames=list()
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- cnmtnames.append(str(nca._path))
- content_number=len(cnmtnames)
- if content_number>1:
- file,mcstring,mGame=self.choosecnmt(cnmtnames)
- DBdict=self.getDBdict(file,content_number=content_number,mcstring=mcstring,mGame=mGame)
- else:
- DBdict=self.getDBdict(cnmtnames[0],content_number=content_number)
- return DBdict
-
- def choosecnmt(self,cnmtnames):
- file=False;titleid=False;nG=0;nU=0;nD=0;mcstring="";mGame=False
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if str(nca._path) in cnmtnames:
- if type(nca) == Nca:
- if titleid==False:
- file=str(nca._path)
- titleid=str(nca.header.titleId)
- if str(nca.header.titleId).endswith('000'):
- nG+=1
- elif str(nca.header.titleId).endswith('800'):
- if nU==0 and nG<2:
- file=str(nca._path)
- nU+=1
- else:
- nD+=1
- if nG>0:
- mcstring+='{} Game'.format(str(nG))
- if nG>1:
- mcstring+='s'
- mGame=True
- if nU>0:
- if nG>0:
- mcstring+=', '
- mcstring+='{} Update'.format(str(nU))
- if nU>1:
- mcstring+='s'
- if nD>0:
- if nG>0 or nU>0:
- mcstring+=', '
- mcstring+='{} DLC'.format(str(nD))
- if nD>1:
- mcstring+='s'
- return file,mcstring,mGame
-
- def getDBdict(self,cnmtname,json=False,trans=True,content_number=False,mcstring=False,mGame=False):
- DBdict={}
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=self.get_data_from_cnmt(cnmtname)
- try:
- sqname,sqeditor,SupLg,ctype=self.DB_get_names(ctype,ncadata)
- except:
- sqname,sqeditor,SupLg=self.DB_get_names_from_nutdb(titleid)
- if ctype != 'DLC':
- nacpdict=self.get_data_from_nacp(ncadata)
- titlekey,dectkey=self.db_get_titlekey(rightsId,keygeneration)
- #cnmt flags
- DBdict['id']=titleid
- DBdict['baseid']=base_ID
- DBdict['rightsId']=rightsId
- DBdict['Type']=ctype
- DBdict['version']=titleversion
- DBdict['keygeneration']=keygeneration
- DBdict['RSV']=RSV[1:-1]
- DBdict['RGV']=RGV
- DBdict['metasdkversion']=metasdkversion
- DBdict['exesdkversion']=exesdkversion
- DBdict['InstalledSize']=Installedsize
- DBdict['deltasize']=DeltaSize
- DBdict['HtmlManual']=hasHtmlManual
- DBdict['ncasizes']=ncadata
-
- #nacpt flags
- if ctype != 'DLC':
- DBdict['baseName']=sqname
- DBdict['contentname']='-'
- else:
- DBdict['baseName']='-'
- DBdict['contentname']=sqname
- DBdict['editor']=sqeditor
- DBdict['languages']=SupLg
- if ctype != 'DLC':
- try:
- if nacpdict['StartupUserAccount']=='RequiredWithNetworkServiceAccountAvailable' or nacpdict['RequiredNetworkServiceLicenseOnLaunch']!='None':
- DBdict['linkedAccRequired']='True'
- else:
- DBdict['linkedAccRequired']='False'
- DBdict['dispversion']=nacpdict['BuildNumber']
- except:pass
-
- #ticket flags
- if titlekey==False:
- titlekey='-'
- dectkey='-'
- DBdict['key']=titlekey
- DBdict['deckey']=dectkey
-
- #Distribution type flags
- if ctype=='GAME':
- DBdict['DistEshop']='-'
- DBdict['DistCard']='True'
- else:
- DBdict['DistEshop']='-'
- DBdict['DistCard']='True'
-
- #xci flags
- DBdict['FWoncard']='-'
- try:
- DBdict['FWoncard']=DBmodule.FWDB.detect_xci_fw(self._path)
- except:pass
-
- GCFlag=(str(hx(self.gamecardSize))[2:-1]).upper()
- valid_data=int(((self.validDataEndOffset+0x1)*0x200))
- GCSize=sq_tools.getGCsizeinbytes(GCFlag)
- GCSize=int(GCSize)
- DBdict['GCSize']=GCSize
- DBdict['TrimmedSize']=valid_data
-
- #Check if multicontent
- if content_number==False:
- content_number=sq_tools.count_content(self._path)
- # print(str(content_number))
- DBdict['ContentNumber']=str(content_number)
- if mcstring !=False:
- DBdict['ContentString']=mcstring
- if content_number!=False and content_number>1:
- if mGame==True:
- DBdict['Type']="MULTIGAME"
- else:
- DBdict['Type']="MULTICONTENT"
- DBdict['InstalledSize']=sq_tools.get_mc_isize(self._path)
- ProgramIDS=[]
- # if ctype=='MultiProgram' or ctype=='MultiProgram UPD' or DBdict['Type']="MULTIGAME":
- # idoffsets=[]
- # for ent in ncadata:
- # idoff=ent[4]
- # if not idoff in idoffsets:
- # idoffsets.append[idoff]
- # ProgramIDS.append(str(titleid[:-2])+str(idoff))
- # DBdict['ProgramIDS']=ProgramIDS
-
- # # if content_number>1 or DBdict['Type']=="MultiProgram" or DBdict['Type']=="MultiProgram UPD":
- # # DBdict['IDS']=[titleid]
- # # if DBdict['Type']=="MULTIGAME" or DBdict['Type']=="MULTICONTENT":
-
- # # elif DBdict['Type']=="MultiProgram":
-
- # # elif DBdict['Type']=="MultiProgram UPD":
-
- DBdict['nsuId']='-'
- DBdict['genretags']='-'
- DBdict['ratingtags']='-'
- DBdict['worldreleasedate']='-'
- DBdict['numberOfPlayers']='-'
- DBdict['iconUrl']='-'
- DBdict['screenshots']='-'
- DBdict['bannerUrl']='-'
- DBdict['intro']='-'
- DBdict['description']='-'
- if ctype=='GAME' or ctype=='DLC' or ctype=='DEMO':
- nsuId,worldreleasedate,genretags,ratingtags,numberOfPlayers,intro,description,iconUrl,screenshots,bannerUrl,region,rating,developer,productCode,OnlinePlay,SaveDataCloud,playmodes,video,shopurl=nutdb.get_content_data(titleid,trans)
- regions=nutdb.get_contenregions(titleid)
- else:
- nsuId,worldreleasedate,genretags,ratingtags,numberOfPlayers,intro,description,iconUrl,screenshots,bannerUrl,region,rating,developer,productCode,OnlinePlay,SaveDataCloud,playmodes,video,shopurl=nutdb.get_content_data(base_ID,trans)
- regions=nutdb.get_contenregions(base_ID)
- if nsuId!=False:
- DBdict['nsuId']=nsuId
- if genretags!=False:
- DBdict['genretags']=genretags
- if ratingtags!=False:
- DBdict['ratingtags']=ratingtags
- if worldreleasedate!=False:
- DBdict['worldreleasedate']=worldreleasedate
- if numberOfPlayers!=False:
- DBdict['numberOfPlayers']=numberOfPlayers
- if iconUrl!=False:
- DBdict['iconUrl']=iconUrl
- if screenshots!=False:
- DBdict['screenshots']=screenshots
- if bannerUrl!=False:
- DBdict['bannerUrl']=bannerUrl
- if intro!=False:
- DBdict['intro']=intro
- if description!=False:
- DBdict['description']=description
- if rating!=False:
- DBdict['eshoprating']=rating
- if developer!=False:
- DBdict['developer']=developer
- if productCode!=False:
- DBdict['productCode']=productCode
- if OnlinePlay!=False:
- DBdict['OnlinePlay']=OnlinePlay
- if SaveDataCloud!=False:
- DBdict['SaveDataCloud']=SaveDataCloud
- if playmodes!=False:
- DBdict['playmodes']=playmodes
- if video!=False:
- DBdict['video']=video
- if shopurl!=False:
- DBdict['shopurl']=shopurl
- if len(regions)>0:
- DBdict['regions']=regions
- metascore,userscore,openscore=nutdb.get_metascores(titleid)
- DBdict['metascore']='-'
- DBdict['userscore']='-'
- DBdict['openscore']='-'
- if metascore!=False:
- DBdict['metascore']=metascore
- if userscore!=False:
- DBdict['userscore']=userscore
- if openscore!=False:
- DBdict['openscore']=openscore
- return DBdict
-
- def DB_get_names_from_nutdb(self,titleid):
- try:
- sqname,sqeditor=nutdb.get_dlcData(titleid)
- SupLg=nutdb.get_content_langue(titleid)
- return sqname,sqeditor,SupLg
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return "-","-","-"
-
- def get_data_from_cnmt(self,cnmtname):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.META':
- if str(nca._path)==cnmtname:
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- keygeneration=crypto2
- if crypto2<=crypto1:
- keygeneration=crypto1
- for f in nca:
- for cnmt in f:
- nca.rewind()
- f.rewind()
- cnmt.rewind()
- titleid=cnmt.readInt64()
- titleid2 = str(hx(titleid.to_bytes(8, byteorder='big')))
- titleid2 = titleid2[2:-1]
- titleid=(str(hx(titleid.to_bytes(8, byteorder='big')))[2:-1]).upper()
- titleversion = cnmt.read(0x4)
- titleversion=str(int.from_bytes(titleversion, byteorder='little'))
- type_n = cnmt.read(0x1)
- cnmt.rewind()
- cnmt.seek(0xE)
- offset=cnmt.readInt16()
- content_entries=cnmt.readInt16()
- meta_entries=cnmt.readInt16()
- cnmt.rewind()
- cnmt.seek(0x20)
- base_ID=cnmt.readInt64()
- base_ID=(str(hx(base_ID.to_bytes(8, byteorder='big')))[2:-1]).upper()
- cnmt.rewind()
- cnmt.seek(0x28)
- min_sversion=cnmt.readInt32()
- length_of_emeta=cnmt.readInt32()
- content_type_cnmt=str(cnmt._path)
- content_type_cnmt=content_type_cnmt[:-22]
- if content_type_cnmt != 'AddOnContent':
- RSV=str(min_sversion)
- RSV=sq_tools.getFWRangeRSV(int(RSV))
- RGV=0
- if content_type_cnmt == 'AddOnContent':
- RSV_rq_min=sq_tools.getMinRSV(keygeneration, 0)
- RSV=sq_tools.getFWRangeRSV(int(RSV_rq_min))
- RGV=str(min_sversion)
- if str(hx(type_n)) == "b'1'":
- ctype='SystemProgram'
- if str(hx(type_n)) == "b'2'":
- ctype='SystemData'
- if str(hx(type_n)) == "b'3'":
- ctype='SystemUpdate'
- if str(hx(type_n)) == "b'4'":
- ctype='BootImagePackage'
- if str(hx(type_n)) == "b'5'":
- ctype='BootImagePackageSafe'
- if str(hx(type_n)) == "b'80'":
- ctype='GAME'
- if str(hx(type_n)) == "b'81'":
- ctype='UPDATE'
- if str(hx(type_n)) == "b'82'":
- ctype='DLC'
- if str(hx(type_n)) == "b'83'":
- ctype='Delta'
- metasdkversion=nca.get_sdkversion()
- programSDKversion,dataSDKversion=self.getsdkvertit(titleid2)
- if content_type_cnmt == 'AddOnContent':
- exesdkversion=dataSDKversion
- if content_type_cnmt != 'AddOnContent':
- exesdkversion=programSDKversion
- ncadata=list()
- hasHtmlManual=False
- Installedsize=int(nca.header.size);DeltaSize=0
- cnmt.rewind()
- cnmt.seek(0x20+offset)
- for i in range(content_entries):
- data={}
- vhash = cnmt.read(0x20)
- vhash=str(hx(vhash))
- NcaId = cnmt.read(0x10)
- NcaId=str(hx(NcaId))
- size = cnmt.read(0x6)
- size=str(int.from_bytes(size, byteorder='little', signed=True))
- ncatype = cnmt.read(0x1)
- ncatype=str(int.from_bytes(ncatype, byteorder='little', signed=True))
- ncatype=sq_tools.getmetacontenttype(ncatype)
- IdOffset = cnmt.read(0x1)
- IdOffset=str(int.from_bytes(IdOffset, byteorder='little', signed=True))
- data['NcaId']=NcaId[2:-1]
- data['NCAtype']=ncatype
- data['Size']=size
- data['Hash']=vhash[2:-1]
- data['IdOffset']=IdOffset
- if int(IdOffset)>0:
- if ctype=='MultiProgram' or ctype=='MultiProgram UPD':
- pass
- elif ctype != 'UPDATE':
- ctype='MultiProgram'
- else:
- ctype='MultiProgram UPD'
- if ncatype != "DeltaFragment":
- Installedsize=Installedsize+int(size)
- else:
- DeltaSize=DeltaSize+int(size)
- if ncatype == "HtmlDocument":
- hasHtmlManual=True
- ncadata.append(data)
- cnmtdata={}
- metaname=str(nca._path);metaname = metaname[:-9]
- nca.rewind();block = nca.read();nsha=sha256(block).hexdigest()
- cnmtdata['NcaId']=metaname
- cnmtdata['NCAtype']='Meta'
- cnmtdata['Size']=str(nca.header.size)
- cnmtdata['Hash']=str(nsha)
- cnmtdata['IdOffset']=IdOffset
- ncadata.append(cnmtdata)
- rightsId=titleid+'000000000000000'+str(crypto2)
- return titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata
-
- def get_data_from_nacp(self,ncadata):
- for entry in ncadata:
- if entry['NCAtype'].lower()=='control':
- target=entry['NcaId']+'.nca'
- dict={}
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if target==str(nca._path):
- offset=nca.get_nacp_offset()
- for f in nca:
- nacp = Nacp()
- f.seek(offset+0x3025)
- dict['StartupUserAccount']=nacp.get_StartupUserAccount(f.readInt8('little'))
- f.seek(offset+0x3060)
- try:
- dict['BuildNumber']=nacp.get_DisplayVersion(f.read(0xF))
- f.seek(offset+0x3213)
- dict['RequiredNetworkServiceLicenseOnLaunch']=nacp.get_RequiredNetworkServiceLicenseOnLaunch(f.readInt8('little'))
- except:continue
- return dict
-
- def DB_get_names(self,ctype,ncadata):
- #print(ncadata)
- for entry in ncadata:
- if str(entry['NCAtype']).lower()=='control':
- target=str(entry['NcaId'])+'.nca'
-
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- if target==str(nca._path):
- title,editor,ediver,SupLg,regionstr,isdemo=nca.get_langueblock('DLC',roman=True)
- if ctype=='GAME':
- if isdemo==1:
- ctype='DEMO'
- if isdemo==2:
- ctype='RETAILINTERACTIVEDISPLAY'
- elif ctype=='UPDATE':
- if isdemo==1:
- ctype='DEMO UPDATE'
- if isdemo==2:
- ctype='RETAILINTERACTIVEDISPLAY UPDATE'
- else:
- ctype=ctype
- return title,editor,SupLg,ctype
-
- def db_get_titlekey(self,rightsId,keygeneration):
- #print(rightsId)
- #print(keygeneration)
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for ticket in nspF:
- if type(ticket) == Ticket:
- tikrights = hex(ticket.getRightsId())
- tikrights = '0'+tikrights[2:]
- if str(rightsId).lower() == str(tikrights).lower():
- titleKey = ticket.getTitleKeyBlock()
- titleKey=str(hx(titleKey.to_bytes(16, byteorder='big')))
- titleKey=titleKey[2:-1].upper()
- deckey = Keys.decryptTitleKey(ticket.getTitleKeyBlock().to_bytes(16, byteorder='big'), Keys.getMasterKeyIndex(int(keygeneration)))
- deckey=(str(hx(deckey))[2:-1]).upper()
- #print(titleKey)
- #print(deckey)
- return str(titleKey),str(deckey)
- return False,False
-
- def icon_info(self,files_list,buffer = 65536):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if type(nca) == Nca:
- if str(nca.header.contentType) == 'Content.CONTROL':
- tk=nca.header.titleKeyDec
- for i in range(len(files_list)):
- if str(nca._path) == files_list[i][0]:
- offset=files_list[i][1]
- #print(offset)
- break
- checksums = list()
- try:
- fp=open(str(self._path), 'rb')
- nca3=NCA3(fp,int(offset),str(nca._path),tk)
- for dat in self.open_all_dat(nca3):
- checksum = sha1(dat.read()).digest()
- if checksum not in checksums:
- checksums.append(checksum)
- a=self.print_dat(dat)
- return a
- break
- fp.close()
- except BaseException as e:
- # Print.error('Exception: ' + str(e))
- try:
- with open(str(self._path), 'rb') as f:
- f.seek(offset)
- inmemoryfile = io.BytesIO(f.read(files_list[i][3]))
- nca3=NCA3(inmemoryfile,int(0),str(nca._path),tk,buffer)
- for dat in self.open_all_dat(nca3):
- checksum = sha1(dat.read()).digest()
- if checksum not in checksums:
- checksums.append(checksum)
- a=self.print_dat(dat)
- return a
- break
- fp.close()
- except BaseException as e:
- #Print.error('Exception: ' + str(e))
- pass
- def open_all_dat(self,nca):
- # Iterators that yields all the icon file handles inside an NCA
- for _, sec in enumerate(nca.sections):
- if sec.fs_type == 'PFS0':
- continue
- cur_file = sec.fs.first_file
- while cur_file is not None:
- if cur_file.name.endswith('.dat'):
- yield sec.fs.open(cur_file)
- cur_file = cur_file.next
-
- def print_dat(self,dat):
- dat.seek(0)
- a=dat.read()
- # print(a)
- # img = Image.open(dat)
- # img.show()
- return a
- # print(a)
- # img = Image.open(dat)
- # img.show()
- return a
-
- def verify_ncz(self,target):
- files_list=sq_tools.ret_xci_offsets(self._path)
- files=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]
- filepath=entry[0]
- if filepath.endswith('.cnmt.nca'):
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=self.get_data_from_cnmt(filepath)
- 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')
- if str(row['NcaId'])==target[:-4]:
- files.append(str(row['NcaId'])+'.nca')
- break
- for file in files:
- if file.endswith('nca'):
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if str(nca._path)[:-1]==file[:-1] and str(nca._path).endswith('ncz'):
- # print(nca._path)
- nca.rewind()
- header = nca.read(0x4000)
- magic = readInt64(nca)
- sectionCount = readInt64(nca)
- sections = []
- for i in range(sectionCount):
- sections.append(Section(nca))
- count=0;checkstarter=0
- if titleid.endswith('000'):
- for s in sections:
- count+=1
- if count==2:
- break
- # print(s.cryptoType)
- # print(s.size)
- checkstarter+=s.size
- # print(s.size)
- dctx = zstandard.ZstdDecompressor()
- reader = dctx.stream_reader(nca)
- test=int(checkstarter/(16384))
- for i in (range(test+1)):
- reader.seek(16384,1)
- chunk = reader.read(16384)
- b1=chunk[:32];# Hex.dump(b1)
- b2=chunk[32:64];# Hex.dump(b2)
- if sum(b1)!=0 and sum(b2)==0:
- return True
- else:
- return 'ncz'
- if not titleid.endswith('800'):
- dctx = zstandard.ZstdDecompressor()
- reader = dctx.stream_reader(nca)
- chunk = reader.read(16384)
- b1=chunk[:32];# Hex.dump(b1)
- b2=chunk[32:64];# Hex.dump(b2)
- if sum(b1)!=0 and sum(b2)==0:
- return True
- else:
- return 'ncz'
- elif titleid.endswith('800'):
- dctx = zstandard.ZstdDecompressor()
- reader = dctx.stream_reader(nca)
- chunk = reader.read(16384)
- b1=chunk[:32];# Hex.dump(b1)
- b2=chunk[32:64];# Hex.dump(b2)
- if sum(b1)!=0 and sum(b2)==0:
- return True
- else:
- return 'ncz'
-
- def xcz_hasher(self,buffer,headerlist,didverify,feed):
- buffer=int(buffer)
- verdict=True
- if feed == False:
- feed=''
- message='\n***************';feed+=message+'\n'
- message=('HASH TEST');feed+=message+'\n'
- message='***************';feed+=message+'\n'
- for nspF in self.hfs0:
- if str(nspF._path)=="secure":
- for f in nspF:
- if type(f) == Nca:
- origheader=False
- for i in range(len(headerlist)):
- if str(f._path)==headerlist[i][0]:
- origheader=headerlist[i][1]
- listedhash=headerlist[i][2]
- break
- message=(str(f.header.titleId)+' - '+str(f.header.contentType));feed+=message+'\n'
- ncasize=f.header.size
- t = tqdm(total=ncasize, unit='B', unit_scale=True, leave=False)
- i=0
- f.rewind();
- rawheader=f.read(0xC00)
- f.rewind()
- for data in iter(lambda: f.read(int(buffer)), ""):
- if i==0:
- sha=sha256()
- f.seek(0xC00)
- sha.update(rawheader)
- if origheader != False and listedhash == False:
- sha0=sha256()
- sha0.update(origheader)
- i+=1
- t.update(len(data))
- f.flush()
- else:
- sha.update(data)
- if origheader != False and listedhash == False:
- sha0.update(data)
- t.update(len(data))
- f.flush()
- if not data:
- break
- t.close()
- sha=sha.hexdigest()
- if listedhash != False:
- sha0=listedhash
- elif origheader != False:
- sha0=sha0.hexdigest()
- message=(' - File name: '+f._path);feed+=message+'\n'
- message=(' - SHA256: '+sha);feed+=message+'\n'
- if origheader != False:
- message=(' - ORIG_SHA256: '+sha0);feed+=message+'\n'
- if str(f._path)[:16] == str(sha)[:16]:
- message=(' > FILE IS CORRECT');feed+=message+'\n'
- elif origheader != False:
- if str(f._path)[:16] == str(sha0)[:16]:
- message=(' > FILE IS CORRECT');feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');feed+=message+'\n'
- verdict = False
- elif f.header.contentType == Type.Content.META and didverify == True:
- message=(' > RSV WAS CHANGED');feed+=message+'\n'
- #print(' > CHECKING INTERNAL HASHES')
- message=(' * FILE IS CORRECT');feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');feed+=message+'\n'
- verdict = False
- message=('');feed+=message+'\n'
- if (f._path).endswith('ncz'):
- ncz=Nca(f)
- ncz._path=f._path
- origheader=False
- for i in range(len(headerlist)):
- if str(f._path)==headerlist[i][0]:
- origheader=headerlist[i][1]
- listedhash=headerlist[i][2]
- break
- message=(str(ncz.header.titleId)+' - '+str(ncz.header.contentType));feed+=message+'\n'
- ncasize=ncz.header.size
- t = tqdm(total=ncasize, unit='B', unit_scale=True, leave=False)
- i=0
- f.rewind();
- rawheader=f.read(0xC00)
- f.rewind()
- sha=sha256()
- f.seek(0xC00)
- sha.update(rawheader)
- if origheader != False and listedhash == False:
- sha0=sha256()
- sha0.update(origheader)
- i+=1
- t.update(len(rawheader))
- f.flush()
- size=0x4000-0xC00
- dif = f.read(size)
- sha.update(dif)
- if origheader != False and listedhash == False:
- sha0.update(dif)
- t.update(len(dif))
- f.flush()
- f.seek(0x4000)
- magic = readInt64(f)
- sectionCount = readInt64(f)
- sections = []
- for i in range(sectionCount):
- sections.append(Section(f))
- # print(sections)
- count=0;checkstarter=0
- dctx = zstandard.ZstdDecompressor()
- reader = dctx.stream_reader(f)
- c=0;spsize=0
- for s in sections:
- end = s.offset + s.size
- if s.cryptoType == 1: #plain text
- 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
- sha.update(chunk)
- if origheader != False and listedhash == False:
- sha0.update(chunk)
- t.update(len(chunk))
- i += chunkSz
- elif s.cryptoType not in (3, 4):
- raise IOError('Unknown crypto type: %d' % s.cryptoType)
- else:
- 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
- crpt=crypto.encrypt(chunk)
- sha.update(crpt)
- if origheader != False and listedhash == False:
- sha0.update(crpt)
- t.update(len(chunk))
- i += chunkSz
- t.close()
- sha=sha.hexdigest()
- if listedhash != False:
- sha0=listedhash
- elif origheader != False:
- sha0=sha0.hexdigest()
- message=(' - File name: '+ncz._path);feed+=message+'\n'
- message=(' - SHA256: '+sha);feed+=message+'\n'
- if origheader != False:
- message=(' - ORIG_SHA256: '+sha0);feed+=message+'\n'
- if str(ncz._path)[:16] == str(sha)[:16]:
- message=(' > FILE IS CORRECT');feed+=message+'\n'
- elif origheader != False:
- if str(ncz._path)[:16] == str(sha0)[:16]:
- message=(' > FILE IS CORRECT');feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');feed+=message+'\n'
- verdict = False
- elif ncz.header.contentType == Type.Content.META and didverify == True:
- message=(' > RSV WAS CHANGED');feed+=message+'\n'
- #print(' > CHECKING INTERNAL HASHES')
- message=(' * FILE IS CORRECT');feed+=message+'\n'
- else:
- message=(' > FILE IS CORRUPT');feed+=message+'\n'
- verdict = False
- message=('');feed+=message+'\n'
- if str(self.path).endswith('.xcz'):
- token='XCZ'
- elif str(self.path).endswith('.xci'):
- token='XCI'
- else:
- token='XCI'
- if verdict == False:
- message=("VERDICT: {} FILE IS CORRUPT").format(token);feed+=message+'\n'
- if verdict == True:
- message=('VERDICT: {} FILE IS CORRECT').format(token);feed+=message+'\n'
- return verdict,feed
diff --git a/py/Fs/Xci.py b/py/Fs/Xci.py
index e86ad156..02e6892e 100644
--- a/py/Fs/Xci.py
+++ b/py/Fs/Xci.py
@@ -13,7 +13,7 @@
import os
import Print
-from Fs import Header, BlockDecompressorReader
+from ZstdBlock import Header, BlockDecompressorReader
import Keys
import aes128
diff --git a/py/Fs/__init__.py b/py/Fs/__init__.py
index 874239a0..5543cd61 100644
--- a/py/Fs/__init__.py
+++ b/py/Fs/__init__.py
@@ -10,31 +10,28 @@
from Fs.Hfs0 import Hfs0
from Fs.Ticket import Ticket
from Fs.File import File
-from Fs.ChromeNsp import ChromeNsp
-from Fs.ChromeXci import ChromeXci
-from Fs.ChromeNacp import ChromeNacp
def factory(name):
if name.endswith('.xci'):
f = Xci()
elif name.endswith('.xcz'):
- f = Xci()
+ f = Xci()
elif name.endswith('.nsp'):
f = Nsp()
elif name.endswith('.nsz'):
- f = Nsp()
+ f = Nsp()
elif name.endswith('.nsx'):
f = Nsp()
elif name.endswith('.nca'):
f = Nca()
elif name.endswith('.ncz'):
- f = File()
+ f = File()
elif name.endswith('.nacp'):
f = Nacp()
elif name.endswith('.tik'):
f = Ticket()
elif name.endswith('.hfs0'):
- f = Hfs0()
+ f = Hfs0()
else:
f = File()
- return f
\ No newline at end of file
+ return f
diff --git a/py/Fs/BlockDecompressorReader.py b/py/ZstdBlock/BlockDecompressorReader.py
similarity index 100%
rename from py/Fs/BlockDecompressorReader.py
rename to py/ZstdBlock/BlockDecompressorReader.py
diff --git a/py/Fs/Header.py b/py/ZstdBlock/Header.py
similarity index 100%
rename from py/Fs/Header.py
rename to py/ZstdBlock/Header.py
diff --git a/py/mtp/__init__.py b/py/ZstdBlock/__init__.py
similarity index 100%
rename from py/mtp/__init__.py
rename to py/ZstdBlock/__init__.py
diff --git a/py/_EEL_/EEL LICENSE b/py/_EEL_/EEL LICENSE
deleted file mode 100644
index 65f8831f..00000000
--- a/py/_EEL_/EEL LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2018 Chris Knott
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/py/_EEL_/__init__.py b/py/_EEL_/__init__.py
deleted file mode 100644
index 0a35925f..00000000
--- a/py/_EEL_/__init__.py
+++ /dev/null
@@ -1,405 +0,0 @@
-from builtins import range
-import traceback
-from io import open
-import sys
-import os
-
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-web_folder=os.path.join(ztools_dir,'web')
-debug_folder=os.path.join(web_folder,'_debug_')
-flag_file=os.path.join(debug_folder,'flag')
-
-if not os.path.exists(debug_folder):
- os.makedirs(debug_folder)
-with open(os.path.join(debug_folder,'log.txt'), 'w') as tfile:
- tfile.write('')
-
-if not os.path.exists(flag_file):
- with open(flag_file,'wt') as tfile:
- tfile.write('False')
-with open(flag_file,'rt') as tfile:
- flag=(tfile.read())
- if flag=='True':
- sys.stdout = open(os.path.join(debug_folder,'log.txt'), 'w')
- sys.stderr = sys.stdout
-import gevent as gvt
-import json as jsn
-import bottle as btl
-import _bottle_websocket_ as wbs
-import re as rgx
-import _EEL_.browsers as brw
-import random as rnd
-
-import pkg_resources as pkg
-import socket
-import mimetypes
-
-mimetypes.add_type('application/javascript', '.js')
-_eel_js_file = pkg.resource_filename('eel', 'eel.js')
-_eel_js = open(_eel_js_file, encoding='utf-8').read()
-_websockets = []
-_call_return_values = {}
-_call_return_callbacks = {}
-_call_number = 0
-_exposed_functions = {}
-_js_functions = []
-_mock_queue = []
-_mock_queue_done = set()
-
-# The maximum time (in milliseconds) that Python will try to retrieve a return value for functions executing in JS
-# Can be overridden through `eel.init` with the kwarg `js_result_timeout` (default: 10000)
-_js_result_timeout = 10000
-
-# All start() options must provide a default value and explanation here
-_start_args = {
- 'mode': 'chrome', # What browser is used
- 'host': 'localhost', # Hostname use for Bottle server
- 'port': 8000, # Port used for Bottle server (use 0 for auto)
- 'block': True, # Whether start() blocks calling thread
- 'jinja_templates': None, # Folder for jinja2 templates
- 'cmdline_args': ['--disable-http-cache'], # Extra cmdline flags to pass to browser start
- 'size': None, # (width, height) of main window
- 'position': None, # (left, top) of main window
- 'geometry': {}, # Dictionary of size/position for all windows
- 'close_callback': None, # Callback for when all windows have closed
- 'app_mode': True, # (Chrome specific option)
- 'all_interfaces': False, # Allow bottle server to listen for connections on all interfaces
- 'disable_cache': True, # Sets the no-store response header when serving assets
- 'app': btl.default_app(), # Allows passing in a custom Bottle instance, e.g. with middleware
- 'ssl_cert':False,
- 'ssl_key':False
-}
-
-# == Temporary (suppressable) error message to inform users of breaking API change for v1.0.0 ===
-_start_args['suppress_error'] = False
-api_error_message = '''
-----------------------------------------------------------------------------------
- 'options' argument deprecated in v1.0.0, see https://github.com/ChrisKnott/Eel
- To suppress this error, add 'suppress_error=True' to start() call.
- This option will be removed in future versions
-----------------------------------------------------------------------------------
-'''
-# ===============================================================================================
-
-# Public functions
-
-def expose(name_or_function=None):
- # Deal with '@eel.expose()' - treat as '@eel.expose'
- if name_or_function is None:
- return expose
-
- if type(name_or_function) == str: # Called as '@eel.expose("my_name")'
- name = name_or_function
-
- def decorator(function):
- _expose(name, function)
- return function
- return decorator
- else:
- function = name_or_function
- _expose(function.__name__, function)
- return function
-
-
-def init(path, allowed_extensions=['.js', '.html', '.txt', '.htm',
- '.xhtml', '.vue'], js_result_timeout=10000):
- global root_path, _js_functions, _js_result_timeout
- root_path = _get_real_path(path)
-
- js_functions = set()
- for root, _, files in os.walk(root_path):
- for name in files:
- if not any(name.endswith(ext) for ext in allowed_extensions):
- continue
-
- try:
- with open(os.path.join(root, name), encoding='utf-8') as file:
- contents = file.read()
- expose_calls = set()
- finder = rgx.findall(r'eel\.expose\(([^\)]+)\)', contents)
- for expose_call in finder:
- # If name specified in 2nd argument, strip quotes and store as function name
- if ',' in expose_call:
- expose_call = rgx.sub(r'["\']', '', expose_call.split(',')[1])
- expose_call = expose_call.strip()
- # Verify that function name is valid
- msg = "eel.expose() call contains '(' or '='"
- assert rgx.findall(r'[\(=]', expose_call) == [], msg
- expose_calls.add(expose_call)
- js_functions.update(expose_calls)
- except UnicodeDecodeError:
- pass # Malformed file probably
-
- _js_functions = list(js_functions)
- for js_function in _js_functions:
- _mock_js_function(js_function)
-
- _js_result_timeout = js_result_timeout
-
-
-def start(*start_urls, **kwargs):
- _start_args.update(kwargs)
- if 'options' in kwargs:
- if _start_args['suppress_error']:
- _start_args.update(kwargs['options'])
- else:
- raise RuntimeError(api_error_message)
-
- if _start_args['port'] == 0:
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.bind(('localhost', 0))
- _start_args['port'] = sock.getsockname()[1]
- sock.close()
-
- if _start_args['jinja_templates'] != None:
- from jinja2 import Environment, FileSystemLoader, select_autoescape
- templates_path = os.path.join(root_path, _start_args['jinja_templates'])
- _start_args['jinja_env'] = Environment(loader=FileSystemLoader(templates_path),
- autoescape=select_autoescape(['html', 'xml']))
-
-
- # Launch the browser to the starting URLs
- show(*start_urls)
-
- def run_lambda():
- if _start_args['all_interfaces'] == True:
- HOST = '0.0.0.0'
- else:
- HOST = _start_args['host']
-
- app = _start_args['app'] # type: btl.Bottle
- for route_path, route_params in BOTTLE_ROUTES.items():
- route_func, route_kwargs = route_params
- btl.route(path=route_path, callback=route_func, **route_kwargs)
- if _start_args['ssl_cert']==False or _start_args['ssl_key']==False:
- return btl.run(
- host=HOST,
- port=_start_args['port'],
- server=wbs.GeventWebSocketServer,
- quiet=True,
- app=app
- )
- else:
- ssldict = {'keyfile': _start_args['ssl_key'], 'certfile': _start_args['ssl_cert']}
- return btl.run(
- host=HOST,
- port=_start_args['port'],
- server=wbs.GeventWebSocketServer,
- quiet=True,
- app=app,
- **ssldict)
-
- # Start the webserver
- if _start_args['block']:
- run_lambda()
- else:
- spawn(run_lambda)
-
-
-def show(*start_urls):
- brw.open(start_urls, _start_args)
-
-
-def sleep(seconds):
- gvt.sleep(seconds)
-
-
-def spawn(function, *args, **kwargs):
- return gvt.spawn(function, *args, **kwargs)
-
-# Bottle Routes
-
-def _eel():
- start_geometry = {'default': {'size': _start_args['size'],
- 'position': _start_args['position']},
- 'pages': _start_args['geometry']}
-
- page = _eel_js.replace('/** _py_functions **/',
- '_py_functions: %s,' % list(_exposed_functions.keys()))
- page = page.replace('/** _start_geometry **/',
- '_start_geometry: %s,' % _safe_json(start_geometry))
- btl.response.content_type = 'application/javascript'
- _set_response_headers(btl.response)
- return page
-
-def _static(path):
- response = None
- if 'jinja_env' in _start_args and 'jinja_templates' in _start_args:
- template_prefix = _start_args['jinja_templates'] + '/'
- if path.startswith(template_prefix):
- n = len(template_prefix)
- template = _start_args['jinja_env'].get_template(path[n:])
- response = btl.HTTPResponse(template.render())
-
- if response is None:
- response = btl.static_file(path, root=root_path)
-
- _set_response_headers(response)
- return response
-
-def _websocket(ws):
- global _websockets
-
- for js_function in _js_functions:
- _import_js_function(js_function)
-
- page = btl.request.query.page
- if page not in _mock_queue_done:
- for call in _mock_queue:
- _repeated_send(ws, _safe_json(call))
- _mock_queue_done.add(page)
-
- _websockets += [(page, ws)]
-
- while True:
- msg = ws.receive()
- if msg is not None:
- message = jsn.loads(msg)
- spawn(_process_message, message, ws)
- else:
- _websockets.remove((page, ws))
- break
-
- _websocket_close(page)
-
-
-BOTTLE_ROUTES = {
- "/eel.js": (_eel, dict()),
- "/": (_static, dict()),
- "/eel": (_websocket, dict(apply=[wbs.websocket]))
-}
-
-# Private functions
-
-def _safe_json(obj):
- return jsn.dumps(obj, default=lambda o: None)
-
-
-def _repeated_send(ws, msg):
- for attempt in range(100):
- try:
- ws.send(msg)
- break
- except Exception:
- sleep(0.001)
-
-
-def _process_message(message, ws):
- if 'call' in message:
- error_info = {}
- try:
- return_val = _exposed_functions[message['name']](*message['args'])
- status = 'ok'
- except Exception as e:
- err_traceback = traceback.format_exc()
- traceback.print_exc()
- return_val = None
- status = 'error'
- error_info['errorText'] = repr(e)
- error_info['errorTraceback'] = err_traceback
- _repeated_send(ws, _safe_json({ 'return': message['call'],
- 'status': status,
- 'value': return_val,
- 'error': error_info,}))
- elif 'return' in message:
- call_id = message['return']
- if call_id in _call_return_callbacks:
- callback, error_callback = _call_return_callbacks.pop(call_id)
- if message['status'] == 'ok':
- callback(message['value'])
- elif message['status'] == 'error' and error_callback is not None:
- error_callback(message['error'], message['stack'])
- else:
- _call_return_values[call_id] = message['value']
- else:
- print('Invalid message received: ', message)
-
-
-def _get_real_path(path):
- if getattr(sys, 'frozen', False):
- return os.path.join(sys._MEIPASS, path)
- else:
- return os.path.abspath(path)
-
-
-def _mock_js_function(f):
- exec('%s = lambda *args: _mock_call("%s", args)' % (f, f), globals())
-
-
-def _import_js_function(f):
- exec('%s = lambda *args: _js_call("%s", args)' % (f, f), globals())
-
-
-def _call_object(name, args):
- global _call_number
- _call_number += 1
- call_id = _call_number + rnd.random()
- return {'call': call_id, 'name': name, 'args': args}
-
-
-def _mock_call(name, args):
- call_object = _call_object(name, args)
- global _mock_queue
- _mock_queue += [call_object]
- return _call_return(call_object)
-
-
-def _js_call(name, args):
- call_object = _call_object(name, args)
- for _, ws in _websockets:
- _repeated_send(ws, _safe_json(call_object))
- return _call_return(call_object)
-
-
-def _call_return(call):
- global _js_result_timeout
- call_id = call['call']
-
- def return_func(callback=None, error_callback=None):
- if callback is not None:
- _call_return_callbacks[call_id] = (callback, error_callback)
- else:
- for w in range(_js_result_timeout):
- if call_id in _call_return_values:
- return _call_return_values.pop(call_id)
- sleep(0.001)
- return return_func
-
-
-def _expose(name, function):
- msg = 'Already exposed function with name "%s"' % name
- assert name not in _exposed_functions, msg
- _exposed_functions[name] = function
-
-
-def _websocket_close(page):
- close_callback = _start_args.get('close_callback')
-
- if close_callback is not None:
- sockets = [p for _, p in _websockets]
- close_callback(page, sockets)
- else:
- # Default behaviour - wait 1s, then quit if all sockets are closed
- sleep(1.0)
- if len(_websockets) == 0:
- sys.exit()
-
-
-def _set_response_headers(response):
- if _start_args['disable_cache']:
- # https://stackoverflow.com/a/24748094/280852
- response.set_header('Cache-Control', 'no-store')
diff --git a/py/_EEL_/__main__.py b/py/_EEL_/__main__.py
deleted file mode 100644
index e97a612d..00000000
--- a/py/_EEL_/__main__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import sys
-import pkg_resources as pkg
-import PyInstaller.__main__ as pyi
-import os
-
-args = sys.argv[1:]
-main_script = args.pop(0)
-web_folder = args.pop(0)
-
-print("Building executable with main script '%s' and web folder '%s'...\n" %
- (main_script, web_folder))
-
-eel_js_file = pkg.resource_filename('eel', 'eel.js')
-js_file_arg = '%s%seel' % (eel_js_file, os.pathsep)
-web_folder_arg = '%s%s%s' % (web_folder, os.pathsep, web_folder)
-
-needed_args = ['--hidden-import', 'bottle_websocket',
- '--add-data', js_file_arg, '--add-data', web_folder_arg]
-full_args = [main_script] + needed_args + args
-
-print('Running:\npyinstaller', ' '.join(full_args), '\n')
-
-pyi.run(full_args)
diff --git a/py/_EEL_/browsers.py b/py/_EEL_/browsers.py
deleted file mode 100644
index 18640e38..00000000
--- a/py/_EEL_/browsers.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import subprocess as sps
-import webbrowser as wbr
-
-import _EEL_.chrome as chm
-import _EEL_.electron as ele
-import _EEL_.edge as edge
-#import _EEL_.firefox as ffx TODO
-#import _EEL_.safari as saf TODO
-
-_browser_paths = {}
-_browser_modules = {'chrome': chm,
- 'electron': ele,
- 'edge': edge}
-
-
-def _build_url_from_dict(page, options):
- scheme = page.get('scheme', 'http')
- host = page.get('host', 'localhost')
- port = page.get('port', options["port"])
- path = page.get('path', '')
- return '%s://%s:%d/%s' % (scheme, host, port, path)
-
-
-def _build_url_from_string(page, options):
- base_url = 'http://%s:%d/' % (options['host'], options['port'])
- return base_url + page
-
-
-def _build_urls(start_pages, options):
- urls = []
-
- for page in start_pages:
- method = _build_url_from_dict if isinstance(
- page, dict) else _build_url_from_string
- url = method(page, options)
- urls.append(url)
-
- return urls
-
-
-def open(start_pages, options):
- # Build full URLs for starting pages (including host and port)
- start_urls = _build_urls(start_pages, options)
-
- mode = options.get('mode')
- if mode in [None, False]:
- # Don't open a browser
- pass
- elif mode == 'custom':
- # Just run whatever command the user provided
- sps.Popen(options['cmdline_args'],
- stdout=sps.PIPE, stderr=sps.PIPE, stdin=sps.PIPE)
- elif mode in _browser_modules:
- # Run with a specific browser
- browser_module = _browser_modules[mode]
- path = _browser_paths.get(mode)
- if path is None:
- # Don't know this browser's path, try and find it ourselves
- path = browser_module.find_path()
- _browser_paths[mode] = path
-
- if path is not None:
- browser_module.run(path, options, start_urls)
- else:
- raise EnvironmentError("Can't find %s installation" % browser_module.name)
- else:
- # Fall back to system default browser
- for url in start_urls:
- wbr.open(url)
-
-
-def set_path(browser_name, path):
- _browser_paths[browser_name] = path
-
-
-def get_path(browser_name):
- return _browser_paths.get(browser_name)
-
diff --git a/py/_EEL_/chrome.py b/py/_EEL_/chrome.py
deleted file mode 100644
index d83ece35..00000000
--- a/py/_EEL_/chrome.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import sys, subprocess as sps, os
-
-# Every browser specific module must define run(), find_path() and name like this
-
-name = 'Google Chrome/Chromium'
-
-def run(path, options, start_urls):
- if options['app_mode']:
- for url in start_urls:
- sps.Popen([path, '--app=%s' % url] +
- options['cmdline_args'],
- stdout=sps.PIPE, stderr=sps.PIPE, stdin=sps.PIPE)
- else:
- args = options['cmdline_args'] + start_urls
- sps.Popen([path, '--new-window'] + args,
- stdout=sps.PIPE, stderr=sys.stderr, stdin=sps.PIPE)
-
-
-def find_path():
- if sys.platform in ['win32', 'win64']:
- return _find_chrome_win()
- elif sys.platform == 'darwin':
- return _find_chrome_mac() or _find_chromium_mac()
- elif sys.platform.startswith('linux'):
- return _find_chrome_linux()
- else:
- return None
-
-
-def _find_chrome_mac():
- default_dir = r'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
- if os.path.exists(default_dir):
- return default_dir
- # use mdfind ci to locate Chrome in alternate locations and return the first one
- name = 'Google Chrome.app'
- alternate_dirs = [x for x in sps.check_output(["mdfind", name]).decode().split('\n') if x.endswith(name)]
- if len(alternate_dirs):
- return alternate_dirs[0] + '/Contents/MacOS/Google Chrome'
- return None
-
-def _find_chromium_mac():
- default_dir = r'/Applications/Chromium.app/Contents/MacOS/Chromium'
- if os.path.exists(default_dir):
- return default_dir
- # use mdfind ci to locate Chromium in alternate locations and return the first one
- name = 'Chromium.app'
- alternate_dirs = [x for x in sps.check_output(["mdfind", name]).decode().split('\n') if x.endswith(name)]
- if len(alternate_dirs):
- return alternate_dirs[0] + '/Contents/MacOS/Chromium'
- return None
-
-def _find_chrome_linux():
- import whichcraft as wch
- chrome_names = ['chromium-browser',
- 'chromium',
- 'google-chrome',
- 'google-chrome-stable']
-
- for name in chrome_names:
- chrome = wch.which(name)
- if chrome is not None:
- return chrome
- return None
-
-def _find_chrome_win():
- import winreg as reg
- reg_path = r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe'
-
- for install_type in reg.HKEY_CURRENT_USER, reg.HKEY_LOCAL_MACHINE:
- try:
- reg_key = reg.OpenKey(install_type, reg_path, 0, reg.KEY_READ)
- chrome_path = reg.QueryValue(reg_key, None)
- reg_key.Close()
- if not os.path.isfile(chrome_path):
- continue
- except WindowsError:
- chrome_path = None
- else:
- break
-
- return chrome_path
diff --git a/py/_EEL_/edge.py b/py/_EEL_/edge.py
deleted file mode 100644
index 34be572b..00000000
--- a/py/_EEL_/edge.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import sys, subprocess as sps, os
-
-name = 'Edge'
-
-def run(path, options, start_urls):
- if options['app_mode']:
- for url in start_urls:
- sps.Popen([path, '--app=%s' % url] + options['cmdline_args'], stdout=sps.PIPE, stderr=sps.PIPE, stdin=sps.PIPE)
- else:
- cmd = 'start microsoft-edge:{}'.format(start_urls[0])
- sps.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=sps.PIPE, shell=True)
-
-def find_path():
- if sys.platform in ['win32', 'win64']:
- return _find_edge_win()
- else:
- return None
-
-def _find_edge_win():
- import winreg as reg
- reg_path = r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe'
-
- for install_type in reg.HKEY_CURRENT_USER, reg.HKEY_LOCAL_MACHINE:
- try:
- reg_key = reg.OpenKey(install_type, reg_path, 0, reg.KEY_READ)
- edge_path = reg.QueryValue(reg_key, None)
- reg_key.Close()
- if not os.path.isfile(edge_path):
- continue
- except WindowsError:
- edge_path = None
- else:
- break
-
- return edge_path
\ No newline at end of file
diff --git a/py/_EEL_/eel.js b/py/_EEL_/eel.js
deleted file mode 100644
index 5ac638f5..00000000
--- a/py/_EEL_/eel.js
+++ /dev/null
@@ -1,173 +0,0 @@
-eel = {
- _host: window.location.origin,
-
- set_host: function (hostname) {
- eel._host = hostname
- },
-
- expose: function(f, name) {
- if(name === undefined){
- name = f.toString();
- let i = 'function '.length, j = name.indexOf('(');
- name = name.substring(i, j).trim();
- }
-
- eel._exposed_functions[name] = f;
- },
-
- guid: function() {
- return eel._guid;
- },
-
- // These get dynamically added by library when file is served
- /** _py_functions **/
- /** _start_geometry **/
-
- _guid: ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
- (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
- ),
-
- _exposed_functions: {},
-
- _mock_queue: [],
-
- _mock_py_functions: function() {
- for(let i = 0; i < eel._py_functions.length; i++) {
- let name = eel._py_functions[i];
- eel[name] = function() {
- let call_object = eel._call_object(name, arguments);
- eel._mock_queue.push(call_object);
- return eel._call_return(call_object);
- }
- }
- },
-
- _import_py_function: function(name) {
- let func_name = name;
- eel[name] = function() {
- let call_object = eel._call_object(func_name, arguments);
- eel._websocket.send(eel._toJSON(call_object));
- return eel._call_return(call_object);
- }
- },
-
- _call_number: 0,
-
- _call_return_callbacks: {},
-
- _call_object: function(name, args) {
- let arg_array = [];
- for(let i = 0; i < args.length; i++){
- arg_array.push(args[i]);
- }
-
- let call_id = (eel._call_number += 1) + Math.random();
- return {'call': call_id, 'name': name, 'args': arg_array};
- },
-
- _sleep: function(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- },
-
- _toJSON: function(obj) {
- return JSON.stringify(obj, (k, v) => v === undefined ? null : v);
- },
-
- _call_return: function(call) {
- return function(callback = null) {
- if(callback != null) {
- eel._call_return_callbacks[call.call] = {resolve: callback};
- } else {
- return new Promise(function(resolve, reject) {
- eel._call_return_callbacks[call.call] = {resolve: resolve, reject: reject};
- });
- }
- }
- },
-
- _position_window: function(page) {
- let size = eel._start_geometry['default'].size;
- let position = eel._start_geometry['default'].position;
-
- if(page in eel._start_geometry.pages) {
- size = eel._start_geometry.pages[page].size;
- position = eel._start_geometry.pages[page].position;
- }
-
- if(size != null){
- window.resizeTo(size[0], size[1]);
- }
-
- if(position != null){
- window.moveTo(position[0], position[1]);
- }
- },
-
- _init: function() {
- eel._mock_py_functions();
-
- document.addEventListener("DOMContentLoaded", function(event) {
- let page = window.location.pathname.substring(1);
- eel._position_window(page);
-
- let websocket_addr = (eel._host + '/eel').replace('http', 'ws');
- websocket_addr += ('?page=' + page);
- eel._websocket = new WebSocket(websocket_addr);
-
- eel._websocket.onopen = function() {
- for(let i = 0; i < eel._py_functions.length; i++){
- let py_function = eel._py_functions[i];
- eel._import_py_function(py_function);
- }
-
- while(eel._mock_queue.length > 0) {
- let call = eel._mock_queue.shift();
- eel._websocket.send(eel._toJSON(call));
- }
- };
-
- eel._websocket.onmessage = function (e) {
- let message = JSON.parse(e.data);
- if(message.hasOwnProperty('call') ) {
- // Python making a function call into us
- if(message.name in eel._exposed_functions) {
- try {
- let return_val = eel._exposed_functions[message.name](...message.args);
- eel._websocket.send(eel._toJSON({'return': message.call, 'status':'ok', 'value': return_val}));
- } catch(err) {
- debugger
- eel._websocket.send(eel._toJSON(
- {'return': message.call,
- 'status':'error',
- 'error': err.message,
- 'stack': err.stack}));
- }
- }
- } else if(message.hasOwnProperty('return')) {
- // Python returning a value to us
- if(message['return'] in eel._call_return_callbacks) {
- if(message['status']==='ok'){
- eel._call_return_callbacks[message['return']].resolve(message.value);
- }
- else if(message['status']==='error' && eel._call_return_callbacks[message['return']].reject) {
- eel._call_return_callbacks[message['return']].reject(message['error']);
- }
- }
- } else {
- throw 'Invalid message ' + message;
- }
-
- };
- });
- }
-};
-
-eel._init();
-
-if(typeof require !== 'undefined'){
- // Avoid name collisions when using Electron, so jQuery etc work normally
- window.nodeRequire = require;
- delete window.require;
- delete window.exports;
- delete window.module;
-}
diff --git a/py/_EEL_/electron.py b/py/_EEL_/electron.py
deleted file mode 100644
index 7a443025..00000000
--- a/py/_EEL_/electron.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import sys
-import os
-import subprocess as sps
-import whichcraft as wch
-
-name = 'Electron'
-
-def run(path, options, start_urls):
- cmd = [path] + options['cmdline_args']
- cmd += ['.', ';'.join(start_urls)]
- sps.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr, stdin=sps.PIPE)
-
-
-def find_path():
- if sys.platform in ['win32', 'win64']:
- # It doesn't work well passing the .bat file to Popen, so we get the actual .exe
- bat_path = wch.which('electron')
- return os.path.join(bat_path, r'..\node_modules\electron\dist\electron.exe')
- elif sys.platform in ['darwin', 'linux']:
- # This should work find...
- return wch.which('electron')
- else:
- return None
-
diff --git a/py/_bottle_websocket_/server.py b/py/_bottle_websocket_/server.py
deleted file mode 100644
index 3b676147..00000000
--- a/py/_bottle_websocket_/server.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import logging
-from bottle import ServerAdapter
-from gevent import pywsgi
-from geventwebsocket.handler import WebSocketHandler
-from geventwebsocket.logging import create_logger
-
-
-class GeventWebSocketServer(ServerAdapter):
- def run(self, handler):
- server = pywsgi.WSGIServer((self.host, self.port), handler, handler_class=WebSocketHandler, **self.options)
-
- if not self.quiet:
- server.logger = create_logger('geventwebsocket.logging')
- server.logger.setLevel(logging.INFO)
- server.logger.addHandler(logging.StreamHandler())
-
- server.serve_forever()
diff --git a/py/lib/Interface.py b/py/lib/Interface.py
deleted file mode 100644
index 060f4c46..00000000
--- a/py/lib/Interface.py
+++ /dev/null
@@ -1,1376 +0,0 @@
-import io
-import _EEL_ as eel
-import sq_tools
-import Fs
-import sys
-
-import platform
-if sys.platform in ['win32', 'win64']:
- import win32com.client
-from base64 import b64encode
-try:
- import tkinter as tk
- from tkinter import filedialog
-except:pass
-import sq_tools
-import os
-import ast
-import Print
-import nutdb
-from subprocess import call
-import File_chunk2 as file_chunk
-import csv
-from listmanager import folder_to_list
-import html
-
-def About(noconsole=False):
- if noconsole==True:
- print('NSC_Builder by JulesOnTheRoad')
- sys.stdout.flush()
- return
- print(' __ _ __ __ ')
- print(' ____ _____ ____ / /_ __ __(_) /___/ /__ _____ ')
- print(' / __ \/ ___/ ___/ / __ \/ / / / / / __ / _ \/ ___/ ')
- print(' / / / (__ ) /__ / /_/ / /_/ / / / /_/ / __/ / ')
- print(' /_/ /_/____/\___/____/_.___/\__,_/_/_/\__,_/\___/_/ ')
- print(' /_____/ ')
- print('------------------------------------------------------------------------------------- ')
- print(' NINTENDO SWITCH CLEANER AND BUILDER ')
- print('------------------------------------------------------------------------------------- ')
- print('============================= BY JULESONTHEROAD ============================= ')
- print('------------------------------------------------------------------------------------- ')
- print('" POWERED BY SQUIRREL " ')
- print('" BASED ON THE WORK OF BLAWAR AND LUCA FRAGA " ')
- print('------------------------------------------------------------------------------------- ')
- print("Program's github: https://github.com/julesontheroad/NSC_BUILDER ")
- print('Cheats and Eshop information from nutdb and http://tinfoil.io ')
- print('------------------------------------------------------------------------------------- ')
-eel.init('web')
-
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-
-tmpfolder =os.path.join(NSCB_dir,'tmp')
-chromiumpath=os.path.join(ztools_dir,'chromium')
-chromiumpath_alt=os.path.join(squirrel_dir,'chromium')
-if sys.platform in ['win32', 'win64']:
- slimjet='slimjet.exe'
- slimpath=os.path.join(chromiumpath,slimjet)
- slimpath_alt=os.path.join(chromiumpath_alt,slimjet)
- chrom='chrlauncher.exe'
- chromiumpath=os.path.join(chromiumpath,chrom)
- chromiumpath_alt=os.path.join(chromiumpath_alt,chrom)
-else:
- chromiumdir=os.path.join(ztools_dir, 'chromium')
- chromiumpath=os.path.join(chromiumdir, 'chrome')
-
-
-local_lib_file = os.path.join(zconfig_dir, 'local_libraries.txt')
-remote_lib_file = os.path.join(zconfig_dir, 'remote_libraries.txt')
-cache_lib_file= os.path.join(zconfig_dir, 'remote_cache_location.txt')
-download_lib_file = os.path.join(zconfig_dir, 'download_libraries.txt')
-ssl_cert= os.path.join(zconfig_dir, 'certificate.pem')
-ssl_key= os.path.join(zconfig_dir, 'key.pem')
-web_folder=os.path.join(ztools_dir,'web')
-debug_folder=os.path.join(web_folder,'_debug_')
-flag_file=os.path.join(debug_folder,'flag')
-debug_log=os.path.join(debug_folder,'log')
-if os.path.exists(ssl_cert) and os.path.exists(ssl_key):
- pass
-else:
- ssl_cert=False;ssl_key=False
-
-globalpath=None;globalTD=None;globalremote=None
-globalocalpath=None
-gl_current_local_lib='all';gl_current_remote_lib='all'
-threads=[]
-from Drive import Private as DrivePrivate
-from Drive import DriveHtmlInfo
-from Drive import Public as DrivePublic
-
-def libraries(tfile):
- db={}
- try:
- with open(tfile,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- else:
- dict_={}
- for j in range(len(csvheader)):
- try:
- if row[j]==None or row[j]=='':
- dict_[csvheader[j]]=None
- else:
- dict_[csvheader[j]]=row[j]
- except:
- dict_[csvheader[j]]=None
- db[row[0]]=dict_
- # print(db)
- return db
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return False
-
-def get_library_from_path(tfile,filename):
- db=libraries(remote_lib_file)
- TD=None;lib=None;path="null"
- for entry in db:
- path=db[entry]['path']
- if filename.startswith(path):
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- else:
- pass
- if lib==None:
- db=libraries(cache_lib_file)
- TD=None;lib=None;path="null"
- for entry in db:
- path=db[entry]['path']
- if filename.startswith(path):
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- else:
- pass
- if TD=='':
- TD=None
- return lib,TD,libpath
-
-def get_cache_lib():
- db=libraries(cache_lib_file)
- TD=None;lib=None;path="null";libpath=None
- for entry in db:
- path=db[entry]['path']
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- if TD=='':
- TD=None
- return lib,TD,libpath
-
-
-def deepcheck_path(path1,path2,accuracy=0.9):
- path1=path1.lower()
- path1 = list([val for val in path1 if val.isalnum()])
- path1 = "".join(path1)
- path2=path2.lower()
- path2 = list([val for val in path2 if val.isalnum()])
- path2 = "".join(path2)
- ratio=SequenceMatcher(None, path1, path2).ratio()
- if ratio>=accuracy:
- return True
- else:
- return False
-
-def html_feed(feed='',style=1,message=''):
- if style==1:
- feed+='{}
'.format(message)
- return feed
- if style==2:
- feed+='{}
'.format(message)
- return feed
- if style==3:
- feed+='{} {} '.format(message[0],message[1])
- return feed
- if style==4:
- feed+='{} {}. {} {} '.format(message[0],message[1],message[2],message[3])
- return feed
- if style==5:
- feed+='{}
'.format(message)
- return feed
- if style==6:
- feed+='{}
'.format(message)
- return feed
- if style==7:
- feed+='{} {}
'.format(message[0],message[1])
- return feed
- if style==8:
- feed+='{}
'.format(message)
- return feed
- if style==9:
- feed+='{} {} '.format(message[0],message[1])
- return feed
- if style==10:
- feed+='{} {} '.format(message[0],message[1])
- return feed
-
-def get_screen_gallery(banner,screenlist):
- try:
- ret=''.format(banner)
- banner1="'{}'".format(banner)
- ret+='
'.format(banner1,banner)
- screenlist=ast.literal_eval(str(screenlist))
- if isinstance(screenlist, list):
- i=0
- for x in screenlist:
- x1="'{}'".format(x)
- ret+='
'.format(x1,x,i)
- i+=1
- if len(screenlist)<5:
- number=5-len(screenlist)
- for x in range(number):
- x="img/placeholder3.jpg"
- ret+='
'.format(x)
-
- ret+='
'
- return ret
- except:
- return "Not available"
-# @eel.expose
-# def show_picture(img):
- # eel.show(img)
-
-def format_file_tree(baselist,updlist,dlclist,titleid):
- feed=''
- feed=html_feed(feed,1,message=str('CURRENT GAME FILE TREE: '))
- feed=html_feed(feed,2,message=str('- Games: '))
- feed+=''
- if len(baselist)==0:
- message=['TitleID:',(titleid.upper()+' Version: '+'0')];feed=html_feed(feed,3,message)
- else:
- for i in range(len(baselist)):
- p_=baselist[i]
- message=['TitleID: ',(p_[0].upper()+' Version: '+p_[1])];feed=html_feed(feed,3,message)
- feed+=' '
-
- # feed=html_feed(feed,2,message=str('- Updates: '))
- if len(updlist)==0:
- # feed+=''
- # message=['None.',''];feed=html_feed(feed,3,message)
- # feed+=' '
- pass
- else:
- feed=html_feed(feed,2,message=str('- Updates: '))
- feed+=''
- for i in range(len(updlist)):
- p_=updlist[i]
- message=['TitleID: ',(p_[0].upper()+' Version: '+p_[1])];feed=html_feed(feed,3,message)
- feed+=' '
-
- # feed=html_feed(feed,2,message=str('- DLCs: '))
- if len(dlclist)==0:
- # feed+=''
- # message=['None.',''];feed=html_feed(feed,3,message)
- # feed+=' '
- pass
- else:
- feed=html_feed(feed,2,message=str('- DLCs: '))
- feed+=''
- for i in range(len(dlclist)):
- p_=dlclist[i]
- message=['TitleID: ',(p_[0].upper()+' Version: '+p_[1])];feed=html_feed(feed,3,message)
- feed+=' '
- return feed
-
-@eel.expose
-def clear():
- try:
- call('cls')
- except:
- try:
- call('clear')
- except:pass
-
-def search_local_lib(value,library):
- if library=='None':
- html='You need to create a library config file first
'
- eel.load_local_results(html)
- return
- try:
- db=libraries(local_lib_file)
- if db==False:
- eel.load_local_results(False)
- return
- if not library.lower()=='all':
- path=db[library]['path']
- print("* Searching library {}".format(library))
- sys.stdout.flush()
- results=folder_to_list(path,'all',value)
- sr=sortbyname(results)
- html=''
- i=0
- print(" - Retrieved {} files".format(str(len(results))))
- sys.stdout.flush()
- for it in sorted(sr.keys()):
- i+=1;type=''
- item=sr[it]
- item2=' '+it
- if item2.endswith('.nsp'):
- type=' nsp  '
- elif item2.endswith('.xci'):
- type=' xci   '
- var='local_res_'+str(i)
- html+='{} {}{} '.format(var,var,item,type,item2)
- html+=' '
- else:
- results=[]
- for entry in db:
- path=db[entry]['path']
- print("* Searching library {}".format(entry))
- sys.stdout.flush()
- res=folder_to_list(path,'all',value)
- print(" - Retrieved {} files".format(str(len(res))))
- sys.stdout.flush()
- results+=res
- sr=sortbyname(results)
- html=''
- i=0
- for it in sorted(sr.keys()):
- i+=1;type=''
- item=sr[it]
- item2=' '+it
- if item2.endswith('.nsp'):
- type=' nsp  '
- elif item2.endswith('.xci'):
- type=' xci   '
- var='local_res_'+str(i)
- html+='{} {}{} '.format(var,var,item,type,item2)
- # print(item)
- html+=' '
- eel.load_local_results(html)
- return
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- sys.stdout.flush()
-
-@eel.expose
-def get_libraries():
- try:
- htmlcode=''
- htmlcode+='All '
- db=libraries(local_lib_file)
- if db==False:
- htmlcode=''
- htmlcode+='{} '.format('None','Missing libraries')
- htmlcode+=' '
- # print(db[item])
- return htmlcode
- for item in db:
- htmlcode+='{} '.format(item,item)
- # print(db[item])
- htmlcode+=' '
- return htmlcode
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- sys.stdout.flush()
-
-@eel.expose
-def get_drive_libraries():
- try:
- htmlcode=''
- htmlcode+='All '
- db=libraries(remote_lib_file)
- if db==False:
- htmlcode=''
- htmlcode+='{} '.format('None','Missing libraries')
- htmlcode+=' '
- return htmlcode
- for item in db:
- htmlcode+='{} '.format(item,item)
- # print(db[item])
- htmlcode+=' '
- return htmlcode
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- sys.stdout.flush()
-
-def search_remote_lib(value,library):
- if library=='None':
- html='You need to create a library config file first
'
- eel.load_remote_results(html)
- return
- try:
- db=libraries(remote_lib_file)
- if db==False:
- eel.load_remote_results(False)
- return
- if not library.lower()=='all':
- results=[]
- path=db[library]['path']
- TD=db[library]['TD_name']
- print("* Searching library {}".format(library))
- sys.stdout.flush()
- response=DrivePrivate.search_folder(path,TD=TD,filter=value,Pick=False)
- if response!=False:
- results+=response
- send_results=[]
- try:
- for entry in results:
- send_results.append('{}/{}'.format(entry[2],entry[0]))
- sr=sortbyname(send_results)
- except:pass
- html=''
- i=0
- for it in sorted(sr.keys()):
- i+=1;type=''
- item=sr[it]
- item2=' '+it
- if item2.endswith('.nsp'):
- type=' nsp  '
- elif item2.endswith('.xci'):
- type=' xci   '
- var='remote_res_'+str(i)
- html+='{} {}{} '.format(var,var,item,type,item2)
- html+=' '
- else:
- results=[]
- for entry in db:
- path=db[entry]['path']
- TD=db[entry]['TD_name']
- print("* Searching library {}".format(entry))
- sys.stdout.flush()
- response=DrivePrivate.search_folder(path,TD=TD,filter=value,Pick=False)
- if response!=False:
- results+=response
- send_results=[]
- try:
- for entry in results:
- send_results.append('{}/{}'.format(entry[2],entry[0]))
- sr=sortbyname(send_results)
- except:pass
- html=''
- i=0
- for it in sorted(sr.keys()):
- i+=1;type=''
- item=sr[it]
- item2=' '+it
- if item2.endswith('.nsp'):
- type=' nsp  '
- elif item2.endswith('.xci'):
- type=' xci   '
- var='remote_res_'+str(i)
- html+='{} {}{} '.format(var,var,item,type,item2)
- html+=' '
- eel.load_remote_results(html)
- return
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- sys.stdout.flush()
-def sortbyname(files):
- results={};
- for f in files:
- k=str(os.path.basename(os.path.abspath(f)))
- results[k]=f
- return results
-
-@eel.expose
-def getfname():
- try:
- global threads
- for t in threads:
- # print(t)
- t.kill()
- treads=[]
- except BaseException as e:
- # Print.error('Exception: ' + str(e))
- pass
- root = tk.Tk()
- root.withdraw()
- root.wm_attributes('-topmost', 1)
- filename = filedialog.askopenfilename(
- filetypes=[
- ("All Filetypes","*.xci"),("All Filetypes","*.xcz"),("All Filetypes","*.xc0"),("All Filetypes","*.nsp"),("All Filetypes","*.nsz"),("All Filetypes","*.ns0"),("All Filetypes","*.nsx"),("All Filetypes","00"),
- ("xci","*.xci"),("xci","*.xcz"),("xci","*.xc0"),
- ("nsp","*.nsp"),("nsp","*.nsz"),("nsp","*.ns0"),("nsp","*.nsx"),
- ("Compressed files","*.nsz"),("Compressed files","*.xcz"),
- ("Uncompressed files","*.nsp"),("Uncompressed files","*.nsx"),("Uncompressed files","*.xci"),("Uncompressed files","*.xc0"),("Uncompressed files","*.ns0"),("Uncompressed files","00"),
- ("Split Files","*.ns0"),("Split Files","*.xc0"),("Split Files","00")
- ]
- )
- print('\nLoaded: '+filename)
- sys.stdout.flush()
- return str(filename)
-
-@eel.expose
-def call_search_remote_lib(value,selected):
- global threads
- threads.append(eel.spawn(search_remote_lib,value,selected))
- return
-
-@eel.expose
-def call_search_local_lib(value,selected):
- global threads
- threads.append(eel.spawn(search_local_lib,value,selected))
- return
-
-@eel.expose
-def call_showicon(filename):
- global threads
- threads.append(eel.spawn(showicon,filename))
- return
-
-@eel.expose
-def call_showicon_remote(filename):
- global threads
- threads.append(eel.spawn(showicon_remote,filename))
- return
-
-@eel.expose
-def addtodrive(filename):
- if os.path.exists(cache_lib_file):
- lib,TD,libpath=get_cache_lib()
- if lib!=None:
- file_id, is_download_link=DrivePublic.parse_url(filename)
- if is_download_link:
- remote=DrivePrivate.location(route=libpath,TD_Name=TD)
- result=remote.drive_service.files().get(fileId=file_id, fields="name,mimeType").execute()
- name=result['name']
- testpath=('{}/{}').format(libpath,name)
- remote=DrivePrivate.location(route=testpath,TD_Name=TD)
- if remote.name==None:
- name=DrivePrivate.add_to_drive(url=filename,filepath=libpath,makecopy=True,TD=TD)
- filename=('{}/{}').format(libpath,name)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- else:
- filename=testpath
- globalremote=remote
- return filename
-
-@eel.expose
-def call_getinfo(filename,remotelocation=False):
- global threads
- threads.append(eel.spawn(getinfo,filename,remotelocation))
- return
-
-@eel.expose
-def call_get_adv_filelist(filename,remotelocation=False):
- global threads
- threads.append(eel.spawn(getfiledata,filename,remotelocation))
- return
-
-@eel.expose
-def call_get_nacp_data(filename,remotelocation=False):
- global threads
- threads.append(eel.spawn(getnacpdata,filename,remotelocation))
- return
-
-@eel.expose
-def call_get_npdm_data(filename,remotelocation=False):
- global threads
- threads.append(eel.spawn(getnpdmdata,filename,remotelocation))
- return
-
-@eel.expose
-def call_get_cnmt_data(filename,remotelocation=False):
- global threads
- threads.append(eel.spawn(getcnmtdata,filename,remotelocation))
- return
-
-@eel.expose
-def call_get_verification_data(filename,remotelocation=False):
- global threads
- threads.append(eel.spawn(getverificationdata,filename,remotelocation))
- return
-
-@eel.expose
-def download(filename,remotelocation=False):
- filename=html.unescape(filename)
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- # header=DrivePrivate.get_html_header(remote.access_token)
- token=remote.access_token
- URL='https://www.googleapis.com/drive/v3/files/'+remote.ID+'?alt=media'
- eel.browser_download(URL,token)
-
-def showicon(filename):
- filename=html.unescape(filename)
- # global globalocalpath;
- # if globalocalpath!=filename:
- # result=deepcheck_path(globalocalpath,filename)
- # if result==False:
- # globalocalpath=filename
- # else:
- # filename=globalocalpath
- print('* Seeking icon')
- sys.stdout.flush()
- # print(filename)
- try:
- if filename.endswith('.nsp')or filename.endswith('.nsx') or filename.endswith('.nsz'):
- files_list=sq_tools.ret_nsp_offsets(filename)
- f = Fs.Nsp(filename, 'rb')
- elif filename.endswith('.xci') or filename.endswith('.xcz'):
- files_list=sq_tools.ret_xci_offsets(filename)
- f = Fs.Xci(filename)
- elif filename.endswith('.xc0') or filename.endswith('.ns0') or filename.endswith('00'):
- a=file_chunk.icon_info(filename)
- encoded = b64encode(a).decode("ascii")
- data= "data:image/png;base64, " + encoded
- eel.setImage(data)
- return
- else:
- eel.setImage("")
- return
- a=f.icon_info(files_list)
- f.flush()
- f.close()
- encoded = b64encode(a).decode("ascii")
- data= "data:image/png;base64, " + encoded
- eel.setImage(data)
- return
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- sys.stdout.flush()
- iconurl=retrieve_icon_from_server(filename)
- if iconurl!=False:
- eel.setImage(iconurl)
- return
- else:
- eel.setImage("")
- return
-
-def retrieve_icon_from_server(filename):
- if filename.endswith('.nsp')or filename.endswith('.nsx') or filename.endswith('.nsz'):
- f = Fs.Nsp(filename, 'rb')
- titleid=f.getnspid()
- elif filename.endswith('.xci') or filename.endswith('.xcz'):
- f = Fs.Xci(filename)
- titleid=f.getxciid()
- else:
- return False
- iconurl=nutdb.get_icon(titleid)
- return iconurl
-
-def showicon_remote(filename):
- filename=html.unescape(filename)
- global globalpath; global globalremote
- if globalpath!=filename:
- if not filename.startswith('http'):
- globalpath=filename
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- globalremote=remote
- else:
- globalpath=filename
- remote=DrivePublic.location(filename);readable=remote.readable
- globalremote=remote
- if not readable:
- eel.setImage("")
- return
- try:
- a=DriveHtmlInfo.icon_info(file=globalremote)
- # a=DriveHtmlInfo.icon_info(path=filename,TD=TD)
- encoded = b64encode(a).decode("ascii")
- data="data:image/png;base64, " + encoded
- eel.setImage(data)
- return
- except:
- iconurl=nutdb.get_icon(remote.id)
- if iconurl!=False:
- eel.setImage(iconurl)
- return
- else:
- eel.setImage("")
- return
-
-def getinfo(filename,remotelocation=False):
- filename=html.unescape(filename)
- print('* Retrieving Game Information')
- sys.stdout.flush()
- if remotelocation == False:
- if filename.endswith('.nsp')or filename.endswith('.nsx') or filename.endswith('.nsz'):
- f = Fs.ChromeNsp(filename, 'rb')
- elif filename.endswith('.xci') or filename.endswith('.xcz'):
- f = Fs.ChromeXci(filename)
- elif filename.endswith('.xc0') or filename.endswith('.ns0') or filename.endswith('00'):
- f=file_chunk.chunk(filename)
- else: return []
- if not filename.endswith('0'):
- dict=f.return_DBdict()
- else:
- dict=f.getDBdict()
- # print(dict)
- else:
- global globalpath; global globalremote
- if globalpath!=filename:
- globalpath=filename
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- globalremote=remote
- dict=DriveHtmlInfo.getDBdict(file=globalremote)
- if remotelocation == False:
- try:
- ModuleId,BuildID8,BuildID16=f.read_buildid()
- ModuleId=sq_tools.trimm_module_id(ModuleId)
- except BaseException as e:
- Print.error("Can't read buildID: " + str(e))
- ModuleId="-";BuildID8="-";BuildID16="-";
- else:
- try:
- ModuleId=dict['ModuleId']
- BuildID8=dict['BuildID8']
- BuildID16=dict['BuildID16']
- ModuleId=sq_tools.trimm_module_id(ModuleId)
- except:
- ModuleId="-";BuildID8="-";BuildID16="-";
- try:
- MinRSV=sq_tools.getMinRSV(dict['keygeneration'],dict['RSV'])
- RSV_rq_min=sq_tools.getFWRangeRSV(MinRSV)
- FW_rq=sq_tools.getFWRangeKG(dict['keygeneration'])
- except:
- MinRSV="-";RSV_rq_min="-";FW_rq="-"
- try:
- RGV=dict['RGV']
- RS_number=int(int(RGV)/65536)
- except:
- RGV="0";RS_number="0";
- send_=list()
- try:
- if str(dict['Type']).upper()=='DLC':
- send_.append(dict['contentname'])
- else:
- send_.append(dict['baseName'])
- except:send_.append('-')
- try:
- send_.append(dict['editor'])
- except:send_.append('-')
- try:
- send_.append(dict['id'])
- except:send_.append('-')
- try:
- send_.append(dict['version'])
- except:send_.append('-')
- try:
- send_.append(dict['Type'])
- except:send_.append('-')
- try:
- send_.append(dict['dispversion'])
- except:send_.append('-')
- try:
- send_.append(dict['metasdkversion'])
- except:send_.append('-')
- try:
- send_.append(dict['exesdkversion'])
- except:send_.append('-')
- try:
- lang=str((', '.join(dict['languages'])))
- send_.append(lang)
- except:send_.append('-')
- try:
- send_.append(dict['RSV'])
- except:send_.append('-')
- try:
- send_.append(str(dict['keygeneration'])+" -> " +FW_rq)
- except:send_.append('-')
- try:
- send_.append(dict['nsuId'])
- except:send_.append('-')
- try:
- genres=str((', '.join(dict['genretags'])))
- send_.append(genres)
- except:send_.append('-')
- try:
- ratags=str((', '.join(dict['ratingtags'])))
- send_.append(ratags)
- except:send_.append('-')
- try:
- send_.append(dict['worldreleasedate'])
- except:send_.append('-')
- try:
- send_.append(dict['numberOfPlayers'])
- except:send_.append('-')
- try:
- send_.append(str(dict['eshoprating']))
- except:send_.append('-')
- try:
- send_.append(sq_tools.getSize(dict['InstalledSize']))
- except:send_.append('-')
- try:
- send_.append(BuildID8)
- except:send_.append('-')
- try:
- send_.append(ModuleId)
- except:send_.append('-')
- try:
- send_.append(dict['key'])
- except:send_.append('-')
- try:
- send_.append(RSV_rq_min[1:-1])
- except:send_.append('-')
- try:
- if 'regions' in dict:
- reg=str((', '.join(dict['regions'])))
- send_.append(reg)
- else:
- send_.append('-')
- except:
- send_.append('-')
- try:
- if dict["intro"] !='-' and dict["intro"] !=None and dict["intro"] !='':
- if str(dict['Type']).upper()!='DLC':
- send_.append(dict['baseName']+". "+dict["intro"])
- else:
- send_.append(dict['contentname']+". "+dict["intro"])
- else:
- if str(dict['Type']).upper()!='DLC':
- send_.append(dict['baseName'])
- else:
- send_.append(dict['contentname'])
- except:
- try:
- if str(dict['Type']).upper()!='DLC':
- try:
- send_.append(dict['baseName'])
- except:send_.append('-')
- else:
- try:
- send_.append(dict['contentname'])
- except:send_.append('-')
- except:send_.append('-')
- try:
- if dict["description"] !='-':
- send_.append(dict["description"])
- else:
- send_.append("Not available")
- except:send_.append("Not available")
- try:
- if str(dict['HtmlManual']).lower()=="true":
- send_.append("Yes")
- else:
- send_.append("No")
- except:send_.append('-')
- try:
- # print(str(dict['linkedAccRequired']))
- if str(dict['linkedAccRequired']).lower()=="true":
- send_.append("Yes")
- else:
- send_.append("No")
- except:send_.append('-')
- try:
- if dict["ContentNumber"] !='-':
- if int(dict["ContentNumber"])>1:
- if 'ContentString' in dict:
- send_.append(dict["ContentString"])
- else:
- send_.append("Yes ({})".format(dict["ContentNumber"]))
- else:
- send_.append("No")
- else:
- send_.append("-")
- except:send_.append("-")
- if remotelocation == False:
- try:
- if filename.endswith('.nsp')or filename.endswith('.nsx') or filename.endswith('.nsz'):
- send_.append("Eshop")
- elif filename.endswith('.xci') or filename.endswith('.xcz'):
- send_.append("Gamecard")
- else:
- send_.append("-")
- except:send_.append("-")
- else:
- remotename=globalremote.name
- try:
- if remotename.endswith('.nsp')or remotename.endswith('.nsx') or remotename.endswith('.nsz'):
- send_.append("Eshop")
- elif remotename.endswith('.xci') or remotename.endswith('.xcz'):
- send_.append("Gamecard")
- else:
- send_.append("-")
- except:send_.append("-")
- try:
- send_.append(sq_tools.getSize(dict['GCSize']))
- except:send_.append('-')
- try:#data[30]
- x=get_screen_gallery(dict["bannerUrl"],dict["screenshots"])
- send_.append(x)
- except:send_.append("Not available")
- try:#data[31]
- RQversion=0
- if str(dict['Type']).upper()=='DLC':
- if int(RGV)>0:
- RQversion=str(RGV)+" -> Patch ({})".format(str(RS_number))
- else:
- RQversion=str(RGV)+" -> Application ({})".format(str(RS_number))
- send_.append(RQversion)
- except:send_.append('-')
- ###NEW JSON STUFF###
- try:#data[32]
- send_.append(dict['developer'])
- except:send_.append('-')
- try:#data[33]
- send_.append(dict['productCode'])
- except:send_.append('-')
- try:#data[34]
- if str(dict['OnlinePlay']).lower()=="true":
- send_.append("Yes")
- else:
- send_.append("No")
- except:send_.append('No')
- try:#data[35]
- if str(dict['SaveDataCloud']).lower()=="true":
- send_.append("Yes")
- else:
- send_.append("No")
- except:send_.append('No')
- try:#data[36]
- playmodes=str((', '.join(dict['playmodes'])))
- send_.append(playmodes)
- except:send_.append('-')
- try:#data[37]
- if str(dict['metascore']).lower()=='false':
- send_.append('-')
- else:
- send_.append(dict['metascore'])
- except:send_.append('-')
- try:#data[38]
- if str(dict['userscore']).lower()=='false':
- send_.append('-')
- else:
- send_.append(dict['userscore'])
- except:send_.append('-')
- try:#data[39]
- FWoncard=dict['FWoncard']
- FWoncard=str(FWoncard).strip("'")
- send_.append(FWoncard)
- except:send_.append('-')
- try:#data[40]
- if str(enablevideoplayback).lower() == 'true':
- video=dict['video']
- video=ast.literal_eval(str(video))
- video=video[0]
- send_.append(str(video))
- else:
- send_.append('-')
- except:send_.append('-')
- try:#data[41]
- if str(dict['openscore']).lower()=='false':
- send_.append('-')
- else:
- if dict['openscore'] != dict['metascore']:
- send_.append(dict['openscore'])
- else:
- send_.append('-')
- except:send_.append('-')
- try:
- f.flush()
- f.close()
- except:pass
- eel.setInfo(send_)
- return
-
-@eel.expose
-def getfiletree(ID):
- print('* Generating BaseID FileTree from DB')
- sys.stdout.flush()
- feed=''
- try:
- message,baselist,updlist,dlclist=nutdb.BaseID_tree(ID,printinfo=False)
- feed=format_file_tree(baselist,updlist,dlclist,ID)
- except:pass
- return feed
-
-@eel.expose
-def getcheats(ID):
- print('* Seeking cheats from DB')
- sys.stdout.flush()
- feed=''
- try:
- cheatID_list=nutdb.get_content_cheats(ID)
- feed=html_feed(feed,1,message=str('LIST OF CHEATS: '))
- if len(cheatID_list)==0:
- feed=html_feed(feed,2,message=str('- Cheats not found'))
- for i in range(len(cheatID_list)):
- data=cheatID_list[i]
- titleid=data[0];version=data[1];buildid=data[2]
- message=[str(titleid+' v'+version+'. '+'BuildID: '+buildid),''];feed=html_feed(feed,3,message)
- cheats=data[3]
- feed+=''
- for j in range(len(cheats)):
- entry=cheats[j]
- cheat_title=entry[0]
- cheat_source=entry[1]
- message=[cheat_title,""];feed=html_feed(feed,9,message)
- feed=html_feed(feed,8,message=(cheat_source))
- feed+='
'
- except:
- feed=html_feed(feed,1,message=str('LIST OF CHEATS: '))
- feed=html_feed(feed,2,message=str('- Cheats not found'))
- print(' DONE')
- sys.stdout.flush()
- return feed
-
-def getfiledata(filename,remotelocation=False):
- filename=html.unescape(filename)
- print('* Generating Titles File Data')
- sys.stdout.flush()
- if remotelocation != False:
- global globalpath; global globalremote
- if globalpath!=filename:
- globalpath=filename
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- globalremote=remote
- feed=DriveHtmlInfo.adv_file_list(file=globalremote)
- eel.set_adv_filelist(feed)
- return
- else:
- if filename.endswith('.nsp') or filename.endswith('.nsx') or filename.endswith('.nsz'):
- f = Fs.ChromeNsp(filename, 'rb')
- elif filename.endswith('.xci') or filename.endswith('.xcz'):
- f = Fs.ChromeXci(filename)
- elif filename.endswith('.xc0') or filename.endswith('.ns0') or filename.endswith('00'):
- ck=file_chunk.chunk(filename)
- feed=ck.send_html_adv_file_list()
- eel.set_adv_filelist(feed)
- return
- else: eel.set_adv_filelist("")
- feed=f.adv_file_list()
- f.flush()
- f.close()
- eel.set_adv_filelist(feed)
- return
-
-def getnacpdata(filename,remotelocation=False):
- filename=html.unescape(filename)
- print('* Reading Data from Nacp')
- sys.stdout.flush()
- if remotelocation != False:
- global globalpath; global globalremote
- if globalpath!=filename:
- globalpath=filename
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- globalremote=remote
- feed=DriveHtmlInfo.read_nacp(file=globalremote)
- if feed=='':
- feed=html_feed(feed,2,message=str('No nacp in the file'))
- eel.set_nacp_data(feed)
- return
- else:
- if filename.endswith('.nsp')or filename.endswith('.nsx') or filename.endswith('.nsz'):
- f = Fs.ChromeNsp(filename, 'rb')
- elif filename.endswith('.xci') or filename.endswith('.xcz'):
- f = Fs.ChromeXci(filename)
- elif filename.endswith('.xc0') or filename.endswith('.ns0') or filename.endswith('00'):
- ck=file_chunk.chunk(filename)
- feed=ck.send_html_nacp_data()
- if feed=='':
- feed=html_feed(feed,2,message=str('No nacp in the file'))
- eel.set_nacp_data(feed)
- return
- else:
- eel.set_nacp_data("")
- return
- feed=f.read_nacp(gui=True)
- f.flush()
- f.close()
- if feed=='':
- feed=html_feed(feed,2,message=str('No nacp in the file'))
- eel.set_nacp_data(feed)
- return
-
-def getnpdmdata(filename,remotelocation=False):
- filename=html.unescape(filename)
- print('* Reading Data from Npdm')
- sys.stdout.flush()
- if remotelocation != False:
- global globalpath; global globalremote
- if globalpath!=filename:
- globalpath=filename
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- globalremote=remote
- feed=DriveHtmlInfo.read_npdm(file=globalremote)
- if feed=='':
- feed=html_feed(feed,2,message=str('No npdm in the file'))
- eel.set_npdm_data(feed)
- return
- else:
- if filename.endswith('.nsp')or filename.endswith('.nsx') or filename.endswith('.nsz'):
- f = Fs.ChromeNsp(filename, 'rb')
- files_list=sq_tools.ret_nsp_offsets(filename)
- elif filename.endswith('.xci') or filename.endswith('.xcz'):
- f = Fs.ChromeXci(filename)
- files_list=sq_tools.ret_xci_offsets(filename)
- else:
- eel.set_npdm_data("")
- return
- feed=f.read_npdm(files_list)
- f.flush()
- f.close()
- if feed=='':
- feed=html_feed(feed,2,message=str('No npdm in the file'))
- eel.set_npdm_data(feed)
- return
-
-def getcnmtdata(filename,remotelocation=False):
- filename=html.unescape(filename)
- print('* Reading Data from Cnmt')
- sys.stdout.flush()
- if remotelocation != False:
- global globalpath; global globalremote
- if globalpath!=filename:
- globalpath=filename
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- globalremote=remote
- feed=DriveHtmlInfo.read_cnmt(file=globalremote)
- eel.set_cnmt_data(feed)
- return
- else:
- if filename.endswith('.nsp')or filename.endswith('.nsx') or filename.endswith('.nsz'):
- f = Fs.ChromeNsp(filename, 'rb')
- elif filename.endswith('.xci') or filename.endswith('.xcz'):
- f = Fs.ChromeXci(filename)
- elif filename.endswith('.xc0') or filename.endswith('.ns0') or filename.endswith('00'):
- ck=file_chunk.chunk(filename)
- feed=ck.send_html_cnmt_data()
- eel.set_cnmt_data(feed)
- return
- else:
- eel.set_cnmt_data("")
- return
- feed=f.read_cnmt()
- f.flush()
- f.close()
- eel.set_cnmt_data(feed)
- return
-
-def getverificationdata(filename,remotelocation=False):
- filename=html.unescape(filename)
- print('* Verifying files')
- sys.stdout.flush()
- if filename.endswith('.nsp')or filename.endswith('.nsx') or filename.endswith('.nsz'):
- f = Fs.ChromeNsp(filename, 'rb')
- elif filename.endswith('.xci') or filename.endswith('.xcz'):
- f = Fs.ChromeXci(filename)
- else:
- eel.set_ver_data("")
- return
- check,feed=f.verify()
- if not os.path.exists(tmpfolder):
- os.makedirs(tmpfolder)
- verdict,headerlist,feed=f.verify_sig(feed,tmpfolder)
- f.flush()
- f.close()
- try:
- os.remove(tmpfolder)
- except:pass
- eel.set_ver_data(feed)
- return
-
-def server(port=8000,host='localhost',videoplayback=True,ssl=False,noconsole=False,overwrite=False):
- ssl_cert= os.path.join(zconfig_dir, 'certificate.pem')
- ssl_key= os.path.join(zconfig_dir, 'key.pem')
- web_folder=os.path.join(ztools_dir,'web')
- debug_folder=os.path.join(web_folder,'_debug_')
- flag_file=os.path.join(debug_folder,'flag')
- debug_log=os.path.join(debug_folder,'log')
- if ssl==False:
- ssl_cert=False
- ssl_key=False
- else:
- if os.path.exists(ssl_cert) and os.path.exists(ssl_key):
- pass
- else:
- ssl_cert=False;ssl_key=False
- with open(flag_file,'wt') as tfile:
- if noconsole==True:
- tfile.write('True')
- else:
- tfile.write('False')
- global enablevideoplayback
- enablevideoplayback=videoplayback
- host=str(host)
- if port=='auto':
- port=0
- elif port=='rg8000':
- import socket, errno
- for p in range(8000,8999):
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- try:
- s.bind(("localhost", p))
- port=p
- s.close()
- break
- except socket.error as e:
- # print(e)
- pass
- s.close()
- else:
- try:
- port=int(port)
- except:
- port=8000
- if noconsole==True:
- sys.stdout = open(os.path.join(debug_folder,'log_{}.txt'.format(str(port))), 'w')
- sys.stderr = sys.stdout
- else:
- with open(os.path.join(debug_folder,'log_{}.txt'.format(str(port))), 'w') as tfile:
- tfile.write("")
- About(noconsole)
- if host=='0.0.0.0':
- h_='serverdomain'
- else:
- h_=str(host)
- if overwrite!=False:
- print("Launched in {}/nscb.html".format(overwrite))
- sys.stdout.flush()
- elif ssl_cert!=False and ssl_key!=False:
- print("Server launched in https://{}:{}/nscb.html".format(h_,port))
- print("Local Interface launched in https://{}:{}/main.html".format(h_,port))
- sys.stdout.flush()
- else:
- print("Server launched in http://{}:{}/nscb.html".format(h_,port))
- print("Local Interface in http://{}:{}/main.html".format(h_,port))
- sys.stdout.flush()
- while True:
- try:
- eel.start('nscb.html', mode=False,port=port,host=host,ssl_cert=ssl_cert,ssl_key=ssl_key)
- except:pass
-
-def start(browserpath='auto',videoplayback=True,height=800,width=740,port=8000,host='localhost',noconsole=False):
- if not (sys.platform in ['win32', 'win64']) and browserpath=='auto':
- browserpath='default'
- flag_file=os.path.join(debug_folder,'flag')
- with open(flag_file,'wt') as tfile:
- if noconsole==True:
- tfile.write('True')
- else:
- tfile.write('False')
- global enablevideoplayback
- enablevideoplayback=videoplayback
- host=str(host)
- if port=='auto':
- port=0
- elif port=='rg8000':
- import socket, errno
- for p in range(8000,8999):
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- try:
- s.bind(("localhost", p))
- port=p
- s.close()
- break
- except socket.error as e:
- # print(e)
- pass
- s.close()
- else:
- try:
- port=int(port)
- except:
- port=8000
- if noconsole==True:
- sys.stdout = open(os.path.join(debug_folder,'log_{}.txt'.format(str(port))), 'w')
- sys.stderr = sys.stdout
- else:
- with open(os.path.join(debug_folder,'log_{}.txt'.format(str(port))), 'w') as tfile:
- tfile.write("")
- try:
- if browserpath == 'default':
- print("Launched using default system browser")
- sys.stdout.flush()
- eel.start('main.html', mode='default', size=(height, width),port=port,host=host)
- elif str(browserpath).lower() == 'false' or str(browserpath).lower() == 'none':
- About(noconsole)
- if host=='0.0.0.0':
- print("Launched in http://{}:{}/main.html".format('localhost',port))
- print("Launched in http://{}:{}/main.html".format('127.0.0.1',port))
- sys.stdout.flush()
- else:
- print("Launched in http://{}:{}/main.html".format(host,port))
- sys.stdout.flush()
- if permanent==True:
- while True:
- try:
- eel.start('main.html', mode=False, size=(height, width),port=port,host=host)
- except:pass
- elif browserpath != 'auto' and os.path.exists(browserpath) and not browserpath.endswith('.ink'):
- eel.browsers.set_path('chrome', browserpath)
- About(noconsole)
- print("Launched using: "+browserpath)
- sys.stdout.flush()
- eel.start('main.html', mode='chrome', size=(height, width),port=port,host=host)
- elif browserpath != 'auto' and browserpath.endswith('.lnk') and sys.platform in ['win32', 'win64']:
- chrpath=os.path.join(ztools_dir,'chromium')
- chrpath_alt=os.path.join(squirrel_dir,'chromium')
- if not os.path.exists(browserpath) and os.path.exists(chrpath):
- browserpath=os.path.join(chrpath,browserpath)
- elif not os.path.exists(browserpath) and os.path.exists(chrpath_alt):
- browserpath=os.path.join(chrpath_alt,browserpath)
- elif not os.path.exists(browserpath) and sys.platform == 'win32':
- print(".lnk file doesn't exist")
- sys.stdout.flush()
- return False
- shell = win32com.client.Dispatch("WScript.Shell")
- browserpath = shell.CreateShortCut(browserpath)
- browserpath=browserpath.Targetpath
- eel.browsers.set_path('chrome', browserpath)
- About(noconsole)
- print("Launched using: "+browserpath)
- sys.stdout.flush()
- eel.start('main.html', mode='chrome', size=(height, width),port=port,host=host)
- elif os.path.exists(chromiumpath):
- eel.browsers.set_path('chrome', chromiumpath)
- About(noconsole)
- print("Launched using: "+chromiumpath)
- sys.stdout.flush()
- eel.start('main.html', mode='chrome', size=(height, width),port=port,host=host)
- elif os.path.exists(chromiumpath_alt):
- eel.browsers.set_path('chrome', chromiumpath_alt)
- About(noconsole)
- print("Launched using: "+chromiumpath_alt)
- sys.stdout.flush()
- eel.start('main.html', mode='chrome', size=(height, width),port=port,host=host)
- elif os.path.exists(slimpath):
- eel.browsers.set_path('chrome', slimpath)
- About(noconsole)
- print("Launched using: "+slimpath)
- sys.stdout.flush()
- eel.start('main.html', mode='chrome', size=(height, width),port=port,host=host)
- elif os.path.exists(slimpath_alt):
- eel.browsers.set_path('chrome', slimpath_alt)
- About(noconsole)
- print("Launched using: "+slimpath_alt)
- sys.stdout.flush()
- eel.start('main.html', mode='chrome', size=(height, width),port=port,host=host)
- else:
- try:
- About(noconsole)
- print("Launched using Chrome Installation")
- sys.stdout.flush()
- eel.start('main.html', mode='chrome', size=(height, width),port=port,host=host)
- except EnvironmentError:
- print("Chrome wasn't detected. Launched using Windows Edge with limited compatibility")
- sys.stdout.flush()
- if sys.platform in ['win32', 'win64'] and int(platform.release()) >= 10:
- # print(platform.release())
- eel.start('main.html', mode='edge', size=(height, width),port=port,host=host)
- else:
- raise
- except (SystemExit, MemoryError, KeyboardInterrupt):
- try:
- try:
- global threads
- for t in threads:
- # print(t)
- t.kill()
- treads=[]
- except BaseException as e:
- # Print.error('Exception: ' + str(e))
- pass
- print("User closed the program")
- sys.stdout.flush()
- sys.exit()
- except:pass
\ No newline at end of file
diff --git a/py/lib/picker_walker.py b/py/lib/picker_walker.py
deleted file mode 100644
index d5d892aa..00000000
--- a/py/lib/picker_walker.py
+++ /dev/null
@@ -1,710 +0,0 @@
-import os
-import string
-from shutil import disk_usage
-from python_pick import pick
-from python_pick import Picker
-import listmanager
-import os
-import shutil
-from secondary import clear_Screen
-import csv
-try:
- import ujson as json
-except:
- import json
-
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-testroute1=os.path.join(squirrel_dir, "squirrel.py")
-testroute2=os.path.join(squirrel_dir, "squirrel.exe")
-urlconfig=os.path.join(zconfig_dir,'NUT_DB_URL.txt')
-isExe=False
-if os.path.exists(testroute1):
- squirrel=testroute1
- isExe=False
-elif os.path.exists(testroute2):
- squirrel=testroute2
- isExe=True
-local_libraries=os.path.join(zconfig_dir,'local_libraries.txt')
-remote_lib_file = os.path.join(zconfig_dir, 'remote_libraries.txt')
-remote_lib_cache=os.path.join(zconfig_dir, 'remote_lib_cache')
-cache_lib_file= os.path.join(zconfig_dir, 'remote_cache_location.txt')
-
-def About():
- print(' __ _ __ __ ')
- print(' ____ _____ ____ / /_ __ __(_) /___/ /__ _____ ')
- print(' / __ \/ ___/ ___/ / __ \/ / / / / / __ / _ \/ ___/ ')
- print(' / / / (__ ) /__ / /_/ / /_/ / / / /_/ / __/ / ')
- print(' /_/ /_/____/\___/____/_.___/\__,_/_/_/\__,_/\___/_/ ')
- print(' /_____/ ')
- print('------------------------------------------------------------------------------------- ')
- print(' NINTENDO SWITCH CLEANER AND BUILDER ')
- print('------------------------------------------------------------------------------------- ')
- print('============================= BY JULESONTHEROAD ============================= ')
- print('------------------------------------------------------------------------------------- ')
- print('" POWERED BY SQUIRREL " ')
- print('" BASED ON THE WORK OF BLAWAR AND LUCA FRAGA " ')
- print('------------------------------------------------------------------------------------- ')
- print("Program's github: https://github.com/julesontheroad/NSC_BUILDER ")
- print('Cheats and Eshop information from nutdb and http://tinfoil.io ')
- print('------------------------------------------------------------------------------------- ')
-
-def libraries(tfile):
- db={}
- try:
- with open(tfile,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- else:
- dict_={}
- for j in range(len(csvheader)):
- try:
- if row[j]==None or row[j]=='':
- dict_[csvheader[j]]=None
- else:
- dict_[csvheader[j]]=row[j]
- except:
- dict_[csvheader[j]]=None
- db[row[0]]=dict_
- return db
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return False
-
-def get_library_from_path(tfile=None,filename=None):
- if tfile==None:
- db=libraries(remote_lib_file)
- else:
- db=libraries(tfile)
- TD=None;lib=None;path="null"
- for entry in db:
- path=db[entry]['path']
- if filename.startswith(path):
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- else:
- pass
- if lib==None:
- db=libraries(cache_lib_file)
- TD=None;lib=None;path="null"
- for entry in db:
- path=db[entry]['path']
- if filename.startswith(path):
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- else:
- pass
- if TD=='':
- TD=None
- return lib,TD,libpath
-
-def get_cache_lib():
- db=libraries(cache_lib_file)
- TD=None;lib=None;path="null";libpath=None
- for entry in db:
- path=db[entry]['path']
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- if TD=='':
- TD=None
- return lib,TD,libpath
-
-def get_disks():
- available_drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
- for d in available_drives:
- dsktotal, dskused, dskfree=disk_usage(str(d))
- if int(dsktotal)==0:
- available_drives.remove(d)
- title = 'Pick drive: \n + Press enter or intro to select \n + Press E to scape back to menu'
- options = available_drives
- picker = Picker(options, title, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection)
- picker.register_custom_handler(ord('E'), end_selection)
- selected = picker.start()
- if selected[0]==False:
- return False
- drive=selected[0]
- return drive
-
-def pick_order():
- title = 'Select order to list the files: \n + Press enter or intro to select \n + Press E to scape back to menu'
- options = ['name_ascending','name_descending','size_ascending','size_descending','date_ascending','date_descending']
- picker = Picker(options, title, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection)
- picker.register_custom_handler(ord('E'), end_selection)
- selected = picker.start()
- if selected[0]==False:
- return False
- order=selected[0]
- return order
-
-def pick_folder(folders,previous):
- Recursive=False
- title = 'Pick folder:\n + Press space or right to select content \n + Press E to move to file selection phase (Shows files in current folder) \n + Press R to move to file selection phase (Shows files recursevely including subfolders) \n + Press X to exit selection'
- options = folders
- picker = Picker(options, title, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection)
- picker.register_custom_handler(ord('E'), end_selection)
- def end_selection_recursive(picker):
- return True,-1
- picker.register_custom_handler(ord('r'), end_selection_recursive)
- picker.register_custom_handler(ord('R'), end_selection_recursive)
- def exit_selection(picker):
- return "EXIT",-1
- picker.register_custom_handler(ord('x'), exit_selection)
- picker.register_custom_handler(ord('X'), exit_selection)
- selected=picker.start()
- if selected[0]==False:
- return False,False
- if selected[0]=="EXIT":
- return "EXIT",False
- if selected[0]==True:
- return False,True
- folder=os.path.join(previous,selected[0])
- return folder,Recursive
-
-def folder_walker():
- Recursive=False
- folder=get_disks()
- if folder==False:
- return False,False
- while True:
- folders=next(os.walk(folder))[1]
- if not folders:
- break
- choice,Recursive=pick_folder(folders,folder)
- if choice==False:
- break
- if choice=="EXIT":
- return False,False
- folder=choice
- return folder,Recursive
-
-def get_files_from_walk(tfile=None,extlist=['nsp','nsz','xci','xcz'],filter=False,recursive=False,doPrint=False):
- if not isinstance(extlist, list):
- if str(extlist).lower()!='all':
- ext=extlist.split()
- extlist=[]
- for x in ext:
- extlist.append(x)
- folder,rec=folder_walker()
- if folder==False:
- return False
- if rec==True:
- recursive=True
- print("Parsing files. Please wait...")
- title = 'Add a search filter?: '
- options = ['Yes','No']
- selected = pick(options, title, min_selection_count=1)
- response=selected[0]
- if response=='No':
- pass
- else:
- clear_Screen()
- About()
- filter=input('INPUT SEARCH FILTER: ')
- if recursive==False:
- files=listmanager.nextfolder_to_list(folder,extlist=extlist,filter=filter)
- else:
- files=listmanager.folder_to_list(folder,extlist=extlist,filter=filter)
- if not files:
- sys.exit("Query didn't return any files")
- order=pick_order()
- if order==False:
- return False
- filedata={}
- for file in files:
- try:
- fname=os.path.basename(file)
- fsize=os.path.getsize(file)
- fdate=os.path.getctime(file)
- entry={'filepath':file,'filename':fname,'size':fsize,'date':fdate}
- if not fname in filedata:
- filedata[fname]=entry
- except:pass
- options=[]
- if order=='name_ascending':
- options=sorted(filedata,key=lambda x:filedata[x]['filename'])
- elif order=='name_descending':
- options=sorted(filedata,key=lambda x:filedata[x]['filename'])
- options.reverse()
- elif order=='size_ascending':
- options=sorted(filedata,key=lambda x:filedata[x]['size'])
- elif order=='size_descending':
- options=sorted(filedata,key=lambda x:filedata[x]['size'])
- options.reverse()
- elif order=='date_ascending':
- options=sorted(filedata,key=lambda x:filedata[x]['date'])
- elif order=='date_descending':
- options=sorted(filedata,key=lambda x:filedata[x]['date'])
- options.reverse()
- title = 'Select content: \n + Press space or right to select entries \n + Press Enter to confirm selection \n + Press E to exit selection \n + Press A to select all entries'
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- def select_all(picker):
- return "ALL",-1
- picker.register_custom_handler(ord('e'), end_selection)
- picker.register_custom_handler(ord('E'), end_selection)
- picker.register_custom_handler(ord('a'), select_all)
- picker.register_custom_handler(ord('A'), select_all)
- selected=picker.start()
- if selected[0]==False:
- print("User didn't select any files")
- return False
- newgpaths=[]
- if selected[0]=="ALL":
- for game in options:
- newgpaths.append(os.path.join(folder,game))
- else:
- for game in selected:
- newgpaths.append(os.path.join(folder,game[0]))
- if tfile!=None:
- with open(tfile,'w', encoding='utf8') as textfile:
- for i in newgpaths:
- textfile.write((i).strip()+"\n")
- if doPrint!=False:
- for i in newgpaths:
- print(i)
- return newgpaths
-
-def get_libs(lib="local"):
- libraries={}
- if lib=="local":
- libtfile=local_libraries
- else:
- libtfile=lib
- with open(libtfile,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0;up=False
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- if 'library_name' and 'path' and 'Update' in csvheader:
- lb=csvheader.index('library_name')
- pth=csvheader.index('path')
- up=csvheader.index('Update')
- else:
- if 'library_name' and 'path' in csvheader:
- lb=csvheader.index('library_name')
- pth=csvheader.index('path')
- else:break
- else:
- try:
- update=False
- library=str(row[lb])
- route=str(row[pth])
- if up!=False:
- update=str(row[up])
- if update.upper()=="TRUE":
- update=True
- else:
- update=False
- else:
- update=False
- libraries[library]=[route,update]
- except:pass
- return libraries
-
-def search_with_filter(folder_paths,extlist=['nsp','nsz','xci','xcz']):
- filepaths=[]
- title = 'Add a search filter?: '
- options = ['Yes','No']
- selected = pick(options, title, min_selection_count=1)
- response=selected[0]
- if response=='No':
- for fo in folder_paths:
- rlist=listmanager.folder_to_list(fo,extlist)
- filepaths=[*filepaths,*rlist]
- return filepaths
- else:
- clear_Screen()
- About()
- ck=input('INPUT SEARCH FILTER: ')
- for fo in folder_paths:
- rlist=listmanager.folder_to_list(fo,extlist,ck)
- filepaths=[*filepaths,*rlist]
- return filepaths
-
-def select_from_local_libraries(tfile=None,extlist=['nsp','nsz','xci','xcz']):
- if not os.path.exists(local_libraries):
- sys.exit("Missing local_libraries.txt")
- if not isinstance(extlist, list):
- if str(extlist).lower()!='all':
- ext=extlist.split()
- extlist=[]
- for x in ext:
- extlist.append(x)
- db=get_libs()
- title = 'Select libraries to search: \n + Press space or right to select content \n + Press E to finish selection \n + Press A to select all libraries'
- folder_paths= []
- options=[]
- for k in db:
- options.append(k)
- picker = Picker(options, title,multi_select=True,min_selection_count=1)
- def end_selection(picker):
- return False,-1
- def select_all(picker):
- return True,options
- picker.register_custom_handler(ord('e'), end_selection)
- picker.register_custom_handler(ord('E'), end_selection)
- picker.register_custom_handler(ord('a'), select_all)
- picker.register_custom_handler(ord('A'), select_all)
- selected = picker.start()
- if selected[0]==False:
- print("User didn't select any libraries")
- return False,False
- if selected[0]==True:
- for k in db.keys():
- folder_paths.append((db[k])[0])
- else:
- for entry in selected:
- folder_paths.append((db[entry[0]])[0])
- order=pick_order()
- if order==False:
- return False
- filepaths=search_with_filter(folder_paths,extlist)
- filedata={}
- for file in filepaths:
- try:
- fname=os.path.basename(file)
- fsize=os.path.getsize(file)
- fdate=os.path.getctime(file)
- entry={'filepath':file,'filename':fname,'size':fsize,'date':fdate}
- if not fname in filedata:
- filedata[fname]=entry
- except:pass
- options=[]
- if order=='name_ascending':
- options=sorted(filedata,key=lambda x:filedata[x]['filename'])
- elif order=='name_descending':
- options=sorted(filedata,key=lambda x:filedata[x]['filename'])
- options.reverse()
- elif order=='size_ascending':
- options=sorted(filedata,key=lambda x:filedata[x]['size'])
- elif order=='size_descending':
- options=sorted(filedata,key=lambda x:filedata[x]['size'])
- options.reverse()
- elif order=='date_ascending':
- options=sorted(filedata,key=lambda x:filedata[x]['date'])
- elif order=='date_descending':
- options=sorted(filedata,key=lambda x:filedata[x]['date'])
- options.reverse()
- print(" * Entering File Picker")
- title = 'Select content to install or transfer: \n + Press space or right to select content \n + Press E to finish selection'
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection);picker.register_custom_handler(ord('E'), end_selection)
- selected=picker.start()
- if selected[0]==False:
- print(" User didn't select any files")
- return False
- if tfile!=None:
- with open(tfile,'a') as textfile:
- for f in selected:
- fpath=(filedata[f[0]])['filepath']
- textfile.write(fpath+'\n')
- else:
- filespaths=[]
- for f in selected:
- fpath=(filedata[f[0]])['filepath']
- filespaths.append(fpath)
- return filespaths
-
-def remote_interface_filter(path=None):
- title = 'Add a search filter?: '
- options = ['Yes','No']
- selected = pick(options, title, min_selection_count=1)
- response=selected[0]
- if response=='No':
- return None
- else:
- clear_Screen()
- About()
- if path != None:
- print("Filepath {}\n".format(str(path)))
- ck=input('INPUT SEARCH FILTER: ')
- return ck
-
-def remote_interface_filter_local(filelist):
- title = 'Add a search filter?: '
- options = ['Yes','No']
- selected = pick(options, title, min_selection_count=1)
- response=selected[0]
- if response=='No':
- return filelist
- else:
- clear_Screen()
- About()
- ck=input('INPUT SEARCH FILTER: ')
- filelist=listmanager.filter_vlist(filelist,token=ck,Print=False)
- return filelist
-
-def eval_link(tfile,userfile):
- link=input("Enter your choice: ")
- link=link.strip()
- if '&' in link:
- varout='999'
- elif len(link)<2:
- varout=link
- else:
- varout='999'
- with open(userfile,"w", encoding='utf8') as userinput:
- userinput.write(varout)
- if link.startswith('https://1fichier.com'):
- with open(tfile,"a", encoding='utf8') as textfile:
- textfile.write(link+'\n')
- elif link.startswith('https://drive.google.com'):
- with open(tfile,"a", encoding='utf8') as textfile:
- textfile.write(link+'\n')
-
-def drive_pick_libraries():
- title = 'Select libraries to search: \n + Press space or right to select content \n + Press E to finish selection'
- db=libraries(remote_lib_file)
- if db==False:
- return False,False
- options = [x for x in db.keys()]
- picker = Picker(options, title,multi_select=True,min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection)
- picker.register_custom_handler(ord('E'), end_selection)
- selected = picker.start()
- if selected[0]==False:
- return False,False
- # print(selected)
- paths=list();TDs=list()
- for entry in selected:
- paths.append((db[entry[0]])['path'])
- try:
- TDs.append((db[entry[0]])['TD_name'])
- except:
- TDs.append(None)
- # for p in range(len(paths)):
- # print(paths[p])
- # print(TDs[p])
- return paths,TDs
-
-def remote_select_from_libraries(tfile):
- from workers import concurrent_scrapper
- db=libraries(remote_lib_file)
- if db==False:
- sys.exit(f"Missing {remote_lib_file}")
- paths,TDs=drive_pick_libraries()
- if paths==False:
- return False
- order=pick_order()
- if order==False:
- return False
- filter=remote_interface_filter()
- print(" * Parsing files from Google Drive. Please Wait...")
- if isinstance(paths, list):
- db={}
- for i in range(len(paths)):
- db[paths[i]]={'path':paths[i],'TD_name':TDs[i]}
- files=concurrent_scrapper(filter=filter,order=order,remotelib='all',db=db)
- else:
- db={}
- db[paths]={'path':paths,'TD_name':TDs}
- files=concurrent_scrapper(filter=filter,order=order,remotelib='all',db=db)
- print(" * Entering File Picker")
- title = 'Select content to install or transfer: \n + Press space or right to select content \n + Press Enter to confirm selection \n + Press E to exit selection'
- filenames=[]
- for f in files:
- filenames.append(f[0])
- options=filenames
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection);picker.register_custom_handler(ord('E'), end_selection)
- selected=picker.start()
- if selected[0]==False:
- print(" User didn't select any files")
- return False
- with open(tfile,'a') as textfile:
- for f in selected:
- fpath=(files[f[1]])[2]+'/'+(files[f[1]])[0]
- lib,TD,libpath=get_library_from_path(remote_lib_file,fpath)
- try:
- ID=(files[f[1]])[4]
- textfile.write(f"{fpath}|{TD}|{ID}\n")
- except:
- textfile.write(f"{fpath}|{TD}\n")
-
-def remote_select_from_cache(tfile):
- from workers import concurrent_scrapper
- cache_is_setup=False
- if not os.path.exists(remote_lib_cache):
- os.makedirs(remote_lib_cache)
- jsonlist=listmanager.folder_to_list(remote_lib_cache,extlist=['json'])
- if not jsonlist:
- print("Cache wasn't found. Generating cache up...")
- from workers import concurrent_cache
- concurrent_cache()
- jsonlist=listmanager.folder_to_list(remote_lib_cache,extlist=['json'])
- if not jsonlist:
- sys.exit("Can't setup remote cache. Are libraries set up?")
- libnames=[]
- for j in jsonlist:
- bname=os.path.basename(j)
- bname=bname.replace('.json','')
- libnames.append(bname)
- title = 'Select libraries to search: \n + Press space or right to select content \n + Press Enter to confirm selection \n + Press E to exit selection \n + Press A to select all libraries'
- db=libraries(remote_lib_file)
- options = libnames
- picker = Picker(options, title,multi_select=True,min_selection_count=1)
- def end_selection(picker):
- return False,-1
- def select_all(picker):
- return True,libnames
- picker.register_custom_handler(ord('e'), end_selection)
- picker.register_custom_handler(ord('E'), end_selection)
- picker.register_custom_handler(ord('a'), select_all)
- picker.register_custom_handler(ord('A'), select_all)
- selected = picker.start()
- if selected[0]==False:
- print("User didn't select any libraries")
- return False,False
- if selected[0]==True:
- cachefiles=jsonlist
- else:
- cachefiles=[]
- for entry in selected:
- fname=entry[0]+'.json'
- fpath=os.path.join(remote_lib_cache,fname)
- cachefiles.append(fpath)
- cachedict={}
- for cach in cachefiles:
- with open(cach) as json_file:
- data = json.load(json_file)
- for entry in data:
- if not entry in cachedict:
- cachedict[entry]=data[entry]
- # for k in cachedict.keys():
- # print(k)
- order=pick_order()
- if order==False:
- return False
- options=[]
- if order=='name_ascending':
- options=sorted(cachedict,key=lambda x:cachedict[x]['filepath'])
- elif order=='name_descending':
- options=sorted(cachedict,key=lambda x:cachedict[x]['filepath'])
- options.reverse()
- elif order=='size_ascending':
- options=sorted(cachedict,key=lambda x:cachedict[x]['size'])
- elif order=='size_descending':
- options=sorted(cachedict,key=lambda x:cachedict[x]['size'])
- options.reverse()
- elif order=='date_ascending':
- options=sorted(cachedict,key=lambda x:cachedict[x]['date'])
- elif order=='date_descending':
- options=sorted(cachedict,key=lambda x:cachedict[x]['date'])
- options.reverse()
- options=remote_interface_filter_local(options)
- print(" * Entering File Picker")
- title = 'Select content to install or transfer: \n + Press space or right to select content \n + Press Enter to confirm selection \n + Press E to exit selection'
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection);picker.register_custom_handler(ord('E'), end_selection)
- selected=picker.start()
- if selected[0]==False:
- print(" User didn't select any files")
- return False
- with open(tfile,'a') as textfile:
- for f in selected:
- fpath=(cachedict[f[0]])['filepath']
- TD=(cachedict[f[0]])['TD']
- try:
- ID=(cachedict[f[0]])['id']
- textfile.write(f"{fpath}|{TD}|{ID}\n")
- except:
- textfile.write(f"{fpath}|{TD}\n")
-
-def remote_select_from_walker(tfile,types='all'):
- from workers import concurrent_scrapper
- from Drive import Private as DrivePrivate
- ext=[]
- if types!='all':
- items=types.split(' ')
- for x in items:
- ext.append(str(x).lower())
- folder,TeamDrive=DrivePrivate.folder_walker()
- if TeamDrive=="" or TeamDrive==False:
- TeamDrive=None
- if folder==False:
- return False
- filt=remote_interface_filter()
- order=pick_order()
- if order==False:
- return False
- print(f"- Checking {folder}")
- print(" * Parsing files from Google Drive. Please Wait...")
- db={}
- db[folder]={'path':folder,'TD_name':TeamDrive}
- files=concurrent_scrapper(filter=filt,order=order,remotelib='all',db=db)
- if files==False:
- return False
- print(" * Entering File Picker")
- title = 'Select content to install or transfer: \n + Press space or right to select content \n + Press Enter to confirm selection \n + Press E to exit selection'
- filenames=[]
- for f in files:
- if types=='all':
- filenames.append(f[0])
- else:
- for x in ext:
- if (str(f[0]).lower()).endswith(x):
- filenames.append(f[0])
- break
- if filenames==[]:
- print(" * Request didn't retrieve any files")
- return False
- options=filenames
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection);picker.register_custom_handler(ord('E'), end_selection)
- selected=picker.start()
- if selected[0]==False:
- print(" User didn't select any files")
- return False
- with open(tfile,'a') as textfile:
- for f in selected:
- fpath=(files[f[1]])[2]+'/'+(files[f[1]])[0]
- TD=str(TeamDrive)
- try:
- ID=(files[f[1]])[4]
- textfile.write(f"{fpath}|{TD}|{ID}\n")
- except:
- textfile.write(f"{fpath}|{TD}\n")
\ No newline at end of file
diff --git a/py/lib/python_pick.py b/py/lib/python_pick.py
deleted file mode 100644
index 5135b8ef..00000000
--- a/py/lib/python_pick.py
+++ /dev/null
@@ -1,201 +0,0 @@
-#-*-coding:utf-8-*-
-'''
-Based on python-pick
-python-pick - Library Data:
-* Name: python-pick
-* Author: wong2
-* License: MIT -> https://github.com/wong2/pick/blob/master/LICENSE
-* Modifications by julesontheroad for:
-https://github.com/julesontheroad/NSC_BUILDER/
-Currently the only modification was to add intro (459) to the selector, more things may be added on the future.
-'''
-
-import curses
-
-__all__ = ['Picker', 'pick']
-
-
-KEYS_ENTER = (curses.KEY_ENTER, ord('\n'), ord('\r'),459)
-KEYS_UP = (curses.KEY_UP, ord('k'))
-KEYS_DOWN = (curses.KEY_DOWN, ord('j'))
-KEYS_SELECT = (curses.KEY_RIGHT, ord(' '))
-
-class Picker(object):
- """The :class:`Picker ` object
-
- :param options: a list of options to choose from
- :param title: (optional) a title above options list
- :param multi_select: (optional) if true its possible to select multiple values by hitting SPACE, defaults to False
- :param indicator: (optional) custom the selection indicator
- :param default_index: (optional) set this if the default selected option is not the first one
- :param options_map_func: (optional) a mapping function to pass each option through before displaying
- """
-
- def __init__(self, options, title=None, indicator='*', default_index=0, multi_select=False, min_selection_count=0, options_map_func=None):
-
- if len(options) == 0:
- raise ValueError('options should not be an empty list')
-
- self.options = options
- self.title = title
- self.indicator = indicator
- self.multi_select = multi_select
- self.min_selection_count = min_selection_count
- self.options_map_func = options_map_func
- self.all_selected = []
-
- if default_index >= len(options):
- raise ValueError('default_index should be less than the length of options')
-
- if multi_select and min_selection_count > len(options):
- raise ValueError('min_selection_count is bigger than the available options, you will not be able to make any selection')
-
- if options_map_func is not None and not callable(options_map_func):
- raise ValueError('options_map_func must be a callable function')
-
- self.index = default_index
- self.custom_handlers = {}
-
- def register_custom_handler(self, key, func):
- self.custom_handlers[key] = func
-
- def move_up(self):
- self.index -= 1
- if self.index < 0:
- self.index = len(self.options) - 1
-
- def move_down(self):
- self.index += 1
- if self.index >= len(self.options):
- self.index = 0
-
- def mark_index(self):
- if self.multi_select:
- if self.index in self.all_selected:
- self.all_selected.remove(self.index)
- else:
- self.all_selected.append(self.index)
-
- def get_selected(self):
- """return the current selected option as a tuple: (option, index)
- or as a list of tuples (in case multi_select==True)
- """
- if self.multi_select:
- return_tuples = []
- for selected in self.all_selected:
- return_tuples.append((self.options[selected], selected))
- return return_tuples
- else:
- return self.options[self.index], self.index
-
- def get_title_lines(self):
- if self.title:
- return self.title.split('\n') + ['']
- return []
-
- def get_option_lines(self):
- lines = []
- for index, option in enumerate(self.options):
- # pass the option through the options map of one was passed in
- if self.options_map_func:
- option = self.options_map_func(option)
-
- if index == self.index:
- prefix = self.indicator
- else:
- prefix = len(self.indicator) * ' '
-
- if self.multi_select and index in self.all_selected:
- format = curses.color_pair(1)
- line = ('{0} {1}'.format(prefix, option), format)
- else:
- line = '{0} {1}'.format(prefix, option)
- lines.append(line)
-
- return lines
-
- def get_lines(self):
- title_lines = self.get_title_lines()
- option_lines = self.get_option_lines()
- lines = title_lines + option_lines
- current_line = self.index + len(title_lines) + 1
- return lines, current_line
-
- def draw(self):
- """draw the curses ui on the screen, handle scroll if needed"""
- self.screen.clear()
-
- x, y = 1, 1 # start point
- max_y, max_x = self.screen.getmaxyx()
- max_rows = max_y - y # the max rows we can draw
-
- lines, current_line = self.get_lines()
-
- # calculate how many lines we should scroll, relative to the top
- scroll_top = getattr(self, 'scroll_top', 0)
- if current_line <= scroll_top:
- scroll_top = 0
- elif current_line - scroll_top > max_rows:
- scroll_top = current_line - max_rows
- self.scroll_top = scroll_top
-
- lines_to_draw = lines[scroll_top:scroll_top+max_rows]
-
- for line in lines_to_draw:
- if type(line) is tuple:
- self.screen.addnstr(y, x, line[0], max_x-2, line[1])
- else:
- self.screen.addnstr(y, x, line, max_x-2)
- y += 1
-
- self.screen.refresh()
-
- def run_loop(self):
- while True:
- self.draw()
- c = self.screen.getch()
- if c in KEYS_UP:
- self.move_up()
- elif c in KEYS_DOWN:
- self.move_down()
- elif c in KEYS_ENTER:
- if self.multi_select and len(self.all_selected) < self.min_selection_count:
- continue
- return self.get_selected()
- elif c in KEYS_SELECT and self.multi_select:
- self.mark_index()
- elif c in self.custom_handlers:
- ret = self.custom_handlers[c](self)
- if ret:
- return ret
-
- def config_curses(self):
- # use the default colors of the terminal
- curses.use_default_colors()
- # hide the cursor
- curses.curs_set(0)
- #add some color for multi_select
- #@todo make colors configurable
- curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_WHITE)
-
- def _start(self, screen):
- self.screen = screen
- self.config_curses()
- return self.run_loop()
-
- def start(self):
- return curses.wrapper(self._start)
-
-
-def pick(options, title=None, indicator='*', default_index=0, multi_select=False, min_selection_count=0, options_map_func=None):
- """Construct and start a :class:`Picker `.
-
- Usage::
-
- >>> from pick import pick
- >>> title = 'Please choose an option: '
- >>> options = ['option1', 'option2', 'option3']
- >>> option, index = pick(options, title)
- """
- picker = Picker(options, title, indicator, default_index, multi_select, min_selection_count, options_map_func)
- return picker.start()
diff --git a/py/lib/secondary.py b/py/lib/secondary.py
deleted file mode 100644
index 5115987e..00000000
--- a/py/lib/secondary.py
+++ /dev/null
@@ -1,612 +0,0 @@
-import subprocess
-import os
-import sys
-import argparse
-import listmanager
-import Print
-import inspect
-from tqdm import tqdm
-
-# SET ENVIRONMENT
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-
-testroute1=os.path.join(squirrel_dir, "squirrel.py")
-testroute2=os.path.join(squirrel_dir, "squirrel.exe")
-isExe=False
-if os.path.exists(testroute1):
- squirrel=testroute1
- isExe=False
-elif os.path.exists(testroute2):
- squirrel=testroute2
- isExe=True
-allowedlist=['--renamef','--addtodb','--addtodb_new','--verify','--compress']
-
-#print (squirrel)
-
-def call_library(args,xarg=None):
- vret=None
- try:
- if args[0]:
- components = args[0].split('.')
- if len(components)>1:
- vret=call_class(args,xarg)
- try:
- if str(args[2]).lower() == 'print' or str(args[3]).lower() == 'print':
- print(str(vret))
- else:
- print(str(vret))
- except:
- return vret
- else:
- library=args[0]
- lib=__import__(library)
- except:pass
-
- if len(args)>1:
- if xarg==None:
- try:
- if args[2]:
- var=args[2]
- try:
- var=var.split(',')
- for i in range(len(var)):
- if str(var[i]).lower()=='true':
- var[i]=True
- elif str(var[i]).lower()=='false':
- var[i]=False
- elif str(var[i]).lower()=='none':
- var[i]=None
- elif '=' in var[i]:
- try:
- asignation=var[i].split("=")
- if str(asignation[1]).lower()=='true':
- var[i]=True
- elif str(asignation[1]).lower()=='false':
- var[i]=False
- elif str(asignation[1]).lower()=='none':
- var[i]=None
- else:
- toks=list()
- toks=[pos for pos, char in enumerate(var[i]) if char == '=']
- indx=toks[0]+1
- var[i]=(var[i])[indx:]
- except:pass
- else:pass
- except:pass
- except:
- var=None
- else:
- var=xarg
- for i in range(len(var)):
- try:
- if str(var[i]).lower()=='true':
- var[i]=True
- elif str(var[i]).lower()=='false':
- var[i]=False
- elif str(var[i]).lower()=='none':
- var[i]=None
- elif '=' in var[i]:
- try:
- asignation=var[i].split("=")
- if str(asignation[1]).lower()=='true':
- var[i]=True
- elif str(asignation[1]).lower()=='false':
- var[i]=False
- elif str(asignation[1]).lower()=='none':
- var[i]=None
- else:
- toks=list()
- toks=[pos for pos, char in enumerate(var[i]) if char == '=']
- indx=toks[0]+1
- var[i]=(var[i])[indx:]
- except:pass
- else:pass
- except:pass
- try:
- if args[1]:
- fimport=args[1]
- if library=='Interface' and fimport=='start':
- try:
- if str(var[6]).lower()=='true':
- debug_write(True)
- except:
- debug_write(False)
- if library=='Interface' and fimport=='server':
- try:
- if str(var[4]).lower()=='true':
- debug_write(True)
- except:
- debug_write(False)
- function = getattr(__import__(library,fromlist=[fimport]), fimport)
- if var==None:
- vret=function()
- else:
- vret=function(*var)
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- try:
- if str(args[2]).lower() == 'print' or str(args[3]).lower() == 'print':
- print(str(vret))
- else:
- print(str(vret))
- except:
- return vret
-
-def call_class(args,xarg=None):
- vret=None
- try:
- if args[0]:
- components = args[0].split('.')
- library=components[0]
- lib = __import__(library)
- importedclass = getattr(lib, components[1])
- except:pass
-
- if len(args)>1:
- if xarg==None:
- try:
- if args[2]:
- var=args[2]
- try:
- var=var.split(',')
- for i in range(len(var)):
- if str(var[i]).lower()=='true':
- var[i]=True
- elif str(var[i]).lower()=='false':
- var[i]=False
- elif str(var[i]).lower()=='none':
- var[i]=None
- elif '=' in var[i]:
- try:
- asignation=var[i].split("=")
- if str(asignation[1]).lower()=='true':
- var[i]=True
- elif str(asignation[1]).lower()=='false':
- var[i]=False
- elif str(asignation[1]).lower()=='none':
- var[i]=None
- else:
- var[i]=asignation[1]
- except:pass
- else:pass
- except:pass
- except:
- var=None
- else:
- var=xarg
- for i in range(len(var)):
- try:
- if str(var[i]).lower()=='true':
- var[i]=True
- elif str(var[i]).lower()=='false':
- var[i]=False
- elif str(var[i]).lower()=='none':
- var[i]=None
- elif '=' in var[i]:
- try:
- asignation=var[i].split("=")
- if str(asignation[1]).lower()=='true':
- var[i]=True
- elif str(asignation[1]).lower()=='false':
- var[i]=False
- elif str(asignation[1]).lower()=='none':
- var[i]=None
- else:
- var[i]=asignation[1]
- except:pass
- else:pass
- except:pass
- try:
- if args[1]:
- fimport=args[1]
- function = getattr(importedclass, fimport)
- if var==None:
- vret=function()
- else:
- vret=function(*var)
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- try:
- if str(args[2]).lower() == 'print' or str(args[3]).lower() == 'print':
- print(str(vret))
- else:
- print(str(vret))
- except:
- return vret
-
-def debug_write(state):
- squirrel_dir=os.path.abspath(os.curdir)
- NSCB_dir=os.path.abspath('../'+(os.curdir))
- if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
- elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- web_folder=os.path.join(ztools_dir,'web')
- debug_folder=os.path.join(web_folder,'_debug_')
- flag_file=os.path.join(debug_folder,'flag')
- with open(flag_file,'wt') as tfile:
- tfile.write(str(state))
-
-def route(args,workers,silence=False):
- arguments,tfile=getargs(args)
- #print(arguments)
- # print(tfile)
- if tfile==False:
- if silence==False:
- process=subprocess.Popen(arguments)
- else:
- process=subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- while process.poll()==None and process2.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if process2.poll()!=None:
- process2.terminate();
- #op,oe=process.communicate();#print (op);print (oe)
- #process.terminate();process2.terminate()
- else:
- filelist=listmanager.read_lines_to_list(tfile,number=workers)
- commands=list();i=0
- #print(filelist)
- for allw in allowedlist:
- if allw in arguments:
- ind=arguments.index(allw)
- ind+=1
- break
- ind2=False
- try:
- ind2=arguments.index('--db_file')
- ind2+=1
- sub_r=arguments[ind2]
- except:pass
- process=list();sub_r=arguments[ind2];c=0
- if ind2 !=False:
- if not os.path.isdir(sub_r) and not str(sub_r).endswith('all_DB.txt'):
- folder=os.path.dirname(os.path.abspath(sub_r))
- ruta=os.path.abspath(os.path.join(folder, "temp"))
- else:
- folder=os.path.dirname(os.path.abspath(sub_r))
- ruta=os.path.abspath(os.path.join(folder, "temp"))
- if not os.path.exists(ruta):
- os.makedirs(ruta)
- for f in filelist:
- arguments[ind]=f
- #print (arguments)
- if ind2 !=False:
- if not os.path.isdir(sub_r) and not str(sub_r).endswith('all_DB.txt'):
- fi=str(os.path.basename(os.path.abspath(sub_r)))+'_'+str(c)
- ruta2=os.path.abspath(os.path.join(ruta,fi))
- arguments[ind2]=ruta2
- #print(ruta2)
- else:
- ruta2=os.path.abspath(os.path.join(ruta,str(c)))
- if not os.path.exists(ruta2):
- os.makedirs(ruta2)
- fi=os.path.join(ruta2,'all_DB.txt')
- arguments[ind2]=fi
- #print(arguments)
- c+=1
- if silence==False:
- process.append(subprocess.Popen(arguments))
- else:
- process.append(subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
- #print(process)
- #print(f)
- #print(len(process))
- for p in process:
- p.wait()
- # print(str(p.poll()))
- while p.poll()==None:
- if p.poll()!=None:
- p.terminate();
-
- if ind2 !=False:
- if not os.path.isdir(sub_r) and not str(sub_r).endswith('all_DB.txt'):
- for i in range(int(workers-1)):
- fi=str(os.path.basename(os.path.abspath(sub_r)))+'_'+str(i)
- t=os.path.join(ruta,fi)
- if os.path.exists(t):
- with open(t,"r+", encoding='utf8') as filelist:
- if not os.path.exists(sub_r):
- with open(sub_r,"w", encoding='utf8') as dbt:
- for line in filelist:
- dbt.write(line)
- else:
- c=0
- with open(sub_r,"a", encoding='utf8') as dbt:
- for line in filelist:
- if not c==0:
- dbt.write(line)
- c+=1
- i+=1
- try:
- os.remove(ruta)
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- pass
- else:
- include=['extended_DB.txt','nutdb_DB.txt','keyless_DB.txt','simple_DB.txt']
- for i in range(int(workers-1)):
- for input in include:
- ruta2=os.path.abspath(os.path.join(ruta,str(i)))
- t=os.path.join(ruta2,input)
- t2=os.path.join(folder,input)
- # print(t)
- # print(t2)
- if os.path.exists(t):
- with open(t,"r+", encoding='utf8') as filelist:
- if not os.path.exists(t2):
- with open(t2,"w", encoding='utf8') as dbt:
- for line in filelist:
- dbt.write(line)
- else:
- c=0
- with open(t2,"a", encoding='utf8') as dbt:
- for line in filelist:
- if not c==0:
- dbt.write(line)
- c+=1
- i+=1
- try:
- os.remove(t)
- except:pass
- try:
- os.remove(ruta2)
- except:pass
- try:
- os.remove(ruta)
- except:pass
-
- listmanager.striplines(tfile,number=workers,counter=True)
-
-
-def getargs(args,separate_list=True,current=False,pos=0,tothreads=1):
-
- tfile=False
- # args=str(args)
- # args=args.split(', ')
- arguments=list()
- if not isExe==True:
- arguments.append(sys.executable)
- arguments.append(squirrel)
-
- if args.compress!=None and current!=False:
- nargs=list()
- args.text_file=None
- f=None
- f=current
- nargs.append(f)
- nargs.append(args.compress[-1])
- args.compress=nargs
- args.position=str(pos)
- try:
- tothreads=int(tothreads)
- if tothreads>1:
- args.n_instances=str(tothreads)
- except:pass
-
- for a in vars(args):
- if not 'None' in str(a) and str(a) != 'file=[]' and not 'threads' in str(a) and not 'pararell' in str(a):
- # a=a.split('=')
- # print(a)
- # try:
- # b=a[1]
- # b=b[1:-1]
- # except:
- # b=None
- # pass
- b=getattr(args, a)
- if isinstance(b, list):
- c=0
- for x in b:
- if x=="" and c==0:
- b=""
- c+=1
- break
- # print(a)
- # if a == 'type':
- # print(b)
- if a=='text_file' and separate_list==True:
- tfile=b
- else:
- if b!=None:
- a='--'+a
- arguments.append(a)
- if isinstance(b, list):
- for x in b:
- if x!='':
- arguments.append(x)
- # narg=narg[:-1]
- # arguments.append(narg)
- # print(narg)
- else:
- arguments.append(b)
- return arguments,tfile
-
-def pass_command(args,silence=False):
- c=0
- if not args.findfile:
- items=listmanager.counter(args.text_file)
- process=list()
- while items!=0:
- if c==0:
- c+=1
- else:
- print("")
- listmanager.printcurrent(args.text_file)
- arguments,nonevar=getargs(args,separate_list=False)
- # print(arguments)
- if silence==False:
- process.append(subprocess.Popen(arguments))
- else:
- process.append(subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
- for p in process:
- p.wait()
- # print(str(p.poll()))
- while p.poll()==None:
- if p.poll()!=None:
- p.terminate();
- listmanager.striplines(args.text_file,number=1,counter=True)
- items-=1
- return items
- else:
- arguments,nonevar=getargs(args,separate_list=False)
- # print(arguments)
- process=list()
- if silence==False:
- process.append(subprocess.Popen(arguments))
- else:
- process.append(subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
- for p in process:
- p.wait()
- # print(str(p.poll()))
- while p.poll()==None:
- if p.poll()!=None:
- p.terminate();
- return 0
-
-def pararell(args,workers,silence=False,nocls=False):
- from subprocess import call
- from time import sleep
- c=0;workers=int(workers);tfile=args.text_file;args0=args;f=False
- filelist=listmanager.read_lines_to_list(tfile,all=True)
- if not args.findfile:
- items=listmanager.counter(args.text_file);index=0
- process=list()
- while items!=0:
- if c==0:
- c+=1
- else:
- #print("")
- pass
-
- from colorama import Fore
- colors=Fore.__dict__
- p=0;threads=0
- for r in range(workers):
- if index != items:
- k=0;l=p
- for col in colors:
- if l>len(colors):
- l=l-len(colors)
- color=colors[col]
- if k==(l+1):
- break
- else:
- k+=1
- #listmanager.printcurrent(tfile)
- try:
- f=filelist[index]
- except:break
- tq = tqdm(leave=False,position=0)
- #tq = tqdm(leave=False,position=0,bar_format="{l_bar}%s{bar}%s{r_bar}" % (color, color))
- tq.write('Opening thread for '+f)
- threads+=1
- tq.close()
- tq = tqdm(total=1, unit='|', leave=True,position=0,bar_format="{l_bar}%s{bar}%s{r_bar}" % (color, Fore.RESET))
- tq.update(1)
- tq.close()
- opworkers=workers
- if itemslen(colors):
- l=l-len(colors)
- color=colors[col]
- if k==(l+1):
- break
- else:
- k+=1
- #listmanager.printcurrent(tfile)
- try:
- f=filelist[index2]
- except:break
- if nocls==False:
- tq = tqdm(leave=False,position=0)
- # tq = tqdm(leave=False,position=0,bar_format="{l_bar}%s{bar}%s{r_bar}" % (color, color))
- tq.write('Opening thread for '+f)
- tq.close()
- tq = tqdm(total=1, unit='|', leave=True,position=0,bar_format="{l_bar}%s{bar}%s{r_bar}" % (color, Fore.RESET))
- tq.update(1)
- tq.close()
- index2+=1;p+=1
- if pr.poll()!=None:
- pr.terminate();
- pr_n+=1
- if nocls==False:
- clear_Screen()
- if nocls==False:
- listmanager.striplines(tfile,number=threads,counter=False)
- else:
- listmanager.striplines(tfile,number=threads,counter=True,jump_line=True)
- items-=threads
- if items<0:
- items=0
- return items
-
-
-def clear_Screen():
- from subprocess import call
- from time import sleep
- if os.name =='posix':
- call('clear')#linux
- else:
- try:
- call('cls')#macos
- except:
- print ("\n" * 100)
- os.system('cls')#windows
\ No newline at end of file
diff --git a/py/lib/sq_settings.py b/py/lib/sq_settings.py
index 45144796..7e67651a 100644
--- a/py/lib/sq_settings.py
+++ b/py/lib/sq_settings.py
@@ -9,4 +9,4 @@ def set_prod_environment():
try:
a=key_system
except:
- set_prod_environment()
\ No newline at end of file
+ set_prod_environment()
diff --git a/py/mtp/mtp_game_manager.py b/py/mtp/mtp_game_manager.py
deleted file mode 100644
index 174380c7..00000000
--- a/py/mtp/mtp_game_manager.py
+++ /dev/null
@@ -1,586 +0,0 @@
-import Print
-import os
-import shutil
-import json
-from Fs import Nsp as squirrelNSP
-from Fs import Xci as squirrelXCI
-import sq_tools
-
-import Keys
-import sys
-import subprocess
-from mtp.wpd import is_switch_connected
-import listmanager
-import csv
-import time
-from secondary import clear_Screen
-from python_pick import pick
-from python_pick import Picker
-
-def check_connection():
- if not is_switch_connected():
- sys.exit("Switch device isn't connected.\nCheck if mtp responder is running!!!")
-
-bucketsize = 81920
-
-# SET ENVIRONMENT
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-
-testroute1=os.path.join(squirrel_dir, "squirrel.py")
-testroute2=os.path.join(squirrel_dir, "squirrel.exe")
-urlconfig=os.path.join(zconfig_dir,'NUT_DB_URL.txt')
-isExe=False
-if os.path.exists(testroute1):
- squirrel=testroute1
- isExe=False
-elif os.path.exists(testroute2):
- squirrel=testroute2
- isExe=True
-bin_folder=os.path.join(ztools_dir, 'bin')
-nscb_mtp=os.path.join(bin_folder, 'nscb_mtp.exe')
-cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
-if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
-games_installed_cache=os.path.join(cachefolder, 'games_installed.txt')
-sd_xci_cache=os.path.join(cachefolder, 'sd_xci.txt')
-valid_saves_cache=os.path.join(cachefolder, 'valid_saves.txt')
-mtp_source_lib=os.path.join(zconfig_dir,'mtp_source_libraries.txt')
-mtp_internal_lib=os.path.join(zconfig_dir,'mtp_SD_libraries.txt')
-storage_info=os.path.join(cachefolder, 'storage.csv')
-download_lib_file = os.path.join(zconfig_dir, 'mtp_download_libraries.txt')
-sx_autoloader_db=os.path.join(zconfig_dir, 'sx_autoloader_db')
-xci_locations=os.path.join(zconfig_dir, 'mtp_xci_locations.txt')
-
-def libraries(tfile):
- db={}
- try:
- with open(tfile,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- else:
- dict_={}
- for j in range(len(csvheader)):
- try:
- if row[j]==None or row[j]=='':
- dict_[csvheader[j]]=None
- else:
- dict_[csvheader[j]]=row[j]
- except:
- dict_[csvheader[j]]=None
- db[row[0]]=dict_
- # print(db)
- return db
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return False
-
-def retrieve_installed():
- check_connection()
- try:
- for f in os.listdir(cachefolder):
- fp = os.path.join(cachefolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- except:pass
- print(" * Parsing games in device. Please Wait...")
- process=subprocess.Popen([nscb_mtp,"ShowInstalled","-tfile",games_installed_cache,"-show","false","-exci","true"],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(games_installed_cache):
- print(" Success")
-
-def retrieve_registered():
- check_connection()
- try:
- for f in os.listdir(cachefolder):
- fp = os.path.join(cachefolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- except:pass
- print("1. Retrieving registered...")
- dbicsv=os.path.join(cachefolder,"registered.csv")
- process=subprocess.Popen([nscb_mtp,"Download","-ori","4: Installed gamesInstalledApplications.csv","-dst",dbicsv],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(dbicsv):
- print(" Success")
- else:
- sys.exit("Couldn't retrieved Game Registry")
- dbi_dict={}
- with open(dbicsv,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter=',')
- id=0;ver=1;tname=2;
- for row in readCSV:
- try:
- tid=(str(row[id]).upper())[2:]
- version=int(row[ver])
- name=str(row[tname])
- dbi_dict[tid]=[tid,version,name]
- except:pass
- return dbi_dict
-
-def retrieve_xci_paths():
- check_connection()
- try:
- for f in os.listdir(cachefolder):
- fp = os.path.join(cachefolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- except:pass
- print(" * Parsing games in device. Please Wait...")
- process=subprocess.Popen([nscb_mtp,"Retrieve_XCI_paths","-tfile",sd_xci_cache,"-show","false","-exci","false","-xci_lc",xci_locations],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(games_installed_cache):
- print(" Success")
-
-def parsedinstalled():
- installed={}
- if os.path.exists(games_installed_cache):
- gamelist=listmanager.read_lines_to_list(games_installed_cache,all=True)
- for g in gamelist:
- try:
- entry=listmanager.parsetags(g)
- entry=list(entry)
- entry.append(g)
- installed[entry[0]]=entry
- except:pass
- return installed
-
-def get_gamelist(dosort=True,file=games_installed_cache):
- gamelist=[]
- if os.path.exists(file):
- gamelist=listmanager.read_lines_to_list(file,all=True)
- gamelist.sort()
- return gamelist
-
-def pick_download_folder():
- title = 'Select download folder: '
- db=libraries(download_lib_file)
- if db==False:
- return False,False
- options = [x for x in db.keys()]
- selected = pick(options, title,min_selection_count=1)
- path=(db[selected[0]])['path']
- return path
-
-def pick_transfer_folder():
- if not os.path.exists(mtp_internal_lib):
- return "SD"
- title = 'Select transfer folder: '
- db=libraries(mtp_internal_lib)
- if db==False:
- return "SD"
- options = [x for x in db.keys()]
- selected = pick(options, title,min_selection_count=1)
- path=(db[selected[0]])['path']
- return path
-
-def transfer(filepath=None,destiny="SD",):
- check_connection()
- if filepath=="":
- filepath=None
- if filepath==None:
- print("File input = null")
- return False
- print("- Retrieving Space on device")
- from mtp.mtpinstaller import get_storage_info
- SD_ds,SD_fs,NAND_ds,NAND_fs,FW,device=get_storage_info()
- print("- Calculating File size")
- file_size=os.path.getsize(filepath)
- print(f" * SD free space: {SD_fs} ({sq_tools.getSize(SD_fs)})")
- print(f" * File installed size: {file_size} ({sq_tools.getSize(file_size)})")
- if file_size>SD_fs:
- print(" Not enough space on SD. Changing target to EMMC")
- print(f" * EMMC free space: {NAND_fs} ({sq_tools.getSize(NAND_fs)})")
- sys.exit(" NOT ENOUGH SPACE SD STORAGE")
- if destiny=="SD":
- destiny="1: External SD Card/"
- basename=os.path.basename(filepath)
- destiny=os.path.join(destiny, basename)
- process=subprocess.Popen([nscb_mtp,"Transfer","-ori",filepath,"-dst",destiny])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
-
-def loop_transfer(tfile):
- check_connection()
- if not os.path.exists(tfile):
- sys.exit(f"Couldn't find {tfile}")
- destiny=pick_transfer_folder()
- file_list=listmanager.read_lines_to_list(tfile,all=True)
- for item in file_list:
- transfer(filepath=item,destiny=destiny)
- print("")
- listmanager.striplines(tfile,counter=True)
-
-def gen_sx_autoloader_sd_files():
- check_connection()
- retrieve_xci_paths()
- gamelist=get_gamelist(file=sd_xci_cache)
- SD_folder=os.path.join(sx_autoloader_db, 'sd')
- if not os.path.exists(sx_autoloader_db):
- os.makedirs(sx_autoloader_db)
- if not os.path.exists(SD_folder):
- os.makedirs(SD_folder)
- for f in os.listdir(SD_folder):
- fp = os.path.join(SD_folder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- print(' * Genereting autoloader files')
- for g in gamelist:
- try:
- fileid,fileversion,cctag,nG,nU,nD,baseid=listmanager.parsetags(g)
- tfile=os.path.join(SD_folder,fileid)
- new_path=g.replace('\\1: External SD Card\\','sdmc:/')
- new_path=new_path.replace('\\','/')
- with open(tfile,'w') as text_file:
- text_file.write(new_path)
- except:pass
- print(' * Pushing autoloader files')
- destiny="1: External SD Card\\sxos\\titles\\00FF0012656180FF\\cach\\sd"
- process=subprocess.Popen([nscb_mtp,"TransferFolder","-ori",SD_folder,"-dst",destiny,"-fbf","true"])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
-
-
-def dump_content():
- check_connection()
- retrieve_installed()
- installed=get_gamelist()
- print(" * Entering File Picker")
- title = 'Select content to dump: \n + Press space or right to select content \n + Press Enter or Intro to accept selection \n + Press E to exit selection'
- options=installed
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection);picker.register_custom_handler(ord('E'), end_selection)
- selected=picker.start()
- if selected[0]==False:
- print(" User didn't select any files")
- return False
- outfolder=pick_download_folder()
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- print(" * Starting dumping process...")
- counter=len(selected)
- for file in selected:
- outputfile=os.path.join(outfolder, file[0])
- process=subprocess.Popen([nscb_mtp,"Dump","-sch",file[0],"-dst",outputfile])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- counter-=1
- print('...................................................')
- print('STILL '+str(counter)+' FILES TO PROCESS')
- print('...................................................')
-
-def uninstall_content():
- check_connection()
- retrieve_installed()
- installed=get_gamelist()
- print(" * Entering File Picker")
- title = 'Select content to uninstall: \n + Press space or right to select content \n + Press E to finish selection'
- options=installed
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection);picker.register_custom_handler(ord('E'), end_selection)
- selected=picker.start()
- if selected[0]==False:
- print(" User didn't select any files")
- return False
- print(" * Starting uninstalling process...")
- counter=len(selected)
- for file in selected:
- process=subprocess.Popen([nscb_mtp,"DeleteID","-ID",file[0]])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- counter-=1
- print('...................................................')
- print('STILL '+str(counter)+' FILES TO PROCESS')
- print('...................................................')
-
-def delete_archived():
- check_connection()
- retrieve_installed()
- installed=get_gamelist()
- registered=retrieve_registered()
- for game in installed:
- try:
- fileid,fileversion,cctag,nG,nU,nD,baseid=listmanager.parsetags(game)
- try:
- del registered[fileid]
- except:pass
- except:pass
- games=[]
- # Name [version][title]
- for k in registered.keys():
- games.append(f"{(registered[k])[2]} [{(registered[k])[0]}][{(registered[k])[1]}]")
- print(" * Entering File Picker")
- if not games:
- sys.exit("There isn't any archived games or placeholder on the device")
- title = 'Select registries to delete: \n + Press space or right to select entries \n + Press E to finish selection \n + Press A to select all entries'
- options=games
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- def select_all(picker):
- return "ALL",-1
- picker.register_custom_handler(ord('e'), end_selection)
- picker.register_custom_handler(ord('E'), end_selection)
- picker.register_custom_handler(ord('a'), select_all)
- picker.register_custom_handler(ord('A'), select_all)
- selected=picker.start()
- if selected[0]==False:
- print(" User didn't select any files")
- return False
- print(" * Starting uninstalling process...")
- arch2delete=[]
- if selected[0]=="ALL":
- for k in registered:
- g0=(registered[k])[2]
- arch2delete.append(g0)
- else:
- for game in selected:
- g=game[0]
- g0=[pos for pos, char in enumerate(g) if char == '[']
- g0=(g[0:g0[0]]).strip()
- arch2delete.append(g0)
- counter=len(arch2delete)
- for file in arch2delete:
- process=subprocess.Popen([nscb_mtp,"DeleteRegistry","-ID",file])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- counter-=1
- print('...................................................')
- print('STILL '+str(counter)+' FILES TO PROCESS')
- print('...................................................')
-
-def back_up_saves(backup_all=False,inline=False,tidandver=True,romaji=True,outfolder=None,onlyInstalled=False):
- check_connection()
- import zipfile
- from datetime import datetime
- if outfolder=="":
- outfolder=None
- try:
- for f in os.listdir(cachefolder):
- fp = os.path.join(cachefolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- except:pass
- if outfolder==None:
- outfolder=pick_download_folder()
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- if onlyInstalled==True:
- process=subprocess.Popen([nscb_mtp,"ShowSaveGames","-saves",valid_saves_cache,"-show","False","-oinst","True"])
- else:
- process=subprocess.Popen([nscb_mtp,"ShowSaveGames","-saves",valid_saves_cache,"-show","False","-oinst","False"])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if not os.path.exists(valid_saves_cache):
- sys.exit("Savegames couldn't be retrieved")
- valid_saves=[];options=[]
- with open(valid_saves_cache,'rt',encoding='utf8') as tfile:
- for line in tfile:
- valid_saves.append(line.strip())
- if line.startswith("Installed games\\"):
- line=line.replace("Installed games\\","")
- options.append(line.strip())
- elif line.startswith("Uninstalled games\\"):
- line=line.replace("Uninstalled games\\","")
- options.append(line.strip())
- if backup_all==False:
- title = 'Select saves to backup: \n + Press space or right to select content \n + Press E to finish selection'
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- picker.register_custom_handler(ord('e'), end_selection);picker.register_custom_handler(ord('E'), end_selection)
- selected=picker.start()
- if selected[0]==False:
- print(" User didn't select any games")
- return False
- else:
- selected=[]
- for i in range(len(valid_saves)):
- selected.append([valid_saves[i],i])
- print("- Retrieving registered to get TitleIDs...")
- dbicsv=os.path.join(cachefolder,"registered.csv")
- process=subprocess.Popen([nscb_mtp,"Download","-ori","4: Installed gamesInstalledApplications.csv","-dst",dbicsv],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(dbicsv):
- print(" Success")
- if tidandver==True:
- dbi_dict={}
- with open(dbicsv,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter=',')
- id=0;ver=1;tname=2;
- for row in readCSV:
- version=0;
- try:
- tid=(str(row[id]).upper())[2:]
- tid=tid[:-3]+'000'
- version=int(row[ver])
- name=str(row[tname])
- if name in dbi_dict:
- if version<((dbi_dict[name])['version']):
- continue
- dbi_dict[name]={'tid':tid,'version':version,'name':name}
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return False
- counter=len(selected)
- for file in selected:
- if tidandver==True:
- print(f'- Searching "{file[0]}" in registered data')
- titleid="";version=""
- name=file[0]
- if name.startswith("Installed games\\"):
- name=name.replace("Installed games\\","")
- elif name.startswith("Uninstalled games\\"):
- name=name.replace("Uninstalled games\\","")
- try:
- titleid=((dbi_dict[name])['tid'])
- titleid=f'[{titleid}]'
- version=str((dbi_dict[file[0]])['version'])
- version=f'[v{version}]'
- except:
- pass
- game=f"{name} {titleid}{version}"
- else:
- name=file[0]
- if name.startswith("Installed games\\"):
- name=name.replace("Installed games\\","")
- elif name.startswith("Uninstalled games\\"):
- name=name.replace("Uninstalled games\\","")
- game=f"{name}"
- game=sanitize(game,romaji)
- if inline==False:
- dmpfolder=os.path.join(outfolder,game)
- else:
- dmpfolder=outfolder
- tmpfolder=os.path.join(dmpfolder,'tmp')
- if not os.path.exists(dmpfolder):
- os.makedirs(dmpfolder)
- if not os.path.exists(tmpfolder):
- os.makedirs(tmpfolder)
- process=subprocess.Popen([nscb_mtp,"BackSaves","-sch",file[0],"-dst",tmpfolder])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- counter-=1
- subfolders = [ f.path for f in os.scandir(tmpfolder) if f.is_dir() ]
- for folder in subfolders:
- dname=os.path.basename(folder)
- timedate = datetime.now()
- timedate = timedate.strftime("[%Y.%m.%d @ %H.%M.%S]")
- zipname=f"{game}{timedate}[{dname}].zip"
- output=os.path.join(dmpfolder,zipname)
- print(f"- Zipping {folder} to {output}")
- file_list=listmanager.folder_to_list(folder,extlist='all')
- if not file_list:
- continue
- z = zipfile.ZipFile(output, "w",zipfile.ZIP_DEFLATED)
- basedir=os.path.dirname(folder)
- for dirpath, dirnames, filenames in os.walk(folder):
- for filename in filenames:
- filePath = os.path.join(dirpath, filename)
- dirname = filePath.replace(folder,'')
- dirname=dirname[0:]
- if os.path.isfile(filePath):
- z.write(filePath, dirname)
- z.close()
- try:
- shutil.rmtree(tmpfolder, ignore_errors=True)
- except: pass
- print('...................................................')
- print('STILL '+str(counter)+' FILES TO PROCESS')
- print('...................................................')
-
-def sanitize(filename,roma=True):
- import re
- filename = (re.sub(r'[\/\\\:\*\?]+', '', filename))
- filename = re.sub(r'[™©®`~^´ªº¢#£€¥$ƒ±¬½¼♡«»±•²‰œæƳ☆<<>>|]', '', filename)
- filename = re.sub(r'[Ⅰ]', 'I', filename);filename = re.sub(r'[Ⅱ]', 'II', filename)
- filename = re.sub(r'[Ⅲ]', 'III', filename);filename = re.sub(r'[Ⅳ]', 'IV', filename)
- filename = re.sub(r'[Ⅴ]', 'V', filename);filename = re.sub(r'[Ⅵ]', 'VI', filename)
- filename = re.sub(r'[Ⅶ]', 'VII', filename);filename = re.sub(r'[Ⅷ]', 'VIII', filename)
- filename = re.sub(r'[Ⅸ]', 'IX', filename);filename = re.sub(r'[Ⅹ]', 'X', filename)
- filename = re.sub(r'[Ⅺ]', 'XI', filename);filename = re.sub(r'[Ⅻ]', 'XII', filename)
- filename = re.sub(r'[Ⅼ]', 'L', filename);filename = re.sub(r'[Ⅽ]', 'C', filename)
- filename = re.sub(r'[Ⅾ]', 'D', filename);filename = re.sub(r'[Ⅿ]', 'M', filename)
- filename = re.sub(r'[—]', '-', filename);filename = re.sub(r'[√]', 'Root', filename)
- filename = re.sub(r'[àâá@äå]', 'a', filename);filename = re.sub(r'[ÀÂÁÄÅ]', 'A', filename)
- filename = re.sub(r'[èêéë]', 'e', filename);filename = re.sub(r'[ÈÊÉË]', 'E', filename)
- filename = re.sub(r'[ìîíï]', 'i', filename);filename = re.sub(r'[ÌÎÍÏ]', 'I', filename)
- filename = re.sub(r'[òôóöø]', 'o', filename);filename = re.sub(r'[ÒÔÓÖØ]', 'O', filename)
- filename = re.sub(r'[ùûúü]', 'u', filename);filename = re.sub(r'[ÙÛÚÜ]', 'U', filename)
- filename = re.sub(r'[’]', "'", filename);filename = re.sub(r'[“”]', '"', filename)
- filename = re.sub(' {3,}', ' ',filename);re.sub(' {2,}', ' ',filename);
- filename = filename.replace("( ", "(");filename = filename.replace(" )", ")")
- filename = filename.replace("[ ", "[");filename = filename.replace(" ]", "]")
- filename = filename.replace("[ (", "[(");filename = filename.replace(") ]", ")]")
- filename = filename.replace("[]", "");filename = filename.replace("()", "")
- filename = filename.replace('" ','"');filename = filename.replace(' "','"')
- filename = filename.replace(" !", "!");filename = filename.replace(" ?", "?")
- filename = filename.replace(" ", " ");filename = filename.replace(" ", " ")
- filename = filename.replace('"', '');
- filename = filename.replace(')', ') ');filename = filename.replace(']', '] ')
- filename = filename.replace("[ (", "[(");filename = filename.replace(") ]", ")]")
- filename = filename.replace(" ", " ")
- if roma==True:
- converter = kakashi_conv()
- filename=converter.do(filename)
- filename.strip()
- return filename
-
-def kakashi_conv():
- import pykakasi
- kakasi = pykakasi.kakasi()
- kakasi.setMode("H", "a")
- kakasi.setMode("K", "a")
- kakasi.setMode("J", "a")
- kakasi.setMode("s", True)
- kakasi.setMode("E", "a")
- kakasi.setMode("a", None)
- kakasi.setMode("C", False)
- converter = kakasi.getConverter()
- return converter
diff --git a/py/mtp/mtp_gdrive.py b/py/mtp/mtp_gdrive.py
deleted file mode 100644
index ba88e77c..00000000
--- a/py/mtp/mtp_gdrive.py
+++ /dev/null
@@ -1,1012 +0,0 @@
-import Print
-import os
-import shutil
-import sq_tools
-import io
-import sys
-import subprocess
-import listmanager
-import csv
-from Drive import Private as DrivePrivate
-from Drive import Public as DrivePublic
-from Drive import DriveTools
-import requests
-from workers import concurrent_scrapper
-from mtpinstaller import get_storage_info
-try:
- import ujson as json
-except:
- import json
-
-def check_connection():
- from mtp.wpd import is_switch_connected
- if not is_switch_connected():
- sys.exit("Switch device isn't connected.\nCheck if mtp responder is running!!!")
-
-bucketsize = 81920
-
-# SET ENVIRONMENT
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-
-testroute1=os.path.join(squirrel_dir, "squirrel.py")
-testroute2=os.path.join(squirrel_dir, "squirrel.exe")
-urlconfig=os.path.join(zconfig_dir,'NUT_DB_URL.txt')
-isExe=False
-if os.path.exists(testroute1):
- squirrel=testroute1
- isExe=False
-elif os.path.exists(testroute2):
- squirrel=testroute2
- isExe=True
-bin_folder=os.path.join(ztools_dir, 'bin')
-nscb_mtp=os.path.join(bin_folder, 'nscb_mtp.exe')
-cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
-if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
-games_installed_cache=os.path.join(cachefolder, 'games_installed.txt')
-mtp_source_lib=os.path.join(zconfig_dir,'mtp_source_libraries.txt')
-mtp_internal_lib=os.path.join(zconfig_dir,'mtp_SD_libraries.txt')
-storage_info=os.path.join(cachefolder, 'storage.csv')
-download_lib_file = os.path.join(zconfig_dir, 'mtp_download_libraries.txt')
-remote_lib_file = os.path.join(zconfig_dir, 'remote_libraries.txt')
-cache_lib_file= os.path.join(zconfig_dir, 'remote_cache_location.txt')
-_1fichier_token=os.path.join((os.path.join(zconfig_dir, 'credentials')),'_1fichier_token.tk')
-remote_lib_cache=os.path.join(zconfig_dir, 'remote_lib_cache')
-xci_locations=os.path.join(zconfig_dir, 'mtp_xci_locations.txt')
-
-def libraries(tfile):
- db={}
- try:
- with open(tfile,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- else:
- dict_={}
- for j in range(len(csvheader)):
- try:
- if row[j]==None or row[j]=='':
- dict_[csvheader[j]]=None
- else:
- dict_[csvheader[j]]=row[j]
- except:
- dict_[csvheader[j]]=None
- db[row[0]]=dict_
- return db
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return False
-
-def get_cache_lib():
- db=libraries(cache_lib_file)
- TD=None;lib=None;path="null";libpath=None
- for entry in db:
- path=db[entry]['path']
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- if TD=='':
- TD=None
- return lib,TD,libpath
-
-def addtodrive(filename,truecopy=True):
- if os.path.exists(cache_lib_file):
- lib,TD,libpath=get_cache_lib()
- if lib!=None:
- file_id, is_download_link=DrivePublic.parse_url(filename)
- if is_download_link:
- remote=DrivePrivate.location(route=libpath,TD_Name=TD)
- result=remote.drive_service.files().get(fileId=file_id, fields="name,mimeType").execute()
- name=result['name']
- testpath=('{}/{}').format(libpath,name)
- remote=DrivePrivate.location(route=testpath,TD_Name=TD)
- if remote.name==None:
- name=DrivePrivate.add_to_drive(url=filename,filepath=libpath,makecopy=truecopy,TD=TD)
- filename=('{}/{}').format(libpath,name)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- else:
- filename=testpath
- globalremote=remote
- return filename
-
-def loop_install(tfile,destiny="SD",outfolder=None,ch_medium=True,check_fw=True,patch_keygen=False,ch_base=False,ch_other=False,truecopy=True,checked=False):
- check_connection()
- if not os.path.exists(tfile):
- sys.exit(f"Couldn't find {tfile}")
- from mtpinstaller import retrieve_installed,parsedinstalled
- installed=[]
- if ch_base==True or ch_other==True:
- if checked==False:
- print("Content check activated")
- retrieve_installed()
- installed=parsedinstalled()
- elif checked==True:
- print("Content check activated. Games are preparsed")
- installed=parsedinstalled()
- file_list=listmanager.read_lines_to_list(tfile,all=True)
- for item in file_list:
- if item.startswith('https://1fichier.com'):
- print("Item is 1fichier link. Redirecting...")
- fichier_install(item,destiny,ch_medium,ch_base=ch_base,ch_other=ch_other,installed_list=installed)
- elif item.startswith('https://drive.google.com'):
- print("Item is google drive public link. Redirecting...")
- public_gdrive_install(item,destiny,outfolder=outfolder,ch_medium=ch_medium,check_fw=check_fw,patch_keygen=patch_keygen,ch_base=ch_base,ch_other=ch_other,checked=checked,truecopy=truecopy,installed_list=installed)
- elif os.path.exists(item):
- print("Item is a local link. Skipping...")
- else:
- try:
- test=item.split('|')
- if len(test)<2:
- item=test[0]
- lib,TD,libpath=get_library_from_path(remote_lib_file,item)
- if lib!=None:
- print("Item is a remote library link. Redirecting...")
- gdrive_install(item,destiny,outfolder=outfolder,ch_medium=ch_medium,check_fw=check_fw,patch_keygen=patch_keygen,ch_base=ch_base,ch_other=ch_other,checked=checked,installed_list=installed)
- else:
- print("Couldn't find file. Skipping...")
- else:
- gdrive_install(item,destiny,outfolder=outfolder,ch_medium=ch_medium,check_fw=check_fw,patch_keygen=patch_keygen,ch_base=ch_base,ch_other=ch_other,checked=checked,installed_list=installed)
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- print(f"Couldn't find {test[0]}. Skipping...")
- print("")
- listmanager.striplines(tfile,1,True)
-
-def get_library_from_path(tfile=None,filename=None):
- if tfile==None:
- db=libraries(remote_lib_file)
- else:
- db=libraries(tfile)
- TD=None;lib=None;path="null"
- for entry in db:
- path=db[entry]['path']
- if filename.startswith(path):
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- else:
- pass
- if lib==None:
- db=libraries(cache_lib_file)
- TD=None;lib=None;path="null"
- for entry in db:
- path=db[entry]['path']
- if filename.startswith(path):
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- else:
- pass
- if TD=='':
- TD=None
- return lib,TD,libpath
-
-def gdrive_install(filename,destiny="SD",outfolder=None,ch_medium=True,check_fw=True,patch_keygen=False,ch_base=False,ch_other=False,checked=False,installed_list=False):
- check_connection();ID=None;gvID=None
- test=filename.split('|')
- if len(test)<2:
- filename=test[0]
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- elif len(test)<3:
- filename=test[0]
- TD=test[1]
- if str(TD).upper()=="NONE":
- TD=None
- else:
- filename=test[0]
- TD=test[1]
- if str(TD).upper()=="NONE":
- TD=None
- ID=test[2]
- if str(ID).upper()=="NONE":
- ID=None
- gvID=ID
- if ID==None:
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- else:
- try:
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False,ID=ID)
- if ID==None:
- try:
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- if ID==None:
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- except:
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- except:
- try:
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- if ID==None:
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- except:
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- if ID==None and gvID!=None:
- remote.ID=gvID
- # header=DrivePrivate.get_html_header(remote.access_token)
- 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 filesizeNAND_fs:
- if filesizeint(FW_kg):
- kgwarning=True
- tgkg=int(FW_kg)
- else:
- tgkg=keygeneration
- else:
- tgkg=keygeneration
- except:
- print("Error getting cnmtdata from file")
- 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
- 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 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 filesizeNAND_fs:
- if filesizeint(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 fichier_install(url,destiny="SD",ch_medium=True,ch_base=False,ch_other=False,installed_list=False):
- check_connection()
- if not os.path.exists(_1fichier_token):
- sys.exit("No 1fichier token setup")
- with open(_1fichier_token,'rt',encoding='utf8') as tfile:
- token=(tfile.readline().strip())
- if token==None:
- sys.exit("Missing 1fichier token")
- APIkey=token
- auth={'Authorization':f'Bearer {APIkey}','Content-Type':'application/json'}
- session = requests.session()
- download_params = {
- 'url' : url,
- 'inline' : 0,
- 'cdn' : 0,
- 'restrict_ip': 0,
- 'no_ssl' : 0,
- }
- info_params={
- 'url' : url
- }
- r=session.post('https://api.1fichier.com/v1/file/info.cgi',json=info_params,headers=auth)
- info_dict=r.json()
- # print(info_dict)
- sz=info_dict['size']
- name=info_dict['filename']
- r=session.post('https://api.1fichier.com/v1/download/get_token.cgi',json=download_params,headers=auth)
- dict_=r.json()
- # print(dict_)
- ext=name.split('.')
- ext=ext[-1]
- if not name.endswith('nsp') and not name.endswith('nsz'):
- sys.exit(f"Extension not supported for direct instalation {ext} in {name}")
- if not dict_['status']=="OK":
- sys.exit(f"API call returned {dict_['status']}")
- URL=dict_['url']
- 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 filesizeNAND_fs:
- if filesize((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
- process=subprocess.Popen([nscb_mtp,"fichierInstall","-ori",URL,"-dst",destiny,"-name",name,"-size",str(sz)])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
-
-def gdrive_transfer(filename,destiny="SD"):
- check_connection()
- if destiny=="SD":
- destiny="1: External SD Card/"
- lib,TD,libpath=get_library_from_path(remote_lib_file,filename)
- ID,name,type,size,md5,remote=DrivePrivate.get_Data(filename,TD=TD,Print=False)
- # header=DrivePrivate.get_html_header(remote.access_token)
- 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]
- file_size=int(sz)
- print("- Retrieving Space on device")
- SD_ds,SD_fs,NAND_ds,NAND_fs,FW,device=get_storage_info()
- print(f" * SD free space: {SD_fs} ({sq_tools.getSize(SD_fs)})")
- print(f" * File installed size: {file_size} ({sq_tools.getSize(file_size)})")
- if file_size>SD_fs:
- print(" Not enough space on SD. Changing target to EMMC")
- print(f" * EMMC free space: {NAND_fs} ({sq_tools.getSize(NAND_fs)})")
- sys.exit(" NOT ENOUGH SPACE SD STORAGE")
- process=subprocess.Popen([nscb_mtp,"DriveTransfer","-ori",URL,"-dst",destiny,"-name",name,"-size",sz,"-tk",token])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
-
-def public_gdrive_transfer(filepath,destiny="SD",truecopy=True):
- check_connection()
- lib,TD,libpath=get_cache_lib()
- if lib==None:
- sys.exit(f"Google Drive Public Links are only supported via cache folder")
- if destiny=="SD":
- destiny="1: External SD Card/"
- 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]
- file_size=int(sz)
- print("- Retrieving Space on device")
- SD_ds,SD_fs,NAND_ds,NAND_fs,FW,device=get_storage_info()
- print(f" * SD free space: {SD_fs} ({sq_tools.getSize(SD_fs)})")
- print(f" * File installed size: {file_size} ({sq_tools.getSize(file_size)})")
- if file_size>SD_fs:
- print(" Not enough space on SD. Changing target to EMMC")
- print(f" * EMMC free space: {NAND_fs} ({sq_tools.getSize(NAND_fs)})")
- sys.exit(" NOT ENOUGH SPACE SD STORAGE")
- process=subprocess.Popen([nscb_mtp,"DriveTransfer","-ori",URL,"-dst",destiny,"-name",name,"-size",sz,"-tk",token])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
-
-def fichier_transfer(url,destiny="SD"):
- check_connection()
- if not os.path.exists(_1fichier_token):
- sys.exit("No 1fichier token setup")
- with open(_1fichier_token,'rt',encoding='utf8') as tfile:
- token=(tfile.readline().strip())
- if token==None:
- sys.exit("Missing 1fichier token")
- APIkey=token
- auth={'Authorization':f'Bearer {APIkey}','Content-Type':'application/json'}
- session = requests.session()
- download_params = {
- 'url' : url,
- 'inline' : 0,
- 'cdn' : 0,
- 'restrict_ip': 0,
- 'no_ssl' : 0,
- }
- info_params={
- 'url' : url
- }
- r=session.post('https://api.1fichier.com/v1/file/info.cgi',json=info_params,headers=auth)
- info_dict=r.json()
- # print(info_dict)
- sz=info_dict['size']
- name=info_dict['filename']
- r=session.post('https://api.1fichier.com/v1/download/get_token.cgi',json=download_params,headers=auth)
- dict_=r.json()
- # print(dict_)
- ext=name.split('.')
- ext=ext[-1]
- if not dict_['status']=="OK":
- sys.exit(f"API call returned {dict_['status']}")
- URL=dict_['url']
- print("- Retrieving Space on device")
- SD_ds,SD_fs,NAND_ds,NAND_fs,FW,device=get_storage_info()
- print("- Calculating File size")
- file_size=int(sz)
- print(f" * SD free space: {SD_fs} ({sq_tools.getSize(SD_fs)})")
- print(f" * File installed size: {file_size} ({sq_tools.getSize(file_size)})")
- if file_size>SD_fs:
- print(" Not enough space on SD. Changing target to EMMC")
- print(f" * EMMC free space: {NAND_fs} ({sq_tools.getSize(NAND_fs)})")
- sys.exit(" NOT ENOUGH SPACE SD STORAGE")
- process=subprocess.Popen([nscb_mtp,"fichierTransfer","-ori",URL,"-dst",destiny,"-name",name,"-size",str(sz)])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
-
-def loop_transfer(tfile):
- check_connection()
- if not os.path.exists(tfile):
- sys.exit(f"Couldn't find {tfile}")
- from mtp_game_manager import pick_transfer_folder
- destiny=pick_transfer_folder()
- file_list=listmanager.read_lines_to_list(tfile,all=True)
- for item in file_list:
- if item.startswith('https://1fichier.com'):
- print("Item is 1fichier link. Redirecting...")
- fichier_transfer(item,destiny)
- elif item.startswith('https://drive.google.com'):
- print("Item is google drive public link. Redirecting...")
- public_gdrive_transfer(item,destiny)
- elif os.path.exists(item):
- print("Item is a local link. Skipping...")
- else:
- test=item.split('|')
- item=test[0]
- lib,TD,libpath=get_library_from_path(remote_lib_file,item)
- if lib!=None:
- print("Item is a remote library link. Redirecting...")
- gdrive_transfer(item,destiny)
- print("")
-
-def get_libs_remote_source(lib=remote_lib_file):
- libraries={}
- libtfile=lib
- with open(libtfile,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0;up=False;tdn=False;
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- if 'library_name' and 'path' and 'TD_name' and 'Update' in csvheader:
- lb=csvheader.index('library_name')
- pth=csvheader.index('path')
- tdn=csvheader.index('TD_name')
- up=csvheader.index('Update')
- else:
- if 'library_name' and 'path' and 'TD_name' in csvheader:
- lb=csvheader.index('library_name')
- tdn=csvheader.index('TD_name')
- pth=csvheader.index('path')
- else:break
- else:
- try:
- update=False
- library=str(row[lb])
- route=str(row[pth])
- if tdn!=False:
- try:
- TD=str(row[tdn])
- if TD=='':
- TD=None
- except:
- TD=None
- else:
- TD=None
- if up!=False:
- try:
- update=str(row[up])
- if update.upper()=="TRUE":
- update=True
- else:
- update=False
- except:
- update=True
- else:
- update=False
- libraries[library]=[route,TD,update]
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- pass
- if not libraries:
- return False
- return libraries
-
-def update_console_from_gd(libraries="all",destiny="SD",exclude_xci=True,prioritize_nsz=True,tfile=None,verification=True,ch_medium=True,ch_other=False,autoupd_aut=True,use_archived=False):
- check_connection()
- if use_archived==True:
- autoupd_aut=False
- if tfile==None:
- tfile=os.path.join(NSCB_dir, 'MTP1.txt')
- if os.path.exists(tfile):
- try:
- os.remove(tfile)
- except: pass
- libdict=get_libs_remote_source(remote_lib_file);
- if libdict==False:
- sys.exit("No libraries set up")
- pths={};TDs={};
- if libraries=="all":
- for entry in libdict.keys():
- pths[entry]=((libdict[entry])[0])
- TDs[entry]=((libdict[entry])[1])
- else:
- for entry in libdict.keys():
- if (libdict[entry])[2]==True:
- pths[entry]=((libdict[entry])[0])
- TDs[entry]=((libdict[entry])[1])
- # print(pths);print(TDs);
- if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
- for f in os.listdir(cachefolder):
- fp = os.path.join(cachefolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- if use_archived!=True:
- print("1. Parsing games in device. Please Wait...")
- if exclude_xci==True:
- process=subprocess.Popen([nscb_mtp,"ShowInstalled","-tfile",games_installed_cache,"-show","false","-exci","true"],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- else:
- process=subprocess.Popen([nscb_mtp,"ShowInstalled","-tfile",games_installed_cache,"-show","false","-exci","false","-xci_lc",xci_locations],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(games_installed_cache):
- print(" Success")
- gamelist=listmanager.read_lines_to_list(games_installed_cache,all=True)
- installed={}
- for g in gamelist:
- try:
- if exclude_xci==True:
- if g.endswith('xci') or g.endswith('xc0'):
- continue
- entry=listmanager.parsetags(g)
- entry=list(entry)
- entry.append(g)
- installed[entry[0]]=entry
- except:pass
- # for i in pths:
- # print(i)
- else:
- print("1. Retrieving registered...")
- dbicsv=os.path.join(cachefolder,"registered.csv")
- process=subprocess.Popen([nscb_mtp,"Download","-ori","4: Installed games\\InstalledApplications.csv","-dst",dbicsv],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(dbicsv):
- print(" Success")
- installed={}
- with open(dbicsv,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter=',')
- id=0;ver=1;tname=2;
- for row in readCSV:
- try:
- tid=(str(row[id]).upper())[2:]
- version=int(row[ver])
- if version>0 and tid.endswith('000'):
- tid=tid[:-3]+'800'
- name=str(row[tname])
- g=f"{name} [{tid}][v{version}].nsp"
- entry=listmanager.parsetags(g)
- entry=list(entry)
- entry.append(g)
- if entry[0] in installed.keys():
- if int((intalled[entry[0]])[1])int(v):
- remotegames[entry[0]]=entry
- except:pass
- print("3. Searching new updates. Please Wait...")
- gamestosend={}
- for g in installed.keys():
- if g.endswith('000') or g.endswith('800'):
- try:
- updid=g[:-3]+'800'
- if updid in remotegames:
- if updid in installed:
- if ((installed[updid])[1])<((remotegames[updid])[1]):
- if not updid in gamestosend:
- gamestosend[updid]=remotegames[updid]
- else:
- if ((gamestosend[updid])[1])<((remotegames[updid])[1]):
- gamestosend[updid]=remotegames[updid]
- else:
- if not updid in gamestosend:
- gamestosend[updid]=remotegames[updid]
- else:
- if ((gamestosend[updid])[1])<((remotegames[updid])[1]):
- gamestosend[updid]=remotegames[updid]
- except:pass
- else:
- try:
- if g in remotegames:
- if ((installed[g])[1])<((remotegames[g])[1]):
- if not g in gamestosend:
- gamestosend[g]=remotegames[g]
- else:
- if ((gamestosend[g])[1])<((remotegames[g])[1]):
- gamestosend[g]=remotegames[g]
- except:pass
- print("4. Searching new dlcs. Please Wait...")
- for g in installed.keys():
- try:
- if g.endswith('000') or g.endswith('800'):
- baseid=g[:-3]+'000'
- else:
- baseid=(installed[g])[6]
- for k in remotegames.keys():
- try:
- if not (k.endswith('000') or k.endswith('800')) and not k in installed:
- test=get_dlc_baseid(k)
- if baseid ==test:
- if not k in gamestosend:
- gamestosend[k]=remotegames[k]
- else:
- if ((gamestosend[k])[1])<((remotegames[k])[1]):
- gamestosend[k]=remotegames[k]
- except BaseException as e:
- # Print.error('Exception: ' + str(e))
- pass
- except BaseException as e:
- # Print.error('Exception: ' + str(e))
- pass
- print("5. List of content that will get installed...")
- gamepaths=[]
- if len(gamestosend.keys())>0:
- if autoupd_aut==True:
- for i in sorted(gamestosend.keys()):
- fileid,fileversion,cctag,nG,nU,nD,baseid,path=gamestosend[i]
- bname=os.path.basename(path)
- gamepaths.append(path)
- g0=[pos for pos, char in enumerate(bname) if char == '[']
- g0=(bname[0:g0[0]]).strip()
- print(f" * {g0} [{fileid}][{fileversion}] [{cctag}] - {(bname[-3:]).upper()}")
- else:
- options=[]
- for i in sorted(gamestosend.keys()):
- fileid,fileversion,cctag,nG,nU,nD,baseid,path=gamestosend[i]
- bname=os.path.basename(path)
- gamepaths.append(path)
- g0=[pos for pos, char in enumerate(bname) if char == '[']
- g0=(bname[0:g0[0]]).strip()
- cstring=f"{g0} [{fileid}][{fileversion}] [{cctag}] - {(bname[-3:]).upper()}"
- options.append(cstring)
- if options:
- from python_pick import Picker
- title = 'Select content to install: \n + Press space or right to select entries \n + Press Enter to confirm selection \n + Press E to exit selection \n + Press A to select all entries'
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- def select_all(picker):
- return "ALL",-1
- picker.register_custom_handler(ord('e'), end_selection)
- picker.register_custom_handler(ord('E'), end_selection)
- picker.register_custom_handler(ord('a'), select_all)
- picker.register_custom_handler(ord('A'), select_all)
- selected=picker.start()
- if selected[0]==False:
- print(" User didn't select any files")
- return False
- if selected[0]=="ALL":
- pass
- else:
- newgpaths=[]
- for game in selected:
- g=game[1]
- g0=gamepaths[g]
- newgpaths.append(g0)
- gamepaths=newgpaths
- print("6. Generating text file...")
- with open(tfile,'w', encoding='utf8') as textfile:
- wpath=''
- for i in gamepaths:
- location=None
- for f in files:
- TD=None;ID=None
- if f[0]==i:
- location=f[2]
- try:
- ID=f[4]
- except:pass
- break
- if location==None:
- print(f"Can't find location for {i}")
- continue
- wpath=f"{location}/{i}"
- lib,TD,libpath=get_library_from_path(filename=wpath)
- if ID==None:
- textfile.write(f"{(wpath).strip()}|{TD}\n")
- else:
- textfile.write(f"{(wpath).strip()}|{TD}|{ID}\n")
- print("7. Triggering installer on loop mode.")
- print(" Note:If you interrupt the list use normal install mode to continue list")
- loop_install(tfile,destiny=destiny,outfolder=None,ch_medium=ch_medium,check_fw=True,patch_keygen=False,ch_base=False,ch_other=False,checked=True)
- else:
- print("\n --- DEVICE IS UP TO DATE ---")
\ No newline at end of file
diff --git a/py/mtp/mtp_tools.py b/py/mtp/mtp_tools.py
deleted file mode 100644
index 0496f5ae..00000000
--- a/py/mtp/mtp_tools.py
+++ /dev/null
@@ -1,235 +0,0 @@
-import os
-from listmanager import folder_to_list
-from listmanager import parsetags
-from pathlib import Path
-import Print
-import shutil
-from mtp.wpd import is_switch_connected
-import sys
-import subprocess
-from python_pick import pick
-from python_pick import Picker
-
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-
-testroute1=os.path.join(squirrel_dir, "squirrel.py")
-testroute2=os.path.join(squirrel_dir, "squirrel.exe")
-urlconfig=os.path.join(zconfig_dir,'NUT_DB_URL.txt')
-isExe=False
-if os.path.exists(testroute1):
- squirrel=testroute1
- isExe=False
-elif os.path.exists(testroute2):
- squirrel=testroute2
- isExe=True
-bin_folder=os.path.join(ztools_dir, 'bin')
-nscb_mtp=os.path.join(bin_folder, 'nscb_mtp.exe')
-cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
-if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
-games_installed_cache=os.path.join(cachefolder, 'games_installed.txt')
-autoloader_files_cache=os.path.join(cachefolder, 'autoloader_files.txt')
-sd_xci_cache=os.path.join(cachefolder, 'sd_xci.txt')
-valid_saves_cache=os.path.join(cachefolder, 'valid_saves.txt')
-mtp_source_lib=os.path.join(zconfig_dir,'mtp_source_libraries.txt')
-mtp_internal_lib=os.path.join(zconfig_dir,'mtp_SD_libraries.txt')
-storage_info=os.path.join(cachefolder, 'storage.csv')
-download_lib_file = os.path.join(zconfig_dir, 'mtp_download_libraries.txt')
-sx_autoloader_db=os.path.join(zconfig_dir, 'sx_autoloader_db')
-
-def gen_sx_autoloader_files_menu():
- print('***********************************************')
- print('SX AUTOLOADER GENERATE FILES FROM HDD OR FOLDER')
- print('***********************************************')
- print('')
- folder=input("Input a drive path: ")
- if not os.path.exists(folder):
- sys.exit("Can't find location")
- title = 'Target for autoloader files: '
- options = ['HDD','SD']
- selected = pick(options, title, min_selection_count=1)
- if selected[0]=='HDD':
- type='hdd'
- else:
- type='sd'
- title = 'Push files after generation?: '
- options = ['YES','NO']
- selected = pick(options, title, min_selection_count=1)
- if selected[0]=='YES':
- push=True
- else:
- push=False
- title = "Ensure files can't colide after transfer?: "
- options = ['YES','NO']
- selected = pick(options, title, min_selection_count=1)
- if selected[0]=='YES':
- no_colide=True
- else:
- no_colide=False
- gen_sx_autoloader_files(folder,type=type,push=push,no_colide=no_colide)
-
-def gen_sx_autoloader_files(folder,type='hdd',push=False,no_colide=False):
- gamelist=folder_to_list(folder,['xci','xc0'])
- if type=='hdd':
- SD_folder=os.path.join(sx_autoloader_db, 'hdd')
- else:
- SD_folder=os.path.join(sx_autoloader_db, 'sd')
- if not os.path.exists(sx_autoloader_db):
- os.makedirs(sx_autoloader_db)
- if not os.path.exists(SD_folder):
- os.makedirs(SD_folder)
- for f in os.listdir(SD_folder):
- fp = os.path.join(SD_folder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- print(' * Generating autoloader files')
- try:
- for g in gamelist:
- try:
- fileid,fileversion,cctag,nG,nU,nD,baseid=parsetags(g)
- if fileid=='unknown':
- continue
- tfile=os.path.join(SD_folder,fileid)
- fileparts=Path(g).parts
- if type=='hdd':
- new_path=g.replace(fileparts[0],'"usbhdd:/')
- else:
- new_path=g.replace(fileparts[0],'"sdmc:/')
- new_path=new_path.replace('\\','/')
- with open(tfile,'w') as text_file:
- text_file.write(new_path)
- except:pass
- print(' DONE')
- if push==True:
- if not is_switch_connected():
- sys.exit("Can't push files. Switch device isn't connected.\nCheck if mtp responder is running!!!")
- print(' * Pushing autoloader files')
- if type=='hdd':
- destiny="1: External SD Card\\sxos\\titles\\00FF0012656180FF\\cach\\hdd"
- else:
- destiny="1: External SD Card\\sxos\\titles\\00FF0012656180FF\\cach\\sd"
- process=subprocess.Popen([nscb_mtp,"TransferFolder","-ori",SD_folder,"-dst",destiny,"-fbf","true"])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if no_colide==True:
- cleanup_sx_autoloader_files()
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- pass
-
-def cleanup_sx_autoloader_files():
- from mtp_game_manager import retrieve_xci_paths
- from mtp_game_manager import get_gamelist
- try:
- for f in os.listdir(cachefolder):
- fp = os.path.join(cachefolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- except:pass
- if not is_switch_connected():
- sys.exit("Can't push files. Switch device isn't connected.\nCheck if mtp responder is running!!!")
- retrieve_xci_paths()
- print(" * Retriving autoloader files in device. Please Wait...")
- process=subprocess.Popen([nscb_mtp,"Retrieve_autoloader_files","-tfile",autoloader_files_cache,"-show","false"],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(autoloader_files_cache):
- print(" Success")
- else:
- sys.exit("Autoloader files weren't retrieved properly")
- gamelist=get_gamelist(file=sd_xci_cache)
- autoloader_list=get_gamelist(file=autoloader_files_cache)
- sd_xci_ids=[]
- for g in gamelist:
- try:
- fileid,fileversion,cctag,nG,nU,nD,baseid=parsetags(g)
- sd_xci_ids.append(fileid)
- except:pass
- files_to_remove=[]
- for f in autoloader_list:
- fileparts=Path(f).parts
- if 'sdd' in fileparts and not (fileparts[-1] in sd_xci_ids):
- files_to_remove.append(f)
- elif 'hdd' in fileparts and (fileparts[-1] in sd_xci_ids):
- files_to_remove.append(f)
- print(" * The following files will be removed")
- for f in files_to_remove:
- print(" - "+f)
- for f in files_to_remove:
- process=subprocess.Popen([nscb_mtp,"DeleteFile","-fp",f])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
-
-def push_sx_autoloader_libraries():
- if not is_switch_connected():
- sys.exit("Can't push files. Switch device isn't connected.\nCheck if mtp responder is running!!!")
- title = "Ensure files can't colide after transfer?: "
- options = ['YES','NO']
- selected = pick(options, title, min_selection_count=1)
- if selected[0]=='YES':
- no_colide=True
- else:
- no_colide=False
- print(' * Pushing autoloader files in hdd folder')
- HDD_folder=os.path.join(sx_autoloader_db, 'hdd')
- destiny="1: External SD Card\\sxos\\titles\\00FF0012656180FF\\cach\\hdd"
- process=subprocess.Popen([nscb_mtp,"TransferFolder","-ori",HDD_folder,"-dst",destiny,"-fbf","true"])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- print(' * Pushing autoloader files in SD folder')
- SD_folder=os.path.join(sx_autoloader_db, 'sd')
- destiny="1: External SD Card\\sxos\\titles\\00FF0012656180FF\\cach\\sd"
- process=subprocess.Popen([nscb_mtp,"TransferFolder","-ori",SD_folder,"-dst",destiny,"-fbf","true"])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if no_colide==True:
- cleanup_sx_autoloader_files()
-
-def get_nca_ticket(filepath,nca):
- import Fs
- from binascii import hexlify as hx, unhexlify as uhx
- if filepath.endswith('xci') or filepath.endswith('xcz'):
- f = Fs.Xci(filepath)
- check=False;titleKey=0
- for nspF in f.hfs0:
- if str(nspF._path)=="secure":
- for file in nspF:
- if (file._path).endswith('.tik'):
- titleKey = file.getTitleKeyBlock().to_bytes(16, byteorder='big')
- check=f.verify_key(nca,str(file._path))
- if check==True:
- break
- return check,titleKey
- elif filepath.endswith('nsp') or filepath.endswith('nsz'):
- f = Fs.Nsp(filepath)
- check=False;titleKey=0
- for file in f:
- if (file._path).endswith('.tik'):
- titleKey = file.getTitleKeyBlock().to_bytes(16, byteorder='big')
- check=f.verify_key(nca,str(file._path))
- if check==True:
- break
- return check,titleKey
\ No newline at end of file
diff --git a/py/mtp/mtpinstaller.py b/py/mtp/mtpinstaller.py
deleted file mode 100644
index b67f9e7c..00000000
--- a/py/mtp/mtpinstaller.py
+++ /dev/null
@@ -1,1148 +0,0 @@
-import Print
-import os
-import shutil
-import json
-from Fs import Nsp as squirrelNSP
-from Fs import Xci as squirrelXCI
-import sq_tools
-from Fs import factory
-from binascii import hexlify as hx, unhexlify as uhx
-import sys
-import subprocess
-from mtp.wpd import is_switch_connected
-import listmanager
-import csv
-import copy
-from colorama import Fore, Back, Style
-from python_pick import pick
-from python_pick import Picker
-from secondary import clear_Screen
-
-def check_connection():
- if not is_switch_connected():
- sys.exit("Switch device isn't connected.\nCheck if mtp responder is running!!!")
-
-bucketsize = 81920
-
-# SET ENVIRONMENT
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-
-testroute1=os.path.join(squirrel_dir, "squirrel.py")
-testroute2=os.path.join(squirrel_dir, "squirrel.exe")
-urlconfig=os.path.join(zconfig_dir,'NUT_DB_URL.txt')
-isExe=False
-if os.path.exists(testroute1):
- squirrel=testroute1
- isExe=False
-elif os.path.exists(testroute2):
- squirrel=testroute2
- isExe=True
-bin_folder=os.path.join(ztools_dir, 'bin')
-nscb_mtp=os.path.join(bin_folder, 'nscb_mtp.exe')
-cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
-if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
-games_installed_cache=os.path.join(cachefolder, 'games_installed.txt')
-mtp_source_lib=os.path.join(zconfig_dir,'mtp_source_libraries.txt')
-mtp_internal_lib=os.path.join(zconfig_dir,'mtp_SD_libraries.txt')
-storage_info=os.path.join(cachefolder, 'storage.csv')
-xci_locations=os.path.join(zconfig_dir, 'mtp_xci_locations.txt')
-
-def About():
- print(' __ _ __ __ ')
- print(' ____ _____ ____ / /_ __ __(_) /___/ /__ _____ ')
- print(' / __ \/ ___/ ___/ / __ \/ / / / / / __ / _ \/ ___/ ')
- print(' / / / (__ ) /__ / /_/ / /_/ / / / /_/ / __/ / ')
- print(' /_/ /_/____/\___/____/_.___/\__,_/_/_/\__,_/\___/_/ ')
- print(' /_____/ ')
- print('------------------------------------------------------------------------------------- ')
- print(' NINTENDO SWITCH CLEANER AND BUILDER ')
- print('------------------------------------------------------------------------------------- ')
- print('============================= BY JULESONTHEROAD ============================= ')
- print('------------------------------------------------------------------------------------- ')
- print('" POWERED BY SQUIRREL " ')
- print('" BASED ON THE WORK OF BLAWAR AND LUCA FRAGA " ')
- print('------------------------------------------------------------------------------------- ')
- print("Program's github: https://github.com/julesontheroad/NSC_BUILDER ")
- print('Cheats and Eshop information from nutdb and http://tinfoil.io ')
- print('------------------------------------------------------------------------------------- ')
-
-#One CJK character is 2 English characters wide when printed
-def display_len(s:str):
- cnt=0
- for c in s:
- if len(c.encode('utf-8'))>=3: # CJK character take >=3 bytes in utf-8
- cnt+=2
- else:
- cnt+=1
- return cnt
-
-def pretty_str(name:str,name_length=40):
- g0=copy.copy(name)
- if display_len(g0)>name_length+3:
- cnt=0
- short_g0=''
- for i in range(len(g0)):
- if cnt+display_len(g0[i])<=name_length:
- cnt+=display_len(g0[i])
- short_g0+=g0[i]
- else:
- break
- g0=short_g0
- if cnt FILE WAS MODIFIED BUT ORIGIN IS CONFIRMED AS LEGIT\n')
- else:
- print('\nFILE VERIFICATION CORRECT. \n -> FILE WAS MODIFIED AND CNMT PATCHED\n')
- else:
- print("\nFILE VERIFICATION CORRECT. FILE IS SAFE.\n")
- if verdict == False:
- print("\nFILE VERIFICATION INCORRECT. \n -> UNCONFIRMED ORIGIN FILE HAS BEEN TAMPERED WITH\n")
- if hash==True and verdict==True:
- if filename.endswith('.nsp') or filename.endswith('.nsx') or filename.endswith('.xci'):
- verdict,feed=f.verify_hash_nca(65536,headerlist,verdict,feed)
- elif filename.endswith('.nsz'):
- verdict,feed=f.nsz_hasher(65536,headerlist,verdict,feed)
- elif filename.endswith('.xcz'):
- verdict,feed=f.xcz_hasher(65536,headerlist,verdict,feed)
- f.flush()
- f.close()
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return verdict,isrestored,cnmt_is_patched
-
-def install(filepath=None,destiny="SD",verification=True,outfolder=None,ch_medium=True,check_fw=True,patch_keygen=False,install_mode="spec1",st_crypto=False):
- print(check_fw)
- check_connection()
- if install_mode!="legacy":
- from mtpnsp import install_nsp_csv
- kgwarning=False;dopatch=False;keygeneration=0;tgkg=0
- if filepath=="":
- filepath=None
- if filepath==None:
- print("File input = null")
- return False
- if verification==True or str(verification).upper()=="HASH":
- if str(verification).upper()=="HASH":
- verdict,isrestored,cnmt_is_patched=file_verification(filepath,hash=True)
- else:
- verdict,isrestored,cnmt_is_patched=file_verification(filepath)
- if verdict==False:
- print("File didn't pass verification. Skipping...")
- return False
- print("- Retrieving Space on device")
- SD_ds,SD_fs,NAND_ds,NAND_fs,FW,device=get_storage_info()
- print("- Calculating Installed size")
- dbDict=get_DB_dict(filepath)
- installedsize=dbDict['InstalledSize']
- if destiny=="SD":
- print(f" * SD free space: {SD_fs} ({sq_tools.getSize(SD_fs)})")
- print(f" * File installed size: {installedsize} ({sq_tools.getSize(installedsize)})")
- if installedsize>SD_fs:
- if installedsizeNAND_fs:
- if installedsizeint(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 and patch_keygen==False:
- print("File requires a higher firmware. Skipping...")
- return False
- elif kgwarning==True and patch_keygen==True:
- print("File requires a higher firmware. It'll will be prepatch")
- dopatch=True
- if (filepath.endswith('nsp') or filepath.endswith('nsz')) and st_crypto==True:
- if install_mode=="legacy":
- install_converted(filepath=filepath,outfolder=outfolder,destiny=destiny,kgpatch=dopatch,tgkg=tgkg)
- else:
- if dopatch==False:
- tgkg=False
- install_nsp_csv(filepath,destiny=destiny,cachefolder=outfolder,keypatch=tgkg)
- return
- if (filepath.endswith('nsp') or filepath.endswith('nsz')) and dopatch==True:
- if install_mode=="legacy":
- install_converted(filepath=filepath,outfolder=outfolder,destiny=destiny,kgpatch=dopatch,tgkg=tgkg)
- else:
- install_nsp_csv(filepath,destiny=destiny,cachefolder=outfolder,keypatch=tgkg)
- return
- if filepath.endswith('xci') or filepath.endswith('xcz'):
- if install_mode=="legacy":
- install_converted(filepath=filepath,outfolder=outfolder,destiny=destiny,kgpatch=dopatch,tgkg=tgkg)
- else:
- from mtpxci import install_xci_csv
- if dopatch==False:
- tgkg=False
- install_xci_csv(filepath,destiny=destiny,cachefolder=outfolder,keypatch=tgkg)
- return
- process=subprocess.Popen([nscb_mtp,"Install","-ori",filepath,"-dst",destiny])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
-
-def install_conv_st1(filepath,outfolder,keypatch='false'):
- tname=str(os.path.basename(filepath))[:-3]+'nsp'
- tmpfile=os.path.join(outfolder,tname)
- if filepath.endswith('xci') or filepath.endswith('xcz'):
- f = factory(filepath)
- f.open(filepath, 'rb')
- elif filepath.endswith('nsp') or filepath.endswith('nsz'):
- f = squirrelNSP(filepath, 'rb')
- f.c_nsp_direct(65536,tmpfile,outfolder,keypatch=keypatch)
- f.flush()
- f.close()
-
-def install_converted(filepath=None,outfolder=None,destiny="SD",kgpatch=False,tgkg=0):
- check_connection()
- if filepath=="":
- filepath=None
- if outfolder=="":
- filepath=None
- if outfolder==None:
- outfolder=cachefolder
- if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
- if kgpatch==False:
- keypatch='false'
- else:
- keypatch=int(tgkg)
- if filepath==None:
- print("File input = null")
- return False
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- for f in os.listdir(outfolder):
- fp = os.path.join(outfolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- tname=str(os.path.basename(filepath))[:-3]+'nsp'
- tmpfile=os.path.join(outfolder,tname)
- if isExe==False:
- process0=subprocess.Popen([sys.executable,squirrel,"-lib_call","mtp.mtpinstaller","install_conv_st1","-xarg",filepath,outfolder,keypatch])
- else:
- process0=subprocess.Popen([squirrel,"-lib_call","mtp.mtpinstaller","install_conv_st1","-xarg",filepath,outfolder,keypatch])
- while process0.poll()==None:
- if process0.poll()!=None:
- process0.terminate();
- process=subprocess.Popen([nscb_mtp,"Install","-ori",tmpfile,"-dst",destiny])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- try:
- for f in os.listdir(outfolder):
- fp = os.path.join(outfolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- except:pass
-
-def loop_install(tfile,destiny="SD",verification=True,outfolder=None,ch_medium=True,check_fw=True,patch_keygen=False,ch_base=False,ch_other=False,install_mode="spec1",st_crypto=False,checked=False):
- check_connection()
- if not os.path.exists(tfile):
- sys.exit(f"Couldn't find {tfile}")
- if ch_base==True or ch_other==True:
- if checked==False:
- print("Content check activated")
- retrieve_installed()
- installed=parsedinstalled()
- elif checked==True:
- print("Content check activated. Games are preparsed")
- installed=parsedinstalled()
- file_list=listmanager.read_lines_to_list(tfile,all=True)
- for item in file_list:
- try:
- if ch_base==True or ch_other==True:
- fileid,fileversion,cctag,nG,nU,nD,baseid=listmanager.parsetags(item)
- if fileid.endswith('000') and fileversion==0 and fileid in installed.keys() and ch_base==True:
- print("Base game already installed. Skipping...")
- listmanager.striplines(tfile,counter=True)
- continue
- elif fileid.endswith('000') and fileid in installed.keys() and ch_other==True:
- updid=fileid[:-3]+'800'
- if fileversion>((installed[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)
- continue
- elif ch_other==True and fileid in installed.keys():
- if fileversion>((installed[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)
- continue
- except:pass
- install(filepath=item,destiny=destiny,verification=verification,outfolder=outfolder,ch_medium=ch_medium,check_fw=check_fw,patch_keygen=patch_keygen,install_mode=install_mode,st_crypto=st_crypto)
- print("")
- listmanager.striplines(tfile,counter=True)
-
-def retrieve_installed():
- check_connection()
- print(" * Parsing games in device. Please Wait...")
- process=subprocess.Popen([nscb_mtp,"ShowInstalled","-tfile",games_installed_cache,"-show","false","-exci","true"],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(games_installed_cache):
- print(" Success")
-
-def parsedinstalled(exclude_xci=True):
- installed={}
- if os.path.exists(games_installed_cache):
- gamelist=listmanager.read_lines_to_list(games_installed_cache,all=True)
- for g in gamelist:
- try:
- if exclude_xci==True:
- if g.endswith('xci') or g.endswith('xc0'):
- continue
- entry=listmanager.parsetags(g)
- entry=list(entry)
- entry.append(g)
- installed[entry[0]]=entry
- except:pass
- return installed
-
-
-def get_installed_info(tfile=None,search_new=True,excludehb=True,exclude_xci=False):
- check_connection()
- if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
- forecombo=Style.BRIGHT+Back.GREEN+Fore.WHITE
- if tfile=="":
- tfile=None
- if os.path.exists(games_installed_cache):
- try:
- os.remove(games_installed_cache)
- except:pass
- if tfile==None:
- for f in os.listdir(cachefolder):
- fp = os.path.join(cachefolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- if exclude_xci==True:
- process=subprocess.Popen([nscb_mtp,"ShowInstalled","-tfile",games_installed_cache,"-show","false","-exci","true"],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- else:
- process=subprocess.Popen([nscb_mtp,"ShowInstalled","-tfile",games_installed_cache,"-show","false","-exci","false","-xci_lc",xci_locations],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- print("Parsing games in device. Please Wait...")
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(games_installed_cache):
- gamelist=listmanager.read_lines_to_list(games_installed_cache,all=True)
- gamelist.sort()
- print("..........................................................")
- print("CONTENT FOUND ON DEVICE")
- print("..........................................................")
- installed={}
- for g in gamelist:
- try:
- fileid,fileversion,cctag,nG,nU,nD,baseid=listmanager.parsetags(g)
- g0=[pos for pos, char in enumerate(g) if char == '[']
- g0=(g[0:g0[0]]).strip()
- installed[fileid]=[fileid,fileversion,cctag,nG,nU,nD,baseid,g0,g]
- g0=pretty_str(g0)
- verprint=str(fileversion)
- if len(verprint)<9:
- verprint=verprint+((9-len(verprint))*' ')
- if excludehb==True:
- if not fileid.startswith('05') and not fileid.startswith('04') and not str(fileid).lower()=='unknown':
- if g.endswith('.xci') or g.endswith('.xc0'):
- print(f"{g0} |{fileid}|{verprint}|XCI|{nG}G|{nU}U|{nD}D")
- else:
- print(f"{g0} |{fileid}|{verprint}|{cctag}")
- else:
- if g.endswith('.xci') or g.endswith('.xc0'):
- print(f"{g0} |{fileid}|{verprint}|XCI|{nG}G|{nU}U|{nD}D")
- else:
- print(f"{g0} |{fileid}|{verprint}|{cctag}")
- except:pass
- if search_new==True:
- import nutdb
- nutdb.check_other_file(urlconfig,'versions_txt')
- f='nutdb_'+'versions'+'.txt'
- DATABASE_folder=nutdb.get_DBfolder()
- _dbfile_=os.path.join(DATABASE_folder,f)
- versiondict={}
- with open(_dbfile_,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- if 'id' and 'version' in csvheader:
- id=csvheader.index('id')
- ver=csvheader.index('version')
- else:break
- else:
- try:
- tid=str(row[id]).upper()
- version=str(row[ver]).upper()
- if tid.endswith('800'):
- keyid=tid[:-3]+'000'
- else:
- keyid=tid
- if keyid in versiondict.keys():
- v=versiondict[keyid]
- if vint(fileversion):
- if fileid.endswith('000') or fileid.endswith('800'):
- updid=fileid[:-3]+'800'
- print(f"{g0} [{baseid}][{verprint}]{fillver} -> "+forecombo+ f"[{updid}] [v{v}]"+Style.RESET_ALL)
- else:
- print(f"{g0} [{fileid}][{verprint}]{fillver} -> "+forecombo+ f"[{fileid}] [v{v}]"+Style.RESET_ALL)
- check_xcis=False;xci_dlcs={}
- print("..........................................................")
- print("NEW DLCS")
- print("..........................................................")
- for k in versiondict.keys():
- if k in installed.keys() or k.endswith('000') or k.endswith('800'):
- continue
- else:
- try:
- baseid=get_dlc_baseid(k)
- except:
- baseid=k
- updid=baseid[:-3]+'800'
- if baseid in installed.keys() or updid in installed.keys():
- fileid,fileversion,cctag,nG,nU,nD,baseid,g0,g=installed[baseid]
- if nD>0:
- if check_xcis==False:
- check_xcis=True
- if not baseid in xci_dlcs.keys():
- entry=list()
- entry.append(k)
- xci_dlcs[baseid]=entry
- else:
- entry=xci_dlcs[baseid]
- entry.append(k)
- xci_dlcs[baseid]=entry
- continue
- g0=pretty_str(g0)
- print(f"{g0} [{baseid}] -> "+forecombo+ f"[{k}] [v{versiondict[k]}]"+Style.RESET_ALL)
- t=0
- if check_xcis==True:
- for bid in xci_dlcs.keys():
- fileid,fileversion,cctag,nG,nU,nD,baseid,g0,g=installed[bid]
- entry=xci_dlcs[bid]
- test=len(entry)
- if test>nD:
- if t==0:
- print("..........................................................")
- print("XCI MAY HAVE NEW DLCS. LISTING AVAILABLE")
- print("..........................................................")
- t+=1
- g0=pretty_str(g0)
- for k in xci_dlcs[baseid]:
- print(f"{g0} [{baseid}] -> "+forecombo+ f"[{k}] [v{versiondict[k]}]"+Style.RESET_ALL)
-
-
-def get_archived_info(search_new=True,excludehb=True,exclude_xci=False):
- check_connection()
- forecombo=Style.BRIGHT+Back.GREEN+Fore.WHITE
- if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
- for f in os.listdir(cachefolder):
- fp = os.path.join(cachefolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- print("1. Retrieving registered...")
- dbicsv=os.path.join(cachefolder,"registered.csv")
- process=subprocess.Popen([nscb_mtp,"Download","-ori","4: Installed games\\InstalledApplications.csv","-dst",dbicsv],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(dbicsv):
- print(" Success")
- print("2. Checking Installed...")
- if exclude_xci==True:
- process=subprocess.Popen([nscb_mtp,"ShowInstalled","-tfile",games_installed_cache,"-show","false","-exci","true"],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- else:
- process=subprocess.Popen([nscb_mtp,"ShowInstalled","-tfile",games_installed_cache,"-show","false","-exci","false","-xci_lc",xci_locations],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(games_installed_cache):
- print(" Success")
- gamelist=listmanager.read_lines_to_list(games_installed_cache,all=True)
- dbi_dict={}
- with open(dbicsv,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter=',')
- id=0;ver=1;tname=2;
- for row in readCSV:
- try:
- tid=(str(row[id]).upper())[2:]
- version=int(row[ver])
- name=str(row[tname])
- dbi_dict[tid]=[tid,version,name]
- except:pass
- installed={}
- for g in gamelist:
- entry=listmanager.parsetags(g)
- installed[entry[0]]=entry
- print("..........................................................")
- print("ARCHIVED|REGISTERED GAMES")
- print("..........................................................")
- for g in dbi_dict.keys():
- if not g in installed.keys():
- tid,version,g0=dbi_dict[g]
- g0=pretty_str(g0)
- print(f"{g0} [{tid}][{version}]")
- if search_new==True:
- import nutdb
- nutdb.check_other_file(urlconfig,'versions_txt')
- f='nutdb_'+'versions'+'.txt'
- DATABASE_folder=nutdb.get_DBfolder()
- _dbfile_=os.path.join(DATABASE_folder,f)
- versiondict={}
- with open(_dbfile_,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- if 'id' and 'version' in csvheader:
- id=csvheader.index('id')
- ver=csvheader.index('version')
- else:break
- else:
- try:
- tid=str(row[id]).upper()
- version=str(row[ver]).upper()
- if tid.endswith('800'):
- keyid=tid[:-3]+'000'
- else:
- keyid=tid
- if keyid in versiondict.keys():
- v=versiondict[keyid]
- if vint(fileversion):
- if fileid.endswith('000') or fileid.endswith('800'):
- baseid=fileid[:-3]+'000'
- updid=fileid[:-3]+'800'
- print(f"{g0} [{baseid}][{verprint}]{fillver} -> "+forecombo+ f"[{updid}] [v{v}]"+Style.RESET_ALL)
- else:
- print(f"{g0} [{fileid}][{verprint}]{fillver} -> "+forecombo+ f"[{fileid}] [v{v}]"+Style.RESET_ALL)
- print("..........................................................")
- print("NEW DLCS")
- print("..........................................................")
- for k in versiondict.keys():
- if k in dbi_dict.keys() or k.endswith('000') or k.endswith('800'):
- continue
- else:
- try:
- baseid=get_dlc_baseid(k)
- except:
- baseid=k
- updid=baseid[:-3]+'800'
- if baseid in dbi_dict.keys() or updid in dbi_dict.keys():
- fileid,fileversion,g0=dbi_dict[baseid]
- g0=pretty_str(g0)
- print(f"{g0} [{baseid}] -> "+forecombo+ f"[{k}] [v{versiondict[k]}]"+Style.RESET_ALL)
-
-
-def update_console(libraries="all",destiny="SD",exclude_xci=True,prioritize_nsz=True,tfile=None,verification=True,ch_medium=True,ch_other=False,autoupd_aut=True,use_archived=False):
- check_connection()
- if use_archived==True:
- autoupd_aut=False
- if tfile==None:
- tfile=os.path.join(NSCB_dir, 'MTP1.txt')
- if os.path.exists(tfile):
- try:
- os.remove(tfile)
- except: pass
- libdict=get_libs("source");
- pths={}
- if libraries=="all":
- for entry in libdict.keys():
- pths[entry]=((libdict[entry])[0])
- else:
- for entry in libdict.keys():
- if (libdict[entry])[1]==True:
- pths[entry]=((libdict[entry])[0])
- if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
- for f in os.listdir(cachefolder):
- fp = os.path.join(cachefolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- if use_archived!=True:
- print("1. Parsing games in device. Please Wait...")
- if exclude_xci==True:
- process=subprocess.Popen([nscb_mtp,"ShowInstalled","-tfile",games_installed_cache,"-show","false","-exci","true"],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- else:
- process=subprocess.Popen([nscb_mtp,"ShowInstalled","-tfile",games_installed_cache,"-show","false","-exci","false","-xci_lc",xci_locations],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(games_installed_cache):
- print(" Success")
- gamelist=listmanager.read_lines_to_list(games_installed_cache,all=True)
- installed={}
- for g in gamelist:
- try:
- if exclude_xci==True:
- if g.endswith('xci') or g.endswith('xc0'):
- continue
- entry=listmanager.parsetags(g)
- entry=list(entry)
- entry.append(g)
- installed[entry[0]]=entry
- except:pass
- else:
- print("1. Retrieving registered...")
- dbicsv=os.path.join(cachefolder,"registered.csv")
- process=subprocess.Popen([nscb_mtp,"Download","-ori","4: Installed games\\InstalledApplications.csv","-dst",dbicsv],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(dbicsv):
- print(" Success")
- installed={}
- with open(dbicsv,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter=',')
- id=0;ver=1;tname=2;
- for row in readCSV:
- try:
- tid=(str(row[id]).upper())[2:]
- version=int(row[ver])
- if version>0 and tid.endswith('000'):
- tid=tid[:-3]+'800'
- name=str(row[tname])
- g=f"{name} [{tid}][v{version}].nsp"
- entry=listmanager.parsetags(g)
- entry=list(entry)
- entry.append(g)
- if entry[0] in installed.keys():
- if int((intalled[entry[0]])[1])int(v):
- localgames[entry[0]]=entry
- except:pass
- print("3. Searching new updates. Please Wait...")
- gamestosend={}
- for g in installed.keys():
- if g.endswith('000') or g.endswith('800'):
- try:
- updid=g[:-3]+'800'
- if updid in localgames:
- if updid in installed:
- if int((installed[updid])[1])0:
- if autoupd_aut==True:
- for i in sorted(gamestosend.keys()):
- fileid,fileversion,cctag,nG,nU,nD,baseid,path=gamestosend[i]
- bname=os.path.basename(path)
- gamepaths.append(path)
- g0=[pos for pos, char in enumerate(bname) if char == '[']
- g0=(bname[0:g0[0]]).strip()
- print(f" * {g0} [{fileid}][{fileversion}] [{cctag}] - {(bname[-3:]).upper()}")
- else:
- options=[]
- for i in sorted(gamestosend.keys()):
- fileid,fileversion,cctag,nG,nU,nD,baseid,path=gamestosend[i]
- bname=os.path.basename(path)
- gamepaths.append(path)
- g0=[pos for pos, char in enumerate(bname) if char == '[']
- g0=(bname[0:g0[0]]).strip()
- cstring=f"{g0} [{fileid}][{fileversion}] [{cctag}] - {(bname[-3:]).upper()}"
- options.append(cstring)
- if options:
- title = 'Select content to install: \n + Press space or right to select entries \n + Press E to finish selection \n + Press A to select all entries'
- picker = Picker(options, title, multi_select=True, min_selection_count=1)
- def end_selection(picker):
- return False,-1
- def select_all(picker):
- return "ALL",-1
- picker.register_custom_handler(ord('e'), end_selection)
- picker.register_custom_handler(ord('E'), end_selection)
- picker.register_custom_handler(ord('a'), select_all)
- picker.register_custom_handler(ord('A'), select_all)
- selected=picker.start()
- if selected[0]==False:
- print(" User didn't select any files")
- return False
- if selected[0]=="ALL":
- pass
- else:
- newgpaths=[]
- for game in selected:
- g=game[1]
- g0=gamepaths[g]
- newgpaths.append(g0)
- gamepaths=newgpaths
- print("6. Generating text file...")
- with open(tfile,'w', encoding='utf8') as textfile:
- for i in gamepaths:
- textfile.write((i).strip()+"\n")
- print("7. Triggering installer on loop mode.")
- print(" Note:If you interrupt the list use normal install mode to continue list")
- loop_install(tfile,destiny=destiny,verification=verification,ch_medium=ch_medium,ch_other=ch_other,checked=True)
- else:
- print("\n --- DEVICE IS UP TO DATE ---")
-
-def get_libs(lib="source"):
- libraries={}
- if lib=="source":
- libtfile=mtp_source_lib
- elif lib=="internal":
- libtfile=mtp_internal_lib
- else:
- libtfile=lib
- with open(libtfile,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0;up=False
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- if 'library_name' and 'path' and 'Update' in csvheader:
- lb=csvheader.index('library_name')
- pth=csvheader.index('path')
- up=csvheader.index('Update')
- else:
- if 'library_name' and 'path' in csvheader:
- lb=csvheader.index('library_name')
- pth=csvheader.index('path')
- else:break
- else:
- try:
- update=False
- library=str(row[lb])
- route=str(row[pth])
- if up!=False:
- update=str(row[up])
- if update.upper()=="TRUE":
- update=True
- else:
- update=False
- else:
- update=False
- libraries[library]=[route,update]
- except:pass
- return libraries
-
-def get_dlc_baseid(titleid):
- baseid=str(titleid)
- token=int(hx(bytes.fromhex('0'+baseid[-4:-3])),16)-int('1',16)
- token=str(hex(token))[-1]
- token=token.upper()
- baseid=baseid[:-4]+token+'000'
- return baseid
-
-def get_storage_info():
- check_connection()
- SD_ds=0;SD_fs=0;NAND_ds=0;NAND_fs=0;device='unknown';FW='unknown'
- if os.path.exists(storage_info):
- try:
- os.remove(storage_info)
- except:pass
- process=subprocess.Popen([nscb_mtp,"ReportStorage","-tfile",storage_info],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- if os.path.exists(storage_info):
- with open(storage_info,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- if 'device' and 'disk_name' and 'capacity' and 'freespace' and 'FW' in csvheader:
- idev=csvheader.index('device')
- idn=csvheader.index('disk_name')
- idc=csvheader.index('capacity')
- ifs=csvheader.index('freespace')
- ifw=csvheader.index('FW')
- else:break
- else:
- if i==1:
- try:
- device=str(row[idev])
- FW=str(row[ifw])
- except:pass
- i+1;
- if 'SD CARD' in str(row[idn]).upper() or 'SD' in str(row[idn]).upper():
- # print(str(row[idn]));print(str(row[idc]));print(str(row[ifs]));
- SD_ds=int(row[idc])
- SD_fs=int(row[ifs])
- if 'USER' in str(row[idn]).upper():
- # print(str(row[idn]));print(str(row[idc]));print(str(row[ifs]));
- NAND_ds=int(row[idc])
- NAND_fs=int(row[ifs])
- if str(row[idev]).upper()=="TINFOIL":
- SD_ds=int(row[idc])
- SD_fs=int(row[ifs])
- NAND_ds=0
- NAND_fs=0
- return SD_ds,SD_fs,NAND_ds,NAND_fs,FW,device
-
-def get_DB_dict(filepath):
- installedsize=0
- if filepath.endswith('xci') or filepath.endswith('xcz'):
- f = factory(filepath)
- f.open(filepath, 'rb')
- dict=f.return_DBdict()
- f.flush()
- f.close()
- elif filepath.endswith('nsp') or filepath.endswith('nsz') or filepath.endswith('nsx'):
- f = squirrelNSP(filepath)
- dict=f.return_DBdict()
- f.flush()
- f.close()
- return dict
-
-def get_files_from_walk(tfile=None,extlist=['nsp','nsz','xci','xcz'],filter=False):
- from picker_walker import get_files_from_walk
- files=get_files_from_walk(tfile=tfile,extlist=extlist,filter=filter)
- if files==False:
- return False
- elif tfile==None:
- return files
diff --git a/py/mtp/mtpnsp.py b/py/mtp/mtpnsp.py
deleted file mode 100644
index 7cf18c05..00000000
--- a/py/mtp/mtpnsp.py
+++ /dev/null
@@ -1,266 +0,0 @@
-import aes128
-import Print
-import os
-import shutil
-import json
-import listmanager
-from Fs import Nsp as squirrelNSP
-from Fs import Xci as squirrelXCI
-from Fs import Nca
-import sq_tools
-
-import Keys
-from binascii import hexlify as hx, unhexlify as uhx
-import subprocess
-import sys
-from mtp.wpd import is_switch_connected
-from python_pick import pick
-from python_pick import Picker
-import csv
-from tqdm import tqdm
-
-def check_connection():
- if not is_switch_connected():
- sys.exit("Switch device isn't connected.\nCheck if mtp responder is running!!!")
-
-bucketsize = 81920
-
-# SET ENVIRONMENT
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-
-testroute1=os.path.join(squirrel_dir, "squirrel.py")
-testroute2=os.path.join(squirrel_dir, "squirrel.exe")
-urlconfig=os.path.join(zconfig_dir,'NUT_DB_URL.txt')
-isExe=False
-if os.path.exists(testroute1):
- squirrel=testroute1
- isExe=False
-elif os.path.exists(testroute2):
- squirrel=testroute2
- isExe=True
-bin_folder=os.path.join(ztools_dir, 'bin')
-nscb_mtp=os.path.join(bin_folder, 'nscb_mtp.exe')
-cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
-if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
-games_installed_cache=os.path.join(cachefolder, 'games_installed.txt')
-valid_saves_cache=os.path.join(cachefolder, 'valid_saves.txt')
-mtp_source_lib=os.path.join(zconfig_dir,'mtp_source_libraries.txt')
-mtp_internal_lib=os.path.join(zconfig_dir,'mtp_SD_libraries.txt')
-storage_info=os.path.join(cachefolder, 'storage.csv')
-download_lib_file = os.path.join(zconfig_dir, 'mtp_download_libraries.txt')
-
-
-def install_nsp_csv(filepath,destiny="SD",cachefolder=None,override=False,keypatch=False):
- check_connection()
- if cachefolder==None:
- cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
- files_list=sq_tools.ret_nsp_offsets(filepath)
- print(f"Installing {filepath} by content")
- counter=0
- for i in range(len(files_list)):
- entry=files_list[i]
- 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_nsp_parts_spec1(filepath,target_cnmt=target_cnmt,cachefolder=cachefolder,keypatch=keypatch)
- if filepath.endswith('nsz'):
- nspname=nspname[:-1]+'z'
- files_csv=os.path.join(cachefolder, 'files.csv')
- process=subprocess.Popen([nscb_mtp,"InstallfromCSV","-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)
-
-def gen_nsp_parts_spec1(filepath,target_cnmt=None,cachefolder=None,keypatch=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)
- files_list=sq_tools.ret_nsp_offsets(filepath)
- 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:
- f=squirrelNSP(filepath)
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=f.get_data_from_cnmt(cnmtfile)
- f.flush()
- f.close()
- for j in range(len(ncadata)):
- row=ncadata[j]
- # print(row)
- 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
- f.flush()
- f.close()
- outheader = sq_tools.gen_nsp_header(files,filesizes)
- properheadsize=len(outheader)
- # print(properheadsize)
- # print(bucketsize)
- i=0;sum=properheadsize;
- nsp=squirrelNSP(filepath)
- outfile=os.path.join(cachefolder, "0")
- outf = open(outfile, 'w+b')
- outf.write(outheader)
- written=0
- for fi in files:
- for nca in nsp:
- if nca._path==fi:
- nca=Nca(nca)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if nca.header.getRightsId() != 0:
- nca.rewind()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- from mtp_tools import get_nca_ticket
- check,titleKey=get_nca_ticket(filepath,fi)
- if check==False:
- sys.exit("Can't verify titleckey")
- 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 < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelNSP.get_new_cryptoblock(squirrelNSP,nca,keypatch,encKeyBlock,t)
- t.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- encKeyBlock = nca.header.getKeyBlock()
- if str(keypatch) != "False":
- t = tqdm(total=False, unit='B', unit_scale=False, leave=False)
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelNSP.get_new_cryptoblock(squirrelNSP,nca,keypatch,encKeyBlock,t)
- t.close()
- nca.rewind()
- i=0
- newheader=nsp.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- outf.write(newheader)
- written+=len(newheader)
- nca.seek(0xC00)
- break
- else:pass
- nsp.flush()
- nsp.close()
- outf.flush()
- outf.close()
- tfile=os.path.join(cachefolder, "files.csv")
- with open(tfile,'w') as csvfile:
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format("step","filepath","size","targetsize","off1","off2"))
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format(0,outfile,properheadsize+written,properheadsize,0,properheadsize))
- 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)))
- off1=j[1]+0xC00
- off2=j[2]
- targetsize=j[3]-0xC00
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format(k+2,filepath,(os.path.getsize(filepath)),targetsize,off1,off2))
- break
- k+=2;l+=1
- nspname="test.nsp"
- try:
- g=os.path.basename(filepath)
- g0=[pos for pos, char in enumerate(g) if char == '[']
- g0=(g[0:g0[0]]).strip()
- nspname=f"{g0} [{titleid}] [v{titleversion}] [{ctype}].nsp"
- except:pass
- return nspname
diff --git a/py/mtp/mtpxci.py b/py/mtp/mtpxci.py
deleted file mode 100644
index 411e7f2a..00000000
--- a/py/mtp/mtpxci.py
+++ /dev/null
@@ -1,915 +0,0 @@
-import aes128
-import Print
-import os
-import shutil
-import json
-import listmanager
-from Fs import Nsp as squirrelNSP
-from Fs import Xci as squirrelXCI
-from Fs import factory
-from Fs import Nca
-import sq_tools
-import io
-
-import Keys
-from binascii import hexlify as hx, unhexlify as uhx
-import math
-import subprocess
-import sys
-from mtp.wpd import is_switch_connected
-from python_pick import pick
-from python_pick import Picker
-import csv
-from tqdm import tqdm
-
-def check_connection():
- if not is_switch_connected():
- sys.exit("Switch device isn't connected.\nCheck if mtp responder is running!!!")
-
-bucketsize = 81920
-
-# SET ENVIRONMENT
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-
-testroute1=os.path.join(squirrel_dir, "squirrel.py")
-testroute2=os.path.join(squirrel_dir, "squirrel.exe")
-urlconfig=os.path.join(zconfig_dir,'NUT_DB_URL.txt')
-isExe=False
-if os.path.exists(testroute1):
- squirrel=testroute1
- isExe=False
-elif os.path.exists(testroute2):
- squirrel=testroute2
- isExe=True
-bin_folder=os.path.join(ztools_dir, 'bin')
-nscb_mtp=os.path.join(bin_folder, 'nscb_mtp.exe')
-cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
-if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
-games_installed_cache=os.path.join(cachefolder, 'games_installed.txt')
-valid_saves_cache=os.path.join(cachefolder, 'valid_saves.txt')
-mtp_source_lib=os.path.join(zconfig_dir,'mtp_source_libraries.txt')
-mtp_internal_lib=os.path.join(zconfig_dir,'mtp_SD_libraries.txt')
-storage_info=os.path.join(cachefolder, 'storage.csv')
-download_lib_file = os.path.join(zconfig_dir, 'mtp_download_libraries.txt')
-
-def libraries(tfile):
- db={}
- try:
- with open(tfile,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- else:
- dict_={}
- for j in range(len(csvheader)):
- try:
- if row[j]==None or row[j]=='':
- dict_[csvheader[j]]=None
- else:
- dict_[csvheader[j]]=row[j]
- except:
- dict_[csvheader[j]]=None
- db[row[0]]=dict_
- return db
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return False
-
-def pick_transfer_folder():
- if not os.path.exists(mtp_internal_lib):
- return "SD"
- title = 'Select transfer folder: '
- db=libraries(mtp_internal_lib)
- if db==False:
- return "SD"
- options = [x for x in db.keys()]
- selected = pick(options, title,min_selection_count=1)
- path=(db[selected[0]])['path']
- return path
-
-def file_verification(filename,hash=False):
- if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
- tempfolder=os.path.join(cachefolder, 'temp')
- if not os.path.exists(tempfolder):
- os.makedirs(tempfolder)
- verdict=False;isrestored=False;cnmt_is_patched=False
- if filename.endswith('.nsp') or filename.endswith('.nsx') or filename.endswith('.nsz') or filename.endswith('.xcz') or filename.endswith('.xci'):
- try:
- if filename.endswith('.nsp') or filename.endswith('.nsx') or filename.endswith('.nsz'):
- f = squirrelNSP(filename, 'rb')
- elif filename.endswith('.xci') or filename.endswith('.xcz'):
- f = factory(filename)
- f.open(filename, 'rb')
- check,feed=f.verify()
- if filename.endswith('.nsp') or filename.endswith('.nsx') or filename.endswith('.nsz'):
- verdict,headerlist,feed=f.verify_sig(feed,tempfolder,cnmt='nocheck')
- else:
- verdict,headerlist,feed=f.verify_sig(feed,tempfolder)
- output_type='nsp';multi=False;cnmtcount=0
- if verdict == True:
- isrestored=True
- for i in range(len(headerlist)):
- entry=headerlist[i]
- if str(entry[0]).endswith('.cnmt.nca'):
- cnmtcount+=1
- if cnmt_is_patched==False:
- status=entry[2]
- if status=='patched':
- cnmt_is_patched=True
- if entry[1]!=False:
- if int(entry[-1])==1:
- output_type='xci'
- isrestored=False
- else:
- pass
- if isrestored == False:
- if cnmt_is_patched !=True:
- print('\nFILE VERIFICATION CORRECT.\n -> FILE WAS MODIFIED BUT ORIGIN IS CONFIRMED AS LEGIT\n')
- else:
- print('\nFILE VERIFICATION CORRECT. \n -> FILE WAS MODIFIED AND CNMT PATCHED\n')
- else:
- print("\nFILE VERIFICATION CORRECT. FILE IS SAFE.\n")
- if verdict == False:
- print("\nFILE VERIFICATION INCORRECT. \n -> UNCONFIRMED ORIGIN FILE HAS BEEN TAMPERED WITH\n")
- if hash==True and verdict==True:
- if filename.endswith('.nsp') or filename.endswith('.nsx') or filename.endswith('.xci'):
- verdict,feed=f.verify_hash_nca(65536,headerlist,verdict,feed)
- elif filename.endswith('.nsz'):
- verdict,feed=f.nsz_hasher(65536,headerlist,verdict,feed)
- elif filename.endswith('.xcz'):
- verdict,feed=f.xcz_hasher(65536,headerlist,verdict,feed)
- f.flush()
- f.close()
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return verdict,isrestored,cnmt_is_patched
-
-def generate_and_transfer_st1(filepath,outfolder,keypatch='false'):
- tname=str(os.path.basename(filepath))[:-3]+'xci'
- tmpfile=os.path.join(outfolder,tname)
- if filepath.endswith('xci') or filepath.endswith('xcz'):
- f = factory(filepath)
- f.open(filepath, 'rb')
- elif filepath.endswith('nsp') or filepath.endswith('nsz'):
- f = squirrelNSP(filepath, 'rb')
- f.c_xci_direct(65536,tmpfile,outfolder,metapatch='true',keypatch=keypatch)
- f.flush()
- f.close()
-
-def generate_xci_and_transfer(filepath=None,outfolder=None,destiny="SD",kgpatch=False,verification=False):
- check_connection()
- if destiny=="SD":
- destiny="1: External SD Card\\"
- from mtpinstaller import get_storage_info,get_DB_dict
- tgkg=0;kgwarning=False
- if filepath=="":
- filepath=None
- if filepath==None:
- print("File input = null")
- return False
- if outfolder=="":
- outfolder=None
- if outfolder==None:
- outfolder=cachefolder
- if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- for f in os.listdir(outfolder):
- fp = os.path.join(outfolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- if verification==True or str(verification).upper()=="HASH":
- if str(verification).upper()=="HASH":
- verdict,isrestored,cnmt_is_patched=file_verification(filepath,hash=True)
- else:
- verdict,isrestored,cnmt_is_patched=file_verification(filepath)
- if verdict==False:
- print("File didn't pass verification. Skipping...")
- return False
- dopatch=False
- print("- Retrieving Space on device")
- SD_ds,SD_fs,NAND_ds,NAND_fs,FW,device=get_storage_info()
- print("- Calculating Size")
- head_xci_size,keygeneration,sz=get_header_size(filepath)
- installedsize=head_xci_size+sz
- print(f" * SD free space: {SD_fs} ({sq_tools.getSize(SD_fs)})")
- print(f" * File installed size: {installedsize} ({sq_tools.getSize(installedsize)})")
- if installedsize>SD_fs:
- sys.exit(" NOT ENOUGH SPACE SD STORAGE")
- if kgpatch==True:
- 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}")
- else:
- tgkg=keygeneration
- if kgwarning==True:
- print("File requires a higher firmware. It'll will be prepatch")
- dopatch=True
- keypatch=int(tgkg)
- tname=str(os.path.basename(filepath))[:-3]+'xci'
- tmpfile=os.path.join(outfolder,tname)
- if filepath.endswith('xcz') or filepath.endswith('nsz'):
- if isExe==False:
- process0=subprocess.Popen([sys.executable,squirrel,"-lib_call","mtp.mtpxci","generate_and_transfer_st1","-xarg",filepath,outfolder,str(keypatch)])
- else:
- process0=subprocess.Popen([squirrel,"-lib_call","mtp.mtpxci","generate_and_transfer_st1","-xarg",filepath,outfolder,str(keypatch)])
- while process0.poll()==None:
- if process0.poll()!=None:
- process0.terminate();
- if isExe==False:
- process1=subprocess.Popen([sys.executable,squirrel,"-renf",tmpfile,"-t","xci","-renm","force","-nover","xci_no_v0","-addl","false","-roma","TRUE"])
- else:
- process1=subprocess.Popen([sys.executable,squirrel,"-renf",tmpfile,"-t","xci","-renm","force","-nover","xci_no_v0","-addl","false","-roma","TRUE"])
- while process1.poll()==None:
- if process1.poll()!=None:
- process1.terminate();
- files2transfer=listmanager.folder_to_list(outfolder,['xci'])
- for f in files2transfer:
- bname=str(os.path.basename(f))
- destinypath=os.path.join(destiny,bname)
- process=subprocess.Popen([nscb_mtp,"Transfer","-ori",f,"-dst",destinypath])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- try:
- for f in os.listdir(outfolder):
- fp = os.path.join(outfolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- except:pass
- elif filepath.endswith('xci') or filepath.endswith('nsp'):
- from mtpxci_gen import transfer_xci_csv
- transfer_xci_csv(filepath,destiny,cachefolder=outfolder,keypatch=keypatch)
-
-def generate_multixci_and_transfer(tfile=None,outfolder=None,destiny="SD",kgpatch=False,verification=False):
- check_connection()
- if destiny==False or destiny=="pick" or destiny=="":
- destiny=pick_transfer_folder()
- if destiny=="SD":
- destiny="1: External SD Card\\"
- from mtpinstaller import get_storage_info,get_DB_dict
- tgkg=0;kgwarning=False
- if tfile=="":
- tfile=None
- if tfile==None:
- print("File input = null")
- return False
- if not os.path.exists(tfile):
- sys.exit(f"Couldn't find {tfile}")
- if outfolder=="":
- outfolder=None
- if outfolder==None:
- outfolder=cachefolder
- if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
- if not os.path.exists(outfolder):
- os.makedirs(outfolder)
- for f in os.listdir(outfolder):
- fp = os.path.join(outfolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- file_list=listmanager.read_lines_to_list(tfile,all=True)
- if verification==True or str(verification).upper()=="HASH":
- verdict=False
- for fp in file_list:
- if str(verification).upper()=="HASH":
- verdict,isrestored,cnmt_is_patched=file_verification(fp,hash=True)
- else:
- verdict,isrestored,cnmt_is_patched=file_verification(fp)
- if verdict==False:
- print(f"{fp} didn't pass verification. Skipping {tfile}")
- return False
- dopatch=False
- print("- Retrieving Space on device")
- SD_ds,SD_fs,NAND_ds,NAND_fs,FW,device=get_storage_info()
- print("- Calculating Size")
- fullxcisize=0;maxkg=0
- for fp in file_list:
- head_xci_size,keygeneration,sz=get_header_size(fp)
- installedsize=head_xci_size+sz
- fullxcisize+=installedsize
- if int(maxkg)SD_fs:
- sys.exit(" NOT ENOUGH SPACE SD STORAGE")
- if kgpatch==True:
- 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}")
- else:
- tgkg=keygeneration
- if kgwarning==True:
- print("File requires a higher firmware. It'll will be prepatch")
- dopatch=True
- keypatch=int(tgkg)
- input_files=listmanager.read_lines_to_list(tfile,all=True)
- has_compressed=False
- for fi in input_files:
- if fi.endswith('xcz') or fi.endswith('nsz'):
- has_compressed=True
- break
- if has_compressed==True:
- if isExe==False:
- process0=subprocess.Popen([sys.executable,squirrel,"-b","65536","-pv","true","-kp",str(keypatch),"--RSVcap","268435656","-fat","exfat","-fx","files","-ND","true","-t","xci","-o",outfolder,"-tfile",tfile,"-roma","TRUE","-dmul","calculate"])
- else:
- process0=subprocess.Popen([squirrel,"-b","65536","-pv","true","-kp",str(keypatch),"--RSVcap","268435656","-fat","exfat","-fx","files","-ND","true","-t","xci","-o",outfolder,"-tfile",tfile,"-roma","TRUE","-dmul","calculate"])
- while process0.poll()==None:
- if process0.poll()!=None:
- process0.terminate();
- files2transfer=listmanager.folder_to_list(outfolder,['xci'])
- for f in files2transfer:
- bname=str(os.path.basename(f))
- destinypath=os.path.join(destiny,bname)
- process=subprocess.Popen([nscb_mtp,"Transfer","-ori",f,"-dst",destinypath])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- try:
- for f in os.listdir(outfolder):
- fp = os.path.join(outfolder, f)
- try:
- shutil.rmtree(fp)
- except OSError:
- os.remove(fp)
- except:pass
- elif has_compressed==False:
- from mtpxci_gen import transfer_mxci_csv
- transfer_mxci_csv(tfile=tfile,destiny=destiny,cachefolder=outfolder,keypatch=keypatch,input_files=input_files)
-
-def loop_xci_transfer(tfile,destiny=False,verification=True,outfolder=None,patch_keygen=False,mode="single"):
- check_connection()
- if destiny==False or destiny=="pick" or destiny=="":
- destiny=pick_transfer_folder()
- if not os.path.exists(tfile):
- sys.exit(f"Couldn't find {tfile}")
- file_list=listmanager.read_lines_to_list(tfile,all=True)
- for item in file_list:
- if mode=="single":
- generate_xci_and_transfer(filepath=item,destiny=destiny,verification=verification,outfolder=outfolder,kgpatch=patch_keygen)
- print("")
- listmanager.striplines(tfile,counter=True)
- elif mode=="multi":
- continue
-
-def get_header_size(filepath):
- properheadsize=0;sz=0
- if filepath.endswith('xci') or filepath.endswith('xcz'):
- files_list=sq_tools.ret_xci_offsets(filepath)
- 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]
- if cnmtfile.endswith('.cnmt.nca'):
- f=squirrelXCI(filepath)
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=f.get_data_from_cnmt(cnmtfile)
- 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']))
- sz+=int(row['Size'])
- elif row['NCAtype']=='Meta':
- # print(str(row['NcaId'])+'.cnmt.nca')
- files.append(str(row['NcaId'])+'.cnmt.nca')
- filesizes.append(int(row['Size']))
- sz+=int(row['Size'])
- sec_hashlist=list()
- try:
- for file in files:
- sha,size,gamecard=f.file_hash(file)
- # print(sha)
- if sha != False:
- sec_hashlist.append(sha)
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- f.flush()
- f.close()
- 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
- elif filepath.endswith('nsp') or filepath.endswith('nsz'):
- files_list=sq_tools.ret_nsp_offsets(filepath)
- 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]
- if cnmtfile.endswith('.cnmt.nca'):
- f=squirrelNSP(filepath)
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=f.get_data_from_cnmt(cnmtfile)
- f.flush()
- f.close()
- 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']))
- sz+=int(row['Size'])
- elif row['NCAtype']=='Meta':
- # print(str(row['NcaId'])+'.cnmt.nca')
- files.append(str(row['NcaId'])+'.cnmt.nca')
- filesizes.append(int(row['Size']))
- sz+=int(row['Size'])
- f.flush()
- f.close()
- outheader = sq_tools.gen_nsp_header(files,filesizes)
- properheadsize=len(outheader)
- return properheadsize,keygeneration,sz
-
-def install_xci_csv(filepath,destiny="SD",cachefolder=None,override=False,keypatch=False):
- check_connection()
- if cachefolder==None:
- cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
- files_list=sq_tools.ret_xci_offsets(filepath)
- print(f"Installing {filepath} by content")
- counter=0
- for i in range(len(files_list)):
- entry=files_list[i]
- 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_spec1(filepath,target_cnmt=target_cnmt,cachefolder=cachefolder,keypatch=keypatch)
- if filepath.endswith('xcz'):
- nspname=nspname[:-1]+'z'
- files_csv=os.path.join(cachefolder, 'files.csv')
- process=subprocess.Popen([nscb_mtp,"InstallfromCSV","-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)
-
-def gen_xci_parts_spec0(filepath,target_cnmt=None,cachefolder=None,keypatch=False,export_type='csv'):
- 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)
- program_name=None
- files_list=sq_tools.ret_xci_offsets(filepath)
- 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:
- f=squirrelXCI(filepath)
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=f.get_data_from_cnmt(cnmtfile)
- f.flush()
- f.close()
- for j in range(len(ncadata)):
- row=ncadata[j]
- # print(row)
- if row['NCAtype']!='Meta' and row['NCAtype']!='Program':
- test1=str(row['NcaId'])+'.nca';test2=str(row['NcaId'])+'.ncz'
- if test1 in fplist or test2 in fplist:
- # print(str(row['NcaId'])+'.nca')
- 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 or test2 in fplist:
- # print(str(row['NcaId'])+'.nca')
- 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
- f.flush()
- f.close()
- outheader = sq_tools.gen_nsp_header(files,filesizes)
- properheadsize=len(outheader)
- # print(properheadsize)
- # print(bucketsize)
- i=0;sum=properheadsize;
- for file in files:
- # print(file)
- # print(filesizes[i])
- if i<(len(files)-1):
- sum+=filesizes[i]
- i+=1
- # print(sum)
- # print(sum/bucketsize)
- multiplier=math.ceil(sum/bucketsize)
- # print(multiplier)
- remainder = bucketsize*multiplier - sum
- # print(bucketsize*multiplier)
- xci=squirrelXCI(filepath)
- outfile=os.path.join(cachefolder, "0")
- written=0;
- outf = open(outfile, 'w+b')
- outf.write(outheader)
- written+=len(outheader)
- movoffset=0
- for fi in files:
- for nspF in xci.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if nca._path==fi:
- nca=Nca(nca)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if nca.header.getRightsId() != 0:
- nca.rewind()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- from mtp_tools import get_nca_ticket
- check,titleKey=get_nca_ticket(filepath,fi)
- if check==False:
- sys.exit("Can't verify titleckey")
- 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 < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelXCI.get_new_cryptoblock(squirrelXCI,nca, keypatch,encKeyBlock,t)
- t.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- encKeyBlock = nca.header.getKeyBlock()
- if str(keypatch) != "False":
- t = tqdm(total=False, unit='B', unit_scale=False, leave=False)
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelXCI.get_new_cryptoblock(squirrelXCI,nca,keypatch,encKeyBlock,t)
- t.close()
- nca.rewind()
- i=0
- newheader=xci.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- outf.write(newheader)
- written+=len(newheader)
- nca.seek(0xC00)
- movoffset+=0xC00
- if (str(nca.header.contentType) != 'Content.PROGRAM'):
- data=nca.read()
- nca.close()
- outf.write(data)
- written+=len(data)
- break
- else:pass
- xci.flush()
- xci.close()
- outf.close()
- if export_type=='json':
- files_json=os.path.join(cachefolder, "files.json")
- d={}
- d['0']={"step": 0,
- "filepath":outfile ,
- "size":( written) ,
- "targetsize":( written) ,
- "off1":0,
- "off2":( written)
- }
- for j in files_list:
- if j[0]==files[-1]:
- off1=j[1]+0xC00
- off2=j[2]
- targetsize=j[3]-0xC00
- d['1']={"step": 1,
- "filepath":filepath ,
- "size":( os.path.getsize(filepath)) ,
- "targetsize":targetsize,
- "off1":off1,
- "off2":off2
- }
- app_json = json.dumps(d, indent=1)
- with open(files_json, 'w') as json_file:
- json_file.write(app_json)
- # print(os.path.getsize(filepath))
- else:
- for j in files_list:
- if j[0]==files[-1]:
- off1=j[1]+0xC00
- off2=j[2]
- targetsize=j[3]-0xC00
- tfile=os.path.join(cachefolder, "files.csv")
- i=0;
- while True:
- with open(tfile,'w') as csvfile:
- if i==0:
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format("step","filepath","size","targetsize","off1","off2"))
- i+=1
- if i==1:
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format(0,outfile,os.path.getsize(outfile),os.path.getsize(outfile),0,os.path.getsize(outfile)))
- i+=1
- if i==2:
- csvfile.write("{}|{}|{}|{}|{}|{}".format(1,filepath,( os.path.getsize(filepath)),targetsize,off1,off2))
- i+=1
- break
- nspname="test.nsp"
- try:
- g=os.path.basename(filepath)
- g0=[pos for pos, char in enumerate(g) if char == '[']
- g0=(g[0:g0[0]]).strip()
- nspname=f"{g0} [{titleid}] [v{titleversion}] [{ctype}].nsp"
- except:pass
- return nspname
-
-def gen_xci_parts_spec1(filepath,target_cnmt=None,cachefolder=None,keypatch=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)
- files_list=sq_tools.ret_xci_offsets(filepath)
- 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:
- f=squirrelXCI(filepath)
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=f.get_data_from_cnmt(cnmtfile)
- f.flush()
- f.close()
- for j in range(len(ncadata)):
- row=ncadata[j]
- # print(row)
- 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
- f.flush()
- f.close()
- outheader = sq_tools.gen_nsp_header(files,filesizes)
- properheadsize=len(outheader)
- # print(properheadsize)
- # print(bucketsize)
- i=0;sum=properheadsize;
- xci=squirrelXCI(filepath)
- outfile=os.path.join(cachefolder, "0")
- outf = open(outfile, 'w+b')
- outf.write(outheader)
- written=0
- for fi in files:
- for nspF in xci.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if nca._path==fi:
- nca=Nca(nca)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if nca.header.getRightsId() != 0:
- nca.rewind()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- from mtp_tools import get_nca_ticket
- check,titleKey=get_nca_ticket(filepath,fi)
- if check==False:
- sys.exit("Can't verify titleckey")
- 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 < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelXCI.get_new_cryptoblock(squirrelXCI,nca,keypatch,encKeyBlock,t)
- t.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- encKeyBlock = nca.header.getKeyBlock()
- if str(keypatch) != "False":
- t = tqdm(total=False, unit='B', unit_scale=False, leave=False)
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelXCI.get_new_cryptoblock(squirrelXCI,nca,keypatch,encKeyBlock,t)
- t.close()
- nca.rewind()
- i=0
- newheader=xci.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- outf.write(newheader)
- written+=len(newheader)
- nca.seek(0xC00)
- break
- else:pass
- xci.flush()
- xci.close()
- outf.flush()
- outf.close()
- tfile=os.path.join(cachefolder, "files.csv")
- with open(tfile,'w') as csvfile:
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format("step","filepath","size","targetsize","off1","off2"))
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format(0,outfile,properheadsize+written,properheadsize,0,properheadsize))
- 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)))
- off1=j[1]+0xC00
- off2=j[2]
- targetsize=j[3]-0xC00
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format(k+2,filepath,(os.path.getsize(filepath)),targetsize,off1,off2))
- break
- k+=2;l+=1
- nspname="test.nsp"
- try:
- g=os.path.basename(filepath)
- g0=[pos for pos, char in enumerate(g) if char == '[']
- g0=(g[0:g0[0]]).strip()
- nspname=f"{g0} [{titleid}] [v{titleversion}] [{ctype}].nsp"
- except:pass
- return nspname
diff --git a/py/mtp/mtpxci_gen.py b/py/mtp/mtpxci_gen.py
deleted file mode 100644
index d3eecea0..00000000
--- a/py/mtp/mtpxci_gen.py
+++ /dev/null
@@ -1,601 +0,0 @@
-import aes128
-import Print
-import os
-import shutil
-import json
-import listmanager
-from Fs import Nsp as squirrelNSP
-from Fs import Xci as squirrelXCI
-from Fs import factory
-from Fs.Nca import NcaHeader
-from Fs import Nca
-from Fs.File import MemoryFile
-from Fs import Ticket
-import sq_tools
-import io
-from Fs import Type as FsType
-
-import Keys
-from binascii import hexlify as hx, unhexlify as uhx
-from DBmodule import Exchange as exchangefile
-import math
-import subprocess
-import sys
-from mtp.wpd import is_switch_connected
-from python_pick import pick
-from python_pick import Picker
-import csv
-from tqdm import tqdm
-import Print
-
-def check_connection():
- if not is_switch_connected():
- sys.exit("Switch device isn't connected.\nCheck if mtp responder is running!!!")
-
-bucketsize = 81920
-
-# SET ENVIRONMENT
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-
-testroute1=os.path.join(squirrel_dir, "squirrel.py")
-testroute2=os.path.join(squirrel_dir, "squirrel.exe")
-urlconfig=os.path.join(zconfig_dir,'NUT_DB_URL.txt')
-isExe=False
-if os.path.exists(testroute1):
- squirrel=testroute1
- isExe=False
-elif os.path.exists(testroute2):
- squirrel=testroute2
- isExe=True
-bin_folder=os.path.join(ztools_dir, 'bin')
-nscb_mtp=os.path.join(bin_folder, 'nscb_mtp.exe')
-cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
-if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
-games_installed_cache=os.path.join(cachefolder, 'games_installed.txt')
-valid_saves_cache=os.path.join(cachefolder, 'valid_saves.txt')
-mtp_source_lib=os.path.join(zconfig_dir,'mtp_source_libraries.txt')
-mtp_internal_lib=os.path.join(zconfig_dir,'mtp_SD_libraries.txt')
-storage_info=os.path.join(cachefolder, 'storage.csv')
-download_lib_file = os.path.join(zconfig_dir, 'mtp_download_libraries.txt')
-
-def get_header_size(flist):
- properheadsize=0;sz=0;total_list=[]
- for filepath in flist:
- if filepath.endswith('xci') or filepath.endswith('xcz'):
- files_list=sq_tools.ret_xci_offsets(filepath)
- joined_list = [*total_list, *files_list]
- total_list=joined_list
- 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]
- if cnmtfile.endswith('.cnmt.nca'):
- f=squirrelXCI(filepath)
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=f.get_data_from_cnmt(cnmtfile)
- 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']))
- sz+=int(row['Size'])
- elif row['NCAtype']=='Meta':
- # print(str(row['NcaId'])+'.cnmt.nca')
- files.append(str(row['NcaId'])+'.cnmt.nca')
- filesizes.append(int(row['Size']))
- sz+=int(row['Size'])
- sec_hashlist=list()
- try:
- for file in files:
- sha,size,gamecard=f.file_hash(file)
- # print(sha)
- if sha != False:
- sec_hashlist.append(sha)
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- f.flush()
- f.close()
- 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
- elif filepath.endswith('nsp') or filepath.endswith('nsz'):
- files_list=sq_tools.ret_nsp_offsets(filepath)
- joined_list = [*total_list, *files_list]
- total_list=joined_list
- 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]
- if cnmtfile.endswith('.cnmt.nca'):
- f=squirrelNSP(filepath)
- titleid,titleversion,base_ID,keygeneration,rightsId,RSV,RGV,ctype,metasdkversion,exesdkversion,hasHtmlManual,Installedsize,DeltaSize,ncadata=f.get_data_from_cnmt(cnmtfile)
- 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']))
- sz+=int(row['Size'])
- elif row['NCAtype']=='Meta':
- # print(str(row['NcaId'])+'.cnmt.nca')
- files.append(str(row['NcaId'])+'.cnmt.nca')
- filesizes.append(int(row['Size']))
- sz+=int(row['Size'])
- try:
- sec_hashlist=list()
- # print(files)
- for file in files:
- sha,size,gamecard=f.file_hash(file)
- # print(sha)
- if sha != False:
- sec_hashlist.append(sha)
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- f.flush()
- f.close()
- 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)
- return outheader,properheadsize,keygeneration,sz,files,total_list
-
-def transfer_xci_csv(filepath,destiny="SD",cachefolder=None,override=False,keypatch=False):
- check_connection()
- if destiny=="SD":
- destiny="1: External SD Card\\"
- if cachefolder==None:
- cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
- print(f"Creating xci for {filepath}")
- xciname=gen_xci_parts(filepath,cachefolder=cachefolder,keypatch=keypatch)
- destinypath=os.path.join(destiny,xciname)
- files_csv=os.path.join(cachefolder, 'files.csv')
- process=subprocess.Popen([nscb_mtp,"TransferfromCSV","-cs",files_csv,"-dst",destinypath])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- 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)
-
-def gen_xci_parts(filepath,cachefolder=None,keypatch=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)
- outheader,properheadsize,keygeneration,sz,files,files_list=get_header_size([filepath])
- properheadsize=len(outheader)
- # print(properheadsize)
- # print(bucketsize)
- i=0;sum=properheadsize;
- if filepath.endswith('xci'):
- xci=squirrelXCI(filepath)
- outfile=os.path.join(cachefolder, "0")
- outf = open(outfile, 'w+b')
- outf.write(outheader)
- written=0
- for fi in files:
- for nspF in xci.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if nca._path==fi:
- nca=Nca(nca)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if nca.header.getRightsId() != 0:
- nca.rewind()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- from mtp_tools import get_nca_ticket
- check,titleKey=get_nca_ticket(filepath,fi)
- if check==False:
- sys.exit("Can't verify titleckey")
- 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 < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelXCI.get_new_cryptoblock(squirrelXCI,nca,keypatch,encKeyBlock,t)
- t.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- encKeyBlock = nca.header.getKeyBlock()
- if str(keypatch) != "False":
- t = tqdm(total=False, unit='B', unit_scale=False, leave=False)
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelXCI.get_new_cryptoblock(squirrelXCI,nca,keypatch,encKeyBlock,t)
- t.close()
- nca.rewind()
- i=0
- newheader=xci.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- outf.write(newheader)
- written+=len(newheader)
- nca.seek(0xC00)
- break
- else:pass
- xci.flush()
- xci.close()
- elif filepath.endswith('nsp'):
- nsp=squirrelNSP(filepath)
- outfile=os.path.join(cachefolder, "0")
- outf = open(outfile, 'w+b')
- outf.write(outheader)
- written=0
- for fi in files:
- for nca in nsp:
- if nca._path==fi:
- nca=Nca(nca)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if nca.header.getRightsId() != 0:
- nca.rewind()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- from mtp_tools import get_nca_ticket
- check,titleKey=get_nca_ticket(filepath,fi)
- if check==False:
- sys.exit("Can't verify titleckey")
- 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 < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelNSP.get_new_cryptoblock(squirrelNSP,nca,keypatch,encKeyBlock,t)
- t.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- encKeyBlock = nca.header.getKeyBlock()
- if str(keypatch) != "False":
- t = tqdm(total=False, unit='B', unit_scale=False, leave=False)
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelNSP.get_new_cryptoblock(squirrelNSP,nca,keypatch,encKeyBlock,t)
- t.close()
- nca.rewind()
- i=0
- newheader=nsp.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- outf.write(newheader)
- written+=len(newheader)
- nca.seek(0xC00)
- break
- else:pass
- nsp.flush()
- nsp.close()
- outf.flush()
- outf.close()
- tfile=os.path.join(cachefolder, "files.csv")
- with open(tfile,'w') as csvfile:
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format("step","filepath","size","targetsize","off1","off2"))
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format(0,outfile,properheadsize+written,properheadsize,0,properheadsize))
- 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)))
- off1=j[1]+0xC00
- off2=j[2]
- targetsize=j[3]-0xC00
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format(k+2,filepath,(os.path.getsize(filepath)),targetsize,off1,off2))
- break
- k+=2;l+=1
- xciname="test.xci"
- try:
- g=os.path.basename(filepath)
- xciname=g[:-3]+'xci'
- except:pass
- return xciname
-
-def transfer_mxci_csv(tfile=None,destiny="SD",cachefolder=None,override=False,keypatch=False,input_files=None):
- check_connection()
- if input_files==None and tfile==None:
- sys.exit("Missing input!!!")
- if destiny=="SD":
- destiny="1: External SD Card\\"
- if cachefolder==None:
- cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
- if input_files==None:
- input_files=listmanager.read_lines_to_list(tfile,all=True)
- print(f"Creating mxci from {tfile}")
- xciname=gen_mxci_parts(input_files,cachefolder=cachefolder,keypatch=keypatch)
- destinypath=os.path.join(destiny,xciname)
- files_csv=os.path.join(cachefolder, 'files.csv')
- process=subprocess.Popen([nscb_mtp,"TransferfromCSV","-cs",files_csv,"-dst",destinypath])
- while process.poll()==None:
- if process.poll()!=None:
- process.terminate();
- 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)
-
-def gen_multi_file_header(prlist,filelist):
- oflist=[];osizelist=[];ototlist=[];files=[]
- totSize=0
- for i in range(len(prlist)):
- for j in prlist[i][4]:
- el=j[0]
- if el.endswith('.nca'):
- oflist.append(j[0])
- #print(j[0])
- totSize = totSize+j[1]
- #print(j[1])
- ototlist.append(j[0])
- sec_hashlist=list()
- GClist=list()
- # print(filelist)
- for file in oflist:
- for filepath in filelist:
- if filepath.endswith('.nsp') or filepath.endswith('.nsz'):
- try:
- f = squirrelNSP(filepath)
- sha,size,gamecard=f.file_hash(file)
- if sha != False:
- sec_hashlist.append(sha)
- osizelist.append(size)
- GClist.append([file,gamecard])
- f.flush()
- f.close()
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- if filepath.endswith('.xci') or filepath.endswith('.xcz'):
- try:
- f = squirrelXCI(filepath)
- sha,size,gamecard=f.file_hash(file)
- if sha != False:
- sec_hashlist.append(sha)
- osizelist.append(size)
- GClist.append([file,gamecard])
- f.flush()
- f.close()
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- 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(oflist,osizelist,sec_hashlist)
- totSize=len(xci_header)+len(game_info)+len(sig_padding)+len(xci_certificate)+rootSize
- 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)
- return outheader,properheadsize,totSize,oflist
-
-def gen_mxci_parts(input_files,cachefolder=None,keypatch=False):
- from listmanager import calculate_name
- 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)
- end_name,prlist=calculate_name(input_files,romanize=True,ext='.xci')
- print(f"Calculated name {end_name}")
- outheader,properheadsize,sz,files=gen_multi_file_header(prlist,input_files)
- properheadsize=len(outheader)
- outfile=os.path.join(cachefolder, "0")
- outf = open(outfile, 'w+b')
- outf.write(outheader)
- # print(properheadsize)
- # print(bucketsize)
- i=0;sum=properheadsize;
- for fi in files:
- for filepath in input_files:
- if filepath.endswith('xci'):
- xci=squirrelXCI(filepath)
- written=0
- for nspF in xci.hfs0:
- if str(nspF._path)=="secure":
- for nca in nspF:
- if nca._path==fi:
- nca=Nca(nca)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if nca.header.getRightsId() != 0:
- nca.rewind()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- from mtp_tools import get_nca_ticket
- check,titleKey=get_nca_ticket(filepath,fi)
- if check==False:
- sys.exit("Can't verify titleckey")
- 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 < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelXCI.get_new_cryptoblock(squirrelXCI,nca,keypatch,encKeyBlock,t)
- t.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- encKeyBlock = nca.header.getKeyBlock()
- if str(keypatch) != "False":
- t = tqdm(total=False, unit='B', unit_scale=False, leave=False)
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelXCI.get_new_cryptoblock(squirrelXCI,nca,keypatch,encKeyBlock,t)
- t.close()
- nca.rewind()
- i=0
- newheader=xci.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- outf.write(newheader)
- written+=len(newheader)
- nca.seek(0xC00)
- break
- else:pass
- xci.flush()
- xci.close()
- elif filepath.endswith('nsp'):
- nsp=squirrelNSP(filepath)
- written=0
- for nca in nsp:
- if nca._path==fi:
- nca=Nca(nca)
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- crypto = aes128.AESECB(Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev), nca.header.keyIndex))
- hcrypto = aes128.AESXTS(uhx(Keys.get('header_key')))
- gc_flag='00'*0x01
- crypto1=nca.header.getCryptoType()
- crypto2=nca.header.getCryptoType2()
- if nca.header.getRightsId() != 0:
- nca.rewind()
- if crypto2>crypto1:
- masterKeyRev=crypto2
- if crypto2<=crypto1:
- masterKeyRev=crypto1
- from mtp_tools import get_nca_ticket
- check,titleKey=get_nca_ticket(filepath,fi)
- if check==False:
- sys.exit("Can't verify titleckey")
- 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 < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelNSP.get_new_cryptoblock(squirrelNSP,nca,keypatch,encKeyBlock,t)
- t.close()
- if nca.header.getRightsId() == 0:
- nca.rewind()
- encKeyBlock = nca.header.getKeyBlock()
- if str(keypatch) != "False":
- t = tqdm(total=False, unit='B', unit_scale=False, leave=False)
- if keypatch < nca.header.getCryptoType2():
- encKeyBlock,crypto1,crypto2=squirrelNSP.get_new_cryptoblock(squirrelNSP,nca,keypatch,encKeyBlock,t)
- t.close()
- nca.rewind()
- i=0
- newheader=nsp.get_newheader(nca,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag)
- outf.write(newheader)
- written+=len(newheader)
- nca.seek(0xC00)
- break
- else:pass
- nsp.flush()
- nsp.close()
- outf.flush()
- outf.close()
- tfile=os.path.join(cachefolder, "files.csv")
- with open(tfile,'w') as csvfile:
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format("step","filepath","size","targetsize","off1","off2"))
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format(0,outfile,properheadsize+written,properheadsize,0,properheadsize))
- k=0;l=0
- for fi in files:
- for filepath in input_files:
- if filepath.endswith('xci'):
- files_list=sq_tools.ret_xci_offsets(filepath)
- elif filepath.endswith('nsp'):
- files_list=sq_tools.ret_nsp_offsets(filepath)
- 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)))
- off1=j[1]+0xC00
- off2=j[2]
- targetsize=j[3]-0xC00
- csvfile.write("{}|{}|{}|{}|{}|{}\n".format(k+2,filepath,(os.path.getsize(filepath)),targetsize,off1,off2))
- break
- k+=2;l+=1
- return end_name
diff --git a/py/mtp/mtpxci_remote.py b/py/mtp/mtpxci_remote.py
deleted file mode 100644
index 4a19b94d..00000000
--- a/py/mtp/mtpxci_remote.py
+++ /dev/null
@@ -1,678 +0,0 @@
-import aes128
-import Print
-import os
-import shutil
-import json
-import listmanager
-from Fs import Nsp as squirrelNSP
-from Fs import Xci as squirrelXCI
-from Fs.Nca import NcaHeader
-from Fs import Nca
-from Fs.File import MemoryFile
-import sq_tools
-from Fs import Type as FsType
-
-import Keys
-from binascii import hexlify as hx, unhexlify as uhx
-import subprocess
-import sys
-from mtp.wpd import is_switch_connected
-from python_pick import pick
-from python_pick import Picker
-import csv
-from tqdm import tqdm
-from Drive import Private as DrivePrivate
-from Drive import DriveTools
-
-def check_connection():
- if not is_switch_connected():
- sys.exit("Switch device isn't connected.\nCheck if mtp responder is running!!!")
-
-bucketsize = 81920
-
-# SET ENVIRONMENT
-squirrel_dir=os.path.abspath(os.curdir)
-NSCB_dir=os.path.abspath('../'+(os.curdir))
-
-if os.path.exists(os.path.join(squirrel_dir,'ztools')):
- NSCB_dir=squirrel_dir
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
- ztools_dir=os.path.join(NSCB_dir,'ztools')
- squirrel_dir=ztools_dir
-elif os.path.exists(os.path.join(NSCB_dir,'ztools')):
- squirrel_dir=squirrel_dir
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-else:
- ztools_dir=os.path.join(NSCB_dir, 'ztools')
- zconfig_dir=os.path.join(NSCB_dir, 'zconfig')
-
-testroute1=os.path.join(squirrel_dir, "squirrel.py")
-testroute2=os.path.join(squirrel_dir, "squirrel.exe")
-urlconfig=os.path.join(zconfig_dir,'NUT_DB_URL.txt')
-isExe=False
-if os.path.exists(testroute1):
- squirrel=testroute1
- isExe=False
-elif os.path.exists(testroute2):
- squirrel=testroute2
- isExe=True
-bin_folder=os.path.join(ztools_dir, 'bin')
-nscb_mtp=os.path.join(bin_folder, 'nscb_mtp.exe')
-cachefolder=os.path.join(ztools_dir, '_mtp_cache_')
-if not os.path.exists(cachefolder):
- os.makedirs(cachefolder)
-games_installed_cache=os.path.join(cachefolder, 'games_installed.txt')
-valid_saves_cache=os.path.join(cachefolder, 'valid_saves.txt')
-mtp_source_lib=os.path.join(zconfig_dir,'mtp_source_libraries.txt')
-mtp_internal_lib=os.path.join(zconfig_dir,'mtp_SD_libraries.txt')
-storage_info=os.path.join(cachefolder, 'storage.csv')
-download_lib_file = os.path.join(zconfig_dir, 'mtp_download_libraries.txt')
-remote_lib_file = os.path.join(zconfig_dir, 'remote_libraries.txt')
-cache_lib_file= os.path.join(zconfig_dir, 'remote_cache_location.txt')
-_1fichier_token=os.path.join((os.path.join(zconfig_dir, 'credentials')),'_1fichier_token.tk')
-remote_lib_cache=os.path.join(zconfig_dir, 'remote_lib_cache')
-
-def libraries(tfile):
- db={}
- try:
- with open(tfile,'rt',encoding='utf8') as csvfile:
- readCSV = csv.reader(csvfile, delimiter='|')
- i=0
- for row in readCSV:
- if i==0:
- csvheader=row
- i=1
- else:
- dict_={}
- for j in range(len(csvheader)):
- try:
- if row[j]==None or row[j]=='':
- dict_[csvheader[j]]=None
- else:
- dict_[csvheader[j]]=row[j]
- except:
- dict_[csvheader[j]]=None
- db[row[0]]=dict_
- return db
- except BaseException as e:
- Print.error('Exception: ' + str(e))
- return False
-
-def get_library_from_path(tfile=None,filename=None):
- if tfile==None:
- db=libraries(remote_lib_file)
- else:
- db=libraries(tfile)
- TD=None;lib=None;path="null"
- for entry in db:
- path=db[entry]['path']
- if filename.startswith(path):
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- else:
- pass
- if lib==None:
- db=libraries(cache_lib_file)
- TD=None;lib=None;path="null"
- for entry in db:
- path=db[entry]['path']
- if filename.startswith(path):
- TD=db[entry]['TD_name']
- lib=entry
- libpath=path
- break
- else:
- pass
- if TD=='':
- TD=None
- return lib,TD,libpath
-
-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)
-
-def gen_xci_parts_spec0(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']
- content_type=metadict['ctype']
- if content_type!="DLC":
- for j in range(len(ncadata)):
- row=ncadata[j]
- if row['NCAtype']!='Meta' and row['NCAtype']!='Program' and row['NCAtype']!='DeltaFragment':
- 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
- else:
- for j in range(len(ncadata)):
- row=ncadata[j]
- if row['NCAtype']!='Meta' and row['NCAtype']!='Data':
- 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']=='Data':
- 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
- nca_program=''
- 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
- 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
- rightsId=metadict['rightsId']
- titleKeyDec = DriveTools.get_titlekey(remote,rightsId,masterKeyRev,files_list=files_list)
- 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)
- if content_type!="DLC":
- if (str(ncaHeader.contentType) != 'Content.PROGRAM'):
- nca = Nca()
- nca.open(MemoryFile(remote.read_at(off1,nca_size)))
- nca.seek(0xC00)
- data=nca.read()
- outf.write(data)
- written+=len(data)
- # print(nca_name)
- # print(len(newheader)+len(data))
- else:
- nca_program=nca_name
- # print(nca_name)
- # print(len(newheader))
- else:
- if (str(ncaHeader.contentType) != 'Content.PUBLIC_DATA'):
- nca = Nca()
- nca.open(MemoryFile(remote.read_at(off1,nca_size)))
- nca.seek(0xC00)
- data=nca.read()
- outf.write(data)
- written+=len(data)
- # print(nca_name)
- # print(len(newheader)+len(data))
- else:
- nca_program=nca_name
- # print(nca_name)
- # print(len(newheader))
- 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,os.path.getsize(outfile),os.path.getsize(outfile),0,os.path.getsize(outfile),"False"))
- k=0;
- for j in files_list:
- if j[0]==nca_program:
- # print(j[0])
- 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+1,URL,remote.size,targetsize,off1,off2,token))
- k+=1
- break
- 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 BaseException as e:
- Print.error('Exception: ' + str(e))
- pass
- return nspname
-
-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
-
-def get_new_cryptoblock(ncaHeader, newMasterKeyRev,encKeyBlock,t):
- indent = 1
- tabs = '\t' * indent
- indent2 = 2
- tabs2 = '\t' * indent2
-
- masterKeyRev = ncaHeader.getCryptoType2()
-
- if type(ncaHeader) == NcaHeader():
- if ncaHeader.getCryptoType2() != newMasterKeyRev:
- t.write(tabs + '-----------------------------------')
- t.write(tabs + 'Changing keygeneration from %d to %s' % ( ncaHeader.getCryptoType2(), str(newMasterKeyRev)))
- t.write(tabs + '-----------------------------------')
- if sum(encKeyBlock) != 0:
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(masterKeyRev),ncaHeader.keyIndex)
- t.write(tabs2 + '+ decrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(masterKeyRev), ncaHeader.keyIndex))
- crypto = aes128.AESECB(key)
- decKeyBlock = crypto.decrypt(encKeyBlock)
- key = Keys.keyAreaKey(Keys.getMasterKeyIndex(newMasterKeyRev),ncaHeader.keyIndex)
- t.write(tabs2 + '+ encrypting with %s (%d, %d)' % (str(hx(key)), Keys.getMasterKeyIndex(newMasterKeyRev), ncaHeader.keyIndex))
- crypto = aes128.AESECB(key)
- reEncKeyBlock = crypto.encrypt(decKeyBlock)
- encKeyBlock = reEncKeyBlock
- if newMasterKeyRev >= 3:
- crypto1=2
- crypto2=newMasterKeyRev
- if newMasterKeyRev == 2:
- crypto1=2
- crypto2=0
- if newMasterKeyRev < 2:
- crypto1=newMasterKeyRev
- crypto2=0
- return encKeyBlock,crypto1,crypto2
- return encKeyBlock,ncaHeader.getCryptoType(),ncaHeader.getCryptoType2()
-
-def get_newheader(ncaHeader,encKeyBlock,crypto1,crypto2,hcrypto,gc_flag):
- ncaHeader.rewind()
- rawhead=ncaHeader.read(0xC00)
- rawhead=hcrypto.decrypt(rawhead)
- header = b''
- header += rawhead[0x00:0x00+0x204]
- #isgamecard 0x204
- GC=bytes.fromhex(gc_flag)
- header += GC
- #contentType 0x205
- header += rawhead[0x205:0x206]
- #crypto 1 0x206
- c1=crypto1.to_bytes(1, byteorder='big')
- header += c1
- #########
- header += rawhead[0x207:0x220]
- #crypto 1 0x220
- c2=crypto2.to_bytes(1, byteorder='big')
- header += c2
- #########
- header += rawhead[0x221:0x230]
- tr='00'*0x10
- tr=bytes.fromhex(tr)
- header += tr
- header += rawhead[0x240:0x240+0xC0]
- header += encKeyBlock
- header += rawhead[0x340:]
- newheader=hcrypto.encrypt(header)
- return newheader
diff --git a/py/mtp/wpd.py b/py/mtp/wpd.py
deleted file mode 100644
index 46fd0f14..00000000
--- a/py/mtp/wpd.py
+++ /dev/null
@@ -1,325 +0,0 @@
-"""A wrapper to use the Windows Portable Device Api (WPD)"""
-
-import comtypes
-from comtypes import GUID
-from comtypes.automation import *
-from comtypes.client import *
-from ctypes import *
-import re
-from collections import namedtuple
-import struct
-import binascii
-import struct
-
-def parse32le(data):
- return struct.unpack('I', data)[0]
-
-def dump32be(value):
- return struct.pack('>I', value)
-
-def parse16le(data):
- return struct.unpack('H', data)[0]
-
-def dump16be(value):
- return struct.pack('>H', value)
-
-def parse8(data):
- return struct.unpack('B', data)[0]
-
-def dump8(value):
- return struct.pack('B', value)
-
-class Struct(object):
- LITTLE_ENDIAN = '<'
- BIG_ENDIAN = '>'
- PADDING = '%dx'
- CHAR = 'c'
- STR = '%ds'
- INT64 = 'Q'
- INT32 = 'I'
- INT16 = 'H'
- INT8 = 'B'
-
- def __init__(self, name, fields, byteorder=LITTLE_ENDIAN):
- self.tuple = namedtuple(name, (n for n, fmt in fields if not isinstance(fmt, int)))
- self.format = byteorder + ''.join(self.PADDING % fmt if isinstance(fmt, int) else fmt for n, fmt in fields)
- self.size = struct.calcsize(self.format)
-
- def unpack(self, data, offset = 0):
- return self.tuple(*struct.unpack_from(self.format, data, offset))
-
- def pack(self, **kwargs):
- return struct.pack(self.format, *self.tuple(**kwargs))
-
-
-UsbDevice = namedtuple('UsbDevice', 'handle, idVendor, idProduct')
-MtpDeviceInfo = namedtuple('MtpDeviceInfo', 'manufacturer, model, serialNumber, operationsSupported, vendorExtension')
-USB_CLASS_PTP = 6
-PTP_OC_GetDeviceInfo = 0x1001
-PTP_OC_OpenSession = 0x1002
-PTP_OC_CloseSession = 0x1003
-PTP_RC_OK = 0x2001
-PTP_RC_SessionNotOpen = 0x2003
-PTP_RC_ParameterNotSupported = 0x2006
-PTP_RC_DeviceBusy = 0x2019
-PTP_RC_SessionAlreadyOpened = 0x201E
-
-
-def parseDeviceId(id):
- match = re.search('(#|\\\\)vid_([a-f0-9]{4})&pid_([a-f0-9]{4})(&|#|\\\\)', id, re.IGNORECASE)
- return [int(match.group(i), 16) if match else None for i in [2, 3]]
-
-
-# from . import *
-# from .. import *
-
-# Create and import the python comtypes wrapper for the needed DLLs
-comtypes.client._generate.__verbose__ = False
-_oldImport = comtypes.client._generate._my_import
-def _newImport(fullname):
- try:
- import importlib
- importlib.invalidate_caches()
- except:
- # Python 2
- pass
- _oldImport(fullname)
-comtypes.client._generate._my_import = _newImport
-
-GetModule('PortableDeviceApi.dll')
-GetModule('PortableDeviceTypes.dll')
-from comtypes.gen.PortableDeviceApiLib import *
-from comtypes.gen.PortableDeviceApiLib import _tagpropertykey as tpka
-from comtypes.gen.PortableDeviceTypesLib import *
-
-
-def propkey(fmtid, pid):
- key = _tagpropertykey()
- key.fmtid = GUID(fmtid)
- key.pid = pid
- return pointer(key)
-
-wpdCommonGuid = '{F0422A9C-5DC8-4440-B5BD-5DF28835658A}'
-wpdMtpGuid = '{4D545058-1A2E-4106-A357-771E0819FC56}'
-
-WPD_PROPERTY_COMMON_COMMAND_CATEGORY = propkey(wpdCommonGuid, 1001)
-WPD_PROPERTY_COMMON_COMMAND_ID = propkey(wpdCommonGuid, 1002)
-WPD_PROPERTY_COMMON_HRESULT = propkey(wpdCommonGuid, 1003)
-
-WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITHOUT_DATA_PHASE = propkey(wpdMtpGuid, 12)
-WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_READ = propkey(wpdMtpGuid, 13)
-WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_WRITE = propkey(wpdMtpGuid, 14)
-WPD_COMMAND_MTP_EXT_READ_DATA = propkey(wpdMtpGuid, 15)
-WPD_COMMAND_MTP_EXT_WRITE_DATA = propkey(wpdMtpGuid, 16)
-WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER = propkey(wpdMtpGuid, 17)
-
-WPD_PROPERTY_MTP_EXT_OPERATION_CODE = propkey(wpdMtpGuid, 1001)
-WPD_PROPERTY_MTP_EXT_OPERATION_PARAMS = propkey(wpdMtpGuid, 1002)
-WPD_PROPERTY_MTP_EXT_RESPONSE_CODE = propkey(wpdMtpGuid, 1003)
-WPD_PROPERTY_MTP_EXT_TRANSFER_CONTEXT = propkey(wpdMtpGuid, 1006)
-WPD_PROPERTY_MTP_EXT_TRANSFER_TOTAL_DATA_SIZE = propkey(wpdMtpGuid, 1007)
-WPD_PROPERTY_MTP_EXT_TRANSFER_NUM_BYTES_TO_READ = propkey(wpdMtpGuid, 1008)
-WPD_PROPERTY_MTP_EXT_TRANSFER_NUM_BYTES_TO_WRITE = propkey(wpdMtpGuid, 1010)
-WPD_PROPERTY_MTP_EXT_TRANSFER_DATA = propkey(wpdMtpGuid, 1012)
-
-class PROPVARIANT(Structure):
- _fields_ = [
- ('vt', c_ushort),
- ('reserved1', c_ubyte),
- ('reserved2', c_ubyte),
- ('reserved3', c_ulong),
- ('ulVal', c_ulong),
- ('reserved4', c_ulong),
- ]
-
-
-class MtpContext(object):
- def __init__(self):
- self.name = 'Windows-MTP'
- self.classType = USB_CLASS_PTP
-
- def __enter__(self):
- comtypes.CoInitialize()
- return self
-
- def __exit__(self, *ex):
- comtypes.CoUninitialize()
-
- def listDevices(self, vendor):
- return (dev for dev in _listDevices() if dev.idVendor == vendor)
-
- def openDevice(self, device):
- return _MtpDriver(device.handle)
-
-
-def _listDevices():
- """Lists all detected MTP devices"""
- # Create a device manager object
- pdm = CreateObject(PortableDeviceManager)
-
- length = c_ulong(0)
- pdm.GetDevices(POINTER(c_wchar_p)(), pointer(length))
- devices = (c_wchar_p * length.value)()
- pdm.GetDevices(devices, pointer(length))
-
- for id in devices:
- idVendor, idProduct = parseDeviceId(id)
- yield UsbDevice(id, idVendor, idProduct)
-
-class _MtpDriver(object):
- """Send and receive MTP packages to a device."""
- def __init__(self, device):
- self.device = CreateObject(PortableDevice)
- self.device.Open(device, CreateObject(PortableDeviceValues))
-
- def _initCommandValues(self, command, context=None):
- params = CreateObject(PortableDeviceValues)
- params.SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, command.contents.fmtid)
- params.SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, command.contents.pid)
- if context:
- params.SetStringValue(WPD_PROPERTY_MTP_EXT_TRANSFER_CONTEXT, context)
- return params
-
- def _initPropCollection(self, values):
- params = CreateObject(PortableDevicePropVariantCollection)
- for value in values:
- p = PROPVARIANT()
- p.vt = VT_UI4
- p.ulVal = value
- params.Add(cast(pointer(p), POINTER(tag_inner_PROPVARIANT)))
- return params
-
- def _initInitialCommand(self, command, code, args):
- params = self._initCommandValues(command)
- params.SetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_OPERATION_CODE, code)
- params.SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_MTP_EXT_OPERATION_PARAMS, self._initPropCollection(args))
- return params
-
- def _initDataCommand(self, command, context, lengthKey, data):
- params = self._initCommandValues(command, context)
- params.SetUnsignedLargeIntegerValue(lengthKey, len(data))
- params.SetBufferValue(WPD_PROPERTY_MTP_EXT_TRANSFER_DATA, (c_ubyte * len(data)).from_buffer_copy(data), len(data))
- return params
-
- def _send(self, params):
- result = self.device.SendCommand(0, params)
- code = result.GetErrorValue(cast(WPD_PROPERTY_COMMON_HRESULT, POINTER(tpka)))
- if code != 0:
- raise Exception('MTP SendCommand failed: 0x%x' % code)
- return result
-
- def _readResponse(self, context):
- params = self._initCommandValues(WPD_COMMAND_MTP_EXT_END_DATA_TRANSFER, context)
- result = self._send(params)
- return self._getResponse(result)
-
- def _getContext(self, result):
- return result.GetStringValue(cast(WPD_PROPERTY_MTP_EXT_TRANSFER_CONTEXT, POINTER(tpka)))
-
- def _getResponse(self, result):
- return result.GetUnsignedIntegerValue(cast(WPD_PROPERTY_MTP_EXT_RESPONSE_CODE, POINTER(tpka)))
-
- def reset(self):
- pass
-
- def sendCommand(self, code, args):
- """Send a PTP/MTP command without data phase"""
- params = self._initInitialCommand(WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITHOUT_DATA_PHASE, code, args)
- result = self._send(params)
- return self._getResponse(result)
-
- def sendWriteCommand(self, code, args, data):
- """Send a PTP/MTP command with write data phase"""
- params = self._initInitialCommand(WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_WRITE, code, args)
- params.SetUnsignedLargeIntegerValue(WPD_PROPERTY_MTP_EXT_TRANSFER_TOTAL_DATA_SIZE, len(data))
- result = self._send(params)
- context = self._getContext(result)
-
- params = self._initDataCommand(WPD_COMMAND_MTP_EXT_WRITE_DATA, context, WPD_PROPERTY_MTP_EXT_TRANSFER_NUM_BYTES_TO_WRITE, data)
- result = self._send(params)
-
- return self._readResponse(context)
-
- def sendReadCommand(self, code, args):
- """Send a PTP/MTP command with read data phase"""
- params = self._initInitialCommand(WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITH_DATA_TO_READ, code, args)
- result = self._send(params)
- context = self._getContext(result)
- length = result.GetUnsignedIntegerValue(cast(WPD_PROPERTY_MTP_EXT_TRANSFER_TOTAL_DATA_SIZE, POINTER(tpka)))
-
- params = self._initDataCommand(WPD_COMMAND_MTP_EXT_READ_DATA, context, WPD_PROPERTY_MTP_EXT_TRANSFER_NUM_BYTES_TO_READ, b'\0'*length)
- result = self._send(params)
- data, length = result.GetBufferValue(cast(WPD_PROPERTY_MTP_EXT_TRANSFER_DATA, POINTER(tpka)))
-
- return self._readResponse(context), bytes(bytearray(data[:length]))
-
-def _checkResponse(code, acceptedCodes=[]):
- if code not in [PTP_RC_OK] + acceptedCodes:
- msg = 'MTP error 0x%x' % code
- if code == PTP_RC_ParameterNotSupported:
- raise InvalidCommandException(msg)
- else:
- raise MtpException(msg)
-
-def parseString(data, offset):
- length = parse8(data[offset:offset+1])
- offset += 1
- end = offset + 2*length
- return end, data[offset:end].decode('utf16')[:-1]
-
-def parseIntArray(data, offset):
- length = parse32le(data[offset:offset+4])
- offset += 4
- end = offset + 2*length
- return end, [parse16le(data[o:o+2]) for o in range(offset, end, 2)]
-
-def _parseDeviceInfo(data):
- offset = 8
- offset, vendorExtension = parseString(data, offset)
- offset += 2
-
- offset, operationsSupported = parseIntArray(data, offset)
- offset, eventsSupported = parseIntArray(data, offset)
- offset, devicePropertiesSupported = parseIntArray(data, offset)
- offset, captureFormats = parseIntArray(data, offset)
- offset, imageFormats = parseIntArray(data, offset)
-
- offset, manufacturer = parseString(data, offset)
- offset, model = parseString(data, offset)
- offset, version = parseString(data, offset)
- offset, serial = parseString(data, offset)
- return MtpDeviceInfo(manufacturer, model, serial, set(operationsSupported), vendorExtension)
-
-def is_switch_connected():
- try:
- pdm = CreateObject(PortableDeviceManager)
- length = c_ulong(0)
- pdm.GetDevices(POINTER(c_wchar_p)(), pointer(length))
- devices = (c_wchar_p * length.value)()
- pdm.GetDevices(devices, pointer(length))
- for id in devices:
- idVendor, idProduct = parseDeviceId(id)
- if idVendor!=None and idProduct!=None:
- dev=UsbDevice(id, idVendor, idProduct)
- dev2=MtpContext()
- switch=dev2.openDevice(dev)
- break
- response, data = switch.sendReadCommand(PTP_OC_GetDeviceInfo, [])
- TEST=_parseDeviceInfo(data)
- if TEST.model=="Switch":
- return True
- else:
- return False
- except:return False
\ No newline at end of file