Poke_Transporter_GB/source/pokedex.cpp
Philippe Symons 47cd143de6 Replace ZX0 by the builtin LZ10 compression.
LZ10 decompression is builtin to the GBA's bios, so we don't need ZX0. It's also significantly faster
(618 usec instead of 2311 usec in my personal benchmark code for decompression of the same data)

And it seems like by doing so, we saved 1 KB as well!

So, seems like replacing ZX0 is the right move.

The reason I didn't initially is because I misunderstood the documentation. I assumed LZ77UnCompWram could only uncompress into EWRAM, not IWRAM.
But it turns out it can do both.

And using standardized tools is usually better than using a custom implementation.

The only downside of this right now, is that we can no longer stream text tables through a smaller buffer than the entire decompressed size.

Anyway, things seem to work fine, so bye bye ZX0. It's been fun.
2025-07-18 16:19:34 +02:00

270 lines
7.8 KiB
C++

#include <tonc.h>
#include <cmath>
#include "pokedex.h"
#include "dex_handler.h"
#include "sprite_data.h"
#include "pokemon_data.h"
#include "global_frame_controller.h"
#include "save_data_manager.h"
#include "button_handler.h"
#include "translated_text.h"
#include "text_engine.h"
#include "text_data_table.h"
#include "TYPES_lz10_bin.h"
Dex dex_array[DEX_MAX];
int dex_shift = 0;
int dex_x_cord = 8;
int speed = 0;
int delay = 0;
int count = 0;
Button kanto_count;
Button johto_count;
int kanto_dex_num;
int johto_dex_num;
int kanto_offset;
int johto_offset;
bool mew_caught;
bool celebi_caught;
bool missingno_caught = false;
static void load_text_entry_into_buffer(text_data_table& data_table, u8 *output_buffer, u8 entry_index)
{
const u8 *entry = data_table.get_text_entry(entry_index);
const u8 *entry_end = (const u8*)strchr((const char*)entry, 0xFF);
// copy the text_entry including the 0xFF at the end
memcpy(output_buffer, entry, entry_end + 1 - entry);
}
static void load_general_table_text_entries(u8 *decompression_buffer, u8 *kanto_buffer, u8 *johto_buffer)
{
text_data_table data_table(decompression_buffer);
data_table.decompress(get_compressed_general_table());
load_text_entry_into_buffer(data_table, kanto_buffer, GENERAL_kanto_name);
load_text_entry_into_buffer(data_table, johto_buffer, GENERAL_johto_name);
}
void pokedex_init()
{
for (int i = 0; i < DEX_MAX; i++)
{
// dex_array[i] = Dex(dex_sprites[DEX_SPRITE_LEFT][i], dex_sprites[DEX_SPRITE_MID][i], dex_sprites[DEX_SPRITE_RIGHT][i]);
// dex_array[i].set_location(dex_x_cord, (i * (8 * 3)) + 8);
}
// kanto_count = Button(button_blank_left, button_blank_right, 64);
// johto_count = Button(button_blank_left_2, button_blank_right_2, 64);
kanto_dex_num = get_dex_completion(1, true);
johto_dex_num = get_dex_completion(2, true);
if (kanto_dex_num >= 100)
{
kanto_offset = 0;
}
else if (kanto_dex_num >= 10)
{
kanto_offset = 1;
}
else
{
kanto_offset = 2;
}
if (johto_dex_num >= 100)
{
johto_offset = 0;
}
else if (johto_dex_num >= 10)
{
johto_offset = 1;
}
else
{
johto_offset = 2;
}
mew_caught = is_caught(151);
celebi_caught = is_caught(251);
obj_unhide(up_arrow, 0);
obj_hide(down_arrow);
}
#include "gen_3_charsets_lz10_bin.h"
#include "libstd_replacements.h"
int pokedex_loop()
{
u8 TYPES[POKEMON_ARRAY_SIZE][2];
u8 kanto_name[12];
u8 johto_name[12];
u8 decompression_buffer[3072];
u16 charset[256];
LZ77UnCompWram(TYPES_lz10_bin, (u8*)TYPES);
LZ77UnCompWram(gen_3_charsets_lz10_bin, (u8*)charset);
load_general_table_text_entries(decompression_buffer, kanto_name, johto_name);
text_data_table PKMN_NAMES(decompression_buffer);
PKMN_NAMES.decompress(get_compressed_pkmn_names_table());
pokedex_init();
pokedex_show();
bool update = true;
const byte undiscovered_text[] = {0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xFF};
byte temp_string[4] = {}; // Should never be longer than 4 characters (including endline)
// TODO: For some reason there is screen tearing here. Probably not noticable on console,
// but it should be removed at some point
tte_set_pos(8, 148);
ptgb_write(kanto_name, true);
convert_int_to_ptgb_str(kanto_dex_num, temp_string, 3);
ptgb_write(temp_string, true);
temp_string[0] = 0xBA; // "/"
temp_string[1] = 0xFF;
ptgb_write(temp_string, true);
convert_int_to_ptgb_str(mew_caught ? 151 : 150, temp_string, 3);
ptgb_write(temp_string, true);
tte_set_pos(128, 148);
ptgb_write(johto_name, true);
convert_int_to_ptgb_str(johto_dex_num, temp_string, 3);
ptgb_write(temp_string, true);
temp_string[0] = 0xBA; // "/"
temp_string[1] = 0xFF;
ptgb_write(temp_string, true);
convert_int_to_ptgb_str(celebi_caught ? 100 : 99, temp_string, 3);
ptgb_write(temp_string, true);
while (true)
{
if (key_hit(KEY_B))
{
pokedex_hide();
return 0;
}
else if (key_hit(KEY_DOWN) || key_hit(KEY_UP))
{
dex_shift += key_tri_vert();
update = true;
}
else if (key_held(KEY_DOWN) || key_held(KEY_UP))
{
if (delay > SPEED_DELAY)
{
if ((get_frame_count() % speed == 0))
{
if (count > 3 && speed >= 5)
{
speed = speed / 2;
count = 0;
}
else
{
count++;
}
dex_shift += key_tri_vert();
update = true;
}
}
else
{
delay++;
}
}
else
{
speed = 20;
delay = 0;
count = 0;
}
if (dex_shift > DEX_SHIFT_MAX - !celebi_caught)
{
dex_shift = DEX_SHIFT_MAX - !celebi_caught;
}
else if (dex_shift < 0 - missingno_caught)
{
dex_shift = 0 - missingno_caught;
}
if (dex_shift == DEX_SHIFT_MAX - !celebi_caught)
{
obj_unhide(up_arrow, 0);
obj_hide(down_arrow);
}
else if (dex_shift == 0 - missingno_caught)
{
obj_hide(up_arrow);
obj_unhide(down_arrow, 0);
}
else
{
obj_unhide(up_arrow, 0);
obj_unhide(down_arrow, 0);
}
if (update)
{
tte_erase_rect(0, 0, 240, 140);
int mythic_skip = 0;
for (int i = 0; i < DEX_MAX; i++)
{
if (dex_shift + i + 1 == 151 && !mew_caught)
{
mythic_skip++;
}
if (is_caught(dex_shift + i + 1 + mythic_skip))
{
tte_set_pos(dex_x_cord + (1.5 * 8), (i * 8 * 2) + 32);
temp_string[0] = 0xF7;
temp_string[1] = 0xF8;
temp_string[2] = 0xFF;
ptgb_write(temp_string, true);
}
tte_set_pos(dex_x_cord + (3 * 8), (i * 8 * 2) + 32);
convert_int_to_ptgb_str(dex_shift + i + 1 + mythic_skip, temp_string, 3);
ptgb_write(temp_string, true);
tte_set_pos(dex_x_cord + (7 * 8), (i * 8 * 2) + 32);
ptgb_write(is_caught(dex_shift + i + 1 + mythic_skip) ? PKMN_NAMES.get_text_entry(dex_shift + i + 1 + mythic_skip) : undiscovered_text, true);
}
global_next_frame(); // This is a bit silly, but it works. Makes the types one frame off from the text, but that's 'fine'
// Eventually it could be optimized to move the labels around, but this honestly makes the most sense. Less code but one frame different
for (int i = 0; i < DEX_MAX; i++)
{
load_type_sprites((const u8*)TYPES, dex_shift + i + 1 + mythic_skip, i, is_caught(dex_shift + i + 1 + mythic_skip));
}
update = false;
}
global_next_frame();
}
}
void pokedex_show()
{
for (int i = 0; i < DEX_MAX; i++)
{
dex_array[i].show();
}
kanto_count.set_location(160, 40);
johto_count.set_location(160, 88);
kanto_count.show();
johto_count.show();
}
void pokedex_hide()
{
for (int i = 0; i < DEX_MAX; i++)
{
tte_erase_rect(0, 0, 240, 160);
dex_array[i].hide();
}
kanto_count.hide();
johto_count.hide();
obj_hide(up_arrow);
obj_hide(down_arrow);
obj_hide_multi(type_sprites[0], 14);
}