code:
import functools
import random
import re
import sys
import time
from io import BytesIO
from pathlib import Path

import tools
from dolreader import DolFile, SectionCountFullError, UnmappedAddressError
from fileutils import (get_alignment, read_uint16, read_uint32, write_bool,
                       write_sint32, write_ubyte, write_uint16, write_uint32)

try:
    import chardet
except ImportError as IE:
    print(IE)
    sys.exit(1)

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        value = func(*args, **kwargs)
        end = time.perf_counter()
        print(tools.color_text(f"\n  :: Completed in {(end - start):0.4f} seconds!\n", defaultColor=tools.TGREENLIT))
        return value
    return wrapper

class InvalidGeckoCodeError(Exception): pass

class GCT(object):

    def __init__(self, f):
        self.codeList = BytesIO(f.read())
        self.rawLineCount = tools.stream_size(self.codeList) >> 3
        self.lineCount = self.rawLineCount - 2
        f.seek(0)

    @property
    def size(self):
        return len(self.codeList.getbuffer())

    @staticmethod
    def determine_codelength(codetype, info: bytes) -> int:
        if codetype.startswith(b"\x06"):
            bytelength = int.from_bytes(info, byteorder="big", signed=False)
            padding = get_alignment(bytelength, 8)
            return 0x8 + bytelength + padding

        elif (codetype.startswith(b"\x08") or codetype.startswith(b"\x09")
            or codetype.startswith(b"\x18") or codetype.startswith(b"\x18")):
            return 0x16

        elif (codetype.startswith(b"\xC0") or codetype.startswith(b"\xC2") or codetype.startswith(b"\xC4")
            or codetype.startswith(b"\xC3") or codetype.startswith(b"\xC5") or codetype.startswith(b"\xD2")
            or codetype.startswith(b"\xD4") or codetype.startswith(b"\xD3") or codetype.startswith(b"\xD5")):
            return 0x8 + (int.from_bytes(info, byteorder="big", signed=False) << 3)

        elif (codetype.startswith(b"\xF2") or codetype.startswith(b"\xF3")
            or codetype.startswith(b"\xF4") or codetype.startswith(b"\xF5")):
            return 0x8 + (int.from_bytes(info[:2], byteorder="big", signed=False) << 3)

        elif codetype.startswith(b"\xF6"):
            return 0x8 + (int.from_bytes(info[:4], byteorder="big", signed=False) << 3)

        else:
            return 0x8

    def optimize_codelist(self, dolFile: DolFile):
        codelist = b"\x00\xD0\xC0\xDE"*2
        skipcodes = 0

        self.codeList.seek(8)
        while codetype := self.codeList.read(4):
            info = self.codeList.read(4)
            address = 0x80000000 | (int.from_bytes(codetype, byteorder="big", signed=False) & 0x1FFFFFF)
            try:
                if skipcodes <= 0:
                    if (codetype.startswith(b"\x00") or codetype.startswith(b"\x01")
                        or codetype.startswith(b"\x10") or codetype.startswith(b"\x11")):
                        dolFile.seek(address)

                        counter = int.from_bytes(info[:-2], byteorder="big", signed=False)
                        value = info[2:]

                        while counter + 1 > 0:
                            dolFile.write(value[1:])
                            counter -= 1
                        continue

                    elif (codetype.startswith(b"\x02") or codetype.startswith(b"\x03")
                        or codetype.startswith(b"\x12") or codetype.startswith(b"\x13")):
                        dolFile.seek(address)

                        counter = int.from_bytes(info[:-2], byteorder="big", signed=False)
                        value = info[2:]

                        while counter + 1 > 0:
                            dolFile.write(value)
                            counter -= 1
                        continue

                    elif (codetype.startswith(b"\x04") or codetype.startswith(b"\x05")
                        or codetype.startswith(b"\x14") or codetype.startswith(b"\x15")):
                        dolFile.seek(address)
                        dolFile.write(info)
                        continue

                    elif (codetype.startswith(b"\x06") or codetype.startswith(b"\x07")
                        or codetype.startswith(b"\x16") or codetype.startswith(b"\x17")):
                        dolFile.seek(address)

                        arraylength = int.from_bytes(info, byteorder="big", signed=False)
                        padding = get_alignment(arraylength, 8)

                        dolFile.write(self.codeList.read(arraylength))

                        self.codeList.seek(padding, 1)
                        continue

                    elif (codetype.startswith(b"\x08") or codetype.startswith(b"\x09")
                        or codetype.startswith(b"\x18") or codetype.startswith(b"\x19")):
                        dolFile.seek(address)

                        value = int.from_bytes(info, byteorder="big", signed=False)
                        data = read_uint16(self.codeList)
                        size = (data & 0x3000) >> 12
                        counter = data & 0xFFF
                        address_increment = read_uint16(self.codeList)
                        value_increment = read_uint32(self.codeList)

                        while counter + 1 > 0:
                            if size == 0:
                                write_ubyte(dolFile, value & 0xFF)
                                dolFile.seek(-1, 1)
                            elif size == 1:
                                write_uint16(dolFile, value & 0xFFFF)
                                dolFile.seek(-2, 1)
                            elif size == 2:
                                write_uint32(dolFile, value)
                                dolFile.seek(-4, 1)
                            else:
                                raise ValueError("Size type {} does not match 08 codetype specs".format(size))

                            dolFile.seek(address_increment, 1)
                            value += value_increment
                            counter -= 1
                            if value > 0xFFFFFFFF:
                                value -= 0x100000000
                        continue

                    elif (codetype.startswith(b"\xC6") or codetype.startswith(b"\xC7")
                        or codetype.startswith(b"\xC6") or codetype.startswith(b"\xC7")):
                        dolFile.insert_branch(int.from_bytes(info, byteorder="big", signed=False), address, lk=address&1)
                        continue

                if codetype.hex().startswith("2") or codetype.hex().startswith("3"):
                    skipcodes += 1

                elif codetype.startswith(b"\xE0"):
                    skipcodes -= 1

                elif codetype.startswith(b"\xF0"):
                    codelist += b"\xF0\x00\x00\x00\x00\x00\x00\x00"
                    break

                self.codeList.seek(-8, 1)
                codelist += self.codeList.read(GCT.determine_codelength(codetype, info))

            except (RuntimeError, UnmappedAddressError):
                self.codeList.seek(-8, 1)
                codelist += self.codeList.read(GCT.determine_codelength(codetype, info))

        self.codeList = BytesIO(codelist)

