Пример #1
0
def initialize_segments(options, config_path, config_segments):
    seen_segment_names = set()
    ret = []

    for i, segment in enumerate(config_segments[:-1]):
        seg_type = parse_segment_type(segment)

        platform = get_platform(options)

        segment_class = get_base_segment_class(seg_type, platform)
        if segment_class == None:
            # Look in extensions
            segment_class = get_extension_class(options, config_path, seg_type,
                                                platform)

        if segment_class == None:
            log.write(
                f"fatal error: could not load segment type '{seg_type}'\n(hint: confirm your extension directory is configured correctly)",
                status="error")
            return 2

        segment = segment_class(segment, config_segments[i + 1], options)

        if segment_class.require_unique_name:
            if segment.name in seen_segment_names:
                segment.error("segment name is not unique")
            seen_segment_names.add(segment.name)

        ret.append(segment)

    return ret
Пример #2
0
 def round(self):
     # Each round, every player should get its turn
     for player in self.players:
         if not player.is_bankrupt():
             self.turn(player)
             if player.is_bankrupt():
                 player.bankrupt()
                 self.info_dic["bankrupt_turn"][player.num] = self.cur_round
                 if util.verbose:
                     log.write(
                         "player {} has bankrupted, return all properties to the bank.\n"
                         .format(player.num))
                 self.bankrupt_count += 1
         if len(self.players) - self.bankrupt_count <= 1:
             for i in self.players:
                 if not i.is_bankrupt():
                     if util.verbose:
                         log.write("player {} wins\n".format(i.num))
                     self.info_dic["bankrupt_turn"][i.num] = -1
                     self.info_dic["winner"] = i.num
                     self.info_dic["end"] = self.cur_round
             return True
     if util.verbose:
         log.write("\n")
     for player in self.players:
         if util.verbose:
             log.write("player {} has {} cash.\n".format(
                 player.num, player.cash))
     if util.verbose:
         log.write("\n")
     return False
Пример #3
0
    def turn(self, player):
        # Get number of eyes on dice
        dice1, dice2 = diceThrow()
        if util.verbose:
            log.write("player {0} rolls two dices, {1} and {2}.\n".format(
                player.num, dice1, dice2))
        # Move the player to new position, goingToJail True is 3 doubles thrown
        player.move(self.board, dice1, dice2)

        # Get tile type
        tileType = self.board.getTileType(player.position)

        # Set to go to jail if on 'Go To Jail' tile
        if tileType == "gotojail":
            player.go_to_jail()

        # Do chance card if player has landed on a chance tile
        elif player.position in Board.TILES_CHANCE:
            player.doChanceCard(self.chancePile.pullCard(), self.board)

        # Do commmunity card if player has landed on a community chest tile
        elif player.position in Board.TILES_COMMUNITY:
            player.doCommunityCard(self.communityPile.pullCard(), self.board)

        elif Board.TILE_BUILDING[player.position] is not None:
            building = Board.TILE_BUILDING[player.position]
            if building.owner is None or building.owner.num == player.num:
                player.buy_building(Board.TILE_BUILDING[player.position])
            elif building.owner is not None and building.owner != player.num:
                fined = int(building.cur_price * 0.2)
                player.fine_money(fined, other=building.owner)

        # equivalent to trading
        for building_to_sell in player.building_to_sell_list:
            for other_player in self.players:
                if not other_player.is_bankrupt():
                    boundary = other_player.choose_boundary(
                        other_player.strategy, other_player.strategy_para)
                    if other_player != player and other_player.cash - building_to_sell.cur_price >= boundary:
                        other_player.cash -= building_to_sell.cur_price
                        building_to_sell.set_owner(other_player)
                        other_player.building.append(building_to_sell)
                        player.cash += int(building_to_sell.cur_price * 0.1)
                        if util.verbose:
                            log.write(
                                "player {0} successfully sell land {1} to player {2}, get the remaining {3}, player {0} currently has {4}, player {2} has {5}.\n"
                                .format(player.num, building_to_sell.name,
                                        other_player.num,
                                        building_to_sell.cur_price * 0.1,
                                        player.cash, other_player.cash))
                        break
        player.building_to_sell_list = []

        # Log the fact that a player has landed on a tile, after all movements
        self.board.hit(player.position)

        # Go again if not on jail and has thrown double
        if tileType != "jail" and dice1 == dice2:
            self.turn(player)
Пример #4
0
    def doCommunityCard(self, card, board):
        # Go to given position if card is of the advance kind
        # Check the type of the chance card
        # card for moving
        if util.verbose:
            log.write("player {0} get a community card {1}.\n".format(
                self.num, card))
        if card.kind == "advance":
            self.position = card.value
            if self.position == board.TILES_JAIL[0]:
                self.at_jail = True
            if util.verbose:
                log.write("player {0} moves to {1}.\n".format(
                    self.num, board.TILE_NAME[self.position]))
        # card for get money
        elif card.kind == "cash":
            self.cash += card.value
            if util.verbose:
                log.write(
                    "player {0} gets {1} cash, currently has {2} cash.\n".
                    format(self.num, card.value, self.cash))

        # card for tax
        elif card.kind == "tax":
            fined = card.value[0] * self.house + card.value[1] * self.hotel
            self.fine_money(fined)
            if util.verbose:
                log.write(
                    "player {0} pay {1} tax, currently has {2} cash.\n".format(
                        self.num, fined, self.cash))
Пример #5
0
def get_extension_class(options, config_path, seg_type):
    ext_dir = get_extension_dir(options, config_path)
    if ext_dir == None:
        return None

    try:
        ext_spec = importlib.util.spec_from_file_location(
            f"segtypes.{seg_type}", os.path.join(ext_dir, f"{seg_type}.py"))
        ext_mod = importlib.util.module_from_spec(ext_spec)
        ext_spec.loader.exec_module(ext_mod)
    except Exception as err:
        log.write(err, status="error")
        return None

    return getattr(ext_mod, "PS2Seg" + seg_type[0].upper() + seg_type[1:])
Пример #6
0
    def run(self):
        # Play the game for a given amount of rounds
        end = False
        for i in range(0, self.rounds):
            # dev_print("round", i)
            self.cur_round = i
            if util.verbose:
                log.write("round {0}:\n".format(i))
            end = self.round()
            if util.verbose:
                log.write("\n")
            if end:
                break
            # for player in self.players:
            # 	dev_print("player {0}: cash {1}, property {2}".format(player.num, player.cash, player.total_property()))
        if not end:
            self.info_dic["end"] = -1

        return self.info_dic
