示例#1
0
def extract_refs(language: str, tmp_dir: Path, files: set = None):
    x_args = [
        get_7z_path(),
        "x",
        str(util.get_exec_dir() / "data" / "text_refs.7z"),
        f'-o{str(tmp_dir / "refs")}',
    ]
    if files:
        x_args.extend(files)
    else:
        x_args.append(language)
    result: subprocess.CompletedProcess
    if system() == "Windows":
        result = subprocess.run(
            x_args,
            capture_output=True,
            creationflags=util.CREATE_NO_WINDOW,
            check=False,
            text=True,
        )
    else:
        result = subprocess.run(x_args,
                                capture_output=True,
                                text=True,
                                check=False)
    if result.stderr:
        raise RuntimeError(result.stderr)
示例#2
0
文件: _api.py 项目: arkhamsaiyan/BCML
 def update_bcml(self):
     if util.get_latest_bcml() <= VERSION:
         return
     exe = util.get_python_exe().replace("pythonw", "python")
     args = [
         exe,
         "-m",
         "pip",
         "install",
         "--disable-pip-version-check",
         "--no-warn-script-location",
         "--upgrade",
         "--pre" if DEBUG else "",
         "bcml",
     ]
     parent = util.get_exec_dir().parent
     if parent.name == "pkgs":
         (parent / "bcml").rename(parent / "bcml.bak")
     if SYSTEM == "Windows":
         result = run(
             args,
             creationflags=util.CREATE_NO_WINDOW,
             capture_output=True,
             check=False,
             text=True,
         )
     else:
         result = run(
             args,
             capture_output=True,
             check=False,
             text=True,
         )
     if result.stderr:
         raise RuntimeError(result.stderr)
示例#3
0
 def __init__(self) -> None:
     self._gameid = "5866" if util.get_settings("wiiu") else "6386"
     if not GB_DATA.exists():
         GB_DATA.write_bytes(
             util.decompress(
                 (util.get_exec_dir() / "data" / "gb.sjson").read_bytes()))
     self._data = json.loads(GB_DATA.read_text("utf-8"))
示例#4
0
文件: install.py 项目: sammysung/BCML
def restore_backup(backup: Union[str, Path]):
    """
    Restores a BCML mod configuration backup

    :param backup: The backup to restore, either by name or by path
    :type backup: Union[str, Path]
    """
    if isinstance(backup, str):
        backup = util.get_cemu_dir() / 'bcml_backups' / f'{backup}.7z'
    if not backup.exists():
        raise FileNotFoundError(f'The backup "{backup.name}" does not exist.')
    print('Clearing installed mods...')
    for folder in [
            item for item in util.get_modpack_dir().glob('*') if item.is_dir()
    ]:
        shutil.rmtree(str(folder))
    print('Extracting backup...')
    x_args = [
        str(util.get_exec_dir() / 'helpers' / '7z.exe'), 'x',
        str(backup), f'-o{str(util.get_modpack_dir())}'
    ]
    subprocess.run(x_args,
                   stdout=subprocess.PIPE,
                   stderr=subprocess.PIPE,
                   creationflags=util.CREATE_NO_WINDOW)
    print('Re-enabling mods in Cemu...')
    refresh_cemu_mods()
    print(f'Backup "{backup.name}" restored')
示例#5
0
文件: install.py 项目: sammysung/BCML
def create_backup(name: str = ''):
    """
    Creates a backup of the current mod installations. Saves it as a 7z file in
    `CEMU_DIR/bcml_backups`.

    :param name: The name to give the backup, defaults to "BCML_Backup_YYYY-MM-DD"
    :type name: str, optional
    """
    import re
    if not name:
        name = f'BCML_Backup_{datetime.datetime.now().strftime("%Y-%m-%d")}'
    else:
        name = re.sub(r'(?u)[^-\w.]', '', name.strip().replace(' ', '_'))
    output = util.get_cemu_dir() / 'bcml_backups' / f'{name}.7z'
    output.parent.mkdir(parents=True, exist_ok=True)
    print(f'Saving backup {name}...')
    x_args = [
        str(util.get_exec_dir() / 'helpers' / '7z.exe'), 'a',
        str(output), f'{str(util.get_modpack_dir() / "*")}'
    ]
    subprocess.run(x_args,
                   stdout=subprocess.PIPE,
                   stderr=subprocess.PIPE,
                   creationflags=util.CREATE_NO_WINDOW)
    print(f'Backup "{name}" created')