class CodeHandler(object):

    class Types:
        MINI = "MINI"
        FULL = "FULL"

    def __init__(self, f):
        self._rawData = BytesIO(f.read())

        """Get codelist pointer"""
        self._rawData.seek(0xFA)
        codelistUpper = self._rawData.read(2).hex()
        self._rawData.seek(0xFE)
        codelistLower = self._rawData.read(2).hex()

        self.codeListPointer = int(codelistUpper[2:] + codelistLower[2:], 16)
        self.handlerLength = tools.stream_size(self._rawData)
        self.initAddress = 0x80001800
        self.startAddress = 0x800018A8

        self.wiiVIHook = b"\x7C\xE3\x3B\x78\x38\x87\x00\x34\x38\xA7\x00\x38\x38\xC7\x00\x4C"
        self.gcnVIHook = b"\x7C\x03\x00\x34\x38\x83\x00\x20\x54\x85\x08\x3C\x7C\x7F\x2A\x14\xA0\x03\x00\x00\x7C\x7D\x2A\x14\x20\xA4\x00\x3F\xB0\x03\x00\x00"
        self.wiiGXDrawHook = b"\x3C\xA0\xCC\x01\x38\x00\x00\x61\x3C\x80\x45\x00\x98\x05\x80\x00"
        self.gcnGXDrawHook = b"\x38\x00\x00\x61\x3C\xA0\xCC\x01\x3C\x80\x45\x00\x98\x05\x80\x00"
        self.wiiPADHook = b"\x3A\xB5\x00\x01\x3A\x73\x00\x0C\x2C\x15\x00\x04\x3B\x18\x00\x0C"
        self.gcnPADHook = b"\x3A\xB5\x00\x01\x2C\x15\x00\x04\x3B\x18\x00\x0C\x3B\xFF\x00\x0C"

        self.allocation = None
        self.hookAddress = None
        self.hookType = None
        self.geckoCodes = None
        self.includeAll = False
        self.optimizeList = False

        if self.handlerLength < 0x900:
            self.type = CodeHandler.Types.MINI
        else:
            self.type = CodeHandler.Types.FULL

        f.seek(0)

    def init_gct(self, gctFile: Path, tmpdir: Path=None):
        if tmpdir is not None:
            _tmpGct = tmpdir / "gct.bin"
        else:
            _tmpGct = Path("gct.bin")

        if gctFile.suffix.lower() == ".txt":
            with _tmpGct.open("wb+") as temp:
                temp.write(bytes.fromhex("00D0C0DE"*2 + self.parse_input(gctFile) + "F000000000000000"))
                temp.seek(0)
                self.geckoCodes = GCT(temp)
        elif gctFile.suffix.lower() == ".gct":
            with gctFile.open("rb") as gct:
                self.geckoCodes = GCT(gct)
        elif gctFile.suffix == "":
            with _tmpGct.open("wb+") as temp:
                temp.write(b"\x00\xD0\xC0\xDE"*2)

                for file in gctFile.iterdir():
                    if file.is_file():
                        if file.suffix.lower() == ".txt":
                            temp.write(bytes.fromhex(self.parse_input(file)))
                        elif file.suffix.lower() == ".gct":
                            with file.open("rb") as gct:
                                temp.write(gct.read()[8:-8])
                        else:
                            print(tools.color_text(f"  :: HINT: {file} is not a .txt or .gct file", defaultColor=tools.TYELLOWLIT))

                temp.write(b"\xF0\x00\x00\x00\x00\x00\x00\x00")
                temp.seek(0)
                self.geckoCodes = GCT(temp)
        else:
            raise NotImplementedError(f"Parsing file type `{gctFile.suffix}' as a GCT is unsupported")

    def parse_input(self, geckoText: Path) -> str:
        with geckoText.open("rb") as gecko:
            result = chardet.detect(gecko.read())
            encodeType = result["encoding"]

        with geckoText.open("r", encoding=encodeType) as gecko:
            geckoCodes = ""
            state = None

            for line in gecko.readlines():
                if line in ("", "\n"):
                    continue

                if state is None:
                    if line.startswith("$") or line.startswith("["):
                        state = "Dolphin"
                    else:
                        state = "OcarinaM"

                try:
                    if state == "OcarinaM":
                        if self.includeAll:
                            geckoLine = re.findall(r"[A-F0-9]{8}[\t\f ][A-F0-9]{8}", line, re.IGNORECASE)[0]
                        else:
                            geckoLine = re.findall(r"(?:\*\s*)([A-F0-9]{8}[\t\f ][A-F0-9]{8})", line, re.IGNORECASE)[0]
                    else:
                        geckoLine = re.findall(r"(?<![$\*])[A-F0-9]{8}[\t\f ][A-F0-9]{8}", line, re.IGNORECASE)[0]
                except IndexError:
                    continue

                geckoCodes += geckoLine.replace(" ", "").strip()

        return geckoCodes

    @staticmethod
    def encrypt_key(key: int)  -> int:
        b1 = key & 0xFF
        b2 = (key >> 8) & 0xFF
        b3 = (key >> 16) & 0xFF
        b4 = (key >> 24) & 0xFF
        b3 ^= b4
        b2 ^= b3
        b1 ^= b2
        return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4

    def encrypt_codes(self, key: int):
        self.geckoCodes.codeList.seek(0)
        i = 0
        while True:
            try:
                packet = read_uint32(self.geckoCodes.codeList)
                self.geckoCodes.codeList.seek(-4, 1)
                write_uint32(self.geckoCodes.codeList, (packet^key) & 0xFFFFFFFF)
                key += (i << 3) & 0xFFFFFFFF
                if key > 0xFFFFFFFF:
                    key -= 0x100000000
                i += 1
            except:
                break

    def find_variable_data(self, variable) -> int:
        self._rawData.seek(0)

        while sample := self._rawData.read(4):
            if sample == variable:
                return self._rawData.tell() - 4

        return None

    def set_hook_instruction(self, dolFile: DolFile, address: int, varOffset: int, lk=0):
        self._rawData.seek(varOffset)
        dolFile.seek(address)
        ppc = read_uint32(dolFile)

        if ((((ppc >> 24) & 0xFF) > 0x47 and ((ppc >> 24) & 0xFF) < 0x4C) or (((ppc >> 24) & 0xFF) > 0x3F and ((ppc >> 24) & 0xFF) < 0x44)):
            to, conditional = dolFile.extract_branch_addr(address)
            if conditional:
                raise NotImplementedError("Hooking to a conditional non spr branch is unsupported")
            write_uint32(self._rawData, (to - (self.initAddress + varOffset)) & 0x3FFFFFD | 0x48000000 | lk)
        else:
            write_uint32(self._rawData, ppc)

    def set_variables(self, dolFile: DolFile):
        varOffset = self.find_variable_data(b"\x00\xDE\xDE\xDE")
        if varOffset is None:
            raise RuntimeError(tools.color_text("Variable codehandler data not found\n", defaultColor=tools.TREDLIT))

        self.set_hook_instruction(dolFile, self.hookAddress, varOffset, 0)

        self._rawData.seek(varOffset + 4)
        write_uint32(self._rawData, ((self.hookAddress + 4) - (self.initAddress + (varOffset + 4))) & 0x3FFFFFD | 0x48000000 | 0)

