Exemple #1
0
    def create_tracker(self):
        self.delete_tracker()
        game_enum = RandovaniaGame.PRIME2

        resource_database = default_database.resource_database_for(game_enum)
        with get_data_path().joinpath(f"gui_assets/tracker/{game_enum.value}.json").open("r") as tracker_details_file:
            tracker_details = json.load(tracker_details_file)

        for element in tracker_details["elements"]:
            text_template = ""

            if "image_path" in element:
                image_path = get_data_path().joinpath(element["image_path"])
                pixmap = QPixmap(str(image_path))

                label = ClickableLabel(self.inventory_group, paint_with_opacity(pixmap, 0.3),
                                       paint_with_opacity(pixmap, 1.0))
                label.set_checked(False)
                label.set_ignore_mouse_events(True)

            elif "label" in element:
                label = QLabel(self.inventory_group)
                label.setAlignment(Qt.AlignCenter)
                text_template = element["label"]

            else:
                raise ValueError(f"Invalid element: {element}")

            resources = [
                find_resource_info_with_long_name(resource_database.item, resource_name)
                for resource_name in element["resources"]
            ]
            self._tracker_elements.append(Element(label, resources, text_template))
            self.inventory_layout.addWidget(label, element["row"], element["column"])
def read_json_then_binary(game: RandovaniaGame) -> Tuple[Path, dict]:
    json_path = get_data_path().joinpath("json_data", f"{game.value}.json")
    if json_path.exists():
        with json_path.open("r") as open_file:
            return json_path, json.load(open_file)

    binary_path = get_data_path().joinpath("binary_data", f"{game.value}.bin")
    return binary_path, decode_file_path(binary_path)
def decode_default_prime2() -> dict:
    json_database = get_data_path().joinpath("json_data", "prime2.json")

    if json_database.exists():
        with json_database.open("r") as open_file:
            return json.load(open_file)

    return decode_file_path(
        get_data_path().joinpath("binary_data", "prime2.bin"),
        get_data_path().joinpath("binary_data", "prime2_extra.json"))
Exemple #4
0
def test_commited_human_readable_description(echoes_game_description):
    buffer = io.StringIO()
    pretty_print.write_human_readable_world_list(echoes_game_description,
                                                 buffer)

    assert randovania.get_data_path().joinpath(
        "json_data", "prime2.txt").read_text("utf-8") == buffer.getvalue()
Exemple #5
0
def _all_hash_words() -> Dict[RandovaniaGame, typing.List[str]]:
    with (get_data_path() / "hash_words" /
          "hash_words.json").open() as hash_words_file:
        return {
            RandovaniaGame(key): words
            for key, words in json.load(hash_words_file).items()
        }
def default_prime2_memo_data() -> dict:
    with get_data_path().joinpath("item_database", "memo_data.json").open("r") as memo_data_file:
        memo_data = json.load(memo_data_file)

    item_database.add_memo_data_keys(memo_data)

    return memo_data
def set_default_window_icon(window: QWidget):
    """
    Sets the window icon for the given widget to the default icon
    :param window:
    :return:
    """
    window.setWindowIcon(QIcon(str(get_data_path().joinpath("icons", "sky_temple_key_NqN_icon.ico"))))
Exemple #8
0
def read_preset_list() -> List[Path]:
    base_path = randovania.get_data_path().joinpath("presets")

    with base_path.joinpath("presets.json").open() as presets_file:
        preset_list = json.load(presets_file)["presets"]

    return [base_path.joinpath(preset["path"]) for preset in preset_list]
Exemple #9
0
def test_full_data_encode_is_equal(game_enum):
    # The json data may be missing if we're running using a Pyinstaller binary
    # Setup

    data_dir = game_enum.data_path.joinpath("json_data")
    if not data_dir.is_dir() and get_data_path().joinpath(
            "binary_data", f"{game_enum.value}.bin").is_file():
        pytest.skip("Missing json-based data")

    json_database = data_reader.read_split_file(data_dir)

    b = io.BytesIO()
    binary_data.encode(json_database, b)

    b.seek(0)
    decoded_database = binary_data.decode(b)

    # Run
    assert decoded_database == json_database

    comparable_json = _comparable_dict(json_database)
    comparable_binary = _comparable_dict(decoded_database)
    for a, b in zip(comparable_json, comparable_binary):
        assert a == b

    assert comparable_binary == comparable_json
Exemple #10
0
def get_default_ammo_configurations() -> AmmoConfiguration:
    item_database = default_prime2_item_database()

    with get_data_path().joinpath("item_database", "default_state", "ammo.json").open() as open_file:
        data = json.load(open_file)

    return AmmoConfiguration.from_json(data, item_database)
