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+='' - 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.sizecrypto1: - 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(' 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+=''.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+=''.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