class KernelLoader(object):

    def __init__(self, f, cli: tools.CommandLineParser=None):
        self._rawData = BytesIO(f.read())
        self._initDataList = None
        self._gpModDataList = None
        self._gpDiscDataList = None
        self._gpKeyAddrList = None
        self._cli = cli
        self.initAddress = None
        self.protect = False
        self.verbosity = 0
        self.quiet = False
        self.encrypt = False

    def error(self, msg: str):
        if self._cli is not None:
            self._cli.error(msg)
        else:
            print(msg)
            sys.exit(1)

    def set_variables(self, entryPoint: list, baseOffset: int=0):
        self._rawData.seek(0)

        if self._gpModDataList is None:
            return

        while sample := self._rawData.read(2):
            if sample == b"GH":
                self._rawData.seek(-2, 1)
                write_uint16(self._rawData, self._gpModDataList[0])
            elif sample == b"GL":
                self._rawData.seek(-2, 1)
                write_uint16(self._rawData, baseOffset + self._gpModDataList[1])
            elif sample == b"IH":
                self._rawData.seek(-2, 1)
                write_uint16(self._rawData, entryPoint[0])
            elif sample == b"IL":
                self._rawData.seek(-2, 1)
                write_uint16(self._rawData, entryPoint[1])
            elif sample == b"KH":
                self._rawData.seek(-2, 1)
                write_uint16(self._rawData, self._gpKeyAddrList[0])
            elif sample == b"KL":
                self._rawData.seek(-2, 1)
                write_uint16(self._rawData, baseOffset + self._gpKeyAddrList[1])

    def complete_data(self, codeHandler: CodeHandler, initpoint: list):
        _upperAddr, _lowerAddr = ((self.initAddress >> 16) & 0xFFFF, self.initAddress & 0xFFFF)
        _key = random.randrange(0x100000000)
        self._rawData.seek(0)

        while sample := self._rawData.read(4):
            if sample == b"HEAP": #Found keyword "HEAP". Goes with the resize of the heap
                self._rawData.seek(-4, 1)

                gpModInfoOffset = self._rawData.tell()
                gpModUpperAddr = _upperAddr + 1 if (_lowerAddr + gpModInfoOffset) > 0x7FFF else _upperAddr #Absolute addressing

                if codeHandler.allocation == None:
                    codeHandler.allocation = (codeHandler.handlerLength + codeHandler.geckoCodes.size + 7) & -8

                write_uint32(self._rawData, codeHandler.allocation)

            elif sample == b"LSIZ": #Found keyword "LSIZ". Goes with the size of the loader
                self._rawData.seek(-4, 1)
                write_uint32(self._rawData, len(self._rawData.getbuffer()))

            elif sample == b"HSIZ": #Found keyword "HSIZ". Goes with the size of the codehandler
                self._rawData.seek(-4, 1)
                write_sint32(self._rawData, codeHandler.handlerLength)

            elif sample == b"CSIZ": #Found keyword "CSIZ". Goes with the size of the codes
                self._rawData.seek(-4, 1)
                write_sint32(self._rawData, codeHandler.geckoCodes.size)

            elif sample == b"HOOK": #Found keyword "HOOK". Goes with the codehandler hook
                self._rawData.seek(-4, 1)
                write_uint32(self._rawData, codeHandler.hookAddress)

            elif sample == b"CRPT": #Found keyword "CRPT". Boolean of the encryption
                self._rawData.seek(-4, 1)
                write_bool(self._rawData, self.encrypt, 4)

            elif sample == b"CYPT": #Found keyword "CYPT". Encryption Key
                self._rawData.seek(-4, 1)

                gpKeyOffset = self._rawData.tell()
                gpKeyUpperAddr = _upperAddr + 1 if (_lowerAddr + gpKeyOffset) > 0x7FFF else _upperAddr #Absolute addressing

                write_uint32(self._rawData, CodeHandler.encrypt_key(_key))

        if _lowerAddr + gpModInfoOffset > 0xFFFF:
            _lowerAddr -= 0x10000

        self._gpModDataList = (gpModUpperAddr, gpModInfoOffset)
        self._gpKeyAddrList = (gpKeyUpperAddr, gpKeyOffset)

        self.set_variables(initpoint, _lowerAddr)

        if self.encrypt:
            codeHandler.encrypt_codes(_key)

    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)
        self._rawData.write(codeHandler._rawData.getvalue() + codeHandler.geckoCodes.codeList.getvalue())

        self._rawData.seek(0)
        _kernelData = self._rawData.getvalue()

        try:
            dolFile.append_text_sections([(_kernelData, self.initAddress)])
        except SectionCountFullError:
            try:
                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))

        dolFile.entryPoint = self.initAddress
        return True, None

    def patch_legacy(self, codeHandler: CodeHandler, dolFile: DolFile) -> tuple:
        codeHandler._rawData.seek(0)
        codeHandler.geckoCodes.codeList.seek(0)

        _handlerData = codeHandler._rawData.getvalue() + codeHandler.geckoCodes.codeList.getvalue()

        try:
            dolFile.append_text_sections([(_handlerData, codeHandler.initAddress)])
        except SectionCountFullError:
            try:
                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 True, None

    def protect_game(self, codeHandler: CodeHandler):
        _oldpos = codeHandler.geckoCodes.codeList.tell()

        protectdata = (b"\xC0\x00\x00\x00\x00\x00\x00\x17",
					   b"\x7C\x08\x02\xA6\x94\x21\xFF\x70",
                       b"\x90\x01\x00\x08\xBC\x61\x00\x0C",
                       b"\x48\x00\x00\x0D\x00\xD0\xC0\xDE",
                       b"\x00\xD0\xDE\xAD\x7F\xE8\x02\xA6",
                       b"\x3B\xDF\x00\x08\x3C\x60\x80\x00",
                       b"\x38\x80\x11\x00\x38\xA0\x00\x00",
                       b"\x60\x63\x1E\xF8\x7C\x89\x03\xA6",
                       b"\x38\x80\x00\x00\x7D\x03\x22\x14",
                       b"\x54\xE9\x06\x3E\x89\x08\x00\x08",
                       b"\x7D\x3F\x48\xAE\x38\xE7\x00\x01",
                       b"\x7C\x08\x48\x40\x41\x82\x00\x0C",
                       b"\x60\xA7\x00\x00\x48\x00\x00\x04",
                       b"\x54\xE8\x06\x3E\x28\x08\x00\x03",
                       b"\x41\x81\x00\x10\x38\x84\x00\x01",
                       b"\x42\x00\xFF\xCC\x48\x00\x00\x2C",
                       b"\x38\xA0\x00\x08\x7C\x84\x1A\x14",
                       b"\x7C\xA9\x03\xA6\x38\x60\x00\x00",
                       b"\x38\x84\xFF\xFF\x54\x66\x07\xBE",
                       b"\x7C\xDE\x30\xAE\x38\x63\x00\x01",
                       b"\x9C\xC4\x00\x01\x42\x00\xFF\xF0",
                       b"\xB8\x61\x00\x0C\x80\x01\x00\x08",
                       b"\x38\x21\x00\x90\x7C\x08\x03\xA6",
                       b"\x4E\x80\x00\x20\x00\x00\x00\x00")

        codeHandler.geckoCodes.codeList.seek(-8, 2)

        for line in protectdata:
            codeHandler.geckoCodes.codeList.write(line)

        codeHandler.geckoCodes.codeList.write(b"\xF0\x00\x00\x00\x00\x00\x00\x00")
        codeHandler.geckoCodes.codeList.seek(_oldpos)

    @timer
    def build(self, gctFile: Path, dolFile: DolFile, codeHandler: CodeHandler, tmpdir: Path, dump: Path):
        _oldStart = dolFile.entryPoint

        """Initialize our codes"""

        codeHandler.init_gct(gctFile, tmpdir)

        if codeHandler.geckoCodes is None:
            self.error(tools.color_text("Valid codelist not found. Please provide a .txt/.gct file, or a folder of .txt/.gct files\n", defaultColor=tools.TREDLIT))

        if self.protect:
            self.protect_game(codeHandler)

        """Get entrypoint (or BSS midpoint) for insert"""

        if self.initAddress:
            try:
                dolFile.resolve_address(self.initAddress)
                self.error(tools.color_text(f"Init address specified for GeckoLoader (0x{self.initAddress:X}) clobbers existing dol sections", defaultColor=tools.TREDLIT))
            except UnmappedAddressError:
                pass
        else:
            self.initAddress = dolFile.seek_nearest_unmapped(dolFile.bssAddress, len(self._rawData.getbuffer()) + codeHandler.handlerLength + codeHandler.geckoCodes.size)
            self._rawData.seek(0)

        if codeHandler.optimizeList:
            codeHandler.geckoCodes.optimize_codelist(dolFile)

        """Is codelist optimized away?"""

        if codeHandler.geckoCodes.codeList.getvalue() == b"\x00\xD0\xC0\xDE\x00\xD0\xC0\xDE\xF0\x00\x00\x00\x00\x00\x00\x00":
            with dump.open("wb") as final:
                dolFile.save(final)

            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

        hooked = determine_codehook(dolFile, codeHandler, False)
        if hooked:
            _status, _msg = self.patch_arena(codeHandler, dolFile)
        else:
            self.error(tools.color_text("Failed to find a hook address. Try using option --codehook to use your own address\n", defaultColor=tools.TREDLIT))

        if _status is False:
            self.error(tools.color_text(_msg + "\n", defaultColor=tools.TREDLIT))
        elif codeHandler.allocation < codeHandler.geckoCodes.size:
            self.error(tools.color_text("Allocated codespace was smaller than the given codelist\n", defaultColor=tools.TYELLOW))

        with dump.open("wb") as final:
            dolFile.save(final)

        if self.quiet:
            return

        if self.verbosity >= 3:
            dolFile.print_info()
            print("-"*64)

        if self.verbosity >= 2:
            print("")
            info = [f"  :: Start of game modified to address 0x{self.initAddress: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 {DolFile.maxTextSections} text sections in this DOL file, {len(dolFile.textSections)} are now being used",
                    f"  :: Of the {DolFile.maxDataSections} data sections in this DOL file, {len(dolFile.dataSections)} are now being used"]

            for bit in info:
                print(tools.color_text(bit, defaultColor=tools.TGREENLIT))

        elif self.verbosity >= 1:
            print("")
            info = [f"  :: GeckoLoader set at address 0x{self.initAddress:X}",
                    f"  :: Legacy size is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}",
                    f"  :: Codehandler is of type `{codeHandler.type}'"]
            for bit in info:
                print(tools.color_text(bit, defaultColor=tools.TGREENLIT))

