Ejemplo n.º 1
0
def _get_sizes_in_sarc(
    file: Union[Path, oead.Sarc], guess: bool, is_aoc: bool = False
) -> {}:
    sizes = {}
    if isinstance(file, Path):
        is_aoc = util.get_dlc_path() in file.as_posix()
        try:
            file = oead.Sarc(util.unyaz_if_needed(file.read_bytes()))
        except (RuntimeError, oead.InvalidDataError):
            print(f"{file} could not be opened")
            return {}
    for nest_file, data in [(file.name, file.data) for file in file.get_files()]:
        canon = nest_file.replace(".s", ".")
        if data[0:4] == b"Yaz0":
            data = util.decompress(data)
        ext = Path(canon).suffix
        if (
            util.is_file_modded(canon, data)
            and ext not in EXCLUDE_EXTS
            and canon not in EXCLUDE_NAMES
        ):
            sizes[canon] = calculate_size(canon, data, guess=guess)
            if ext in util.SARC_EXTS - SARC_EXCLUDES:
                try:
                    nest_sarc = oead.Sarc(data)
                except (ValueError, RuntimeError, oead.InvalidDataError):
                    continue
                sizes.update(_get_sizes_in_sarc(nest_sarc, guess, is_aoc=is_aoc))
                del nest_sarc
        del data
    del file
    return sizes
Ejemplo n.º 2
0
def get_gamedata_sarc() -> oead.Sarc:
    bootup_path: Path = root_dir() / "content" / "Pack" / "Bootup.pack"
    bootup_sarc = oead.Sarc(bootup_path.read_bytes())
    gamedata_sarc = oead.Sarc(
        oead.yaz0.decompress(
            bootup_sarc.get_file("GameData/gamedata.ssarc").data))
    return gamedata_sarc
Ejemplo n.º 3
0
def _clean_sarc_file(file: Path, hashes: dict, tmp_dir: Path):
    canon = util.get_canon_name(file.relative_to(tmp_dir))
    try:
        stock_file = util.get_game_file(file.relative_to(tmp_dir))
    except FileNotFoundError:
        return
    try:
        old_sarc = oead.Sarc(util.unyaz_if_needed(stock_file.read_bytes()))
    except (RuntimeError, ValueError, oead.InvalidDataError):
        return
    if canon not in hashes:
        return
    try:
        base_sarc = oead.Sarc(util.unyaz_if_needed(file.read_bytes()))
    except (RuntimeError, ValueError, oead.InvalidDataError):
        return
    new_sarc = _clean_sarc(old_sarc, base_sarc)
    if not new_sarc:
        file.unlink()
    else:
        write_bytes = new_sarc.write()[1]
        file.write_bytes(
            write_bytes
            if not (file.suffix.startswith(".s") and file.suffix != ".ssarc")
            else util.compress(write_bytes)
        )
Ejemplo n.º 4
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.º 5
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)
Ejemplo n.º 6
0
    def from_actor(self, pack: Union[Path, str]) -> None:
        handled_filenames = set()
        if isinstance(pack, str):
            pack_nests = pack.split("//")
            pack = Path(pack_nests[-1])
            titlebg = oead.Sarc(Path(pack_nests[0]).read_bytes())
            data = util.unyaz_if_needed(titlebg.get_file(pack_nests[-1]).data)
        else:
            data = util.unyaz_if_needed(pack.read_bytes())
        self._actorname = pack.stem
        sarcdata = oead.Sarc(data)

        actorlink_name = f"Actor/ActorLink/{self._actorname}.bxml"
        actorlink = oead.aamp.ParameterIO.from_binary(
            sarcdata.get_file(actorlink_name).data)
        name_table = oead.aamp.get_default_name_table()
        for key in actorlink.objects:
            if name_table.get_name(key.hash, 0, 0) == "LinkTarget":
                for link in actorlink.objects[key].params:
                    linkstr = name_table.get_name(link.hash, 0, 0)
                    self._links[linkstr] = actorlink.objects[
                        "LinkTarget"].params[link].v
            elif name_table.get_name(key.hash, 0, 0) == "Tags":
                for tag in actorlink.objects[key].params:
                    self._tags.append(actorlink.objects["Tags"].params[tag].v)
            else:
                self._misc_tags.append({key: actorlink.objects[key]})
        handled_filenames.add(actorlink_name)

        for link in util.AAMP_LINK_REFS:
            folder, ext = util.AAMP_LINK_REFS[link]
            linkref = self._links[link]
            if linkref == "Dummy":
                continue
            filename = f"Actor/{folder}/{linkref}{ext}"
            filedata = sarcdata.get_file(filename).data
            self._aampfiles[link] = oead.aamp.ParameterIO.from_binary(filedata)
            handled_filenames.add(filename)

        for link in util.BYML_LINK_REFS:
            folder, ext = util.BYML_LINK_REFS[link]
            linkref = self._links[link]
            if linkref == "Dummy":
                continue
            filename = f"Actor/{folder}/{linkref}{ext}"
            filedata = sarcdata.get_file(filename).data
            self._bymlfiles[link] = oead.byml.from_binary(filedata)
            handled_filenames.add(filename)

        for f in sarcdata.get_files():
            if not f.name in handled_filenames:
                self._miscfiles[f"{f.name}"] = bytes(f.data)