示例#6
0
def get_text_hashes(language: str = None) -> {}:
    hashes = json.loads(
        util.decompress((util.get_exec_dir() / "data" / "hashes" /
                         "msyts.sjson").read_bytes()).decode("utf8"))
    if language:
        return hashes[language if not language.endswith("en") else "XXen"]
    else:
        return hashes
示例#7
0
def _win_create_handler():
    # pylint: disable=import-error,import-outside-toplevel,undefined-variable
    import winreg

    if (util.get_exec_dir().parent.parent.parent / "Scripts" /
            "bcml.exe").exists():
        exec_path = ('"' + str((util.get_exec_dir().parent.parent.parent /
                                "Scripts" / "bcml.exe").resolve()) + '"')
    elif (util.get_exec_dir().parent.parent / "bin" / "bcml.exe").exists():
        exec_path = ('"' + str(
            (util.get_exec_dir().parent.parent / "bin" / "bcml.exe").resolve())
                     + '"')
    else:
        exec_path = f'"{sys.executable}" -m bcml'

    with winreg.CreateKey(winreg.HKEY_CURRENT_USER,
                          r"Software\Classes\bcml") as key:
        try:
            with winreg.OpenKey(
                    winreg.HKEY_CURRENT_USER,
                    r"Software\Classes\bcml\shell\open\command",
                    0,
                    winreg.KEY_READ,
            ) as okey:
                assert exec_path in winreg.QueryValueEx(okey, "")[0]
        except (WindowsError, OSError, AssertionError):
            winreg.SetValueEx(key, "URL Protocol", 0, winreg.REG_SZ, "")
            with winreg.CreateKey(key, r"shell\open\command") as key2:
                winreg.SetValueEx(key2, "", 0, winreg.REG_SZ,
                                  f'{exec_path} "%1"')
    with winreg.CreateKey(winreg.HKEY_CURRENT_USER,
                          r"Software\Classes\.bnp") as key:
        try:
            with winreg.OpenKey(
                    winreg.HKEY_CURRENT_USER,
                    r"Software\Classes\.bnp",
                    0,
                    winreg.KEY_READ,
            ) as okey:
                assert winreg.QueryValueEx(okey, "")[0] == "bcml"
        except (WindowsError, OSError, AssertionError):
            winreg.SetValueEx(key, "", 0, winreg.REG_SZ, "bcml")
示例#8
0
文件: install.py 项目: sammysung/BCML
def open_mod(path: Path) -> Path:
    """
    Extracts a provided mod and returns the root path of the graphicpack inside

    :param path: The path to the mod archive.
    :type path: class:`pathlib.Path`
    :returns: The path to the extracted root of the mod where the rules.txt file is found.
    :rtype: class:`pathlib.Path`
    """
    if isinstance(path, str):
        path = Path(path)
    tmpdir = util.get_work_dir() / f'tmp_{xxhash.xxh32(str(path)).hexdigest()}'
    formats = ['.rar', '.zip', '.7z', '.bnp']
    if tmpdir.exists():
        shutil.rmtree(tmpdir, ignore_errors=True)
    if path.suffix.lower() in formats:
        x_args = [
            str(util.get_exec_dir() / 'helpers' / '7z.exe'), 'x',
            str(path), f'-o{str(tmpdir)}'
        ]
        subprocess.run(x_args,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE,
                       creationflags=util.CREATE_NO_WINDOW)
    else:
        raise Exception(
            'The mod provided was not a supported archive (BNP, ZIP, RAR, or 7z).'
        )
    if not tmpdir.exists():
        raise Exception('No files were extracted.')
    rulesdir = tmpdir
    found_rules = (rulesdir / 'rules.txt').exists()
    if not found_rules:
        for subdir in tmpdir.rglob('*'):
            if (subdir / 'rules.txt').exists():
                rulesdir = subdir
                found_rules = True
                break
        if not found_rules:
            raise FileNotFoundError(
                f'No rules.txt was found in "{path.name}".')
    return rulesdir
