Fix gen2 japanese

Start implementing sanity checks for patch sets
This commit is contained in:
Lorenzo Carletti 2022-05-28 02:47:29 +02:00
parent 362caa5c1c
commit 0765b746db
14 changed files with 185 additions and 104 deletions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,2 @@

$&(*,.0345678:;<=>?@ABCDEFGHIJKLMNTVXZ\^`cdefghjklmnopqrstuvwxyz{|}~剢垔寧悡敃枟槡洔潪煚、¥ウЖ┆<D096><E29486>炊负季烂呐魄仁颂臀闲岩釉罩棕仝圮蒉滏桕祛痼趱鲼<E8B6B1><E9B2BC>

View File

@ -0,0 +1,2 @@

 "$

Binary file not shown.

View File

@ -0,0 +1 @@
  !$&(*,/9:<=>?@ABCDEFGHIJKLMPRTVX[efhijklmnopqrstuvwxy|~<7E>굜뇫뮅븭뿕솞썫씆윝、ⅷが<E285B7>낸씬좌쳐탬픽<ED83AC>盖誥國喇路舶笙庾狀穽增彩充坂沆樺

View File

@ -0,0 +1,2 @@


View File

@ -375,8 +375,6 @@ class GSCTrading:
max_consecutive_no_data = 0x100
next_section = 0xFD
mail_next_section = 0x20
patch_set_base_pos = [0x13, 0]
patch_set_start_info_pos = [7, 0x11A]
no_input = 0xFE
no_input_alternative = 0xFF
no_data = 0
@ -505,6 +503,7 @@ class GSCTrading:
# Prepare sanity checks stuff
self.checks.prepare_text_buffer()
checks.prepare_patch_sets_buffer()
self.checks.prepare_species_buffer()
if not buffered:
@ -956,28 +955,6 @@ class GSCTrading:
if self.exit_or_new:
self.verbose_print(GSCTradingStrings.sit_table_str)
return self.send_predefined_section(self.start_trading_states, die_on_no_data=True)
def apply_patches(self, data, patch_set, is_mail=False):
"""
Applies patch data (turns the previously read data into 0xFE)
"""
patch_sets_num = 2
patch_sets_index = 0
if is_mail:
patch_sets_num = 1
patch_sets_index = 1
base = self.patch_set_base_pos[patch_sets_index]
start = self.patch_set_start_info_pos[patch_sets_index]
i = 0
while (patch_sets_num > 0) and ((start+i) < len(patch_set)):
read_pos = patch_set[start+i]
i += 1
if read_pos == 0xFF:
patch_sets_num -= 1
base += 0xFC
elif read_pos > 0 and (read_pos+base) < len(data):
data[read_pos+base-1] = 0xFE
def trade_starting_sequence(self, buffered, send_data = [None, None, None, None]):
"""
@ -992,10 +969,10 @@ class GSCTrading:
# Send and get the first two sections
random_data, random_data_other = self.read_section(0, send_data[0], buffered)
pokemon_data, pokemon_data_other = self.read_section(1, send_data[1], buffered)
# Get and apply patches for the Pokémon data
patches_data, patches_data_other = self.read_section(2, send_data[2], buffered)
self.apply_patches(pokemon_data, patches_data)
self.apply_patches(pokemon_data_other, patches_data_other)
self.utils_class.apply_patches(pokemon_data, patches_data)
self.utils_class.apply_patches(pokemon_data_other, patches_data_other)
pokemon_own = self.party_reader(pokemon_data)
pokemon_other = self.party_reader(pokemon_data_other)
@ -1006,7 +983,6 @@ class GSCTrading:
if (pokemon_own_mail and pokemon_other_mail) or buffered:
send_data[3] = self.convert_mail_data(send_data[3], True)
mail_data, mail_data_other = self.read_section(self.get_mail_section_id(), send_data[3], buffered)
self.apply_patches(mail_data, mail_data, is_mail=True)
mail_data = self.convert_mail_data(mail_data, False)
else:
send_data[3] = self.utils_class.no_mail_section
@ -1020,7 +996,6 @@ class GSCTrading:
# Exchange mail data with the device
send_data[3] = self.convert_mail_data(send_data[3], True)
mail_data, mail_data_other = self.read_section(self.get_mail_section_id(), send_data[3], True)
self.apply_patches(mail_data, mail_data, is_mail=True)
mail_data = self.convert_mail_data(mail_data, False)
# Send mail data if only this client has it
@ -1028,6 +1003,10 @@ class GSCTrading:
self.verbose_print(GSCTradingStrings.send_mail_other_data_str)
self.comms.send_mail_data_only(mail_data)
# Apply patches for the mail data
self.utils_class.apply_patches(mail_data, mail_data, is_mail=True)
self.utils_class.apply_patches(mail_data_other, mail_data_other, is_mail=True)
return [random_data, pokemon_data, mail_data], [random_data_other, pokemon_data_other, mail_data_other]
def synchronous_trade(self):
@ -1041,7 +1020,7 @@ class GSCTrading:
# Generate the trading data for the device
# from the other player's one and use it
self.verbose_print(GSCTradingStrings.recycle_data_str)
data, data_other = self.trade_starting_sequence(True, send_data=self.other_pokemon.create_trading_data(self.special_sections_len, self.patch_set_base_pos, self.patch_set_start_info_pos))
data, data_other = self.trade_starting_sequence(True, send_data=self.other_pokemon.create_trading_data(self.special_sections_len))
self.own_pokemon = self.party_reader(data[1], data_mail=data[2])
self.other_pokemon = self.party_reader(data_other[1], data_mail=data_other[2])
return True
@ -1061,7 +1040,7 @@ class GSCTrading:
else:
# Generate the trading data for the device
# from the other player's one and use it
data = self.other_pokemon.create_trading_data(self.special_sections_len, self.patch_set_base_pos, self.patch_set_start_info_pos)
data = self.other_pokemon.create_trading_data(self.special_sections_len)
valid = True
self.verbose_print(GSCTradingStrings.recycle_data_str)
data, data_other = self.trade_starting_sequence(True, send_data=data)
@ -1108,7 +1087,7 @@ class GSCTrading:
# Generate the trading data for the device
# from the other player's one and use it
self.verbose_print(GSCTradingStrings.reuse_data_str)
data, data_other = self.trade_starting_sequence(True, send_data=self.other_pokemon.create_trading_data(self.special_sections_len, self.patch_set_base_pos, self.patch_set_start_info_pos))
data, data_other = self.trade_starting_sequence(True, send_data=self.other_pokemon.create_trading_data(self.special_sections_len))
# If only this client requires user inputs,
# send its data
@ -1145,7 +1124,7 @@ class GSCTrading:
self.other_pokemon = self.force_receive(self.comms.get_pool_trading_data)
else:
self.verbose_print(GSCTradingStrings.pool_recycle_data_str)
data, data_other = self.trade_starting_sequence(True, send_data=self.other_pokemon.create_trading_data(self.special_sections_len, self.patch_set_base_pos, self.patch_set_start_info_pos))
data, data_other = self.trade_starting_sequence(True, send_data=self.other_pokemon.create_trading_data(self.special_sections_len))
self.own_pokemon = self.party_reader(data[1], data_mail=data[2])
# Start interacting with the trading menu

View File

@ -132,6 +132,8 @@ class GSCUtils:
stat_id_base_conv_table = [0,1,2,5,3,4]
stat_id_iv_conv_table = [0,0,1,2,3,3]
stat_id_exp_conv_table = [0,1,2,3,4,4]
patch_set_base_pos = [0x13, 0, 0]
patch_set_start_info_pos = [7, 0x11A, 0xFC]
egg_value = 0x38
min_level = 2
max_level = 100
@ -233,6 +235,62 @@ class GSCUtils:
def get_evolution_item(species):
return GSCUtils.evolution_ids[species][1]
def get_patch_set_num_index(is_mail, is_japanese):
patch_sets_num = 2
patch_sets_index = 0
if is_mail:
patch_sets_num = 1
patch_sets_index = 1
if is_japanese:
patch_sets_index = 2
return patch_sets_num, patch_sets_index
def apply_patches(data, patch_set, utils_class, is_mail=False, is_japanese=False):
"""
Applies patch data (turns the previously read data into 0xFE)
"""
patch_sets_num, patch_sets_index = utils_class.get_patch_set_num_index(is_mail, is_japanese)
base = utils_class.patch_set_base_pos[patch_sets_index]
start = utils_class.patch_set_start_info_pos[patch_sets_index]
i = 0
while (patch_sets_num > 0) and ((start+i) < len(patch_set)):
read_pos = patch_set[start+i]
i += 1
if read_pos == 0xFF:
patch_sets_num -= 1
base += 0xFC
elif read_pos > 0 and (read_pos+base) < len(data):
data[read_pos+base-1] = 0xFE
def create_patches_data(data, patch_set, utils_class, is_mail=False, is_japanese=False):
"""
Creates patch data (turns 0xFE into a patch offset)
"""
patch_sets_num, patch_sets_index = utils_class.get_patch_set_num_index(is_mail, is_japanese)
base = utils_class.patch_set_base_pos[patch_sets_index]
start = utils_class.patch_set_start_info_pos[patch_sets_index]
i = 0
j = 0
while (patch_sets_num > 0) and ((start+i) < len(patch_set)) and ((base+j) < len(data)):
read_data = data[base+j]
if read_data == 0xFE:
data[base+j] = 0xFF
patch_set[start+i] = j+1
i+=1
j += 1
if j == 0xFC:
base += 0xFC
j = 0
patch_set[start+i] = 0xFF
i+=1
patch_sets_num -= 1
if j != 0:
patch_set[start+i] = 0xFF
i+=1
def single_mon_from_data(checks, data):
ret = None
@ -240,6 +298,7 @@ class GSCUtils:
checks.reset_species_item_list()
checks.set_single_team_size()
checks.prepare_text_buffer()
checks.prepare_patch_sets_buffer()
checks.prepare_species_buffer()
checker = checks.single_pokemon_checks_map
@ -824,6 +883,7 @@ class GSCTradingData:
checks.reset_species_item_list()
checks.set_single_team_size()
checks.prepare_text_buffer()
checks.prepare_patch_sets_buffer()
checks.prepare_species_buffer()
# Apply checks
@ -854,39 +914,7 @@ class GSCTradingData:
self.party_info.set_id(self.get_last_mon_index(), pa_info)
self.pokemon[self.get_last_mon_index()] = po_data
def create_patches_data(self, data, patch_set, patch_base, patch_start_info, is_mail=False):
"""
Creates patch data (turns 0xFE into a patch offset)
"""
patch_sets_num = 2
patch_sets_index = 0
if is_mail:
patch_sets_num = 1
patch_sets_index = 1
base = patch_base[patch_sets_index]
start = patch_start_info[patch_sets_index]
i = 0
j = 0
while (patch_sets_num > 0) and ((start+i) < len(patch_set)) and ((base+j) < len(data)):
read_data = data[base+j]
if read_data == 0xFE:
data[base+j] = 0xFF
patch_set[start+i] = j+1
i+=1
j += 1
if j == 0xFC:
base += 0xFC
j = 0
patch_set[start+i] = 0xFF
i+=1
patch_sets_num -= 1
if j != 0:
patch_set[start+i] = 0xFF
i+=1
def create_trading_data(self, lengths, patch_base, patch_start_info):
def create_trading_data(self, lengths):
"""
Creates the data which can be loaded to the hardware.
"""
@ -907,8 +935,8 @@ class GSCTradingData:
if self.pokemon[i].mail is not None:
GSCUtilsMisc.copy_to_data(data[3], self.trading_pokemon_mail_pos + (i * self.trading_mail_length), self.pokemon[i].mail.values)
GSCUtilsMisc.copy_to_data(data[3], self.trading_pokemon_mail_sender_pos + (i * self.trading_mail_sender_length), self.pokemon[i].mail_sender.values, self.trading_mail_sender_length)
self.create_patches_data(data[1], data[2], patch_base, patch_start_info)
self.create_patches_data(data[3], data[3], patch_base, patch_start_info, is_mail=True)
self.utils_class.create_patches_data(data[1], data[2])
self.utils_class.create_patches_data(data[3], data[3], is_mail=True)
return data
class GSCChecks:
@ -925,10 +953,18 @@ class GSCChecks:
checks_map_path = "checks_map.bin"
single_pokemon_checks_map_path = "single_pokemon_checks_map.bin"
moves_checks_map_path = "moves_checks_map.bin"
pokemon_patch_set_0_path = "pokemon_patch_set_0.bin"
pokemon_patch_set_1_path = "pokemon_patch_set_1.bin"
mail_patch_set_path = "mail_patch_set.bin"
japanese_mail_patch_set_path = "japanese_mail_patch_set.bin"
curr_exp_pos_masks = [0, 0xFF0000, 0xFFFF00]
free_value_species = 0xFF
empty_value_species = 0
free_value_moves = 0
no_conversion_patch = 0
end_of_patch = 0xFF
patch_set_cover = 0xFC
tackle_id = 0x21
rattata_id = 0x13
question_mark = 0xE6
@ -941,6 +977,9 @@ class GSCChecks:
self.bad_ids_moves = GSCUtilsLoaders.prepare_check_list(GSCUtilsMisc.read_data(self.get_path(self.bad_ids_moves_path)))
self.bad_ids_pokemon = GSCUtilsLoaders.prepare_check_list(GSCUtilsMisc.read_data(self.get_path(self.bad_ids_pokemon_path)))
self.bad_ids_text = GSCUtilsLoaders.prepare_check_list(GSCUtilsMisc.read_data(self.get_path(self.bad_ids_text_path)))
self.pokemon_patch_sets = [GSCUtilsLoaders.prepare_check_list(GSCUtilsMisc.read_data(self.get_path(self.pokemon_patch_set_0_path))), GSCUtilsLoaders.prepare_check_list(GSCUtilsMisc.read_data(self.get_path(self.pokemon_patch_set_1_path)))]
self.mail_patch_set = [GSCUtilsLoaders.prepare_check_list(GSCUtilsMisc.read_data(self.get_path(self.mail_patch_set_path)))]
self.japanese_mail_patch_set = [GSCUtilsLoaders.prepare_check_list(GSCUtilsMisc.read_data(self.get_path(self.mail_patch_set_path)))]
self.check_functions = [
self.clean_nothing,
self.clean_text,
@ -964,7 +1003,10 @@ class GSCChecks:
self.clean_species_force_terminate,
self.clean_mail_species,
self.clean_mail_item,
self.clean_mail_same_species
self.clean_mail_same_species,
self.clean_pokemon_patch_set,
self.clean_mail_patch_set,
self.clean_japanese_mail_patch_set
]
self.checks_map = self.prepare_checks_map(GSCUtilsMisc.read_data(self.get_path(self.checks_map_path)), section_sizes, self.check_functions)
self.single_pokemon_checks_map = GSCUtilsLoaders.prepare_functions_map(GSCUtilsMisc.read_data(self.get_path(self.single_pokemon_checks_map_path)), self.check_functions)
@ -994,12 +1036,6 @@ class GSCChecks:
else:
return True
return wrapper
def apply_checks_to_data(self, checker, data):
new_data = list(data)
for j in range(len(checker)):
new_data[j] = checker[j](data[j])
return new_data
def prepare_text_buffer(self):
self.curr_text = []
@ -1019,6 +1055,9 @@ class GSCChecks:
def prepare_species_buffer(self):
self.curr_species_pos = 0
def prepare_patch_sets_buffer(self):
self.curr_patch_set = 0
def prepare_checks_map(self, data, lengths, functions_list):
raw_data_sections = GSCUtilsMisc.divide_data(data, lengths)
@ -1026,6 +1065,12 @@ class GSCChecks:
for i in range(len(raw_data_sections)):
call_map[i] = GSCUtilsLoaders.prepare_functions_map(raw_data_sections[i], functions_list)
return call_map
def prepare_patch_set(self, data):
new_data = [False] * self.patch_set_cover
for d in data:
new_data[d] = True
return new_data
@clean_check_sanity_checks
def clean_nothing(self, val):
@ -1251,6 +1296,26 @@ class GSCChecks:
def clean_egg_cycles_friendship(self, cycles_friendship):
return cycles_friendship
def check_patch_set(self, val, patch_sets):
if self.curr_patch_set >= len(patch_sets):
return self.no_conversion_patch
if val == self.end_of_patch
self.curr_patch_set += 1
return val
return GSCUtilsMisc.check_normal_list(patch_sets[self.curr_patch_set], val)
@clean_check_sanity_checks
def clean_pokemon_patch_set(self, val):
return self.check_patch_set(val, self.pokemon_patch_sets)
@clean_check_sanity_checks
def clean_mail_patch_set(self, val):
return self.check_patch_set(val, self.mail_patch_set)
@clean_check_sanity_checks
def clean_japanese_mail_patch_set(self, val):
return self.check_patch_set(val, self.japanese_mail_patch_set)
def clean_value(self, value, checker, default_value):
if checker(value):
return value

View File

@ -19,36 +19,36 @@ class GSCJPMailConverter:
mail_len = 0x21
sender_int_len = 0xE
mail_pos_jp = [
0xCB + (0*full_mail_jp_len),
0xCB + (1*full_mail_jp_len),
0xCB + (2*full_mail_jp_len),
0xCB + (3*full_mail_jp_len),
0xCB + (4*full_mail_jp_len),
0xCB + (5*full_mail_jp_len)
(0*full_mail_jp_len),
(1*full_mail_jp_len),
(2*full_mail_jp_len),
(3*full_mail_jp_len),
(4*full_mail_jp_len),
(5*full_mail_jp_len)
]
mail_pos_int = [
0xCB + (0*mail_len),
0xCB + (1*mail_len),
0xCB + (2*mail_len),
0xCB + (3*mail_len),
0xCB + (4*mail_len),
0xCB + (5*mail_len)
(0*mail_len),
(1*mail_len),
(2*mail_len),
(3*mail_len),
(4*mail_len),
(5*mail_len)
]
sender_pos_jp = [
0xEC + (0*full_mail_jp_len),
0xEC + (1*full_mail_jp_len),
0xEC + (2*full_mail_jp_len),
0xEC + (3*full_mail_jp_len),
0xEC + (4*full_mail_jp_len),
0xEC + (5*full_mail_jp_len)
0x21 + (0*full_mail_jp_len),
0x21 + (1*full_mail_jp_len),
0x21 + (2*full_mail_jp_len),
0x21 + (3*full_mail_jp_len),
0x21 + (4*full_mail_jp_len),
0x21 + (5*full_mail_jp_len)
]
sender_pos_int = [
0x191 + (0*sender_int_len),
0x191 + (1*sender_int_len),
0x191 + (2*sender_int_len),
0x191 + (3*sender_int_len),
0x191 + (4*sender_int_len),
0x191 + (5*sender_int_len)
0xC6 + (0*sender_int_len),
0xC6 + (1*sender_int_len),
0xC6 + (2*sender_int_len),
0xC6 + (3*sender_int_len),
0xC6 + (4*sender_int_len),
0xC6 + (5*sender_int_len)
]
def __init__(self, checks):
@ -130,6 +130,7 @@ class GSCTradingJP(GSCTrading):
Class which handles the trading process for the player.
"""
next_section = 0xFD
mail_next_section = 0x20
no_input = 0xFE
end_of_line = 0x50
single_text_len = 0xB
@ -149,14 +150,15 @@ class GSCTradingJP(GSCTrading):
0x13B + (single_text_len * 10): [5, end_of_line],
0x13B + (single_text_len * 11): [5, end_of_line]
}, {}, {}]
drop_bytes_checks = [[0xA, 0x1B9, 0x1E6, 0x1B8], [next_section, next_section, no_input, no_input], [0,4,0,0]]
special_sections_starter = [next_section, next_section, next_section, mail_next_section, mail_next_section]
drop_bytes_checks = [[0xA, 0x1B9, 0xC5, 0x181, 0x11D], [next_section, next_section, mail_next_section, no_input, no_input], [0,4,0,0,0]]
def __init__(self, sending_func, receiving_func, connection, menu, kill_function):
super(GSCTradingJP, self).__init__(sending_func, receiving_func, connection, menu, kill_function)
self.jp_mail_converter = GSCJPMailConverter(self.checks)
def get_mail_section_id(self):
return 3
return 4
def get_printable_index(self, index):
if index != self.get_mail_section_id():
@ -179,7 +181,11 @@ class GSCTradingJP(GSCTrading):
"""
if data is not None:
if to_device:
self.utils_class.apply_patches(data, data, is_mail=True)
data = self.jp_mail_converter.convert_to_jp(data)
self.utils_class.create_patches_data(data, data, is_mail=True, is_japanese=True)
else:
self.utils_class.apply_patches(data, data, is_mail=True, is_japanese=True)
data = self.jp_mail_converter.convert_to_int(data)
self.utils_class.create_patches_data(data, data, is_mail=True)
return data

View File

@ -81,8 +81,6 @@ class RBYTrading(GSCTrading):
next_section = 0xFD
no_input = 0xFE
drop_bytes_checks = [[0xA, 0x19F, 0xC5], [next_section, next_section, no_input], [0,0,0]]
patch_set_base_pos = [0x11, 0]
patch_set_start_info_pos = [7, 0x11A]
stop_trade = 0x6F
first_trade_index = 0x60
decline_trade = 0x61
@ -122,4 +120,4 @@ class RBYTrading(GSCTrading):
self.apply_patches(pokemon_data, patches_data)
self.apply_patches(pokemon_data_other, patches_data_other)
return [random_data, pokemon_data, []], [random_data_other, pokemon_data_other, []]
return [random_data, pokemon_data, None], [random_data_other, pokemon_data_other, None]

View File

@ -22,6 +22,8 @@ class RBYUtils(GSCUtils):
stat_id_base_conv_table = [0,1,2,3,4]
stat_id_iv_conv_table = [0,0,1,2,3]
stat_id_exp_conv_table = [0,1,2,3,4]
patch_set_base_pos = [0x13]
patch_set_start_info_pos = [7]
num_stats = 5
types_list = None
@ -52,6 +54,11 @@ class RBYUtils(GSCUtils):
def get_evolution_item(species):
return None
def get_patch_set_num_index(is_mail, is_japanese):
patch_sets_num = 2
patch_sets_index = 0
return patch_sets_num, patch_sets_index
def single_mon_from_data(checks, data):
ret = None
@ -59,6 +66,7 @@ class RBYUtils(GSCUtils):
checks.reset_species_item_list()
checks.set_single_team_size()
checks.prepare_text_buffer()
checks.prepare_patch_sets_buffer()
checks.prepare_species_buffer()
checker = checks.single_pokemon_checks_map
@ -226,6 +234,24 @@ class RBYTradingData(GSCTradingData):
return True
return False
def create_trading_data(self, lengths):
"""
Creates the data which can be loaded to the hardware.
"""
data = []
for i in range(3):
data += [lengths[i]*[0]]
GSCUtilsMisc.copy_to_data(data[1], self.trader_name_pos, self.trader.values, self.trading_name_length)
data[1][self.trading_party_info_pos] = self.get_party_size()
GSCUtilsMisc.copy_to_data(data[1], self.trading_party_info_pos + 1, self.party_info.actual_mons)
data[1][self.trading_party_final_pos] = 0xFF
for i in range(self.get_party_size()):
GSCUtilsMisc.copy_to_data(data[1], self.trading_pokemon_pos + (i * self.trading_pokemon_length), self.pokemon[i].values)
GSCUtilsMisc.copy_to_data(data[1], self.trading_pokemon_ot_pos + (i * self.trading_name_length), self.pokemon[i].ot_name.values, self.trading_name_length)
GSCUtilsMisc.copy_to_data(data[1], self.trading_pokemon_nickname_pos + (i * self.trading_name_length), self.pokemon[i].nickname.values, self.trading_name_length)
self.utils_class.create_patches_data(data[1], data[2])
return data
class RBYChecks(GSCChecks):
"""
Class which handles sanity checks and cleaning of the received data.