Ejemplo n.º 7
0
def _convert_text_logs(logs_path: Path):
    diffs = {}
    with Pool(maxtasksperchild=500) as pool:
        for diff in pool.imap_unordered(_convert_text_log,
                                        logs_path.glob("texts_*.yml")):
            diffs.update(diff)
    fails = set()
    for text_pack in logs_path.glob("newtexts_*.sarc"):
        lang = text_pack.stem[9:]
        sarc = oead.Sarc(text_pack.read_bytes())
        for file in sarc.get_files():
            if lang not in diffs:
                diffs[lang] = {}
            try:
                diffs[lang].update(
                    {file.name: read_msbt(bytes(file.data))["entries"]})
            except RuntimeError:
                print(
                    f"Warning: {file.name} could not be processed and will not be used"
                )
                fails.add(file.name)
                continue
        util.vprint(f"{len(fails)} text files failed to process:\n{fails}")
        text_pack.unlink()
    (logs_path / "texts.json").write_text(json.dumps(diffs,
                                                     ensure_ascii=False,
                                                     indent=2),
                                          encoding="utf-8")
Ejemplo n.º 8
0
def _unbuild_file(
    f: Path, out: Path, content: str, mod: Path, verbose: bool, skip_texts: bool = False
) -> set:
    of = out / f.relative_to(mod)
    if not of.parent.exists():
        of.parent.mkdir(parents=True, exist_ok=True)
    names = set()
    canon = get_canon_name(f.relative_to(mod))
    if canon:
        names.add(canon)
    if f.name in HANDLED:
        pass
    elif f.suffix in AAMP_EXTS:
        of.with_suffix(f"{f.suffix}.yml").write_bytes(_aamp_to_yml(f.read_bytes()))
    elif f.suffix in BYML_EXTS:
        of.with_suffix(f"{f.suffix}.yml").write_bytes(_byml_to_yml(f.read_bytes()))
    elif f.suffix in SARC_EXTS:
        with f.open("rb") as file:
            data = file.read()
            if data[0:4] == b"Yaz0":
                data = decompress(data)
            if data[0:4] != b"SARC":
                return names
            s = oead.Sarc(data)
        if "bactorpack" in f.suffix:
            names.update(_unbuild_actorpack(s, out / content))
        else:
            names.update(_unbuild_sarc(s, of, skip_texts=skip_texts))
        del s
    else:
        of.write_bytes(f.read_bytes())
    if verbose:
        print(f"Unbuilt {f.relative_to(mod).as_posix()}")
    return names
