Ejemplo n.º 1
0
def _build_sarc(d: Path, params: BuildParams):
    rvs = {}
    for f in {
        f for f in (params.mod / d.relative_to(params.out)).rglob("**/*") if f.is_file()
    }:
        canon = get_canon_name(f.relative_to(params.mod), True)
        if params.table.is_file_modded(canon, f.read_bytes()):
            modified = True
            break
    else:
        modified = False
    try:
        s = oead.SarcWriter(
            endian=oead.Endianness.Big if params.be else oead.Endianness.Little
        )
        lead = ""
        if (d / ".align").exists():
            alignment = int((d / ".align").read_text())
            s.set_mode(oead.SarcWriter.Mode.Legacy)
            s.set_min_alignment(alignment)
            (d / ".align").unlink()
        if (d / ".slash").exists():
            lead = "/"
            (d / ".slash").unlink()

        f: Path
        for f in {f for f in d.rglob("**/*") if f.is_file()}:
            path = f.relative_to(d).as_posix()
            canon = path.replace(".s", ".")
            data = f.read_bytes()
            if (
                f.suffix not in SARC_EXTS | AAMP_EXTS | BYML_EXTS | RSTB_EXCLUDE_EXTS
                and d.suffix not in {".sarc", ".ssarc"}
            ) and params.table.is_file_modded(canon, data, flag_new=True):
                rvs.update({canon: _get_rstb_val(path, data, params.guess, params.be)})
            s.files[lead + path] = data
            f.unlink()

        shutil.rmtree(d)
        _, sb = s.write()
        if modified and _should_rstb(d):
            rvs.update(
                {
                    get_canon_name(d.relative_to(params.out)): _get_rstb_val(
                        d.name, sb, params.guess, params.be
                    )
                }
            )
        d.write_bytes(
            sb
            if not (d.suffix.startswith(".s") and d.suffix != ".sarc")
            else compress(sb)
        )
    except Exception as err:  # pylint: disable=broad-except
        params.warning(f"Failed to build {d.relative_to(params.out).as_posix()}. {err}")
        return {}
    else:
        if params.verbose:
            print(f"Built {d.relative_to(params.out).as_posix()}")
        return rvs
Ejemplo n.º 2
0
def make_new_savedata(store: FlagStore, big_endian: bool,
                      orig_files: list) -> bytes:
    svwriter = oead.SarcWriter(
        endian=oead.Endianness.Big if big_endian else oead.Endianness.Little)
    svdata_array = store.flags_to_svdata_Array()
    num_files = ceil(len(svdata_array) / 8192)
    for idx in range(num_files):
        start = idx * 8192
        end = (idx + 1) * 8192
        if end > len(svdata_array):
            end = len(svdata_array)
        svwriter.files[f"/saveformat_{idx}.bgsvdata"] = 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(svdata_array[start:end]),
                ]),
                "save_info":
                oead.byml.Array([{
                    "directory_num": oead.S32(num_files + 2),
                    "is_build_machine": True,
                    "revision": oead.S32(18203),
                }]),
            }),
            big_endian,
        )
    svwriter.files[f"/saveformat_{num_files}.bgsvdata"] = orig_files[0]
    svwriter.files[f"/saveformat_{num_files+1}.bgsvdata"] = orig_files[1]
    return svwriter.write()[1]
