mirror of
https://github.com/Lorenzooone/Pokemon-Gen3-to-Gen-X.git
synced 2026-03-21 17:24:39 -05:00
294 lines
11 KiB
C
294 lines
11 KiB
C
#include <gba.h>
|
|
#include "save.h"
|
|
#include "gen3_save.h"
|
|
#include "gen_converter.h"
|
|
#include "text_handler.h"
|
|
|
|
#define MAGIC_NUMBER 0x08012025
|
|
#define INVALID_SLOT 2
|
|
#define SAVE_SLOT_INDEX_POS 0xDFFC
|
|
#define SECTION_ID_POS 0xFF4
|
|
#define CHECKSUM_POS 0xFF6
|
|
#define MAGIC_NUMBER_POS 0xFF8
|
|
#define SAVE_NUMBER_POS 0xFFC
|
|
#define SECTION_TOTAL 14
|
|
|
|
#define SECTION_TRAINER_INFO_ID 0
|
|
#define SECTION_PARTY_INFO_ID 1
|
|
#define SECTION_DEX_SEEN_1_ID 1
|
|
#define SECTION_DEX_SEEN_2_ID 4
|
|
#define SECTION_MAIL_ID 3
|
|
#define SECTION_GIFT_RIBBON_ID 4
|
|
|
|
#define DEX_POS_0 0x28
|
|
#define RSE_PARTY 0x234
|
|
#define FRLG_PARTY 0x34
|
|
|
|
#define VALID_MAPS 36
|
|
|
|
u32 read_section_id(int, int);
|
|
void read_game_data_trainer_info(int, struct game_data_t*);
|
|
void register_dex_entry(struct game_data_t*, struct gen3_mon_data_unenc*);
|
|
void handle_mail_trade(struct game_data_t*, u8, u8);
|
|
void process_party_data(struct game_data_t*);
|
|
void read_party(int, struct game_data_t*);
|
|
u32 read_slot_index(int);
|
|
u8 validate_slot(int);
|
|
u8 get_slot(void);
|
|
|
|
const u16 summable_bytes[SECTION_TOTAL] = {3884, 3968, 3968, 3968, 3848, 3968, 3968, 3968, 3968, 3968, 3968, 3968, 3968, 2000};
|
|
const u16 pokedex_extra_pos_1 [] = {0x938, 0x5F8, 0x988};
|
|
const u16 pokedex_extra_pos_2 [] = {0xC0C, 0xB98, 0xCA4};
|
|
const u16 mail_pos [] = {0xC4C, 0xDD0, 0xCE0};
|
|
const u16 ribbon_pos [] = {0x290, 0x21C, 0x328};
|
|
const u16 special_area [] = {0, 9, 9};
|
|
const u16 rs_valid_maps[VALID_MAPS] = {0x0202, 0x0203, 0x0301, 0x0302, 0x0405, 0x0406, 0x0503, 0x0504, 0x0603, 0x0604, 0x0700, 0x0701, 0x0804, 0x0805, 0x090a, 0x090b, 0x0a05, 0x0a06, 0x0b05, 0x0b06, 0x0c02, 0x0c03, 0x0d06, 0x0d07, 0x0e03, 0x0e04, 0x0f02, 0x0f03, 0x100c, 0x100d, 0x100a, 0x1918, 0x1919, 0x191a, 0x191b, 0x1a05};
|
|
|
|
struct game_data_t* own_game_data_ptr;
|
|
|
|
struct game_data_t* get_own_game_data() {
|
|
return own_game_data_ptr;
|
|
}
|
|
|
|
void init_game_data(struct game_data_t* game_data) {
|
|
init_game_identifier(&game_data->game_identifier);
|
|
|
|
game_data->party_1.total = 0;
|
|
game_data->party_2.total = 0;
|
|
game_data->party_3.total = 0;
|
|
|
|
for(int i = 0; i < (OT_NAME_GEN3_SIZE+1); i++)
|
|
game_data->trainer_name[i] = GEN3_EOL;
|
|
}
|
|
|
|
u32 read_section_id(int slot, int section_pos) {
|
|
if(slot)
|
|
slot = 1;
|
|
if(section_pos < 0)
|
|
section_pos = 0;
|
|
if(section_pos >= SECTION_TOTAL)
|
|
section_pos = SECTION_TOTAL - 1;
|
|
|
|
return read_short_save((slot * SAVE_SLOT_SIZE) + (section_pos * SECTION_SIZE) + SECTION_ID_POS);
|
|
}
|
|
|
|
void read_game_data_trainer_info(int slot, struct game_data_t* game_data) {
|
|
if(slot)
|
|
slot = 1;
|
|
|
|
for(int i = 0; i < SECTION_TOTAL; i++)
|
|
if(read_section_id(slot, i) == SECTION_TRAINER_INFO_ID) {
|
|
copy_save_to_ram((slot * SAVE_SLOT_SIZE) + (i * SECTION_SIZE), game_data->trainer_name, OT_NAME_GEN3_SIZE+1);
|
|
game_data->trainer_gender = *(vu8*)((slot * SAVE_SLOT_SIZE) + (i * SECTION_SIZE) + 8);
|
|
game_data->trainer_id = read_int_save((slot * SAVE_SLOT_SIZE) + (i * SECTION_SIZE) + 10);
|
|
copy_save_to_ram((slot * SAVE_SLOT_SIZE) + (i * SECTION_SIZE) + DEX_POS_0, game_data->pokedex_owned, DEX_BYTES);
|
|
copy_save_to_ram((slot * SAVE_SLOT_SIZE) + (i * SECTION_SIZE) + DEX_POS_0 + DEX_BYTES, game_data->pokedex_seen, DEX_BYTES);
|
|
determine_game_with_save(&game_data->game_identifier, slot, i, summable_bytes[SECTION_TRAINER_INFO_ID]);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
void register_dex_entry(struct game_data_t* game_data, struct gen3_mon_data_unenc* data_src) {
|
|
u16 dex_index = get_dex_index_raw(data_src);
|
|
if(dex_index != NO_DEX_INDEX) {
|
|
u8 base_index = dex_index >> 3;
|
|
u8 rest_index = dex_index & 7;
|
|
game_data->pokedex_seen[base_index] |= (1 << rest_index);
|
|
game_data->pokedex_owned[base_index] |= (1 << rest_index);
|
|
}
|
|
}
|
|
|
|
void handle_mail_trade(struct game_data_t* game_data, u8 own_mon, u8 other_mon) {
|
|
u8 mail_id = get_mail_id_raw(&game_data[0].party_3_undec[own_mon]);
|
|
if((mail_id != GEN3_NO_MAIL) && (mail_id < PARTY_SIZE)) {
|
|
clean_mail_gen3(&game_data[0].mails_3[get_mail_id_raw(&game_data[0].party_3_undec[own_mon])], game_data[0].party_3_undec[own_mon].src);
|
|
}
|
|
|
|
mail_id = get_mail_id_raw(&game_data[1].party_3_undec[other_mon]);
|
|
if((mail_id != GEN3_NO_MAIL) && (mail_id < PARTY_SIZE)) {
|
|
u8 is_mail_free[PARTY_SIZE] = {1,1,1,1,1,1};
|
|
for(u32 i = 0; i < game_data[0].party_3.total; i++) {
|
|
u8 inner_mail_id = get_mail_id_raw(&game_data[0].party_3_undec[i]);
|
|
if((inner_mail_id != GEN3_NO_MAIL) && (inner_mail_id < PARTY_SIZE))
|
|
is_mail_free[inner_mail_id] = 0;
|
|
}
|
|
u8 target = PARTY_SIZE-1;
|
|
for(int i = 0; i < PARTY_SIZE; i++)
|
|
if(is_mail_free[i]) {
|
|
target = i;
|
|
break;
|
|
}
|
|
u8* dst = (u8*)&game_data[0].mails_3[target];
|
|
u8* src = (u8*)&game_data[1].mails_3[mail_id];
|
|
for(u32 i = 0; i < sizeof(struct mail_gen3); i++)
|
|
dst[i] = src[i];
|
|
game_data[1].party_3_undec[other_mon].src->mail_id = target;
|
|
}
|
|
else
|
|
game_data[1].party_3_undec[other_mon].src->mail_id = GEN3_NO_MAIL;
|
|
}
|
|
|
|
u8 trade_mons(struct game_data_t* game_data, u8 own_mon, u8 other_mon, const u16** learnset_ptr, u8 curr_gen) {
|
|
handle_mail_trade(game_data, own_mon, other_mon);
|
|
|
|
u8* dst = (u8*)&game_data[0].party_3.mons[own_mon];
|
|
u8* src = (u8*)&game_data[1].party_3.mons[other_mon];
|
|
for(u32 i = 0; i < sizeof(struct gen3_mon); i++)
|
|
dst[i] = src[i];
|
|
dst = (u8*)&game_data[0].party_3_undec[own_mon];
|
|
src = (u8*)&game_data[1].party_3_undec[other_mon];
|
|
for(u32 i = 0; i < sizeof(struct gen3_mon_data_unenc); i++)
|
|
dst[i] = src[i];
|
|
game_data[0].party_3_undec[own_mon].src = &game_data[0].party_3.mons[own_mon];
|
|
for(int i = 0; i < GIFT_RIBBONS; i++)
|
|
if(!game_data[0].giftRibbons[i])
|
|
game_data[0].giftRibbons[i] = game_data[1].giftRibbons[i];
|
|
register_dex_entry(&game_data[0], &game_data[0].party_3_undec[own_mon]);
|
|
u8 ret_val = trade_evolve(&game_data[0].party_3.mons[own_mon], &game_data[0].party_3_undec[own_mon], learnset_ptr, curr_gen);
|
|
if(ret_val)
|
|
register_dex_entry(&game_data[0], &game_data[0].party_3_undec[own_mon]);
|
|
return ret_val;
|
|
}
|
|
|
|
u8 is_invalid_offer(struct game_data_t* game_data, u8 own_mon, u8 other_mon) {
|
|
// Check for validity
|
|
if(!game_data[1].party_3_undec[other_mon].is_valid_gen3)
|
|
return 1 + 0;
|
|
// Check that the receiving party has at least one active mon
|
|
if(game_data[1].party_3_undec[other_mon].is_egg) {
|
|
u8 found_normal = 0;
|
|
for(int i = 0; i < PARTY_SIZE; i++) {
|
|
if((i != own_mon) && (game_data[0].party_3_undec[i].is_valid_gen3) && (!game_data[0].party_3_undec[i].is_egg)) {
|
|
found_normal = 1;
|
|
break;
|
|
}
|
|
}
|
|
if(!found_normal)
|
|
return 1 + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void process_party_data(struct game_data_t* game_data) {
|
|
if(game_data->party_3.total > PARTY_SIZE)
|
|
game_data->party_3.total = PARTY_SIZE;
|
|
u8 curr_slot = 0;
|
|
u8 found = 0;
|
|
for(u32 i = 0; i < game_data->party_3.total; i++) {
|
|
process_gen3_data(&game_data->party_3.mons[i], &game_data->party_3_undec[i], game_data->game_identifier.game_main_version, game_data->game_identifier.game_sub_version);
|
|
if(game_data->party_3_undec[i].is_valid_gen3)
|
|
found = 1;
|
|
}
|
|
if (!found)
|
|
game_data->party_3.total = 0;
|
|
for(u32 i = 0; i < game_data->party_3.total; i++)
|
|
if(gen3_to_gen2(&game_data->party_2.mons[curr_slot], &game_data->party_3_undec[i], game_data->trainer_id)) {
|
|
curr_slot++;
|
|
game_data->party_3_undec[i].is_valid_gen2 = 1;
|
|
}
|
|
else
|
|
game_data->party_3_undec[i].is_valid_gen2 = 0;
|
|
game_data->party_2.total = curr_slot;
|
|
curr_slot = 0;
|
|
for(u32 i = 0; i < game_data->party_3.total; i++)
|
|
if(gen3_to_gen1(&game_data->party_1.mons[curr_slot], &game_data->party_3_undec[i], game_data->trainer_id)) {
|
|
curr_slot++;
|
|
game_data->party_3_undec[i].is_valid_gen1 = 1;
|
|
}
|
|
else
|
|
game_data->party_3_undec[i].is_valid_gen1 = 0;
|
|
game_data->party_1.total = curr_slot;
|
|
}
|
|
|
|
void read_party(int slot, struct game_data_t* game_data) {
|
|
if(slot)
|
|
slot = 1;
|
|
|
|
read_game_data_trainer_info(slot, game_data);
|
|
if(game_data->game_identifier.game_is_jp == UNDETERMINED) {
|
|
if(game_data->trainer_name[OT_NAME_JP_GEN3_SIZE+1] == 0)
|
|
game_data->game_identifier.game_is_jp = 1;
|
|
else
|
|
game_data->game_identifier.game_is_jp = 0;
|
|
}
|
|
u16 add_on = RSE_PARTY;
|
|
if(game_data->game_identifier.game_main_version == FRLG_MAIN_GAME_CODE)
|
|
add_on = FRLG_PARTY;
|
|
|
|
for(int i = 0; i < SECTION_TOTAL; i++)
|
|
if(read_section_id(slot, i) == SECTION_PARTY_INFO_ID) {
|
|
copy_save_to_ram((slot * SAVE_SLOT_SIZE) + (i * SECTION_SIZE) + add_on, (u8*)(&game_data->party_3), sizeof(struct gen3_party));
|
|
break;
|
|
}
|
|
process_party_data(game_data);
|
|
}
|
|
|
|
u32 read_slot_index(int slot) {
|
|
if(slot)
|
|
slot = 1;
|
|
|
|
return read_int_save((slot * SAVE_SLOT_SIZE) + SAVE_SLOT_INDEX_POS);
|
|
}
|
|
|
|
u8 validate_slot(int slot) {
|
|
if(slot)
|
|
slot = 1;
|
|
|
|
u16 valid_sections = 0;
|
|
u32 buffer[SECTION_SIZE>>2];
|
|
u16* buffer_16 = (u16*)buffer;
|
|
u32 current_save_index = read_slot_index(slot);
|
|
|
|
for(int i = 0; i < SECTION_TOTAL; i++)
|
|
{
|
|
copy_save_to_ram((slot * SAVE_SLOT_SIZE) + (i * SECTION_SIZE), (u8*)buffer, SECTION_SIZE);
|
|
if(buffer[MAGIC_NUMBER_POS>>2] != MAGIC_NUMBER)
|
|
return 0;
|
|
if(buffer[SAVE_NUMBER_POS>>2] != current_save_index)
|
|
return 0;
|
|
u16 section_id = buffer_16[SECTION_ID_POS>>1];
|
|
if(section_id >= SECTION_TOTAL)
|
|
return 0;
|
|
u16 prepared_checksum = buffer_16[CHECKSUM_POS>>1];
|
|
u32 checksum = 0;
|
|
for(int j = 0; j < (summable_bytes[section_id] >> 2); j++)
|
|
checksum += buffer[j];
|
|
checksum = ((checksum & 0xFFFF) + (checksum >> 16)) & 0xFFFF;
|
|
if(checksum != prepared_checksum)
|
|
return 0;
|
|
valid_sections |= (1 << section_id);
|
|
}
|
|
|
|
if(valid_sections != (1 << (SECTION_TOTAL))-1)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
u8 get_slot(){
|
|
u32 last_valid_save_index = 0;
|
|
u8 slot = INVALID_SLOT;
|
|
|
|
for(int i = 0; i < 2; i++) {
|
|
u32 current_save_index = read_slot_index(i);
|
|
if((current_save_index >= last_valid_save_index) && validate_slot(i)) {
|
|
slot = i;
|
|
last_valid_save_index = current_save_index;
|
|
}
|
|
}
|
|
return slot;
|
|
}
|
|
|
|
void read_gen_3_data(struct game_data_t* game_data){
|
|
init_bank();
|
|
|
|
u8 slot = get_slot();
|
|
if(slot == INVALID_SLOT) {
|
|
return;
|
|
}
|
|
|
|
read_party(slot, game_data);
|
|
own_game_data_ptr = game_data;
|
|
}
|