Beispiel #1
0
def _clean_sarc(old_sarc: oead.Sarc,
                base_sarc: oead.Sarc) -> Optional[oead.SarcWriter]:
    old_files = {f.name for f in old_sarc.get_files()}
    new_sarc = oead.SarcWriter(endian=oead.Endianness.Big if util.get_settings(
        "wiiu") else oead.Endianness.Little)
    can_delete = True
    for nest_file, file_data in [(f.name, f.data)
                                 for f in base_sarc.get_files()]:
        ext = Path(nest_file).suffix
        if ext in {".yml", ".bak"}:
            continue
        if nest_file in old_files:
            old_data = util.unyaz_if_needed(old_sarc.get_file(nest_file).data)
        file_data = util.unyaz_if_needed(file_data)
        if nest_file not in old_files or (file_data != old_data
                                          and ext not in util.AAMP_EXTS):
            if (ext in util.SARC_EXTS and nest_file in old_files
                    and nest_file not in SPECIAL):
                nest_old_sarc = oead.Sarc(old_data)
                nest_base_sarc = oead.Sarc(file_data)
                nest_new_sarc = _clean_sarc(nest_old_sarc, nest_base_sarc)
                if nest_new_sarc:
                    new_bytes = nest_new_sarc.write()[1]
                    if ext.startswith(".s") and ext != ".sarc":
                        new_bytes = util.compress(new_bytes)
                    new_sarc.files[nest_file] = oead.Bytes(new_bytes)
                    can_delete = False
                else:
                    continue
            else:
                if ext.startswith(".s") and ext != ".sarc":
                    file_data = util.compress(file_data)
                new_sarc.files[nest_file] = oead.Bytes(file_data)
                can_delete = False
    return None if can_delete else new_sarc
Beispiel #2
0
def inject_files_into_actor(actor: str, files: Dict[str, ByteString]):
    actor_sarc: oead.Sarc
    if actor in TITLE_ACTORS:
        title_path = (get_master_modpack_dir() / get_content_path() / "Pack" /
                      "TitleBG.pack")
        if not title_path.exists():
            title_path = get_game_file("Pack/TitleBG.pack")
        title_sarc = oead.Sarc(title_path.read_bytes())
        actor_sarc = oead.Sarc(
            decompress(
                title_sarc.get_file_data(
                    f"Actor/Pack/{actor}.sbactorpack").data))
        del title_sarc
    else:
        actor_path = (get_master_modpack_dir() / get_content_path() / "Actor" /
                      "Pack" / f"{actor}.sbactorpack")
        if not actor_path.exists():
            actor_path = get_game_file(f"Actor/Pack/{actor}.sbactorpack")
        actor_sarc = oead.Sarc(decompress(actor_path.read_bytes()))
    new_sarc = oead.SarcWriter.from_sarc(actor_sarc)
    del actor_sarc
    for file, data in files.items():
        new_sarc.files[file] = oead.Bytes(data)
    out_bytes = compress(new_sarc.write()[1])

    if actor in TITLE_ACTORS:
        inject_file_into_sarc(f"Actor/Pack/{actor}.sbactorpack", out_bytes,
                              "Pack/TitleBG.pack", True)
    else:
        output = (get_master_modpack_dir() / get_content_path() / "Actor" /
                  "Pack" / f"{actor}.sbactorpack")
        output.parent.mkdir(parents=True, exist_ok=True)
        output.write_bytes(out_bytes)
Beispiel #3
0
def swap_region(mod_pack: Path, user_lang: str) -> Path:
    mod_sarc = oead.Sarc(mod_pack.read_bytes())
    mod_msg_data = mod_sarc.get_file(0).data
    new_pack = oead.SarcWriter(
        endian=oead.Endianness.Big
        if util.get_settings("wiiu")
        else oead.Endianness.Little
    )
    new_pack.files[f"Message/Msg_{user_lang}.product.ssarc"] = oead.Bytes(mod_msg_data)
    new_pack_path = mod_pack.with_name(f"Bootup_{user_lang}.temppack")
    new_pack_path.write_bytes(new_pack.write()[1])
    return new_pack_path
