Exemple #1
0
    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
Exemple #2
0
    def _exec(self, args, tmpdir):
        context = self._validate_args(args)

        try:
            with context["dol"].open("rb") as dol:
                dolFile = DolFile(dol)

            with resource_path(context["codehandler"]).open("rb") as handler:
                codeHandler = CodeHandler(handler)
                codeHandler.allocation = context["allocation"]
                codeHandler.hookAddress = context["hookaddress"]
                codeHandler.hookType = context["hooktype"]
                codeHandler.includeAll = context["includeall"]
                codeHandler.optimizeList = context["optimize"]

            with resource_path("bin/geckoloader.bin").open("rb") as kernelfile:
                geckoKernel = KernelLoader(kernelfile, cli)
                geckoKernel.initAddress = context["initaddress"]
                geckoKernel.verbosity = context["verbosity"]
                geckoKernel.quiet = context["quiet"]
                geckoKernel.encrypt = context["encrypt"]
                geckoKernel.protect = context["protect"]

            if not context["destination"].parent.exists():
                context["destination"].parent.mkdir(parents=True,
                                                    exist_ok=True)

            geckoKernel.build(context["codepath"], dolFile, codeHandler,
                              TMPDIR, context["destination"])

        except FileNotFoundError as e:
            self.error(color_text(e, defaultColor=TREDLIT))
Exemple #3
0
    def print_splash(self):
        helpMessage = 'Try option -h for more info on this program'.center(
            64, ' ')
        version = self.__version__.rjust(9, ' ')

        logo = [
            '                                                                ',
            ' ╔═══════════════════════════════════════════════════════════╗  ',
            ' ║                                                           ║  ',
            ' ║  ┌───┐┌───┐┌───┐┌┐┌─┐┌───┐┌┐   ┌───┐┌───┐┌───┐┌───┐┌───┐  ║  ',
            ' ║  │┌─┐││┌──┘│┌─┐││││┌┘│┌─┐│││   │┌─┐││┌─┐│└┐┌┐││┌──┘│┌─┐│  ║  ',
            ' ║  ││ └┘│└──┐││ └┘│└┘┘ ││ ││││   ││ ││││ ││ │││││└──┐│└─┘│  ║  ',
            ' ║  ││┌─┐│┌──┘││ ┌┐│┌┐│ ││ ││││ ┌┐││ │││└─┘│ │││││┌──┘│┌┐┌┘  ║  ',
            ' ║  │└┴─││└──┐│└─┘││││└┐│└─┘││└─┘││└─┘││┌─┐│┌┘└┘││└──┐│││└┐  ║  ',
            ' ║  └───┘└───┘└───┘└┘└─┘└───┘└───┘└───┘└┘ └┘└───┘└───┘└┘└─┘  ║  ',
            ' ║                                                           ║  ',
            ' ║           ┌┐┌───┐┌───┐┌┐ ┌┐┌┐ ┌┐┌───┐┌─┐┌─┐┌┐┌─┐          ║  ',
            ' ║           │││┌─┐││┌─┐│││ ││││ │││┌─┐││ └┘ ││││┌┘          ║  ',
            ' ║           ││││ │││└──┐│└─┘│││ ││││ │││┌┐┌┐││└┘┘           ║  ',
            ' ║     ┌──┐┌┐││││ ││└──┐││┌─┐│││ │││└─┘││││││││┌┐│ ┌──┐      ║  ',
            ' ║     └──┘│└┘││└─┘││└─┘│││ │││└─┘││┌─┐││││││││││└┐└──┘      ║  ',
            ' ║         └──┘└───┘└───┘└┘ └┘└───┘└┘ └┘└┘└┘└┘└┘└─┘          ║  ',
            f' ║                                                {version}  ║  ',
            ' ╚═══════════════════════════════════════════════════════════╝  ',
            '                                                                ',
            '        GeckoLoader is a cli tool for allowing extended         ',
            '           gecko code space in all Wii and GC games.            ',
            '                                                                ',
            f'{helpMessage}',
            '                                                                '
        ]

        for line in logo:
            print(
                color_text(line, [('║', TREDLIT), ('╔╚╝╗═', TRED)], TGREENLIT))