Ejemplo n.º 3
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
Ejemplo n.º 4
0
def _build_actor(link: Path, params: BuildParams):
    pack = oead.SarcWriter(
        endian=oead.Endianness.Big if params.be else oead.Endianness.Little
    )
    actor_name = link.stem
    modified = False
    rvs = {}
    files = _parse_actor_link(link, params)
    for name, path in files.items():
        try:
            data = path.read_bytes()
        except FileNotFoundError as e:
            if name.startswith("Physics"):
                params.warning(
                    f"Havok physics file {name} not found for actor {actor_name}. "
                    "Ignore if intentionally using a file not in the actor pack."
                )
                continue
            params.warning(
                f'Failed to build actor "{actor_name}". Could not find linked file "'
                f'{Path(e.filename).relative_to(params.out)}".'
            )
            return {}
        pack.files[name] = data
        canon = name.replace(".s", ".")
        if params.table.is_file_modded(canon, memoryview(data), True):
            if not modified:
                modified = True
            rvs.update({canon: _get_rstb_val(canon, data, params.guess, params.be)})
    _, sb = pack.write()
    dest: Path
    if actor_name in TITLE_ACTORS | params.titles:
        dest = (
            params.out
            / params.content
            / "Pack"
            / "TitleBG.pack"
            / "Actor"
            / "Pack"
            / f"{actor_name}.sbactorpack"
        )
    else:
        dest = (
            params.out / params.content / "Actor" / "Pack" / f"{actor_name}.sbactorpack"
        )
    if not dest.parent.exists():
        dest.parent.mkdir(parents=True, exist_ok=True)
    dest.write_bytes(compress(sb))
    if modified:
        rvs.update(
            {
                f"Actor/Pack/{actor_name}.bactorpack": _get_rstb_val(
                    f"{actor_name}.sbactorpack", sb, params.guess, params.be
                )
            }
        )
    return rvs
Ejemplo n.º 5
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
Ejemplo n.º 6
0
 def create_sarc(self, big_endian: bool, alignment: int) -> dict:
     tmp_sarc = oead.SarcWriter(
         oead.Endianness.Big if big_endian else oead.Endianness.Little,
         oead.SarcWriter.Mode.New
         if alignment == 4 else oead.SarcWriter.Mode.Legacy,
     )
     self._open_sarc, tree, modded = _sarc.open_sarc(
         oead.Sarc(tmp_sarc.write()[1]))
     return {
         "sarc": tree,
         "be": big_endian,
         "path": "",
         "modded": modded,
     }
Ejemplo n.º 7
0
def make_new_gamedata(store: FlagStore, big_endian: bool) -> bytes:
    bgwriter = oead.SarcWriter(
        endian=oead.Endianness.Big if big_endian else oead.Endianness.Little)
    for prefix, data_type in BGDATA_MAPPING.items():
        bgdata_array = store.flags_to_bgdata_Array(prefix)
        num_files = ceil(len(bgdata_array) / 4096)
        for idx in range(num_files):
            start = idx * 4096
            end = (idx + 1) * 4096
            if end > len(bgdata_array):
                end = len(bgdata_array)
            bgwriter.files[f"/{prefix}_{idx}.bgdata"] = oead.byml.to_binary(
                oead.byml.Hash({data_type: bgdata_array[start:end]}),
                big_endian)
    return bgwriter.write()[1]
Ejemplo n.º 8
0
def _build_sarc(d: Path, params: BuildParams):
    rvs = {}
    for f in {
            f
            for f in (params.mod / d.relative_to(params.out)).rglob('**/*')
            if f.is_file()
    }:
        canon = get_canon_name(f.relative_to(params.mod), True)
        if params.table.is_file_modded(canon, f.read_bytes()):
            modified = True
            break
    else:
        modified = False
    try:
        s = oead.SarcWriter(endian=oead.Endianness.Big if params.be else oead.
                            Endianness.Little)
        lead = ''
        if (d / '.align').exists():
            alignment = int((d / '.align').read_text())
            s.set_mode(oead.SarcWriter.Mode.Legacy)
            s.set_min_alignment(alignment)
            (d / '.align').unlink()
        if (d / '.slash').exists():
            lead = '/'
            (d / '.slash').unlink()

        f: Path
        for f in {f for f in d.rglob('**/*') if f.is_file()}:
            path = f.relative_to(d).as_posix()
            s.files[lead + path] = f.read_bytes()
            f.unlink()

        shutil.rmtree(d)
        _, sb = s.write()
        if modified and _should_rstb(d):
            rvs.update({
                get_canon_name(d.relative_to(params.out)):
                _get_rstb_val(d.suffix, sb, params.guess, params.be)
            })
        d.write_bytes(sb if not (d.suffix.startswith('.s')
                                 and d.suffix != '.sarc') else compress(sb))
    except:
        print(f'Failed to build {d.relative_to(params.out).as_posix()}')
        return {}
    else:
        if params.verbose:
            print(f'Built {d.relative_to(params.out).as_posix()}')
        return rvs
