How to use the bcml.mergers function in bcml

To help you get started, we’ve selected a few bcml examples, based on popular ways it is used in public projects.

Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.

github NiceneNerd / BCML / bcml / mergers / drop.py View on Github external
if table not in drop_table:
                del ref_drop[table]
            else:
                for item in set(ref_drop[table]["items"].keys()):
                    if item not in drop_table[table]["items"]:
                        del ref_drop[table]["items"][item]
        util.dict_merge(ref_drop, drop_table)
        drop_table = ref_drop
    except (FileNotFoundError, AttributeError, RuntimeError):
        pass
    actor_name = re.search(r"Pack\/(.+)\.sbactorpack", file).groups()[0]
    pio = _dict_to_drop(drop_table)
    util.inject_files_into_actor(actor_name, {file.split("//")[-1]: pio.to_binary()})


class DropMerger(mergers.Merger):
    NAME: str = "drops"

    def __init__(self):
        super().__init__(
            "drop merger", "Merges changes to drop tables", "drops.json", options={}
        )

    def generate_diff(self, mod_dir: Path, modded_files: List[Union[str, Path]]):
        drops = {f for f in modded_files if isinstance(f, str) and f.endswith(".bdrop")}
        if not drops:
            return {}
        print("Logging changes to drop tables...")
        pool = self._pool or Pool()
        diffs = {}
        for result in pool.map(partial(log_drop_file, mod_dir=mod_dir), drops):
            diffs.update(result)
github NiceneNerd / BCML / bcml / rstable.py View on Github external
rstb_path = util.get_master_modpack_dir() / 'content' / 'System' / 'Resource' / \
                                                'ResourceSizeTable.product.srsizetable'
    if not rstb_path.exists():
        rstb_path.parent.mkdir(parents=True, exist_ok=True)
    with rstb_path.open('wb') as r_file:
        with io.BytesIO() as buf:
            table.write(buf, True)
            r_file.write(util.compress(buf.getvalue()))

    rstb_log = util.get_master_modpack_dir() / 'logs' / 'master-rstb.log'
    rstb_log.parent.mkdir(parents=True, exist_ok=True)
    with rstb_log.open('w', encoding='utf-8') as r_file:
        r_file.write('\n'.join([change[0].strip() for change in rstb_changes]))


class RstbMerger(mergers.Merger):
    """ A merger for the ResourceSizeTable.product.srsizetable """
    NAME: str = 'rstb'

    def __init__(self, guess: bool = True, leave: bool = False, shrink: bool = False):
        super().__init__('RSTB merge', 'Merges changes to ResourceSizeTable.product.srsizetable',
                         'rstb.log')
        self._options = {
            'no_guess': not guess,
            'leave': leave,
            'shrink': shrink
        }

    def generate_diff(self, mod_dir: Path, modded_files: List[Path]):
        rstb_diff = {}
        open_sarcs = {}
        for file in modded_files:
github NiceneNerd / BCML / bcml / mergers / quests.py View on Github external
from functools import lru_cache
from pathlib import Path
from typing import List, Union

import oead
from bcml import mergers, util


def get_stock_quests() -> oead.byml.Array:
    title_sarc = oead.Sarc(util.get_game_file("Pack/TitleBG.pack").read_bytes())
    return oead.byml.from_binary(
        util.decompress(title_sarc.get_file("Quest/QuestProduct.sbquestpack").data)
    )


class QuestMerger(mergers.Merger):
    NAME: str = "quests"

    def __init__(self):
        super().__init__(
            "quests", "Merges changes to Quest.product.byml", "quests.yml", options={}
        )

    def generate_diff(self, mod_dir: Path, modded_files: List[Union[Path, str]]):
        if (
            f"{util.get_content_path()}/Pack/TitleBG.pack//Quest/QuestProduct.sbquestpack"
            not in modded_files
        ):
            return {}
        print("Logging modified quests...")
        stock_quests = get_stock_quests()
        stock_names = [q["Name"] for q in stock_quests]
github NiceneNerd / BCML / bcml / _api.py View on Github external
def update_mod(self, params):
        try:
            update_file = self.file_pick({"multiple": False})[0]
        except IndexError:
            return
        mod = BcmlMod.from_json(params["mod"])
        if (mod.path / "options.json").exists():
            options = json.loads(
                (mod.path / "options.json").read_text(), encoding="utf-8"
            )
        else:
            options = {}
        remergers = mergers.get_mergers_for_mod(mod)
        rmtree(mod.path)
        with Pool(maxtasksperchild=1000) as pool:
            new_mod = install.install_mod(
                Path(update_file),
                insert_priority=mod.priority,
                options=options,
                pool=pool,
            )
            remergers |= {
                m
                for m in mergers.get_mergers_for_mod(new_mod)
                if m.NAME not in {m.NAME for m in remergers}
            }
            try:
                install.refresh_merges()
            except Exception:  # pylint: disable=broad-except
github NiceneNerd / BCML / bcml / install.py View on Github external
if not options:
        options = {
            'disable': [],
            'options': {}
        }
    if 'disable' not in options:
        options['disable'] = []

    pool = original_pool or Pool(cpu_count())
    print('Scanning for modified files...')
    modded_files = find_modded_files(tmp_dir, verbose=verbose, original_pool=original_pool)
    if not modded_files:
        raise RuntimeError('No modified files were found. Very unusual.')

    (tmp_dir / 'logs').mkdir(parents=True, exist_ok=True)
    for merger_class in [merger_class for merger_class in mergers.get_mergers() \
                        if merger_class.NAME not in options['disable']]:
        merger = merger_class()
        merger.set_pool(pool)
        if options is not None and merger.NAME in options:
            merger.set_options(options[merger.NAME])
        merger.log_diff(tmp_dir, modded_files)
    if not original_pool:
        pool.close()
        pool.join()
    return modded_files