Пример #7
0
    def create_c_file(self, funcs_text, asm_out_dir, c_path):
        c_lines = self.get_c_preamble()

        for func in funcs_text:
            func_name = self.parent.get_symbol(func,
                                               type="func",
                                               local_only=True).name

            # Terrible hack to "auto-decompile" empty functions
            # TODO move disassembly into funcs_text or somewhere we can access it from here
            if (options.get_auto_decompile_empty_functions()
                    and len(funcs_text[func][0]) == 3
                    and funcs_text[func][0][1][-3:] in ["$ra", "$31"]
                    and funcs_text[func][0][2][-3:] == "nop"):
                c_lines.append("void " + func_name + "(void) {")
                c_lines.append("}")
            else:
                if options.get_compiler() in [GCC, SN64]:
                    if options.get_use_legacy_include_asm():
                        rel_asm_out_dir = asm_out_dir.relative_to(
                            options.get_nonmatchings_path())
                        c_lines.append(
                            f'INCLUDE_ASM(s32, "{rel_asm_out_dir / self.name}", {func_name});'
                        )
                    else:
                        c_lines.append(
                            f'INCLUDE_ASM("{asm_out_dir / self.name}", {func_name});'
                        )
                else:
                    asm_outpath = Path(
                        os.path.join(asm_out_dir, self.name, func_name + ".s"))
                    rel_asm_outpath = os.path.relpath(asm_outpath,
                                                      options.get_base_path())
                    c_lines.append(f'#pragma GLOBAL_ASM("{rel_asm_outpath}")')
            c_lines.append("")

        Path(c_path).parent.mkdir(parents=True, exist_ok=True)
        with open(c_path, "w") as f:
            f.write("\n".join(c_lines))
        log.write(f"Wrote {self.name} to {c_path}")
Пример #8
0
    def fine_money(self, fined, other=None):
        boundary = self.choose_boundary(self.strategy, self.strategy_para)
        if self.cash - fined < boundary:
            building_to_sell = self.find_min_house_to_sell(fined)
            # dev_print(self.building)
            if building_to_sell is not None:
                building_to_sell.sell()
                # dev_print("here", building_to_sell)
                self.building.remove(building_to_sell)
                sell_price = int(building_to_sell.cur_price * 0.9)
                self.cash += sell_price
                self.building_to_sell_list.append(building_to_sell)
                if util.verbose:
                    log.write(
                        "player {0} sells the property on {1} to the bank for {2} cash, currently has {3}.\n"
                        .format(self.num, building_to_sell.name, sell_price,
                                self.cash))

        self.cash -= fined
        if other is not None:
            other.cash += fined
            if util.verbose:
                log.write(
                    "player {0} lands on player {1}'s property, player {0} gives {2} to player {1}, now player {0} has {3}, player {1} has {4}.\n"
                    .format(self.num, other.num, fined, self.cash, other.cash))
        else:
            if util.verbose:
                log.write(
                    "player {0} is fined {1} by the country, currently has {2}.\n"
                    .format(self.num, fined, self.cash))
Пример #9
0
    def get_extension_segment_class(seg_type):
        platform = options.get_platform()

        ext_path = options.get_extensions_path()
        if not ext_path:
            log.error(
                f"could not load presumed extended segment type '{seg_type}' because no extensions path is configured"
            )

        try:
            ext_spec = importlib.util.spec_from_file_location(
                f"{platform}.segtypes.{seg_type}", ext_path / f"{seg_type}.py")
            ext_mod = importlib.util.module_from_spec(ext_spec)
            ext_spec.loader.exec_module(ext_mod)
        except Exception as err:
            log.write(err, status="error")
            log.error(
                f"could not load segment type '{seg_type}'\n(hint: confirm your extension directory is configured correctly)"
            )

        return getattr(
            ext_mod,
            f"{platform.upper()}Seg{seg_type[0].upper()}{seg_type[1:]}")
Пример #10
0
    def create_c_file(self, asm_out_dir, c_path):
        c_lines = self.get_c_preamble()

        for func in self.text_section.symbolList:
            assert isinstance(func, spimdisasm.mips.symbols.SymbolFunction)

            # Terrible hack to "auto-decompile" empty functions
            if (options.get_auto_decompile_empty_functions()
                    and func.instructions[0].isJrRa()
                    and func.instructions[1].isNop()):
                c_lines.append("void " + func.getName() + "(void) {")
                c_lines.append("}")
            else:
                if options.get_compiler() in [GCC, SN64]:
                    if options.get_use_legacy_include_asm():
                        rel_asm_out_dir = asm_out_dir.relative_to(
                            options.get_nonmatchings_path())
                        c_lines.append(
                            f'INCLUDE_ASM(s32, "{rel_asm_out_dir / self.name}", {func.getName()});'
                        )
                    else:
                        c_lines.append(
                            f'INCLUDE_ASM("{asm_out_dir / self.name}", {func.getName()});'
                        )
                else:
                    asm_outpath = Path(
                        os.path.join(asm_out_dir, self.name,
                                     func.getName() + ".s"))
                    rel_asm_outpath = os.path.relpath(asm_outpath,
                                                      options.get_base_path())
                    c_lines.append(f'#pragma GLOBAL_ASM("{rel_asm_outpath}")')
            c_lines.append("")

        Path(c_path).parent.mkdir(parents=True, exist_ok=True)
        with open(c_path, "w") as f:
            f.write("\n".join(c_lines))
        log.write(f"Wrote {self.name} to {c_path}")
Пример #11
0
Файл: c.py Проект: Alto1772/pm64
    def create_c_file(self, funcs_text, asm_out_dir, c_path):
        c_lines = self.get_c_preamble()

        for func in funcs_text:
            func_name = self.parent.get_symbol(func, type="func", local_only=True).name

            # Terrible hack to "auto-decompile" empty functions
            # TODO move disassembly into funcs_text or somewhere we can access it from here
            if len(funcs_text[func][0]) == 3 and funcs_text[func][0][1][-3:] == "$ra" and funcs_text[func][0][2][-3:] == "nop":
                c_lines.append("void " + func_name + "(void) {")
                c_lines.append("}")
            else:
                if options.get_compiler() == "GCC":
                    c_lines.append("INCLUDE_ASM(s32, \"{}\", {});".format(self.name, func_name))
                else:
                    asm_outpath = Path(os.path.join(asm_out_dir, self.dir, self.name, func_name + ".s"))
                    rel_asm_outpath = os.path.relpath(asm_outpath, options.get_base_path())
                    c_lines.append(f"#pragma GLOBAL_ASM(\"{rel_asm_outpath}\")")
            c_lines.append("")

        Path(c_path).parent.mkdir(parents=True, exist_ok=True)
        with open(c_path, "w") as f:
            f.write("\n".join(c_lines))
        log.write(f"Wrote {self.name} to {c_path}")
Пример #12
0
    def buy_building(self, building):
        assert isinstance(building, Building)

        if building.owner is None:  # the player can buy the building
            # based on the player's strategy, decide how much cash could be used to buy buildings
            boundary = self.choose_boundary(self.strategy, self.strategy_para)
            if self.cash - building.base_price >= boundary:
                self.cash -= building.base_price
                self.building.append(building)
                building.set_owner(self)
                self.land += 1
                if util.verbose:
                    log.write(
                        "player {0} buys a land on {1}, costs {2}, currently has {3} cash.\n"
                        .format(self.num, building.name, building.base_price,
                                self.cash))
            else:
                return False
        else:
            if building.owner.num == self.num:  # the player is the owner of the building
                boundary = self.choose_boundary(self.strategy,
                                                self.strategy_para)
                if self.cash - building.base_price >= boundary:
                    if building.level == 0:
                        self.cash -= building.base_price
                        self.land -= 1
                        self.house += 1
                        building.improve()
                        if util.verbose:
                            log.write(
                                'player {0} upgrades the land on {1} to a house, costs {2}, currently has {3} cash.\n'
                                .format(self.num, building.name,
                                        building.base_price, self.cash))
                    elif building.level == 1:
                        self.cash -= building.base_price
                        self.house -= 1
                        self.hotel += 1
                        building.improve()
                        if util.verbose:
                            log.write(
                                'player {0} upgrades the house on {1} to a hotel, costs {2}, currently has {3} cash.\n'
                                .format(self.num, building.name,
                                        building.base_price, self.cash))

                else:
                    return False
            else:
                raise PermissionError(
                    "You have no right to upgrade the property.\n")

        return True
