Poke_Transporter_GB/source/pokemon.cpp
Philippe Symons eef173b0d2 Fix crash + unrelated buffer overflow + some optimizations
There was a crash happening with ptgb::vector when you'd press A on the CONFIRM button of the box screen. It only occurred on actual gba hardware and
was a real heisenbug: as soon as you'd add code to display logs on screen, the problem would disappear. So it was very difficult to figure this one
out. We're not even entirely sure why, but it looks like the malloc/realloc/free use in ptgb::vector would cause issues.

Maybe it was alignment, but after messing with the code we also saw a warning appear in the terminal telling us that realloc wouldn't properly
deal with non-POD types. It complained about this very thing while referring to the add_track() function, which stores ptgb::vectors inside another
ptgb::vector. We also didn't have a custom copy constructor yet to actually copy the buffer instead of its pointer.
All of these could potentially have led to the crash. But debugging during the link cable flow was difficult, so we were never able to confirm it in
a debugger, log or dump.

Because I suspected the high IWRAM consumption (especially now with ZX0 decompression) for a while, I also did an optimization in mystery_gift_builder
to pass global_memory_buffer as its section_30_data buffer instead. This reduces IWRAM consumption by 4 KB.

There was another problem I discovered during my crash hunt: the out_array (now payload_buffer) was allocated as a 672 byte array, but the payloads
were actually 707 bytes. Therefore writing this to the buffer caused a buffer overflow, thereby corrupting the global variables appearing after it in
IWRAM. It turned out eventually that none of these variables were really critical, but it could explain some minor bugs GearsProgress has seen.

I also did a few performance optimizations:

- At various stages in the code, for loops were used to copy data from one buffer into another byte-by-byte. This was far from optimal because the gba
cpu can load/copy 4 bytes at a time if you ask it to. So I replaced those with memcpy(), which is a hand-optimized assembly function to copy data
using this principle.

- generate_payload was being called twice: once at start_link and once at continue_link, giving the exact same result, even though it was already
being stored in a global buffer allocated in IWRAM. This was also a fairly heavy function. So I optimized the code to only initialize it once in
the script chain and then just retrieve the buffer.

- generate_payload was constructing the eventual payload twice even within the same call. That's because it first merged z80_rng_seed, z80_payload
and z80_patchlist into a full_data ptgb::vector, after which it then copied the data again to out_array (now called payload_buffer). I eliminated the
full_data vector now.
2025-06-18 10:23:03 +02:00

1145 lines
36 KiB
C++