def get_vanilla_colors_translator_configurations(
) -> Dict[TranslatorGate, LayoutTranslatorRequirement]:
    with get_data_path().joinpath(
            "item_database", "prime2", "default_state",
            "translator_vanilla_colors.json").open() as open_file:
        data = json.load(open_file)

    return _raw_translator_configurations(data)
def default_prime2_item_database() -> item_database.ItemDatabase:
    configuration_path = get_data_path().joinpath("item_database", "configuration")

    with configuration_path.joinpath("major-items.json").open() as major_items_file:
        major_items_data = json.load(major_items_file)

    with configuration_path.joinpath("ammo.json").open() as ammo_file:
        ammo_data = json.load(ammo_file)

    return item_database.read_database(major_items_data, ammo_data)
Exemple #13
0
def read_preset_list() -> List[Preset]:
    base_path = get_data_path().joinpath("presets")

    with base_path.joinpath("presets.json").open() as presets_file:
        preset_list = json.load(presets_file)["presets"]

    return [
        read_preset_file(base_path.joinpath(preset["path"]))
        for preset in preset_list
    ]
Exemple #14
0
def refresh_presets_command_logic(args):
    base_path = get_data_path().joinpath("presets")

    with base_path.joinpath("presets.json").open() as presets_file:
        preset_list = json.load(presets_file)["presets"]

    for preset_relative_path in preset_list:
        preset_path = base_path.joinpath(preset_relative_path["path"])
        preset = VersionedPreset.from_file_sync(preset_path)
        preset.ensure_converted()
        preset.save_to_file(preset_path)
    def create_tracker(self, game_enum: RandovaniaGame):
        effective_theme = self.theme
        if not path_for(game_enum, effective_theme).is_file():
            effective_theme = TrackerTheme.CLASSIC

        if (game_enum, effective_theme) == self._last_tracker_config:
            return
        self.delete_tracker()

        resource_database = default_database.resource_database_for(game_enum)

        with path_for(game_enum,
                      effective_theme).open("r") as tracker_details_file:
            tracker_details = json.load(tracker_details_file)

        for element in tracker_details["elements"]:
            text_template = ""

            if "image_path" in element:
                image_path = get_data_path().joinpath(element["image_path"])
                pixmap = QPixmap(str(image_path))

                label = ClickableLabel(self.inventory_group,
                                       paint_with_opacity(pixmap, 0.3),
                                       paint_with_opacity(pixmap, 1.0))
                label.set_checked(False)
                label.set_ignore_mouse_events(True)

            elif "label" in element:
                label = QLabel(self.inventory_group)
                label.setAlignment(Qt.AlignCenter)
                text_template = element["label"]

            else:
                raise ValueError(f"Invalid element: {element}")

            resources = [
                find_resource_info_with_long_name(resource_database.item,
                                                  resource_name)
                for resource_name in element["resources"]
            ]
            self._tracker_elements.append(
                Element(label, resources, text_template))
            self.inventory_layout.addWidget(label, element["row"],
                                            element["column"])

        self.inventory_spacer = QSpacerItem(5, 5, QSizePolicy.Expanding,
                                            QSizePolicy.Expanding)
        self.inventory_layout.addItem(self.inventory_spacer,
                                      self.inventory_layout.rowCount(),
                                      self.inventory_layout.columnCount())
        self._update_tracker_from_hook({})

        self._last_tracker_config = (game_enum, effective_theme)
Exemple #16
0
def read_json_then_binary(game: RandovaniaGame) -> tuple[Path, dict]:
    dir_path = game.data_path.joinpath("json_data")
    if dir_path.exists():
        return dir_path, data_reader.read_split_file(dir_path)

    json_path = dir_path.joinpath(f"{game.value}.json")
    if json_path.exists():
        with json_path.open("r") as open_file:
            return json_path, data_reader.read_json_file(open_file)

    binary_path = get_data_path().joinpath("binary_data", f"{game.value}.bin")
    return binary_path, binary_data.decode_file_path(binary_path)
Exemple #17
0
def get_default_major_items_configurations() -> MajorItemsConfiguration:
    item_database = default_prime2_item_database()

    with get_data_path().joinpath("item_database", "default_state", "major-items.json").open() as open_file:
        data = json.load(open_file)

    return MajorItemsConfiguration(
        items_state={
            item_database.major_items[name]: MajorItemState.from_json(state_data)
            for name, state_data in data["items_state"].items()
        }
    )