Пример #13
0
def do_statistics(seg_sizes, rom_bytes, seg_split, seg_cached):
    unk_size = seg_sizes.get("unk", 0)
    rest_size = 0
    total_size = len(rom_bytes)

    for typ in seg_sizes:
        if typ != "unk":
            rest_size += seg_sizes[typ]

    known_ratio = rest_size / total_size
    unk_ratio = unk_size / total_size

    log.write(f"Split {fmt_size(rest_size)} ({known_ratio:.2%}) in defined segments")
    for typ in seg_sizes:
        if typ != "unk":
            tmp_size = seg_sizes[typ]
            tmp_ratio = tmp_size / total_size
            log.write(f"{typ:>20}: {fmt_size(tmp_size):>8} ({tmp_ratio:.2%}) {Fore.GREEN}{seg_split[typ]} split{Style.RESET_ALL}, {Style.DIM}{seg_cached[typ]} cached")
    log.write(f"{'unknown':>20}: {fmt_size(unk_size):>8} ({unk_ratio:.2%}) from unknown bin files")
Пример #14
0
def experiment(configs, fold_paths, train_path, test_path, log_dir):
    for index, config in enumerate(configs):
        session_log_dir = os.path.join(log_dir, "session_{}".format(datetime.datetime.now().strftime("%Y%m%d-%H%M%S")))
        print("\nEXPERIMENT: {}/{}".format(index+1, len(configs)))
        print(config)
        config["log_dir"] = session_log_dir
        config["class_weight"] = {
              0: 1,
              1: 1
        }
        config["verbose"] = 2
        train_result, dev_result, test_result = k_fold_experiment(
            config=config,
            fold_paths=fold_paths,
            train_path=train_path,
            test_path=test_path
        )

        log_result(train_result, dev_result, test_result)

        write(session_log_dir, "train", train_result, config["hparams"])
        write(session_log_dir, "dev", dev_result, config["hparams"])
        write(session_log_dir, "test", test_result, config["hparams"])
Пример #15
0
def main(config_path,
         out_dir,
         target_path,
         modes,
         verbose,
         ignore_cache=False):
    # Load config
    with open(config_path) as f:
        config = yaml.safe_load(f.read())

    options = config.get("options")
    options["modes"] = modes
    options["verbose"] = verbose

    if not out_dir:
        out_dir = options.get("out_dir", None)
        if not out_dir:
            print(
                "Error: Output dir not specified as a command line arg or via the config yaml (out_dir)"
            )
            sys.exit(2)
        else:
            out_dir = os.path.join(Path(config_path).parent, out_dir)

    if not target_path:
        target_path = options.get("target_path", None)
        if not target_path:
            print(
                "Error: Target binary path not specified as a command line arg or via the config yaml (target_path)"
            )
            sys.exit(2)
        else:
            target_path = os.path.join(out_dir, target_path)

    with open(target_path, "rb") as f:
        rom_bytes = f.read()

    # Create main output dir
    Path(out_dir).mkdir(parents=True, exist_ok=True)

    symbol_addrs_path = get_symbol_addrs_path(out_dir, options)
    undefined_syms_path = get_undefined_syms_path(out_dir, options)
    all_symbols = gather_symbols(symbol_addrs_path, undefined_syms_path)
    symbol_ranges = [s for s in all_symbols if s.size > 4]
    platform = get_platform(options)

    processed_segments = []
    ld_sections = []

    seg_sizes = {}
    seg_split = {}
    seg_cached = {}

    # Load cache
    cache_path = get_cache_path(out_dir, options)
    try:
        with open(cache_path, "rb") as f:
            cache = pickle.load(f)
    except Exception:
        cache = {}

    # Initialize segments
    all_segments = initialize_segments(options, config_path,
                                       config["segments"])

    for segment in all_segments:
        if platform == "n64" and type(
                segment) == N64SegCode:  # remove special-case sometime
            segment_symbols, other_symbols = get_segment_symbols(
                segment, all_symbols, all_segments)
            segment.seg_symbols = segment_symbols
            segment.ext_symbols = other_symbols
            segment.all_symbols = all_symbols
            segment.symbol_ranges = symbol_ranges

        segment.check()

        typ = segment.type
        if segment.type == "bin" and segment.is_name_default():
            typ = "unk"

        if typ not in seg_sizes:
            seg_sizes[typ] = 0
            seg_split[typ] = 0
            seg_cached[typ] = 0
        seg_sizes[typ] += segment.size

        if len(segment.errors) == 0:
            if segment.should_run():
                # Check cache
                cached = segment.cache()
                if not ignore_cache and cached == cache.get(
                        segment.unique_id()):
                    # Cache hit
                    seg_cached[typ] += 1
                else:
                    # Cache miss; split
                    cache[segment.unique_id()] = cached

                    segment.did_run = True
                    segment.split(rom_bytes, out_dir)

                    if len(segment.errors) == 0:
                        processed_segments.append(segment)

                    seg_split[typ] += 1

        log.dot(status=segment.status())
        ld_sections.append(segment.get_ld_section())

    for segment in processed_segments:
        segment.postsplit(processed_segments)
        log.dot(status=segment.status())

    # Write ldscript
    if "ld" in options["modes"] or "all" in options["modes"]:
        if verbose:
            log.write(f"saving {config['basename']}.ld")
        write_ldscript(config['basename'], out_dir, ld_sections, options)

    undefined_syms_to_write = [
        s for s in all_symbols
        if s.referenced and not s.defined and not s.type == "func"
    ]
    undefined_funcs_to_write = [
        s for s in all_symbols
        if s.referenced and not s.defined and s.type == "func"
    ]

    # Write undefined_funcs_auto.txt
    undefined_funcs_auto_path = get_undefined_funcs_auto_path(out_dir, options)

    to_write = undefined_funcs_to_write
    if len(to_write) > 0:
        with open(undefined_funcs_auto_path, "w", newline="\n") as f:
            for symbol in to_write:
                f.write(f"{symbol.name} = 0x{symbol.vram_start:X};\n")

    # write undefined_syms_auto.txt
    undefined_syms_auto_path = get_undefined_syms_auto_path(out_dir, options)

    to_write = undefined_syms_to_write
    if len(to_write) > 0:
        with open(undefined_syms_auto_path, "w", newline="\n") as f:
            for symbol in to_write:
                f.write(f"{symbol.name} = 0x{symbol.vram_start:X};\n")

    # print warnings during split/postsplit
    for segment in all_segments:
        if len(segment.warnings) > 0:
            log.write(
                f"{Style.DIM}0x{segment.rom_start:06X}{Style.RESET_ALL} {segment.type} {Style.BRIGHT}{segment.name}{Style.RESET_ALL}:"
            )

            for warn in segment.warnings:
                log.write("warning: " + warn, status="warn")

            log.write("")  # empty line

    # Statistics
    unk_size = seg_sizes.get("unk", 0)
    rest_size = 0
    total_size = len(rom_bytes)

    for typ in seg_sizes:
        if typ != "unk":
            rest_size += seg_sizes[typ]

    assert (unk_size + rest_size == total_size)

    known_ratio = rest_size / total_size
    unk_ratio = unk_size / total_size

    log.write(
        f"Split {fmt_size(rest_size)} ({known_ratio:.2%}) in defined segments")
    for typ in seg_sizes:
        if typ != "unk":
            tmp_size = seg_sizes[typ]
            tmp_ratio = tmp_size / total_size
            log.write(
                f"{typ:>20}: {fmt_size(tmp_size):>8} ({tmp_ratio:.2%}) {Fore.GREEN}{seg_split[typ]} split{Style.RESET_ALL}, {Style.DIM}{seg_cached[typ]} cached"
            )
    log.write(
        f"{'unknown':>20}: {fmt_size(unk_size):>8} ({unk_ratio:.2%}) from unknown bin files"
    )

    # Save cache
    if cache != {}:
        if verbose:
            print("Writing cache")
        with open(cache_path, "wb") as f:
            pickle.dump(cache, f)

    return 0  # no error