示例#9
0
    "EUen",
    "USfr",
    "USes",
    "EUde",
    "EUes",
    "EUfr",
    "EUit",
    "EUnl",
    "EUru",
    "CNzh",
    "JPja",
    "KRko",
    "TWzh",
]

MSYT_PATH = str(util.get_exec_dir() / "helpers" /
                "msyt{}".format(".exe" if system() == "Windows" else ""))


@lru_cache(2)
def get_text_hashes(language: str = None) -> {}:
    hashes = json.loads(
        util.decompress((util.get_exec_dir() / "data" / "hashes" /
                         "msyts.sjson").read_bytes()).decode("utf8"))
    if language:
        return hashes[language if not language.endswith("en") else "XXen"]
    else:
        return hashes


def get_user_languages() -> set:
示例#10
0
def main(debug: bool = False):
    set_start_method("spawn", True)
    global logger  # pylint: disable=invalid-name,global-statement
    logger = None

    try:
        if SYSTEM != "Windows":
            chmod(util.get_exec_dir() / "helpers/msyt", int("755", 8))
            chmod(util.get_exec_dir() / "helpers/7z", int("755", 8))
            os.setpgrp()
        LOG.parent.mkdir(parents=True, exist_ok=True)
        for folder in util.get_work_dir().glob("*"):
            rmtree(folder)
        (util.get_data_dir() / "tmp_settings.json").unlink()
    except (FileNotFoundError, OSError, PermissionError):
        pass

    _oneclick.register_handlers()
    oneclick = Thread(target=_oneclick.listen)
    oneclick.daemon = True
    oneclick.start()

    server_port = util.get_open_port()
    server = Process(target=start_server, args=(server_port,))
    server.daemon = True
    server.start()
    host = f"http://localhost:{server_port}"

    api = Api(host)

    gui = "cef" if SYSTEM == "Windows" else "qt"

    if not debug:
        debug = DEBUG or "bcml-debug" in sys.argv

    if SYSTEM == "Windows":
        configure_cef(debug)

    now = datetime.now()
    if (
        now.month == 4
        and now.day == 1
        and not (util.get_data_dir() / ".fooled").exists()
    ):
        (util.get_data_dir() / ".fooled").write_bytes(b"")
        url = (
            [
                "https://www.youtube.com/embed/Lrj2Hq7xqQ8",
                "https://www.youtube.com/embed/8B1fu3AuDrQ",
                "https://www.youtube.com/embed/jRMHp7_kPec",
                "https://www.youtube.com/embed/N9qYF9DZPdw",
                "https://www.youtube.com/embed/j1FGaCNN1aw",
            ][randint(0, 4)]
        ) + "?autoplay=1"
        width, height = 640, 360
    elif (util.get_data_dir() / "settings.json").exists():
        url = f"{host}/index.html"
        width, height = 907, 680
    else:
        url = f"{host}/index.html?firstrun=yes"
        width, height = 750, 600

    api.window = webview.create_window(
        "BOTW Cross-Platform Mod Loader",
        url=url,
        js_api=api,
        text_select=DEBUG,
        width=width,
        height=height,
        min_size=(width if width == 750 else 820, 600),
    )
    logger = Messager(api.window)
    api.window.closing += stop_it

    messager = Messager(api.window)
    with redirect_stderr(sys.stdout):
        with redirect_stdout(messager):  # type: ignore
            sleep(0.5)
            webview.start(
                gui=gui, debug=debug, http_server=True, func=_oneclick.process_arg
            )
    api.cleanup()
    stop_it(messager=messager)