github NiceneNerd / BCML / bcml / mergers / effects.py View on Github external
from typing import List, Union

import oead
import rstb
from bcml import util, mergers
from bcml.mergers import rstable


def get_stock_effects() -> oead.byml.Hash:
    bootup_sarc = oead.Sarc(util.get_game_file("Pack/Bootup.pack").read_bytes())
    return oead.byml.from_binary(
        util.decompress(bootup_sarc.get_file("Ecosystem/StatusEffectList.sbyml").data)
    )[0]


class StatusEffectMerger(mergers.Merger):
    NAME: str = "effects"

    def __init__(self):
        super().__init__(
            "status effect",
            "Merges changes to StatusEffectList.byml",
            "effects.yml",
            options={},
        )

    def generate_diff(self, mod_dir: Path, modded_files: List[Union[str, Path]]):
        needle = f"{util.get_content_path()}/Pack/Bootup.pack//Ecosystem/StatusEffectList.sbyml"
        if needle not in modded_files:
            return {}
        print("Logging changes to effect status levels...")
        stock_effects = get_stock_effects()
github NiceneNerd / BCML / bcml / data.py View on Github external
    @staticmethod
    def is_bootup_injector():
        return True

    def get_bootup_injection(self):
        tmp_sarc = util.get_master_modpack_dir() / 'logs' / 'savedata.sarc'
        if tmp_sarc.exists():
            return (
                'GameData/savedataformat.ssarc',
                util.compress(tmp_sarc.read_bytes())
            )
        else:
            return


class ActorInfoMerger(mergers.Merger):
    NAME: str = 'actors'

    def __init__(self):
        super().__init__('actor info merge', 'Merges changes to ActorInfo.product.byml',
                         'actorinfo.yml ', {})

    def generate_diff(self, mod_dir: Path, modded_files: List[Union[Path, str]]):
        try:
            actor_file = next(iter([file for file in modded_files \
                               if Path(file).name == 'ActorInfo.product.sbyml']))
        except StopIteration:
            return {}
        actor_info = byml.Byml(util.decompress_file(str(actor_file))).parse()
        print('Detecting modified actor info entries...')
        return get_modded_actors(actor_info)
github NiceneNerd / BCML / bcml / __init__.py View on Github external
def resort_mods(self):
            all_mergers = [merger() for merger in mergers.get_mergers()]
            remergers = set()
            partials = {}
            mods_to_change = []
            for i in range(self.listWidget.count()):
                mod = self.listWidget.item(i).data(QtCore.Qt.UserRole)
                target_priority = i + 100 if util.get_settings_bool(
                    'load_reverse') else 100 + ((self.listWidget.count() - 1) - i)
                if mod.priority != target_priority:
                    mods_to_change.append(BcmlMod(mod.name, target_priority, mod.path))
                    for merger in all_mergers:
                        if merger.is_mod_logged(mod):
                            remergers.add(merger)
                            if merger.can_partial_remerge():
                                if merger.NAME not in partials:
                                    partials[merger.NAME] = set()
                                partials[merger.NAME] |= set(merger.get_mod_affected(mod))
github NiceneNerd / BCML / bcml / texts.py View on Github external
rstb_path = util.get_modpack_dir() / '9999_BCML' / 'content' / 'System' / 'Resource' /\
                                         'ResourceSizeTable.product.srsizetable'
    if rstb_path.exists():
        table: rstb.ResourceSizeTable = rstb.util.read_rstb(
            str(rstb_path), True)
    else:
        table = rstable.get_stock_rstb()
    msg_path = f'Message/Msg_{lang}.product.sarc'
    if table.is_in_table(msg_path):
        print('Correcting RSTB...')
        table.delete_entry(msg_path)
    rstb_path.parent.mkdir(parents=True, exist_ok=True)
    rstb.util.write_rstb(table, str(rstb_path), True)

class TextsMerger(mergers.Merger):
    """ A merger for game texts """
    NAME: str = 'texts'

    def __init__(self, user_only: bool = True):
        super().__init__('texts merge', 'Merges changes to game texts', '', options={
            'user_only': user_only
        })

    def generate_diff(self, mod_dir: Path, modded_files: List[Union[str, Path]]):
        diffs = {}
        bootups = {util.get_file_language(file): file for file in modded_files
                   if 'Bootup_' in str(file) and 'Graphics' not in str(file) 
                   and isinstance(file, Path)}
        if not bootups:
            return {}
        mod_langs = list(bootups.keys())
github NiceneNerd / BCML / bcml / mergers / pack.py View on Github external
break

    if "Bootup.pack" in file_name:
        for merger in [
            merger() for merger in mergers.get_mergers() if merger.is_bootup_injector()
        ]:
            inject = merger.get_bootup_injection()
            if not inject:
                continue
            file, data = inject
            new_sarc.files[file] = data

    return (file_name, bytes(new_sarc.write()[1]))


class PackMerger(mergers.Merger):
    """ A merger for modified pack files """

    NAME: str = "packs"

    def __init__(self):
        super().__init__("packs", "Merges modified files within SARCs", "packs.json", {})

    def can_partial_remerge(self):
        return True

    def get_mod_affected(self, mod):
        return self.get_mod_diff(mod)

    def generate_diff(self, mod_dir: Path, modded_files: List[Union[str, Path]]):
        print("Finding modified SARCs...")
        packs = {}