Пример #16
0
def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False):
    with open(rom_path, "rb") as f:
        rom_bytes = f.read()

    # Create main output dir
    Path(repo_path).mkdir(parents=True, exist_ok=True)

    # Load config
    with open(config_path) as f:
        config = yaml.safe_load(f.read())

    options = config.get("options")
    options["modes"] = modes
    options["verbose"] = verbose

    symbol_addrs_path = get_symbol_addrs_path(repo_path, options)
    undefined_syms_path = get_undefined_syms_path(repo_path, options)
    provided_symbols, c_func_labels_to_add, special_labels, ranges = gather_symbols(
        symbol_addrs_path, undefined_syms_path)
    platform = get_platform(options)

    processed_segments = []
    ld_sections = []

    defined_funcs = {}
    undefined_funcs = set()
    undefined_syms = set()

    seg_sizes = {}
    seg_split = {}
    seg_cached = {}

    # Load cache
    cache_path = get_cache_path(repo_path, options)
    try:
        with open(cache_path, "rb") as f:
            cache = pickle.load(f)
    except Exception:
        cache = {}

    # Initialize segments
    all_segments = initialize_segments(options, config_path,
                                       config["segments"])

    for segment in all_segments:
        if platform == "n64" and type(
                segment) == N64SegCode:  # remove special-case sometime
            segment.all_functions = defined_funcs
            segment.provided_symbols = provided_symbols
            segment.special_labels = special_labels
            segment.c_labels_to_add = c_func_labels_to_add
            segment.symbol_ranges = ranges

        segment.check()

        tp = segment.type
        if segment.type == "bin" and segment.is_name_default():
            tp = "unk"

        if tp not in seg_sizes:
            seg_sizes[tp] = 0
            seg_split[tp] = 0
            seg_cached[tp] = 0
        seg_sizes[tp] += segment.size

        if len(segment.errors) == 0:
            if segment.should_run():
                # Check cache
                cached = segment.cache()
                if not ignore_cache and cached == cache.get(
                        segment.unique_id()):
                    # Cache hit
                    seg_cached[tp] += 1
                else:
                    # Cache miss; split
                    cache[segment.unique_id()] = cached

                    segment.did_run = True
                    segment.split(rom_bytes, repo_path)

                    if len(segment.errors) == 0:
                        processed_segments.append(segment)

                        if platform == "n64" and type(
                                segment) == N64SegCode:  # edge case
                            undefined_funcs |= segment.glabels_to_add
                            defined_funcs = {
                                **defined_funcs,
                                **segment.glabels_added
                            }
                            undefined_syms |= segment.undefined_syms_to_add

                    seg_split[tp] += 1

        log.dot(status=segment.status())
        ld_sections.append(segment.get_ld_section())

    for segment in processed_segments:
        segment.postsplit(processed_segments)
        log.dot(status=segment.status())

    # Write ldscript
    if "ld" in options["modes"] or "all" in options["modes"]:
        if verbose:
            log.write(f"saving {config['basename']}.ld")
        write_ldscript(config['basename'], repo_path, ld_sections, options)

    # Write undefined_funcs_auto.txt
    undefined_funcs_auto_path = get_undefined_funcs_auto_path(
        repo_path, options)
    if verbose:
        log.write(f"saving {undefined_funcs_auto_path}")
    c_predefined_funcs = set(provided_symbols.keys())
    to_write = sorted(undefined_funcs - set(defined_funcs.values()) -
                      c_predefined_funcs)
    if len(to_write) > 0:
        with open(undefined_funcs_auto_path, "w", newline="\n") as f:
            for line in to_write:
                f.write(line + " = 0x" + line.split("_")[1][:8].upper() +
                        ";\n")

    # write undefined_syms_auto.txt
    undefined_syms_auto_path = get_undefined_syms_auto_path(repo_path, options)
    if verbose:
        log.write(f"saving {undefined_syms_auto_path}")
    to_write = sorted(undefined_syms, key=lambda x: x[0])
    if len(to_write) > 0:
        with open(undefined_syms_auto_path, "w", newline="\n") as f:
            for sym in to_write:
                f.write(f"{sym[0]} = 0x{sym[1]:X};\n")

    # print warnings and errors during split/postsplit
    had_error = False
    for segment in all_segments:
        if len(segment.warnings) > 0 or len(segment.errors) > 0:
            log.write(
                f"{Style.DIM}0x{segment.rom_start:06X}{Style.RESET_ALL} {segment.type} {Style.BRIGHT}{segment.name}{Style.RESET_ALL}:"
            )

            for warn in segment.warnings:
                log.write("warning: " + warn, status="warn")

            for error in segment.errors:
                log.write("error: " + error, status="error")
                had_error = True

            log.write("")  # empty line

    if had_error:
        return 1

    # Statistics
    unk_size = seg_sizes.get("unk", 0)
    rest_size = 0
    total_size = len(rom_bytes)

    for tp in seg_sizes:
        if tp != "unk":
            rest_size += seg_sizes[tp]

    assert (unk_size + rest_size == total_size)

    known_ratio = rest_size / total_size
    unk_ratio = unk_size / total_size

    log.write(
        f"Split {fmt_size(rest_size)} ({known_ratio:.2%}) in defined segments")
    for tp in seg_sizes:
        if tp != "unk":
            tmp_size = seg_sizes[tp]
            tmp_ratio = tmp_size / total_size
            log.write(
                f"{tp:>20}: {fmt_size(tmp_size):>8} ({tmp_ratio:.2%}) {Fore.GREEN}{seg_split[tp]} split{Style.RESET_ALL}, {Style.DIM}{seg_cached[tp]} cached"
            )
    log.write(
        f"{'unknown':>20}: {fmt_size(unk_size):>8} ({unk_ratio:.2%}) from unknown bin files"
    )

    # Save cache
    if cache != {}:
        if verbose:
            print("Writing cache")
        with open(cache_path, "wb") as f:
            pickle.dump(cache, f)

    return 0  # no error