def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler, hook=False) -> bool:
    if codeHandler.hookAddress is None:
        if not assert_code_hook(dolFile, codeHandler):
            return False

    if hook:
        codeHandler.set_variables(dolFile)
        insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress)

    return True

def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler) -> bool:
    for section in dolFile.textSections:
        dolFile.seek(section["address"])
        sample = dolFile.read(section["size"])

        if codeHandler.hookType == "VI":
            result = sample.find(codeHandler.gcnVIHook)
        elif codeHandler.hookType == "GX":
            result = sample.find(codeHandler.gcnGXDrawHook)
        elif codeHandler.hookType == "PAD":
            result = sample.find(codeHandler.gcnPADHook)
        else:
            raise NotImplementedError(tools.color_text(f"Unsupported hook type specified ({codeHandler.hookType})", defaultColor=tools.TREDLIT))

        if result >= 0:
            dolFile.seek(section["address"] + result)
        else:
            if codeHandler.hookType == "VI":
                result = sample.find(codeHandler.wiiVIHook)
            elif codeHandler.hookType == "GX":
                result = sample.find(codeHandler.wiiGXDrawHook)
            elif codeHandler.hookType == "PAD":
                result = sample.find(codeHandler.wiiPADHook)
            else:
                raise NotImplementedError(tools.color_text(f"Unsupported hook type specified ({codeHandler.hookType})", defaultColor=tools.TREDLIT))

            if result >= 0:
                dolFile.seek(section["address"] + result)
            else:
                continue

        while (sample := read_uint32(dolFile)) != 0x4E800020:
            pass

        dolFile.seek(-4, 1)
        codeHandler.hookAddress = dolFile.tell()

        return True
    return False

def insert_code_hook(dolFile: DolFile, codeHandler: CodeHandler, address: int):
    dolFile.seek(address)
    ppc = read_uint32(dolFile)

    if ((ppc >> 24) & 0xFF) > 0x3F and ((ppc >> 24) & 0xFF) < 0x48:
        raise NotImplementedError(tools.color_text("Hooking the codehandler to a conditional non spr branch is unsupported", defaultColor=tools.TREDLIT))

    dolFile.seek(-4, 1)
    dolFile.insert_branch(codeHandler.startAddress, address, lk=0)


code:
//#include <vector>
//#include <string>

//using std::array;
//using std::memcpy;
//using std::memcmp;
//using std::memset;
//using std::string;

#define dcbst(_val) asm volatile("dcbst 0, %0" \
                                 :             \
                                 : "r"(_val))
#define dcbf(_val) asm volatile("dcbf 0, %0" \
                                :            \
                                : "r"(_val))
#define icbi(_val) asm volatile("icbi 0, %0" \
                                :            \
                                : "r"(_val))

#define call(addr) ((void (*)(...))addr)
#define CODEHANDLER 0x800018A8
#define GCT_MAGIC 0x00D0C0DE

#define __start call(0x4948494C)

using u32 = unsigned int;
using u16 = unsigned short;
using u8 = unsigned char;
using s32 = int;
using s16 = short;
using s8 = char;
using f32 = float;
using f64 = double;

__attribute__((noreturn)) int main();

struct CodeList
{

  u16 mBaseASM;
  u16 mUpperBase;
  u16 mOffsetASM;
  u16 mLowerOffset;
};

struct Info
{

  const u32 allocsize;
  const u32 loaderSize;
  const u32 handlerSize;
  const u32 codeSize;
  const u32 *codehandlerHook;
  const u32 crypted;
};

class DiscHeader
{

public:
  enum class TVMODE
  {
    NTSC,
    PAL,
    DEBUG,
    DEBUGPAL,
    MPAL,
    PAL60
  };

  struct MetaData
  {

    const u8 mDiscID;                 //0x0000
    const u16 mGameCode;              //0x0001
    const u8 mRegionCode;             //0x0003
    const u16 mMakerCode;             //0x0004
    const u8 mDiscNumber;             //0x0006
    const u8 mDiscVersion;            //0x0007
    const u8 mAudioStreaming;         //0x0008
    const u8 mStreamBufferSize;       //0x0009
    const u8 _00[12];                 //0x000A
    const u32 mWiiMagic;              //0x0018
    const u32 mGCNMagic;              //0x001C
    const u32 mNinBootCode;           //0x0020
    const u32 mAppVersion;            //0x0024
    const u32 mPhysicalRAMSize;       //0x0028
    const u32 mBoardModel;            //0x002C
    u8 *mOSArenaLo;                   //0x0030
    u8 *mOSArenaHi;                   //0x0034
    u32 *mFstStart;                   //0x0038
    u32 mFstSize;                     //0x003C
    u32 mDebuggerPresent;             //0x0040
    const u32 mDebuggerExceptionMask; //0x0044
    void *mExceptionHookDest;         //0x0048
    const u32 mExceptionReturn;       //0x004C
    u32 _01[0x10 / 4];                //0x0050
    u32 mDebuggerHook[0x24 / 4];      //0x0060
    u32 _02[0x3C / 4];                //0x0084
    u32 mCurrentOSContext;            //0x00C0
    u32 mPreviousOSMask;              //0x00C4
    u32 mCurrentOSMask;               //0x00C8
    DiscHeader::TVMODE mTVMode;       //0x00CC
    u32 mARAMSize;                    //0x00D0
    void *mCurOSContextLogical;       //0x00D4
    void *mDefaultOSThreadLogical;    //0x00D8
    u32 *mThreadQueueHead;            //0x00DC
    u32 *mThreadQueueTail;            //0x00E0
    u32 *mCurrentOSThread;            //0x00E4
    u32 mDebuggerSize;                //0x00E8
    u32 *mDebuggerMonitorLoc;         //0x00EC
    u32 mSimulatedMemSize;            //0x00F0
    u8 *mBi2HeaderLoc;                //0x00F4
    u32 mBusClockSpeed;               //0x00F8
    u32 mCPUClockSpeed;               //0x00FC
    u32 _04[0x3010 / 4];              //0x0100
    u8 *mWiiHeap;                     //0x3110
  };