def refresh_presets_command_logic(args):

    base_path = get_data_path().joinpath("presets")

    with base_path.joinpath("presets.json").open() as presets_file:
        preset_list = json.load(presets_file)["presets"]

    for preset_relative_path in preset_list:
        preset_path = base_path.joinpath(preset_relative_path["path"])
        save_preset_file(
            read_preset_file(preset_path),
            preset_path
        )
def _config_with_data(request):
    with get_data_path().joinpath("item_database", "default_state",
                                  "ammo.json").open() as open_file:
        data = json.load(open_file)

    for key, value in request.param.get("items_state", {}).items():
        data["items_state"][key] = value

    for key, value in request.param.get("maximum_ammo", {}).items():
        data["maximum_ammo"][key] = value

    return request.param["encoded"], AmmoConfiguration.from_json(
        data, default_prime2_item_database())
def _config_with_data(request):
    with get_data_path().joinpath("item_database", "default_state", "major-items.json").open() as open_file:
        data = json.load(open_file)

    data["progressive_suit"] = True
    data["progressive_grapple"] = True
    data["progressive_launcher"] = True
    data["minimum_random_starting_items"] = True
    data["maximum_random_starting_items"] = True

    for field, value in request.param["replace"].items():
        for key, inner_value in value.items():
            data[field][key] = inner_value

    return request.param["encoded"], MajorItemsConfiguration.from_json(data, default_prime2_item_database())
def _create_config_for(game: RandovaniaGame, replace: dict):
    with get_data_path().joinpath("item_database", game.value, "default_state",
                                  "major-items.json").open() as open_file:
        default_data = json.load(open_file)

    default_data["minimum_random_starting_items"] = 0
    default_data["maximum_random_starting_items"] = 0

    data = copy.deepcopy(default_data)
    for field, value in replace.items():
        for key, inner_value in value.items():
            data[field][key] = inner_value

    return (
        MajorItemsConfiguration.from_json(default_data, game),
        MajorItemsConfiguration.from_json(data, game),
    )
Exemple #22
0
def _config_with_data(request):
    game: RandovaniaGame = request.param["game"]

    with get_data_path().joinpath("item_database", game.value, "default_state",
                                  "ammo.json").open() as open_file:
        default_data = json.load(open_file)

    default = AmmoConfiguration.from_json(default_data, game)
    data = copy.deepcopy(default_data)

    for key, value in request.param.get("items_state", {}).items():
        data["items_state"][key] = value

    for key, value in request.param.get("maximum_ammo", {}).items():
        data["maximum_ammo"][key] = value

    config = AmmoConfiguration.from_json(data, game)
    return request.param["encoded"], config, default
Exemple #23
0
def test_full_file_round_trip(game):
    # Setup
    json_database_path = get_data_path().joinpath("json_data", f"{game.value}.json")
    if not json_database_path.exists():
        pytest.skip("Missing database")

    with json_database_path.open("r") as json_file:
        original_data = json.load(json_file)

    # Run 1
    output_io = io.BytesIO()
    binary_data.encode(original_data, output_io)

    # Run 2
    output_io.seek(0)
    final_data = binary_data.decode(output_io)

    # Assert
    assert final_data == original_data
def test_full_data_encode_is_equal():
    # The prime2.json may be missing if we're running using a Pyinstaller binary
    # Setup
    json_database_file = get_data_path().joinpath("json_data", "prime2.json")

    with json_database_file.open("r") as open_file:
        json_database = json.load(open_file)

    b = io.BytesIO()
    binary_data.encode(json_database, b)

    b.seek(0)
    decoded_database = binary_data.decode(b)

    # Run
    assert json_database == decoded_database

    comparable_json = _comparable_dict(json_database)
    comparable_binary = _comparable_dict(decoded_database)
    assert comparable_json == comparable_binary