Пример #17
0
def run_simulation(args):
    # Init results class for saving the results
    # r = Results()
    if args.verbose:
        util.verbose = True
    # Print start message
    dev_print("Starting simulation")

    # Set simulation variables
    num_of_players = args.players
    # Go through set amount of simulations
    # Start a new game, run it and save the results
    # dev_print(args.trading_range)
    # dev_print(args.upgrading_range)
    strategy = args.strategy
    # for k in range(num_of_players):
    # 	dev_print(np.arange(args.buying_range[k * 3], args.buying_range[k * 3 + 1], args.buying_range[k * 3 + 2]))
    # single_player_param_list = []
    # for params in player_params:
    #
    # 	single_player_param_list.append(generate_combination(num=7, params=params))
    # # dev_print(single_player_param_list)
    # single_player_list = []
    player_combination = None
    if args.mode in [1, 3]:
        strategy_parameter = [[
            args.strategy_parameter[k * 3] +
            args.strategy_parameter[k * 3 + 1] * i
            for i in range(int(args.strategy_parameter[k * 3 + 2]))
        ] for k in range(num_of_players)]
        income = [[
            args.income[k * 3] + args.income[k * 3 + 1] * i
            for i in range(int(args.income[k * 3 + 2]))
        ] for k in range(num_of_players)]
        tax = [[
            args.tax[k * 3] + args.tax[k * 3 + 1] * i
            for i in range(int(args.tax[k * 3 + 2]))
        ] for k in range(num_of_players)]
        b_tax = [[
            args.building_tax[k * 3] + args.building_tax[k * 3 + 1] * i
            for i in range(int(args.building_tax[k * 3 + 2]))
        ] for k in range(num_of_players)]
        start_capital = [[
            args.start_capital[k * 3] + args.start_capital[k * 3 + 1] * i
            for i in range(int(args.start_capital[k * 3 + 2]))
        ] for k in range(num_of_players)]

        player_params = [[
            strategy_parameter[k], income[k], tax[k], start_capital[k],
            b_tax[k]
        ] for k in range(num_of_players)]
        # dev_print(player_params)
        if args.mode == 3:
            single_player_param_list = []
            for params in player_params:
                single_player_param_list.append(
                    generate_combination(num=5, params=params))
            # dev_print(single_player_param_list)
            single_player_list = []
            for num in range(num_of_players):
                tmp = []
                for p in single_player_param_list[num]:
                    tmp.append(
                        Player(num=num,
                               strategy=strategy[num],
                               strategy_para=p[0],
                               income=p[1],
                               tax=p[2],
                               start_capital=p[3],
                               building_tax=p[4]))
                single_player_list.append(tmp)

            player_combination = generate_combination(
                num=num_of_players, params=single_player_list)
        elif args.mode == 1:
            n = int(args.tax[2])
            player_combination = []
            for i in range(n):
                tmp = []
                for num in range(num_of_players):
                    tmp_player = Player(
                        num=num,
                        strategy=strategy[num],
                        strategy_para=strategy_parameter[num][i],
                        income=income[num][i],
                        tax=tax[num][i],
                        start_capital=start_capital[num][i],
                        building_tax=b_tax[num][i])

                    tmp.append(tmp_player)
                player_combination.append(tmp)
    elif args.mode == 2:
        player_combination = []
        para_list = [
            args.strategy_parameter, args.income, args.tax, args.start_capital,
            args.building_tax
        ]
        para_combination = generate_combination(5, para_list)
        for p in para_combination:
            tmp = []
            for i in range(num_of_players):
                tmp.append(
                    Player(num=i,
                           strategy=args.strategy[0],
                           strategy_para=p[0],
                           income=p[1],
                           tax=p[2],
                           start_capital=p[3],
                           building_tax=p[4]))
            player_combination.append(tmp)
    else:
        raise ValueError("Unknown type.")

    count = 1
    last = time.time()
    simulation_list = []
    for players in player_combination:
        cur_simulation_dic = {"settings": {}, "details": {}, "results": {}}
        player_info_lst = []
        for i in range(len(players)):
            cur_player_dic = {
                "strategy": players[i].strategy,
                "strategy_para": players[i].strategy_para,
                "income": players[i].income,
                "tax": players[i].tax,
                "start_capital": players[i].start_capital,
                "building_tax": players[i].building_tax
            }
            player_info_lst.append(cur_player_dic)
        cur_simulation_dic["settings"] = player_info_lst

        total_rounds = 0
        valid_simulation = 0
        if util.verbose:
            log.write("player combination: " + str(players) + "\n")
        for i in range(1, args.number + 1):
            if util.verbose:
                log.write("simulation number" + str(i) + "\n")
            for n in players:
                n.reset()
            g = Game(players, rounds=args.rounds)
            tmp_info_dic = g.run()

            if tmp_info_dic["end"] != -1:
                total_rounds += tmp_info_dic["end"]
                valid_simulation += 1

            cur_simulation_dic["details"][i] = tmp_info_dic
            if i % 100 == 0:
                dev_print(
                    "{} out of {} simulation of combination {} finished.".
                    format(i, args.number, count))

        # r.addHitResults(g.board.hits)

        # Calculate the amount of simulations per second
        now = time.time()
        duration = now - last
        avg_time = duration / args.number
        try:
            avg_round = total_rounds / valid_simulation
        except ZeroDivisionError:
            avg_round = float("inf")
        last = time.time()

        cur_simulation_dic["results"]["avg_time"] = avg_time
        cur_simulation_dic["results"]["avg_round"] = avg_round
        cur_simulation_dic["results"]["total_time"] = duration
        cur_simulation_dic["results"][
            "end_percent"] = valid_simulation / args.number
        # speed = i / (now - start)
        dev_print("ended: ", valid_simulation)
        dev_print("avg_time: ", avg_time)
        dev_print("avg_round: ", avg_round)
        # Display the progress every 1/1000 of the way to begin finished
        dev_print("{} out of {} combination finished.".format(
            count, len(player_combination)))
        count += 1
        simulation_list.append(cur_simulation_dic)
    metadata_dic["simulations"] = simulation_list
    prod_print(json.dumps(metadata_dic) + "\n")
    # Print that the simulation is finished
    dev_print("\nDone!")
Пример #18
0
 def log(self, msg):
     if self.options.get("verbose", False):
         log.write(f"{self.type} {self.name}: {msg}")
Пример #19
0
    def doChanceCard(self, card, board):
        # Check the type of the chance card
        # card for moving
        if util.verbose:
            log.write("player {0} get a chance card {1}.\n".format(
                self.num, card))
        if card.kind == "advance":
            # Move to next utilities if necessary
            if card.value == "utility":
                # Keep track if suitable utilities is found
                if util.verbose:
                    log.write(
                        "player {0} goes to the nearest utility.\n".format(
                            self.num))
                moved = False
                # Go through possible utilities
                for pos in board.TILES_UTILITIES:
                    # If player is before current utility, go to that one
                    if self.position < pos:
                        self.position = pos
                        moved = True
                        break

                # If not yet moved, go to first utilities in array
                if not moved:
                    self.position = board.TILES_UTILITIES[0]

            # Move to next railroad if necessary
            elif card.value == "railroad":
                # Keep track if suitable railroad is found
                if util.verbose:
                    log.write(
                        "player {0} goes to the nearest railroad.\n".format(
                            self.num))
                moved = False
                # Go through possible railroad
                for pos in board.TILES_TRANSPORT:
                    # If player is before current railroad, go to that one
                    if self.position < pos:
                        self.position = pos
                        moved = True
                        break

                # If not yet moved, go to first railroad in array
                if not moved:
                    self.position = board.TILES_TRANSPORT[0]

            # If negative, thus should move back, do that
            elif card.value <= 0:
                self.position, _ = self.getNewPosition(card.value, board)
                if util.verbose:
                    log.write("player {0} moves to {1}.\n".format(
                        self.num, board.TILE_NAME[self.position]))
            # Move player to given position otherwise
            else:
                self.position = card.value
                if self.position == board.TILES_JAIL[0]:
                    self.at_jail = True
                if util.verbose:
                    log.write("player {0} moves to {1}.\n".format(
                        self.num, board.TILE_NAME[self.position]))
        # card for get money
        elif card.kind == "cash":
            if self.cash >= 0:
                self.cash += card.value
            else:
                self.fine_money(-self.cash)
            if util.verbose:
                log.write(
                    "player {0} gets {1} cash, currently has {2} cash.\n".
                    format(self.num, card.value, self.cash))

        # card for tax
        elif card.kind == "tax":
            fined = card.value[0] * self.house + card.value[1] * self.hotel
            self.fine_money(fined)
            if util.verbose:
                log.write(
                    "player {0} pay {1} tax, currently has {2} cash.\n".format(
                        self.num, fined, self.cash))