  static MetaData sMetaData;

  enum class CONSOLETYPE
  {
    Gamecube,
    Wii,
    Unknown
  };

  inline u32 getGameID() { return ((u32)sMetaData.mDiscID << 24) | ((u32)sMetaData.mGameCode << 8) | ((u32)sMetaData.mRegionCode); }
  inline u16 getMakerID() { return sMetaData.mMakerCode; }
  inline u8 getDiscNumber() { return sMetaData.mDiscNumber; }
  inline u8 getDiscVersion() { return sMetaData.mDiscVersion; }

  CONSOLETYPE detectHomeConsole()
  {
    if (sMetaData.mGCNMagic)
    {
      return CONSOLETYPE::Gamecube;
    }
    else if (sMetaData.mWiiMagic)
    {
      return CONSOLETYPE::Wii;
    }

    return CONSOLETYPE::Unknown;
  }

  inline void setHeap(u32 alloc)
  {
    if (sMetaData.mBi2HeaderLoc < sMetaData.mOSArenaHi)
    {
      sMetaData.mOSArenaHi = sMetaData.mBi2HeaderLoc - alloc;
      if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii)
      {
        sMetaData.mWiiHeap = sMetaData.mBi2HeaderLoc - alloc;
      }
    }
    else
    {
      if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii)
      {
        sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc;
        sMetaData.mWiiHeap -= alloc;
      }
      else
      {
        sMetaData.mOSArenaHi -= alloc;
      }

    }
  }
};

static DiscHeader sDisc;
Info gpModInfo = {
    0x48454150,
    0x4C53495A,
    0x4853495A,
    0x4353495A,
    (const u32 *)0x484F4F4B,
    0x43525054,
};

inline u32 extractBranchAddr(u32 *bAddr)
{
  s32 offset;
  if (*bAddr & 0x2000000)
    offset = (*bAddr & 0x3FFFFFD) - 0x4000000;
  else
    offset = *bAddr & 0x3FFFFFD;
  return (u32)bAddr + offset;
}

namespace Memory
{

  static void memcpy(u8 *to, u8 *from, s32 size)
  {
    for (s32 i = 0; i < size; ++i)
    {
      *to++ = *from++;
    }
  }

  namespace Cache
  {

    static inline void flushAddr(void *addr)
    {
      dcbf(addr);
      icbi(addr);
    }

    static void flushRange(u8 *addr, s32 size)
    {
      size += 31 + (((u32)addr & 31) > 0);

      for (u32 i = 0; i < (size >> 5); ++i)
      {
        flushAddr((void *)(addr + (i << 5)));
      }
    }

    static void storeAddr(void *addr)
    {
      dcbst(addr);
      icbi(addr);
    }

    static void storeRange(u8 *addr, s32 size)
    {
      size += 31 + (((u32)addr & 31) > 0);

      for (u32 i = 0; i < (size >> 5); ++i)
      {
        storeAddr((void *)(addr + (i << 5)));
      }
    }

  } // namespace Cache

  namespace Direct
  {

    template <typename T>
    static inline void write(T *addr, T value)
    {
      *addr = value;
      Cache::flushAddr(addr);
    }

    /*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | BRANCH_TYPE | !!isLink*/
    static inline void branch(void *addr, void *to, bool lk)
    {
      Direct::write<u32>((u32 *)(addr), ((((u32)(to) - (u32)(addr)) & 0x3ffffff) | 0x48000000 | lk));
    }

  } // namespace Direct

  namespace Search
  {

    static u32 *array(u32 *start, u32 *end, u32 arrayLength, const u32 *hookData)
    {
      u32 index = 0;

      /*Loop through the games RAM, make sure we don't find our own hook data by accident*/
      for (u32 i = 0; &start[i] < end; ++i)
      {
        /*If the data matches, increase the index counter and continue search,
        else set index to 0 and continue searching*/
        if (start[i] == hookData[index])
          ++index;
        else
          index = 0;

        /*If the data has matched the whole array, return the address of the match*/
        if (index >= (arrayLength) && (&start[i] < (u32 *)&gpModInfo || &start[i] > (u32 *)&gpModInfo + sizeof(Info)))
          return &start[i];
      }
      return nullptr;
    }

    template <typename T>
    static T *single(T *start, T *end, T match)
    {
      for (u32 i = 0; &start[i] < end; ++i)
      {
        if (start[i] == match)
        {
          return &start[i];
        }
      }
      return nullptr;
    }

    /*Call this after viHook, finds the address of the first instance
    of targetVal, and hooks it to the pointer hookTo*/
    static inline void hookFunction(u32 *start, u32 targetVal, void *hookTo, bool lk)
    {
      Direct::branch(Search::single<u32>(start, start + 0x500, targetVal), hookTo, lk);
    }

  } // namespace Search

  class Crypt
  {

  private:
    u32 key;

    u32 getKey()
    {
      u32 b1 = (this->key >> 24) & 0xFF;
      u32 b2 = (this->key >> 16) & 0xFF;
      u32 b3 = (this->key >> 8) & 0xFF;
      u32 b4 = this->key & 0xFF;
      b1 ^= b2;
      b2 ^= b3;
      b3 ^= b4;
      return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
    }

    void setKey(u32 key)
    {
      u32 b1 = key & 0xFF;
      u32 b2 = (key >> 8) & 0xFF;
      u32 b3 = (key >> 16) & 0xFF;
      u32 b4 = (key >> 24) & 0xFF;
      b3 ^= b4;
      b2 ^= b3;
      b1 ^= b2;
      this->key = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
    }

  public:
    Crypt(u32 key)
    {
      this->key = key;
    }

    inline void xorCrypt(u32 *dest, u32 *buffer, u32 size)
    {
      auto key = this->getKey();

      for (u32 i = 0; i < size; ++i)
      {
        dest[i] = buffer[i] ^ key;
        key += i << 3;
      }
    }
  };

  enum class Space : u32
  {
    Start = 0x80000000,
    End = 0x81800000,
    Size = 0x1800000
  };

} // namespace Memory

Memory::Crypt gpCryptor = {0x43595054};

