From 790e0c6ec8efa9c4ea20b989429c994d40733625 Mon Sep 17 00:00:00 2001 From: seiya-git Date: Wed, 25 Oct 2023 21:23:43 +0300 Subject: [PATCH] update --- bat/install_dependencies.bat | 14 ----- bat/nscb-compress.bat | 6 -- bat/nscb-decopress.bat | 6 -- bat/nscb-verify-folder-lv2.bat | 8 --- bat/nscb-verify-folder-lv3.bat | 8 --- bat/nscb-verify-lv2.bat | 6 -- bat/nscb-verify-lv3.bat | 6 -- bat/nscb-verify-test.bat | 6 -- bat/verify-folder.bat | 6 ++ py/lib/Verify.py | 33 +++++------ py/ns_extract_hashes.py | 62 ++++++++++++++++++++ py/{verify_folder.py => ns_verify_folder.py} | 46 +++++++++------ 12 files changed, 110 insertions(+), 97 deletions(-) delete mode 100644 bat/install_dependencies.bat delete mode 100644 bat/nscb-compress.bat delete mode 100644 bat/nscb-decopress.bat delete mode 100644 bat/nscb-verify-folder-lv2.bat delete mode 100644 bat/nscb-verify-folder-lv3.bat delete mode 100644 bat/nscb-verify-lv2.bat delete mode 100644 bat/nscb-verify-lv3.bat delete mode 100644 bat/nscb-verify-test.bat create mode 100644 bat/verify-folder.bat create mode 100644 py/ns_extract_hashes.py rename py/{verify_folder.py => ns_verify_folder.py} (75%) diff --git a/bat/install_dependencies.bat b/bat/install_dependencies.bat deleted file mode 100644 index 0585f209..00000000 --- a/bat/install_dependencies.bat +++ /dev/null @@ -1,14 +0,0 @@ -@echo off - -set wd=%cd% -cd /d %~dp0../py - -pip install --upgrade pip -pip install wheel - -pip install -r "requirements.txt" -pip install --upgrade google-api-python-client - -cd /d %wd% - -pause diff --git a/bat/nscb-compress.bat b/bat/nscb-compress.bat deleted file mode 100644 index fe0dd764..00000000 --- a/bat/nscb-compress.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -set squirrel=%~dp0../py/squirrel.py - -py "%squirrel%" -o "%~dp1." --nodelta "true" --compress "%~1" 22 - -pause diff --git a/bat/nscb-decopress.bat b/bat/nscb-decopress.bat deleted file mode 100644 index 90a39231..00000000 --- a/bat/nscb-decopress.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -set squirrel=%~dp0../py/squirrel.py - -py "%squirrel%" -o "%~dp1." --decompress "%~1" - -pause diff --git a/bat/nscb-verify-folder-lv2.bat b/bat/nscb-verify-folder-lv2.bat deleted file mode 100644 index 1190f02c..00000000 --- a/bat/nscb-verify-folder-lv2.bat +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -set squirrel=%~dp0../py/squirrel.py - -for %%a in ("*.xci", "*.xcz", "*.nsp", "*.nsz") do ( - py "%squirrel%" -v "%%~dpnxa" -vt lv2 -o "%%~dpa." --saveverifylog 1 -) - -pause diff --git a/bat/nscb-verify-folder-lv3.bat b/bat/nscb-verify-folder-lv3.bat deleted file mode 100644 index 7a592516..00000000 --- a/bat/nscb-verify-folder-lv3.bat +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -set squirrel=%~dp0../py/squirrel.py - -for %%a in ("*.xci", "*.xcz", "*.nsp", "*.nsz") do ( - py "%squirrel%" -v "%%~dpnxa" -vt lv3 -o "%%~dpa." --saveverifylog 1 -) - -pause diff --git a/bat/nscb-verify-lv2.bat b/bat/nscb-verify-lv2.bat deleted file mode 100644 index d00a91ea..00000000 --- a/bat/nscb-verify-lv2.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -set squirrel=%~dp0../py/squirrel.py - -py "%squirrel%" -v "%~1" -vt lv2 -o "%~dp1." --saveverifylog 1 - -pause diff --git a/bat/nscb-verify-lv3.bat b/bat/nscb-verify-lv3.bat deleted file mode 100644 index c7398cda..00000000 --- a/bat/nscb-verify-lv3.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -set squirrel=%~dp0../py/squirrel.py - -py "%squirrel%" -v "%~1" -vt lv3 -o "%~dp1." --saveverifylog 1 - -pause diff --git a/bat/nscb-verify-test.bat b/bat/nscb-verify-test.bat deleted file mode 100644 index 69e09c51..00000000 --- a/bat/nscb-verify-test.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -set squirrel=%~dp0../py/verify_folder.py - -py "%squirrel%" -i "%~1" - -pause diff --git a/bat/verify-folder.bat b/bat/verify-folder.bat new file mode 100644 index 00000000..990daa82 --- /dev/null +++ b/bat/verify-folder.bat @@ -0,0 +1,6 @@ +@echo off +set app=%~dp0../py/ns_verify_folder.py + +py "%app%" -i "%~1" --save-log + +pause diff --git a/py/lib/Verify.py b/py/lib/Verify.py index c855c25f..c1083395 100644 --- a/py/lib/Verify.py +++ b/py/lib/Verify.py @@ -17,14 +17,14 @@ import enlighten def parse_name(file): - res = re.search(r'(?P\[0100[A-F0-9]{12}\])\s?(?P\[v\d+\]).*?(?P\[(BASE|UPD(ATE)?|DLC( \d+)?)\])?.*?\.(xci|xcz|nsp|nsz)$', file, re.I) + res_id = re.search(r'(?P\[0100[A-F0-9]{12}\])', file) + res_ver = re.search(r'(?P\[v\d+\])', file) - if res is None: + if res_id is None or res_ver is None: return None - # define main data - title_id = res.group('title_id')[1:-1] - version = int(res.group('version')[2:-1]) + title_id = res_id.group('title_id')[1:-1] + version = int(res_ver.group('version')[2:-1]) title_type = None if title_id[:-4] == '010000000000': @@ -43,10 +43,14 @@ def parse_name(file): elif title_oei % 2 == 1 and int(title_ext, 16) > 0: title_type = 'DLC' - # can't define title_type if title_type is None: return None + if title_type != 'DLC': + title_ext = '' + else: + title_ext = f'{int(title_ext, 16):04}' + return { 'title_id': title_id, 'title_type': title_type, @@ -58,6 +62,9 @@ def verify(file): try: filename = os.path.abspath(file) + check = True + vmsg = list() + if file.lower().endswith('.xci'): f = Fs.factory(Path(filename)) f.open(filename, 'rb') @@ -66,16 +73,7 @@ def verify(file): elif file.lower().endswith(('.nsp', '.nsz')): f = Fs.Nsp.Nsp(filename, 'rb') else: - return False, {} - - res = parse_name(file) - log_info = f"{file.upper()[-3:]} {res['title_id']} v{round(res['version']/65536)} {res['title_type']}" - if res['title_type'] == 'DLC': - log_info += f" {str(int(res['title_ext'], 16)).zfill(4)}" - print(f'[:INFO:] Verifying... {log_info}\n') - - vmsg = list() - check = True + return False, vmsg check_decrypt, vmsg = verify_decrypt(f, vmsg) if check_decrypt == False: @@ -110,9 +108,6 @@ def verify_decrypt(nspx, vmsg = None): verdict = True - if type(nspx) != Fs.Xci.Xci and type(nspx) != Fs.Nsp.Nsp: - return False, msg - tvmsg = '\n[:INFO:] DECRYPTION TEST' print(tvmsg) vmsg.append(tvmsg) diff --git a/py/ns_extract_hashes.py b/py/ns_extract_hashes.py new file mode 100644 index 00000000..c968904a --- /dev/null +++ b/py/ns_extract_hashes.py @@ -0,0 +1,62 @@ +import os +import sys + +from pathlib import Path +from Fs import Pfs0, Nca, Type, factory + +# set app path +appPath = Path(sys.argv[0]) +while not appPath.is_dir(): + appPath = appPath.parents[0] +appPath = os.path.abspath(appPath) +print(f'[:INFO:] App Path: {appPath}') + +# set logs path +# logs_dir = os.path.abspath(os.path.join(appPath, '..', 'logs')) +# print(f'[:INFO:] Logs Path: {logs_dir}') + +import argparse +parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument('-i', '--input', help = 'input file') +args = parser.parse_args() + +INCP_PATH = args.input + +def send_hook(message_content): + try: + print(message_content) + except: + pass + +def scan_file(): + ipath = os.path.abspath(INCP_PATH) + if not os.path.isfile(ipath): + return + if not ipath.lower().endswith(('.xci', '.xcz', '.nsp', '.nsz')): + return + + container = factory(Path(ipath).resolve()) + container.open(ipath, 'rb') + if ipath.lower().endswith(('.xci', '.xcz')): + container = container.hfs0['secure'] + try: + for nspf in container: + if isinstance(nspf, Nca.Nca) and nspf.header.contentType == Type.Content.META: + for section in nspf: + if isinstance(section, Pfs0.Pfs0): + Cnmt = section.getCnmt() + print(Cnmt.__dict__) + for entry in Cnmt.contentEntries: + print(f'\n:{Cnmt.titleId} - Content.{Type.Content(entry.type)._name_}') + print(f'> FILENAME: {entry.ncaId}') + print(f'> HASH: {entry.hash.hex()}') + finally: + container.close() + + +if __name__ == "__main__": + if INCP_PATH: + scan_file() + else: + parser.print_help() + print() diff --git a/py/verify_folder.py b/py/ns_verify_folder.py similarity index 75% rename from py/verify_folder.py rename to py/ns_verify_folder.py index b3009e58..7b326653 100644 --- a/py/verify_folder.py +++ b/py/ns_verify_folder.py @@ -2,7 +2,6 @@ import sys import json import requests -import shutil import re from pathlib import Path @@ -23,15 +22,19 @@ parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('-i', '--input', help = 'input folder') parser.add_argument('-w', '--webhook-url', help = 'discord webhook url', required = False) +parser.add_argument('--save-log', help = 'save verify log', required = False, action='store_true') args = parser.parse_args() -config = vars(args) -INCP_PATH = config['input'] -WHOOK_URL = config['webhook_url'] +INCP_PATH = args.input +WHOOK_URL = args.webhook_url +SAVE_VLOG = bool(args.save_log) -def send_hook(message_content): +def send_hook(message_content, PadPrint = False): try: - print(message_content) + print_msg = message_content + if PadPrint == True: + print_msg = f'\n{message_content}' + print(print_msg) payload = { 'username': 'Contributions', 'content': message_content.strip() @@ -53,12 +56,12 @@ def scan_folder(): if not os.path.exists(logs_dir): os.makedirs(logs_dir) - if os.path.exists(lpath_badfolder): - os.remove(lpath_badfolder) - if os.path.exists(lpath_badname): - os.remove(lpath_badname) - if os.path.exists(lpath_badfile): - os.remove(lpath_badfile) + # if os.path.exists(lpath_badfolder): + # os.remove(lpath_badfolder) + # if os.path.exists(lpath_badname): + # os.remove(lpath_badname) + # if os.path.exists(lpath_badfile): + # os.remove(lpath_badfile) if not os.path.exists(ipath): print(f'[:WARN:] Please put your files in "{ipath}" and run this script again.') @@ -78,12 +81,13 @@ def scan_folder(): item_path = os.path.join(ipath, item) findex += 1 - send_hook(f'\n[:INFO:] File found ({findex} of {len(files)}): {item}') - send_hook(f'[:INFO:] Checking syntax...') + send_hook(f'[:INFO:] File found ({findex} of {len(files)}): {item}', True) + send_hook(f'[:INFO:] Checking filename...') data = Verify.parse_name(item) if data is None: + send_hook(f'{item_path}: BAD NAME') with open(lpath_badname, 'a') as f: f.write(f'{item_path}\n') continue @@ -114,15 +118,21 @@ def scan_folder(): log_name = os.path.join(rootpath, basename) try: + send_hook(f'[:INFO:] Verifying...') nspTest, nspLog = Verify.verify(item_path) if nspTest != True: + send_hook(f'{item_path}: BAD', True) with open(lpath_badfile, 'a') as f: f.write(f'{item_path}\n') - with open(f'{log_name}-bad.txt', 'w') as f: - f.write(f'{nspLog}') else: - with open(f'{log_name}.txt', 'w') as f: - f.write(f'{nspLog}') + send_hook(f'{item_path}: OK', True) + if SAVE_VLOG == True: + if nspTest != True: + with open(f'{log_name}-bad.log', 'w') as f: + f.write(f'{nspLog}') + else: + with open(f'{log_name}-ok.log', 'w') as f: + f.write(f'{nspLog}') except Exception as e: send_hook(f'[:WARN:] An error occurred:\n{item}: {str(e)}')