Пример #20
0
    def move(self, board, dice1, dice2):
        # Determine whether to go to jail due to double throws
        if not self.at_jail:
            if dice1 == dice2:
                # Add one to the number of consecutive doubles
                self.consecutiveDoubles += 1

                # Signal to go to jail if 3 doubles in a row
                if self.consecutiveDoubles >= 3:
                    self.go_to_jail()
                    # Reset consecutive throws
                    self.consecutiveDoubles = 0
                    if util.verbose:
                        log.write(
                            "player {0} goes to jail because of 3 consecutive doubles.\n"
                            .format(self.num))
                    return
            else:
                # Reset consecutive doubles every time different numbers are roled
                self.consecutiveDoubles = 0

            # Calculate new position, overflow if necessary
            newPosition, pass_go = self.getNewPosition(dice1 + dice2, board)

            # gain money when pass Go
            if pass_go:
                tax = self.pay_tax()
                self.cash += self.income
                if util.verbose:
                    log.write(
                        "player {0} pays {1} tax and gains {2} because of passing Go, currently has {3} cash.\n"
                        .format(self.num, tax, self.income, self.cash))

            if newPosition >= board.TILES_JAIL[0] > self.position:
                newPosition += 1
            # Apply new position
            self.position = newPosition
            if util.verbose:
                log.write("player {0} move to {1}.\n".format(
                    self.num, board.TILE_NAME[self.position]))
        else:
            if util.verbose:
                log.write("player {0} is in jail.\n".format(self.num))
            if dice1 == dice2:
                self.go_out_of_jail()
                if util.verbose:
                    log.write(
                        "player {0} goes out of jail because of a double.\n".
                        format(self.num))
                return
            else:
                self.consecutive_not_Doubles += 1

            if self.consecutive_not_Doubles >= 3:
                self.fine_money(50)
                self.go_out_of_jail()
                self.consecutive_not_Doubles = 0
                if util.verbose:
                    log.write(
                        "player {0} goes out of jail because of paying 50.\n".
                        format(self.num))
Пример #21
0
 def log(self, msg):
     if options.verbose():
         log.write(f"{self.type} {self.name}: {msg}")
Пример #22
0
def initialize(all_segments):
    global all_symbols
    global symbol_ranges

    all_symbols = []
    symbol_ranges = []

    # Manual list of func name / addrs
    for path in options.get_symbol_addrs_paths():
        if path.exists():
            with open(path) as f:
                sym_addrs_lines = f.readlines()
                for line_num, line in enumerate(sym_addrs_lines):
                    line = line.strip()
                    if not line == "" and not line.startswith("//"):
                        comment_loc = line.find("//")
                        line_main = line
                        line_ext = ""

                        if comment_loc != -1:
                            line_ext = line[comment_loc + 2:].strip()
                            line_main = line[:comment_loc].strip()

                        try:
                            line_split = line_main.split("=")
                            name = line_split[0].strip()
                            addr = int(line_split[1].strip()[:-1], 0)
                        except:
                            log.parsing_error_preamble(path, line_num, line)
                            log.write("Line should be of the form")
                            log.write(
                                "<function_name> = <address> // attr0:val0 attr1:val1 [...]"
                            )
                            log.write(
                                "with <address> in hex preceded by 0x, or dec")
                            log.write("")
                            raise

                        sym = Symbol(addr, given_name=name)

                        if line_ext:
                            for info in line_ext.split(" "):
                                if ":" in info:
                                    if info.count(':') > 1:
                                        log.parsing_error_preamble(
                                            path, line_num, line)
                                        log.write(f"Too many ':'s in '{info}'")
                                        log.error("")

                                    attr_name, attr_val = info.split(":")
                                    if attr_name == "":
                                        log.parsing_error_preamble(
                                            path, line_num, line)
                                        log.write(
                                            f"Missing attribute name in '{info}', is there extra whitespace?"
                                        )
                                        log.error("")
                                    if attr_val == "":
                                        log.parsing_error_preamble(
                                            path, line_num, line)
                                        log.write(
                                            f"Missing attribute value in '{info}', is there extra whitespace?"
                                        )
                                        log.error("")

                                    # Non-Boolean attributes
                                    try:
                                        if attr_name == "type":
                                            type = attr_val
                                            sym.type = type
                                            continue
                                        if attr_name == "size":
                                            size = int(attr_val, 0)
                                            sym.size = size
                                            continue
                                        if attr_name == "rom":
                                            rom_addr = int(attr_val, 0)
                                            sym.rom = rom_addr
                                            continue
                                    except:
                                        log.parsing_error_preamble(
                                            path, line_num, line)
                                        log.write(
                                            f"value of attribute '{attr_name}' could not be read:"
                                        )
                                        log.write("")
                                        raise

                                    # Boolean attributes
                                    tf_val = (
                                        True if is_truey(attr_val) else
                                        False if is_falsey(attr_val) else None)
                                    if tf_val is None:
                                        log.parsing_error_preamble(
                                            path, line_num, line)
                                        log.write(
                                            f"Invalid Boolean value '{attr_val}' for attribute '{attr_name}', should be one of"
                                        )
                                        log.write([*TRUEY_VALS, *FALSEY_VALS])
                                        log.error("")

                                    if attr_name == "dead":
                                        sym.dead = tf_val
                                        continue
                                    if attr_name == "defined":
                                        sym.defined = tf_val
                                        continue
                                    if attr_name == "extract":
                                        sym.extract = tf_val
                                        continue
                        all_symbols.append(sym)

                        # Symbol ranges
                        if sym.size > 4:
                            symbol_ranges.append(sym)

                        is_symbol_isolated(sym, all_segments)
