New GUI, lots of code restructuring
This commit is contained in:
parent
a6b20ae53e
commit
12d031d2ec
6 changed files with 1592 additions and 366 deletions
803
GeckoLoader.py
803
GeckoLoader.py
|
@ -1,15 +1,26 @@
|
|||
#Written by JoshuaMK 2020
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import sys
|
||||
import atexit
|
||||
import logging
|
||||
import os
|
||||
import pickle as cPickle
|
||||
import re
|
||||
import shutil
|
||||
import signal
|
||||
import sys
|
||||
import tempfile
|
||||
from contextlib import redirect_stdout, redirect_stderr
|
||||
from distutils.version import LooseVersion
|
||||
from io import StringIO
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from children_ui import PrefWindow, SettingsWindow
|
||||
from dolreader import DolFile
|
||||
from fileutils import resource_path, get_program_folder
|
||||
from kernel import CodeHandler, KernelLoader
|
||||
from main_ui import MainWindow
|
||||
from tools import CommandLineParser, color_text
|
||||
from versioncheck import Updater
|
||||
|
||||
|
@ -34,30 +45,16 @@ except ImportError:
|
|||
TRED = ''
|
||||
TREDLIT = ''
|
||||
|
||||
__version__ = 'v6.2.0'
|
||||
__version__ = "v6.3.0"
|
||||
|
||||
def resource_path(relative_path: str = ""):
|
||||
""" Get absolute path to resource, works for dev and for PyInstaller """
|
||||
base_path = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
return os.path.join(base_path, relative_path)
|
||||
|
||||
TMPDIR = resource_path(''.join(random.choice('1234567890-_abcdefghijklomnpqrstuvwxyz') for i in range(6)) + '-GeckoLoader')
|
||||
|
||||
def sort_file_args(fileA, fileB):
|
||||
if os.path.splitext(fileA)[1].lower() == '.dol':
|
||||
dolFile = fileA
|
||||
gctFile = fileB
|
||||
elif os.path.splitext(fileB)[1].lower() == '.dol':
|
||||
dolFile = fileB
|
||||
gctFile = fileA
|
||||
else:
|
||||
parser.error(color_text('No dol file was passed\n', defaultColor=TREDLIT))
|
||||
return dolFile, gctFile
|
||||
TMPDIR = tempfile.mkdtemp("GeckoLoader-")
|
||||
|
||||
@atexit.register
|
||||
def clean_tmp_resources():
|
||||
if os.path.isdir(TMPDIR):
|
||||
shutil.rmtree(TMPDIR)
|
||||
tmpfolder = os.path.dirname(TMPDIR)
|
||||
for entry in os.listdir(tmpfolder):
|
||||
if entry.startswith("GeckoLoader-"):
|
||||
shutil.rmtree(entry, ignore_errors=True)
|
||||
|
||||
class GeckoLoaderCli(CommandLineParser):
|
||||
|
||||
|
@ -69,34 +66,34 @@ class GeckoLoaderCli(CommandLineParser):
|
|||
self.add_argument('dolfile', help='DOL file')
|
||||
self.add_argument('codelist', help='Folder or Gecko GCT|TXT file')
|
||||
self.add_argument('-a', '--alloc',
|
||||
help='Define the size of the code allocation in hex, only applies when using the ARENA space',
|
||||
metavar ='SIZE')
|
||||
help='Define the size of the code allocation in hex, only applies when using the ARENA space',
|
||||
metavar ='SIZE')
|
||||
self.add_argument('-i', '--init',
|
||||
help='Define where GeckoLoader is initialized in hex',
|
||||
metavar='ADDRESS')
|
||||
help='Define where GeckoLoader is initialized in hex',
|
||||
metavar='ADDRESS')
|
||||
self.add_argument('-m', '--movecodes',
|
||||
help='''["AUTO", "LEGACY", "ARENA"] Choose if GeckoLoader moves the codes to OSArenaHi,
|
||||
or the legacy space. Default is "AUTO",
|
||||
which auto decides where to insert the codes''',
|
||||
default='AUTO',
|
||||
choices=['AUTO', 'LEGACY', 'ARENA'],
|
||||
metavar='TYPE')
|
||||
help='''["AUTO", "LEGACY", "ARENA"] Choose if GeckoLoader moves the codes to OSArenaHi,
|
||||
or the legacy space. Default is "AUTO",
|
||||
which auto decides where to insert the codes''',
|
||||
default='AUTO',
|
||||
choices=['AUTO', 'LEGACY', 'ARENA'],
|
||||
metavar='TYPE')
|
||||
self.add_argument('-tc', '--txtcodes',
|
||||
help='''["ACTIVE", "ALL"] What codes get parsed when a txt file is used.
|
||||
"ALL" makes all codes get parsed,
|
||||
"ACTIVE" makes only activated codes get parsed.
|
||||
"ACTIVE" is the default''',
|
||||
default='ACTIVE',
|
||||
metavar='TYPE')
|
||||
help='''["ACTIVE", "ALL"] What codes get parsed when a txt file is used.
|
||||
"ALL" makes all codes get parsed,
|
||||
"ACTIVE" makes only activated codes get parsed.
|
||||
"ACTIVE" is the default''',
|
||||
default='ACTIVE',
|
||||
metavar='TYPE')
|
||||
self.add_argument('--handler',
|
||||
help='''["MINI", "FULL"] Which codehandler gets used. "MINI" uses a smaller codehandler
|
||||
which only supports (0x, 2x, Cx, and E0 types) and supports up to
|
||||
600 lines of gecko codes when using the legacy codespace.
|
||||
"FULL" is the standard codehandler, supporting up to 350 lines of code
|
||||
in the legacy codespace. "FULL" is the default''',
|
||||
default='FULL',
|
||||
choices=['MINI', 'FULL'],
|
||||
metavar='TYPE')
|
||||
help='''["MINI", "FULL"] Which codehandler gets used. "MINI" uses a smaller codehandler
|
||||
which only supports (0x, 2x, Cx, and E0 types) and supports up to
|
||||
600 lines of gecko codes when using the legacy codespace.
|
||||
"FULL" is the standard codehandler, supporting up to 350 lines of code
|
||||
in the legacy codespace. "FULL" is the default''',
|
||||
default='FULL',
|
||||
choices=['MINI', 'FULL'],
|
||||
metavar='TYPE')
|
||||
self.add_argument('--hooktype',
|
||||
help='''["VI", "GX", "PAD"] The type of hook used for the RAM search. "VI" or "GX" are recommended,
|
||||
although "PAD" can work just as well. "VI" is the default''',
|
||||
|
@ -117,16 +114,17 @@ class GeckoLoaderCli(CommandLineParser):
|
|||
self.add_argument('--dest',
|
||||
help='Target path to put the modified DOL, can be a folder or file',
|
||||
metavar='PATH')
|
||||
self.add_argument('--check-update',
|
||||
self.add_argument('--checkupdate',
|
||||
help='''Checks to see if a new update exists on the GitHub Repository releases page,
|
||||
this option overrides all other commands.''',
|
||||
action='store_true')
|
||||
self.add_argument('--splash',
|
||||
help='''Print the splash screen, this option overrides
|
||||
all other commands excluding --checkupdate''',
|
||||
action='store_true')
|
||||
self.add_argument('--encrypt',
|
||||
help='Encrypts the codelist on compile time, helping to slow the snoopers',
|
||||
action='store_true')
|
||||
self.add_argument('--disablecolor',
|
||||
help='Disables the colorization of text output',
|
||||
action='store_true')
|
||||
self.add_argument('-q', '--quiet',
|
||||
help='Print nothing to the console',
|
||||
action='store_true')
|
||||
|
@ -135,7 +133,7 @@ class GeckoLoaderCli(CommandLineParser):
|
|||
default=0,
|
||||
action='count')
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self.__doc__
|
||||
|
||||
def print_splash(self):
|
||||
|
@ -170,8 +168,6 @@ class GeckoLoaderCli(CommandLineParser):
|
|||
for line in logo:
|
||||
print(color_text(line, [('║', TREDLIT), ('╔╚╝╗═', TRED)], TGREENLIT))
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
def check_updates(self):
|
||||
repoChecker = Updater('JoshuaMKW', 'GeckoLoader')
|
||||
|
||||
|
@ -193,89 +189,656 @@ class GeckoLoaderCli(CommandLineParser):
|
|||
print(color_text(f' :: Current version is "{self.__version__}(release)", Most recent version is "{tag}(release)"', defaultColor=TGREENLIT))
|
||||
|
||||
print('')
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = GeckoLoaderCli('GeckoLoader', __version__, description='Dol editing tool for allocating extended codespace')
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_splash()
|
||||
elif '--check-update' in sys.argv:
|
||||
parser.check_updates()
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.alloc:
|
||||
try:
|
||||
_allocation = int(args.alloc, 16)
|
||||
except ValueError:
|
||||
parser.error(color_text('The allocation was invalid\n', defaultColor=TREDLIT))
|
||||
else:
|
||||
_allocation = None
|
||||
|
||||
if args.hookaddress:
|
||||
if 0x80000000 > int(args.hookaddress, 16) >= 0x81800000:
|
||||
parser.error(color_text('The codehandler hook address was beyond bounds\n', defaultColor=TREDLIT))
|
||||
else:
|
||||
def _validate_args(self, args) -> tuple:
|
||||
if args.alloc:
|
||||
try:
|
||||
_codehook = int(args.hookaddress, 16)
|
||||
_allocation = int(args.alloc, 16)
|
||||
except ValueError:
|
||||
parser.error(color_text('The codehandler hook address was invalid\n', defaultColor=TREDLIT))
|
||||
else:
|
||||
_codehook = None
|
||||
self.error(color_text('The allocation was invalid\n', defaultColor=TREDLIT))
|
||||
else:
|
||||
_allocation = None
|
||||
|
||||
if args.handler == 'MINI':
|
||||
codeHandlerFile = 'codehandler-mini.bin'
|
||||
else:
|
||||
codeHandlerFile = 'codehandler.bin'
|
||||
if args.hookaddress:
|
||||
if 0x80000000 > int(args.hookaddress, 16) >= 0x81800000:
|
||||
self.error(color_text('The codehandler hook address was beyond bounds\n', defaultColor=TREDLIT))
|
||||
else:
|
||||
try:
|
||||
_codehook = int(args.hookaddress, 16)
|
||||
except ValueError:
|
||||
self.error(color_text('The codehandler hook address was invalid\n', defaultColor=TREDLIT))
|
||||
else:
|
||||
_codehook = None
|
||||
|
||||
if args.handler == CodeHandler.Types.MINI:
|
||||
codeHandlerFile = 'codehandler-mini.bin'
|
||||
elif args.handler == CodeHandler.Types.FULL:
|
||||
codeHandlerFile = 'codehandler.bin'
|
||||
else:
|
||||
self.error(color_text(f'Codehandler type {args.handler} is invalid\n', defaultColor=TREDLIT))
|
||||
|
||||
try:
|
||||
if not os.path.isfile(args.dolfile):
|
||||
parser.error(color_text(f'File "{args.dolfile}" does not exist\n', defaultColor=TREDLIT))
|
||||
self.error(color_text(f'File "{args.dolfile}" does not exist\n', defaultColor=TREDLIT))
|
||||
|
||||
if not os.path.exists(args.codelist):
|
||||
parser.error(color_text(f'File/folder "{args.codelist}" does not exist\n', defaultColor=TREDLIT))
|
||||
self.error(color_text(f'File/folder "{args.codelist}" does not exist\n', defaultColor=TREDLIT))
|
||||
|
||||
with open(os.path.normpath(args.dolfile), 'rb') as dol:
|
||||
dolFile = DolFile(dol)
|
||||
return _allocation, _codehook, codeHandlerFile
|
||||
|
||||
with open(resource_path(os.path.join('bin', os.path.normpath(codeHandlerFile))), 'rb') as handler:
|
||||
codeHandler = CodeHandler(handler)
|
||||
codeHandler.allocation = _allocation
|
||||
codeHandler.hookAddress = _codehook
|
||||
codeHandler.hookType = args.hooktype
|
||||
codeHandler.includeAll = args.txtcodes.lower() == 'all'
|
||||
codeHandler.optimizeList = args.optimize
|
||||
def _exec(self, args, tmpdir):
|
||||
if not os.path.isabs(args.dolfile):
|
||||
args.dolfile = os.path.abspath(args.dolfile)
|
||||
|
||||
with open(resource_path(os.path.join('bin', 'geckoloader.bin')), 'rb') as kernelfile:
|
||||
geckoKernel = KernelLoader(kernelfile, parser)
|
||||
|
||||
if args.init is not None:
|
||||
geckoKernel.initAddress = int(args.init, 16)
|
||||
|
||||
geckoKernel.patchJob = args.movecodes
|
||||
geckoKernel.verbosity = args.verbose
|
||||
geckoKernel.quiet = args.quiet
|
||||
geckoKernel.encrypt = args.encrypt
|
||||
geckoKernel.protect = args.protect
|
||||
if not os.path.isabs(args.codelist):
|
||||
args.codelist = os.path.abspath(args.codelist)
|
||||
|
||||
if args.dest:
|
||||
if os.path.splitext(args.dest)[1] == "":
|
||||
dest = os.path.normpath(os.path.join(os.getcwd(), args.dest.lstrip('\\').lstrip('/'), os.path.basename(args.dolfile)))
|
||||
if not os.path.isabs(args.dest):
|
||||
args.dest = os.path.abspath(args.dest)
|
||||
|
||||
_allocation, _codehook, codeHandlerFile = self._validate_args(args)
|
||||
|
||||
try:
|
||||
with open(os.path.normpath(args.dolfile), 'rb') as dol:
|
||||
dolFile = DolFile(dol)
|
||||
|
||||
with open(resource_path(os.path.join('bin', os.path.normpath(codeHandlerFile))), 'rb') as handler:
|
||||
codeHandler = CodeHandler(handler)
|
||||
codeHandler.allocation = _allocation
|
||||
codeHandler.hookAddress = _codehook
|
||||
codeHandler.hookType = args.hooktype
|
||||
codeHandler.includeAll = args.txtcodes.lower() == 'all'
|
||||
codeHandler.optimizeList = args.optimize
|
||||
|
||||
with open(resource_path(os.path.join('bin', 'geckoloader.bin')), 'rb') as kernelfile:
|
||||
geckoKernel = KernelLoader(kernelfile, cli)
|
||||
|
||||
if args.init is not None:
|
||||
geckoKernel.initAddress = int(args.init, 16)
|
||||
|
||||
geckoKernel.patchJob = args.movecodes
|
||||
geckoKernel.verbosity = args.verbose
|
||||
geckoKernel.quiet = args.quiet
|
||||
geckoKernel.encrypt = args.encrypt
|
||||
geckoKernel.protect = args.protect
|
||||
|
||||
if args.dest:
|
||||
if not os.path.isabs(args.dest):
|
||||
if os.path.splitext(args.dest)[1] == "":
|
||||
dest = os.path.normpath(os.path.join(os.getcwd(), args.dest.lstrip('.').lstrip('\\').lstrip('/'), os.path.basename(args.dolfile)))
|
||||
else:
|
||||
dest = os.path.normpath(os.path.join(os.getcwd(), args.dest.lstrip('.').lstrip('\\').lstrip('/')))
|
||||
else:
|
||||
if os.path.splitext(args.dest)[1] == "":
|
||||
dest = os.path.normpath(os.path.join(args.dest.lstrip('.').lstrip('\\').lstrip('/'), os.path.basename(args.dolfile)))
|
||||
else:
|
||||
dest = os.path.normpath(os.path.join(args.dest.lstrip('.').lstrip('\\').lstrip('/')))
|
||||
else:
|
||||
dest = os.path.normpath(os.path.join(os.getcwd(), args.dest.lstrip('\\').lstrip('/')))
|
||||
dest = os.path.normpath(os.path.join(os.getcwd(), "geckoloader-build", os.path.basename(args.dolfile)))
|
||||
|
||||
if not os.path.exists(dest) and os.path.dirname(dest) not in ('', '/'):
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
|
||||
geckoKernel.build(args.codelist, dolFile, codeHandler, TMPDIR, dest)
|
||||
|
||||
except FileNotFoundError as e:
|
||||
self.error(color_text(e, defaultColor=TREDLIT))
|
||||
|
||||
class GUI(object):
|
||||
|
||||
class Dialogs:
|
||||
LOAD_DEST = 0
|
||||
LOAD_GCT = 1
|
||||
LOAD_FOLDER = 2
|
||||
LOAD_DOL = 3
|
||||
LOAD_SESSION = 4
|
||||
SAVE_SESSION = 5
|
||||
SAVE_SESSION_AS = 6
|
||||
|
||||
def __init__(self, cli: GeckoLoaderCli):
|
||||
self.cli = cli
|
||||
self.app = None
|
||||
self.ui = None
|
||||
self.uiprefs = None
|
||||
self.uiexSettings = None
|
||||
self.dolPath = None
|
||||
self.codePath = [None, None]
|
||||
self.destPath = None
|
||||
self.sessionPath = None
|
||||
self.prefs = {"qtstyle": "Default", "darktheme": False}
|
||||
self.style_log = []
|
||||
|
||||
self.log = logging.getLogger(f"GeckoLoader {self.cli.__version__}")
|
||||
|
||||
hdlr = logging.FileHandler(os.path.join(get_program_folder("GeckoLoader"), "error.log"))
|
||||
formatter = logging.Formatter("\n%(levelname)s (%(asctime)s): %(message)s")
|
||||
hdlr.setFormatter(formatter)
|
||||
self.log.addHandler(hdlr)
|
||||
|
||||
def show_dialog(self, dialog_type=None):
|
||||
if dialog_type == "aboutqt":
|
||||
QtWidgets.QMessageBox.aboutQt(self.app.activeWindow())
|
||||
elif dialog_type == "aboutGeckoLoader":
|
||||
desc = "".join([ "GeckoLoader is a cross platform tool designed to give ",
|
||||
"the user the most efficient codespace usage possible.\n\n ",
|
||||
"This application supports various features, such as ",
|
||||
"pre-patching codes, dynamic codehandler hooks, codespace ",
|
||||
"extension through memory reallocation, multiple patching ",
|
||||
"of a single DOL, and more.\n\n",
|
||||
f"Current running version: {self.cli.__version__}\n\n"
|
||||
"Copyright (c) 2020\n\n",
|
||||
"JoshuaMK <joshuamkw2002@gmail.com> \n\n",
|
||||
"All rights reserved." ])
|
||||
|
||||
QtWidgets.QMessageBox.about(self.app.activeWindow(), "About GeckoLoader", desc)
|
||||
elif dialog_type == "Preferences":
|
||||
self.uiprefs.show()
|
||||
else:
|
||||
dest = os.path.normpath(os.path.join(os.getcwd(), "geckoloader-build", os.path.basename(args.dolfile)))
|
||||
self.uiexSettings.show()
|
||||
|
||||
if not os.path.exists(dest) and os.path.dirname(dest) not in ('', '/'):
|
||||
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
||||
def _open_dol(self) -> tuple:
|
||||
if self.dolPath is None: # Just start in the home directory
|
||||
fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open DOL", os.path.expanduser("~"),
|
||||
"Nintendo DOL Executable (*.dol);;All files (*)")[0])
|
||||
else: # Start in the last directory used by the user
|
||||
fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open DOL", os.path.split(self.dolPath)[0],
|
||||
"Nintendo DOL Executable (*.dol);;All files (*)")[0])
|
||||
|
||||
if not os.path.isdir(TMPDIR):
|
||||
os.mkdir(TMPDIR)
|
||||
if fname == "" or fname is None: # Make sure we have something to open
|
||||
return False, None
|
||||
else:
|
||||
self.dolPath = os.path.normpath(fname)
|
||||
|
||||
geckoKernel.build(args.codelist, dolFile, codeHandler, TMPDIR, dest)
|
||||
if os.path.isfile(self.dolPath):
|
||||
self.ui.dolTextBox.setText(self.dolPath)
|
||||
return True, None
|
||||
else:
|
||||
return False, "The file does not exist!"
|
||||
|
||||
def _load_codes(self, isFolder: bool=False) -> tuple:
|
||||
if not isFolder:
|
||||
if self.codePath[0] is None: # Just start in the home directory
|
||||
fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open Codelist", os.path.expanduser("~"),
|
||||
"Gecko Code Table (*.gct);;Gecko Codelist (*.txt);;All files (*)")[0])
|
||||
else: # Start in the last directory used by the user
|
||||
fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open Codelist", os.path.split(self.codePath[0])[0],
|
||||
"Gecko Code Table (*.gct);;Gecko Codelist (*.txt);;All files (*)")[0])
|
||||
else:
|
||||
if self.codePath[0] is None: # Just start in the home directory
|
||||
fname = str(QtWidgets.QFileDialog.getExistingDirectory(self.ui, "Open Codelist", os.path.expanduser("~"),
|
||||
QtWidgets.QFileDialog.ShowDirsOnly))
|
||||
else: # Start in the last directory used by the user
|
||||
fname = str(QtWidgets.QFileDialog.getExistingDirectory(self.ui, "Open Codelist", os.path.split(self.codePath[0])[0],
|
||||
QtWidgets.QFileDialog.ShowDirsOnly))
|
||||
|
||||
if fname == "" or fname is None: # Make sure we have something to open
|
||||
return False, None
|
||||
else:
|
||||
self.codePath = [os.path.normpath(fname), isFolder]
|
||||
|
||||
if not isFolder:
|
||||
self.ui.gctFileTextBox.setText(self.codePath[0])
|
||||
self.ui.gctFolderTextBox.setText("")
|
||||
else:
|
||||
self.ui.gctFileTextBox.setText("")
|
||||
self.ui.gctFolderTextBox.setText(self.codePath[0])
|
||||
|
||||
return True, None
|
||||
|
||||
def _open_dest(self) -> tuple:
|
||||
if self.dolPath is None: # Just start in the home directory
|
||||
fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open DOL", os.path.expanduser("~"),
|
||||
"Nintendo DOL Executable (*.dol);;All files (*)")[0])
|
||||
else: # Start in the last directory used by the user
|
||||
fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open DOL", os.path.split(self.dolPath)[0],
|
||||
"Nintendo DOL Executable (*.dol);;All files (*)")[0])
|
||||
|
||||
if fname == "" or fname is None: # Make sure we have something to open
|
||||
return False, None
|
||||
else:
|
||||
self.destPath = os.path.normpath(fname)
|
||||
self.ui.destTextBox.setText(self.destPath)
|
||||
|
||||
return True, None
|
||||
|
||||
def _load_session(self) -> tuple:
|
||||
if self.sessionPath is None:
|
||||
fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open Session", os.path.expanduser("~"),
|
||||
"GeckoLoader Session (*.gprf);;All files (*)")[0])
|
||||
else: # Start in the last directory used by the user
|
||||
fname = str(QtWidgets.QFileDialog.getOpenFileName(self.ui, "Open Session", os.path.split(self.sessionPath)[0],
|
||||
"GeckoLoader Session (*.gprf);;All files (*)")[0])
|
||||
|
||||
if fname == "" or fname is None: # Make sure we have something to open
|
||||
return False, None
|
||||
else:
|
||||
self.sessionPath = os.path.normpath(fname)
|
||||
|
||||
with open(self.sessionPath, "rb") as session:
|
||||
p = cPickle.load(session)
|
||||
|
||||
self.ui.dolTextBox.setText(p["dolPath"])
|
||||
|
||||
if p["gctFilePath"] != "":
|
||||
self.ui.gctFileTextBox.setText(p["gctFilePath"])
|
||||
self.ui.gctFolderTextBox.setText("")
|
||||
elif p["gctFolderPath"] != "":
|
||||
self.ui.gctFileTextBox.setText("")
|
||||
self.ui.gctFolderTextBox.setText(p["gctFolderPath"])
|
||||
else:
|
||||
self.ui.gctFileTextBox.setText("")
|
||||
self.ui.gctFolderTextBox.setText("")
|
||||
|
||||
self.ui.destTextBox.setText(p["destPath"])
|
||||
self.ui.allocLineEdit.setText(p["alloc"])
|
||||
self.ui.patchTypeSelect.setCurrentIndex(p["patchIndex"])
|
||||
self.ui.handlerTypeSelect.setCurrentIndex(p["handlerIndex"])
|
||||
self.ui.hookTypeSelect.setCurrentIndex(p["hookIndex"])
|
||||
self.ui.txtCodesIncludeSelect.setCurrentIndex(p["txtIndex"])
|
||||
self.ui.optimizeSelect.setCurrentIndex(p["optimizeIndex"])
|
||||
self.uiexSettings.protectCodes.setChecked(p["protect"])
|
||||
self.uiexSettings.encryptCodes.setChecked(p["encrypt"])
|
||||
self.uiexSettings.codehookLineEdit.setText(p["hookAddress"])
|
||||
self.uiexSettings.kernelHookLineEdit.setText(p["initAddress"])
|
||||
self.uiexSettings.verbositySelect.setCurrentIndex(p["verbosity"])
|
||||
|
||||
return True, None
|
||||
|
||||
def _save_session(self, saveAs=False):
|
||||
if saveAs or self.sessionPath is None or self.sessionPath == "":
|
||||
if self.sessionPath is None: # Just start in the home directory
|
||||
fname = str(QtWidgets.QFileDialog.getSaveFileName(self.ui, "Save Session", os.path.expanduser("~"),
|
||||
"GeckoLoader Session (*.gprf);;All files (*)")[0])
|
||||
else: # Start in the last directory used by the user
|
||||
fname = str(QtWidgets.QFileDialog.getSaveFileName(self.ui, "Save Session", os.path.split(self.dolPath)[0],
|
||||
"GeckoLoader Session (*.gprf);;All files (*)")[0])
|
||||
|
||||
if fname == "" or fname is None: # Make sure we have something to open
|
||||
return False, None
|
||||
else:
|
||||
self.sessionPath = os.path.normpath(fname)
|
||||
|
||||
try:
|
||||
with open(self.sessionPath, "wb") as session:
|
||||
p = {}
|
||||
|
||||
p["dolPath"] = self.ui.dolTextBox.text().strip()
|
||||
p["gctFilePath"] = self.ui.gctFileTextBox.text().strip()
|
||||
p["gctFolderPath"] = self.ui.gctFolderTextBox.text().strip()
|
||||
p["destPath"] = self.ui.destTextBox.text().strip()
|
||||
p["alloc"] = self.ui.allocLineEdit.text().strip()
|
||||
p["patchIndex"] = self.ui.patchTypeSelect.currentIndex()
|
||||
p["handlerIndex"] = self.ui.handlerTypeSelect.currentIndex()
|
||||
p["hookIndex"] = self.ui.hookTypeSelect.currentIndex()
|
||||
p["txtIndex"] = self.ui.txtCodesIncludeSelect.currentIndex()
|
||||
p["optimizeIndex"] = self.ui.optimizeSelect.currentIndex()
|
||||
p["protect"] = self.uiexSettings.protectCodes.isChecked()
|
||||
p["encrypt"] = self.uiexSettings.encryptCodes.isChecked()
|
||||
p["hookAddress"] = self.uiexSettings.codehookLineEdit.text().strip()
|
||||
p["initAddress"] = self.uiexSettings.kernelHookLineEdit.text().strip()
|
||||
p["verbosity"] = self.uiexSettings.verbositySelect.currentIndex()
|
||||
|
||||
try:
|
||||
cPickle.dump(p, session)
|
||||
except cPickle.PicklingError as e:
|
||||
return False, str(e)
|
||||
|
||||
return True, None
|
||||
except (IOError, PermissionError) as e:
|
||||
return False, str(e)
|
||||
|
||||
def file_dialog_exec(self, event: Dialogs):
|
||||
try:
|
||||
if event == GUI.Dialogs.LOAD_DOL:
|
||||
status, msg = self._open_dol()
|
||||
elif event == GUI.Dialogs.LOAD_GCT:
|
||||
status, msg = self._load_codes(False)
|
||||
elif event == GUI.Dialogs.LOAD_FOLDER:
|
||||
status, msg = self._load_codes(True)
|
||||
elif event == GUI.Dialogs.LOAD_DEST:
|
||||
status, msg = self._open_dest()
|
||||
elif event == GUI.Dialogs.LOAD_SESSION:
|
||||
status, msg = self._load_session()
|
||||
elif event == GUI.Dialogs.SAVE_SESSION:
|
||||
status, msg = self._save_session()
|
||||
elif event == GUI.Dialogs.SAVE_SESSION_AS:
|
||||
status, msg = self._save_session(True)
|
||||
else:
|
||||
return
|
||||
except IndexError:
|
||||
self.ui.set_edit_fields()
|
||||
return
|
||||
|
||||
if status is False and msg is not None:
|
||||
reply = QtWidgets.QErrorMessage(self)
|
||||
reply.setWindowTitle("I/O Failure")
|
||||
reply.setText(msg)
|
||||
reply.setInformativeText("Please try again.")
|
||||
reply.setIcon(QtWidgets.QMessageBox.Warning)
|
||||
reply.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
reply.exec_()
|
||||
else:
|
||||
self.ui.set_edit_fields()
|
||||
|
||||
def close_session(self):
|
||||
self.dolPath = None
|
||||
self.codePath = None
|
||||
self.gctData = None
|
||||
self.ui.dolTextBox.setText("")
|
||||
self.ui.gctFileTextBox.setText("")
|
||||
self.ui.gctFolderTextBox.setText("")
|
||||
self.ui.destTextBox.setText("")
|
||||
self.ui.allocLineEdit.setText("")
|
||||
self.ui.patchTypeSelect.setCurrentIndex(0)
|
||||
self.ui.handlerTypeSelect.setCurrentIndex(0)
|
||||
self.ui.hookTypeSelect.setCurrentIndex(0)
|
||||
self.ui.txtCodesIncludeSelect.setCurrentIndex(0)
|
||||
self.ui.optimizeSelect.setCurrentIndex(0)
|
||||
self.ui.responses.setPlainText("")
|
||||
self.uiexSettings.protectCodes.setChecked(False)
|
||||
self.uiexSettings.encryptCodes.setChecked(False)
|
||||
self.uiexSettings.codehookLineEdit.setText("")
|
||||
self.uiexSettings.kernelHookLineEdit.setText("")
|
||||
self.uiexSettings.verbositySelect.setCurrentIndex(0)
|
||||
self.ui.set_edit_fields()
|
||||
#Reset all ui elements as needed
|
||||
|
||||
def load_prefs(self):
|
||||
datapath = get_program_folder("GeckoLoader")
|
||||
|
||||
try:
|
||||
with open(os.path.join(datapath, ".GeckoLoader.conf"), "rb") as f:
|
||||
try:
|
||||
p = cPickle.load(f)
|
||||
except cPickle.UnpicklingError as e:
|
||||
self.log.exception(e) # Use defaults for prefs
|
||||
else:
|
||||
# Input validation
|
||||
if (p.get("qtstyle") in list(QtWidgets.QStyleFactory.keys()) or
|
||||
p.get("qtstyle") == "Default"):
|
||||
self.prefs["qtstyle"] = p.get("qtstyle")
|
||||
|
||||
if p.get("darktheme") in (True, False):
|
||||
self.prefs["darktheme"] = p.get("darktheme")
|
||||
|
||||
setCIndex = self.uiprefs.qtstyleSelect.setCurrentIndex
|
||||
|
||||
if self.prefs.get("qtstyle") in (None, "Default"):
|
||||
setCIndex(0)
|
||||
else:
|
||||
setCIndex(self.uiprefs.qtstyleSelect.findText(
|
||||
self.prefs.get("qtstyle"),
|
||||
flags=QtCore.Qt.MatchFixedString))
|
||||
|
||||
self.uiprefs.qtdarkButton.setChecked(self.prefs.get("darktheme"))
|
||||
self.update_theme()
|
||||
|
||||
except FileNotFoundError:
|
||||
self.log.warning("No preferences file found; using defaults.")
|
||||
|
||||
def save_prefs(self):
|
||||
datapath = get_program_folder("GeckoLoader")
|
||||
|
||||
self.prefs["qtstyle"] = str(self.uiprefs.qtstyleSelect.currentText())
|
||||
self.prefs["darktheme"] = self.uiprefs.qtdarkButton.isChecked()
|
||||
|
||||
try:
|
||||
with open(os.path.join(datapath, ".GeckoLoader.conf"), "wb") as f:
|
||||
cPickle.dump(self.prefs, f)
|
||||
except IOError as e:
|
||||
self.log.exception(e)
|
||||
|
||||
def load_qtstyle(self, style, first_style_load=False):
|
||||
self.style_log.append( [self.app.style, self.uiprefs.qtstyleSelect.currentText()] )
|
||||
|
||||
if len(self.style_log) > 2:
|
||||
self.style_log.pop(0)
|
||||
|
||||
if style != "Default":
|
||||
self.app.setStyle(style)
|
||||
else:
|
||||
self.app.setStyle(self.default_qtstyle)
|
||||
|
||||
if first_style_load:
|
||||
setCIndex = self.uiprefs.qtstyleSelect.setCurrentIndex
|
||||
setCIndex(self.uiprefs.qtstyleSelect.findText(style,
|
||||
flags=QtCore.Qt.MatchFixedString))
|
||||
|
||||
def update_theme(self):
|
||||
if self.uiprefs.qtdarkButton.isChecked():
|
||||
self.app.setPalette(self.ui.DarkTheme)
|
||||
self.load_qtstyle("Fusion", True)
|
||||
self.uiprefs.qtstyleSelect.setDisabled(True)
|
||||
else:
|
||||
self.app.setPalette(self.ui.LightTheme)
|
||||
self.load_qtstyle(self.style_log[0][1], True)
|
||||
self.uiprefs.qtstyleSelect.setEnabled(True)
|
||||
|
||||
def display_update(self):
|
||||
_outpipe = StringIO()
|
||||
_errpipe = StringIO()
|
||||
|
||||
with redirect_stdout(_outpipe), redirect_stderr(_errpipe):
|
||||
try:
|
||||
self.cli.check_updates()
|
||||
except SystemExit:
|
||||
_status = False
|
||||
else:
|
||||
_status = True
|
||||
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(resource_path(os.path.join("bin", "icon.ico"))), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
|
||||
if _status is False:
|
||||
reply = QtWidgets.QErrorMessage()
|
||||
reply.setWindowIcon(icon)
|
||||
reply.setWindowTitle("Response Error")
|
||||
reply.setText(self._remove_ansi(_errpipe.getvalue()))
|
||||
reply.setInformativeText("Make sure you have an internet connection")
|
||||
reply.setIcon(QtWidgets.QMessageBox.Warning)
|
||||
reply.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
reply.exec_()
|
||||
else:
|
||||
reply = QtWidgets.QMessageBox()
|
||||
reply.setWindowIcon(icon)
|
||||
reply.setWindowTitle("Update Info")
|
||||
reply.setText(self._remove_ansi(_outpipe.getvalue()).strip("\n") + "\n\nYou can find all GeckoLoader releases at:\nhttps://github.com/JoshuaMKW/GeckoLoader/releases")
|
||||
reply.setIcon(QtWidgets.QMessageBox.Information)
|
||||
reply.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
reply.exec_()
|
||||
|
||||
@staticmethod
|
||||
def _enforce_mask(textbox: QtWidgets.QTextEdit, mask: int, _or: int = 0):
|
||||
textbox.setText(textbox.text().strip())
|
||||
if len(textbox.text()) > 0:
|
||||
_depth = len(hex(mask)[2:])
|
||||
_address = int(textbox.text(), 16) << ((_depth - len(textbox.text())) << 2)
|
||||
|
||||
aligned = hex(((_address & mask) | _or) >> ((_depth - len(textbox.text())) << 2))[2:].upper()
|
||||
textbox.setText(aligned)
|
||||
|
||||
@staticmethod
|
||||
def _remove_ansi(msg: str) -> str:
|
||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
||||
return ansi_escape.sub('', msg)
|
||||
|
||||
def connect_signals(self):
|
||||
self.ui.actionPreferences.triggered.connect(lambda: self.show_dialog("Preferences"))
|
||||
self.ui.actionAbout_Qt.triggered.connect(lambda: self.show_dialog("aboutqt"))
|
||||
self.ui.actionAbout_GeckoLoader.triggered.connect(lambda: self.show_dialog("aboutGeckoLoader"))
|
||||
self.ui.actionCheck_Update.triggered.connect(lambda: self.display_update())
|
||||
|
||||
self.ui.actionOpen.triggered.connect(lambda: self.file_dialog_exec(GUI.Dialogs.LOAD_SESSION))
|
||||
self.ui.actionClose.triggered.connect(lambda: self.close_session())
|
||||
self.ui.actionSave_As.triggered.connect(lambda: self.file_dialog_exec(GUI.Dialogs.SAVE_SESSION_AS))
|
||||
self.ui.actionSave.triggered.connect(lambda: self.file_dialog_exec(GUI.Dialogs.SAVE_SESSION))
|
||||
|
||||
self.ui.dolButton.clicked.connect(lambda: self.file_dialog_exec(GUI.Dialogs.LOAD_DOL))
|
||||
self.ui.gctFileButton.clicked.connect(lambda: self.file_dialog_exec(GUI.Dialogs.LOAD_GCT))
|
||||
self.ui.gctFolderButton.clicked.connect(lambda: self.file_dialog_exec(GUI.Dialogs.LOAD_FOLDER))
|
||||
self.ui.destButton.clicked.connect(lambda: self.file_dialog_exec(GUI.Dialogs.LOAD_DEST))
|
||||
|
||||
self.ui.dolTextBox.textChanged.connect(lambda: self.ui.set_edit_fields())
|
||||
self.ui.gctFolderTextBox.textChanged.connect(lambda: self.ui.set_edit_fields())
|
||||
self.ui.gctFileTextBox.textChanged.connect(lambda: self.ui.set_edit_fields())
|
||||
|
||||
self.ui.allocLineEdit.textChanged.connect(lambda: self._enforce_mask(self.ui.allocLineEdit, 0xFFFFFC))
|
||||
|
||||
self.ui.exOptionsButton.clicked.connect(lambda: self.show_dialog("Advanced Settings"))
|
||||
self.ui.compileButton.clicked.connect(lambda: self._exec_api())
|
||||
|
||||
self.uiprefs.buttonBox.accepted.connect(self.save_prefs)
|
||||
self.uiprefs.qtstyleSelect.currentIndexChanged.connect(lambda: self.load_qtstyle(self.uiprefs.qtstyleSelect.currentText()))
|
||||
self.uiprefs.qtdarkButton.clicked.connect(lambda: self.update_theme())
|
||||
|
||||
self.uiexSettings.codehookLineEdit.textChanged.connect(lambda: self._enforce_mask(self.uiexSettings.codehookLineEdit, 0x817FFFFC, 0x80000000))
|
||||
self.uiexSettings.kernelHookLineEdit.textChanged.connect(lambda: self._enforce_mask(self.uiexSettings.kernelHookLineEdit, 0x817FFFFC, 0x80000000))
|
||||
|
||||
def _exec_api(self):
|
||||
if self.ui.dolTextBox.isEnabled and self.ui.dolTextBox.text().strip() != "":
|
||||
dol = os.path.normpath(self.ui.dolTextBox.text().strip())
|
||||
else:
|
||||
self.ui.responses.appendPlainText("DOL is missing, please add the path to your codes in the respective textbox" + "\n\n")
|
||||
return
|
||||
|
||||
if self.ui.gctFileTextBox.isEnabled and self.ui.gctFileTextBox.text().strip() != "":
|
||||
gct = os.path.normpath(self.ui.gctFileTextBox.text().strip())
|
||||
elif self.ui.gctFolderTextBox.isEnabled and self.ui.gctFolderTextBox.text().strip() != "":
|
||||
gct = os.path.normpath(self.ui.gctFolderTextBox.text().strip())
|
||||
else:
|
||||
self.ui.responses.appendPlainText("GCT is missing, please add the path to your codes in the respective textbox" + "\n\n")
|
||||
return
|
||||
|
||||
alloc = self.ui.allocLineEdit.text().strip()
|
||||
patchJob = self.ui.patchTypeSelect.currentText().strip()
|
||||
hookType = self.ui.hookTypeSelect.currentText().strip()
|
||||
hookAddress = self.uiexSettings.codehookLineEdit.text().strip()
|
||||
initAddress = self.uiexSettings.kernelHookLineEdit.text().strip()
|
||||
txtInclude = self.ui.txtCodesIncludeSelect.currentText().strip()
|
||||
codeHandlerType = self.ui.handlerTypeSelect.currentText().strip()
|
||||
optimize = self.ui.optimizeSelect.currentText().strip() == "TRUE"
|
||||
protect = self.uiexSettings.protectCodes.isChecked()
|
||||
encrypt = self.uiexSettings.encryptCodes.isChecked()
|
||||
verbosity = int(self.uiexSettings.verbositySelect.currentText().strip())
|
||||
dest = self.ui.destTextBox.text().strip()
|
||||
|
||||
argslist = [ dol, gct, "-m", patchJob, "-t", txtInclude, "--handler", codeHandlerType, "--hooktype", hookType ]
|
||||
|
||||
if alloc != "":
|
||||
argslist.append("-a")
|
||||
argslist.append(hex(int(alloc, 16) & 0xFFFFFC)[2:].upper())
|
||||
|
||||
if hookAddress != "":
|
||||
if int(hookAddress, 16) < 0x80000000:
|
||||
self.ui.responses.appendPlainText("The specified code hook is invalid" + "\n")
|
||||
return
|
||||
|
||||
argslist.append("--hookaddress")
|
||||
argslist.append(hookAddress)
|
||||
|
||||
if initAddress != "":
|
||||
if int(initAddress, 16) < 0x80000000:
|
||||
self.ui.responses.appendPlainText("The specified initialization address is invalid" + "\n")
|
||||
return
|
||||
|
||||
argslist.append("-i")
|
||||
argslist.append(initAddress)
|
||||
|
||||
if dest != "":
|
||||
if dest.lower().endswith(".dol") and len(dest) > 4:
|
||||
argslist.append("--dest")
|
||||
argslist.append(dest)
|
||||
else:
|
||||
self.ui.responses.appendPlainText("The destination file path is not a valid DOL file\n")
|
||||
return
|
||||
|
||||
if optimize:
|
||||
argslist.append("-o")
|
||||
|
||||
if protect:
|
||||
argslist.append("-p")
|
||||
|
||||
if encrypt:
|
||||
argslist.append("--encrypt")
|
||||
|
||||
if verbosity > 0:
|
||||
argslist.append("-" + "v"*verbosity)
|
||||
else:
|
||||
argslist.append("-q")
|
||||
|
||||
args = self.cli.parse_args(argslist)
|
||||
|
||||
_outpipe = StringIO()
|
||||
_errpipe = StringIO()
|
||||
_status = False
|
||||
_msg = ""
|
||||
|
||||
with redirect_stdout(_outpipe), redirect_stderr(_errpipe):
|
||||
try:
|
||||
self.cli._exec(args, tmpdir=TMPDIR)
|
||||
except SystemExit:
|
||||
_status = False
|
||||
else:
|
||||
_status = True
|
||||
|
||||
if _status is False:
|
||||
_msg = f"Arguments failed! GeckoLoader couldn't execute the job\n\nArgs: {args.__repr__()}\n\nstderr: {self._remove_ansi(_errpipe.getvalue())}"
|
||||
self.ui.responses.appendPlainText(_msg.strip() + "\n")
|
||||
else:
|
||||
for line in self._remove_ansi(_outpipe.getvalue()).split("\n"):
|
||||
if line.strip() != "":
|
||||
_msg += line.lstrip()
|
||||
self.ui.responses.appendPlainText(_msg.strip() + "\n")
|
||||
|
||||
def run(self):
|
||||
if sys.platform != "win32":
|
||||
datapath = os.path.join(os.getenv("HOME"), ".GeckoLoader")
|
||||
else:
|
||||
datapath = os.path.join(os.getenv("APPDATA"), "GeckoLoader")
|
||||
|
||||
if not os.path.isdir(datapath):
|
||||
os.mkdir(datapath)
|
||||
|
||||
self.app = QtWidgets.QApplication(sys.argv)
|
||||
self.default_qtstyle = self.app.style().objectName()
|
||||
self.ui = MainWindow(self.cli.__version__)
|
||||
self.uiprefs = PrefWindow()
|
||||
self.uiexSettings = SettingsWindow()
|
||||
|
||||
self.uiprefs.qtstyleSelect.addItem("Default")
|
||||
|
||||
for i in range(0, len(list(QtWidgets.QStyleFactory.keys()))):
|
||||
self.uiprefs.qtstyleSelect.addItem(list(QtWidgets.QStyleFactory.keys())[i])
|
||||
|
||||
self.load_prefs()
|
||||
self.load_qtstyle(self.prefs.get("qtstyle"), True)
|
||||
|
||||
regex = QtCore.QRegExp("[0-9A-Fa-f]*")
|
||||
validator = QtGui.QRegExpValidator(regex)
|
||||
self.ui.allocLineEdit.setValidator(validator)
|
||||
self.uiexSettings.codehookLineEdit.setValidator(validator)
|
||||
self.uiexSettings.kernelHookLineEdit.setValidator(validator)
|
||||
|
||||
self.connect_signals()
|
||||
self.ui.show()
|
||||
sys.exit(self.app.exec_())
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli = GeckoLoaderCli('GeckoLoader', __version__, description='Dol editing tool for allocating extended codespace')
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
cli.print_splash()
|
||||
app = GUI(cli)
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
app.run()
|
||||
sys.exit(1) #Should never reach here
|
||||
|
||||
elif '--checkupdate' in sys.argv:
|
||||
cli.check_updates()
|
||||
sys.exit(0)
|
||||
elif '--splash' in sys.argv:
|
||||
cli.print_splash()
|
||||
sys.exit(0)
|
||||
|
||||
except FileNotFoundError as e:
|
||||
parser.error(color_text(e + '\n', defaultColor=TREDLIT))
|
||||
args = cli.parse_args()
|
||||
cli._exec(args, TMPDIR)
|
||||
|
|
31
fileutils.py
31
fileutils.py
|
@ -1,5 +1,34 @@
|
|||
from tools import get_alignment, align_byte_size
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from tools import align_byte_size, get_alignment
|
||||
|
||||
def resource_path(relative_path: str = "") -> str:
|
||||
""" Get absolute path to resource, works for dev and for cx_freeze """
|
||||
if getattr(sys, "frozen", False):
|
||||
# The application is frozen
|
||||
base_path = os.path.dirname(sys.executable)
|
||||
else:
|
||||
base_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
return os.path.join(base_path, relative_path)
|
||||
|
||||
def get_program_folder(folder: str = "") -> str:
|
||||
""" Get path to appdata """
|
||||
if sys.platform == "win32":
|
||||
datapath = os.path.join(os.getenv("APPDATA"), folder)
|
||||
elif sys.platform == "darwin":
|
||||
if folder:
|
||||
folder = "." + folder
|
||||
datapath = os.path.join(os.path.expanduser("~"), "Library", "Application Support", folder)
|
||||
elif "linux" in sys.platform:
|
||||
if folder:
|
||||
folder = "." + folder
|
||||
datapath = os.path.join(os.getenv("HOME"), folder)
|
||||
else:
|
||||
raise NotImplementedError(f"{sys.platform} OS is unsupported")
|
||||
return datapath
|
||||
|
||||
def read_sbyte(f):
|
||||
return struct.unpack("b", f.read(1))[0]
|
||||
|
|
203
installer.cs
203
installer.cs
|
@ -1,203 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
public class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Installer installer = new Installer();
|
||||
installer.GetUserInput();
|
||||
}
|
||||
}
|
||||
|
||||
public class Installer
|
||||
{
|
||||
public string programfolder;
|
||||
public bool copyfiles;
|
||||
public bool overwrite;
|
||||
|
||||
public Installer()
|
||||
{
|
||||
programfolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "GeckoLoader");
|
||||
copyfiles = true;
|
||||
overwrite = true;
|
||||
}
|
||||
|
||||
void CopyAll (DirectoryInfo source, DirectoryInfo destination, string wildcard, string[] exclude, int maxdepth = 4)
|
||||
{
|
||||
if (maxdepth <= 0) return;
|
||||
|
||||
DirectoryInfo[] subdirs = source.GetDirectories();
|
||||
|
||||
foreach (DirectoryInfo dirPath in source.EnumerateDirectories())
|
||||
{
|
||||
Directory.CreateDirectory(dirPath.FullName.Replace(source.FullName, destination.FullName));
|
||||
}
|
||||
|
||||
|
||||
foreach (FileInfo filePath in source.EnumerateFiles(wildcard))
|
||||
{
|
||||
File.Copy(filePath.FullName, filePath.FullName.Replace(source.FullName, destination.FullName), true);
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo dir in subdirs)
|
||||
{
|
||||
DirectoryInfo dest = new DirectoryInfo(Path.Combine(destination.FullName, dir.Name));
|
||||
DirectoryInfo src = new DirectoryInfo(dir.FullName);
|
||||
|
||||
CopyAll(src, dest, wildcard, exclude, maxdepth - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClearConsoleLine(int index, bool moveto)
|
||||
{
|
||||
int currentLineCursor = Console.CursorTop;
|
||||
Console.SetCursorPosition(0, index);
|
||||
Console.Write(new string(' ', Console.WindowWidth));
|
||||
if (moveto) Console.SetCursorPosition(0, index);
|
||||
else Console.SetCursorPosition(0, currentLineCursor);
|
||||
}
|
||||
|
||||
private static string HandleConsoleQuestion(string msg, string[] options)
|
||||
{
|
||||
bool handled = false;
|
||||
string input = String.Empty;
|
||||
|
||||
while (handled == false)
|
||||
{
|
||||
Console.Write("{0} ({1}): ", msg, String.Join("|", options));
|
||||
|
||||
input = Console.ReadLine();
|
||||
if (options.Any(s => s.Contains(input.ToLower())))
|
||||
{
|
||||
handled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearConsoleLine(Console.CursorTop - 1, true);
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
private void SetProgramFolder(string folderName)
|
||||
{
|
||||
this.programfolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "GeckoLoader");
|
||||
}
|
||||
|
||||
private void SetFolderToPath(string dir, string subdir)
|
||||
{
|
||||
var scope = EnvironmentVariableTarget.User;
|
||||
var curPATH = Environment.GetEnvironmentVariable("PATH", scope);
|
||||
|
||||
if (!curPATH.Contains(Path.Combine(dir, subdir)))
|
||||
{
|
||||
var newValue = curPATH + ";" + Path.Combine(dir, subdir);
|
||||
Environment.SetEnvironmentVariable("PATH", newValue.Replace(";;", ";"), scope);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveFolderGroupFromPath(string dir)
|
||||
{
|
||||
var scope = EnvironmentVariableTarget.User;
|
||||
var curPATH = Environment.GetEnvironmentVariable("PATH", scope);
|
||||
|
||||
string[] oldPATHList = curPATH.Split(';');
|
||||
List<string> newPATHList = new List<string>();
|
||||
|
||||
foreach(string path in oldPATHList)
|
||||
{
|
||||
if (!path.ToLower().Contains(dir.ToLower()))
|
||||
{
|
||||
newPATHList.Add(path);
|
||||
}
|
||||
}
|
||||
Environment.SetEnvironmentVariable("PATH", String.Join(";", newPATHList.ToArray()), scope);
|
||||
|
||||
}
|
||||
|
||||
private bool MoveFilesToprogramfolder(string wildcard, bool copy = true, bool overwrite = false)
|
||||
{
|
||||
DirectoryInfo cwd = new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "data"));
|
||||
DirectoryInfo programspace = new DirectoryInfo(this.programfolder);
|
||||
|
||||
if (!programspace.Exists) programspace.Create();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (FileInfo file in programspace.EnumerateFiles())
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
foreach (DirectoryInfo dir in programspace.EnumerateDirectories())
|
||||
{
|
||||
dir.Delete(true);
|
||||
}
|
||||
|
||||
//Copy dependancies
|
||||
string[] exclude = { "installer.exe" };
|
||||
this.CopyAll(cwd, programspace, wildcard, exclude);
|
||||
}
|
||||
catch (UnauthorizedAccessException e)
|
||||
{
|
||||
Console.WriteLine(String.Format("Insufficient privledges provided! {0}\nTry running with administrator privledges", e));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void DeleteProgramFolder()
|
||||
{
|
||||
if (Directory.Exists(this.programfolder)) Directory.Delete(this.programfolder, true);
|
||||
}
|
||||
|
||||
public void GetUserInput()
|
||||
{
|
||||
string status;
|
||||
string[] continueoptions = { "y", "n" };
|
||||
string[] actionoptions = { "install", "uninstall" };
|
||||
|
||||
Console.SetWindowSize(84, 20);
|
||||
Console.Title = "GeckoLoader Installer";
|
||||
Console.WriteLine("This installer modifies the Windows User PATH variable\n");
|
||||
|
||||
status = HandleConsoleQuestion("Are you sure you want to continue?", continueoptions);
|
||||
|
||||
if (status.ToLower() == (string)continueoptions.GetValue(0))
|
||||
{
|
||||
this.SetProgramFolder("GeckoLoader");
|
||||
|
||||
status = HandleConsoleQuestion("What do you want to do?", actionoptions);
|
||||
if (status.ToLower() == (string)actionoptions.GetValue(0))
|
||||
{
|
||||
this.RemoveFolderGroupFromPath(this.programfolder);
|
||||
this.SetFolderToPath(this.programfolder, "");
|
||||
if (this.MoveFilesToprogramfolder("*", this.copyfiles, this.overwrite) == false)
|
||||
{
|
||||
Console.WriteLine("Failed to install :(");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Finished installation successfully! You can run GeckoLoader from anywhere\nby simply calling \"GeckoLoader <dol> <gct|txt|folder> [options]\"");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.RemoveFolderGroupFromPath(this.programfolder);
|
||||
this.DeleteProgramFolder();
|
||||
Console.WriteLine("Uninstalled successfully!");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("That's okay! You can always run this program again when you feel ready.");
|
||||
}
|
||||
Console.Write("Press any key to exit . . . ");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
77
kernel.py
77
kernel.py
|
@ -153,7 +153,7 @@ class GCT(object):
|
|||
elif (codetype.startswith(b'\xC6') or codetype.startswith(b'\xC7')
|
||||
or codetype.startswith(b'\xC6') or codetype.startswith(b'\xC7')):
|
||||
dolFile.seek(address)
|
||||
dolFile.insertBranch(int.from_bytes(info, byteorder='big', signed=False), dolFile.tell())
|
||||
dolFile.insert_branch(int.from_bytes(info, byteorder='big', signed=False), dolFile.tell())
|
||||
continue
|
||||
|
||||
if codetype.hex().startswith('2') or codetype.hex().startswith('3'):
|
||||
|
@ -183,6 +183,10 @@ class GCT(object):
|
|||
|
||||
class CodeHandler(object):
|
||||
|
||||
class Types:
|
||||
MINI = "MINI"
|
||||
FULL = "FULL"
|
||||
|
||||
def __init__(self, f):
|
||||
self._rawData = BytesIO(f.read())
|
||||
|
||||
|
@ -212,9 +216,9 @@ class CodeHandler(object):
|
|||
self.optimizeList = False
|
||||
|
||||
if self.handlerLength < 0x900:
|
||||
self.type = "Mini"
|
||||
self.type = CodeHandler.Types.MINI
|
||||
else:
|
||||
self.type = "Full"
|
||||
self.type = CodeHandler.Types.FULL
|
||||
|
||||
f.seek(0)
|
||||
|
||||
|
@ -357,13 +361,12 @@ class KernelLoader(object):
|
|||
self.quiet = False
|
||||
self.encrypt = False
|
||||
|
||||
def error(self, msg: str, exit=True):
|
||||
def error(self, msg: str):
|
||||
if self._cli is not None:
|
||||
self._cli.error(msg, exit)
|
||||
self._cli.error(msg)
|
||||
else:
|
||||
print(msg)
|
||||
if exit:
|
||||
sys.exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
def set_variables(self, entryPoint: list, baseOffset: int=0):
|
||||
self._rawData.seek(0)
|
||||
|
@ -450,8 +453,7 @@ class KernelLoader(object):
|
|||
if self.encrypt:
|
||||
codeHandler.encrypt_codes(_key)
|
||||
|
||||
|
||||
def patch_arena(self, codeHandler: CodeHandler, dolFile: DolFile):
|
||||
def patch_arena(self, codeHandler: CodeHandler, dolFile: DolFile) -> tuple:
|
||||
self.complete_data(codeHandler, [(dolFile.entryPoint >> 16) & 0xFFFF, dolFile.entryPoint & 0xFFFF])
|
||||
|
||||
self._rawData.seek(0, 2)
|
||||
|
@ -467,10 +469,12 @@ class KernelLoader(object):
|
|||
dolFile.append_data_sections([(_kernelData, self.initAddress)])
|
||||
except SectionCountFullError:
|
||||
self.error(tools.color_text('There are no unused sections left for GeckoLoader to use!\n', defaultColor=tools.TREDLIT))
|
||||
return False, 'There are no unused sections left for GeckoLoader to use!'
|
||||
|
||||
dolFile.entryPoint = self.initAddress
|
||||
return True, None
|
||||
|
||||
def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile):
|
||||
def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile) -> tuple:
|
||||
codeHandler._rawData.seek(0)
|
||||
codeHandler.geckoCodes.codeList.seek(0)
|
||||
|
||||
|
@ -483,6 +487,9 @@ class KernelLoader(object):
|
|||
dolFile.append_data_sections([(_handlerData, codeHandler.initAddress)])
|
||||
except SectionCountFullError:
|
||||
self.error(tools.color_text('There are no unused sections left for GeckoLoader to use!\n', defaultColor=tools.TREDLIT))
|
||||
return False, 'There are no unused sections left for GeckoLoader to use!'
|
||||
|
||||
return True, None
|
||||
|
||||
def protect_game(self, codeHandler: CodeHandler):
|
||||
_oldpos = codeHandler.geckoCodes.codeList.tell()
|
||||
|
@ -522,7 +529,7 @@ class KernelLoader(object):
|
|||
|
||||
@timer
|
||||
def build(self, gctFile, dolFile: DolFile, codeHandler: CodeHandler, tmpdir, dump):
|
||||
oldStart = dolFile.entryPoint
|
||||
_oldStart = dolFile.entryPoint
|
||||
|
||||
'''Initialize our codes'''
|
||||
|
||||
|
@ -545,7 +552,7 @@ class KernelLoader(object):
|
|||
if self.initAddress:
|
||||
try:
|
||||
dolFile.resolve_address(self.initAddress)
|
||||
print(tools.color_text(f'\n :: WARNING: Init address specified for GeckoLoader (0x{self.initAddress:X}) clobbers existing dol sections', defaultColor=tools.TYELLOW))
|
||||
self.error(tools.color_text(f'Init address specified for GeckoLoader (0x{self.initAddress:X}) clobbers existing dol sections', defaultColor=tools.TREDLIT))
|
||||
except RuntimeError:
|
||||
pass
|
||||
else:
|
||||
|
@ -561,32 +568,33 @@ class KernelLoader(object):
|
|||
with open(dump, 'wb') as final:
|
||||
dolFile.save(final)
|
||||
|
||||
if self.quiet:
|
||||
return
|
||||
if self.verbosity >= 3:
|
||||
dolFile.print_info()
|
||||
print('-'*64)
|
||||
if self.verbosity >= 1:
|
||||
print(tools.color_text('\n :: All codes have been successfully pre patched', defaultColor=tools.TGREENLIT))
|
||||
|
||||
if not self.quiet:
|
||||
if self.verbosity >= 3:
|
||||
dolFile.print_info()
|
||||
print('-'*64)
|
||||
if self.verbosity >= 1:
|
||||
print(tools.color_text('\n :: All codes have been successfully pre patched', defaultColor=tools.TGREENLIT))
|
||||
return
|
||||
|
||||
|
||||
if self.patchJob == 'LEGACY':
|
||||
legacy = True
|
||||
codeHandler.allocation = 0x80003000 - (codeHandler.initAddress + codeHandler.handlerLength)
|
||||
hooked = determine_codehook(dolFile, codeHandler, True)
|
||||
if hooked:
|
||||
self.patch_legacy(codeHandler, dolFile)
|
||||
_status, _msg = self.patch_legacy(codeHandler, dolFile)
|
||||
else:
|
||||
legacy = False
|
||||
hooked = determine_codehook(dolFile, codeHandler, False)
|
||||
if hooked:
|
||||
self.patch_arena(codeHandler, dolFile)
|
||||
_status, _msg = self.patch_arena(codeHandler, dolFile)
|
||||
|
||||
if not hooked:
|
||||
self.error(tools.color_text('Failed to find a hook address. Try using option --codehook to use your own address\n', defaultColor=tools.TREDLIT))
|
||||
elif _status is False:
|
||||
self.error(tools.color_text(_msg + '\n', defaultColor=tools.TREDLIT))
|
||||
elif codeHandler.allocation < codeHandler.geckoCodes.size:
|
||||
self.error(tools.color_text('\n :: Error: Allocated codespace was smaller than the given codelist.\n', defaultColor=tools.TYELLOW))
|
||||
self.error(tools.color_text('Allocated codespace was smaller than the given codelist\n', defaultColor=tools.TYELLOW))
|
||||
|
||||
with open(dump, 'wb') as final:
|
||||
dolFile.save(final)
|
||||
|
@ -606,19 +614,22 @@ class KernelLoader(object):
|
|||
|
||||
if self.verbosity >= 2:
|
||||
print('')
|
||||
if legacy == False:
|
||||
if legacy is False:
|
||||
info = [f' :: Start of game modified to address 0x{self.initAddress:X}',
|
||||
f' :: Game function "__start()" located at address 0x{oldStart:X}',
|
||||
f' :: Game function "__start()" located at address 0x{_oldStart:X}',
|
||||
f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
||||
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
||||
f' :: Codehandler is of type "{codeHandler.type}"',
|
||||
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used']
|
||||
f' :: Of the {DolFile.maxTextSections} text sections in this DOL file, {len(dolFile.textSections)} are now being used',
|
||||
f' :: Of the {DolFile.maxDataSections} text sections in this DOL file, {len(dolFile.dataSections)} are now being used']
|
||||
else:
|
||||
info = [f' :: Game function "__start()" located at address 0x{oldStart:X}',
|
||||
info = [f' :: Game function "__start()" located at address 0x{_oldStart:X}',
|
||||
f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}',
|
||||
f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}',
|
||||
f' :: Codehandler is of type "{codeHandler.type}"',
|
||||
f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used']
|
||||
f' :: Of the {DolFile.maxTextSections} text sections in this DOL file, {len(dolFile.textSections)} are now being used',
|
||||
f' :: Of the {DolFile.maxDataSections} text sections in this DOL file, {len(dolFile.dataSections)} are now being used']
|
||||
|
||||
for bit in info:
|
||||
print(tools.color_text(bit, defaultColor=tools.TGREENLIT))
|
||||
|
||||
|
@ -630,13 +641,7 @@ class KernelLoader(object):
|
|||
for bit in info:
|
||||
print(tools.color_text(bit, defaultColor=tools.TGREENLIT))
|
||||
|
||||
|
||||
def resource_path(relative_path: str):
|
||||
""" Get absolute path to resource, works for dev and for PyInstaller """
|
||||
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
|
||||
return os.path.join(base_path, relative_path)
|
||||
|
||||
def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler, hook=False):
|
||||
def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler, hook=False) -> bool:
|
||||
if codeHandler.hookAddress is None:
|
||||
if not assert_code_hook(dolFile, codeHandler):
|
||||
return False
|
||||
|
@ -648,7 +653,7 @@ def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler, hook=False):
|
|||
return True
|
||||
|
||||
|
||||
def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler):
|
||||
def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler) -> bool:
|
||||
for _, address, size, _, _ in dolFile.textSections:
|
||||
dolFile.seek(address)
|
||||
sample = dolFile.read(size)
|
||||
|
|
832
main_ui.py
Normal file
832
main_ui.py
Normal file
|
@ -0,0 +1,832 @@
|
|||
import logging
|
||||
import os
|
||||
import pickle as cPickle
|
||||
import re
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from children_ui import PrefWindow
|
||||
from dolreader import DolFile
|
||||
from fileutils import resource_path
|
||||
from kernel import CodeHandler, KernelLoader
|
||||
|
||||
class CmdWrapper(object):
|
||||
@staticmethod
|
||||
def call(prog: str, *args):
|
||||
return subprocess.run(" ".join([prog, *args]), shell=True, capture_output=True, text=True)
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow):
|
||||
def __init__(self, version: str):
|
||||
super().__init__()
|
||||
|
||||
self._job_active = False
|
||||
self.apiRevision = version
|
||||
self.setup_ui()
|
||||
|
||||
self.LightTheme = self.palette()
|
||||
|
||||
self.DarkTheme = QtGui.QPalette()
|
||||
self.DarkTheme.setColor(QtGui.QPalette.Window, QtGui.QColor(53, 53, 53))
|
||||
self.DarkTheme.setColor(QtGui.QPalette.WindowText, QtCore.Qt.white)
|
||||
self.DarkTheme.setColor(QtGui.QPalette.Base, QtGui.QColor(25, 25, 25))
|
||||
self.DarkTheme.setColor(QtGui.QPalette.AlternateBase, QtGui.QColor(53, 53, 53))
|
||||
self.DarkTheme.setColor(QtGui.QPalette.ToolTipBase, QtCore.Qt.black)
|
||||
self.DarkTheme.setColor(QtGui.QPalette.ToolTipText, QtCore.Qt.white)
|
||||
self.DarkTheme.setColor(QtGui.QPalette.Text, QtCore.Qt.white)
|
||||
self.DarkTheme.setColor(QtGui.QPalette.Button, QtGui.QColor(53, 53, 53))
|
||||
self.DarkTheme.setColor(QtGui.QPalette.ButtonText, QtCore.Qt.white)
|
||||
self.DarkTheme.setColor(QtGui.QPalette.BrightText, QtCore.Qt.red)
|
||||
self.DarkTheme.setColor(QtGui.QPalette.Link, QtGui.QColor(42, 130, 218))
|
||||
self.DarkTheme.setColor(QtGui.QPalette.Highlight, QtGui.QColor(42, 130, 218))
|
||||
self.DarkTheme.setColor(QtGui.QPalette.HighlightedText, QtCore.Qt.black)
|
||||
|
||||
def set_job_activity(self, active: bool):
|
||||
self._job_active = active
|
||||
|
||||
def close_event(self, event: QtGui.QCloseEvent):
|
||||
if self._job_active:
|
||||
reply = QtWidgets.QMessageBox(self)
|
||||
reply.setWindowTitle("Active job")
|
||||
reply.setText("GeckoLoader is busy!")
|
||||
reply.setInformativeText("Exiting is disabled")
|
||||
reply.setIcon(QtWidgets.QMessageBox.Warning)
|
||||
reply.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
reply.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
||||
reply.exec_()
|
||||
event.ignore()
|
||||
else:
|
||||
event.accept()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setObjectName("MainWindow")
|
||||
self.setWindowModality(QtCore.Qt.NonModal)
|
||||
self.setEnabled(True)
|
||||
self.setFixedSize(550, 680)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
font.setPointSize(10)
|
||||
font.setWeight(42)
|
||||
self.setFont(font)
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(resource_path(os.path.join("bin", "icon.ico"))), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.setWindowIcon(icon)
|
||||
|
||||
#Top level widget
|
||||
self.centerWidget = QtWidgets.QWidget(self)
|
||||
self.centerWidget.setObjectName("centerWidget")
|
||||
|
||||
self.gridLayout = QtWidgets.QGridLayout(self.centerWidget)
|
||||
self.gridLayout.setVerticalSpacing(0)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
|
||||
#Layout for file paths and open boxes
|
||||
self.filesLayout = QtWidgets.QGridLayout()
|
||||
self.filesLayout.setHorizontalSpacing(0)
|
||||
self.filesLayout.setObjectName("filesLayout")
|
||||
|
||||
self.dolLayout = QtWidgets.QGridLayout()
|
||||
self.dolLayout.setHorizontalSpacing(0)
|
||||
self.dolLayout.setObjectName("dolLayout")
|
||||
|
||||
#Layout for folder path
|
||||
self.gctLayout = QtWidgets.QGridLayout()
|
||||
self.gctLayout.setHorizontalSpacing(0)
|
||||
self.gctLayout.setVerticalSpacing(5)
|
||||
self.gctLayout.setObjectName("gctLayout")
|
||||
|
||||
self.destLayout = QtWidgets.QGridLayout()
|
||||
self.dolLayout.setHorizontalSpacing(0)
|
||||
self.dolLayout.setObjectName("dolLayout")
|
||||
|
||||
#Files label
|
||||
self.filesLabel = QtWidgets.QLabel(self.centerWidget)
|
||||
self.filesLabel.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.filesLabel.sizePolicy().hasHeightForWidth())
|
||||
self.filesLabel.setSizePolicy(sizePolicy)
|
||||
self.filesLabel.setMinimumSize(QtCore.QSize(80, 30))
|
||||
self.filesLabel.setMaximumSize(QtCore.QSize(16777215, 30))
|
||||
font = QtGui.QFont("Helvetica")
|
||||
font.setPointSize(21)
|
||||
font.setWeight(82)
|
||||
font.setBold(True)
|
||||
self.filesLabel.setFont(font)
|
||||
self.filesLabel.setTextFormat(QtCore.Qt.PlainText)
|
||||
self.filesLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.filesLabel.setObjectName("filesLabel")
|
||||
|
||||
#Dol button to open file
|
||||
self.dolButton = QtWidgets.QPushButton(self.centerWidget)
|
||||
self.dolButton.setMinimumSize(QtCore.QSize(100, 26))
|
||||
self.dolButton.setMaximumSize(QtCore.QSize(100, 26))
|
||||
font = QtGui.QFont("Helvetica")
|
||||
font.setPointSize(11)
|
||||
self.dolButton.setFont(font)
|
||||
self.dolButton.setCheckable(False)
|
||||
self.dolButton.setChecked(False)
|
||||
self.dolButton.setAutoDefault(True)
|
||||
self.dolButton.setDefault(False)
|
||||
self.dolButton.setFlat(False)
|
||||
self.dolButton.setObjectName("dolButton")
|
||||
self.dolLayout.addWidget(self.dolButton, 1, 0, 1, 1)
|
||||
|
||||
#Dol path textbox
|
||||
self.dolTextBox = QtWidgets.QLineEdit(self.centerWidget)
|
||||
self.dolTextBox.setEnabled(False)
|
||||
self.dolTextBox.setMinimumSize(QtCore.QSize(200, 24))
|
||||
self.dolTextBox.setMaximumSize(QtCore.QSize(16777215, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Consolas")
|
||||
font.setPointSize(10)
|
||||
font.setWeight(42)
|
||||
self.dolTextBox.setFont(font)
|
||||
self.dolTextBox.setText("")
|
||||
self.dolTextBox.setMaxLength(255)
|
||||
self.dolTextBox.setFrame(True)
|
||||
self.dolTextBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.dolTextBox.setObjectName("dolTextBox")
|
||||
self.dolLayout.addWidget(self.dolTextBox, 1, 1, 1, 1)
|
||||
|
||||
#horizontal separater codes
|
||||
self.horiSepFiles = QtWidgets.QFrame(self.centerWidget)
|
||||
self.horiSepFiles.setMinimumSize(QtCore.QSize(474, 30))
|
||||
self.horiSepFiles.setContentsMargins(20, 0, 20, 0)
|
||||
self.horiSepFiles.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
self.horiSepFiles.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.horiSepFiles.setObjectName("horiSepFiles")
|
||||
|
||||
#gctFile button to open file
|
||||
self.gctFileButton = QtWidgets.QPushButton(self.centerWidget)
|
||||
self.gctFileButton.setMinimumSize(QtCore.QSize(100, 26))
|
||||
self.gctFileButton.setMaximumSize(QtCore.QSize(100, 26))
|
||||
font = QtGui.QFont("Helvetica")
|
||||
font.setPointSize(10)
|
||||
self.gctFileButton.setFont(font)
|
||||
self.gctFileButton.setCheckable(False)
|
||||
self.gctFileButton.setChecked(False)
|
||||
self.gctFileButton.setAutoDefault(True)
|
||||
self.gctFileButton.setDefault(False)
|
||||
self.gctFileButton.setFlat(False)
|
||||
self.gctFileButton.setObjectName("gctFileButton")
|
||||
self.gctLayout.addWidget(self.gctFileButton, 0, 0, 1, 1)
|
||||
|
||||
#gctFile path textbox
|
||||
self.gctFileTextBox = QtWidgets.QLineEdit(self.centerWidget)
|
||||
self.gctFileTextBox.setEnabled(False)
|
||||
self.gctFileTextBox.setMinimumSize(QtCore.QSize(200, 24))
|
||||
self.gctFileTextBox.setMaximumSize(QtCore.QSize(16777215, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Consolas")
|
||||
font.setPointSize(10)
|
||||
font.setWeight(42)
|
||||
self.gctFileTextBox.setFont(font)
|
||||
self.gctFileTextBox.setText("")
|
||||
self.gctFileTextBox.setMaxLength(255)
|
||||
self.gctFileTextBox.setFrame(True)
|
||||
self.gctFileTextBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.gctFileTextBox.setObjectName("gctFileTextBox")
|
||||
self.gctLayout.addWidget(self.gctFileTextBox, 0, 1, 1, 1)
|
||||
|
||||
#--or-- Label
|
||||
self.orFolderLabel = QtWidgets.QLabel(self.centerWidget)
|
||||
self.orFolderLabel.setEnabled(False)
|
||||
self.orFolderLabel.setMinimumSize(QtCore.QSize(80, 8))
|
||||
self.orFolderLabel.setMaximumSize(QtCore.QSize(16777215, 8))
|
||||
font = QtGui.QFont("Helvetica")
|
||||
font.setPointSize(8)
|
||||
font.setWeight(82)
|
||||
font.setBold(True)
|
||||
self.orFolderLabel.setFont(font)
|
||||
self.orFolderLabel.setTextFormat(QtCore.Qt.PlainText)
|
||||
self.orFolderLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.orFolderLabel.setObjectName("orFolderLabel")
|
||||
self.gctLayout.addWidget(self.orFolderLabel, 1, 0, 1, 2)
|
||||
|
||||
#gctFolder button to open file
|
||||
self.gctFolderButton = QtWidgets.QPushButton(self.centerWidget)
|
||||
self.gctFolderButton.setMinimumSize(QtCore.QSize(100, 26))
|
||||
self.gctFolderButton.setMaximumSize(QtCore.QSize(100, 26))
|
||||
font = QtGui.QFont("Helvetica")
|
||||
font.setPointSize(10)
|
||||
self.gctFolderButton.setFont(font)
|
||||
self.gctFolderButton.setCheckable(False)
|
||||
self.gctFolderButton.setChecked(False)
|
||||
self.gctFolderButton.setAutoDefault(True)
|
||||
self.gctFolderButton.setDefault(False)
|
||||
self.gctFolderButton.setFlat(False)
|
||||
self.gctFolderButton.setObjectName("gctFolderButton")
|
||||
self.gctLayout.addWidget(self.gctFolderButton, 2, 0, 1, 1)
|
||||
|
||||
#gctFolder path textbox
|
||||
self.gctFolderTextBox = QtWidgets.QLineEdit(self.centerWidget)
|
||||
self.gctFolderTextBox.setEnabled(False)
|
||||
self.gctFolderTextBox.setMinimumSize(QtCore.QSize(200, 24))
|
||||
self.gctFolderTextBox.setMaximumSize(QtCore.QSize(16777215, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Consolas")
|
||||
font.setPointSize(10)
|
||||
font.setWeight(42)
|
||||
self.gctFolderTextBox.setFont(font)
|
||||
self.gctFolderTextBox.setText("")
|
||||
self.gctFolderTextBox.setMaxLength(255)
|
||||
self.gctFolderTextBox.setFrame(True)
|
||||
self.gctFolderTextBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.gctFolderTextBox.setObjectName("gctFolderTextBox")
|
||||
self.gctLayout.addWidget(self.gctFolderTextBox, 2, 1, 1, 1)
|
||||
|
||||
#horizontal separater dest
|
||||
self.horiSepDest = QtWidgets.QFrame(self.centerWidget)
|
||||
self.horiSepDest.setMinimumSize(QtCore.QSize(474, 30))
|
||||
self.horiSepDest.setContentsMargins(20, 0, 20, 0)
|
||||
self.horiSepDest.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
self.horiSepDest.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.horiSepDest.setObjectName("horiSepDest")
|
||||
|
||||
#Dest button to open file
|
||||
self.destButton = QtWidgets.QPushButton(self.centerWidget)
|
||||
self.destButton.setMinimumSize(QtCore.QSize(100, 26))
|
||||
self.destButton.setMaximumSize(QtCore.QSize(100, 26))
|
||||
font = QtGui.QFont("Helvetica")
|
||||
font.setPointSize(11)
|
||||
self.destButton.setFont(font)
|
||||
self.destButton.setCheckable(False)
|
||||
self.destButton.setChecked(False)
|
||||
self.destButton.setAutoDefault(True)
|
||||
self.destButton.setDefault(False)
|
||||
self.destButton.setFlat(False)
|
||||
self.destButton.setObjectName("destButton")
|
||||
self.destLayout.addWidget(self.destButton, 0, 0, 1, 1)
|
||||
|
||||
#Dest path textbox
|
||||
self.destTextBox = QtWidgets.QLineEdit(self.centerWidget)
|
||||
self.destTextBox.setEnabled(False)
|
||||
self.destTextBox.setMinimumSize(QtCore.QSize(200, 24))
|
||||
self.destTextBox.setMaximumSize(QtCore.QSize(16777215, 24))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Consolas")
|
||||
font.setPointSize(10)
|
||||
font.setWeight(42)
|
||||
self.destTextBox.setFont(font)
|
||||
self.destTextBox.setText("")
|
||||
self.destTextBox.setMaxLength(255)
|
||||
self.destTextBox.setFrame(True)
|
||||
self.destTextBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.destTextBox.setObjectName("destTextBox")
|
||||
self.destLayout.addWidget(self.destTextBox, 0, 1, 1, 1)
|
||||
|
||||
self.filesLayout.addLayout(self.dolLayout, 0, 0, 1, 1)
|
||||
self.filesLayout.addWidget(self.horiSepFiles, 1, 0, 1, 1)
|
||||
self.filesLayout.addLayout(self.gctLayout, 2, 0, 1, 1)
|
||||
self.filesLayout.addWidget(self.horiSepDest, 3, 0, 1, 1)
|
||||
self.filesLayout.addLayout(self.destLayout, 4, 0, 1, 1)
|
||||
|
||||
#Options Layout
|
||||
self.optionsLayout = QtWidgets.QGridLayout()
|
||||
self.optionsLayout.setHorizontalSpacing(20)
|
||||
self.optionsLayout.setObjectName("optionsLayout")
|
||||
|
||||
#Options Label
|
||||
self.optionsLabel = QtWidgets.QLabel(self.centerWidget)
|
||||
self.optionsLabel.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.optionsLabel.sizePolicy().hasHeightForWidth())
|
||||
self.optionsLabel.setSizePolicy(sizePolicy)
|
||||
self.optionsLabel.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.optionsLabel.setMaximumSize(QtCore.QSize(16777215, 23))
|
||||
font = QtGui.QFont("Helvetica")
|
||||
font.setPointSize(18)
|
||||
font.setWeight(82)
|
||||
font.setBold(True)
|
||||
self.optionsLabel.setFont(font)
|
||||
self.optionsLabel.setTextFormat(QtCore.Qt.PlainText)
|
||||
self.optionsLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.optionsLabel.setObjectName("optionsLabel")
|
||||
self.optionsLayout.addWidget(self.optionsLabel, 0, 0, 1, 4)
|
||||
|
||||
#Allocation Label
|
||||
self.allocLabel = QtWidgets.QLabel(self.centerWidget)
|
||||
self.allocLabel.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.allocLabel.sizePolicy().hasHeightForWidth())
|
||||
self.allocLabel.setSizePolicy(sizePolicy)
|
||||
self.allocLabel.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.allocLabel.setMaximumSize(QtCore.QSize(16777215, 23))
|
||||
self.allocLabel.setTextFormat(QtCore.Qt.PlainText)
|
||||
self.allocLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.allocLabel.setObjectName("allocLabel")
|
||||
self.optionsLayout.addWidget(self.allocLabel, 1, 0, 1, 1)
|
||||
|
||||
#Allocation Textbox
|
||||
self.allocLineEdit = QtWidgets.QLineEdit(self.centerWidget)
|
||||
self.allocLineEdit.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.allocLineEdit.sizePolicy().hasHeightForWidth())
|
||||
self.allocLineEdit.setSizePolicy(sizePolicy)
|
||||
self.allocLineEdit.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.allocLineEdit.setMaximumSize(QtCore.QSize(79, 23))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Consolas")
|
||||
font.setPointSize(12)
|
||||
font.setWeight(42)
|
||||
self.allocLineEdit.setFont(font)
|
||||
self.allocLineEdit.setText("")
|
||||
self.allocLineEdit.setMaxLength(6)
|
||||
self.allocLineEdit.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.allocLineEdit.setObjectName("allocLineEdit")
|
||||
self.optionsLayout.addWidget(self.allocLineEdit, 2, 0, 1, 1)
|
||||
|
||||
#Patch label
|
||||
self.patchLabel = QtWidgets.QLabel(self.centerWidget)
|
||||
self.patchLabel.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.patchLabel.sizePolicy().hasHeightForWidth())
|
||||
self.patchLabel.setSizePolicy(sizePolicy)
|
||||
self.patchLabel.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.patchLabel.setMaximumSize(QtCore.QSize(16777215, 23))
|
||||
self.patchLabel.setTextFormat(QtCore.Qt.PlainText)
|
||||
self.patchLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.patchLabel.setObjectName("patchLabel")
|
||||
self.optionsLayout.addWidget(self.patchLabel, 1, 1, 1, 1)
|
||||
|
||||
#Patch selection
|
||||
self.patchTypeSelect = QtWidgets.QComboBox(self.centerWidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.allocLabel.sizePolicy().hasHeightForWidth())
|
||||
self.patchTypeSelect.setSizePolicy(sizePolicy)
|
||||
self.patchTypeSelect.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.patchTypeSelect.setMaximumSize(QtCore.QSize(79, 23))
|
||||
self.patchTypeSelect.setObjectName("patchTypeSelect")
|
||||
self.patchTypeSelect.addItems(["AUTO", "LEGACY", "ARENA"])
|
||||
self.optionsLayout.addWidget(self.patchTypeSelect, 2, 1, 1, 1)
|
||||
|
||||
#handlerType label
|
||||
self.handlerTypeLabel = QtWidgets.QLabel(self.centerWidget)
|
||||
self.handlerTypeLabel.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.handlerTypeLabel.sizePolicy().hasHeightForWidth())
|
||||
self.handlerTypeLabel.setSizePolicy(sizePolicy)
|
||||
self.handlerTypeLabel.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.handlerTypeLabel.setMaximumSize(QtCore.QSize(16777215, 23))
|
||||
self.handlerTypeLabel.setTextFormat(QtCore.Qt.PlainText)
|
||||
self.handlerTypeLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.handlerTypeLabel.setObjectName("handlerTypeLabel")
|
||||
self.optionsLayout.addWidget(self.handlerTypeLabel, 1, 2, 1, 1)
|
||||
|
||||
#handlerType selection
|
||||
self.handlerTypeSelect = QtWidgets.QComboBox(self.centerWidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.allocLabel.sizePolicy().hasHeightForWidth())
|
||||
self.handlerTypeSelect.setSizePolicy(sizePolicy)
|
||||
self.handlerTypeSelect.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.handlerTypeSelect.setMaximumSize(QtCore.QSize(79, 23))
|
||||
self.handlerTypeSelect.setObjectName("handlerTypeSelect")
|
||||
self.handlerTypeSelect.addItems(["FULL", "MINI"])
|
||||
self.optionsLayout.addWidget(self.handlerTypeSelect, 2, 2, 1, 1)
|
||||
|
||||
#hookType label
|
||||
self.hookTypeLabel = QtWidgets.QLabel(self.centerWidget)
|
||||
self.hookTypeLabel.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.hookTypeLabel.sizePolicy().hasHeightForWidth())
|
||||
self.hookTypeLabel.setSizePolicy(sizePolicy)
|
||||
self.hookTypeLabel.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.hookTypeLabel.setMaximumSize(QtCore.QSize(16777215, 23))
|
||||
self.hookTypeLabel.setTextFormat(QtCore.Qt.PlainText)
|
||||
self.hookTypeLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.hookTypeLabel.setObjectName("hookTypeLabel")
|
||||
self.optionsLayout.addWidget(self.hookTypeLabel, 1, 3, 1, 1)
|
||||
|
||||
#hookType selection
|
||||
self.hookTypeSelect = QtWidgets.QComboBox(self.centerWidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.allocLabel.sizePolicy().hasHeightForWidth())
|
||||
self.hookTypeSelect.setSizePolicy(sizePolicy)
|
||||
self.hookTypeSelect.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.hookTypeSelect.setMaximumSize(QtCore.QSize(79, 23))
|
||||
self.hookTypeSelect.setObjectName("hookTypeSelect")
|
||||
self.hookTypeSelect.addItems(["VI", "GX", "PAD"])
|
||||
self.optionsLayout.addWidget(self.hookTypeSelect, 2, 3, 1, 1)
|
||||
|
||||
#txtCodesInclude label
|
||||
self.txtCodesIncludeLabel = QtWidgets.QLabel(self.centerWidget)
|
||||
self.txtCodesIncludeLabel.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.txtCodesIncludeLabel.sizePolicy().hasHeightForWidth())
|
||||
self.txtCodesIncludeLabel.setSizePolicy(sizePolicy)
|
||||
self.txtCodesIncludeLabel.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.txtCodesIncludeLabel.setMaximumSize(QtCore.QSize(16777215, 23))
|
||||
self.txtCodesIncludeLabel.setTextFormat(QtCore.Qt.PlainText)
|
||||
self.txtCodesIncludeLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.txtCodesIncludeLabel.setObjectName("txtCodesIncludeLabel")
|
||||
self.optionsLayout.addWidget(self.txtCodesIncludeLabel, 3, 0, 1, 1)
|
||||
|
||||
#txtCodesInclude selection
|
||||
self.txtCodesIncludeSelect = QtWidgets.QComboBox(self.centerWidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.allocLabel.sizePolicy().hasHeightForWidth())
|
||||
self.txtCodesIncludeSelect.setSizePolicy(sizePolicy)
|
||||
self.txtCodesIncludeSelect.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.txtCodesIncludeSelect.setMaximumSize(QtCore.QSize(79, 23))
|
||||
self.txtCodesIncludeSelect.setObjectName("txtCodesIncludeSelect")
|
||||
self.txtCodesIncludeSelect.addItems(["ACTIVE", "ALL"])
|
||||
self.optionsLayout.addWidget(self.txtCodesIncludeSelect, 4, 0, 1, 1)
|
||||
|
||||
#optimize label
|
||||
self.optimizeLabel = QtWidgets.QLabel(self.centerWidget)
|
||||
self.optimizeLabel.setEnabled(False)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.optimizeLabel.sizePolicy().hasHeightForWidth())
|
||||
self.optimizeLabel.setSizePolicy(sizePolicy)
|
||||
self.optimizeLabel.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.optimizeLabel.setMaximumSize(QtCore.QSize(16777215, 23))
|
||||
self.optimizeLabel.setTextFormat(QtCore.Qt.PlainText)
|
||||
self.optimizeLabel.setAlignment(QtCore.Qt.AlignCenter|QtCore.Qt.AlignVCenter)
|
||||
self.optimizeLabel.setObjectName("optimizeLabel")
|
||||
self.optionsLayout.addWidget(self.optimizeLabel, 3, 1, 1, 1)
|
||||
|
||||
#optimize selection
|
||||
self.optimizeSelect = QtWidgets.QComboBox(self.centerWidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.allocLabel.sizePolicy().hasHeightForWidth())
|
||||
self.optimizeSelect.setSizePolicy(sizePolicy)
|
||||
self.optimizeSelect.setMinimumSize(QtCore.QSize(79, 23))
|
||||
self.optimizeSelect.setMaximumSize(QtCore.QSize(79, 23))
|
||||
self.optimizeSelect.setObjectName("optimizeSelect")
|
||||
self.optimizeSelect.addItems(["TRUE", "FALSE"])
|
||||
self.optionsLayout.addWidget(self.optimizeSelect, 4, 1, 1, 1)
|
||||
|
||||
#Advanced options button
|
||||
self.exOptionsButton = QtWidgets.QPushButton(self.centerWidget)
|
||||
font = QtGui.QFont("Helvetica")
|
||||
font.setPointSize(13)
|
||||
self.exOptionsButton.setFont(font)
|
||||
self.exOptionsButton.setCheckable(False)
|
||||
self.exOptionsButton.setChecked(False)
|
||||
self.exOptionsButton.setAutoDefault(True)
|
||||
self.exOptionsButton.setDefault(False)
|
||||
self.exOptionsButton.setFlat(False)
|
||||
self.exOptionsButton.setDisabled(True)
|
||||
self.exOptionsButton.setObjectName("exOptionsButton")
|
||||
self.optionsLayout.addWidget(self.exOptionsButton, 4, 2, 1, 2)
|
||||
|
||||
#horizontal separater 1
|
||||
self.horiSepA = QtWidgets.QFrame(self.centerWidget)
|
||||
self.horiSepA.setMinimumSize(QtCore.QSize(470, 30))
|
||||
self.horiSepA.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
self.horiSepA.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.horiSepA.setObjectName("horiSepA")
|
||||
|
||||
#horizontal separater 2
|
||||
self.horiSepB = QtWidgets.QFrame(self.centerWidget)
|
||||
self.horiSepB.setMinimumSize(QtCore.QSize(470, 30))
|
||||
self.horiSepB.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
self.horiSepB.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.horiSepB.setObjectName("horiSepB")
|
||||
|
||||
#response panel
|
||||
self.responses = QtWidgets.QPlainTextEdit(self.centerWidget)
|
||||
self.responses.setEnabled(True)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.responses.sizePolicy().hasHeightForWidth())
|
||||
self.responses.setSizePolicy(sizePolicy)
|
||||
self.responses.setMinimumSize(QtCore.QSize(474, 180))
|
||||
self.responses.setMaximumSize(QtCore.QSize(16777215, 180))
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Consolas")
|
||||
font.setPointSize(8)
|
||||
font.setWeight(42)
|
||||
fontMetrics = QtGui.QFontMetricsF(font)
|
||||
spaceWidth = fontMetrics.width(' ')
|
||||
self.responses.setFont(font)
|
||||
self.responses.setPlainText("")
|
||||
self.responses.setTabStopDistance(spaceWidth * 4)
|
||||
self.responses.setReadOnly(True)
|
||||
self.responses.setObjectName("responses")
|
||||
|
||||
#Compile button
|
||||
self.compileButton = QtWidgets.QPushButton(self.centerWidget)
|
||||
font = QtGui.QFont("Helvetica")
|
||||
font.setPointSize(34)
|
||||
self.compileButton.setFont(font)
|
||||
self.compileButton.setCheckable(False)
|
||||
self.compileButton.setChecked(False)
|
||||
self.compileButton.setAutoDefault(True)
|
||||
self.compileButton.setDefault(False)
|
||||
self.compileButton.setFlat(False)
|
||||
self.compileButton.setDisabled(True)
|
||||
self.compileButton.setObjectName("compileButton")
|
||||
|
||||
self.gridLayout.addWidget(self.filesLabel, 0, 0, 1, 1)
|
||||
self.gridLayout.addLayout(self.filesLayout, 1, 0, 1, 1)
|
||||
self.gridLayout.addWidget(self.horiSepA, 2, 0, 1, 1)
|
||||
self.gridLayout.addLayout(self.optionsLayout, 3, 0, 1, 1)
|
||||
self.gridLayout.addWidget(self.horiSepB, 4, 0, 1, 1)
|
||||
self.gridLayout.addWidget(self.responses, 5, 0, 1, 1)
|
||||
self.gridLayout.addWidget(self.compileButton, 6, 0, 1, 1)
|
||||
|
||||
self.setCentralWidget(self.centerWidget)
|
||||
|
||||
#Toolbar
|
||||
self.menubar = QtWidgets.QMenuBar(self)
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 470, 22))
|
||||
self.menubar.setObjectName("menubar")
|
||||
|
||||
self.menuFile = QtWidgets.QMenu(self.menubar)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.menuFile.setFont(font)
|
||||
self.menuFile.setObjectName("menuFile")
|
||||
|
||||
self.menuEdit = QtWidgets.QMenu(self.menubar)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.menuEdit.setFont(font)
|
||||
self.menuEdit.setObjectName("menuEdit")
|
||||
|
||||
self.menuHelp = QtWidgets.QMenu(self.menubar)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.menuHelp.setFont(font)
|
||||
self.menuHelp.setObjectName("menuHelp")
|
||||
|
||||
self.setMenuBar(self.menubar)
|
||||
|
||||
self.actionOpen = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionOpen.setFont(font)
|
||||
self.actionOpen.setObjectName("actionOpen")
|
||||
|
||||
self.actionClose = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionClose.setFont(font)
|
||||
self.actionClose.setObjectName("actionClose")
|
||||
|
||||
self.actionSave = QtWidgets.QAction(self)
|
||||
self.actionSave.setEnabled(False)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionSave.setFont(font)
|
||||
self.actionSave.setObjectName("actionSave")
|
||||
|
||||
self.actionSave_As = QtWidgets.QAction(self)
|
||||
self.actionSave_As.setEnabled(False)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionSave_As.setFont(font)
|
||||
self.actionSave_As.setObjectName("actionSave_As")
|
||||
|
||||
self.actionUndo = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionUndo.setFont(font)
|
||||
self.actionUndo.setMenuRole(QtWidgets.QAction.TextHeuristicRole)
|
||||
self.actionUndo.setObjectName("actionUndo")
|
||||
self.actionRedo = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionRedo.setFont(font)
|
||||
self.actionRedo.setObjectName("actionRedo")
|
||||
self.actionCut = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionCut.setFont(font)
|
||||
self.actionCut.setObjectName("actionCut")
|
||||
self.actionCopy = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionCopy.setFont(font)
|
||||
self.actionCopy.setObjectName("actionCopy")
|
||||
self.actionPaste = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionPaste.setFont(font)
|
||||
self.actionPaste.setObjectName("actionPaste")
|
||||
self.actionDelete = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionDelete.setFont(font)
|
||||
self.actionDelete.setObjectName("actionDelete")
|
||||
self.actionSelect_All = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionSelect_All.setFont(font)
|
||||
self.actionSelect_All.setObjectName("actionSelect_All")
|
||||
self.actionPreferences = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionPreferences.setFont(font)
|
||||
self.actionPreferences.setMenuRole(QtWidgets.QAction.PreferencesRole)
|
||||
self.actionPreferences.setObjectName("actionPreferences")
|
||||
|
||||
self.actionAbout_GeckoLoader = QtWidgets.QAction(self)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionAbout_GeckoLoader.setFont(font)
|
||||
self.actionAbout_GeckoLoader.setMenuRole(QtWidgets.QAction.AboutRole)
|
||||
self.actionAbout_GeckoLoader.setObjectName("actionAbout_GeckoLoader")
|
||||
|
||||
self.actionAbout_Qt = QtWidgets.QAction(self)
|
||||
self.actionAbout_Qt.setStatusTip("")
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionAbout_Qt.setFont(font)
|
||||
self.actionAbout_Qt.setMenuRole(QtWidgets.QAction.AboutQtRole)
|
||||
self.actionAbout_Qt.setObjectName("actionAbout_Qt")
|
||||
|
||||
self.actionCheck_Update = QtWidgets.QAction(self)
|
||||
self.actionCheck_Update.setStatusTip("")
|
||||
font = QtGui.QFont()
|
||||
font.setFamily("Helvetica")
|
||||
self.actionCheck_Update.setFont(font)
|
||||
self.actionCheck_Update.setObjectName("actionCheck_Update")
|
||||
|
||||
self.menuFile.addAction(self.actionOpen)
|
||||
self.menuFile.addAction(self.actionClose)
|
||||
self.menuFile.addSeparator()
|
||||
self.menuFile.addAction(self.actionSave)
|
||||
self.menuFile.addAction(self.actionSave_As)
|
||||
|
||||
self.menuEdit.addAction(self.actionPreferences)
|
||||
|
||||
self.menuHelp.addAction(self.actionAbout_GeckoLoader)
|
||||
self.menuHelp.addAction(self.actionAbout_Qt)
|
||||
self.menuHelp.addAction(self.actionCheck_Update)
|
||||
|
||||
self.menubar.addAction(self.menuFile.menuAction())
|
||||
self.menubar.addAction(self.menuEdit.menuAction())
|
||||
self.menubar.addAction(self.menuHelp.menuAction())
|
||||
|
||||
#Statusbar
|
||||
self.statusbar = QtWidgets.QStatusBar(self)
|
||||
self.statusbar.setObjectName("statusbar")
|
||||
self.setStatusBar(self.statusbar)
|
||||
|
||||
self.retranslate_ui()
|
||||
self.set_edit_fields()
|
||||
|
||||
QtCore.QMetaObject.connectSlotsByName(self)
|
||||
|
||||
def _lstrip_textboxes(self):
|
||||
attributes = [item for item in vars(self) if not item.startswith('__')]
|
||||
|
||||
for item in attributes:
|
||||
item = getattr(self, item)
|
||||
if isinstance(item, QtWidgets.QLineEdit):
|
||||
item.setText(item.text().lstrip())
|
||||
elif isinstance(item, QtWidgets.QPlainTextEdit):
|
||||
item.setPlainText(item.toPlainText().lstrip())
|
||||
|
||||
def set_edit_fields(self):
|
||||
self.filesLabel.setEnabled(True)
|
||||
self.dolTextBox.setEnabled(True)
|
||||
self.destTextBox.setEnabled(True)
|
||||
self.optionsLabel.setEnabled(True)
|
||||
self.allocLabel.setEnabled(True)
|
||||
self.allocLineEdit.setEnabled(True)
|
||||
self.patchLabel.setEnabled(True)
|
||||
self.patchTypeSelect.setEnabled(True)
|
||||
self.handlerTypeLabel.setEnabled(True)
|
||||
self.handlerTypeSelect.setEnabled(True)
|
||||
self.hookTypeLabel.setEnabled(True)
|
||||
self.hookTypeSelect.setEnabled(True)
|
||||
self.txtCodesIncludeLabel.setEnabled(True)
|
||||
self.txtCodesIncludeSelect.setEnabled(True)
|
||||
self.optimizeLabel.setEnabled(True)
|
||||
self.optimizeSelect.setEnabled(True)
|
||||
self.exOptionsButton.setEnabled(True)
|
||||
self.actionSave.setEnabled(True)
|
||||
self.actionSave_As.setEnabled(True)
|
||||
|
||||
self._lstrip_textboxes()
|
||||
|
||||
if self.gctFileTextBox.text() != "":
|
||||
self.gctFileTextBox.setEnabled(True)
|
||||
self.gctFolderTextBox.setDisabled(True)
|
||||
elif self.gctFolderTextBox.text() != "":
|
||||
self.gctFileTextBox.setDisabled(True)
|
||||
self.gctFolderTextBox.setEnabled(True)
|
||||
else:
|
||||
self.gctFileTextBox.setEnabled(True)
|
||||
self.gctFolderTextBox.setEnabled(True)
|
||||
|
||||
if self.dolTextBox.text().lower().endswith(".dol") and len(self.dolTextBox.text()) > 4:
|
||||
self.compileButton.setEnabled(self.gctFileTextBox.text() != "" or self.gctFolderTextBox.text() != "")
|
||||
else:
|
||||
self.compileButton.setDisabled(True)
|
||||
|
||||
def retranslate_ui(self):
|
||||
self.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", f"GeckoLoader {self.apiRevision} - untitled", None))
|
||||
self.menuFile.setTitle(QtWidgets.QApplication.translate("MainWindow", "&File", None))
|
||||
self.menuEdit.setTitle(QtWidgets.QApplication.translate("MainWindow", "&Edit", None))
|
||||
self.menuHelp.setTitle(QtWidgets.QApplication.translate("MainWindow", "&Help", None))
|
||||
self.actionOpen.setText(QtWidgets.QApplication.translate("MainWindow", "&Open Session...", None))
|
||||
self.actionOpen.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Open a session", None))
|
||||
self.actionOpen.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+O", None))
|
||||
self.actionClose.setText(QtWidgets.QApplication.translate("MainWindow", "&Close Session...", None))
|
||||
self.actionClose.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Close the current session", None))
|
||||
self.actionClose.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+Shift+C", None))
|
||||
self.actionSave.setText(QtWidgets.QApplication.translate("MainWindow", "&Save Session", None))
|
||||
self.actionSave.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Save the current session", None))
|
||||
self.actionSave.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+S", None))
|
||||
self.actionSave_As.setText(QtWidgets.QApplication.translate("MainWindow", "&Save Session As...", None))
|
||||
self.actionSave_As.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Save the current session to the specified location", None))
|
||||
self.actionSave_As.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+Shift+S", None))
|
||||
self.actionUndo.setText(QtWidgets.QApplication.translate("MainWindow", "Undo", None))
|
||||
self.actionUndo.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Undo the last action", None))
|
||||
self.actionUndo.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+Z", None))
|
||||
self.actionRedo.setText(QtWidgets.QApplication.translate("MainWindow", "Redo", None))
|
||||
self.actionRedo.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Redo the last action", None))
|
||||
self.actionRedo.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+Shift+Z", None))
|
||||
self.actionCut.setText(QtWidgets.QApplication.translate("MainWindow", "Cut", None))
|
||||
self.actionCut.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Cuts the selected text and places it on the clipboard", None))
|
||||
self.actionCut.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+X", None))
|
||||
self.actionCopy.setText(QtWidgets.QApplication.translate("MainWindow", "Copy", None))
|
||||
self.actionCopy.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Copies the selected text and places it on the clipboard", None))
|
||||
self.actionCopy.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+C", None))
|
||||
self.actionPaste.setText(QtWidgets.QApplication.translate("MainWindow", "Paste", None))
|
||||
self.actionPaste.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Paste the contents of the clipboard", None))
|
||||
self.actionPaste.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+V", None))
|
||||
self.actionDelete.setText(QtWidgets.QApplication.translate("MainWindow", "Delete", None))
|
||||
self.actionDelete.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Deletes the selected text", None))
|
||||
self.actionSelect_All.setText(QtWidgets.QApplication.translate("MainWindow", "Select All", None))
|
||||
self.actionSelect_All.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Select all of the text", None))
|
||||
self.actionSelect_All.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+A", None))
|
||||
self.actionPreferences.setText(QtWidgets.QApplication.translate("MainWindow", "&Preferences...", None))
|
||||
self.actionPreferences.setStatusTip(QtWidgets.QApplication.translate("MainWindow", "Open the application preferences dialog", None))
|
||||
self.actionAbout_GeckoLoader.setText(QtWidgets.QApplication.translate("MainWindow", "About &GeckoLoader...", None))
|
||||
self.actionAbout_Qt.setText(QtWidgets.QApplication.translate("MainWindow", "About &Qt...", None))
|
||||
self.actionCheck_Update.setText(QtWidgets.QApplication.translate("MainWindow", "&Check Update", None))
|
||||
|
||||
self.filesLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Files", None))
|
||||
|
||||
self.dolButton.setText(QtWidgets.QApplication.translate("MainWindow", "Open DOL", None))
|
||||
self.gctFileButton.setText(QtWidgets.QApplication.translate("MainWindow", "Open Codes", None))
|
||||
self.orFolderLabel.setText(QtWidgets.QApplication.translate("MainWindow", "-"*40 + "OR" + "-"*40, None))
|
||||
self.gctFolderButton.setText(QtWidgets.QApplication.translate("MainWindow", "Open Folder", None))
|
||||
self.destButton.setText(QtWidgets.QApplication.translate("MainWindow", "Destination", None))
|
||||
|
||||
self.optionsLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Options", None))
|
||||
|
||||
self.allocLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Allocation", None))
|
||||
self.allocLineEdit.setPlaceholderText(QtWidgets.QApplication.translate("MainWindow", "AUTO", None))
|
||||
|
||||
self.patchLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Patch Type", None))
|
||||
self.patchTypeSelect.setItemText(0, QtWidgets.QApplication.translate("Dialog", "AUTO", None))
|
||||
self.patchTypeSelect.setItemText(1, QtWidgets.QApplication.translate("Dialog", "LEGACY", None))
|
||||
self.patchTypeSelect.setItemText(2, QtWidgets.QApplication.translate("Dialog", "ARENA", None))
|
||||
|
||||
self.handlerTypeLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Codehandler", None))
|
||||
self.handlerTypeSelect.setItemText(0, QtWidgets.QApplication.translate("Dialog", "FULL", None))
|
||||
self.handlerTypeSelect.setItemText(1, QtWidgets.QApplication.translate("Dialog", "MINI", None))
|
||||
|
||||
self.hookTypeLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Code Hook", None))
|
||||
self.hookTypeSelect.setItemText(0, QtWidgets.QApplication.translate("Dialog", "VI", None))
|
||||
self.hookTypeSelect.setItemText(1, QtWidgets.QApplication.translate("Dialog", "GX", None))
|
||||
self.hookTypeSelect.setItemText(2, QtWidgets.QApplication.translate("Dialog", "PAD", None))
|
||||
|
||||
self.txtCodesIncludeLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Include Codes", None))
|
||||
self.txtCodesIncludeSelect.setItemText(0, QtWidgets.QApplication.translate("Dialog", "ACTIVE", None))
|
||||
self.txtCodesIncludeSelect.setItemText(1, QtWidgets.QApplication.translate("Dialog", "ALL", None))
|
||||
|
||||
self.optimizeLabel.setText(QtWidgets.QApplication.translate("MainWindow", "Optimize", None))
|
||||
self.optimizeSelect.setItemText(0, QtWidgets.QApplication.translate("Dialog", "TRUE", None))
|
||||
self.optimizeSelect.setItemText(1, QtWidgets.QApplication.translate("Dialog", "FALSE", None))
|
||||
|
||||
self.exOptionsButton.setText(QtWidgets.QApplication.translate("MainWindow", "Advanced Settings", None))
|
||||
|
||||
self.compileButton.setText(QtWidgets.QApplication.translate("MainWindow", "RUN", None))
|
|
@ -14,7 +14,7 @@ class Updater(object):
|
|||
html = response.read()
|
||||
return html
|
||||
|
||||
def get_newest_version(self) -> str:
|
||||
def get_newest_version(self) -> tuple:
|
||||
""" Returns newest release version """
|
||||
try:
|
||||
response = self.request_release_data()
|
||||
|
|
Reference in a new issue