Exemple #4
0
 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
Exemple #5
0
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
Exemple #6
0
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)
Exemple #7
0
    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)
Exemple #8
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"
            )
Exemple #9
0
    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
Exemple #10
0
    def check_updates(self):
        repoChecker = Updater('JoshuaMKW', 'GeckoLoader')

        tag, status = repoChecker.get_newest_version()

        if status is False:
            self.error(color_text(tag + '\n', defaultColor=TREDLIT),
                       print_usage=False)

        print('')

        if LooseVersion(tag) > LooseVersion(self.__version__):
            print(
                color_text(
                    f'  :: A new update is live at {repoChecker.gitReleases.format(repoChecker.owner, repoChecker.repo)}',
                    defaultColor=TYELLOWLIT))
            print(
                color_text(
                    f'  :: Current version is "{self.__version__}", Most recent version is "{tag}"',
                    defaultColor=TYELLOWLIT))
        elif LooseVersion(tag) < LooseVersion(self.__version__):
            print(
                color_text('  :: No update available', defaultColor=TGREENLIT))
            print(
                color_text(
                    f'  :: Current version is "{self.__version__}(dev)", Most recent version is "{tag}(release)"',
                    defaultColor=TGREENLIT))
        else:
            print(
                color_text('  :: No update available', defaultColor=TGREENLIT))
            print(
                color_text(
                    f'  :: Current version is "{self.__version__}(release)", Most recent version is "{tag}(release)"',
                    defaultColor=TGREENLIT))

        print('')
Exemple #11
0
    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} text sections in this DOL file, {len(dolFile.dataSections)} are now being used"
            ]

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

        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))
Exemple #12
0
    def _validate_args(self, args) -> dict:
        dolFile = Path(args.dolfile).resolve()
        codeList = Path(args.codelist).resolve()

        if args.dest:
            dest = Path(args.dest).resolve()
            if dest.suffix == "":
                dest = dest / args.dolfile.name
        else:
            dest = Path.cwd() / "geckoloader-build" / args.dolfile.name

        if args.alloc:
            try:
                _allocation = int(args.alloc, 16)
            except ValueError:
                self.error(
                    color_text('The allocation was invalid\n',
                               defaultColor=TREDLIT))
        else:
            _allocation = None

        if args.hookaddress:
            if 0x80000000 > int(args.hookaddress, 16) >= 0x81800000:
                self.error(
                    color_text(
                        'The codehandler hook address was beyond bounds\n',
                        defaultColor=TREDLIT))
            else:
                try:
                    _codehook = int(args.hookaddress, 16)
                except ValueError:
                    self.error(
                        color_text(
                            'The codehandler hook address was invalid\n',
                            defaultColor=TREDLIT))
        else:
            _codehook = None

        if args.handler == CodeHandler.Types.MINI:
            codeHandlerFile = Path('bin/codehandler-mini.bin')
        elif args.handler == CodeHandler.Types.FULL:
            codeHandlerFile = Path('bin/codehandler.bin')
        else:
            self.error(
                color_text(f'Codehandler type {args.handler} is invalid\n',
                           defaultColor=TREDLIT))

        if not dolFile.is_file():
            self.error(
                color_text(f'File "{dolFile}" does not exist\n',
                           defaultColor=TREDLIT))

        if not codeList.exists():
            self.error(
                color_text(f'File/folder "{codeList}" does not exist\n',
                           defaultColor=TREDLIT))

        return {
            "dol": dolFile,
            "codepath": codeList,
            "codehandler": codeHandlerFile,
            "destination": dest,
            "allocation": _allocation,
            "hookaddress": _codehook,
            "hooktype": args.hooktype,
            "initaddress": None if args.init is None else int(args.init, 16),
            "includeall": args.txtcodes.lower() == "all",
            "optimize": args.optimize,
            "protect": args.protect,
            "encrypt": args.encrypt,
            "verbosity": args.verbose,
            "quiet": args.quiet
        }