def test_prime_thing(default_prime_configuration): config = { "mode": "two-way", "types_state": { "door": { "can_change_from": [ "Ice Door", "Missile Blast Shield", "Normal Door", "Plasma Door", "Wave Door" ], "can_change_to": [ "Ice Door", "Ice Spreader Blast Shield", "Missile Blast Shield (randomprime)", "Normal Door", "Plasma Door", "Power Bomb Blast Shield", "Super Missile Blast Shield", "Wave Door" ] }, "morph_ball": { "can_change_from": [], "can_change_to": [] }, "other": { "can_change_from": [], "can_change_to": [] } } } ref = {"reference": default_prime_configuration.dock_rando} dc = DockRandoConfiguration.from_json(config, RandovaniaGame.METROID_PRIME) encoded = bitpacking.pack_value(dc, metadata=ref) decoder = BitPackDecoder(encoded) decoded = DockRandoConfiguration.bit_pack_unpack(decoder, ref) assert dc == decoded
def as_str(self) -> str: try: b = bitpacking.pack_value(self) b += bytes([single_byte_hash(b)]) return base64.b64encode(b).decode("utf-8") except ValueError as e: return "Unable to create Permalink: {}".format(e)
def as_bytes(self) -> bytes: key = "__cached_as_bytes" result = object.__getattribute__(self, key) if result is None: result = bitpacking.pack_value(self) object.__setattr__(self, key, result) return result
def test_encode_prime3(prime3_data): # Setup expected, default, value = prime3_data # Run result = bitpacking.pack_value(value, {"reference": default}) # Assert assert result == expected
def test_encode(config_with_data): # Setup expected, value = config_with_data # Run result = bitpacking.pack_value(value) # Assert assert result == expected
def test_encode(config_with_data): # Setup expected, value, reference = config_with_data # Run result = bitpacking.pack_value(value, metadata={"reference": reference}) # Assert assert result == expected
def test_encode(state_with_data): # Setup expected, value, ammo = state_with_data # Run result = bitpacking.pack_value(value, {"ammo": ammo}) # Assert assert result == expected
def test_encode(layout_config_with_data): # Setup expected, value, expected_json = layout_config_with_data # Run result = bitpacking.pack_value(value) as_json = value.as_json # Assert assert result == expected assert as_json == expected_json
def as_str(self) -> str: cached_result = object.__getattribute__(self, "__cached_as_str") if cached_result is not None: return cached_result try: b = bitpacking.pack_value(self) b += bytes([single_byte_hash(b)]) result = base64.b64encode(b).decode("utf-8") object.__setattr__(self, "__cached_as_str", result) return result except ValueError as e: return "Unable to create Permalink: {}".format(e)
def serialize(patches: GamePatches, game_data: dict) -> dict: """ Encodes a given GamePatches into a JSON-serializable dict. :param patches: :param game_data: :return: """ game = data_reader.decode_data(game_data) world_list = game.world_list ordered_pickups = [] result = { "starting_location": world_list.area_name( world_list.area_by_area_location(patches.starting_location)), "starting_items": { resource_info.long_name: quantity for resource_info, quantity in patches.starting_items.items() }, "elevators": { world_list.area_name( world_list.nodes_to_area( _find_node_with_teleporter(world_list, teleporter_id))): world_list.area_name( world_list.nodes_to_area( world_list.resolve_teleporter_connection(connection))) for teleporter_id, connection in patches.elevator_connection.items() }, "translators": { _name_for_gate(gate): requirement.long_name for gate, requirement in patches.translator_gates.items() }, "locations": { key: value for key, value in _pickup_assignment_to_item_locations( world_list, patches.pickup_assignment, ordered_pickups).items() }, "hints": { str(asset.asset_id): hint.as_json for asset, hint in patches.hints.items() } } b = bitpacking.pack_value( BitPackPickupEntryList(ordered_pickups, game.resource_database)) result["_locations_internal"] = base64.b64encode(b).decode("utf-8") return result
def test_bit_pack_pickup_entry(has_convert: bool, echoes_resource_database): # Setup name = "Some Random Name" if has_convert: convert_resources = (ResourceConversion( find_resource_info_with_long_name(echoes_resource_database.item, "Morph Ball"), find_resource_info_with_long_name(echoes_resource_database.item, "Item Percentage")), ) else: convert_resources = () pickup = PickupEntry( name=name, model_index=26, item_category=ItemCategory.TEMPLE_KEY, broad_category=ItemCategory.KEY, resources=(ConditionalResources( "Morph Ball", None, ( (find_resource_info_with_long_name( echoes_resource_database.item, "Morph Ball"), 2), (find_resource_info_with_long_name( echoes_resource_database.item, "Item Percentage"), 5), ), ), ConditionalResources( "Grapple Beam", find_resource_info_with_long_name( echoes_resource_database.item, "Morph Ball"), ((find_resource_info_with_long_name( echoes_resource_database.item, "Grapple Beam"), 3), ), )), convert_resources=convert_resources) # Run encoded = bitpacking.pack_value( BitPackPickupEntry(pickup, echoes_resource_database)) decoder = BitPackDecoder(encoded) decoded = BitPackPickupEntry.bit_pack_unpack(decoder, name, echoes_resource_database) # Assert assert pickup == decoded
def test_bit_pack_pickup_entry(has_convert: bool, echoes_resource_database, generic_item_category): # Setup name = "Some Random Name" if has_convert: resource_lock = ResourceLock( find_resource_info_with_long_name(echoes_resource_database.item, "Morph Ball"), find_resource_info_with_long_name(echoes_resource_database.item, "Item Percentage"), find_resource_info_with_long_name(echoes_resource_database.item, "Space Jump Boots"), ) else: resource_lock = None pickup = PickupEntry( name=name, model=PickupModel( game=RandovaniaGame.METROID_PRIME_CORRUPTION, name="HyperMissile", ), item_category=generic_item_category, broad_category=generic_item_category, progression=( (find_resource_info_with_long_name(echoes_resource_database.item, "Morph Ball"), 1), (find_resource_info_with_long_name(echoes_resource_database.item, "Grapple Beam"), 1), ), extra_resources=((find_resource_info_with_long_name( echoes_resource_database.item, "Item Percentage"), 5), ), resource_lock=resource_lock) # Run encoded = bitpacking.pack_value( BitPackPickupEntry(pickup, echoes_resource_database)) decoder = BitPackDecoder(encoded) decoded = BitPackPickupEntry.bit_pack_unpack(decoder, echoes_resource_database) # Assert assert pickup == decoded
def as_bytes(self) -> bytes: cached_result = object.__getattribute__(self, "__cached_as_bytes") if cached_result is not None: return cached_result encoded = bitpacking.pack_value(self) # Add extra bytes so the base64 encoding never uses == at the end encoded += b"\x00" * (3 - (len(encoded) + 1) % 3) # Rotate bytes, so the slightest change causes a cascading effect. # But skip the first byte so the version check can be done early in decoding. byte_hash = single_byte_hash(encoded) new_bytes = [encoded[0]] new_bytes.extend(rotate_bytes(encoded[1:], byte_hash, byte_hash, inverse=False)) # Append the hash, so the rotation can be reversed and the checksum verified new_bytes.append(byte_hash) result = bytes(new_bytes) object.__setattr__(self, "__cached_as_bytes", result) return result
def _base64_encode_pickup(pickup: PickupEntry, resource_database: ResourceDatabase) -> str: encoded_pickup = bitpacking.pack_value( BitPackPickupEntry(pickup, resource_database)) return base64.b85encode(encoded_pickup).decode("utf-8")
def test_compare_encode_round_trip(pickup_quantities: Dict[str, int]): original = PickupQuantities.from_params(pickup_quantities) encoded = pack_value(original) decoded = PickupQuantities.bit_pack_unpack(BitPackDecoder(encoded)) assert original == decoded