static void initMods()
{
  sDisc.setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/

  /*Change codelist pointer to the new address in the allocation*/
  CodeList *codelistPointer = (CodeList *)((u32)&gpModInfo + sizeof(Info) + 0xFC);
  codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF;
  codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF;

  /*Copy codelist to the new allocation*/
  if (gpModInfo.crypted)
  {
    Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize);
    gpCryptor.xorCrypt((u32 *)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32 *)((u8 *)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2);
  }
  else
  {
    Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8 *)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize);
  }

  /*Get codehandler hook resources*/
  auto fillInField = Memory::Search::single<u32>((u32 *)sDisc.sMetaData.mOSArenaHi, (u32 *)(sDisc.sMetaData.mOSArenaHi + 0x600), 0x00DEDEDE);
  auto returnAddress = extractBranchAddr((u32 *)gpModInfo.codehandlerHook);
  auto ppc = *gpModInfo.codehandlerHook;

  /*Write hook branch*/
  Memory::Direct::branch((void *)gpModInfo.codehandlerHook, (void *)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), false); //entryhook

  /*Temporary nop*/
  *fillInField = 0x60000000;

  /*Flush the cache so that the instructions update*/
  Memory::Cache::flushRange((u8 *)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize);

  /*Call the codehandler*/
  call((void *)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))();

  /*Write original instruction or translate offset data if a branch*/
  if (((ppc >> 24) & 0xFF) > 0x47 && ((ppc >> 24) & 0xFF) < 0x4C)
  {
    Memory::Direct::branch((void *)fillInField, (void *)returnAddress, ppc & 1);
  }
  else
  {
    Memory::Direct::write(fillInField, ppc);
  }

  /*Write branch back to the hook address + 4*/
  Memory::Direct::branch((void *)&fillInField[1], (void *)(&gpModInfo.codehandlerHook[1]), false); //return
}

int main()
{
  if (sDisc.detectHomeConsole() != DiscHeader::CONSOLETYPE::Unknown)
  {
    initMods();
  }
  __start();
}

repo_name: Humming/Owl

license: mit


code:
from PyQt5 import QtCore, QtGui, QtWidgets