Пример #23
0
def main(config_path, base_dir, target_path, modes, verbose, use_cache=True):
    global config

    log.write(f"splat {VERSION} (powered by spimdisasm {spimdisasm.__version__})")

    # Load config
    config = {}
    for entry in config_path:
        with open(entry) as f:
            additional_config = yaml.load(f.read(), Loader=yaml.SafeLoader)
        config = merge_configs(config, additional_config)

    options.initialize(config, config_path, base_dir, target_path)
    options.set("modes", modes)

    if verbose:
        options.set("verbose", True)

    with options.get_target_path().open("rb") as f2:
        rom_bytes = f2.read()

    if "sha1" in config:
        sha1 = hashlib.sha1(rom_bytes).hexdigest()
        e_sha1 = config["sha1"].lower()
        if e_sha1 != sha1:
            log.error(f"sha1 mismatch: expected {e_sha1}, was {sha1}")

    # Create main output dir
    options.get_base_path().mkdir(parents=True, exist_ok=True)

    processed_segments: List[Segment] = []

    seg_sizes: Dict[str, int] = {}
    seg_split: Dict[str, int] = {}
    seg_cached: Dict[str, int] = {}

    # Load cache
    if use_cache:
        try:
            with options.get_cache_path().open("rb") as f3:
                cache = pickle.load(f3)

            if verbose:
                log.write(f"Loaded cache ({len(cache.keys())} items)")
        except Exception:
            cache = {}
    else:
        cache = {}

    # invalidate entire cache if options change
    if use_cache and cache.get("__options__") != config.get("options"):
        if verbose:
            log.write("Options changed, invalidating cache")

        cache = {
            "__options__": config.get("options"),
        }

    configure_disassembler()

    # Initialize segments
    all_segments = initialize_segments(config["segments"])

    # Load and process symbols
    symbols.initialize(all_segments)

    # Assign symbols to segments
    assign_symbols_to_segments()

    if options.mode_active("code"):
        symbols.initialize_spim_context(all_segments)

    # Resolve raster/palette siblings
    if options.mode_active("img"):
        palettes.initialize(all_segments)

    # Scan
    scan_bar = tqdm.tqdm(all_segments, total=len(all_segments))
    for segment in scan_bar:
        assert isinstance(segment, Segment)
        scan_bar.set_description(f"Scanning {brief_seg_name(segment, 20)}")
        typ = segment.type
        if segment.type == "bin" and segment.is_name_default():
            typ = "unk"

        if typ not in seg_sizes:
            seg_sizes[typ] = 0
            seg_split[typ] = 0
            seg_cached[typ] = 0
        seg_sizes[typ] += 0 if segment.size is None else segment.size

        if segment.should_scan():
            # Check cache but don't write anything
            if use_cache:
                if segment.cache() == cache.get(segment.unique_id()):
                    continue

            segment.did_run = True
            segment.scan(rom_bytes)

            processed_segments.append(segment)

            seg_split[typ] += 1

    # Split
    for segment in tqdm.tqdm(
        all_segments,
        total=len(all_segments),
        desc=f"Splitting {brief_seg_name(segment, 20)}",
    ):
        if use_cache:
            cached = segment.cache()

            if cached == cache.get(segment.unique_id()):
                # Cache hit
                seg_cached[typ] += 1
                continue
            else:
                # Cache miss; split
                cache[segment.unique_id()] = cached

        if segment.should_split():
            segment.split(rom_bytes)

    if options.mode_active("ld"):
        global linker_writer
        linker_writer = LinkerWriter()
        for segment in tqdm.tqdm(
            all_segments,
            total=len(all_segments),
            desc=f"Writing linker script {brief_seg_name(segment, 20)}",
        ):
            linker_writer.add(segment)
        linker_writer.save_linker_script()
        linker_writer.save_symbol_header()

        # write elf_sections.txt - this only lists the generated sections in the elf, not subsections
        # that the elf combines into one section
        if options.get_create_elf_section_list_auto():
            section_list = ""
            for segment in all_segments:
                section_list += "." + to_cname(segment.name) + "\n"
            with open(options.get_elf_section_list_path(), "w", newline="\n") as f:
                f.write(section_list)

    # Write undefined_funcs_auto.txt
    if options.get_create_undefined_funcs_auto():
        to_write = [
            s
            for s in symbols.all_symbols
            if s.referenced and not s.defined and not s.dead and s.type == "func"
        ]
        to_write.sort(key=lambda x: x.vram_start)

        with open(options.get_undefined_funcs_auto_path(), "w", newline="\n") as f:
            for symbol in to_write:
                f.write(f"{symbol.name} = 0x{symbol.vram_start:X};\n")

    # write undefined_syms_auto.txt
    if options.get_create_undefined_syms_auto():
        to_write = [
            s
            for s in symbols.all_symbols
            if s.referenced
            and not s.defined
            and not s.dead
            and s.type not in {"func", "label", "jtbl_label"}
        ]
        to_write.sort(key=lambda x: x.vram_start)

        with open(options.get_undefined_syms_auto_path(), "w", newline="\n") as f:
            for symbol in to_write:
                f.write(f"{symbol.name} = 0x{symbol.vram_start:X};\n")

    # print warnings during split
    for segment in all_segments:
        if len(segment.warnings) > 0:
            log.write(
                f"{Style.DIM}0x{segment.rom_start:06X}{Style.RESET_ALL} {segment.type} {Style.BRIGHT}{segment.name}{Style.RESET_ALL}:"
            )

            for warn in segment.warnings:
                log.write("warning: " + warn, status="warn")

            log.write("")  # empty line

    # Statistics
    do_statistics(seg_sizes, rom_bytes, seg_split, seg_cached)

    # Save cache
    if cache != {} and use_cache:
        if verbose:
            log.write("Writing cache")
        with open(options.get_cache_path(), "wb") as f4:
            pickle.dump(cache, f4)