Beispiel #4
0
def nested_patch(pack: oead.Sarc, nest: dict) -> Tuple[oead.SarcWriter, dict]:
    new_sarc: oead.SarcWriter = oead.SarcWriter.from_sarc(pack)
    failures: dict = {}

    for file, stuff in nest.items():
        file_bytes = pack.get_file(file).data
        yazd = file_bytes[0:4] == b"Yaz0"
        file_bytes = util.decompress(file_bytes) if yazd else file_bytes

        if isinstance(stuff, dict):
            sub_sarc = oead.Sarc(file_bytes)
            new_sub_sarc, sub_failures = nested_patch(sub_sarc, stuff)
            for failure in sub_failures:
                failure[file + "//" + failure] = sub_failures[failure]
            del sub_sarc
            new_bytes = bytes(new_sub_sarc.write()[1])
            new_sarc.files[file] = new_bytes if not yazd else util.compress(
                new_bytes)

        elif isinstance(stuff, ParameterList):
            try:
                if file_bytes[0:4] == b"AAMP":
                    aamp_contents = ParameterIO.from_binary(file_bytes)
                    try:
                        file_ext = os.path.splitext(file)[1]
                        aamp_contents = shop_merge(
                            aamp_contents,
                            file_ext.replace(".", ""),
                            stuff.lists["Additions"],
                            stuff.lists["Removals"],
                        )
                        aamp_bytes = ParameterIO.to_binary(aamp_contents)
                    except:  # pylint: disable=bare-except
                        raise RuntimeError(
                            f"AAMP file {file} could be merged.")
                    del aamp_contents
                    new_bytes = aamp_bytes if not yazd else util.compress(
                        aamp_bytes)
                    cache_merged_shop(file, new_bytes)
                else:
                    raise ValueError(
                        "Wait, what the heck, this isn't an AAMP file?!")
            except ValueError:
                new_bytes = pack.get_file(file).data
                print(f"Deep merging {file} failed. No changes were made.")

            new_sarc.files[file] = oead.Bytes(new_bytes)
    return new_sarc, failures
Beispiel #5
0
def merge_sarcs(file_name: str, sarcs: List[Union[Path,
                                                  bytes]]) -> (str, bytes):
    opened_sarcs: List[oead.Sarc] = []
    if "ThunderRodLv2" in file_name:
        print()
    if isinstance(sarcs[0], Path):
        for i, sarc_path in enumerate(sarcs):
            sarcs[i] = sarc_path.read_bytes()
    for sarc_bytes in sarcs:
        sarc_bytes = util.unyaz_if_needed(sarc_bytes)
        try:
            opened_sarcs.append(oead.Sarc(sarc_bytes))
        except (ValueError, RuntimeError, oead.InvalidDataError):
            continue

    all_files = {
        file.name
        for open_sarc in opened_sarcs for file in open_sarc.get_files()
    }
    nested_sarcs = {}
    new_sarc = oead.SarcWriter(endian=oead.Endianness.Big if util.get_settings(
        "wiiu") else oead.Endianness.Little)
    files_added = set()

    for opened_sarc in reversed(opened_sarcs):
        for file in [
                f for f in opened_sarc.get_files() if f.name not in files_added
        ]:
            file_data = oead.Bytes(file.data)
            if (file.name[file.name.rindex("."):] in util.SARC_EXTS -
                    EXCLUDE_EXTS) and file.name not in SPECIAL:
                if file.name not in nested_sarcs:
                    nested_sarcs[file.name] = []
                nested_sarcs[file.name].append(util.unyaz_if_needed(file_data))
            elif util.is_file_modded(file.name.replace(".s", "."),
                                     file_data,
                                     count_new=True):
                new_sarc.files[file.name] = file_data
                files_added.add(file.name)
    util.vprint(set(nested_sarcs.keys()))
    for file, sarcs in nested_sarcs.items():
        if not sarcs:
            continue
        merged_bytes = merge_sarcs(file, sarcs[::-1])[1]
        if Path(file).suffix.startswith(".s") and not file.endswith(".sarc"):
            merged_bytes = util.compress(merged_bytes)
        new_sarc.files[file] = merged_bytes
        files_added.add(file)
    for file in [file for file in all_files if file not in files_added]:
        for opened_sarc in [
                open_sarc for open_sarc in opened_sarcs
                if (file in [f.name for f in open_sarc.get_files()])
        ]:
            new_sarc.files[file] = oead.Bytes(opened_sarc.get_file(file).data)
            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]))