from children_ui import PrefWindow
from fileutils import resource_path

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(str(resource_path("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(176, 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(176, 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(176, 23))
        self.allocLineEdit.setMaximumSize(QtCore.QSize(176, 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)

        # 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(176, 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, 1, 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(176, 23))
        self.hookTypeSelect.setMaximumSize(QtCore.QSize(176, 23))
        self.hookTypeSelect.setObjectName("hookTypeSelect")
        self.hookTypeSelect.addItems(["VI", "GX", "PAD"])
        self.optionsLayout.addWidget(self.hookTypeSelect, 2, 1, 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(176, 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, 1, 2, 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(176, 23))
        self.txtCodesIncludeSelect.setMaximumSize(QtCore.QSize(176, 23))
        self.txtCodesIncludeSelect.setObjectName("txtCodesIncludeSelect")
        self.txtCodesIncludeSelect.addItems(["ACTIVE", "ALL"])
        self.optionsLayout.addWidget(self.txtCodesIncludeSelect, 2, 2, 1, 1)

        # horizontal separater options
        self.horiSepOptions = QtWidgets.QFrame(self.centerWidget)
        self.horiSepOptions.setMinimumSize(QtCore.QSize(300, 30))
        self.horiSepOptions.setContentsMargins(20, 0, 20, 0)
        self.horiSepOptions.setFrameShape(QtWidgets.QFrame.HLine)
        self.horiSepOptions.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.horiSepOptions.setObjectName("horiSepOptions")
        self.optionsLayout.addWidget(self.horiSepOptions, 3, 0, 1, 4)

        # 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, 0, 1, 4)

        # 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):
                strlength = len(item.text())
                cursorPos = item.cursorPosition()
                item.setText(item.text().lstrip())
                item.setCursorPosition(cursorPos - (strlength - len(item.text())))
            elif isinstance(item, QtWidgets.QPlainTextEdit):
                sliderPos = item.verticalScrollBar().sliderPosition()
                item.setPlainText(item.toPlainText().lstrip())
                item.verticalScrollBar().setSliderPosition(sliderPos)

    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.hookTypeLabel.setEnabled(True)
        self.hookTypeSelect.setEnabled(True)
        self.txtCodesIncludeLabel.setEnabled(True)
        self.txtCodesIncludeSelect.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.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.exOptionsButton.setText(
            QtWidgets.QApplication.translate("MainWindow", "Advanced Settings", None)
        )

        self.compileButton.setText(
            QtWidgets.QApplication.translate("MainWindow", "RUN", None)
        )


code:
import os
import sys
from cx_Freeze import setup, Executable

include_files = ["bin/"]
excludes = ["tkinter"]

options = {
    "build_exe": {"optimize": 4, "excludes": excludes, "include_files": include_files}
}

executables = [
    Executable("GeckoLoader.py"),
]

setup(
    name="GeckoLoader",
    version="7.1.1",
    description="DOL Patcher for extending the codespace of Wii/GC games",
    executables=[Executable("GeckoLoader.py", icon=os.path.join("bin", "icon.ico"))],
    author="JoshuaMK",
    author_email="joshuamkw2002@gmail.com",
    options=options,
)


code:
import struct
import sys
import os
from io import IOBase
from argparse import ArgumentParser

try:
    import colorama
    from colorama import Fore, Style
    colorama.init()
    TRESET = Style.RESET_ALL
    TGREEN = Fore.GREEN
    TGREENLIT = Style.BRIGHT + Fore.GREEN
    TYELLOW = Fore.YELLOW
    TYELLOWLIT = Style.BRIGHT + Fore.YELLOW
    TRED = Fore.RED
    TREDLIT = Style.BRIGHT + Fore.RED

except ImportError:
    TRESET = ""
    TGREEN = ""
    TGREENLIT = ""
    TYELLOW = ""
    TYELLOWLIT = ""
    TRED = ""
    TREDLIT = ""

def get_alignment(number: int, align: int) -> int:
    if number % align != 0:
        return align - (number % align)
    else:
        return 0

def stream_size(obj, ofs: int = 0) -> int:
    if hasattr(obj, "getbuffer"):
        return len(obj.getbuffer()) + ofs
    elif hasattr(obj, "tell") and hasattr(obj, "seek"):
        _pos = obj.tell()
        obj.seek(0, 2)
        _size = obj.tell()
        obj.seek(_pos, 1)
        return _size + ofs
    else:
        raise NotImplementedError(f"Getting the stream size of class {type(obj)} is unsupported")

def align_byte_size(obj, alignment: int, fillchar="00"):
    if isinstance(obj, bytes):
        obj += bytes.fromhex(fillchar * get_alignment(len(obj), alignment))
    elif isinstance(obj, bytearray):
        obj.append(bytearray.fromhex(fillchar * get_alignment(len(obj), alignment)))
    elif issubclass(type(obj), IOBase):
        _size = stream_size(obj)
        obj.write(bytes.fromhex(fillchar * get_alignment(_size, alignment)))
    else:
        raise NotImplementedError(f"Aligning the size of class {type(obj)} is unsupported")

def color_text(text: str, textToColor: list=[("", None)], defaultColor: str=None) -> str:
    currentColor = None
    formattedText = ""

    format = False
    for itemPair in textToColor:
        if itemPair[0] != "" and itemPair[1] is not None:
            format = True
            break

    if not format:
        return defaultColor + text + TRESET

    for char in text:
        handled = False
        for itemPair in textToColor:
            if (char in itemPair[0] or r"\*" in itemPair[0]) and itemPair[1] is not None:
                if currentColor != itemPair[1]:
                    formattedText += TRESET
                    formattedText += itemPair[1]
                    currentColor = itemPair[1]
                handled = True

            elif defaultColor is not None:
                formattedText += TRESET
                formattedText += defaultColor
                currentColor = defaultColor

            elif currentColor is not None:
                formattedText += TRESET
                currentColor = None

            if handled:
                break

        formattedText += char

    return formattedText + TRESET

class CommandLineParser(ArgumentParser):

    def error(self, message: str, prefix: str=None, print_usage=True, exit=True):
        if print_usage:
            self.print_usage(sys.stderr)

        if prefix is None:
            if exit:
                self.exit(2, f"{self.prog}: error: {message}\n")
            else:
                self._print_message(f"{self.prog}: error: {message}\n")
        else:
            if exit:
                self.exit(2, f"{prefix} {message}\n")
            else:
                self._print_message(f"{prefix} {message}\n")


code:
from urllib import request
from bs4 import BeautifulSoup

class Updater(object):

    def __init__(self, owner: str, repository: str):
        self.owner = owner
        self.repo = repository
        self.gitReleases = "https://github.com/{}/{}/releases/latest"

    def request_release_data(self):
        """Returns soup data of the repository releases tab"""
        with request.urlopen(
            self.gitReleases.format(self.owner, self.repo)
        ) as response:
            html = response.read()
        return html

    def get_newest_version(self) -> tuple:
        """Returns newest release version"""
        try:
            response = self.request_release_data()
            soup = BeautifulSoup(response, "html.parser")
            return (
                soup.find(
                    "a",
                    {
                        "class": "Link",
                        "href": "/{}/{}/releases".format(self.owner, self.repo),
                    },
                )
                .find_next("a", {"class": "Link"})
                .get_text(strip=True),
                True,
            )
        except request.HTTPError as e:
            return f"HTTP request failed with error code ({e.code})", False
        except request.URLError:
            return (
                "Request failed, ensure you have a working internet connection and try again",
                False,
            )
        except AttributeError:
            return (
                "Failed to parse release data, consider contacting JoshuaMK: joshuamkw2002@gmail.com",
                False,
            )


code:
# functions for general XML files
import file_ops
from lxml import etree

# function to check if a XML file is well formatted
def check_xml_well_format(xml_path):

  # check params
  if (file_ops.is_file(xml_path) == False):
    return False

  # load the xml file and check for syntax errors
  # generic parser
  generic_parser = etree.XMLParser(ns_clean = True, remove_comments = True)
  xml = None
  try:
    xml = etree.parse(xml_path, generic_parser)
  # some errors are not saved on parser's error_log for some reason
  except etree.XMLSyntaxError as e:
    print("\nSYNTAX ERRORS ON XML:")
    print(e)
    return False
  # print the parser error_log errors
  except:
    print("\nOTHER ERRORS ON XML:")
    for error in generic_parser.error_log:
      print("Line %s || %s" % (error.line, error.message))
    return False

  # all good!
  return True

# function to check if an schema (XSD) file is well formatted
def check_sch_well_format(xsd_path):

  # check params
  if (check_xml_well_format(xsd_path) == False):
    return False

  # load the XMLSchema.xsd as a parser for the schema files
  schema_tree = etree.parse("XMLSchema.xsd")
  schema_schema = etree.XMLSchema(schema_tree)
  schema_parser = etree.XMLParser(schema = schema_schema)

  # load the custom schema file and check if it has errors
  try:
    custom_schema_tree = etree.parse(xsd_path, schema_parser)
    custom_schema_schema = etree.XMLSchema(custom_schema_tree)
  # some errors are not saved on parser's error_log for some reason
  except etree.XMLSyntaxError as e:
    print("\nSYNTAX ERRORS ON XSD:")
    print(e)
    return False
  except etree.XMLSchemaParseError as e:
    print("\nSCHEMA PARSING ERRORS ON XSD:")
    print(e)
    return False
  # print the parser error_log errors
  except:
    print("\nOTHER ERRORS ON XSD:")
    for error in schema_parser.error_log:
      print("Line %s || %s" % (error.line, error.message))
    return False

  # all good!
  return True

# function to check an XML against an schema (XSD)
def check_with_sch(xml_path, xsd_path):

  # check params
  if ((check_xml_well_format(xml_path) == False) or (check_sch_well_format(xsd_path) == False)):
    return False

  # load the collada file and check for errors agaisnt the custom schema
  schema_tree = etree.parse(xsd_path)
  schema_schema = etree.XMLSchema(schema_tree)
  schema_parser = etree.XMLParser(schema = schema_schema)
  try:
    xml = etree.parse(xml_path, schema_parser)
  # some errors are not saved on parser's error_log for some reason
  except etree.XMLSyntaxError as e:
    print("\nSYNTAX ERRORS ON CUSTOM SCHEMA CHECKING XML:")
    print(e)
    return False
  # print the parser error_log errors
  except etree.XMLSchemaValidateError:
    print("\nOTHER ERRORS ON CUSTOM SCHEMA CHECKING XML:")
    for error in custom_schema_parser.error_log:
      print("Line %s || %s" % (error.line, error.message))
    return False

  # done!
  return True


code:
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!11 &1
AudioManager:
  m_ObjectHideFlags: 0
  m_Volume: 1
  Rolloff Scale: 1
  Doppler Factor: 1
  Default Speaker Mode: 2
  m_SampleRate: 0
  m_DSPBufferSize: 1024
  m_VirtualVoiceCount: 512
  m_RealVoiceCount: 32
  m_SpatializerPlugin:
  m_AmbisonicDecoderPlugin:
  m_DisableAudio: 0
  m_VirtualizeEffects: 1

license: unknown


code:
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!236 &1
ClusterInputManager:
  m_ObjectHideFlags: 0
  m_Inputs: []


code:
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!55 &1
PhysicsManager:
  m_ObjectHideFlags: 0
  serializedVersion: 7
  m_Gravity: {x: 0, y: -9.81, z: 0}
  m_DefaultMaterial: {fileID: 0}
  m_BounceThreshold: 2
  m_SleepThreshold: 0.005
  m_DefaultContactOffset: 0.01
  m_DefaultSolverIterations: 6
  m_DefaultSolverVelocityIterations: 1
  m_QueriesHitBackfaces: 0
  m_QueriesHitTriggers: 1
  m_EnableAdaptiveForce: 0
  m_ClothInterCollisionDistance: 0
  m_ClothInterCollisionStiffness: 0
  m_ContactsGeneration: 1
  m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
  m_AutoSimulation: 1
  m_AutoSyncTransforms: 0
  m_ReuseCollisionCallbacks: 1
  m_ClothInterCollisionSettingsToggle: 0
  m_ContactPairsMode: 0
  m_BroadphaseType: 0
  m_WorldBounds:
    m_Center: {x: 0, y: 0, z: 0}
    m_Extent: {x: 250, y: 250, z: 250}
  m_WorldSubdivisions: 8


code:
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1045 &1
EditorBuildSettings:
  m_ObjectHideFlags: 0
  serializedVersion: 2
  m_Scenes:
  - enabled: 1
    path: Assets/Scenes/SampleScene.unity
    guid: d1c3109bdb54ad54c8a2b2838528e640
  m_configObjects: {}


code:
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!159 &1
EditorSettings:
  m_ObjectHideFlags: 0
  serializedVersion: 7
  m_ExternalVersionControlSupport: Visible Meta Files
  m_SerializationMode: 2
  m_LineEndingsForNewScripts: 0
  m_DefaultBehaviorMode: 0
  m_SpritePackerMode: 0
  m_SpritePackerPaddingPower: 1
  m_EtcTextureCompressorBehavior: 1
  m_EtcTextureFastCompressor: 1
  m_EtcTextureNormalCompressor: 2
  m_EtcTextureBestCompressor: 4
  m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd
  m_ProjectGenerationRootNamespace:
  m_UserGeneratedProjectSuffix:
  m_CollabEditorSettings:
    inProgressEnabled: 1


code:
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!30 &1
GraphicsSettings:
  m_ObjectHideFlags: 0
  serializedVersion: 12
  m_Deferred:
    m_Mode: 1
    m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}
  m_DeferredReflections:
    m_Mode: 1
    m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0}
  m_ScreenSpaceShadows:
    m_Mode: 1
    m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0}
  m_LegacyDeferred:
    m_Mode: 1
    m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0}
  m_DepthNormals:
    m_Mode: 1
    m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0}
  m_MotionVectors:
    m_Mode: 1
    m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0}
  m_LightHalo:
    m_Mode: 1
    m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0}
  m_LensFlare:
    m_Mode: 1
    m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0}
  m_AlwaysIncludedShaders:
  - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0}
  - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0}
  - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0}
  - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0}
  - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
  - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
  m_PreloadedShaders: []
  m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,
    type: 0}
  m_CustomRenderPipeline: {fileID: 11400000, guid: 19ba41d7c0026c3459d37c2fe90c55a0,
    type: 2}
  m_TransparencySortMode: 0
  m_TransparencySortAxis: {x: 0, y: 0, z: 1}
  m_DefaultRenderingPath: 1
  m_DefaultMobileRenderingPath: 1
  m_TierSettings: []
  m_LightmapStripping: 0
  m_FogStripping: 0
  m_InstancingStripping: 0
  m_LightmapKeepPlain: 1
  m_LightmapKeepDirCombined: 1
  m_LightmapKeepDynamicPlain: 1
  m_LightmapKeepDynamicDirCombined: 1
  m_LightmapKeepShadowMask: 1
  m_LightmapKeepSubtractive: 1
  m_FogKeepLinear: 1
  m_FogKeepExp: 1
  m_FogKeepExp2: 1
  m_AlbedoSwatchInfos: []
  m_LightsUseLinearIntensity: 1
  m_LightsUseColorTemperature: 0


