Writing A Merger
BCML uses merger classes to handle different kinds of content that must be merged between mods. You can contribute to extend BCML's functionality by writing additional mergers. A merger will need to identify changes made to the files that it handles, log those changes (preferably as JSON or YAML), consolidate the changes from all installed mods, and apply them to generate properly merged files in the master BCML pack.
The following is an annotated template for a BCML merger class. Bear mind in
mind that the Merger
abstract base class was unfortunately developed in an odd
process and is definitely quirky:
from pathlib import Path
from typing import Any, List, Union
from bcml import util
from bcml.mergers import Merger
# First, inherit from the `bcml.mergers.Merger` class
class MyMerger(Merger):
NAME: str = "mymerger" # merger internal name
def __init__(self):
super().__init__(
"my merger", # human readable merger name in UI
"Merges something", # human readable merger description
"some.log", # the file name of the merger's log
options={} # default setting for advanced options, if any, accessible though self._options
)
def generate_diff(self, mod_dir: Path, modded_files: List[Union[Path, str]]) -> Any:
# This function takes the root mod folder and a list of modified files.
# The modified file lists contains uses pathlib `Path`s for loose files
# and strings for packed files. The packed file strings consist of the
# relative path of the root from the mod, and then the paths to each
# nested file separated by `//`. For example:
# `content/Pack/TitleBG.pack//Actor/Pack/GameROMPlayer.sbactorpack//Actor/ActorLink/GameROMPlayer.bxml`
# If you need to access nested files, BCML's `util` module provides a
# `get_nested_file_bytes()` helper function.
# The return value should represent the detected modifications in a
# which can be passed to the `log_diff()` function for serialization,
# preferably with minimal further processing.
def log_diff(self, mod_dir: Path, diff_material: Union[Any, List[Union[Path, str]]]) -> None:
# This function is meant to save the diffs generated by the merger to
# the file specified in `self._log_name` to the mod's `logs` folder.
# It should accept `diff_material` *either* as a list of modded files,
# so it can call `generate_diff()` within itself, *or* as the return
# value of `generate_diff()`. Ideally, this function should do little
# or no processing of the diff material, only what is required to save
# the log.
def get_mod_diff(self, mod: util.BcmlMod) -> Any:
#