Ejemplo n.º 9
0
def _pack_sarc(folder: Path, tmp_dir: Path, hashes: dict):
    packed = oead.SarcWriter(
        endian=oead.Endianness.Big
        if util.get_settings("wiiu")
        else oead.Endianness.Little
    )
    try:
        canon = util.get_canon_name(
            folder.relative_to(tmp_dir).as_posix(), allow_no_source=True
        )
        if canon not in hashes:
            raise FileNotFoundError("File not in game dump")
        stock_file = util.get_game_file(folder.relative_to(tmp_dir))
        try:
            old_sarc = oead.Sarc(util.unyaz_if_needed(stock_file.read_bytes()))
        except (RuntimeError, ValueError, oead.InvalidDataError):
            raise ValueError("Cannot open file from game dump")
        old_files = {f.name for f in old_sarc.get_files()}
    except (FileNotFoundError, ValueError):
        for file in {f for f in folder.rglob("**/*") if f.is_file()}:
            packed.files[file.relative_to(folder).as_posix()] = file.read_bytes()
    else:
        for file in {
            f
            for f in folder.rglob("**/*")
            if f.is_file() and not f.suffix in EXCLUDE_EXTS
        }:
            file_data = file.read_bytes()
            xhash = xxhash.xxh64_intdigest(util.unyaz_if_needed(file_data))
            file_name = file.relative_to(folder).as_posix()
            if file_name in old_files:
                old_hash = xxhash.xxh64_intdigest(
                    util.unyaz_if_needed(old_sarc.get_file(file_name).data)
                )
            if file_name not in old_files or (xhash != old_hash):
                packed.files[file_name] = file_data
    finally:
        shutil.rmtree(folder)
        if not packed.files:
            return  # pylint: disable=lost-exception
        sarc_bytes = packed.write()[1]
        folder.write_bytes(
            util.compress(sarc_bytes)
            if (folder.suffix.startswith(".s") and not folder.suffix == ".sarc")
            else sarc_bytes
        )
Ejemplo n.º 10
0
def sarc_create(args: argparse.Namespace) -> None:
    sarc = oead.SarcWriter(
        oead.Endianness.Big if args.big_endian else oead.Endianness.Little)

    if args.folder.name == "-":
        raise SystemExit("You cannot pipe in a folder")

    for f in args.folder.glob("**/*.*"):
        if f.is_file():
            sarc.files[f.as_posix()[len(args.folder.as_posix()) +
                                    1:]] = f.read_bytes()

    if args.sarc and args.sarc.name == "!!":
        args.sarc = args.folder.with_suffix(".pack")

    if args.sarc and args.sarc.name != "-":
        [write_stdout(f"{f}\n".encode("utf-8")) for f in sarc.files]

    write_sarc(sarc, args.sarc)
    return