code:
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!13 &1
InputManager:
  m_ObjectHideFlags: 0
  serializedVersion: 2
  m_Axes:
  - serializedVersion: 3
    m_Name: Horizontal
    descriptiveName:
    descriptiveNegativeName:
    negativeButton: left
    positiveButton: right
    altNegativeButton: a
    altPositiveButton: d
    gravity: 3
    dead: 0.001
    sensitivity: 3
    snap: 1
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Vertical
    descriptiveName:
    descriptiveNegativeName:
    negativeButton: down
    positiveButton: up
    altNegativeButton: s
    altPositiveButton: w
    gravity: 3
    dead: 0.001
    sensitivity: 3
    snap: 1
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Fire1
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: left ctrl
    altNegativeButton:
    altPositiveButton: mouse 0
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Fire2
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: left alt
    altNegativeButton:
    altPositiveButton: mouse 1
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Fire3
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: left shift
    altNegativeButton:
    altPositiveButton: mouse 2
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Jump
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: space
    altNegativeButton:
    altPositiveButton:
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Mouse X
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton:
    altNegativeButton:
    altPositiveButton:
    gravity: 0
    dead: 0
    sensitivity: 0.1
    snap: 0
    invert: 0
    type: 1
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Mouse Y
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton:
    altNegativeButton:
    altPositiveButton:
    gravity: 0
    dead: 0
    sensitivity: 0.1
    snap: 0
    invert: 0
    type: 1
    axis: 1
    joyNum: 0
  - serializedVersion: 3
    m_Name: Mouse ScrollWheel
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton:
    altNegativeButton:
    altPositiveButton:
    gravity: 0
    dead: 0
    sensitivity: 0.1
    snap: 0
    invert: 0
    type: 1
    axis: 2
    joyNum: 0
  - serializedVersion: 3
    m_Name: Horizontal
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton:
    altNegativeButton:
    altPositiveButton:
    gravity: 0
    dead: 0.19
    sensitivity: 1
    snap: 0
    invert: 0
    type: 2
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Vertical
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton:
    altNegativeButton:
    altPositiveButton:
    gravity: 0
    dead: 0.19
    sensitivity: 1
    snap: 0
    invert: 1
    type: 2
    axis: 1
    joyNum: 0
  - serializedVersion: 3
    m_Name: Fire1
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: joystick button 0
    altNegativeButton:
    altPositiveButton:
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Fire2
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: joystick button 1
    altNegativeButton:
    altPositiveButton:
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Fire3
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: joystick button 2
    altNegativeButton:
    altPositiveButton:
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Jump
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: joystick button 3
    altNegativeButton:
    altPositiveButton:
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Submit
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: return
    altNegativeButton:
    altPositiveButton: joystick button 0
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Submit
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: enter
    altNegativeButton:
    altPositiveButton: space
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0
  - serializedVersion: 3
    m_Name: Cancel
    descriptiveName:
    descriptiveNegativeName:
    negativeButton:
    positiveButton: escape
    altNegativeButton:
    altPositiveButton: joystick button 1
    gravity: 1000
    dead: 0.001
    sensitivity: 1000
    snap: 0
    invert: 0
    type: 0
    axis: 0
    joyNum: 0


code:
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!126 &1
NavMeshProjectSettings:
  m_ObjectHideFlags: 0
  serializedVersion: 2
  areas:
  - name: Walkable
    cost: 1
  - name: Not Walkable
    cost: 1
  - name: Jump
    cost: 2
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  - name:
    cost: 1
  m_LastAgentTypeID: -887442657
  m_Settings:
  - serializedVersion: 2
    agentTypeID: 0
    agentRadius: 0.5
    agentHeight: 2
    agentSlope: 45
    agentClimb: 0.75
    ledgeDropHeight: 0
    maxJumpAcrossDistance: 0
    minRegionArea: 2
    manualCellSize: 0
    cellSize: 0.16666667
    manualTileSize: 0
    tileSize: 256
    accuratePlacement: 0
    debug:
      m_Flags: 0
  m_SettingNames:
  - Humanoid


code:
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!149 &1
NetworkManager:
  m_ObjectHideFlags: 0
  m_DebugLevel: 0
  m_Sendrate: 15
  m_AssetToPrefab: {}


code:
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!19 &1
Physics2DSettings:
  m_ObjectHideFlags: 0
  serializedVersion: 3
  m_Gravity: {x: 0, y: -9.81}
  m_DefaultMaterial: {fileID: 0}
  m_VelocityIterations: 8
  m_PositionIterations: 3
  m_VelocityThreshold: 1
  m_MaxLinearCorrection: 0.2
  m_MaxAngularCorrection: 8
  m_MaxTranslationSpeed: 100
  m_MaxRotationSpeed: 360
  m_BaumgarteScale: 0.2
  m_BaumgarteTimeOfImpactScale: 0.75
  m_TimeToSleep: 0.5
  m_LinearSleepTolerance: 0.01
  m_AngularSleepTolerance: 2
  m_DefaultContactOffset: 0.01
  m_AutoSimulation: 1
  m_QueriesHitTriggers: 1
  m_QueriesStartInColliders: 1
  m_ChangeStopsCallbacks: 0
  m_CallbacksOnDisable: 1
  m_ReuseCollisionCallbacks: 1
  m_AutoSyncTransforms: 0
  m_AlwaysShowColliders: 0
  m_ShowColliderSleep: 1
  m_ShowColliderContacts: 0
  m_ShowColliderAABB: 0
  m_ContactArrowScale: 0.2
  m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412}
  m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432}
  m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745}
  m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804}
  m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff