Ejemplo n.º 1
0
def get_aamp_diff(file: Union[Path, str], tmp_dir: Path):
    """
    Diffs a modded AAMP file from the stock game version

    :param file: The modded AAMP file to diff
    :type file: class:`typing.Union[class:pathlib.Path, str]`
    :param tmp_dir: The temp directory containing the mod
    :type tmp_dir: class:`pathlib.Path`
    :return: Returns a string representation of the AAMP file diff
    """
    if isinstance(file, str):
        nests = file.split('//')
        mod_bytes = util.get_nested_file_bytes(file)
        ref_path = str(util.get_game_file(
            Path(nests[0]).relative_to(tmp_dir))) + '//' + '//'.join(nests[1:])
        ref_bytes = util.get_nested_file_bytes(ref_path)
    else:
        with file.open('rb') as m_file:
            mod_bytes = m_file.read()
        mod_bytes = util.unyaz_if_needed(mod_bytes)
        with util.get_game_file(file.relative_to(tmp_dir)).open('rb') as r_file:
            ref_bytes = r_file.read()
        ref_bytes = util.unyaz_if_needed(ref_bytes)

    ref_aamp = aamp.Reader(ref_bytes).parse()
    mod_aamp = aamp.Reader(mod_bytes).parse()

    return _aamp_diff(ref_aamp, mod_aamp)
Ejemplo n.º 2
0
def aamp_to_yml(input_data: bytes) -> bytes:
    dumper = yaml.CDumper
    yu.register_representers(dumper)
    reader = aamp.Reader(input_data, track_strings=True)
    root = reader.parse()
    dumper.__aamp_reader = reader
    return yaml.dump(root, Dumper=dumper, allow_unicode=True, encoding='utf-8', default_flow_style=None)
Ejemplo n.º 3
0
def dump_to_csv(content_dir: Path, predicate: ActorPredicate,
                props: typing.Iterable[Prop], f: typing.TextIO) -> None:
    actorpack_dir = (content_dir / 'Actor/Pack')

    writer = csv.writer(f)
    header = ['Actor', 'Name']
    header.extend(prop.column_name for prop in props)
    writer.writerow(header)

    for actorpack in actorpack_dir.glob('*'):
        actor_name = actorpack.stem
        if not predicate(actor_name):
            continue
        try:
            bgparamlist_p = next(
                (actorpack / 'Actor/GeneralParamList').glob('*.bgparamlist'))
            pio = aamp.Reader(bgparamlist_p.open('rb').read()).parse()
            proot = pio.list('param_root')

            cols = [actor_name, _names.get(actor_name, '?')]
            for prop in props:
                value = proot.object(prop.obj).param(prop.param)
                cols.append(prop.format_fn(value))
            writer.writerow(cols)
        except StopIteration:
            continue
Ejemplo n.º 4
0
def threaded_merge(item, verbose: bool) -> (str, dict):
    """Deep merges an individual file, suitable for multiprocessing"""
    file, stuff = item
    failures = {}

    base_file = util.get_game_file(file, file.startswith('aoc'))
    if (util.get_master_modpack_dir() / file).exists():
        base_file = util.get_master_modpack_dir() / file
    file_ext = os.path.splitext(file)[1]
    if file_ext in util.SARC_EXTS and (util.get_master_modpack_dir() /
                                       file).exists():
        base_file = (util.get_master_modpack_dir() / file)
    file_bytes = base_file.read_bytes()
    yazd = file_bytes[0:4] == b'Yaz0'
    file_bytes = file_bytes if not yazd else util.decompress(file_bytes)
    magic = file_bytes[0:4]

    if magic == b'SARC':
        new_sarc, sub_failures = nested_patch(sarc.SARC(file_bytes), stuff)
        del file_bytes
        new_bytes = new_sarc.get_bytes()
        for failure, contents in sub_failures.items():
            print(f'Some patches to {failure} failed to apply.')
            failures[failure] = contents
    else:
        try:
            if magic == b'AAMP':
                aamp_contents = aamp.Reader(file_bytes).parse()
                for change in stuff:
                    aamp_contents = _aamp_merge(aamp_contents, change)
                aamp_bytes = aamp.Writer(aamp_contents).get_bytes()
                del aamp_contents
                new_bytes = aamp_bytes if not yazd else util.compress(
                    aamp_bytes)
            else:
                raise ValueError(f'{file} is not a SARC or AAMP file.')
        except ValueError:
            new_bytes = file_bytes
            del file_bytes
            print(f'Deep merging file {file} failed. No changes were made.')

    new_bytes = new_bytes if not yazd else util.compress(new_bytes)
    output_file = (util.get_master_modpack_dir() / file)
    if base_file == output_file:
        output_file.unlink()
    output_file.parent.mkdir(parents=True, exist_ok=True)
    output_file.write_bytes(new_bytes)
    del new_bytes
    if magic == b'SARC' and verbose:
        print(f'Finished patching files inside {file}')
    elif verbose:
        print(f'Finished patching {file}')
    return util.get_canon_name(file), failures