Ejemplo n.º 11
0
    def get_bytes(self, be: bool) -> bytes:
        writer = oead.SarcWriter()
        endianness = oead.Endianness.Big if be else oead.Endianness.Little
        writer.set_endianness(endianness)

        filename = f"Actor/ActorLink/{self._actorname}.bxml"
        writer.files[filename] = oead.aamp.ParameterIO.to_binary(
            self.get_actorlink())

        for link, data in self._aampfiles.items():
            folder, ext = util.AAMP_LINK_REFS[link]
            filename = f"Actor/{folder}/{self.get_link(link)}{ext}"
            writer.files[filename] = oead.aamp.ParameterIO.to_binary(data)

        for link, data in self._bymlfiles.items():
            folder, ext = util.BYML_LINK_REFS[link]
            filename = f"Actor/{folder}/{self.get_link(link)}{ext}"
            writer.files[filename] = oead.byml.to_binary(data, be)

        for filename, data in self._miscfiles.items():
            writer.files[filename] = data

        return writer.write()[1]
Ejemplo n.º 12
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]))
Ejemplo n.º 13
0
    def perform_merge(self):
        # pylint: disable=unsupported-assignment-operation
        langs = ({util.get_settings("lang")}
                 if not self._options["all_langs"] else get_user_languages())
        for lang in langs:
            print("Loading text mods...")
            diffs = self.consolidate_diffs(self.get_all_diffs())
            if not diffs or lang not in diffs:
                print("No text merge necessary")
                for bootup in util.get_master_modpack_dir().rglob(
                        "**/Bootup_????.pack"):
                    bootup.unlink()
                return
            util.vprint({
                lang: {
                    file: list(entries.keys())
                    for file, entries in diffs[lang].items()
                }
            })

            print(f"Merging modded texts for {lang}...")
            saved_files = set()
            with TemporaryDirectory() as tmp:
                tmp_dir = Path(tmp)
                ref_lang = "XXen" if lang.endswith("en") else lang
                extract_refs(ref_lang, tmp_dir)
                tmp_dir = tmp_dir / "refs" / ref_lang
                this_pool = self._pool or multiprocessing.Pool(
                    maxtasksperchild=500)
                this_pool.map(partial(merge_msyt, tmp_dir=tmp_dir),
                              diffs[lang].items())
                if not self._pool:
                    this_pool.close()
                    this_pool.join()

                m_args = [
                    MSYT_PATH,
                    "create",
                    "-d",
                    str(tmp_dir),
                    "-p",
                    "wiiu" if util.get_settings("wiiu") else "switch",
                    "-o",
                    str(tmp_dir),
                ]
                result: subprocess.CompletedProcess
                if system() == "Windows":
                    result = subprocess.run(
                        m_args,
                        capture_output=True,
                        creationflags=util.CREATE_NO_WINDOW,
                        check=False,
                        text=True,
                    )

                else:
                    result = subprocess.run(
                        m_args,
                        capture_output=True,
                        check=False,
                        text=True,
                    )
                if result.stderr:
                    raise RuntimeError(
                        f"There was an error merging game texts. {result.stderr}"
                    )

                msg_sarc = oead.SarcWriter(
                    endian=oead.Endianness.Big if util.
                    get_settings("wiiu") else oead.Endianness.Little)
                for file in tmp_dir.rglob("**/*.msbt"):
                    msg_sarc.files[file.relative_to(
                        tmp_dir).as_posix()] = file.read_bytes()
                    saved_files.add(file.relative_to(tmp_dir).as_posix())
            bootup_sarc = oead.SarcWriter(
                endian=oead.Endianness.Big if util.
                get_settings("wiiu") else oead.Endianness.Little)
            bootup_sarc.files[
                f"Message/Msg_{lang}.product.ssarc"] = util.compress(
                    msg_sarc.write()[1])

            bootup_path = (util.get_master_modpack_dir() /
                           util.get_content_path() / "Pack" /
                           f"Bootup_{lang}.pack")
            bootup_path.parent.mkdir(parents=True, exist_ok=True)
            bootup_path.write_bytes(bootup_sarc.write()[1])
            del bootup_sarc
            del msg_sarc
            print(f"{lang} texts merged successfully")
