mirror of
https://github.com/GearsProgress/Poke_Transporter_GB.git
synced 2026-03-21 17:34:42 -05:00
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.
635 lines
15 KiB
C++
635 lines
15 KiB
C++
#include <tonc.h>
|
|
#include <cstdlib>
|
|
// #include <maxmod.h> //Music
|
|
#include "libstd_replacements.h"
|
|
#include "flash_mem.h"
|
|
#include "interrupt.h"
|
|
#include "gb_link.h"
|
|
#include "gameboy_colour.h"
|
|
#include "pokemon.h"
|
|
#include "random.h"
|
|
#include "text_engine.h"
|
|
#include "background_engine.h"
|
|
#include "pokemon_party.h"
|
|
#include "pokemon_data.h"
|
|
#include "script_array.h"
|
|
#include "sprite_data.h"
|
|
#include "button_handler.h"
|
|
#include "button_menu.h"
|
|
#include "debug_mode.h"
|
|
// #include "soundbank.h"
|
|
// #include "soundbank_bin.h"
|
|
#include "dex_handler.h"
|
|
#include "pokedex.h"
|
|
#include "global_frame_controller.h"
|
|
#include "pkmn_font.h"
|
|
#include "save_data_manager.h"
|
|
#include "mystery_gift_injector.h"
|
|
#include "mystery_gift_builder.h"
|
|
#include "multiboot_upload.h"
|
|
#include "rom_data.h"
|
|
#include "libraries/Pokemon-Gen3-to-Gen-X/include/save.h"
|
|
#include "text_data_table.h"
|
|
|
|
/*
|
|
|
|
TODO:
|
|
- Add in putting item from Pkmn to bag in gen 2
|
|
- Determine if transfered Shiny Pokemon are square/star sparkles
|
|
- Music and sound effects
|
|
- Wii Channel
|
|
- Events
|
|
- MissingNo/Enigma Berry
|
|
- Text translations
|
|
- Add support for other languages
|
|
- Add in dolls for gen 2/3
|
|
- Doxygen generation
|
|
--------
|
|
*/
|
|
|
|
int delay_counter = 0;
|
|
int curr_selection = 0;
|
|
bool skip = true;
|
|
rom_data curr_rom;
|
|
Button_Menu yes_no_menu(1, 2, 40, 24, false);
|
|
|
|
/*
|
|
int test_main(void) Music
|
|
{
|
|
|
|
irq_init(NULL);
|
|
// Initialize maxmod with default settings
|
|
// pass soundbank address, and allocate 8 channels.
|
|
|
|
irq_set(II_VBLANK, mmVBlank, 0);
|
|
irq_enable(II_VBLANK);
|
|
|
|
mmInitDefault((mm_addr)soundbank_bin, 8);
|
|
|
|
mmStart(MOD_FLATOUTLIES, MM_PLAY_LOOP);
|
|
// Song is playing now (well... almost)
|
|
while (1)
|
|
{
|
|
// ..process game logic..
|
|
|
|
// Update Maxmod
|
|
mmFrame();
|
|
|
|
// Wait for new frame (SWI 5)
|
|
VBlankIntrWait();
|
|
|
|
// ..update graphical data..
|
|
}
|
|
}
|
|
*/
|
|
|
|
// (R + G*32 + B*1024)
|
|
#define RGB(r, g, b) (r + (g * 32) + (b * 1024))
|
|
|
|
// make sure outBuffer is large enough! Should be at least hex_len + 1
|
|
template <typename I>
|
|
void n2hexstr(char* outBuffer, I w, size_t hex_len = sizeof(I) << 1)
|
|
{
|
|
static const char *digits = "0123456789ABCDEF";
|
|
memset(outBuffer, '0', hex_len);
|
|
outBuffer[hex_len] = '\0'; // we must make sure to terminate the string
|
|
|
|
for (size_t i = 0, j = (hex_len - 1) * 4; i < hex_len; ++i, j -= 4)
|
|
outBuffer[i] = digits[(w >> j) & 0x0f];
|
|
}
|
|
|
|
void load_graphics()
|
|
{
|
|
|
|
tte_erase_rect(0, 0, H_MAX, V_MAX);
|
|
// Load opening background first so it hides everything else
|
|
load_flex_background(BG_OPENING, 1);
|
|
load_background();
|
|
load_textbox_background();
|
|
load_eternal_sprites();
|
|
|
|
// Set up global yes no button
|
|
yes_no_menu.clear_vector();
|
|
yes_no_menu.add_button(Button(button_yes), true);
|
|
yes_no_menu.add_button(Button(button_no), false);
|
|
}
|
|
|
|
void initalization_script(void)
|
|
{
|
|
// Initalizations
|
|
REG_DISPCNT = DCNT_BLANK | DCNT_MODE0 | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_BG3 | DCNT_OBJ | DCNT_OBJ_1D;
|
|
irq_init(NULL);
|
|
irq_enable(II_VBLANK);
|
|
|
|
// Disable for save data read/write
|
|
REG_IME = 0;
|
|
REG_IE = 0;
|
|
|
|
// Sound bank init
|
|
irq_init(NULL);
|
|
irq_enable(II_VBLANK);
|
|
// irq_set(II_VBLANK, mmVBlank, 0); //Music
|
|
// mmInitDefault((mm_addr)soundbank_bin, 8); //Music
|
|
// mmStart(MOD_FLATOUTLIES, MM_PLAY_LOOP); //Music
|
|
|
|
// Graphics init
|
|
oam_init(obj_buffer, 128);
|
|
load_graphics();
|
|
|
|
// Prepare text engine for dialogue
|
|
init_text_engine();
|
|
|
|
// Set the random seed
|
|
rand_set_seed(0x1216);
|
|
|
|
// Clean up the main screen quick
|
|
tte_erase_rect(0, 0, 240, 160);
|
|
|
|
VBlankIntrWait();
|
|
REG_DISPCNT &= ~DCNT_BLANK;
|
|
};
|
|
|
|
void game_load_error(void)
|
|
{
|
|
REG_BG2CNT = (REG_BG2CNT & ~BG_PRIO_MASK) | BG_PRIO(1);
|
|
|
|
create_textbox(4, 1, 152, 100, true);
|
|
|
|
{
|
|
u8 general_text_table_buffer[2048];
|
|
text_data_table general_text(general_text_table_buffer);
|
|
|
|
general_text.decompress(get_compressed_general_table());
|
|
ptgb_write(general_text.get_text_entry(GENERAL_cart_load_error), true);
|
|
}
|
|
|
|
key_poll();
|
|
do
|
|
{
|
|
global_next_frame();
|
|
} while (!key_hit(KEY_A) && !key_hit(KEY_SELECT));
|
|
|
|
tte_erase_rect(0, 0, H_MAX, V_MAX);
|
|
|
|
if (key_hit(KEY_SELECT))
|
|
{
|
|
// We also want to give the option in this screen to upload the multiboot rom to another GBA.
|
|
// This can be useful when the user wants to work with a flashcart in single rom mode.
|
|
// The EZ Flash Omega (DE) for instance, triggers a reset of the gba if you insert it while the GBA is turned on.
|
|
// So the only way to work with it, is to boot Poke Transporter GB over multiboot and have the flashcart already inserted.
|
|
// It would be a shame not to support this flashcart, because it's awesome for pokémon fans. After all: it supports ds transfer
|
|
// and should support connecting with the gamecube games.
|
|
multiboot_upload_screen();
|
|
return;
|
|
}
|
|
delay_counter = 0;
|
|
|
|
while (delay_counter < 60)
|
|
{
|
|
delay_counter++;
|
|
global_next_frame();
|
|
}
|
|
}
|
|
|
|
void first_load_message(void)
|
|
{
|
|
tte_set_margins(8, 8, H_MAX - 8, V_MAX);
|
|
tte_set_pos(8, 8);
|
|
tte_set_ink(INK_ROM_COLOR);
|
|
|
|
{
|
|
u8 general_text_table_buffer[2048];
|
|
text_data_table general_text(general_text_table_buffer);
|
|
|
|
general_text.decompress(get_compressed_general_table());
|
|
ptgb_write(general_text.get_text_entry(GENERAL_intro_first), true);
|
|
}
|
|
|
|
while (!key_hit(KEY_A))
|
|
{
|
|
global_next_frame();
|
|
}
|
|
tte_erase_rect(0, 0, H_MAX, V_MAX);
|
|
}
|
|
|
|
#define TIMER_ENABLE 0x80
|
|
#define TIMER_CASCADE 0x4
|
|
#define TIMER_FREQ_1 0x0 // 16.78 MHz
|
|
#define TIMER_FREQ_64 0x1 // 262,144 Hz
|
|
#define TIMER_FREQ_256 0x2 // 65,536 Hz
|
|
#define TIMER_FREQ_1024 0x3 // 16,384 Hz
|
|
|
|
int test_decompress()
|
|
{
|
|
uint16_t charset[256];
|
|
|
|
// Reset both timers
|
|
REG_TM0CNT = 0;
|
|
REG_TM1CNT = 0;
|
|
REG_TM0D = 0;
|
|
REG_TM1D = 0;
|
|
|
|
// Set up TIMER0: count with no prescaler
|
|
REG_TM0CNT = TIMER_ENABLE | TIMER_FREQ_1;
|
|
// Set up TIMER1: cascade mode (increment when TIMER0 overflows)
|
|
REG_TM1CNT = TIMER_ENABLE | TIMER_CASCADE;
|
|
|
|
load_localized_charset(charset, 3, ENG_ID);
|
|
|
|
// Read combined 32-bit timer value
|
|
const u32 ticks = ((u32)REG_TM1D << 16) | REG_TM0D;
|
|
|
|
// Stop timers
|
|
REG_TM0CNT = 0;
|
|
REG_TM1CNT = 0;
|
|
|
|
create_textbox(4, 1, 160, 80, true);
|
|
|
|
ptgb_write_debug(charset, "Test results:\n\nDecompress: ", true);
|
|
ptgb_write_debug(charset, ptgb::to_string(static_cast<unsigned>(ticks * 1000 / 16777)), true);
|
|
ptgb_write_debug(charset, " usec\n", true);
|
|
|
|
|
|
while (true)
|
|
{
|
|
if (key_hit(KEY_B))
|
|
{
|
|
hide_text_box();
|
|
reset_textbox();
|
|
return 0;
|
|
}
|
|
global_next_frame();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int credits()
|
|
{
|
|
u8 text_decompression_buffer[2048];
|
|
text_data_table credits_text_table(text_decompression_buffer);
|
|
int curr_credits_num = 0;
|
|
|
|
credits_text_table.decompress(get_compressed_credits_table());
|
|
bool update = true;
|
|
|
|
global_next_frame();
|
|
while (true)
|
|
{
|
|
if (update)
|
|
{
|
|
create_textbox(4, 1, 160, 80, true);
|
|
show_text_box();
|
|
ptgb_write(credits_text_table.get_text_entry(curr_credits_num), true);
|
|
update = false;
|
|
}
|
|
|
|
if (key_hit(KEY_B))
|
|
{
|
|
hide_text_box();
|
|
reset_textbox();
|
|
return 0;
|
|
}
|
|
if (key_hit(KEY_LEFT) && curr_credits_num > 0)
|
|
{
|
|
curr_credits_num--;
|
|
update = true;
|
|
}
|
|
if (key_hit(KEY_RIGHT) && curr_credits_num < (credits_text_table.get_number_of_text_entries() - 1))
|
|
{
|
|
curr_credits_num++;
|
|
update = true;
|
|
}
|
|
if(key_hit(KEY_SELECT))
|
|
{
|
|
return test_decompress();
|
|
}
|
|
#if 0
|
|
if (ENABLE_DEBUG_SCREEN && key_hit(KEY_SELECT))
|
|
{
|
|
char hexBuffer[16];
|
|
uint16_t charset[256];
|
|
load_localized_charset(charset, 3, ENG_ID);
|
|
if (key_held(KEY_UP) && key_held(KEY_L) && key_held(KEY_R))
|
|
{
|
|
set_treecko(true);
|
|
}
|
|
u32 pkmn_flags = 0;
|
|
bool e4_flag = read_flag(curr_rom.e4_flag);
|
|
bool mg_flag = read_flag(curr_rom.mg_flag);
|
|
bool all_collected_flag = read_flag(curr_rom.all_collected_flag);
|
|
for (int i = 0; i < 30; i++)
|
|
{
|
|
pkmn_flags |= (read_flag(curr_rom.pkmn_collected_flag_start + i) << i);
|
|
}
|
|
|
|
bool tutorial = get_tutorial_flag();
|
|
int def_lang = get_def_lang_num();
|
|
|
|
create_textbox(4, 1, 160, 80, true);
|
|
ptgb_write_debug(charset, "Debug info:\n\nG: ", true);
|
|
ptgb_write_debug(charset, ptgb::to_string(curr_rom.language), true);
|
|
switch (curr_rom.gamecode)
|
|
{
|
|
case RUBY_ID:
|
|
ptgb_write_debug(charset, "-R-", true);
|
|
break;
|
|
case SAPPHIRE_ID:
|
|
ptgb_write_debug(charset, "-S-", true);
|
|
break;
|
|
case FIRERED_ID:
|
|
ptgb_write_debug(charset, "-F-", true);
|
|
break;
|
|
case LEAFGREEN_ID:
|
|
ptgb_write_debug(charset, "-L-", true);
|
|
break;
|
|
case EMERALD_ID:
|
|
ptgb_write_debug(charset, "-E-", true);
|
|
break;
|
|
}
|
|
|
|
ptgb_write_debug(charset, ptgb::to_string(curr_rom.version), true);
|
|
|
|
ptgb_write_debug(charset, "\nF: ", true);
|
|
ptgb_write_debug(charset, ptgb::to_string(e4_flag), true);
|
|
ptgb_write_debug(charset, ptgb::to_string(mg_flag), true);
|
|
ptgb_write_debug(charset, ptgb::to_string(all_collected_flag), true);
|
|
ptgb_write_debug(charset, "-", true);
|
|
|
|
n2hexstr(hexBuffer, pkmn_flags);
|
|
ptgb_write_debug(charset, hexBuffer, true);
|
|
ptgb_write_debug(charset, "\nS: ", true);
|
|
ptgb_write_debug(charset, ptgb::to_string(tutorial), true);
|
|
ptgb_write_debug(charset, "-", true);
|
|
n2hexstr(hexBuffer, def_lang);
|
|
ptgb_write_debug(charset, hexBuffer, true);
|
|
|
|
ptgb_write_debug(charset, "\n", true);
|
|
ptgb_write_debug(charset, VERSION, true);
|
|
if (get_treecko_enabled())
|
|
{
|
|
ptgb_write_debug(charset, ".T", true);
|
|
}
|
|
while (true)
|
|
{
|
|
if (key_hit(KEY_B))
|
|
{
|
|
hide_text_box();
|
|
reset_textbox();
|
|
return 0;
|
|
}
|
|
global_next_frame();
|
|
}
|
|
}
|
|
#endif
|
|
global_next_frame();
|
|
}
|
|
};
|
|
|
|
#define NUM_MENU_OPTIONS 3
|
|
|
|
int main_menu_loop()
|
|
{
|
|
uint8_t general_text_table_buffer[2048];
|
|
text_data_table general_text(general_text_table_buffer);
|
|
bool update = true;
|
|
const uint8_t menu_options[NUM_MENU_OPTIONS] = {GENERAL_option_transfer, GENERAL_option_dreamdex, GENERAL_option_credits};
|
|
const uint8_t *text_entry;
|
|
int return_values[NUM_MENU_OPTIONS] = {BTN_TRANSFER, BTN_POKEDEX, BTN_CREDITS};
|
|
u16 test = 0;
|
|
|
|
general_text.decompress(get_compressed_general_table());
|
|
|
|
while (true)
|
|
{
|
|
if (update)
|
|
{
|
|
tte_erase_rect(0, 80, 240, 160);
|
|
for (int i = 0; i < NUM_MENU_OPTIONS; i++)
|
|
{
|
|
text_entry = general_text.get_text_entry(menu_options[i]);
|
|
int size = get_string_length(text_entry);
|
|
int char_width = (PTGB_BUILD_LANGUAGE == JPN_ID ? 8 : 6);
|
|
int x = ((240 - (size * char_width)) / 2);
|
|
tte_set_pos(x, ((i * 17) + 80));
|
|
if (i == curr_selection)
|
|
{
|
|
tte_set_ink(INK_WHITE);
|
|
}
|
|
else
|
|
{
|
|
tte_set_ink(INK_ROM_COLOR);
|
|
}
|
|
ptgb_write(text_entry, true);
|
|
test++;
|
|
}
|
|
}
|
|
|
|
update = true;
|
|
if (key_hit(KEY_DOWN))
|
|
{
|
|
curr_selection = ((curr_selection + 1) % NUM_MENU_OPTIONS);
|
|
}
|
|
else if (key_hit(KEY_UP))
|
|
{
|
|
curr_selection = ((curr_selection + (NUM_MENU_OPTIONS - 1)) % NUM_MENU_OPTIONS);
|
|
}
|
|
else if (key_hit(KEY_A))
|
|
{
|
|
tte_erase_rect(0, test, H_MAX, V_MAX);
|
|
ptgb_write("#{cx:0xF000}");
|
|
return return_values[curr_selection];
|
|
}
|
|
else
|
|
{
|
|
update = false;
|
|
}
|
|
|
|
global_next_frame();
|
|
}
|
|
}
|
|
|
|
// Legal mumbo jumbo
|
|
static void show_legal_text(const u8* intro_text)
|
|
{
|
|
tte_set_margins(8, 8, H_MAX - 8, V_MAX - 8);
|
|
tte_set_pos(8, 8);
|
|
tte_set_ink(INK_ROM_COLOR);
|
|
ptgb_write(intro_text, true);
|
|
bool wait = true;
|
|
while (wait)
|
|
{
|
|
global_next_frame();
|
|
if (key_hit(KEY_A))
|
|
{
|
|
wait = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Gears of Progress
|
|
static void show_gears_of_progress()
|
|
{
|
|
tte_erase_rect(0, 0, 240, 160);
|
|
REG_BG1VOFS = 0;
|
|
delay_counter = 0;
|
|
while (delay_counter < (15 * 60))
|
|
{
|
|
global_next_frame();
|
|
delay_counter++;
|
|
if (key_hit(KEY_A))
|
|
{
|
|
delay_counter = (15 * 60);
|
|
}
|
|
}
|
|
}
|
|
|
|
// split off from the main function in order to keep the scope of the variables limited to the execution of this function
|
|
// otherwise they stick around for the entire runtime of the program
|
|
// the attribute noinline is used to prevent the compiler from inlining this function back into the main function
|
|
// this decision was based on the output of build/main.su after adding the -fstack-usage compile flag
|
|
static void __attribute__((noinline)) show_intro()
|
|
{
|
|
bool start_pressed = false;
|
|
u8 general_text_table_buffer[2048];
|
|
u8 press_start_text[32];
|
|
u8 press_start_text_length;
|
|
|
|
text_data_table general_text(general_text_table_buffer);
|
|
const u8 *text_entry;
|
|
|
|
general_text.decompress(get_compressed_general_table());
|
|
|
|
text_entry = general_text.get_text_entry(GENERAL_press_start);
|
|
press_start_text_length = get_string_length(text_entry);
|
|
memcpy(press_start_text, text_entry, press_start_text_length + 1);
|
|
text_entry = general_text.get_text_entry(GENERAL_intro_legal);
|
|
|
|
show_legal_text(text_entry);
|
|
show_gears_of_progress();
|
|
|
|
REG_BG1CNT = REG_BG1CNT | BG_PRIO(3);
|
|
|
|
key_poll(); // Reset the keys
|
|
curr_rom.load_rom();
|
|
|
|
obj_set_pos(ptgb_logo_l, 56, 12);
|
|
obj_set_pos(ptgb_logo_r, 56 + 64, 12);
|
|
obj_unhide_multi(ptgb_logo_l, 1, 2);
|
|
|
|
REG_BLDCNT = BLD_BUILD(BLD_BG3, BLD_BG0, 1);
|
|
|
|
int char_width = (PTGB_BUILD_LANGUAGE == JPN_ID ? 8 : 6);
|
|
int x = ((240 - (press_start_text_length * char_width)) / 2);
|
|
tte_set_pos(x, 12 * 8);
|
|
|
|
tte_set_ink(INK_DARK_GREY);
|
|
ptgb_write(press_start_text, true);
|
|
|
|
int fade = 0;
|
|
while (!start_pressed)
|
|
{
|
|
fade = abs(((get_frame_count() / 6) % 24) - 12);
|
|
global_next_frame();
|
|
start_pressed = key_hit(KEY_START) | key_hit(KEY_A);
|
|
REG_BLDALPHA = BLDA_BUILD(0b10000, fade);
|
|
};
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
initalization_script();
|
|
|
|
// Set colors based on current ROM
|
|
set_background_pal(0, false, false);
|
|
|
|
/* First load message doesn't really make sense anymore, since you have to load the ROM first.
|
|
if (!get_tutorial_flag())
|
|
{
|
|
first_load_message();
|
|
}*/
|
|
|
|
show_intro();
|
|
|
|
key_poll();
|
|
tte_erase_rect(0, 0, H_MAX, V_MAX);
|
|
REG_BLDALPHA = BLDA_BUILD(0b10000, 0); // Reset fade
|
|
|
|
// Check if the game has been loaded correctly.
|
|
while (!curr_rom.load_rom())
|
|
{
|
|
obj_hide_multi(ptgb_logo_l, 2);
|
|
global_next_frame();
|
|
game_load_error();
|
|
// initalization_script();
|
|
}
|
|
|
|
// Initalize memory and save data after loading the game
|
|
reset_textbox();
|
|
REG_BG2CNT = REG_BG2CNT | BG_PRIO(3);
|
|
init_bank();
|
|
initalize_memory_locations();
|
|
load_custom_save_data();
|
|
|
|
set_background_pal(curr_rom.gamecode, false, true);
|
|
|
|
if (!IGNORE_MG_E4_FLAGS && (!get_tutorial_flag() || FORCE_TUTORIAL))
|
|
{
|
|
obj_hide_multi(ptgb_logo_l, 2);
|
|
text_loop(BTN_TRANSFER);
|
|
initalize_save_data();
|
|
}
|
|
|
|
obj_unhide_multi(ptgb_logo_l, 1, 2);
|
|
|
|
// MAIN LOOP
|
|
while (true)
|
|
{
|
|
if (DEBUG_MODE && false) // This isn't really needed anymore
|
|
{
|
|
print_mem_section();
|
|
curr_rom.print_rom_info();
|
|
}
|
|
load_flex_background(BG_MAIN_MENU, 2);
|
|
|
|
obj_unhide_multi(ptgb_logo_l, 1, 2);
|
|
obj_set_pos(ptgb_logo_l, 56, 12);
|
|
obj_set_pos(ptgb_logo_r, 56 + 64, 12);
|
|
|
|
switch (main_menu_loop())
|
|
{
|
|
case (BTN_TRANSFER):
|
|
tte_set_ink(INK_DARK_GREY);
|
|
obj_hide_multi(ptgb_logo_l, 2);
|
|
load_flex_background(BG_FENNEL, 3);
|
|
text_loop(BTN_TRANSFER);
|
|
break;
|
|
case (BTN_POKEDEX):
|
|
if (get_tutorial_flag())
|
|
{
|
|
obj_hide_multi(ptgb_logo_l, 2);
|
|
global_next_frame();
|
|
load_flex_background(BG_DEX, 2);
|
|
set_background_pal(curr_rom.gamecode, true, false);
|
|
pokedex_loop();
|
|
load_flex_background(BG_DEX, 3);
|
|
set_background_pal(curr_rom.gamecode, false, false);
|
|
}
|
|
break;
|
|
case (BTN_CREDITS):
|
|
tte_set_ink(INK_DARK_GREY);
|
|
// create_textbox(0, 0, 160, 80, true);
|
|
// show_text_box();
|
|
REG_BG1CNT = (REG_BG1CNT & ~BG_PRIO_MASK) | BG_PRIO(3);
|
|
obj_set_pos(ptgb_logo_l, 56, 108);
|
|
obj_set_pos(ptgb_logo_r, 56 + 64, 108);
|
|
credits();
|
|
break;
|
|
case (BTN_EVENTS):
|
|
obj_hide_multi(ptgb_logo_l, 2);
|
|
text_loop(BTN_EVENTS);
|
|
break;
|
|
default:
|
|
global_next_frame();
|
|
}
|
|
}
|
|
} |