Ejemplo n.º 5
0
def dump_aamp(data: bytes):
    class Dumper(yaml.CDumper):
        pass

    reader = aamp.Reader(data, track_strings=True)
    aamp_root = reader.parse()
    aamp.yaml_util.register_representers(Dumper)
    Dumper.__aamp_reader = reader
    return yaml.dump(aamp_root,
                     Dumper=Dumper,
                     allow_unicode=True,
                     encoding="utf-8")
Ejemplo n.º 6
0
def nested_patch(pack: sarc.SARC, nest: dict) -> (sarc.SARCWriter, dict):
    """
    Recursively patches deep merge files in a SARC

    :param pack: The SARC in which to recursively patch.
    :type pack: class:`sarc.SARC`
    :param nest: A dict of nested patches to apply.
    :type nest: dict
    :return: Returns a new SARC with patches applied and a dict of any failed patches.
    :rtype: (class:`sarc.SARCWriter`, dict, dict)
    """
    new_sarc = sarc.make_writer_from_sarc(pack)
    failures = {}

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

        if isinstance(stuff, dict):
            sub_sarc = sarc.SARC(file_bytes)
            new_sarc.delete_file(file)
            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 = new_sub_sarc.get_bytes()
            new_sarc.add_file(
                file, new_bytes if not yazd else util.compress(new_bytes))

        elif isinstance(stuff, list):
            try:
                if file_bytes[0:4] == b'AAMP':
                    aamp_contents = aamp.Reader(file_bytes).parse()
                    for change in stuff:
                        aamp_contents = _aamp_merge(aamp_contents, change)
                    aamp_bytes = aamp.Writer(aamp_contents).get_bytes()
                    del aamp_contents
                    new_bytes = aamp_bytes if not yazd else util.compress(
                        aamp_bytes)
                    cache_merged_aamp(file, new_bytes)
                else:
                    raise ValueError(
                        'Wait, what the heck, this isn\'t an AAMP file?!')
            except ValueError:
                new_bytes = pack.get_file_data(file).tobytes()
                print(f'Deep merging {file} failed. No changed were made.')

            new_sarc.delete_file(file)
            new_sarc.add_file(file, new_bytes)
    return new_sarc, failures
Ejemplo n.º 7
0
    def load_actor_aiprog(self, actor_name: str) -> bool:
        if not _rom_path:
            return False

        rel_roots = (
            '',
            'Pack/Bootup.pack/',
            'Pack/TitleBG.pack/',
            'Pack/RemainsWind.pack/',
            'Pack/RemainsElectric.pack/',
            'Pack/RemainsWater.pack/',
            'Pack/RemainsFire.pack/',
        )

        for rel_root in rel_roots:
            root = _rom_path / rel_root
            aiprog_dir = root / 'Actor' / 'Pack' / f'{actor_name}.sbactorpack' / 'Actor' / 'AIProgram'
            for path in _list_aiprog_files(aiprog_dir):
                with open(path, 'rb') as aiprog:
                    pio = aamp.Reader(aiprog.read()).parse()
                if self._do_load_actor_aiprog(pio):
                    return True

        return False
def test_aamp_from_bin_aamp(benchmark, file):
    benchmark.group = "from_bin: " + file
    benchmark(lambda d: aamp.Reader(d).parse(), data[file])