Ejemplo n.º 9
0
def find_modded_files(
    tmp_dir: Path,
    pool: Optional[multiprocessing.pool.Pool] = None
) -> List[Union[Path, str]]:
    modded_files = []
    if isinstance(tmp_dir, str):
        tmp_dir = Path(tmp_dir)

    if (tmp_dir / util.get_dlc_path()).exists():
        try:
            util.get_aoc_dir()
        except FileNotFoundError:
            raise FileNotFoundError(
                "This mod uses DLC files, but BCML cannot locate the DLC folder in "
                "your game dump.")

    aoc_field = (tmp_dir / util.get_dlc_path() /
                 ("0010" if util.get_settings("wiiu") else "") / "Pack" /
                 "AocMainField.pack")
    if aoc_field.exists() and aoc_field.stat().st_size > 0:
        if not list((tmp_dir / util.get_dlc_path() /
                     ("0010" if util.get_settings("wiiu") else "")
                     ).rglob("Map/**/?-?_*.smubin")):
            aoc_pack = oead.Sarc(aoc_field.read_bytes())
            for file in aoc_pack.get_files():
                ex_out = (tmp_dir / util.get_dlc_path() /
                          ("0010" if util.get_settings("wiiu") else "") /
                          file.name)
                ex_out.parent.mkdir(parents=True, exist_ok=True)
                ex_out.write_bytes(file.data)
        aoc_field.write_bytes(b"")

    this_pool = pool or Pool(maxtasksperchild=500)
    results = this_pool.map(
        partial(_check_modded, tmp_dir=tmp_dir),
        {
            f
            for f in tmp_dir.rglob("**/*")
            if f.is_file() and "options" not in f.relative_to(tmp_dir).parts
        },
    )
    for result in results:
        if result:
            modded_files.append(result)
    total = len(modded_files)
    print(f'Found {total} modified file{"s" if total > 1 else ""}')

    total = 0
    sarc_files = {f for f in modded_files if f.suffix in util.SARC_EXTS}
    if sarc_files:
        print("Scanning files packed in SARCs...")
        for files in this_pool.imap_unordered(
                partial(find_modded_sarc_files, tmp_dir=tmp_dir), sarc_files):
            total += len(files)
            modded_files.extend(files)
        print(f'Found {total} modified packed file{"s" if total > 1 else ""}')
    if not pool:
        this_pool.close()
        this_pool.join()
    return modded_files
Ejemplo n.º 10
0
    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]

        title_sarc = oead.Sarc((mod_dir / util.get_content_path() / "Pack" /
                                "TitleBG.pack").read_bytes())
        mod_quests = oead.byml.from_binary(
            util.decompress(
                title_sarc.get_file("Quest/QuestProduct.sbquestpack").data))
        mod_names = [q["Name"] for q in mod_quests]
        diffs = oead.byml.Hash({
            "add":
            oead.byml.Array(),
            "mod":
            oead.byml.Hash(),
            "del":
            oead.byml.Array({q
                             for q in stock_names if q not in mod_names}),
        })

        for i, quest in enumerate(mod_quests):
            quest_name = quest["Name"]
            quest["prev_quest"] = mod_quests[
                i - 1]["Name"] if i > 0 else "--index_zero"
            if quest_name not in stock_names:
                diffs["add"].append(quest)
            elif quest != stock_quests[stock_names.index(quest_name)]:
                diffs["mod"][quest_name] = quest

        return diffs
Ejemplo n.º 11
0
 def get_sizes_in_sarc(
     sarc: oead.Sarc, contents: Contents, guess: bool, dlc: bool
 ) -> Dict[str, int]:
     prefix = "" if not dlc else "Aoc/0010/"
     vals = {}
     if isinstance(contents, list):
         for file in contents:
             if file[file.rindex(".") :] in EXCLUDE_EXTS:
                 continue
             canon = prefix + file.replace(".s", ".")
             vals[canon] = calculate_size(canon, sarc.get_file(file).data, guess)
     elif isinstance(contents, dict):
         for subpath, subcontents in contents.items():
             ext = subpath[subpath.rindex(".") :]
             if ext in EXCLUDE_EXTS:
                 continue
             data = util.unyaz_if_needed(sarc.get_file(subpath).data)
             canon = prefix + subpath.replace(".s", ".")
             vals[canon] = calculate_size(canon, data, guess)
             if ext not in SARC_EXCLUDES:
                 try:
                     subsarc = oead.Sarc(data)
                 except (ValueError, RuntimeError, oead.InvalidDataError):
                     continue
                 vals.update(get_sizes_in_sarc(subsarc, subcontents, guess, dlc))
     return vals
Ejemplo n.º 12
0
Archivo: util.py Proyecto: Endrr/BCML
def get_nested_file_bytes(file: str, unyaz: bool = True) -> bytes:
    nests = file.split("//")
    sarcs = []
    sarcs.append(oead.Sarc(unyaz_if_needed(Path(nests[0]).read_bytes())))
    i = 1
    while i < len(nests) - 1:
        sarc_bytes = unyaz_if_needed(sarcs[i - 1].get_file(nests[i]).data)
        sarcs.append(oead.Sarc(sarc_bytes))
        i += 1
    file_bytes = sarcs[-1].get_file(nests[-1]).data
    if file_bytes[0:4] == b"Yaz0" and unyaz:
        file_bytes = decompress(file_bytes)
    else:
        file_bytes = bytes(file_bytes)
    del sarcs
    return file_bytes
