oot: bugfixes ()

* oot client: check types of tables coming from lua script for safety
There was a reported bug with corrupted (?) slot data preventing locations sending. This should safeguard against any instances of that happening in the future, if it ever happens again.

* oot: repair minor hint issues
SMW has # in some location names which breaks ootr's poor text formatting system, so those need to be filtered out.
Also replaces "[X] for [player Y]" with "[player Y]'s X" as frequently requested.

* oot: update required client version

* oot client: fix patching filename bug

* oot: fix broken poachers saw item
how was I this stupid, seriously

* oot: sanitize player, location, and item names everywhere
This commit is contained in:
espeon65536 2023-04-15 17:45:31 -06:00 committed by GitHub
parent ef211da27f
commit 89ec31708e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 33 additions and 15 deletions

View File

@ -179,6 +179,12 @@ async def parse_payload(payload: dict, ctx: OoTContext, force: bool):
locations = payload['locations']
collectibles = payload['collectibles']
# The Lua JSON library serializes an empty table into a list instead of a dict. Verify types for safety:
if isinstance(locations, list):
locations = {}
if isinstance(collectibles, list):
collectibles = {}
if ctx.location_table != locations or ctx.collectible_table != collectibles:
ctx.location_table = locations
ctx.collectible_table = collectibles
@ -293,10 +299,15 @@ async def patch_and_run_game(apz5_file):
if not os.path.exists(rom_file_name):
rom_file_name = Utils.user_path(rom_file_name)
rom = Rom(rom_file_name)
apply_patch_file(rom, apz5_file,
sub_file=(os.path.basename(base_name) + '.zpf'
if zipfile.is_zipfile(apz5_file)
else None))
sub_file = None
if zipfile.is_zipfile(apz5_file):
for name in zipfile.ZipFile(apz5_file).namelist():
if name.endswith('.zpf'):
sub_file = name
break
apply_patch_file(rom, apz5_file, sub_file=sub_file)
rom.write_to_file(decomp_path)
os.chdir(data_path("Compress"))
compress_rom_file(decomp_path, comp_path)

View File

@ -15,7 +15,7 @@ from .Items import OOTItem
from .HintList import getHint, getHintGroup, Hint, hintExclusions, \
misc_item_hint_table, misc_location_hint_table
from .Messages import COLOR_MAP, update_message_by_id
from .TextBox import line_wrap
from .TextBox import line_wrap, character_table, rom_safe_text
from .Utils import data_path, read_json
@ -149,11 +149,11 @@ def isRestrictedDungeonItem(dungeon, item):
# Attach a player name to the item or location text.
# If the associated player of the item/location and the world are the same, does nothing.
# Otherwise, attaches the object's player's name to the string.
# Otherwise, attaches the object's player's name to the string, calling rom_safe_text for foreign items/locations.
def attach_name(text, hinted_object, world):
if hinted_object.player == world.player:
return text
return f"{text} for {world.multiworld.get_player_name(hinted_object.player)}"
return rom_safe_text(f"{world.multiworld.get_player_name(hinted_object.player)}'s {text}")
def add_hint(world, groups, gossip_text, count, location=None, force_reachable=False):
@ -1144,7 +1144,7 @@ def buildMiscItemHints(world, messages):
area = HintArea.at(location, use_alt_hint=data['use_alt_hint']).text(world.clearer_hints, world=None)
else:
area = location.name
text = data['default_item_text'].format(area=(player_text + area))
text = data['default_item_text'].format(area=rom_safe_text(player_text + area))
elif 'default_item_fallback' in data:
text = data['default_item_fallback']
else:
@ -1167,7 +1167,7 @@ def buildMiscLocationHints(world, messages):
item_text = getHint(getItemGenericName(item), world.clearer_hints).text
if item.player != world.player:
item_text += f' for {world.multiworld.get_player_name(item.player)}'
text = data['location_text'].format(item=item_text)
text = data['location_text'].format(item=rom_safe_text(item_text))
update_message_by_id(messages, data['id'], str(GossipText(text, ['Green'], prefix='')), 0x23)

View File

@ -1037,7 +1037,7 @@ class AdultTradeStart(OptionSet):
"Pocket Cucco",
"Cojiro",
"Odd Mushroom",
"Poacher's Saw",
"Poachers Saw",
"Broken Sword",
"Prescription",
"Eyeball Frog",

View File

@ -25,7 +25,7 @@ from .Rom import Rom
from .SaveContext import SaveContext, Scenes, FlagType
from .SceneFlags import get_alt_list_bytes, get_collectible_flag_table, get_collectible_flag_table_bytes, \
get_collectible_flag_addresses
from .TextBox import character_table, NORMAL_LINE_WIDTH
from .TextBox import character_table, NORMAL_LINE_WIDTH, rom_safe_text
from .texture_util import ci4_rgba16patch_to_ci8, rgba16_patch
from .Utils import __version__
@ -2771,7 +2771,7 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F
split_item_name[0] = create_fake_name(split_item_name[0])
if len(world.multiworld.worlds) > 1: # OOTWorld.MultiWorld.AutoWorld[]
description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1], world.multiworld.get_player_name(location.item.player))
description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1], rom_safe_text(world.multiworld.get_player_name(location.item.player)))
else:
description_text = '\x08\x05\x41%s %d Rupees\x01%s\x01\x05\x40Special deal! ONE LEFT!\x01Get it while it lasts!\x09\x0A\x02' % (split_item_name[0], location.price, split_item_name[1])
purchase_text = '\x08%s %d Rupees\x09\x01%s\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02' % (split_item_name[0], location.price, split_item_name[1])
@ -2785,9 +2785,9 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F
shop_item_name = create_fake_name(shop_item_name)
if len(world.multiworld.worlds) > 1:
shop_item_name = ''.join(filter(lambda char: char in character_table, shop_item_name))
shop_item_name = rom_safe_text(shop_item_name)
do_line_break = sum(character_table[char] for char in f"{shop_item_name} {location.price} Rupees") > NORMAL_LINE_WIDTH
description_text = '\x08\x05\x41%s%s%d Rupees\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (shop_item_name, '\x01' if do_line_break else ' ', location.price, world.multiworld.get_player_name(location.item.player))
description_text = '\x08\x05\x41%s%s%d Rupees\x01\x05\x42%s\x05\x40\x01Special deal! ONE LEFT!\x09\x0A\x02' % (shop_item_name, '\x01' if do_line_break else ' ', location.price, rom_safe_text(world.multiworld.get_player_name(location.item.player)))
else:
description_text = '\x08\x05\x41%s %d Rupees\x01\x05\x40Special deal! ONE LEFT!\x01Get it while it lasts!\x09\x0A\x02' % (shop_item_name, location.price)
purchase_text = '\x08%s %d Rupees\x09\x01\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x02' % (shop_item_name, location.price)

View File

@ -367,3 +367,10 @@ def test_support_long_words():
print('"Support Long Words" test failed: Got ' + result + ', wanted ' + expected)
else:
print('"Support Long Words" test passed!')
# AP additions
rom_safe_lambda = lambda c: c if c in character_table else '?'
def rom_safe_text(text):
return ''.join(map(rom_safe_lambda, text))

View File

@ -118,7 +118,7 @@ class OOTWorld(World):
data_version = 3
required_client_version = (0, 3, 7)
required_client_version = (0, 4, 0)
item_name_groups = {
# internal groups