def test_full_file_round_trip():
    # Setup
    json_database_path = get_data_path().joinpath("json_data", "prime2.json")
    if not json_database_path.exists():
        pytest.skip("Missing database")

    with json_database_path.open("r") as json_file:
        original_data = json.load(json_file)

    # Run 1
    output_io = io.BytesIO()
    remaining_data = binary_data.encode(original_data, output_io)

    # Run 2
    output_io.seek(0)
    text_io = io.StringIO(json.dumps(remaining_data))
    final_data = binary_data.decode(output_io, text_io)

    # Assert
    assert final_data == original_data
    def __init__(self, game_connection: GameConnection, options: Options):
        super().__init__()
        self.setupUi(self)
        self.game_connection = game_connection
        self.options = options
        common_qt_lib.set_default_window_icon(self)

        with get_data_path().joinpath(f"gui_assets/tracker/trackers.json"
                                      ).open("r") as trackers_file:
            self.trackers = json.load(trackers_file)["trackers"]

        self._action_to_name = {}
        theme_group = QtGui.QActionGroup(self)
        for name in self.trackers.keys():
            action = QtGui.QAction(self.menu_tracker)
            action.setText(name)
            action.setCheckable(True)
            action.setChecked(name == options.selected_tracker)
            action.triggered.connect(self._on_action_select_tracker)
            self.menu_tracker.addAction(action)
            self._action_to_name[action] = name
            theme_group.addAction(action)

        self._tracker_elements: List[Element] = []
        self.create_tracker()

        self.game_connection_setup = GameConnectionSetup(
            self, self.connection_status_label, self.game_connection, options)
        self.game_connection_setup.create_backend_entries(self.menu_backend)
        self.game_connection_setup.create_upload_nintendont_action(
            self.menu_options)
        self.game_connection_setup.refresh_backend()

        self.action_force_update.triggered.connect(self.on_force_update_button)

        self._update_timer = QtCore.QTimer(self)
        self._update_timer.setInterval(100)
        self._update_timer.timeout.connect(self._on_timer_update)
        self._update_timer.setSingleShot(True)
    async def on_upload_nintendont_action(self):
        nintendont_file = get_data_path().joinpath("nintendont", "boot.dol")
        if not nintendont_file.is_file():
            return await async_dialog.warning(
                self.parent, "Missing Nintendont",
                "Unable to find a Nintendont executable.")

        text = f"Uploading Nintendont to the Wii at {self.options.nintendont_ip}..."
        box = QtWidgets.QMessageBox(QtWidgets.QMessageBox.NoIcon,
                                    "Uploading to Homebrew Channel", text,
                                    QtWidgets.QMessageBox.Ok, self.parent)
        box.button(QtWidgets.QMessageBox.Ok).setEnabled(False)
        box.show()

        try:
            await wiiload.upload_file(nintendont_file, [],
                                      self.options.nintendont_ip)
            box.setText(
                f"Upload finished successfully. Check your Wii for more.")
        except Exception as e:
            box.setText(f"Error uploading to Wii: {e}")
        finally:
            box.button(QtWidgets.QMessageBox.Ok).setEnabled(True)
def decode_randomizer_data() -> dict:
    randomizer_data_path = get_data_path().joinpath("ClarisPrimeRandomizer",
                                                    "RandomizerData.json")

    with randomizer_data_path.open() as randomizer_data_file:
        return json.load(randomizer_data_file)