Ejemplo n.º 13
0
Archivo: util.py Proyecto: Endrr/BCML
def inject_file_into_sarc(file: str,
                          data: bytes,
                          sarc: str,
                          create_sarc: bool = False):
    path = get_master_modpack_dir() / get_content_path() / sarc
    if path.exists() or create_sarc:
        if not path.exists():
            path.parent.mkdir(parents=True, exist_ok=True)
            shutil.copy(get_game_file(sarc), path)
        sarc_data = path.read_bytes()
        yaz = sarc_data[0:4] == b"Yaz0"
        if yaz:
            sarc_data = decompress(sarc_data)
        old_sarc = oead.Sarc(sarc_data)
        del sarc_data
        new_sarc = oead.SarcWriter.from_sarc(old_sarc)
        del old_sarc
        new_sarc.files[file] = data if isinstance(data, bytes) else bytes(data)
        new_bytes = new_sarc.write()[1]
        del new_sarc
        path.write_bytes(new_bytes if not yaz else compress(new_bytes))
        del new_bytes
    else:
        raise FileNotFoundError(
            f"{sarc} is not present in the master BCML mod")
Ejemplo n.º 14
0
def unbuild_sarc() -> None:
    set_start_method("spawn", True)
    parser = argparse.ArgumentParser(
        description="Unbuild a single SARC file completely")
    parser.add_argument("sarc", help="SARC archive to unbuild")
    parser.add_argument(
        "--output",
        "-O",
        help="Output folder for unbuilt SARC,\
        defaults to file name w/o extension",
    )
    args = parser.parse_args()

    try:
        file: Path = Path(args.sarc)
        data = file.read_bytes()
        if data[0:4] == b"Yaz0":
            data = oead.yaz0.decompress(data)
        unsarc(
            oead.Sarc(data),
            Path(args.output) if args.output else file.with_suffix(""),
            True,
        )
    except (FileNotFoundError, oead.InvalidDataError, OSError) as err:
        print(err)
        return
Ejemplo n.º 15
0
def get_last_two_savedata_files(bootup_path) -> list:
    bootup_sarc = oead.Sarc(bootup_path.read_bytes())
    savedata_sarc = oead.Sarc(
        oead.yaz0.decompress(
            bootup_sarc.get_file("GameData/savedataformat.ssarc").data))
    savedata_writer = oead.SarcWriter.from_sarc(savedata_sarc)
    idx = 0
    files = []
    while True:
        try:
            savedata_writer.files[f"/saveformat_{idx+2}.bgsvdata"]
            idx += 1
        except KeyError:
            files.append(savedata_writer.files[f"/saveformat_{idx}.bgsvdata"])
            files.append(
                savedata_writer.files[f"/saveformat_{idx+1}.bgsvdata"])
            return files
Ejemplo n.º 16
0
def read_sarc(src: Path) -> oead.Sarc:
    data = read(src=src)
    data = oead.yaz0.decompress(data) if data[:4] == b"Yaz0" else data

    if data[:4] != b"SARC":
        raise SystemExit("Invalid file")

    return oead.Sarc(data)
Ejemplo n.º 17
0
 def generate_diff(self, mod_dir: Path, modded_files: List[Union[Path,
                                                                 str]]):
     if (f"{util.get_content_path()}/Pack/Bootup.pack//GameData/gamedata.ssarc"
             in modded_files):
         print("Logging changes to game data flags...")
         bootup_sarc = oead.Sarc(
             util.unyaz_if_needed((mod_dir / util.get_content_path() /
                                   "Pack" / "Bootup.pack").read_bytes()))
         data_sarc = oead.Sarc(
             util.decompress(
                 bootup_sarc.get_file("GameData/gamedata.ssarc").data))
         diff = get_modded_gamedata_entries(data_sarc, pool=self._pool)
         del bootup_sarc
         del data_sarc
         return diff
     else:
         return {}
