mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2025-01-09 09:16:08 +00:00
Added logging to file (gui)
This commit is contained in:
parent
0f308da4d0
commit
13a9f86fe9
1 changed files with 87 additions and 37 deletions
|
@ -60,6 +60,9 @@ from argparse import ArgumentParser
|
|||
from io import BufferedWriter
|
||||
from typing import List, Tuple, Any, Callable, Optional
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# Scaling factors.
|
||||
WINDOWS_SCALING_FACTOR = 96.0
|
||||
SCALE = 1.0
|
||||
|
@ -303,8 +306,10 @@ 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_logPath: str = ''
|
||||
|
||||
g_logLevelIntVar: Optional[tk.IntVar] = None
|
||||
g_logToFileBoolVar: Optional[tk.BooleanVar] = None
|
||||
g_osType: str = ''
|
||||
g_osVersion: str = ''
|
||||
|
||||
|
@ -358,6 +363,11 @@ class LogQueueHandler(logging.Handler):
|
|||
print(msg)
|
||||
else:
|
||||
self.log_queue.put(record)
|
||||
if g_logToFileBoolVar.get():
|
||||
logToFileMsg=datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]+'\t['+ \
|
||||
record.levelname+']\t'+self.format(record).strip()+'\n'
|
||||
with open (g_logPath, 'a', encoding="utf-8") as f:
|
||||
f.write(logToFileMsg)
|
||||
|
||||
# Reference: https://beenje.github.io/blog/posts/logging-to-a-tkinter-scrolledtext-widget.
|
||||
class LogConsole:
|
||||
|
@ -567,6 +577,44 @@ def utilsGetPath(path_arg: str, fallback_path: str, is_file: bool, create: bool
|
|||
|
||||
return path
|
||||
|
||||
# Prepends `\\?\` to enable ~64KiB long paths in Windows.
|
||||
# ref0: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#win32-file-namespaces
|
||||
# ref1: https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
|
||||
# ref2: https://stackoverflow.com/a/15373771
|
||||
# Replaces '/' separator with proper '\' in case running under MSYS2 env.
|
||||
|
||||
def utilsGetWinFullPath(path_arg: str) -> str:
|
||||
return '\\\\?\\' + path_arg.replace("/", "\\")
|
||||
|
||||
def utilsResetOutputDir() -> None:
|
||||
global g_outputDir
|
||||
global g_logPath
|
||||
#assert g_tkDirText is not None
|
||||
|
||||
g_outputDir = g_tkDirText.get('1.0', tk.END).strip()
|
||||
if not g_outputDir:
|
||||
# We should never reach this, honestly.
|
||||
messagebox.showerror('Error', 'You must provide an output directory!', parent=g_tkRoot)
|
||||
return
|
||||
|
||||
if g_logToFileBoolVar.get():
|
||||
g_logPath = os.path.abspath(g_outputDir + os.path.sep + \
|
||||
"nxdt_host_" + datetime.now().strftime('%Y-%m-%d_%H%M%S') + '.log')
|
||||
if g_isWindows:
|
||||
g_logPath = utilsGetWinFullPath(g_logPath)
|
||||
print(g_logPath)
|
||||
else:
|
||||
print("Not logging to file.")
|
||||
|
||||
# Make sure the full directory tree exists.
|
||||
try:
|
||||
os.makedirs(g_outputDir, exist_ok=True)
|
||||
except:
|
||||
utilsLogException(traceback.format_exc())
|
||||
messagebox.showerror('Error', 'Unable to create full output directory tree!', parent=g_tkRoot)
|
||||
return
|
||||
|
||||
return
|
||||
def utilsIsValueAlignedToEndpointPacketSize(value: int) -> bool:
|
||||
return bool((value & (g_usbEpMaxPacketSize - 1)) == 0)
|
||||
|
||||
|
@ -787,9 +835,9 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | 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.
|
||||
# Unconditionally enable long paths in Windows.
|
||||
if g_isWindows:
|
||||
fullpath = '\\\\?\\' + fullpath.replace("/", "\\")
|
||||
fullpath = utilsGetWinFullPath(fullpath);
|
||||
|
||||
# Get parent directory path.
|
||||
dirpath = os.path.dirname(fullpath)
|
||||
|
@ -827,10 +875,6 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None:
|
|||
file = g_nspFile
|
||||
fullpath = g_nspFilePath
|
||||
|
||||
# Unconditionally enable 32-bit paths on Windows.
|
||||
if g_isWindows:
|
||||
fullpath = '\\\\?\\' + fullpath.replace("/", "\\")
|
||||
|
||||
dirpath = os.path.dirname(fullpath)
|
||||
|
||||
# Check if we're dealing with an empty file or with the first SendFileProperties command from a NSP.
|
||||
|
@ -1125,29 +1169,26 @@ def uiStopServer() -> None:
|
|||
# Signal the shared stop event.
|
||||
#assert g_stopEvent is not None
|
||||
g_stopEvent.set()
|
||||
# Log the end of the session.
|
||||
g_logger.info("\nServer stopped.\n")
|
||||
|
||||
|
||||
def uiStartServer() -> None:
|
||||
global g_outputDir
|
||||
|
||||
#assert g_tkDirText is not None
|
||||
|
||||
g_outputDir = g_tkDirText.get('1.0', tk.END).strip()
|
||||
if not g_outputDir:
|
||||
# We should never reach this, honestly.
|
||||
messagebox.showerror('Error', 'You must provide an output directory!', parent=g_tkRoot)
|
||||
return
|
||||
|
||||
# Make sure the full directory tree exists.
|
||||
try:
|
||||
os.makedirs(g_outputDir, exist_ok=True)
|
||||
except:
|
||||
utilsLogException(traceback.format_exc())
|
||||
messagebox.showerror('Error', 'Unable to create full output directory tree!', parent=g_tkRoot)
|
||||
return
|
||||
# Reset output path variable based on possible user input unconditionally.
|
||||
utilsResetOutputDir()
|
||||
|
||||
# Update UI.
|
||||
uiToggleElements(False)
|
||||
|
||||
# Log basic session status and info.
|
||||
g_logger.info('\nServer started.')
|
||||
g_logger.info('Output Dir:\t' + g_outputDir)
|
||||
if g_logToFileBoolVar.get():
|
||||
g_logger.info('Logging to:\t' + g_logPath.rsplit('\\', 1)[-1] + '\n')
|
||||
else:
|
||||
g_logger.info('Logging to file disabled.\n')
|
||||
|
||||
# Create background server thread.
|
||||
server_thread = threading.Thread(target=usbCommandHandler, daemon=True)
|
||||
server_thread.start()
|
||||
|
@ -1157,6 +1198,7 @@ def uiToggleElements(flag: bool) -> None:
|
|||
#assert g_tkChooseDirButton is not None
|
||||
#assert g_tkServerButton is not None
|
||||
#assert g_tkCanvas is not None
|
||||
#assert g_tkLogToFileCheckbox is not None
|
||||
#assert g_tkVerboseCheckbox is not None
|
||||
|
||||
if flag:
|
||||
|
@ -1165,7 +1207,7 @@ def uiToggleElements(flag: bool) -> None:
|
|||
g_tkChooseDirButton.configure(state='normal')
|
||||
g_tkServerButton.configure(text='Start server', command=uiStartServer, state='normal')
|
||||
g_tkCanvas.itemconfigure(g_tkTipMessage, state='hidden', text='')
|
||||
|
||||
g_tkLogToFileCheckbox.configure(state='normal')
|
||||
g_tkVerboseCheckbox.configure(state='normal')
|
||||
else:
|
||||
#assert g_tkScrolledTextLog is not None
|
||||
|
@ -1179,7 +1221,7 @@ def uiToggleElements(flag: bool) -> None:
|
|||
g_tkScrolledTextLog.configure(state='normal')
|
||||
g_tkScrolledTextLog.delete('1.0', tk.END)
|
||||
g_tkScrolledTextLog.configure(state='disabled')
|
||||
|
||||
g_tkLogToFileCheckbox.configure(state='disabled')
|
||||
g_tkVerboseCheckbox.configure(state='disabled')
|
||||
|
||||
def uiChooseDirectory() -> None:
|
||||
|
@ -1210,15 +1252,15 @@ def uiHandleVerboseCheckbox() -> None:
|
|||
g_logger.setLevel(g_logLevelIntVar.get())
|
||||
|
||||
def uiInitialize() -> None:
|
||||
global SCALE, g_logLevelIntVar
|
||||
global g_tkRoot, g_tkCanvas, g_tkDirText, g_tkChooseDirButton, g_tkServerButton, g_tkTipMessage, g_tkScrolledTextLog, g_tkVerboseCheckbox
|
||||
global SCALE, g_logLevelIntVar, g_logToFileBoolVar
|
||||
global g_tkRoot, g_tkCanvas, g_tkDirText, g_tkChooseDirButton, g_tkServerButton, g_tkTipMessage, g_tkScrolledTextLog, g_tkLogToFileCheckbox, g_tkVerboseCheckbox
|
||||
global g_stopEvent, g_tlb, g_taskbar, g_progressBarWindow
|
||||
|
||||
# Setup thread event.
|
||||
g_stopEvent = threading.Event()
|
||||
|
||||
# Enable high DPI scaling under Windows (if possible).
|
||||
# This will remove the blur caused by bilineal filtering when automatic scaling is carried out by Windows itself.
|
||||
# This will remove the blur caused by bilinear filtering when automatic scaling is carried out by Windows itself.
|
||||
dpi_aware = False
|
||||
if g_isWindowsVista:
|
||||
try:
|
||||
|
@ -1299,7 +1341,7 @@ def uiInitialize() -> None:
|
|||
g_tkCanvas = tk.Canvas(g_tkRoot, width=window_width_px, height=window_height_px)
|
||||
g_tkCanvas.pack()
|
||||
|
||||
g_tkCanvas.create_text(uiScaleMeasure(60), uiScaleMeasure(30), text='Output directory:', anchor=tk.CENTER)
|
||||
g_tkCanvas.create_text(uiScaleMeasure(60), uiScaleMeasure(30), text='Output Dir:', anchor=tk.CENTER)
|
||||
|
||||
g_tkDirText = tk.Text(g_tkRoot, height=1, width=45, font=default_font, wrap='none', state='disabled', bg='#F0F0F0')
|
||||
uiUpdateDirectoryField(g_outputDir)
|
||||
|
@ -1322,11 +1364,18 @@ def uiInitialize() -> None:
|
|||
g_tkScrolledTextLog.tag_config('CRITICAL', foreground='red', underline=True)
|
||||
g_tkCanvas.create_window(uiScaleMeasure(int(WINDOW_WIDTH / 2)), uiScaleMeasure(280), window=g_tkScrolledTextLog, anchor=tk.CENTER)
|
||||
|
||||
g_tkCanvas.create_text(uiScaleMeasure(5), uiScaleMeasure(WINDOW_HEIGHT - 10), text=COPYRIGHT_TEXT, anchor=tk.W)
|
||||
g_tkCanvas.create_text(uiScaleMeasure(5), uiScaleMeasure(WINDOW_HEIGHT - 13), text=COPYRIGHT_TEXT, anchor=tk.W)
|
||||
|
||||
g_logToFileBoolVar = tk.BooleanVar()
|
||||
g_tkLogToFileCheckbox = tk.Checkbutton(g_tkRoot, text='Log to file', variable=g_logToFileBoolVar,onvalue=True, offvalue=False)
|
||||
g_tkLogToFileCheckbox.select()
|
||||
|
||||
g_tkCanvas.create_window(uiScaleMeasure(WINDOW_WIDTH - 165), uiScaleMeasure(WINDOW_HEIGHT - 13), window=g_tkLogToFileCheckbox, anchor=tk.CENTER)
|
||||
|
||||
g_logLevelIntVar = tk.IntVar()
|
||||
g_tkVerboseCheckbox = tk.Checkbutton(g_tkRoot, text='Verbose output', variable=g_logLevelIntVar, onvalue=logging.DEBUG, offvalue=logging.INFO, command=uiHandleVerboseCheckbox)
|
||||
g_tkCanvas.create_window(uiScaleMeasure(WINDOW_WIDTH - 55), uiScaleMeasure(WINDOW_HEIGHT - 10), window=g_tkVerboseCheckbox, anchor=tk.CENTER)
|
||||
|
||||
g_tkCanvas.create_window(uiScaleMeasure(WINDOW_WIDTH - 55), uiScaleMeasure(WINDOW_HEIGHT - 13), window=g_tkVerboseCheckbox, anchor=tk.CENTER)
|
||||
|
||||
# Initialize console logger.
|
||||
console = LogConsole(g_tkScrolledTextLog)
|
||||
|
@ -1353,7 +1402,7 @@ def cliInitialize() -> None:
|
|||
|
||||
# Print info.
|
||||
g_logger.info('\n' + SCRIPT_TITLE + '. ' + COPYRIGHT_TEXT + '.')
|
||||
g_logger.info('Output directory: "' + g_outputDir + '".\n')
|
||||
g_logger.info('Output Dir: "' + g_outputDir + '".\n')
|
||||
|
||||
# Start USB command handler directly.
|
||||
usbCommandHandler()
|
||||
|
@ -1414,7 +1463,8 @@ if __name__ == "__main__":
|
|||
ret = main()
|
||||
except KeyboardInterrupt:
|
||||
time.sleep(0.2)
|
||||
print('\nScript interrupted.')
|
||||
print("\nHost script interrupted!")
|
||||
|
||||
except Exception as e:
|
||||
utilsLogException(traceback.format_exc())
|
||||
|
||||
|
|
Loading…
Reference in a new issue