#include <tonc.h>
#include "libstd_replacements.h"
#include "pokemon.h"
#include "pokemon_data.h"
#include "random.h"
#include "save_data_manager.h"
#include "debug_mode.h"
#include "text_engine.h"
#include "zx0_decompressor.h"
#include "JPN_NAMES_zx0_bin.h"
Pokemon::Pokemon() {};
// TODO: Rewrite this with two different classes/structs that have arrays as input/output
// GBpkmn and GBApkmn
// A lot of the event Pokemon script is reused. Really should be split into many different functions
// Endian-ness too is all over the place... :/
void Pokemon::load_data(int index, const byte *party_data, int game, int lang)
{
language = lang;
if (lang == JPN_ID)
{
switch (game)
{
case GREEN_ID:
case RED_ID:
case BLUE_ID:
case YELLOW_ID:
gen = 1;
pkmn_size = 44;
ot_size = 6;
nickname_size = 6;
box_size = 30;
break;
case GOLD_ID:
case SILVER_ID:
case CRYSTAL_ID:
gen = 2;
pkmn_size = 32;
ot_size = 6;
nickname_size = 6;
box_size = 30;
break;
}
}
else
{
switch (game)
{
case RED_ID:
case BLUE_ID:
case YELLOW_ID:
gen = 1;
pkmn_size = 33;
ot_size = 11;
nickname_size = 11;
box_size = 20;
break;
case GOLD_ID:
case SILVER_ID:
case CRYSTAL_ID:
gen = 2;
pkmn_size = 32;
ot_size = 11;
nickname_size = 11;
box_size = 20;
break;
}
}
int party_species_offset =
1 + // the num pkmn byte
(1 * index); // the pkmn index we're looking for
int box_struct_offset =
1 + // the num of pkmn byte
(1 * box_size) + 1 + // list of pkmn in box and terminator
(pkmn_size * index); // the pokemon we're looking for
int ot_offset =
1 + // the num of pkmn byte
(1 * box_size) + 1 + // list of pkmn in box and terminator
(pkmn_size * box_size) + // the pokemon structs
(ot_size * index); // the ot we're looking for
int nickname_offset =
1 + // the num of pkmn byte
(1 * box_size) + 1 + // list of pkmn in box and terminator
(pkmn_size * box_size) + // the pokemon structs
(ot_size * box_size) + // the ots
(nickname_size * index); // the nickname we're looking for
num_in_box = party_data[0];
index_in_box = index;
switch (gen)
{
case 1:
// ptgb_write(ptgb::to_string(party_data[1121]));
// while (true){};
species_index_party = party_data[party_species_offset];
species_index_struct = party_data[box_struct_offset + 0x00];
met_level = party_data[box_struct_offset + 0x03];
copy_from_to(&party_data[box_struct_offset + 0x08], &moves[0], 4, false);
copy_from_to(&party_data[box_struct_offset + 0x0C], &trainer_id[0], 2, false);
exp = 0;
for (int i = 0; i < 3; i++)
{
exp += party_data[box_struct_offset + 0x0E + i];
}
copy_from_to(&party_data[box_struct_offset + 0x1B], &dvs[0], 2, false);
copy_from_to(&party_data[box_struct_offset + 0x1D], &pp_values[0], 4, false);
copy_from_to(&party_data[nickname_offset], &nickname[0], 10, false);
copy_from_to(&party_data[ot_offset], &trainer_name[0], 7, false);
// Data not in gen 1
pokerus = 0x00;
caught_data[0] = 0x00;
caught_data[1] = 0x00;
item = 0;
break;
case 2:
species_index_party = party_data[party_species_offset];
species_index_struct = party_data[box_struct_offset + 0x00];
item = party_data[box_struct_offset + 0x01];
copy_from_to(&party_data[box_struct_offset + 0x02], &moves[0], 4, false);
copy_from_to(&party_data[box_struct_offset + 0x06], &trainer_id[0], 2, false);
exp = 0;
for (int i = 0; i < 3; i++)
{
exp += party_data[box_struct_offset + 0x08 + i] << (8 * (2 - i));
}
copy_from_to(&party_data[box_struct_offset + 0x15], &dvs[0], 2, false);
copy_from_to(&party_data[box_struct_offset + 0x17], &pp_values[0], 4, false);
pokerus = party_data[box_struct_offset + 0x1C];
copy_from_to(&party_data[box_struct_offset + 0x1D], &caught_data[0], 2, false);
met_level = party_data[box_struct_offset + 0x1F];
copy_from_to(&party_data[nickname_offset], &nickname[0], 10, false);
copy_from_to(&party_data[ot_offset + 0x00], &trainer_name[0], 7, false);
break;
}
// box_struct_offset = 0;
if (SHOW_DATA_PACKETS)
{
tte_set_pos(8, 120);
ptgb_write("struct offset: ");
ptgb_write(ptgb::to_string(box_struct_offset));
ptgb_write("\nbox_size: ");
ptgb_write(ptgb::to_string(box_size));
ptgb_write("\npkmn_size: ");
ptgb_write(ptgb::to_string(pkmn_size));
ptgb_write("\nindex: ");
ptgb_write(ptgb::to_string(index));
ptgb_write(", game: ");
ptgb_write(ptgb::to_string(game));
while (!key_hit(KEY_A))
{
global_next_frame();
}
global_next_frame();
}
}
void Pokemon::convert_to_gen_three(PokemonTables& data_tables, Conversion_Types conv_type, bool simplified, bool stabilize_mythical)
{
// Convert the species indexes
if (gen == 1)
{
if (species_index_struct > 190)
{
species_index_struct = 0;
}
else
{
species_index_struct = gen_1_index_array[species_index_struct];
if (species_index_struct == 0xFF)
{
is_missingno = true;
species_index_struct = 0x89; // Porygon
}
}
species_index_party = species_index_struct;
}
if ( // index_in_box % 4 == 0 ||
(species_index_struct > NUM_POKEMON - (get_treecko_enabled() ? 0 : 1)) || // Checks if the Pokemon is beyond the supported Pokemon
species_index_struct == 0 || // Checks that the Pokemon isn't a blank party space
species_index_struct != species_index_party || // Checks that the Pokemon isn't a hybrid or an egg
index_in_box >= num_in_box || // Checks that we're not reading beyond the Pokemon in the box
item != 0) // Checks that the Pokemon doesn't have an item
{
if (!DONT_HIDE_INVALID_PKMN)
{
is_valid = false;
return;
}
}
is_valid = true;
nature_mod = exp % 25; // save the nature mod in case the level is changed
// Update dex if not simple
if (!simplified && !is_caught(species_index_struct))
{
is_new = true;
set_caught(species_index_struct);
}
// Set nickname
if (language == KOR_ID)
{
u16 JPN_NAMES[POKEMON_ARRAY_SIZE * 6];
gen_3_pkmn[18] = JPN_ID; // Set to JPN
byte new_nickname[10];
byte new_ot[7];
u16 cur_char;
data_tables.load_gen3_charset(language);
// setup the zx0 decompressor to decompress the JPN_NAMES table
zx0_decompressor_start((u8*)JPN_NAMES, JPN_NAMES_zx0_bin);
zx0_decompressor_read(zx0_decompressor_get_decompressed_size());
for (int i = 0; i < 6; i++)
{ // Read the JPN name and convert it
cur_char = JPN_NAMES[species_index_struct * 6];
new_nickname[i] = data_tables.get_gen_3_char(cur_char);
}
if (gen == 1)
{
// レッド (Red)
new_ot[0] = 0x7A; // レ
new_ot[1] = 0xA0; // ッ
new_ot[2] = 0x95; // ド
new_ot[3] = 0xFF;
}
else
{
if (((caught_data[1] & 0b10000000) >> 7) == 1) // Checks if the Gen 2 player is male or female
{
// クリス (Kurisu)
new_ot[0] = 0x58; // ク
new_ot[1] = 0x78; // リ
new_ot[2] = 0x5D; // ス
new_ot[3] = 0xFF;
}
else
{
// ヒビキ (Hibiki)
new_ot[0] = 0x6B; // ヒ
new_ot[1] = 0x97; // ビ
new_ot[2] = 0x57; // キ
new_ot[3] = 0xFF;
}
}
copy_from_to(&new_nickname[0], &gen_3_pkmn[8], 10, false); // Nickname
copy_from_to(&new_ot[0], &gen_3_pkmn[20], 7, false); // OT Name
}
else
{
gen_3_pkmn[18] = language; // Language
data_tables.load_input_charset(gen, language);
data_tables.load_gen3_charset(language);
copy_from_to(convert_text(data_tables, &nickname[0], 10), &gen_3_pkmn[8], 10, false); // Nickname
copy_from_to(convert_text(data_tables, &trainer_name[0], 7), &gen_3_pkmn[20], 7, false); // OT Name
}
// Make sure Level is not over 100 based on EXP
u32 max_exp = data_tables.get_max_exp(species_index_struct);
if (exp > max_exp)
{
exp = max_exp;
}
// Truncate the EXP down to the current level
if (conv_type == Virtual)
{
data_tables.load_exp_groups();
switch (data_tables.EXP_GROUPS[species_index_struct])
{
case EXP_FAST:
exp = (4 * (met_level * met_level * met_level)) / 5;
break;
case EXP_MED_FAST:
exp = (met_level * met_level * met_level);
break;
case EXP_MED_SLOW:
exp = ((6 * met_level * met_level * met_level) / 5) - (15 * met_level * met_level) + (100 * met_level) - 140;
break;
case EXP_SLOW:
exp = (5 * (met_level * met_level * met_level)) / 4;
break;
}
}
// Check if shiny
is_shiny =
((dvs[1] == 0b10101010) && // Checks if the Speed and Special DVs equal 10
((dvs[0] & 0xF) == 0b1010) && // Checks if the Defense DVs equal 10
((dvs[0] & 0b00100000) >> 5)); // Checks if the second bit of the Attack DV is true
if (species_index_struct == 52 && fnv1a_hash(nickname, 7) == 1515822901 &&
(fnv1a_hash(trainer_name, 7) == 2671449886 || fnv1a_hash(trainer_name, 7) == 1342961308))
{
is_shiny = true;
dvs[0] = 0xFF;
dvs[1] = 0xFF;
}
if (species_index_struct == 201) // Checks if the Pokemon is Unown
{
unown_letter = 0;
unown_letter |= ((dvs[0] >> 5) & 0b11) << 6;
unown_letter |= ((dvs[0] >> 1) & 0b11) << 4;
unown_letter |= ((dvs[1] >> 5) & 0b11) << 2;
unown_letter |= ((dvs[1] >> 1) & 0b11);
unown_letter = unown_letter / 10;
}
else
{
unown_letter = -1;
}
if (simplified)
{
if ((species_index_struct == 151 || species_index_struct == 251) && exp < 560) // Minimum EXP for level 10
{
met_level = 10;
}
return;
}
// Separate the PP Up values from the Move PP values
for (int i = 0; i < 4; i++)
{
pure_pp_values[i] = (pp_values[i] & 0b00111111); // Take only the bottom six bits
if (conv_type == Virtual)
{
pp_bonus[i] = 0; // Reset bonus
}
else
{
pp_bonus[i] = (pp_values[i] >> 6); // Take only the top two bits
}
}
if (conv_type == Legal)
{
// Check that the moves are valid
if (is_missingno)
{
moves[0] = 55; // Water Gun
moves[1] = 143; // Sky Attack
moves[2] = 6; // Pay Day
moves[3] = 20; // Bind
for (int i = 0; i < 4; i++)
{
pp_bonus[i] = 0;
}
}
else if ((species_index_struct != 0xEB) && (species_index_struct != 0xFC)) // Ignore Smeargle due to Sketch, Ignore Treecko because Treecko
{
for (int i = 0; i < 4; i++)
{
if ((!data_tables.can_learn_move(species_index_struct, moves[i])) && (moves[i] != 0))
{
moves[i] = 0; // Remove the move
pp_bonus[i] = 0; // Remove the PP bonus
}
}
}
// Make sure it has at least one move
if (moves[0] + moves[1] + moves[2] + moves[3] == 0)
{
moves[0] = data_tables.get_earliest_move(species_index_struct);
}
// Bubble valid moves to the top
int i, j;
bool swapped;
for (i = 0; i < 3; i++)
{
swapped = false;
for (j = 0; j < 3 - i; j++)
{
if ((moves[j] < moves[j + 1]) && moves[j] == 0)
{
// Move the move *and* PP bonus up if there is a blank space
moves[j] = moves[j + 1];
pp_bonus[j] = pp_bonus[j + 1];
moves[j + 1] = 0;
pp_bonus[j + 1] = 0;
swapped = true;
}
}
// If no two elements were swapped
// by inner loop, then break
if (swapped == false)
break;
}
}
// Restore the PP values
data_tables.load_power_points();
for (int i = 0; i < 4; i++)
{
pure_pp_values[i] = data_tables.POWER_POINTS[moves[i]] + ((data_tables.POWER_POINTS[moves[i]] / 5) * pp_bonus[i]);
}
// This is everything the mythical needs, don't change anything else
if (stabilize_mythical && (species_index_struct == 151 || species_index_struct == 251))
{
set_to_event(data_tables, nature_mod);
return;
}
// Generate PID
disable_auto_random();
u32 n_pid;
if (ENABLE_MATCH_PID)
{
n_pid = generate_pid_iv_match(data_tables, species_index_struct, nature_mod, &dvs[0]);
///n_pid = generate_pid_iv_legendary(data_tables, species_index_struct, &dvs[0], &ivs[0]);
u16 curr_rand = get_rand_u16();
ivs[0] = (curr_rand >> 0) & 0b11111;
ivs[1] = (curr_rand >> 5) & 0b11111;
ivs[2] = (curr_rand >> 10) & 0b11111;
curr_rand = get_rand_u16();
ivs[3] = (curr_rand >> 0) & 0b11111;
ivs[4] = (curr_rand >> 5) & 0b11111;
ivs[5] = (curr_rand >> 10) & 0b11111;
iv_egg_ability = 0;
for (int i = 0; i < 6; i++)
{
iv_egg_ability |= ((ivs[i] & 0b11111) << (i * 5));
}
}
else
{
n_pid = generate_pid_save_iv(data_tables, species_index_struct, nature_mod, &dvs[0]);
// Convert and set IVs
int hp_iv = 0;
for (int i = 0; i < 4; i++)
{
ivs[i + 1] = (dvs[i / 2] >> (((i + 1) % 2) * 4)) & 0b1111;
hp_iv |= ((ivs[i + 1] & 0x1) << i);
};
ivs[0] = hp_iv;
ivs[5] = ivs[4];
for (int i = 0; i < 6; i++)
{
ivs[i] = (ivs[i] * 2) + 1;
iv_egg_ability |= ((ivs[i] & 0b11111) << (i * 5));
}
}
for (int i = 0; i < 4; i++)
{
pid[i] = (n_pid >> (i * 8)) & 0xFF;
}
enable_auto_random();
// Determine and set Ability based off PID
iv_egg_ability |= ((pid[0] & 0x1) ? data_tables.get_num_abilities(species_index_struct) : 0) << 31;
// Origin info
if (conv_type == Faithful || conv_type == Legal)
{
origin_info |= ((caught_data[1] & 0b10000000) << 8); // OT gender - We would shift left 15 bits, but the bit is already shifted over 7
}
if (is_missingno)
{
origin_info |= (1 << 11); // Master Ball
}
else
{
origin_info |= (4 << 11); // Poke Ball
}
origin_info |= (((gen == 1 || conv_type == Legal) ? 4 : 7) << 7); // Game
if (conv_type != Legal)
{
origin_info |= met_level; // Level met. Set to zero (ie do nothing) if legal, so it is "hatched"
}
// Ribbons and Obedience
// ribbons[2] |= 0b00000100; // Artist Ribbon
if (species_index_struct == 151 || species_index_struct == 251) // Pokemon is Mew or Celebi
{
ribbons[3] |= 0b10000000; // Fateful Encounter flag
}
else if (is_missingno)
{
ribbons[3] |= 0b10000000; // Fateful Encounter flag
// ribbons[3] |= 0b00000100; // World Ribbon
}
// Personality Value
copy_from_to(&pid[0], &gen_3_pkmn[0], 4, false);
// Trainer ID
copy_from_to(&trainer_id[0], &gen_3_pkmn[4], 2, true);
// Check if the Pokemon is shiny
if (is_shiny)
{
secret_id[0] = trainer_id[1] ^ pid[0] ^ pid[2] ^ 0x0; // This value at the end should be random between 0 - 15, if that is to be implemented
secret_id[1] = trainer_id[0] ^ pid[1] ^ pid[3] ^ 0x0;
// Randomly shift by 16 (maybe)
}
else // Not shiny, make sure it isn't
{
secret_id[0] = dvs[0];
secret_id[1] = dvs[1];
if (((trainer_id[0] ^ secret_id[0] ^ pid[0] ^ pid[2]) == 0) &&
((trainer_id[1] ^ secret_id[1] ^ pid[1] ^ pid[3]) < 8))
{
secret_id[1] += 8;
}
}
copy_from_to(&secret_id[0], &gen_3_pkmn[6], 2, false); // Set SID
gen_3_pkmn[19] = 0b00000010; // Egg Name (has species sanity flag)
gen_3_pkmn[27] = 0b00000000; // Markings
// Data:
// Reset the data sections (in case the player runs the program twice)
for (int i = 0; i < 12; i++)
{
data_section_G[i] = 0;
data_section_A[i] = 0;
data_section_E[i] = 0;
data_section_M[i] = 0;
}
data_section_G[0] = (species_index_struct != 252 ? species_index_struct : 277); // Treecko check
data_section_G[1] = (species_index_struct != 252 ? 0x00 : 0x01); // Treecko check;
data_section_G[2] = (is_new ? 0x44 : 0x00); // Rare Candy if new
data_section_G[3] = 0x00;
for (int i = 0; i < 3; i++)
{
data_section_G[4 + i] = exp >> (8 * i);
}
data_section_G[8] = (pp_bonus[0] << 0 | pp_bonus[1] << 2 | pp_bonus[2] << 4 | pp_bonus[3] << 6);
data_section_A[0] = moves[0]; // Move 1
data_section_A[2] = moves[1]; // Move 2
data_section_A[4] = moves[2]; // Move 3
data_section_A[6] = moves[3]; // Move 4
copy_from_to(&pure_pp_values[0], &data_section_A[8], 4, false); // PP Values
// Data section E is all zero (EVs and Contest Stats)
byte met_location = 0xFF; // Fateful Encounter
if (conv_type == Legal)
{
switch (species_index_struct)
{
case 144: // Articuno
met_location = 0x8B; // Seafoam Islands
break;
case 145: // Zapdos
met_location = 0x8E; // Power Plant
break;
case 146: // Moltres
met_location = 0xAF; // Mt. Ember
break;
case 150: // Mewtwo
met_location = 0x8D; // Cerulean Cave
break;
case 201: // Unown
met_location = 0xB8; // Tanoby Chambers
break;
case 243: // Raikou
met_location = 0x6E; // Route 10 - in reference to the Power Plant
break;
case 244: // Entei
met_location = 0x79; // Route 21 - in reference to the Cinnabar Volcano
break;
case 245: // Suicune
met_location = 0x7D; // Route 25 - in reference to their final encounter in HGSS
break;
case 249: // Lugia
case 250: // Ho-Oh
met_location = 0xAE; // Navel Rock
break;
default:
met_location == 0xD7; // Met in a trade?
break;
}
}
data_section_M[0] = (conv_type == Virtual ? 0 : pokerus);
data_section_M[1] = met_location; // Met location, will be replaced by Pal Park in gen 4
data_section_M[2] = origin_info & 0x00FF; // Lower origins info
data_section_M[3] = (origin_info >> 8) & 0xFF; // Upper origins info
for (int i = 0; i < 4; i++)
{
data_section_M[i + 4] = (iv_egg_ability >> (i * 8) & 0xFF); // Set IVs, Egg, and Ability
}
copy_from_to(&ribbons[0], &data_section_M[8], 4, false); // Ribbons and Fateful Encounter
// Checksum:
checksum = 0x0000;
for (int i = 0; i < 12; i = i + 2)
{
checksum = checksum + ((data_section_G[i + 1] << 8) | data_section_G[i]);
checksum = checksum + ((data_section_A[i + 1] << 8) | data_section_A[i]);
checksum = checksum + ((data_section_E[i + 1] << 8) | data_section_E[i]);
checksum = checksum + ((data_section_M[i + 1] << 8) | data_section_M[i]);
}
gen_3_pkmn[28] = checksum & 0xFF;
gen_3_pkmn[29] = (checksum & 0xFF00) >> 8;
for (int i = 0; i < 4; i++)
{
encryption_key[i] = gen_3_pkmn[4 + i] ^ pid[i]; // XOR SID and TID with PID
}
for (int i = 0; i < 12; i++)
{
unencrypted_data[i] = data_section_G[i];
data_section_G[i] ^= encryption_key[i % 4];
unencrypted_data[12 + i] = data_section_A[i];
data_section_A[i] ^= encryption_key[i % 4];
unencrypted_data[24 + i] = data_section_E[i];
data_section_E[i] ^= encryption_key[i % 4];
unencrypted_data[36 + i] = data_section_M[i];
data_section_M[i] ^= encryption_key[i % 4];
}
// Puts the four data chunks into their correct locations based on the PID
alocate_data_chunks(data_section_G, data_section_A, data_section_E, data_section_M);
global_next_frame();
}
void Pokemon::copy_from_to(const byte *source, byte *destination, int size, bool reverse_endian)
{
if (reverse_endian)
{
for (int i = 0; i < size; i++)
{
destination[(size - 1) - i] = source[i];
}
}
else
{
memcpy(destination, source, size);
}
}
void Pokemon::alocate_data_chunks(byte *G, byte *A, byte *E, byte *M)
{
word full_pid = (pid[3] << 24 | pid[2] << 16 | pid[1] << 8 | pid[0]);
byte mod_pid = full_pid % 24;
unencrypted_data[48] = mod_pid;
// This is such a stupid way to do this, but I can't for the life of me find a formula for the permutation table.
switch (mod_pid)
{
case 0:
insert_data(G, A, E, M);
break;
case 1:
insert_data(G, A, M, E);
break;
case 2:
insert_data(G, E, A, M);
break;
case 3:
insert_data(G, E, M, A);
break;
case 4:
insert_data(G, M, A, E);
break;
case 5:
insert_data(G, M, E, A);
break;
case 6:
insert_data(A, G, E, M);
break;
case 7:
insert_data(A, G, M, E);
break;
case 8:
insert_data(A, E, G, M);
break;
case 9:
insert_data(A, E, M, G);
break;
case 10:
insert_data(A, M, G, E);
break;
case 11:
insert_data(A, M, E, G);
break;
case 12:
insert_data(E, G, A, M);
break;
case 13:
insert_data(E, G, M, A);
break;
case 14:
insert_data(E, A, G, M);
break;
case 15:
insert_data(E, A, M, G);
break;
case 16:
insert_data(E, M, G, A);
break;
case 17:
insert_data(E, M, A, G);
break;
case 18:
insert_data(M, G, A, E);
break;
case 19:
insert_data(M, G, E, A);
break;
case 20:
insert_data(M, A, G, E);
break;
case 21:
insert_data(M, A, E, G);
break;
case 22:
insert_data(M, E, G, A);
break;
case 23:
insert_data(M, E, A, G);
break;
}
}
void Pokemon::insert_data(byte *first, byte *second, byte *third, byte *fourth)
{
copy_from_to(&first[0], &gen_3_pkmn[32], 12, false);
copy_from_to(&second[0], &gen_3_pkmn[44], 12, false);
copy_from_to(&third[0], &gen_3_pkmn[56], 12, false);
copy_from_to(&fourth[0], &gen_3_pkmn[68], 12, false);
}
byte Pokemon::get_gen_3_data(int index)
{
return gen_3_pkmn[index];
}
byte *Pokemon::get_full_gen_3_array()
{
return gen_3_pkmn;
}
byte Pokemon::get_unencrypted_data(int index)
{
return unencrypted_data[index];
}
byte *Pokemon::convert_text(PokemonTables& data_tables, byte *text_array, int size)
{
for (int i = 0; i < size; i++)
{
text_array[i] = data_tables.get_gen_3_char(data_tables.input_charset[text_array[i]]);
}
return text_array;
}
u32 Pokemon::generate_pid_iv_match(PokemonTables& data_tables, byte pid_species_index, byte nature, byte *pid_dvs)
{
u32 new_pid = 0;
byte new_nature = 0;
byte new_gender = 0;
byte new_letter = 0;
int gen2_gender_threshold = data_tables.get_gender_threshold(pid_species_index, false);
int gen3_gender_threshold = data_tables.get_gender_threshold(pid_species_index, true);
bool gender = (((pid_dvs[0] >> 4) & 0b1111) < gen2_gender_threshold);
do
{
new_pid = get_rand_u16() | (get_rand_u16() << 16);
new_nature = get_nature_from_pid(new_pid);
new_gender = get_gender_from_pid(new_pid);
new_letter = get_letter_from_pid(new_pid);
} while (!(
(unown_letter != -1 ? new_letter == unown_letter : true) &&
new_nature == nature &&
(gen2_gender_threshold != -1
? ((new_gender < gen3_gender_threshold) == gender)
: true)));
return new_pid;
}
u32 Pokemon::generate_pid_iv_legendary(byte pid_species_index, byte *pid_dvs, byte *out_ivs)
{
// Convert and set IVs
// This stores the DVs in the IV order
byte ivs[6] = {
(((dvs[0] >> 4) & 0b1) << 3) |
(((dvs[0] >> 0) & 0b1) << 2) |
(((dvs[1] >> 4) & 0b1) << 1) |
(((dvs[1] >> 0) & 0b1) << 0),
(dvs[0] >> 4) & 0b1111, // Attack
(dvs[0] >> 0) & 0b1111, // Defense
(dvs[1] >> 4) & 0b1111, // Speed
(dvs[1] >> 0) & 0b1111, // Special Attack
(dvs[1] >> 0) & 0b1111, // Special Defense
};
u32 iv_low_rng;
u32 iv_high_rng;
u32 pid_high_rng;
u32 pid_low_rng;
u32 pid;
u32 seed;
u32 comp;
u32 n_val;
u32 n_comp;
for (int i = 0; i < 65535; i++)
{
// Generate the low IV byte, being the SpD, SpA, Spe
iv_low_rng = (i % 2) << 31 | (ivs[5] << 27) | (ivs[4] << 22) | (ivs[3] << 17) | (i >> 1);
iv_high_rng = get_rev_rand_u32(iv_low_rng);
pid_high_rng = get_rev_rand_u32(iv_high_rng);
pid_low_rng = get_rev_rand_u32(pid_high_rng);
seed = get_rev_rand_u32(pid_low_rng);
pid = ((pid_high_rng & 0xFFFF0000) << 0) | (pid_low_rng & 0xFFFF0000) >> 16;
// Test it against the high IV byte, being the Def, Atk, HP
comp = (ivs[2] << 11) | (ivs[1] << 6) | (ivs[0] << 1);
n_val = (iv_high_rng >> 16) & 0b0111101111011110;
n_comp = comp & 0b0111101111011110;
if (n_comp == n_val)
{
if ((pid % 25) % 6 == 0)
{
out_ivs[0] = (iv_low_rng >> 0) & 0b11111; // HP
out_ivs[1] = (iv_low_rng >> 5) & 0b11111; // Atk
out_ivs[2] = (iv_low_rng >> 10) & 0b11111; // Def
out_ivs[3] = (iv_high_rng >> 5) & 0b11111; // SpA
out_ivs[4] = (iv_high_rng >> 10) & 0b11111; // SpD
out_ivs[5] = (iv_high_rng >> 0) & 0b11111; // Spe
return pid_low_rng | seed;
}
}
}
return 0;
}
u8 Pokemon::get_letter_from_pid(u32 pid)
{
return (
((pid & 0x03000000) >> 18) +
((pid & 0x00030000) >> 12) +
((pid & 0x00000300) >> 6) +
((pid & 0x00000003) >> 0)) %
28;
};
u8 Pokemon::get_nature_from_pid(u32 pid)
{
return (pid % 25);
};
u8 Pokemon::get_gender_from_pid(u32 pid)
{
return (pid & 0xFF);
};
u32 Pokemon::generate_pid_save_iv(PokemonTables &data_tables, byte pid_species_index, byte nature, byte *pid_dvs)
{
// Set Unown Letter
u32 new_pid = 0;
if (pid_species_index == 0xC9) // Checks if the Pokemon is Unown
{
byte letter_mod = rand_reverse_mod(28, unown_letter);
for (int i = 0; i < 4; i++)
{
new_pid |= ((letter_mod >> (i * 2)) & 0b11) << (8 * i);
}
// Randomize rest of PID
new_pid |= get_rand_u32() & 0xFCFCFCFC;
// Set Nature
while ((new_pid % 25) != nature)
{
// Keep adding 0b100 to the PID until the nature matches
// 0b100 ensures that the 2 LSBs are maintained, as they determine the letter
new_pid = (new_pid & 0xFFFFFF00) | ((new_pid + 0b100) & 0xFF);
}
return new_pid;
}
else
{
// Set the correct gender for the Pokemon
new_pid |= get_rand_gender_byte(data_tables, pid_species_index, ((pid_dvs[0] >> 4) & 0b1111));
// Randomize rest of PID
new_pid |= get_rand_u32() & 0xFFFFFF00;
// Set nature
while (new_pid % 25 != nature)
{
new_pid = new_pid + 256;
}
return new_pid;
}
}
byte Pokemon::rand_reverse_mod(byte modulo_divisor, byte target_mod)
{
return (modulo_divisor * get_rand_range(0, (255 - target_mod) / modulo_divisor)) + target_mod;
}
byte Pokemon::get_rand_gender_byte(PokemonTables &data_tables, byte index_num, byte attack_DVs)
{
byte gen2_threshold = data_tables.get_gender_threshold(index_num, false);
byte gen3_threshold = data_tables.get_gender_threshold(index_num, true);
if (gen2_threshold == -1) // Is one gender or is genderless
{
return get_rand_range(0, 256);
}
else if (attack_DVs < gen2_threshold) // Is Female
{
return get_rand_range(0, gen3_threshold);
}
else // Is Male
{
return get_rand_range(gen3_threshold, 256);
}
}
byte Pokemon::get_dex_number()
{
return (is_valid ? species_index_struct : 0);
}
bool Pokemon::get_validity()
{
return is_valid;
}
bool Pokemon::get_is_shiny()
{
return is_shiny;
}
bool Pokemon::get_is_new()
{
return (is_valid ? is_new : false);
}
Simplified_Pokemon Pokemon::get_simple_pkmn()
{
Simplified_Pokemon curr_pkmn;
curr_pkmn.dex_number = get_dex_number();
curr_pkmn.met_level = met_level;
for (int i = 0; i < 10; i++)
{
curr_pkmn.nickname[i] = nickname[i];
}
curr_pkmn.is_valid = get_validity();
curr_pkmn.is_transferred = false;
curr_pkmn.is_shiny = get_is_shiny();
curr_pkmn.unown_letter = unown_letter;
curr_pkmn.is_missingno = is_missingno;
return curr_pkmn;
}
void Pokemon::set_to_event(PokemonTables &data_tables, byte nature)
{
int event_id = 0;
if (species_index_struct == 151)
{
switch (language)
{
case JPN_ID:
case KOR_ID:
event_id = 0;
break;
case ENG_ID:
event_id = 1;
break;
case FRE_ID:
event_id = 2;
break;
case ITA_ID:
event_id = 3;
break;
case GER_ID:
event_id = 4;
break;
case SPA_ID:
event_id = 5;
break;
}
}
else
{
switch (language)
{
case JPN_ID:
case KOR_ID:
event_id = 6;
break;
case ENG_ID:
case FRE_ID:
case ITA_ID:
case GER_ID:
case SPA_ID:
event_id = 7;
break;
}
}
data_tables.load_event_pkmn();
// Load the event into the Pokemon array and unencrypted data array
for (int i = 0; i < 0x20; i++)
{
if (i == 0x08)
{
i += 10; // Skip over the nickname
}
gen_3_pkmn[i] = data_tables.EVENT_PKMN[event_id][i];
}
for (int i = 0; i < 12; i++)
{
data_section_G[i] = data_tables.EVENT_PKMN[event_id][i + 0x20 + 0];
data_section_A[i] = data_tables.EVENT_PKMN[event_id][i + 0x20 + 12];
data_section_E[i] = data_tables.EVENT_PKMN[event_id][i + 0x20 + 24];
data_section_M[i] = data_tables.EVENT_PKMN[event_id][i + 0x20 + 36];
}
// insert moves and PP bonuses
data_section_G[8] = (pp_bonus[0] << 0 | pp_bonus[1] << 2 | pp_bonus[2] << 4 | pp_bonus[3] << 6);
data_section_A[0] = moves[0]; // Move 1
data_section_A[2] = moves[1]; // Move 2
data_section_A[4] = moves[2]; // Move 3
data_section_A[6] = moves[3]; // Move 4
// get a new PID in the BACD_R format, and make sure it isn't shiny
disable_auto_random();
u32 n_pid;
do
{
// Make the seed 16 bits long, per the BACD_R format
rand_set_seed(rand_get_seed() & 0xFFFF);
n_pid = (get_rand_u16() << 16) | get_rand_u16();
for (int i = 0; i < 4; i++)
{
gen_3_pkmn[i] = (n_pid >> (i * 8)) & 0xFF;
pid[i] = (n_pid >> (i * 8)) & 0xFF;
};
} while (((pid[0] ^ pid[2] ^ gen_3_pkmn[4] ^ gen_3_pkmn[6]) < 8) &&
((pid[1] ^ pid[3] ^ gen_3_pkmn[5] ^ gen_3_pkmn[7]) == 0) &&
(n_pid % 25 != nature)); // maintain the nature
// Set and fill the IVs
u16 curr_rand = get_rand_u16();
ivs[0] = (curr_rand >> 0) & 0b11111;
ivs[1] = (curr_rand >> 5) & 0b11111;
ivs[2] = (curr_rand >> 10) & 0b11111;
curr_rand = get_rand_u16();
ivs[3] = (curr_rand >> 0) & 0b11111;
ivs[4] = (curr_rand >> 5) & 0b11111;
ivs[5] = (curr_rand >> 10) & 0b11111;
iv_egg_ability = 0;
for (int i = 0; i < 6; i++)
{
iv_egg_ability |= ((ivs[i] & 0b11111) << (i * 5));
}
enable_auto_random();
// Determine and set Ability
iv_egg_ability |= ((pid[0] & 0x1) ? data_tables.get_num_abilities(species_index_struct) : 0) << 31;
// Set IVs, Egg, and Ability
for (int i = 0; i < 4; i++)
{
data_section_M[i + 4] = (iv_egg_ability >> (i * 8) & 0xFF);
}
// Determine and set OT gender
data_section_M[3] |= (caught_data[1] & 0b10000000);
// Check the level
if (exp < 560) // Minimum EXP for level 10
{
exp = 560;
}
data_section_G[2] = (is_new ? 0x44 : 0x00); // Rare Candy if new
// Update and set the checksum
checksum = 0x0000;
for (int i = 0; i < 12; i = i + 2)
{
checksum = checksum + ((data_section_G[i + 1] << 8) | data_section_G[i]);
checksum = checksum + ((data_section_A[i + 1] << 8) | data_section_A[i]);
checksum = checksum + ((data_section_E[i + 1] << 8) | data_section_E[i]);
checksum = checksum + ((data_section_M[i + 1] << 8) | data_section_M[i]);
}
gen_3_pkmn[28] = checksum & 0xFF;
gen_3_pkmn[29] = (checksum & 0xFF00) >> 8;
// Determine the encryption key
for (int i = 0; i < 4; i++)
{
encryption_key[i] = gen_3_pkmn[4 + i] ^ pid[i]; // XOR SID and TID with PID
}
// Encrypt the data
for (int i = 0; i < 12; i++)
{
unencrypted_data[i] = data_section_G[i];
data_section_G[i] ^= encryption_key[i % 4];
unencrypted_data[12 + i] = data_section_A[i];
data_section_A[i] ^= encryption_key[i % 4];
unencrypted_data[24 + i] = data_section_E[i];
data_section_E[i] ^= encryption_key[i % 4];
unencrypted_data[36 + i] = data_section_M[i];
data_section_M[i] ^= encryption_key[i % 4];
}
// Puts the four data chunks into their correct locations based on the PID
alocate_data_chunks(data_section_G, data_section_A, data_section_E, data_section_M);
return;
}