Ejemplo n.º 18
0
def _unbuild_sarc(
    s: oead.Sarc, output: Path, skip_actorpack: bool = False, skip_texts: bool = False
):
    SKIP_SARCS = {
        "tera_resource.Cafe_Cafe_GX2.release.ssarc",
        "tera_resource.Nin_NX_NVN.release.ssarc",
    }

    output.mkdir(parents=True, exist_ok=True)
    if any(f.name.startswith("/") for f in s.get_files()):
        (output / ".slash").write_bytes(b"")

    names = set()
    for sarc_file in s.get_files():
        sf = sarc_file.name
        osf = output / sf
        names.add(sf.replace(".s", "."))
        if sf.startswith("/"):
            osf = output / sf[1:]
        osf.parent.mkdir(parents=True, exist_ok=True)
        ext = osf.suffix
        if ext in SARC_EXTS:
            if osf.name in SKIP_SARCS or (osf.name.startswith("Msg_") and skip_texts):
                osf.write_bytes(sarc_file.data)
                continue
            try:
                ss = oead.Sarc(_if_unyaz(sarc_file.data))
                if (
                    "bactorpack" in ext
                    and output.stem == "TitleBG"
                    and not skip_actorpack
                ):
                    names.update(_unbuild_actorpack(ss, output.parent.parent))
                else:
                    names.update(_unbuild_sarc(ss, osf, skip_texts=skip_texts))
                del ss
            except ValueError:
                osf.write_bytes(b"")
        elif ext in AAMP_EXTS:
            if osf.with_suffix(f"{osf.suffix}.yml").exists():
                continue
            osf.with_suffix(f"{osf.suffix}.yml").write_bytes(
                _aamp_to_yml(sarc_file.data)
            )
        elif ext in BYML_EXTS:
            osf.with_suffix(f"{osf.suffix}.yml").write_bytes(
                _byml_to_yml(sarc_file.data)
            )
        else:
            osf.write_bytes(sarc_file.data)

    if "Msg_" in output.name:
        pymsyt.export(output, output)
        rmtree(output)
        output.with_suffix("").rename(output)
    if output.suffix in {".ssarc", ".sarc"}:
        (output / ".align").write_text(str(s.guess_min_alignment()))
    return names
Ejemplo n.º 19
0
def _convert_sarc_file(pack: Path, to_wiiu: bool) -> list:
    data = pack.read_bytes()
    if not data:
        return []
    sarc = oead.Sarc(util.unyaz_if_needed(data))
    new_bytes, error = _convert_sarc(sarc, to_wiiu)
    pack.write_bytes(
        util.compress(new_bytes) if pack.suffix.startswith(".s")
        and pack.suffix != ".sarc" else new_bytes)
    return error
Ejemplo n.º 20
0
    def write(self, root_str: str, be: bool) -> None:
        if self._texts:
            for entry, text in self._texts.items():
                entry_name = f"{self._actor_name}_{entry}"
                self._misc_texts[entry_name] = {
                    "contents": [{
                        "text": text
                    }]
                }  # type:ignore[index]

            settings = util.BatSettings()
            msyt = {
                "group_count": self._group_count,
                "atr1_unknown": self._atr_unknown,
                "entries": {},
            }
            for entry, data in self._misc_texts.items():
                msyt["entries"][entry] = data  # type:ignore[index]
            platform = "wiiu" if be else "switch"
            temp = settings.get_data_dir() / "temp.msbt"
            pymsyt.write_msbt(msyt, temp, platform=platform)
            msbt = temp.read_bytes()
            temp.unlink()
            lang = settings.get_setting("lang")
            text_pack = Path(f"{root_str}/Pack/Bootup_{lang}.pack")
            text_pack_load = text_pack
            if not text_pack.exists():
                text_pack.parent.mkdir(parents=True, exist_ok=True)
                text_pack.touch()
                text_pack_load = Path(
                    util.find_file(Path(f"Pack/Bootup_{lang}.pack")))
            text_sarc = oead.Sarc(text_pack_load.read_bytes())
            text_sarc_writer = oead.SarcWriter.from_sarc(text_sarc)
            message = f"Message/Msg_{lang}.product.ssarc"
            message_sarc = oead.Sarc(
                oead.yaz0.decompress(text_sarc.get_file(message).data))
            message_sarc_writer = oead.SarcWriter.from_sarc(message_sarc)
            msbt_name = f"ActorType/{self._profile}.msbt"
            message_sarc_writer.files[msbt_name] = msbt
            message_bytes = message_sarc_writer.write()[1]
            text_sarc_writer.files[message] = oead.yaz0.compress(message_bytes)
            text_pack.write_bytes(text_sarc_writer.write()[1])