Ejemplo n.º 14
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)))
Ejemplo n.º 15
0
    def perform_merge(self):
        force = self._options.get("force", False)
        glog_path = util.get_master_modpack_dir() / "logs" / "gamedata.log"

        modded_entries = self.consolidate_diffs(self.get_all_diffs())
        util.vprint("All gamedata diffs:")
        util.vprint(modded_entries)
        if not modded_entries:
            print("No gamedata merging necessary.")
            if glog_path.exists():
                glog_path.unlink()
            if (util.get_master_modpack_dir() / "logs" /
                    "gamedata.sarc").exists():
                (util.get_master_modpack_dir() / "logs" /
                 "gamedata.sarc").unlink()
            return
        if glog_path.exists() and not force:
            with glog_path.open("r") as l_file:
                if xxhash.xxh64_hexdigest(
                        str(modded_entries)) == l_file.read():
                    print("No gamedata merging necessary.")
                    return

        print("Loading stock gamedata...")
        gamedata = consolidate_gamedata(get_stock_gamedata())
        merged_entries = {
            data_type:
            oead.byml.Hash({entry["DataName"]: entry
                            for entry in entries})
            for data_type, entries in gamedata.items()
        }
        del gamedata

        print("Merging changes...")
        for data_type in {d for d in merged_entries if d in modded_entries}:
            util.dict_merge(
                merged_entries[data_type],
                modded_entries[data_type]["add"],
                shallow=True,
            )
            for entry in modded_entries[data_type]["del"]:
                try:
                    del merged_entries[data_type][entry]
                except KeyError:
                    continue

        merged_entries = oead.byml.Hash({
            data_type: oead.byml.Array({value
                                        for _, value in entries.items()})
            for data_type, entries in merged_entries.items()
        })
        print("Creating and injecting new gamedata.sarc...")
        new_gamedata = oead.SarcWriter(
            endian=oead.Endianness.Big if util.get_settings("wiiu") else oead.
            Endianness.Little)
        for data_type in merged_entries:
            num_files = ceil(len(merged_entries[data_type]) / 4096)
            for i in range(num_files):
                end_pos = (i + 1) * 4096
                if end_pos > len(merged_entries[data_type]):
                    end_pos = len(merged_entries[data_type])
                new_gamedata.files[
                    f"/{data_type}_{i}.bgdata"] = oead.byml.to_binary(
                        oead.byml.Hash({
                            data_type:
                            merged_entries[data_type][i * 4096:end_pos]
                        }),
                        big_endian=util.get_settings("wiiu"),
                    )
        new_gamedata_bytes = new_gamedata.write()[1]
        del new_gamedata
        util.inject_file_into_sarc(
            "GameData/gamedata.ssarc",
            util.compress(new_gamedata_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" /
         "gamedata.sarc").write_bytes(new_gamedata_bytes)

        print("Updating RSTB...")
        rstable.set_size(
            "GameData/gamedata.sarc",
            rstable.calculate_size("GameData/gamedata.sarc",
                                   new_gamedata_bytes),
        )
        del new_gamedata_bytes

        glog_path.parent.mkdir(parents=True, exist_ok=True)
        with glog_path.open("w", encoding="utf-8") as l_file:
            l_file.write(xxhash.xxh64_hexdigest(str(modded_entries)))
Ejemplo n.º 16
0
def _build_actor(link: Path, params: BuildParams):
    pack = oead.SarcWriter(
        endian=oead.Endianness.Big if params.be else oead.Endianness.Little)
    actor_name = link.stem
    actor = oead.aamp.ParameterIO.from_binary(link.read_bytes())
    actor_path = params.out / params.content / 'Actor'
    targets = actor.objects['LinkTarget']
    modified = False
    try:
        files = {f'Actor/ActorLink/{actor_name}.bxml': link}
        for p, name in targets.params.items():
            name = name.v
            if name == 'Dummy':
                continue
            if p.hash in LINK_MAP:
                path = LINK_MAP[p.hash].replace('*', name)
                files['Actor/' + path] = actor_path / path
            elif p == 110127898:  # ASUser
                list_path = actor_path / 'ASList' / f'{name}.baslist'
                aslist_bytes = list_path.read_bytes()
                files[f'Actor/ASList/{name}.baslist'] = list_path
                aslist = oead.aamp.ParameterIO.from_binary(aslist_bytes)
                for _, anim in aslist.lists['ASDefines'].objects.items():
                    filename = anim.params["Filename"].v
                    if filename != 'Dummy':
                        files[
                            f'Actor/AS/{filename}.bas'] = actor_path / 'AS' / f'{filename}.bas'
            elif p == 1086735552:  # AttentionUser
                list_path = actor_path / 'AttClientList' / f'{name}.batcllist'
                atcllist_bytes = list_path.read_bytes()
                files[f'Actor/AttClientList/{name}.batcllist'] = list_path
                atcllist = oead.aamp.ParameterIO.from_binary(atcllist_bytes)
                for _, atcl in atcllist.lists['AttClients'].objects.items():
                    filename = atcl.params['FileName'].v
                    if filename != 'Dummy':
                        files[
                            f'Actor/AttClient/{filename}.batcl'] = actor_path / 'AttClient' / f'{filename}.batcl'
            elif p == 4022948047:  # RgConfigListUser
                list_path = actor_path / 'RagdollConfigList' / f'{name}.brgconfiglist'
                rgconfiglist_bytes = list_path.read_bytes()
                files[
                    f'Actor/RagdollConfigList/{name}.brgconfiglist'] = list_path
                rgconfiglist = oead.aamp.ParameterIO.from_binary(
                    rgconfiglist_bytes)
                for _, impl in rgconfiglist.lists[
                        'ImpulseParamList'].objects.items():
                    filename = impl.params['FileName'].v
                    if filename != 'Dummy':
                        files[f'Actor/RagdollConfig/{filename}.brgconfig'] = actor_path / \
                            'RagdollConfig' / f'{filename}.brgconfig'
            elif p == 2366604039:  # PhysicsUser
                phys_source = params.out / params.content / 'Physics'
                phys_path = actor_path / 'Physics' / f'{name}.bphysics'
                phys_bytes = phys_path.read_bytes()
                files[f'Actor/Physics/{name}.bphysics'] = phys_path
                phys = oead.aamp.ParameterIO.from_binary(phys_bytes)
                types = phys.lists['ParamSet'].objects[1258832850]
                if types.params['use_ragdoll'].v:
                    rg_path = str(phys.lists['ParamSet'].objects['Ragdoll'].
                                  params['ragdoll_setup_file_path'].v)
                    files[
                        f'Physics/Ragdoll/{rg_path}'] = phys_source / 'Ragdoll' / rg_path
                if types.params['use_support_bone'].v:
                    sb_path = str(
                        phys.lists['ParamSet'].objects['SupportBone'].
                        params['support_bone_setup_file_path'].v)
                    files[
                        f'Physics/SupportBone/{sb_path}'] = phys_source / 'SupportBone' / sb_path
                if types.params['use_cloth'].v:
                    cloth_path = str(
                        phys.lists['ParamSet'].lists['Cloth'].objects[
                            'ClothHeader'].params['cloth_setup_file_path'].v)
                    files[
                        f'Physics/Cloth/{cloth_path}'] = phys_source / 'Cloth' / cloth_path
                if types.params['use_rigid_body_set_num'].v > 0:
                    for _, rigid in phys.lists['ParamSet'].lists[
                            'RigidBodySet'].lists.items():
                        try:
                            rigid_path = str(rigid.objects[4288596824].
                                             params['setup_file_path'].v)
                            if (phys_path / 'RigidBody' / rigid_path).exists():
                                files[
                                    f'Physics/RigidBody/{rigid_path}'] = phys_path / 'RigidBody' / rigid_path
                        except KeyError:
                            continue
        for name, path in files.items():
            data = path.read_bytes()
            pack.files[name] = data
            if not modified and params.table.is_file_modded(
                    name.replace('.s', ''), memoryview(data), True):
                modified = True
    except FileNotFoundError as e:
        print(
            f'Failed to build actor "{actor_name}": Could not find linked file "{e.filename}".'
        )
        return {}
    _, sb = pack.write()
    dest: Path
    if actor_name in TITLE_ACTORS:
        dest = params.out / params.content / 'Pack' / 'TitleBG.pack' / 'Actor' / 'Pack' / f'{actor_name}.sbactorpack'
    else:
        dest = params.out / params.content / 'Actor' / 'Pack' / f'{actor_name}.sbactorpack'
    if not dest.parent.exists():
        dest.parent.mkdir(parents=True, exist_ok=True)
    dest.write_bytes(compress(sb))
    if modified:
        return {
            f'Actor/Pack/{actor_name}.bactorpack':
            _get_rstb_val('.sbactorpack', sb, params.guess, params.be)
        }
    return {}
Ejemplo n.º 17
0
def build_sarc() -> None:
    set_start_method("spawn", True)
    parser = argparse.ArgumentParser(
        description="Build a SARC file from a single source folder")
    parser.add_argument("source", help="Source folder for SARC")
    parser.add_argument(
        "output",
        help='Path to output SARC file, will auto compress if\
        extension starts with ".s"',
    )
    parser.add_argument("--be",
                        "-B",
                        help="Use big endian where applicable",
                        action="store_true")
    parser.add_argument("--verbose",
                        "-V",
                        help="Provide more detailed output",
                        action="store_true")
    args = parser.parse_args()

    source = Path(args.source)
    output = Path(args.output)

    def build_yaml(file: Path) -> bytes:
        real_file = file.with_suffix("")
        data: bytes
        if real_file.suffix in BYML_EXTS:
            data = oead.byml.to_binary(
                oead.byml.from_text(file.read_text("utf-8")), args.be)
        elif real_file.suffix in AAMP_EXTS:
            data = oead.aamp.ParameterIO.from_text(
                file.read_text("utf-8")).to_binary()
        else:
            raise TypeError("Can only build AAMP or BYML files from YAML")
        if real_file.suffix.startswith(".s"):
            data = oead.yaz0.compress(data)
        return data

    yml_table = {file: build_yaml(file) for file in source.rglob("**/*.yml")}
    all_files = {
        f
        for f in source.rglob("**/*")
        if f.is_file() or (f.is_dir() and f.suffix in SARC_EXTS)
    }
    nest_sarcs = {}

    for nest in sorted(
        {d
         for d in all_files if d.suffix in SARC_EXTS and d.is_dir()}
            | {source},
            key=lambda x: len(x.parts),
            reverse=True,
    ):
        if args.verbose:
            print(f"Building {nest.name}...")
        sarc = oead.SarcWriter(
            oead.Endianness.Big if args.be else oead.Endianness.Little)
        for file in all_files.copy():
            try:
                rel_path = file.relative_to(nest)
                assert file != nest
            except (ValueError, AssertionError):
                continue
            store_path = (rel_path.as_posix() if rel_path.suffix != ".yml" else
                          rel_path.with_suffix("").as_posix())
            if args.verbose:
                print(f"  Adding {store_path} to {nest.name}")
            sarc.files[store_path] = yml_table.pop(
                file,
                nest_sarcs.pop(file, None) or file.read_bytes())
            all_files.remove(file)

        if nest.suffix.startswith(".s") and nest.suffix != ".sarc":
            sarc_data = oead.yaz0.compress(sarc.write()[1])
        else:
            sarc_data = sarc.write()[1]
        nest_sarcs[nest] = sarc_data
        if args.verbose:
            print(f"Finished building {nest.name}")

    output.write_bytes(nest_sarcs[source])