Пример #24
0
def main(config_path, base_dir, target_path, modes, verbose, use_cache=True):
    global config

    # Load config
    with open(config_path) as f:
        config = yaml.load(f.read(), Loader=yaml.SafeLoader)

    options.initialize(config, config_path, base_dir, target_path)
    options.set("modes", modes)

    if verbose:
        options.set("verbose", True)

    with options.get_target_path().open("rb") as f2:
        rom_bytes = f2.read()

    if "sha1" in config:
        sha1 = hashlib.sha1(rom_bytes).hexdigest()
        e_sha1 = config["sha1"]
        if e_sha1 != sha1:
            log.error(f"sha1 mismatch: expected {e_sha1}, was {sha1}")

    # Create main output dir
    options.get_base_path().mkdir(parents=True, exist_ok=True)

    processed_segments: List[Segment] = []

    seg_sizes: Dict[str, int] = {}
    seg_split: Dict[str, int] = {}
    seg_cached: Dict[str, int] = {}

    # Load cache
    if use_cache:
        try:
            with options.get_cache_path().open("rb") as f3:
                cache = pickle.load(f3)

            if verbose:
                log.write(f"Loaded cache ({len(cache.keys())} items)")
        except Exception:
            cache = {}
    else:
        cache = {}

    # invalidate entire cache if options change
    if use_cache and cache.get("__options__") != config.get("options"):
        if verbose:
            log.write("Options changed, invalidating cache")

        cache = {
            "__options__": config.get("options"),
        }

    # Initialize segments
    all_segments = initialize_segments(config["segments"])

    # Load and process symbols
    if options.mode_active("code"):
        log.write("Loading and processing symbols")
        symbols.initialize(all_segments)

    # Resolve raster/palette siblings
    if options.mode_active("img"):
        palettes.initialize(all_segments)

    # Scan
    log.write("Starting scan")
    for segment in all_segments:
        typ = segment.type
        if segment.type == "bin" and segment.is_name_default():
            typ = "unk"

        if typ not in seg_sizes:
            seg_sizes[typ] = 0
            seg_split[typ] = 0
            seg_cached[typ] = 0
        seg_sizes[typ] += 0 if segment.size is None else segment.size

        if segment.should_scan():
            # Check cache but don't write anything
            if use_cache:
                if segment.cache() == cache.get(segment.unique_id()):
                    continue

            if segment.needs_symbols:
                segment_symbols, other_symbols = get_segment_symbols(
                    segment, all_segments)
                segment.given_seg_symbols = segment_symbols
                segment.given_ext_symbols = other_symbols

            segment.did_run = True
            segment.scan(rom_bytes)

            processed_segments.append(segment)

            seg_split[typ] += 1

        log.dot(status=segment.status())

    # Split
    log.write("Starting split")
    for segment in all_segments:
        if use_cache:
            cached = segment.cache()

            if cached == cache.get(segment.unique_id()):
                # Cache hit
                seg_cached[typ] += 1
                continue
            else:
                # Cache miss; split
                cache[segment.unique_id()] = cached

        if segment.should_split():
            segment.split(rom_bytes)

        log.dot(status=segment.status())

    if options.mode_active("ld"):
        global linker_writer
        linker_writer = LinkerWriter()
        for segment in all_segments:
            linker_writer.add(segment)
        linker_writer.save_linker_script()
        linker_writer.save_symbol_header()

    # Write undefined_funcs_auto.txt
    to_write = [
        s for s in symbols.all_symbols
        if s.referenced and not s.defined and not s.dead and s.type == "func"
    ]
    if len(to_write) > 0:
        with open(options.get_undefined_funcs_auto_path(), "w",
                  newline="\n") as f:
            for symbol in to_write:
                f.write(f"{symbol.name} = 0x{symbol.vram_start:X};\n")

    # write undefined_syms_auto.txt
    to_write = [
        s for s in symbols.all_symbols if s.referenced and not s.defined
        and not s.dead and not s.type == "func"
    ]
    if len(to_write) > 0:
        with open(options.get_undefined_syms_auto_path(), "w",
                  newline="\n") as f:
            for symbol in to_write:
                f.write(f"{symbol.name} = 0x{symbol.vram_start:X};\n")

    # print warnings during split
    for segment in all_segments:
        if len(segment.warnings) > 0:
            log.write(
                f"{Style.DIM}0x{segment.rom_start:06X}{Style.RESET_ALL} {segment.type} {Style.BRIGHT}{segment.name}{Style.RESET_ALL}:"
            )

            for warn in segment.warnings:
                log.write("warning: " + warn, status="warn")

            log.write("")  # empty line

    # Statistics
    do_statistics(seg_sizes, rom_bytes, seg_split, seg_cached)

    # Save cache
    if cache != {} and use_cache:
        if verbose:
            log.write("Writing cache")
        with open(options.get_cache_path(), "wb") as f4:
            pickle.dump(cache, f4)
Пример #25
0
def initialize(all_segments: "List[Segment]"):
    global all_symbols
    global all_symbols_dict
    global symbol_ranges

    all_symbols = []
    all_symbols_dict = {}
    symbol_ranges = []

    def get_seg_for_name(name: str) -> Optional["Segment"]:
        for segment in all_segments:
            if segment.name == name:
                return segment
        return None

    # Manual list of func name / addrs
    for path in options.get_symbol_addrs_paths():
        if path.exists():
            with open(path) as f:
                sym_addrs_lines = f.readlines()
                for line_num, line in enumerate(
                        tqdm.tqdm(sym_addrs_lines,
                                  desc=f"Loading symbols ({path.stem})")):
                    line = line.strip()
                    if not line == "" and not line.startswith("//"):
                        comment_loc = line.find("//")
                        line_main = line
                        line_ext = ""

                        if comment_loc != -1:
                            line_ext = line[comment_loc + 2:].strip()
                            line_main = line[:comment_loc].strip()

                        try:
                            line_split = line_main.split("=")
                            name = line_split[0].strip()
                            addr = int(line_split[1].strip()[:-1], 0)
                        except:
                            log.parsing_error_preamble(path, line_num, line)
                            log.write("Line should be of the form")
                            log.write(
                                "<function_name> = <address> // attr0:val0 attr1:val1 [...]"
                            )
                            log.write(
                                "with <address> in hex preceded by 0x, or dec")
                            log.write("")
                            raise

                        sym = Symbol(addr, given_name=name)

                        ignore_sym = False
                        if line_ext:
                            for info in line_ext.split(" "):
                                if ":" in info:
                                    if info.count(":") > 1:
                                        log.parsing_error_preamble(
                                            path, line_num, line)
                                        log.write(f"Too many ':'s in '{info}'")
                                        log.error("")

                                    attr_name, attr_val = info.split(":")
                                    if attr_name == "":
                                        log.parsing_error_preamble(
                                            path, line_num, line)
                                        log.write(
                                            f"Missing attribute name in '{info}', is there extra whitespace?"
                                        )
                                        log.error("")
                                    if attr_val == "":
                                        log.parsing_error_preamble(
                                            path, line_num, line)
                                        log.write(
                                            f"Missing attribute value in '{info}', is there extra whitespace?"
                                        )
                                        log.error("")

                                    # Non-Boolean attributes
                                    try:
                                        if attr_name == "type":
                                            type = attr_val
                                            sym.type = type
                                            continue
                                        if attr_name == "size":
                                            size = int(attr_val, 0)
                                            sym.given_size = size
                                            continue
                                        if attr_name == "rom":
                                            rom_addr = int(attr_val, 0)
                                            sym.rom = rom_addr
                                            continue
                                        if attr_name == "segment":
                                            seg = get_seg_for_name(attr_val)
                                            if seg is None:
                                                log.parsing_error_preamble(
                                                    path, line_num, line)
                                                log.write(
                                                    f"Cannot find segment '{attr_val}'"
                                                )
                                                log.error("")
                                            else:
                                                # Add segment to symbol, symbol to segment
                                                sym.segment = seg
                                                seg.add_symbol(sym)
                                            continue
                                    except:
                                        log.parsing_error_preamble(
                                            path, line_num, line)
                                        log.write(
                                            f"value of attribute '{attr_name}' could not be read:"
                                        )
                                        log.write("")
                                        raise

                                    # Boolean attributes
                                    tf_val = (
                                        True if is_truey(attr_val) else
                                        False if is_falsey(attr_val) else None)
                                    if tf_val is None:
                                        log.parsing_error_preamble(
                                            path, line_num, line)
                                        log.write(
                                            f"Invalid Boolean value '{attr_val}' for attribute '{attr_name}', should be one of"
                                        )
                                        log.write([*TRUEY_VALS, *FALSEY_VALS])
                                        log.error("")
                                    else:
                                        if attr_name == "dead":
                                            sym.dead = tf_val
                                            continue
                                        if attr_name == "defined":
                                            sym.defined = tf_val
                                            continue
                                        if attr_name == "extract":
                                            sym.extract = tf_val
                                            continue
                                        if attr_name == "ignore":
                                            ignore_sym = tf_val
                                            continue
                        if ignore_sym:
                            ignored_addresses.add(sym.vram_start)
                            ignore_sym = False
                            continue

                        sym.user_declared = True
                        add_symbol(sym)

                        # Symbol ranges
                        if sym.size > 4:
                            symbol_ranges.append(sym)