Ejemplo n.º 21
0
 def __init__(self, pack: Path, profile: str):
     self._texts = {}
     self._misc_texts = {}
     self._actor_name = pack.stem
     self._profile = profile
     root_dir = pack.parent
     while True:
         if (root_dir / "Actor").exists() or (not root_dir.stem == "Actor"
                                              and
                                              (root_dir / "Pack").exists()):
             break
         root_dir = root_dir.parent
     settings = util.BatSettings()
     lang = settings.get_setting("lang")
     text_pack = root_dir / f"Pack/Bootup_{lang}.pack"
     if not text_pack.exists():
         root_dir = Path(settings.get_setting("update_dir"))
         text_pack = root_dir / f"Pack/Bootup_{lang}.pack"
     text_sarc = oead.Sarc(text_pack.read_bytes())
     message = f"Message/Msg_{lang}.product.ssarc"
     message_sarc = oead.Sarc(
         oead.yaz0.decompress(text_sarc.get_file(message).data))
     msbt = message_sarc.get_file(f"ActorType/{self._profile}.msbt")
     if not msbt:
         return
     temp = settings.get_data_dir() / "temp.msbt"
     with temp.open("wb") as t_file:
         t_file.write(msbt.data)
     msyt = pymsyt.parse_msbt(temp)
     del text_sarc
     del message_sarc
     del msbt
     temp.unlink()
     self._group_count = msyt["group_count"]
     self._atr_unknown = msyt["atr1_unknown"]
     for entry in msyt["entries"]:
         if self._actor_name in entry:
             entry_name = entry.replace(f"{self._actor_name}_", "")
             for control_type in msyt["entries"][entry]["contents"]:
                 if "text" in control_type:
                     self._texts[entry_name] = control_type["text"]
         self._misc_texts[entry] = msyt["entries"][entry]
Ejemplo n.º 22
0
def _convert_actorpack(actor_pack: Path, to_wiiu: bool) -> Union[None, str]:
    error = None
    sarc = oead.Sarc(util.unyaz_if_needed(actor_pack.read_bytes()))
    new_sarc = oead.SarcWriter.from_sarc(sarc)
    new_sarc.set_endianness(oead.Endianness.Big if to_wiiu else oead.Endianness.Little)
    for file in sarc.get_files():
        if "Physics/" in file.name and "Actor/" not in file.name:
            ext = file.name[file.name.rindex(".") :]
            if ext in NO_CONVERT_EXTS:
                if not util.is_file_modded(
                    util.get_canon_name(file.name, allow_no_source=True),
                    file.data,
                    count_new=True,
                ):
                    actor_name = file.name[
                        file.name.rindex("/") : file.name.rindex(".")
                    ]
                    try:
                        pack_path = util.get_game_file(
                            f"Actor/Pack/{actor_name}.sbactorpack"
                        )
                        stock_data = util.get_nested_file_bytes(
                            f"{str(pack_path)}//{file.name}"
                        )
                        if stock_data:
                            new_sarc.files[file.name] = stock_data
                        else:
                            raise FileNotFoundError(file.name)
                    except (FileNotFoundError, AttributeError):
                        error = (
                            "This mod contains a Havok file not supported by the "
                            f"converter: {file.name}"
                        )
                else:
                    error = (
                        "This mod contains a Havok file not supported by the"
                        f" converter: {file.name}"
                    )
            else:
                if file.data[0:4] == b"AAMP":
                    continue
                try:
                    hk = Havok.from_bytes(bytes(file.data))
                except:  # pylint: disable=bare-except
                    return f"Could not parse Havok file {file.name}"
                if to_wiiu:
                    hk.to_wiiu()
                else:
                    hk.to_switch()
                hk.serialize()
                new_sarc.files[file.name] = hk.to_bytes()
    actor_pack.write_bytes(util.compress(new_sarc.write()[1]))
    return error
Ejemplo n.º 23
0
def inject_bytes_into_sarc(sarc: Path, name: str, data: bytes) -> None:
    sarc_data = sarc.read_bytes()
    yaz = sarc_data[0:4] == b"Yaz0"
    if yaz:
        sarc_data = oead.yaz0.decompress(sarc_data)
    sarc_writer = oead.SarcWriter.from_sarc(oead.Sarc(sarc_data))
    del sarc_data
    sarc_writer.files[name] = data
    new_bytes = sarc_writer.write()[1]
    del sarc_writer
    sarc.write_bytes(new_bytes if not yaz else oead.yaz0.compress(new_bytes))
    del new_bytes