Beispiel #6
0
    def perform_merge(self):
        force = self._options.get("force", False)
        slog_path = util.get_master_modpack_dir() / "logs" / "savedata.log"

        new_entries = self.consolidate_diffs(self.get_all_diffs())
        if not new_entries:
            print("No savedata merging necessary.")
            if slog_path.exists():
                slog_path.unlink()
            if (util.get_master_modpack_dir() / "logs" /
                    "savedata.sarc").exists():
                (util.get_master_modpack_dir() / "logs" /
                 "savedata.sarc").unlink()
            return
        if slog_path.exists() and not force:
            with slog_path.open("r") as l_file:
                if xxhash.xxh64_hexdigest(str(new_entries)) == l_file.read():
                    print("No savedata merging necessary.")
                    return

        savedata = get_stock_savedata()
        save_files = sorted(savedata.get_files(), key=lambda f: f.name)[0:-2]

        print("Merging changes...")
        merged_entries = oead.byml.Array(
            sorted(
                {
                    entry["HashValue"].v: entry
                    for entry in [
                        *[
                            e for file in save_files for e in
                            oead.byml.from_binary(file.data)["file_list"][1]
                        ],
                        *new_entries["add"],
                    ] if entry not in new_entries["del"]
                }.values(),
                key=itemgetter("HashValue"),
            ))
        print("Creating and injecting new savedataformat.sarc...")
        new_savedata = oead.SarcWriter(
            endian=oead.Endianness.Big if util.get_settings("wiiu") else oead.
            Endianness.Little)
        num_files = ceil(len(merged_entries) / 8192)
        for i in range(num_files):
            end_pos = (i + 1) * 8192
            if end_pos > len(merged_entries):
                end_pos = len(merged_entries)
            data = oead.byml.to_binary(
                oead.byml.Hash({
                    "file_list":
                    oead.byml.Array([
                        {
                            "IsCommon": False,
                            "IsCommonAtSameAccount": False,
                            "IsSaveSecureCode": True,
                            "file_name": "game_data.sav",
                        },
                        oead.byml.Array(merged_entries[i * 8192:end_pos]),
                    ]),
                    "save_info":
                    oead.byml.Array([{
                        "directory_num": oead.S32(8),
                        "is_build_machine": True,
                        "revision": oead.S32(18203),
                    }]),
                }),
                big_endian=util.get_settings("wiiu"),
            )
            new_savedata.files[f"/saveformat_{i}.bgsvdata"] = data

        new_savedata.files[f"/saveformat_{num_files}.bgsvdata"] = oead.Bytes(
            savedata.get_file("/saveformat_6.bgsvdata").data)
        new_savedata.files[
            f"/saveformat_{num_files + 1}.bgsvdata"] = oead.Bytes(
                savedata.get_file("/saveformat_7.bgsvdata").data)

        del savedata
        new_save_bytes = new_savedata.write()[1]
        del new_savedata
        util.inject_file_into_sarc(
            "GameData/savedataformat.ssarc",
            util.compress(new_save_bytes),
            "Pack/Bootup.pack",
            create_sarc=True,
        )
        (util.get_master_modpack_dir() / "logs").mkdir(parents=True,
                                                       exist_ok=True)
        ((util.get_master_modpack_dir() / "logs" /
          "savedata.sarc").write_bytes(new_save_bytes))

        print("Updating RSTB...")
        rstable.set_size(
            "GameData/savedataformat.sarc",
            rstable.calculate_size("GameData/savedataformat.sarc",
                                   new_save_bytes),
        )
        del new_save_bytes

        slog_path.parent.mkdir(parents=True, exist_ok=True)
        with slog_path.open("w", encoding="utf-8") as l_file:
            l_file.write(xxhash.xxh64_hexdigest(str(new_entries)))