示例#11
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args,
                      directory=str(get_exec_dir() / "assets"),
                      **kwargs)
示例#12
0
def main(debug: bool = False):
    set_start_method("spawn", True)
    global logger  # pylint: disable=invalid-name,global-statement
    logger = None

    try:
        if SYSTEM != "Windows":
            chmod(util.get_exec_dir() / "helpers/msyt", int("755", 8))
            chmod(util.get_exec_dir() / "helpers/7z", int("755", 8))
            os.setpgrp()
        LOG.parent.mkdir(parents=True, exist_ok=True)
        for folder in util.get_work_dir().glob("*"):
            rmtree(folder)
    except (FileNotFoundError, OSError, PermissionError):
        pass

    _oneclick.register_handlers()
    oneclick = Thread(target=_oneclick.listen)
    oneclick.daemon = True
    oneclick.start()

    server_port = util.get_open_port()
    server = Process(target=start_server, args=(server_port, ))
    server.daemon = True
    server.start()
    host = f"http://localhost:{server_port}"

    api = Api(host)

    gui = "cef" if SYSTEM == "Windows" else "qt"

    if (util.get_data_dir() / "settings.json").exists():
        url = f"{host}/index.html"
        width, height = 907, 680
    else:
        url = f"{host}/index.html?firstrun=yes"
        width, height = 750, 600

    api.window = webview.create_window(
        "BOTW Cross-Platform Mod Loader",
        url=url,
        js_api=api,
        text_select=DEBUG,
        width=width,
        height=height,
        min_size=(width if width == 750 else 820, 600),
    )
    logger = Messager(api.window)
    api.window.closing += stop_it

    if not debug:
        debug = DEBUG or "bcml-debug" in sys.argv

    messager = Messager(api.window)
    with redirect_stderr(sys.stdout):
        with redirect_stdout(messager):
            sleep(0.5)
            webview.start(gui=gui,
                          debug=debug,
                          http_server=True,
                          func=_oneclick.process_arg)
    stop_it(messager=messager)