Ejemplo n.º 24
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.º 25
0
def diff_language(bootup: Path, pool: multiprocessing.Pool = None) -> {}:
    diff = {}
    language = bootup.name[7:-5]
    bootup_sarc = oead.Sarc(bootup.read_bytes())
    msg_sarc = oead.Sarc(
        util.decompress(
            bootup_sarc.get_file(
                f"Message/Msg_{language}.product.ssarc").data))

    with TemporaryDirectory() as tmp:
        tmp_dir = Path(tmp)
        mod_out = tmp_dir / "mod"
        print("Extracting mod texts...")
        for file in msg_sarc.get_files():
            out = mod_out / file.name
            out.parent.mkdir(parents=True, exist_ok=True)
            out.write_bytes(file.data)
        del msg_sarc

        print("Converting texts to MSYT...")
        msbt_to_msyt(mod_out, pool=pool)
        hashes = get_text_hashes(language)
        ref_lang = "XXen" if language.endswith("en") else language
        print("Extracting reference texts...")
        extract_refs(ref_lang, tmp_dir)
        ref_dir = tmp_dir / "refs" / ref_lang

        this_pool = pool or multiprocessing.Pool(maxtasksperchild=500)
        print("Identifying modified text files...")
        results = this_pool.map(
            partial(diff_msyt, ref_dir=ref_dir, hashes=hashes,
                    mod_out=mod_out),
            mod_out.rglob("**/*.msyt"),
        )
        if not pool:
            this_pool.close()
            this_pool.join()
        for result in results:
            diff.update(result)
    return diff
Ejemplo n.º 26
0
def find_modded_sarc_files(mod_sarc: Union[Path, oead.Sarc],
                           tmp_dir: Path,
                           name: str = "",
                           aoc: bool = False) -> List[str]:
    if isinstance(mod_sarc, Path):
        if any(mod_sarc.name.startswith(exclude) for exclude in ["Bootup_"]):
            return []
        name = str(mod_sarc.relative_to(tmp_dir))
        aoc = util.get_dlc_path() in mod_sarc.parts or "Aoc" in mod_sarc.parts
        try:
            mod_sarc = oead.Sarc(util.unyaz_if_needed(mod_sarc.read_bytes()))
        except (RuntimeError, ValueError, oead.InvalidDataError):
            return []
    modded_files = []
    for file, contents in [(f.name, bytes(f.data))
                           for f in mod_sarc.get_files()]:
        canon = file.replace(".s", ".")
        if aoc:
            canon = "Aoc/0010/" + canon
        contents = util.unyaz_if_needed(contents)
        nest_path = str(name).replace("\\", "/") + "//" + file
        if util.is_file_modded(canon, contents, True):
            modded_files.append(nest_path)
            util.vprint(
                f'Found modded file {canon} in {str(name).replace("//", "/")}')
            if util.is_file_sarc(canon) and ".ssarc" not in file:
                try:
                    nest_sarc = oead.Sarc(contents)
                except ValueError:
                    continue
                sub_mod_files = find_modded_sarc_files(nest_sarc,
                                                       name=nest_path,
                                                       tmp_dir=tmp_dir,
                                                       aoc=aoc)
                modded_files.extend(sub_mod_files)
        else:
            util.vprint(
                f'Ignored unmodded file {canon} in {str(name).replace("//", "/")}'
            )
    return modded_files
Ejemplo n.º 27
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.º 28
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
Ejemplo n.º 29
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.º 30
0
def inject_files_into_bootup(bootup_path: Path, files: list, datas: list):
    sarc_data = bootup_path.read_bytes()
    yaz = sarc_data[0:4] == b"Yaz0"
    if yaz:
        sarc_data = oead.yaz0.decompress(sarc_data)
    old_sarc = oead.Sarc(sarc_data)
    del sarc_data
    new_sarc = oead.SarcWriter.from_sarc(old_sarc)
    del old_sarc
    for idx in range(len(files)):
        new_sarc.files[files[idx]] = (datas[idx] if isinstance(
            datas[idx], bytes) else bytes(datas[idx]))
    new_bytes = new_sarc.write()[1]
    del new_sarc
    bootup_path.write_bytes(
        new_bytes if not yaz else oead.yaz0.compress(new_bytes))
    del new_bytes