mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-12-22 08:32:03 +00:00
host: switch to Nuitka-based EXE building.
Other changes include: * host: rename windows_install_deps.py -> install_deps.py and update it to support multiple operating systems. * host: restore assertions and fix most static analysis errors related to type aliases and checks. * host: catch exceptions thrown by usb.core.find() in usbGetDeviceEndpoints(). * host: move code to unconditionally enable 32-bit paths under Windows to uiStartServer() and cliInitialize(), respectively. * host: remove incomplete file if the ongoing transfer was cancelled and we're not dealing with a NSP file.
This commit is contained in:
parent
300c650b8f
commit
bd1b56e5c2
4 changed files with 126 additions and 110 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,9 +1,10 @@
|
|||
.vscode
|
||||
build
|
||||
host/__pycache__
|
||||
host/installer
|
||||
host/build
|
||||
host/dist
|
||||
host/standalone
|
||||
host/nxdt_host.build
|
||||
host/nxdt_host.dist
|
||||
host/nxdt_host.onefile-build
|
||||
host/nxdumptool
|
||||
*.elf
|
||||
*.nacp
|
||||
|
|
|
@ -7,8 +7,11 @@
|
|||
from subprocess import run
|
||||
from os.path import dirname, join
|
||||
from sys import executable
|
||||
from platform import system
|
||||
|
||||
root_dir = dirname(__file__)
|
||||
|
||||
run([executable, '-m', 'pip', 'install', '-r', join(root_dir, 'requirements-win32.txt')])
|
||||
requirements_file = ('requirements-win32.txt' if system() == 'Windows' else 'requirements.txt')
|
||||
|
||||
run([executable, '-m', 'pip', 'install', '-r', join(root_dir, requirements_file)])
|
||||
input('Press enter to close')
|
|
@ -58,7 +58,7 @@ from tqdm import tqdm
|
|||
from argparse import ArgumentParser
|
||||
|
||||
from io import BufferedWriter
|
||||
from typing import List, Tuple, Any, Callable, Optional
|
||||
from typing import Generator, Any, Callable
|
||||
|
||||
# Scaling factors.
|
||||
WINDOWS_SCALING_FACTOR = 96.0
|
||||
|
@ -137,8 +137,8 @@ SERVER_START_MSG = f'Please connect a Nintendo Switch console running {USB_DEV_P
|
|||
SERVER_STOP_MSG = f'Exit {USB_DEV_PRODUCT} on your console or disconnect it at any time to stop the server.'
|
||||
|
||||
# Default directory paths.
|
||||
INITIAL_DIR = os.path.abspath(os.path.dirname(sys.executable if getattr(sys, 'frozen', False) else __file__))
|
||||
DEFAULT_DIR = (INITIAL_DIR + os.path.sep + USB_DEV_PRODUCT)
|
||||
INITIAL_DIR = os.path.dirname(os.path.abspath(os.path.expanduser(os.path.expandvars(sys.argv[0]))))
|
||||
DEFAULT_DIR = os.path.join(INITIAL_DIR, USB_DEV_PRODUCT)
|
||||
|
||||
# Application icon (PNG).
|
||||
# Embedded to load it as the icon for all windows using PhotoImage (which doesn't support ICO files) + wm_iconphoto.
|
||||
|
@ -225,7 +225,7 @@ APP_ICON = b'iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAAR
|
|||
b'43EDnoiNHI8a8FRs5HjMgCdjI8cj7+rp2MhR/Z3p7b5gyzRyjN0ei80cwP+bQrjkWSh1LgAAAABJRU5ErkJggg=='
|
||||
|
||||
# Taskbar Type Library (TLB). Used under Windows 7 or greater.
|
||||
TASKBAR_LIB_PATH = (INITIAL_DIR + os.path.sep + 'TaskbarLib.tlb')
|
||||
TASKBAR_LIB_PATH = os.path.join(INITIAL_DIR, 'TaskbarLib.tlb')
|
||||
|
||||
TASKBAR_LIB = b'TVNGVAIAAQAAAAAACQQAAAAAAABBAAAAAQAAAAAAAAAOAAAA/////wAAAAAAAAAATgAAADMDAAAAAAAA/////xgAAAAgAAAAgAAAAP////8AAAAAAAAAAGQAAADIAAAA' + \
|
||||
b'LAEAAJABAAD0AQAAWAIAALwCAAAgAwAAhAMAAOgDAABMBAAAsAQAABQFAAB8AQAAeAUAAP////8PAAAA/////wAAAAD/////DwAAAP////8AAAAA/////w8AAABMCAAA' + \
|
||||
|
@ -303,7 +303,7 @@ TASKBAR_LIB = b'TVNGVAIAAQAAAAAACQQAAAAAAABBAAAAAQAAAAAAAAAOAAAA/////wAAAAAAAAAA
|
|||
# Global variables used throughout the code.
|
||||
g_cliMode: bool = False
|
||||
g_outputDir: str = ''
|
||||
g_logLevelIntVar: Optional[tk.IntVar] = None
|
||||
g_logLevelIntVar: tk.IntVar | None = None
|
||||
|
||||
g_osType: str = ''
|
||||
g_osVersion: str = ''
|
||||
|
@ -312,18 +312,18 @@ g_isWindows: bool = False
|
|||
g_isWindowsVista: bool = False
|
||||
g_isWindows7: bool = False
|
||||
|
||||
g_tkRoot: Optional[tk.Tk] = None
|
||||
g_tkCanvas: Optional[tk.Canvas] = None
|
||||
g_tkDirText: Optional[tk.Text] = None
|
||||
g_tkChooseDirButton: Optional[tk.Button] = None
|
||||
g_tkServerButton: Optional[tk.Button] = None
|
||||
g_tkTipMessage: Any = None
|
||||
g_tkScrolledTextLog: Optional[scrolledtext.ScrolledText] = None
|
||||
g_tkVerboseCheckbox: Optional[tk.Checkbutton] = None
|
||||
g_tkRoot: tk.Tk | None = None
|
||||
g_tkCanvas: tk.Canvas | None = None
|
||||
g_tkDirText: tk.Text | None = None
|
||||
g_tkChooseDirButton: tk.Button | None = None
|
||||
g_tkServerButton: tk.Button | None = None
|
||||
g_tkTipMessage: int = 0
|
||||
g_tkScrolledTextLog: scrolledtext.ScrolledText | None = None
|
||||
g_tkVerboseCheckbox: tk.Checkbutton | None = None
|
||||
|
||||
g_logger: Optional[logging.Logger] = None
|
||||
g_logger: logging.Logger | None = None
|
||||
|
||||
g_stopEvent: Optional[threading.Event] = None
|
||||
g_stopEvent: threading.Event | None = None
|
||||
|
||||
g_tlb: Any = None
|
||||
g_taskbar: Any = None
|
||||
|
@ -343,12 +343,12 @@ g_nspTransferMode: bool = False
|
|||
g_nspSize: int = 0
|
||||
g_nspHeaderSize: int = 0
|
||||
g_nspRemainingSize: int = 0
|
||||
g_nspFile: Optional[BufferedWriter] = None
|
||||
g_nspFile: BufferedWriter | None = None
|
||||
g_nspFilePath: str = ''
|
||||
|
||||
# Reference: https://beenje.github.io/blog/posts/logging-to-a-tkinter-scrolledtext-widget.
|
||||
class LogQueueHandler(logging.Handler):
|
||||
def __init__(self, log_queue: queue.Queue):
|
||||
def __init__(self, log_queue: queue.Queue) -> None:
|
||||
super().__init__()
|
||||
self.log_queue = log_queue
|
||||
|
||||
|
@ -361,8 +361,8 @@ class LogQueueHandler(logging.Handler):
|
|||
|
||||
# Reference: https://beenje.github.io/blog/posts/logging-to-a-tkinter-scrolledtext-widget.
|
||||
class LogConsole:
|
||||
def __init__(self, scrolled_text: Optional[scrolledtext.ScrolledText] = None):
|
||||
#assert g_logger is not None
|
||||
def __init__(self, scrolled_text: scrolledtext.ScrolledText | None = None) -> None:
|
||||
assert g_logger is not None
|
||||
|
||||
self.scrolled_text = scrolled_text
|
||||
self.frame = (self.scrolled_text.winfo_toplevel() if self.scrolled_text else None)
|
||||
|
@ -406,7 +406,7 @@ class LogConsole:
|
|||
class ProgressBarWindow:
|
||||
global g_tlb, g_taskbar
|
||||
|
||||
def __init__(self, bar_format: str = '', tk_parent: Any = None, window_title: str = '', window_resize: bool = False, window_protocol: Optional[Callable] = None):
|
||||
def __init__(self, bar_format: str = '', tk_parent: Any = None, window_title: str = '', window_resize: bool = False, window_protocol: Callable | None = None) -> None:
|
||||
self.n: int = 0
|
||||
self.total: int = 0
|
||||
self.divider: float = 1.0
|
||||
|
@ -421,11 +421,11 @@ class ProgressBarWindow:
|
|||
self.tk_parent = tk_parent
|
||||
self.tk_window = (tk.Toplevel(self.tk_parent) if self.tk_parent else None)
|
||||
self.withdrawn = False
|
||||
self.tk_text_var: Optional[tk.StringVar] = None
|
||||
self.tk_n_var: Optional[tk.DoubleVar] = None
|
||||
self.tk_pbar: Optional[ttk.Progressbar] = None
|
||||
self.tk_text_var: tk.StringVar | None = None
|
||||
self.tk_n_var: tk.DoubleVar | None = None
|
||||
self.tk_pbar: ttk.Progressbar | None = None
|
||||
|
||||
self.pbar: Optional[tqdm] = None
|
||||
self.pbar: tqdm | None = None
|
||||
|
||||
if self.tk_window:
|
||||
self.tk_window.withdraw()
|
||||
|
@ -451,8 +451,8 @@ class ProgressBarWindow:
|
|||
self.tk_pbar.configure(maximum=100, mode='indeterminate')
|
||||
self.tk_pbar.pack()
|
||||
|
||||
def __del__(self):
|
||||
if self.tk_parent:
|
||||
def __del__(self) -> None:
|
||||
if self.tk_window:
|
||||
self.tk_parent.after(0, self.tk_window.destroy)
|
||||
|
||||
def start(self, total: int, n: int = 0, divider: int = 1, prefix: str = '', unit: str = 'B') -> None:
|
||||
|
@ -479,8 +479,8 @@ class ProgressBarWindow:
|
|||
return
|
||||
|
||||
if self.tk_window:
|
||||
#assert self.tk_text_var is not None
|
||||
#assert self.tk_n_var is not None
|
||||
assert self.tk_text_var is not None
|
||||
assert self.tk_n_var is not None
|
||||
|
||||
cur_n_div = (float(cur_n) / self.divider)
|
||||
self.elapsed_time = (time.time() - self.start_time)
|
||||
|
@ -505,7 +505,7 @@ class ProgressBarWindow:
|
|||
if g_taskbar:
|
||||
g_taskbar.SetProgressValue(self.hwnd, cur_n, self.total)
|
||||
else:
|
||||
#assert self.pbar is not None
|
||||
assert self.pbar is not None
|
||||
n_div = (float(n) / self.divider)
|
||||
self.pbar.update(n_div)
|
||||
|
||||
|
@ -522,7 +522,7 @@ class ProgressBarWindow:
|
|||
self.elapsed_time = 0
|
||||
|
||||
if self.tk_window:
|
||||
#assert self.tk_pbar is not None
|
||||
assert self.tk_pbar is not None
|
||||
|
||||
if g_taskbar:
|
||||
g_taskbar.SetProgressState(self.hwnd, g_tlb.TBPF_NOPROGRESS)
|
||||
|
@ -535,7 +535,7 @@ class ProgressBarWindow:
|
|||
|
||||
self.tk_pbar.configure(maximum=100, mode='indeterminate')
|
||||
else:
|
||||
#assert self.pbar is not None
|
||||
assert self.pbar is not None
|
||||
self.pbar.close()
|
||||
self.pbar = None
|
||||
print()
|
||||
|
@ -543,7 +543,7 @@ class ProgressBarWindow:
|
|||
def set_prefix(self, prefix) -> None:
|
||||
self.prefix = prefix
|
||||
|
||||
g_progressBarWindow: Optional[ProgressBarWindow] = None
|
||||
g_progressBarWindow: ProgressBarWindow | None = None
|
||||
|
||||
def eprint(*args, **kwargs) -> None:
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
@ -586,7 +586,7 @@ def utilsResetNspInfo(delete: bool = False) -> None:
|
|||
g_nspFile = None
|
||||
g_nspFilePath = ''
|
||||
|
||||
def utilsGetSizeUnitAndDivisor(size: int) -> Tuple[str, int]:
|
||||
def utilsGetSizeUnitAndDivisor(size: int) -> tuple[str, int]:
|
||||
size_suffixes = [ 'B', 'KiB', 'MiB', 'GiB' ]
|
||||
size_suffixes_count = len(size_suffixes)
|
||||
|
||||
|
@ -603,10 +603,11 @@ def utilsGetSizeUnitAndDivisor(size: int) -> Tuple[str, int]:
|
|||
def usbGetDeviceEndpoints() -> bool:
|
||||
global g_usbEpIn, g_usbEpOut, g_usbEpMaxPacketSize
|
||||
|
||||
#assert g_logger is not None
|
||||
#assert g_stopEvent is not None
|
||||
assert g_logger is not None
|
||||
assert g_stopEvent is not None
|
||||
|
||||
prev_dev = cur_dev = None
|
||||
cur_dev: Generator[usb.core.Device, Any, None] | None = None
|
||||
prev_dev: usb.core.Device | None = None
|
||||
usb_ep_in_lambda = lambda ep: usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_IN
|
||||
usb_ep_out_lambda = lambda ep: usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_OUT
|
||||
usb_version = 0
|
||||
|
@ -622,8 +623,20 @@ def usbGetDeviceEndpoints() -> bool:
|
|||
|
||||
# Find a connected USB device with a matching VID/PID pair.
|
||||
# Using == here to compare both device instances would also compare the backend, so we'll just compare certain elements manually.
|
||||
cur_dev = usb.core.find(idVendor=USB_DEV_VID, idProduct=USB_DEV_PID)
|
||||
if (cur_dev is None) or ((prev_dev is not None) and (cur_dev.bus == prev_dev.bus) and (cur_dev.address == prev_dev.address)):
|
||||
try:
|
||||
cur_dev = usb.core.find(find_all=False, idVendor=USB_DEV_VID, idProduct=USB_DEV_PID)
|
||||
except:
|
||||
if not g_cliMode:
|
||||
utilsLogException(traceback.format_exc())
|
||||
|
||||
g_logger.error('\nFatal error ocurred while enumerating USB devices.')
|
||||
|
||||
if g_isWindows:
|
||||
g_logger.error('\nTry reinstalling the libusbK driver using Zadig.')
|
||||
|
||||
return False
|
||||
|
||||
if (not isinstance(cur_dev, usb.core.Device)) or (isinstance(prev_dev, usb.core.Device) and (cur_dev.bus == prev_dev.bus) and (cur_dev.address == prev_dev.address)):
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
|
||||
|
@ -672,8 +685,6 @@ def usbGetDeviceEndpoints() -> bool:
|
|||
return True
|
||||
|
||||
def usbRead(size: int, timeout: int = -1) -> bytes:
|
||||
#assert g_logger is not None
|
||||
|
||||
rd = b''
|
||||
|
||||
try:
|
||||
|
@ -682,13 +693,13 @@ def usbRead(size: int, timeout: int = -1) -> bytes:
|
|||
except usb.core.USBError:
|
||||
if not g_cliMode:
|
||||
utilsLogException(traceback.format_exc())
|
||||
g_logger.error('\nUSB timeout triggered or console disconnected.')
|
||||
|
||||
if g_logger is not None:
|
||||
g_logger.error('\nUSB timeout triggered or console disconnected.')
|
||||
|
||||
return rd
|
||||
|
||||
def usbWrite(data: bytes, timeout: int = -1) -> int:
|
||||
#assert g_logger is not None
|
||||
|
||||
wr = 0
|
||||
|
||||
try:
|
||||
|
@ -696,18 +707,20 @@ def usbWrite(data: bytes, timeout: int = -1) -> int:
|
|||
except usb.core.USBError:
|
||||
if not g_cliMode:
|
||||
utilsLogException(traceback.format_exc())
|
||||
g_logger.error('\nUSB timeout triggered or console disconnected.')
|
||||
|
||||
if g_logger is not None:
|
||||
g_logger.error('\nUSB timeout triggered or console disconnected.')
|
||||
|
||||
return wr
|
||||
|
||||
def usbSendStatus(code: int) -> bool:
|
||||
status = struct.pack('<4sIH6p', USB_MAGIC_WORD, code, g_usbEpMaxPacketSize, b'')
|
||||
status = struct.pack('<4sIH6x', USB_MAGIC_WORD, code, g_usbEpMaxPacketSize)
|
||||
return bool(usbWrite(status, USB_TRANSFER_TIMEOUT) == len(status))
|
||||
|
||||
def usbHandleStartSession(cmd_block: bytes) -> int:
|
||||
global g_nxdtVersionMajor, g_nxdtVersionMinor, g_nxdtVersionMicro, g_nxdtAbiVersionMajor, g_nxdtAbiVersionMinor, g_nxdtGitCommit
|
||||
|
||||
#assert g_logger is not None
|
||||
assert g_logger is not None
|
||||
|
||||
if g_cliMode:
|
||||
print()
|
||||
|
@ -736,8 +749,8 @@ def usbHandleStartSession(cmd_block: bytes) -> int:
|
|||
def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
|
||||
global g_nspTransferMode, g_nspSize, g_nspHeaderSize, g_nspRemainingSize, g_nspFile, g_nspFilePath, g_outputDir, g_tkRoot, g_progressBarWindow
|
||||
|
||||
#assert g_logger is not None
|
||||
#assert g_progressBarWindow is not None
|
||||
assert g_logger is not None
|
||||
assert g_progressBarWindow is not None
|
||||
|
||||
if g_cliMode and not g_nspTransferMode:
|
||||
print()
|
||||
|
@ -786,10 +799,7 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
|
|||
if (not g_nspTransferMode) or (g_nspFile is None):
|
||||
# Generate full, absolute path to the destination file.
|
||||
fullpath = os.path.abspath(g_outputDir + os.path.sep + filename)
|
||||
|
||||
# Unconditionally enable 32-bit paths on Windows.
|
||||
if g_isWindows:
|
||||
fullpath = '\\\\?\\' + fullpath.replace("/", "\\")
|
||||
printable_fullpath = (fullpath[4:] if g_isWindows else fullpath)
|
||||
|
||||
# Get parent directory path.
|
||||
dirpath = os.path.dirname(fullpath)
|
||||
|
@ -800,11 +810,11 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
|
|||
# Make sure the output filepath doesn't point to an existing directory.
|
||||
if os.path.exists(fullpath) and (not os.path.isfile(fullpath)):
|
||||
utilsResetNspInfo()
|
||||
g_logger.error(f'Output filepath points to an existing directory! ("{fullpath}").\n')
|
||||
g_logger.error(f'Output filepath points to an existing directory! ("{printable_fullpath}").\n')
|
||||
return USB_STATUS_HOST_IO_ERROR
|
||||
|
||||
# Make sure we have enough free space.
|
||||
(total_space, used_space, free_space) = shutil.disk_usage(dirpath)
|
||||
(_, _, free_space) = shutil.disk_usage(dirpath)
|
||||
if free_space <= file_size:
|
||||
utilsResetNspInfo()
|
||||
g_logger.error('Not enough free space available in output volume!\n')
|
||||
|
@ -826,12 +836,8 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
|
|||
# Retrieve what we need using global variables.
|
||||
file = g_nspFile
|
||||
fullpath = g_nspFilePath
|
||||
|
||||
# Unconditionally enable 32-bit paths on Windows.
|
||||
if g_isWindows:
|
||||
fullpath = '\\\\?\\' + fullpath.replace("/", "\\")
|
||||
|
||||
dirpath = os.path.dirname(fullpath)
|
||||
printable_fullpath = (fullpath[4:] if g_isWindows else fullpath)
|
||||
|
||||
# Check if we're dealing with an empty file or with the first SendFileProperties command from a NSP.
|
||||
if (not file_size) or (g_nspTransferMode and file_size == g_nspSize):
|
||||
|
@ -846,7 +852,7 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
|
|||
usbSendStatus(USB_STATUS_SUCCESS)
|
||||
|
||||
# Start data transfer stage.
|
||||
g_logger.debug(f'Data transfer started. Saving {file_type_str} to: "{fullpath}".')
|
||||
g_logger.debug(f'Data transfer started. Saving {file_type_str} to: "{printable_fullpath}".')
|
||||
|
||||
offset = 0
|
||||
blksize = USB_TRANSFER_BLOCK_SIZE
|
||||
|
@ -858,10 +864,7 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
|
|||
# We're not using dynamic tqdm prefixes under CLI mode.
|
||||
prefix = ''
|
||||
else:
|
||||
idx = filename.rfind(os.path.sep)
|
||||
prefix_filename = (filename[idx+1:] if (idx >= 0) else filename)
|
||||
|
||||
prefix = f'Current {file_type_str}: "{prefix_filename}".\n'
|
||||
prefix = f'Current {file_type_str}: "{os.path.basename(filename)}".\n'
|
||||
prefix += 'Use your console to cancel the file transfer if you wish to do so.'
|
||||
|
||||
if (not g_nspTransferMode) or g_nspRemainingSize == (g_nspSize - g_nspHeaderSize):
|
||||
|
@ -885,8 +888,13 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
|
|||
|
||||
def cancelTransfer():
|
||||
# Cancel file transfer.
|
||||
utilsResetNspInfo(True)
|
||||
if use_pbar:
|
||||
if g_nspTransferMode:
|
||||
utilsResetNspInfo(True)
|
||||
else:
|
||||
file.close()
|
||||
os.remove(fullpath)
|
||||
|
||||
if use_pbar and (g_progressBarWindow is not None):
|
||||
g_progressBarWindow.end()
|
||||
|
||||
# Start transfer process.
|
||||
|
@ -917,7 +925,7 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
|
|||
|
||||
# Check if we're dealing with a CancelFileTransfer command.
|
||||
if chunk_size == USB_CMD_HEADER_SIZE:
|
||||
(magic, cmd_id, cmd_block_size) = struct.unpack_from('<4sII', chunk, 0)
|
||||
(magic, cmd_id, _) = struct.unpack_from('<4sII', chunk, 0)
|
||||
if (magic == USB_MAGIC_WORD) and (cmd_id == USB_CMD_CANCEL_FILE_TRANSFER):
|
||||
# Cancel file transfer.
|
||||
cancelTransfer()
|
||||
|
@ -957,7 +965,7 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
|
|||
return USB_STATUS_SUCCESS
|
||||
|
||||
def usbHandleCancelFileTransfer(cmd_block: bytes) -> int:
|
||||
#assert g_logger is not None
|
||||
assert g_logger is not None
|
||||
|
||||
g_logger.debug(f'Received CancelFileTransfer ({USB_CMD_START_SESSION:02X}) command.')
|
||||
|
||||
|
@ -972,8 +980,8 @@ def usbHandleCancelFileTransfer(cmd_block: bytes) -> int:
|
|||
def usbHandleSendNspHeader(cmd_block: bytes) -> int:
|
||||
global g_nspTransferMode, g_nspHeaderSize, g_nspRemainingSize, g_nspFile, g_nspFilePath
|
||||
|
||||
#assert g_logger is not None
|
||||
#assert g_nspFile is not None
|
||||
assert g_logger is not None
|
||||
assert g_nspFile is not None
|
||||
|
||||
nsp_header_size = len(cmd_block)
|
||||
|
||||
|
@ -1004,12 +1012,12 @@ def usbHandleSendNspHeader(cmd_block: bytes) -> int:
|
|||
return USB_STATUS_SUCCESS
|
||||
|
||||
def usbHandleEndSession(cmd_block: bytes) -> int:
|
||||
#assert g_logger is not None
|
||||
assert g_logger is not None
|
||||
g_logger.debug(f'Received EndSession ({USB_CMD_END_SESSION:02X}) command.')
|
||||
return USB_STATUS_SUCCESS
|
||||
|
||||
def usbHandleStartExtractedFsDump(cmd_block: bytes) -> int:
|
||||
#assert g_logger is not None
|
||||
assert g_logger is not None
|
||||
|
||||
g_logger.debug(f'Received StartExtractedFsDump ({USB_CMD_START_EXTRACTED_FS_DUMP:02X}) command.')
|
||||
|
||||
|
@ -1027,13 +1035,13 @@ def usbHandleStartExtractedFsDump(cmd_block: bytes) -> int:
|
|||
return USB_STATUS_SUCCESS
|
||||
|
||||
def usbHandleEndExtractedFsDump(cmd_block: bytes) -> int:
|
||||
#assert g_logger is not None
|
||||
assert g_logger is not None
|
||||
g_logger.debug(f'Received EndExtractedFsDump ({USB_CMD_END_EXTRACTED_FS_DUMP:02X}) command.')
|
||||
g_logger.info(f'Finished extracted FS dump.')
|
||||
return USB_STATUS_SUCCESS
|
||||
|
||||
def usbCommandHandler() -> None:
|
||||
#assert g_logger is not None
|
||||
assert g_logger is not None
|
||||
|
||||
cmd_dict = {
|
||||
USB_CMD_START_SESSION: usbHandleStartSession,
|
||||
|
@ -1054,8 +1062,8 @@ def usbCommandHandler() -> None:
|
|||
|
||||
if not g_cliMode:
|
||||
# Update UI.
|
||||
#assert g_tkCanvas is not None
|
||||
#assert g_tkServerButton is not None
|
||||
assert g_tkCanvas is not None
|
||||
assert g_tkServerButton is not None
|
||||
g_tkCanvas.itemconfigure(g_tkTipMessage, state='normal', text=SERVER_STOP_MSG)
|
||||
g_tkServerButton.configure(state='disabled')
|
||||
|
||||
|
@ -1123,13 +1131,13 @@ def usbCommandHandler() -> None:
|
|||
|
||||
def uiStopServer() -> None:
|
||||
# Signal the shared stop event.
|
||||
#assert g_stopEvent is not None
|
||||
assert g_stopEvent is not None
|
||||
g_stopEvent.set()
|
||||
|
||||
def uiStartServer() -> None:
|
||||
global g_outputDir
|
||||
|
||||
#assert g_tkDirText is not None
|
||||
assert g_tkDirText is not None
|
||||
|
||||
g_outputDir = g_tkDirText.get('1.0', tk.END).strip()
|
||||
if not g_outputDir:
|
||||
|
@ -1137,6 +1145,10 @@ def uiStartServer() -> None:
|
|||
messagebox.showerror('Error', 'You must provide an output directory!', parent=g_tkRoot)
|
||||
return
|
||||
|
||||
# Unconditionally enable 32-bit paths on Windows.
|
||||
if g_isWindows:
|
||||
g_outputDir = '\\\\?\\' + g_outputDir
|
||||
|
||||
# Make sure the full directory tree exists.
|
||||
try:
|
||||
os.makedirs(g_outputDir, exist_ok=True)
|
||||
|
@ -1153,11 +1165,11 @@ def uiStartServer() -> None:
|
|||
server_thread.start()
|
||||
|
||||
def uiToggleElements(flag: bool) -> None:
|
||||
#assert g_tkRoot is not None
|
||||
#assert g_tkChooseDirButton is not None
|
||||
#assert g_tkServerButton is not None
|
||||
#assert g_tkCanvas is not None
|
||||
#assert g_tkVerboseCheckbox is not None
|
||||
assert g_tkRoot is not None
|
||||
assert g_tkChooseDirButton is not None
|
||||
assert g_tkServerButton is not None
|
||||
assert g_tkCanvas is not None
|
||||
assert g_tkVerboseCheckbox is not None
|
||||
|
||||
if flag:
|
||||
g_tkRoot.protocol('WM_DELETE_WINDOW', uiHandleExitProtocol)
|
||||
|
@ -1168,7 +1180,7 @@ def uiToggleElements(flag: bool) -> None:
|
|||
|
||||
g_tkVerboseCheckbox.configure(state='normal')
|
||||
else:
|
||||
#assert g_tkScrolledTextLog is not None
|
||||
assert g_tkScrolledTextLog is not None
|
||||
|
||||
g_tkRoot.protocol('WM_DELETE_WINDOW', uiHandleExitProtocolStub)
|
||||
|
||||
|
@ -1188,14 +1200,14 @@ def uiChooseDirectory() -> None:
|
|||
uiUpdateDirectoryField(os.path.abspath(dir))
|
||||
|
||||
def uiUpdateDirectoryField(path: str) -> None:
|
||||
#assert g_tkDirText is not None
|
||||
assert g_tkDirText is not None
|
||||
g_tkDirText.configure(state='normal')
|
||||
g_tkDirText.delete('1.0', tk.END)
|
||||
g_tkDirText.insert('1.0', path)
|
||||
g_tkDirText.configure(state='disabled')
|
||||
|
||||
def uiHandleExitProtocol() -> None:
|
||||
#assert g_tkRoot is not None
|
||||
assert g_tkRoot is not None
|
||||
g_tkRoot.destroy()
|
||||
|
||||
def uiHandleExitProtocolStub() -> None:
|
||||
|
@ -1205,8 +1217,8 @@ def uiScaleMeasure(measure: int) -> int:
|
|||
return round(float(measure) * SCALE)
|
||||
|
||||
def uiHandleVerboseCheckbox() -> None:
|
||||
#assert g_logger is not None
|
||||
#assert g_logLevelIntVar is not None
|
||||
assert g_logger is not None
|
||||
assert g_logLevelIntVar is not None
|
||||
g_logger.setLevel(g_logLevelIntVar.get())
|
||||
|
||||
def uiInitialize() -> None:
|
||||
|
@ -1340,9 +1352,9 @@ def uiInitialize() -> None:
|
|||
g_tkRoot.mainloop()
|
||||
|
||||
def cliInitialize() -> None:
|
||||
global g_progressBarWindow
|
||||
global g_progressBarWindow, g_outputDir
|
||||
|
||||
#assert g_logger is not None
|
||||
assert g_logger is not None
|
||||
|
||||
# Initialize console logger.
|
||||
console = LogConsole()
|
||||
|
@ -1352,8 +1364,12 @@ def cliInitialize() -> None:
|
|||
g_progressBarWindow = ProgressBarWindow(bar_format)
|
||||
|
||||
# Print info.
|
||||
g_logger.info('\n' + SCRIPT_TITLE + '. ' + COPYRIGHT_TEXT + '.')
|
||||
g_logger.info('Output directory: "' + g_outputDir + '".\n')
|
||||
g_logger.info(f'\n{SCRIPT_TITLE}. {COPYRIGHT_TEXT}.')
|
||||
g_logger.info(f'Output directory: "{g_outputDir}".\n')
|
||||
|
||||
# Unconditionally enable 32-bit paths on Windows.
|
||||
if g_isWindows:
|
||||
g_outputDir = '\\\\?\\' + g_outputDir
|
||||
|
||||
# Start USB command handler directly.
|
||||
usbCommandHandler()
|
||||
|
@ -1365,9 +1381,9 @@ def main() -> int:
|
|||
warnings.filterwarnings("ignore")
|
||||
|
||||
# Parse command line arguments.
|
||||
parser = ArgumentParser(description=SCRIPT_TITLE + '. ' + COPYRIGHT_TEXT + '.')
|
||||
parser = ArgumentParser(description=f'{SCRIPT_TITLE}. {COPYRIGHT_TEXT}.')
|
||||
parser.add_argument('-c', '--cli', required=False, action='store_true', default=False, help='Start the script in CLI mode.')
|
||||
parser.add_argument('-o', '--outdir', required=False, type=str, metavar='DIR', help='Path to output directory. Defaults to "' + DEFAULT_DIR + '".')
|
||||
parser.add_argument('-o', '--outdir', required=False, type=str, metavar='DIR', help=f'Path to output directory. Defaults to "{DEFAULT_DIR}".')
|
||||
parser.add_argument('-v', '--verbose', required=False, action='store_true', default=False, help='Enable verbose output.')
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@ -1415,7 +1431,7 @@ if __name__ == "__main__":
|
|||
except KeyboardInterrupt:
|
||||
time.sleep(0.2)
|
||||
print('\nScript interrupted.')
|
||||
except Exception as e:
|
||||
except:
|
||||
utilsLogException(traceback.format_exc())
|
||||
|
||||
try:
|
||||
|
|
|
@ -3,26 +3,22 @@
|
|||
set scriptdir=%~dp0
|
||||
set scriptdir=%scriptdir:~0,-1%
|
||||
|
||||
set venvname=installer
|
||||
set venvname=standalone
|
||||
set venvscripts=%scriptdir%\%venvname%\Scripts
|
||||
|
||||
set venvpython=%venvscripts%\python.exe
|
||||
set venvpyinstaller=%venvscripts%\pyinstaller.exe
|
||||
|
||||
cd /D "%scriptdir%"
|
||||
|
||||
python -m venv "%venvname%"
|
||||
|
||||
"%venvpython%" -m pip install --upgrade pyinstaller -r requirements-win32.txt
|
||||
"%venvpython%" -m pip install --upgrade nuitka -r requirements-win32.txt
|
||||
|
||||
"%venvpyinstaller%" -y --clean --log-level WARN -F --add-binary "C:\Windows\System32\libusb0.dll;." -w -i nxdt.ico nxdt_host.py
|
||||
"%venvpython%" -m nuitka --standalone --onefile --deployment --disable-console --windows-icon-from-ico=nxdt.ico --enable-plugin=tk-inter nxdt_host.py
|
||||
|
||||
move dist\nxdt_host.exe nxdt_host.exe
|
||||
|
||||
rmdir /s /q __pycache__
|
||||
rmdir /s /q build
|
||||
rmdir /s /q dist
|
||||
rmdir /s /q installer
|
||||
del nxdt_host.spec
|
||||
rmdir /s /q nxdt_host.build
|
||||
rmdir /s /q nxdt_host.dist
|
||||
rmdir /s /q nxdt_host.onefile-build
|
||||
rmdir /s /q standalone
|
||||
|
||||
pause
|
||||
|
|
Loading…
Reference in a new issue