示例#13
0
文件: install.py 项目: sammysung/BCML
def create_bnp_mod(mod: Path, output: Path, options: dict = None):
    """[summary]
    
    :param mod: [description]
    :type mod: Path
    :param output: [description]
    :type output: Path
    :param options: [description], defaults to {}
    :type options: dict, optional
    """
    if isinstance(mod, str):
        mod = Path(mod)
    if mod.is_file():
        print('Extracting mod...')
        tmp_dir: Path = open_mod(mod)
    elif mod.is_dir():
        print(f'Loading mod from {str(mod)}...')
        tmp_dir: Path = util.get_work_dir() / \
            f'tmp_{xxhash.xxh32(str(mod)).hexdigest()}'
        shutil.copytree(str(mod), str(tmp_dir))
    else:
        print(f'Error: {str(mod)} is neither a valid file nor a directory')
        return

    print('Packing loose files...')
    pack_folders = sorted(
        {
            d
            for d in tmp_dir.rglob('**/*')
            if d.is_dir() and d.suffix in util.SARC_EXTS
        },
        key=lambda d: len(d.parts),
        reverse=True)
    for folder in pack_folders:
        new_tmp: Path = folder.with_suffix(folder.suffix + '.tmp')
        shutil.move(folder, new_tmp)
        new_sarc = sarc.SARCWriter(be=True)
        for file in {f for f in new_tmp.rglob('**/*') if f.is_file()}:
            new_sarc.add_file(
                file.relative_to(new_tmp).as_posix(), file.read_bytes())
        sarc_bytes = new_sarc.get_bytes()
        if str(folder.suffix).startswith('.s') and folder.suffix != '.sarc':
            sarc_bytes = util.compress(sarc_bytes)
        folder.write_bytes(sarc_bytes)
        shutil.rmtree(new_tmp)

    if not options:
        options = {}
    options['texts'] = {'user_only': False}
    pool = Pool(cpu_count())
    logged_files = generate_logs(tmp_dir, options=options, original_pool=pool)

    print('Removing unnecessary files...')
    if (tmp_dir / 'logs' / 'map.yml').exists():
        print('Removing map units...')
        for file in [file for file in logged_files if isinstance(file, Path) and \
                           fnmatch(file.name, '[A-Z]-[0-9]_*.smubin')]:
            file.unlink()
    if [file for file in (tmp_dir / 'logs').glob('*texts*')]:
        print('Removing language bootup packs...')
        for bootup_lang in (tmp_dir / 'content' /
                            'Pack').glob('Bootup_*.pack'):
            bootup_lang.unlink()
    if (tmp_dir / 'logs' / 'actorinfo.yml').exists() and \
       (tmp_dir / 'content' / 'Actor' / 'ActorInfo.product.sbyml').exists():
        print('Removing ActorInfo.product.sbyml...')
        (tmp_dir / 'content' / 'Actor' / 'ActorInfo.product.sbyml').unlink()
    if (tmp_dir / 'logs' / 'gamedata.yml').exists() or (
            tmp_dir / 'logs' / 'savedata.yml').exists():
        print('Removing gamedata sarcs...')
        with (tmp_dir / 'content' / 'Pack' /
              'Bootup.pack').open('rb') as b_file:
            bsarc = sarc.read_file_and_make_sarc(b_file)
        csarc = sarc.make_writer_from_sarc(bsarc)
        bsarc_files = list(bsarc.list_files())
        if 'GameData/gamedata.ssarc' in bsarc_files:
            csarc.delete_file('GameData/gamedata.ssarc')
        if 'GameData/savedataformat.ssarc' in bsarc_files:
            csarc.delete_file('GameData/savedataformat.ssarc')
        with (tmp_dir / 'content' / 'Pack' /
              'Bootup.pack').open('wb') as b_file:
            csarc.write(b_file)

    hashes = util.get_hash_table()
    print('Creating partial packs...')
    sarc_files = {
        file
        for file in tmp_dir.rglob('**/*') if file.suffix in util.SARC_EXTS
    }
    if sarc_files:
        pool.map(partial(_clean_sarc, hashes=hashes, tmp_dir=tmp_dir),
                 sarc_files)
        pool.close()
        pool.join()

        sarc_files = {
            file
            for file in tmp_dir.rglob('**/*') if file.suffix in util.SARC_EXTS
        }
        if sarc_files:
            with (tmp_dir / 'logs' / 'packs.log').open(
                    'w', encoding='utf-8') as p_file:
                final_packs = [
                    file for file in list(tmp_dir.rglob('**/*'))
                    if file.suffix in util.SARC_EXTS
                ]
                if final_packs:
                    p_file.write('name,path\n')
                    for file in final_packs:
                        p_file.write(
                            f'{util.get_canon_name(file.relative_to(tmp_dir))},'
                            f'{file.relative_to(tmp_dir)}\n')
    else:
        if (tmp_dir / 'logs' / 'packs.log').exists():
            (tmp_dir / 'logs' / 'packs.log').unlink()

    print('Cleaning any junk files...')
    for file in tmp_dir.rglob('**/*'):
        if file.parent.stem == 'logs':
            continue
        if file.suffix in ['.yml', '.bak', '.tmp', '.old']:
            file.unlink()

    print('Removing blank folders...')
    for folder in reversed(list(tmp_dir.rglob('**/*'))):
        if folder.is_dir() and not list(folder.glob('*')):
            shutil.rmtree(folder)

    print(f'Saving output file to {str(output)}...')
    x_args = [
        str(util.get_exec_dir() / 'helpers' / '7z.exe'), 'a',
        str(output), f'{str(tmp_dir / "*")}'
    ]
    subprocess.run(x_args,
                   stdout=subprocess.PIPE,
                   stderr=subprocess.PIPE,
                   creationflags=util.CREATE_NO_WINDOW)
    print('Conversion complete.')