Exemple #29
0
def convert_prime2_pickups(output_path: Path, status_update: ProgressUpdateCallable):
    metafile = output_path.joinpath("meta.json")
    if get_asset_cache_version(output_path) >= ECHOES_MODELS_VERSION:
        with open(metafile, "r") as md:
            return json.load(md)

    next_id = 0xFFFF0000

    delete_converted_assets(output_path)

    randomizer_data_path = get_data_path().joinpath("ClarisPrimeRandomizer", "RandomizerData.json")
    with randomizer_data_path.open() as randomizer_data_file:
        randomizer_data = json.load(randomizer_data_file)

    def id_generator(asset_type):
        nonlocal next_id
        result = next_id
        while asset_provider.asset_id_exists(result):
            result += 1

        next_id = result + 1
        return result

    start = time.time()
    with echoes_asset_provider() as asset_provider:
        logging.info("Loading PAKs")
        converter = AssetConverter(
            target_game=Game.PRIME,
            asset_providers={Game.ECHOES: asset_provider},
            id_generator=id_generator,
            converters=conversions.converter_for,
        )
        logging.info(f"Finished loading PAKs: {time.time() - start}")

        # These aren't guaranteed to be available in the paks yet, so skip them for now
        models_to_skip = [
            "VioletTranslator",
            "AmberTranslator",
            "EmeraldTranslator",
            "CobaltTranslator",
            "DarkBeamAmmoExpansion",
            "LightBeamAmmoExpansion"
        ]

        # Fix the Varia Suit's character in the suits ANCS referencing a missing skin.
        # 0x3A5E2FE1 is Light Suit's skin
        # this is fixed by Claris' patcher when exporting for Echoes
        asset_provider.get_asset(0xa3e787b7).character_set.characters[0].skin_id = 0x3A5E2FE1

        # Use echoes missile expansion for unlimited missiles instead of missile launcher
        unlimited_missile_data = next(i for i in randomizer_data["ModelData"] if i["Index"] == 42)
        unlimited_missile_data["Model"] = 1581025172
        unlimited_missile_data["AnimSet"] = 435828657

        result = {}
        assets_to_change = [
            data
            for data in randomizer_data["ModelData"]
            if (data["Model"] != Game.ECHOES.invalid_asset_id
                and data["AnimSet"] != Game.ECHOES.invalid_asset_id
                and data["Name"] not in models_to_skip)
        ]

        for i, data in enumerate(assets_to_change):
            try:
                status_update(f"Converting {data['Name']} from Prime 2", i / len(assets_to_change))
                result["{}_{}".format(RandovaniaGame.METROID_PRIME_ECHOES.value, data["Name"])] = Asset(
                    ancs=converter.convert_id(data["AnimSet"], Game.ECHOES, missing_assets_as_invalid=False),
                    cmdl=converter.convert_id(data["Model"], Game.ECHOES, missing_assets_as_invalid=False),
                    character=data["Character"],
                    scale=data["Scale"][0],
                )
            except (InvalidAssetId, UnknownAssetId) as e:
                raise RuntimeError("Unable to convert {}: {}".format(data["Name"], e))
    end = time.time()
    logging.info(f"Time took: {end - start}")

    start = time.time()
    converted_dependencies = all_converted_dependencies(converter)

    new_id_to_old = {
        new_id: old_id
        for (_, old_id), new_id in converter.converted_ids.items()
    }

    unique_anim = {}
    unique_evnt = []
    dont_delete = []
    for anim in converter.converted_assets.values():
        if anim.type != "ANIM":
            continue

        for ancs in converter.converted_assets.values():
            if ancs.type != "ANCS":
                continue

            # If the anim is a dependency of the ancs
            if any(anim.id in x for x in converted_dependencies[ancs.id]):
                for dep in converted_dependencies[ancs.id]:
                    if anim.id in unique_anim:
                        for depb in converted_dependencies[ancs.id]:
                            if depb.type == "EVNT":
                                if depb.id not in unique_evnt:
                                    unique_evnt.append(depb.id)
                                    converted_dependencies[ancs.id].remove(depb)
                                    converted_dependencies[ancs.id].add(Dependency("EVNT", unique_anim[anim.id]))
                                else:
                                    dont_delete.append(depb.id)
                        continue
                    if dep.type == "EVNT":
                        unique_anim[anim.id] = dep.id
                        anim.resource["anim"]["event_id"] = dep.id
                        break

    deleted_evnts = []
    for id in unique_evnt:
        if id not in dont_delete:
            deleted_evnts.append(id)
    for id in deleted_evnts:
        converter.converted_assets.pop(id)

    output_path.mkdir(exist_ok=True, parents=True)
    with output_path.joinpath("meta.json").open("w") as meta_out:
        metadata = {
            "version": ECHOES_MODELS_VERSION,
            "items": {
                name: {
                    "ancs": asset.ancs,
                    "cmdl": asset.cmdl,
                    "character": asset.character,
                    "scale": asset.scale,
                }
                for name, asset in result.items()
            },
            "new_assets": [
                {
                    "old_id": new_id_to_old.get(asset.id),
                    "new_id": asset.id,
                    "type": asset.type,
                    "dependencies": [
                        {"type": dep.type, "id": dep.id}
                        for dep in converted_dependencies[asset.id]
                    ]
                }
                for asset in converter.converted_assets.values()
            ],
        }
        json.dump(metadata, meta_out, indent=4)

    for asset in converter.converted_assets.values():
        assetdata = format_for(asset.type).build(asset.resource, target_game=Game.PRIME)
        if len(assetdata) % 32 != 0:
            assetdata += b"\xFF" * (32 - (len(assetdata) % 32))
        output_path.joinpath(f"{asset.id}.{asset.type.upper()}").write_bytes(
            assetdata
        )

    logging.info(f"Time took: {time.time() - start}")
    return metadata
    # Assert
    assert final_data == original_data


def _comparable_dict(value):
    if isinstance(value, dict):
        return [(key, _comparable_dict(item)) for key, item in value.items()]

    if isinstance(value, list):
        return [_comparable_dict(item) for item in value]

    return value


@pytest.mark.skipif(
    not get_data_path().joinpath("json_data", "prime2.json").is_file(),
    reason="Missing prime2.json")
def test_full_data_encode_is_equal():
    # The prime2.json may be missing if we're running using a Pyinstaller binary
    # Setup
    json_database_file = get_data_path().joinpath("json_data", "prime2.json")

    with json_database_file.open("r") as open_file:
        json_database = json.load(open_file)

    b = io.BytesIO()
    binary_data.encode(json_database, b)

    b.seek(0)
    decoded_database = binary_data.decode(b)