mirror of
https://github.com/Lorenzooone/PokemonGB_Online_Trades.git
synced 2026-03-21 18:04:50 -05:00
Start implementing patch sets
This commit is contained in:
parent
c05859808a
commit
f4ed93599c
|
|
@ -38,4 +38,6 @@ Egg cycles don't count down in the trading room.
|
|||
There are in-game checks which make sure you always have at least one alive mon.
|
||||
The time capsule won't evolve any Pokémon it recieves if they hold an Everstone...
|
||||
The time capsule won't evolve any Pokémon it recieves which couldn't evolve in Gen 1...
|
||||
The time capsule won't prevent learning new moves from Gen 2.
|
||||
The time capsule won't prevent learning new moves from Gen 2.
|
||||
|
||||
Patch set = where there would be a 0xFE. It's a list covering 0xFC consecutive bytes.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -22,11 +22,11 @@ class GSCTradingClient:
|
|||
buffered_transfer = "BUF2"
|
||||
negotiation_transfer = "NEG2"
|
||||
possible_transfers = {
|
||||
full_transfer: {0x412}, # Sum of special_sections_len
|
||||
full_transfer: {0x412, 0x40C}, # Sum of special_sections_len - Ver 1.0 and 2.0
|
||||
single_transfer: {7},
|
||||
pool_transfer: {1 + 0x75 + 1, 1 + 1}, # Counter + Single Pokémon + Egg OR Counter + Fail
|
||||
moves_transfer: {1 + 8}, # Counter + Moves
|
||||
mail_transfer : {1 + 0x24C}, # Counter + Mail
|
||||
mail_transfer : {1 + 0x24C, 1 + 0x181}, # Counter + Mail - Ver 1.0 and 2.0
|
||||
choice_transfer : {1 + 1 + 0x75 + 1, 1 + 1}, # Counter + Choice + Single Pokémon + Egg OR Counter + Stop
|
||||
accept_transfer : {1 + 1}, # Counter + Accept
|
||||
success_transfer : {1 + 1}, # Counter + Success
|
||||
|
|
@ -369,15 +369,19 @@ class GSCTrading:
|
|||
start_trading_states = [[0x75, 0x75, 0x76], [{0x75}, {0}, {0xFD}]]
|
||||
success_values = set(range(0x70, 0x80))
|
||||
possible_indexes = set(range(0x70, 0x80))
|
||||
fillers = [{}, {}, {}]
|
||||
fillers = [{}, {}, {}, {}]
|
||||
filler_value = 0xFE00
|
||||
last_filler_value = 0xFEFF
|
||||
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_data = 0
|
||||
special_sections_len = [0xA, 0x1BC, 0x24C]
|
||||
drop_bytes_checks = [[0xA, 0x1B9, 0x1E6], [next_section, next_section, no_input], [0,0,0]]
|
||||
special_sections_len = [0xA, 0x1BC, 0xC5, 0x181]
|
||||
special_sections_starter = [next_section, next_section, next_section, mail_next_section]
|
||||
drop_bytes_checks = [[0xA, 0x1B9, 0xC5, 0x181], [next_section, next_section, mail_next_section, no_input], [0,0,0,0]]
|
||||
stop_trade = 0x7F
|
||||
first_trade_index = 0x70
|
||||
decline_trade = 0x71
|
||||
|
|
@ -465,7 +469,7 @@ class GSCTrading:
|
|||
self.printed_warning_drop = True
|
||||
|
||||
def get_mail_section_id(self):
|
||||
return 2
|
||||
return 3
|
||||
|
||||
def get_printable_index(self, index):
|
||||
return index+1
|
||||
|
|
@ -487,7 +491,7 @@ class GSCTrading:
|
|||
Reads a data section and sends it to the device.
|
||||
"""
|
||||
length = self.get_section_length(index)
|
||||
next = self.next_section
|
||||
next = self.special_sections_starter[index]
|
||||
checker = self.get_checker(index)
|
||||
|
||||
# Prepare sanity checks stuff
|
||||
|
|
@ -517,7 +521,7 @@ class GSCTrading:
|
|||
self.verbose_print(GSCTradingStrings.arrived_synchro_str)
|
||||
|
||||
# Sync with the device and start the actual trade
|
||||
while next == self.next_section:
|
||||
while next == self.special_sections_starter[index]:
|
||||
next = self.swap_byte(next)
|
||||
# next now contains the first received byte from the device!
|
||||
|
||||
|
|
@ -943,8 +947,30 @@ 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
|
||||
|
||||
def trade_starting_sequence(self, buffered, send_data = [None, None, None]):
|
||||
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]):
|
||||
"""
|
||||
Handles exchanging with the device the three data sections which
|
||||
are needed in order to trade.
|
||||
|
|
@ -957,7 +983,11 @@ 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)
|
||||
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)
|
||||
|
||||
pokemon_own = self.party_reader(pokemon_data)
|
||||
pokemon_other = self.party_reader(pokemon_data_other)
|
||||
pokemon_own_mail = pokemon_own.party_has_mail()
|
||||
|
|
@ -965,21 +995,23 @@ class GSCTrading:
|
|||
|
||||
# Trade mail data only if needed
|
||||
if (pokemon_own_mail and pokemon_other_mail) or buffered:
|
||||
send_data[2] = self.convert_mail_data(send_data[2], True)
|
||||
mail_data, mail_data_other = self.read_section(self.get_mail_section_id(), send_data[2], 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[2] = self.utils_class.no_mail_section
|
||||
send_data[3] = self.utils_class.no_mail_section
|
||||
# Get mail data if only the other client has it
|
||||
if pokemon_other_mail:
|
||||
self.verbose_print(GSCTradingStrings.mail_other_data_str)
|
||||
send_data[2] = self.force_receive(self.comms.get_mail_data_only)
|
||||
send_data[3] = self.force_receive(self.comms.get_mail_data_only)
|
||||
else:
|
||||
self.verbose_print(GSCTradingStrings.no_mail_other_data_str)
|
||||
|
||||
# Exchange mail data with the device
|
||||
send_data[2] = self.convert_mail_data(send_data[2], True)
|
||||
mail_data, mail_data_other = self.read_section(self.get_mail_section_id(), send_data[2], True)
|
||||
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
|
||||
|
|
@ -1000,7 +1032,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))
|
||||
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))
|
||||
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
|
||||
|
|
@ -1020,7 +1052,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)
|
||||
data = self.other_pokemon.create_trading_data(self.special_sections_len, self.patch_set_base_pos, self.patch_set_start_info_pos)
|
||||
valid = True
|
||||
self.verbose_print(GSCTradingStrings.recycle_data_str)
|
||||
data, data_other = self.trade_starting_sequence(True, send_data=data)
|
||||
|
|
@ -1067,7 +1099,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))
|
||||
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))
|
||||
|
||||
# If only this client requires user inputs,
|
||||
# send its data
|
||||
|
|
@ -1104,7 +1136,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))
|
||||
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))
|
||||
self.own_pokemon = self.party_reader(data[1], data_mail=data[2])
|
||||
|
||||
# Start interacting with the trading menu
|
||||
|
|
|
|||
|
|
@ -696,8 +696,8 @@ class GSCTradingData:
|
|||
trading_pokemon_pos = 0x15
|
||||
trading_pokemon_ot_pos = 0x135
|
||||
trading_pokemon_nickname_pos = 0x177
|
||||
trading_pokemon_mail_pos = 0xCB
|
||||
trading_pokemon_mail_sender_pos = 0x191
|
||||
trading_pokemon_mail_pos = 0
|
||||
trading_pokemon_mail_sender_pos = 0xC6
|
||||
|
||||
trading_pokemon_length = 0x30
|
||||
trading_name_length = 0xB
|
||||
|
|
@ -854,12 +854,44 @@ 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_trading_data(self, lengths):
|
||||
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):
|
||||
"""
|
||||
Creates the data which can be loaded to the hardware.
|
||||
"""
|
||||
data = []
|
||||
for i in range(2):
|
||||
for i in range(3):
|
||||
data += [lengths[i]*[0]]
|
||||
data += [self.utils_class.no_mail_section[:len(self.utils_class.no_mail_section)]]
|
||||
GSCUtilsMisc.copy_to_data(data[1], self.trader_name_pos, self.trader.values, self.trading_name_length)
|
||||
|
|
@ -873,8 +905,10 @@ class GSCTradingData:
|
|||
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)
|
||||
if self.pokemon[i].mail is not None:
|
||||
GSCUtilsMisc.copy_to_data(data[2], self.trading_pokemon_mail_pos + (i * self.trading_mail_length), self.pokemon[i].mail.values)
|
||||
GSCUtilsMisc.copy_to_data(data[2], self.trading_pokemon_mail_sender_pos + (i * self.trading_mail_sender_length), self.pokemon[i].mail_sender.values, self.trading_mail_sender_length)
|
||||
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)
|
||||
return data
|
||||
|
||||
class GSCChecks:
|
||||
|
|
@ -948,9 +982,10 @@ class GSCChecks:
|
|||
self = args[0]
|
||||
val = args[1]
|
||||
if self.do_sanity_checks:
|
||||
return func(*args, **kwargs)
|
||||
else:
|
||||
return val
|
||||
val = func(*args, **kwargs)
|
||||
if val == 0xFE:
|
||||
return 0xFF
|
||||
return val
|
||||
return wrapper
|
||||
|
||||
def valid_check_sanity_checks(func):
|
||||
|
|
@ -989,7 +1024,7 @@ class GSCChecks:
|
|||
|
||||
def prepare_checks_map(self, data, lengths, functions_list):
|
||||
raw_data_sections = GSCUtilsMisc.divide_data(data, lengths)
|
||||
call_map = [[],[],[]]
|
||||
call_map = [[],[],[],[]]
|
||||
for i in range(len(raw_data_sections)):
|
||||
call_map[i] = GSCUtilsLoaders.prepare_functions_map(raw_data_sections[i], functions_list)
|
||||
return call_map
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class RBYTradingClient(GSCTradingClient):
|
|||
single_transfer = "SNG1"
|
||||
pool_transfer = "POL1"
|
||||
moves_transfer = "MVS1"
|
||||
mail_transfer = "MAI1"
|
||||
choice_transfer = "CHC1"
|
||||
accept_transfer = "ACP1"
|
||||
success_transfer = "SUC1"
|
||||
|
|
@ -24,7 +23,6 @@ class RBYTradingClient(GSCTradingClient):
|
|||
single_transfer: {7},
|
||||
pool_transfer: {1 + 0x42, 1 + 1}, # Counter + Single Pokémon OR Counter + Fail
|
||||
moves_transfer: {1 + 1 + 8}, # Counter + Species + Moves
|
||||
mail_transfer : {1 + 0xC5}, # Counter + Mail
|
||||
choice_transfer : {1 + 1 + 0x42, 1 + 1}, # Counter + Choice + Single Pokémon OR Counter + Stop
|
||||
accept_transfer : {1 + 1}, # Counter + Accept
|
||||
success_transfer : {1 + 1}, # Counter + Success
|
||||
|
|
@ -83,6 +81,8 @@ 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
|
||||
|
|
@ -103,4 +103,23 @@ class RBYTrading(GSCTrading):
|
|||
|
||||
def get_checks(self, menu):
|
||||
return RBYChecks(self.special_sections_len, menu.do_sanity_checks)
|
||||
|
||||
|
||||
def trade_starting_sequence(self, buffered, send_data = [None, None, None, None]):
|
||||
"""
|
||||
Handles exchanging with the device the three data sections which
|
||||
are needed in order to trade.
|
||||
Optimizes the synchronous mail_data exchange by executing it only
|
||||
if necessary and in the way which requires less packet transfers.
|
||||
Returns the player's data and the other player's data.
|
||||
"""
|
||||
# Prepare checks
|
||||
self.checks.reset_species_item_list()
|
||||
# Send and get the 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)
|
||||
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)
|
||||
|
||||
return [random_data, pokemon_data, []], [random_data_other, pokemon_data_other, []]
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user