diff --git a/Makefile b/Makefile index 24bac82..096e075 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,9 @@ endif ASFLAGS := $(ARCH) LDFLAGS = -Os $(ARCH) -Wl,-Map,$(notdir $*.map) -Wl,--gc-sections -mthumb -mcpu=arm7tdmi -mtune=arm7tdmi -Wl,-Map,output.map,--cref -nodefaultlibs +# eliminate libsysbase_libsysbase_a-handle_manager.o and its 4KB IWRAM buffer +LDFLAGS += -Wl,--wrap=__get_handle -Wl,--wrap=_close_r + CFLAGS += -flto LDFLAGS += -flto diff --git a/source/global_frame_controller.cpp b/source/global_frame_controller.cpp index 883f2c9..d8197cf 100644 --- a/source/global_frame_controller.cpp +++ b/source/global_frame_controller.cpp @@ -116,7 +116,7 @@ void set_menu_sprite_pal(int frame) } } -int path[12][2] = {{19, 18}, {19, 19}, {18, 19}, {17, 19}, {16, 19}, {15, 19}, {14, 19}, {13, 19}, {12, 19}, {11, 19}, {10, 19}, {24, 24}}; +static const int path[12][2] = {{19, 18}, {19, 19}, {18, 19}, {17, 19}, {16, 19}, {15, 19}, {14, 19}, {13, 19}, {12, 19}, {11, 19}, {10, 19}, {24, 24}}; void run_link_cable_animation(int frame) { diff --git a/source/libstd_replacements.cpp b/source/libstd_replacements.cpp index 9fe3421..051f8aa 100644 --- a/source/libstd_replacements.cpp +++ b/source/libstd_replacements.cpp @@ -11,64 +11,6 @@ // unfortunately itoa is not standardized, so we'll have to make do with snprintf static char conversion_buffer[33]; -extern "C" -{ -// HACK: -// Unfortunately, libtonc references siscanf, which in turn causes a "lot" of binary bloat from newlibc (in regards to locale support) -// to be pulled in by the linker. -// However, if you look at what it is actually used for (in libtonc's tte_iohook.c), is just to extract 1 or 2 integers from a string -// by specifying a custom -extremely simplified- version, we can avoid pulling in the version from libc (alongside all the symbols IT references) -// Obviously it doesn't support everything it should. Just enough to support libtonc's current iteration. -// -// Anyway, doing this optimizes away a lot of scanf related functions from newlib and locale support among which a 13KB "categories" symbol. -int siscanf(const char *str, const char *format, ...) -{ - bool expectingFormatSpecifier = false; - va_list args; - int* resultVal; - int ret = 0; - va_start(args, format); - - while(*format != '\0') - { - if(*str == '\0') - { - //EOF encountered. - return -1; - } - - if(expectingFormatSpecifier) - { - if(*format == 'd') - { - resultVal = va_arg(args, int*); - (*resultVal) = 0; - while(isdigit(*str)) - { - (*resultVal) *= 10; - (*resultVal) += (*str) - '0'; - ++str; - } - // go back to the last character of the int, because we'll forward by one again at the end of the outer loop - --str; - ++ret; - } - expectingFormatSpecifier = false; - } - else if((*format) == '%') - { - expectingFormatSpecifier = true; - } - - ++format; - ++str; - } - - va_end(args); - return ret; -} -} - const char* ptgb::to_string(int intVal) { npf_snprintf(conversion_buffer, sizeof(conversion_buffer), "%d", intVal); diff --git a/source/linker_optimizations.cpp b/source/linker_optimizations.cpp new file mode 100644 index 0000000..90eb450 --- /dev/null +++ b/source/linker_optimizations.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include + +#include + +#define MAX_HANDLES 3 + +static __handle __stdin_handle = {0, 1, NULL}; +static __handle __stdout_handle = {1, 1, NULL}; +static __handle __stderr_handle = {2, 1, NULL}; + +static __handle* handles[MAX_HANDLES] = { + &__stdin_handle, + &__stdout_handle, + &__stderr_handle +}; + +extern "C" +{ +// HACK: +// Unfortunately, libtonc references siscanf, which in turn causes a "lot" of binary bloat from newlibc (in regards to locale support) +// to be pulled in by the linker. +// However, if you look at what it is actually used for (in libtonc's tte_iohook.c), is just to extract 1 or 2 integers from a string +// by specifying a custom -extremely simplified- version, we can avoid pulling in the version from libc (alongside all the symbols IT references) +// Obviously it doesn't support everything it should. Just enough to support libtonc's current iteration. +// +// Anyway, doing this optimizes away a lot of scanf related functions from newlib and locale support among which a 13KB "categories" symbol. +int siscanf(const char *str, const char *format, ...) +{ + bool expectingFormatSpecifier = false; + va_list args; + int* resultVal; + int ret = 0; + va_start(args, format); + + while(*format != '\0') + { + if(*str == '\0') + { + //EOF encountered. + return -1; + } + + if(expectingFormatSpecifier) + { + if(*format == 'd') + { + resultVal = va_arg(args, int*); + (*resultVal) = 0; + while(isdigit(*str)) + { + (*resultVal) *= 10; + (*resultVal) += (*str) - '0'; + ++str; + } + // go back to the last character of the int, because we'll forward by one again at the end of the outer loop + --str; + ++ret; + } + expectingFormatSpecifier = false; + } + else if((*format) == '%') + { + expectingFormatSpecifier = true; + } + + ++format; + ++str; + } + + va_end(args); + return ret; +} + +// HACK: stub the libsysbase_libsysbase_a-handle_manager.o functions +// this, alongside the -Wl,--wrap linker options ensures that this object file +// and its 4KB IWRAM "handles" buffer is not getting pulled in by the linker. +// We don't need it and we could use the 4KB of extra IWRAM! +__handle *__wrap___get_handle(int fd) +{ + if ( fd < 0 || fd >= MAX_HANDLES ) return NULL; + + return handles[fd]; +} + +int __wrap__close_r(struct _reent *ptr, int fd) +{ + return 0; +} +} \ No newline at end of file diff --git a/source/pokemon.cpp b/source/pokemon.cpp index 05e9862..981e406 100644 --- a/source/pokemon.cpp +++ b/source/pokemon.cpp @@ -947,7 +947,6 @@ void Pokemon::set_to_event(PokemonTables &data_tables, byte nature) enable_auto_random(); // Determine and set Ability - data_tables.load_num_abilities(); iv_egg_ability |= ((pid[0] & 0x1) ? data_tables.get_num_abilities(species_index_struct) : 0) << 31; // Set IVs, Egg, and Ability diff --git a/source/text_engine.cpp b/source/text_engine.cpp index 1d37325..01ad03d 100644 --- a/source/text_engine.cpp +++ b/source/text_engine.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include "text_engine.h" #include "global_frame_controller.h" diff --git a/tools/data-generator/include/optimize_movesets.h b/tools/data-generator/include/optimize_movesets.h new file mode 100644 index 0000000..a302d42 --- /dev/null +++ b/tools/data-generator/include/optimize_movesets.h @@ -0,0 +1,32 @@ +#ifndef _OPTIMIZE_MOVESETS_H +#define _OPTIMIZE_MOVESETS_H + +#include "pokemon_data.h" + +typedef struct optimize_moveset_result +{ + u16 num_unique_rows; + u16 num_bytes; +} optimize_moveset_result; + +/** + * input_buffer has 32 bytes for each pokémon. The first row is index 0, which doesn't actually represent a pokémon + * because the pokémon species index is 1-based. + * + * Anyway, the 32 bytes of the input_data have 1 bit for every available gen I/II move + * + * But we want to optimize it better for compression. For instance, there's a lot of overlap between the bits of evolutions vs base forms. + * So, as optimization, we could clear every move bit of an evolution that is also set for its base form. + * + * This way we can make the evolutions' moveset data a lot of zeros. That will compress better. + * + * But another optimization we can do is to deduplicate the resulting table and only store the unique rows. + * + * TL;DR: this function will turn the original MOVESETS table into this format: + * + * First 252 bytes: this chunk will associate a row index to every pokemon based on (species index - 1) + * Next bytes: rows of 32 bytes each for which every bit of these 32 bytes associates with a gen 1/2 move. + */ +optimize_moveset_result optimize_movesets(u8 *output_buffer, const u8 *input_buffer, u16 input_buffer_size); + +#endif \ No newline at end of file diff --git a/tools/data-generator/include/pokemon_data.h b/tools/data-generator/include/pokemon_data.h index dc0db0a..3235cc9 100644 --- a/tools/data-generator/include/pokemon_data.h +++ b/tools/data-generator/include/pokemon_data.h @@ -1,6 +1,20 @@ #ifndef _POKEMON_DATA_H #define _POKEMON_DATA_H +#include +#include + +#define NUM_POKEMON 252 +#define POKEMON_ARRAY_SIZE NUM_POKEMON + 1 + +typedef uint8_t u8; +typedef uint8_t byte; +typedef uint16_t u16; +typedef uint32_t u32; + +extern const byte MOVESETS[POKEMON_ARRAY_SIZE][32]; +extern const size_t MOVESETS_size; + void generate_pokemon_data(); #endif \ No newline at end of file diff --git a/tools/data-generator/src/optimize_movesets.cpp b/tools/data-generator/src/optimize_movesets.cpp new file mode 100644 index 0000000..aacaa6a --- /dev/null +++ b/tools/data-generator/src/optimize_movesets.cpp @@ -0,0 +1,446 @@ +#include "optimize_movesets.h" +#include + +const u8 EVOLUTIONS[POKEMON_ARRAY_SIZE]{ + 0, // Offset the list to remove "off by one" errors + 0x0, // Bulbasaur is a base evolution + 0x1, // Ivysaur evolves from Bulbasaur + 0x2, // Venusaur evolves from Ivysaur + 0x0, // Charmander is a base evolution + 0x4, // Charmeleon evolves from Charmander + 0x5, // Charizard evolves from Charmeleon + 0x0, // Squirtle is a base evolution + 0x7, // Wartortle evolves from Squirtle + 0x8, // Blastoise evolves from Wartortle + 0x0, // Caterpie is a base evolution + 0xa, // Metapod evolves from Caterpie + 0xb, // Butterfree evolves from Metapod + 0x0, // Weedle is a base evolution + 0xd, // Kakuna evolves from Weedle + 0xe, // Beedrill evolves from Kakuna + 0x0, // Pidgey is a base evolution + 0x10, // Pidgeotto evolves from Pidgey + 0x11, // Pidgeot evolves from Pidgeotto + 0x0, // Rattata is a base evolution + 0x13, // Raticate evolves from Rattata + 0x0, // Spearow is a base evolution + 0x15, // Fearow evolves from Spearow + 0x0, // Ekans is a base evolution + 0x17, // Arbok evolves from Ekans + 0xac, // Pikachu evolves from Pichu + 0x19, // Raichu evolves from Pikachu + 0x0, // Sandshrew is a base evolution + 0x1b, // Sandslash evolves from Sandshrew + 0x0, // Nidoran-f is a base evolution + 0x1d, // Nidorina evolves from Nidoran-f + 0x1e, // Nidoqueen evolves from Nidorina + 0x0, // Nidoran-m is a base evolution + 0x20, // Nidorino evolves from Nidoran-m + 0x21, // Nidoking evolves from Nidorino + 0xad, // Clefairy evolves from Cleffa + 0x23, // Clefable evolves from Clefairy + 0x0, // Vulpix is a base evolution + 0x25, // Ninetales evolves from Vulpix + 0xae, // Jigglypuff evolves from Igglybuff + 0x27, // Wigglytuff evolves from Jigglypuff + 0x0, // Zubat is a base evolution + 0x29, // Golbat evolves from Zubat + 0x0, // Oddish is a base evolution + 0x2b, // Gloom evolves from Oddish + 0x2c, // Vileplume evolves from Gloom + 0x0, // Paras is a base evolution + 0x2e, // Parasect evolves from Paras + 0x0, // Venonat is a base evolution + 0x30, // Venomoth evolves from Venonat + 0x0, // Diglett is a base evolution + 0x32, // Dugtrio evolves from Diglett + 0x0, // Meowth is a base evolution + 0x34, // Persian evolves from Meowth + 0x0, // Psyduck is a base evolution + 0x36, // Golduck evolves from Psyduck + 0x0, // Mankey is a base evolution + 0x38, // Primeape evolves from Mankey + 0x0, // Growlithe is a base evolution + 0x3a, // Arcanine evolves from Growlithe + 0x0, // Poliwag is a base evolution + 0x3c, // Poliwhirl evolves from Poliwag + 0x3d, // Poliwrath evolves from Poliwhirl + 0x0, // Abra is a base evolution + 0x3f, // Kadabra evolves from Abra + 0x40, // Alakazam evolves from Kadabra + 0x0, // Machop is a base evolution + 0x42, // Machoke evolves from Machop + 0x43, // Machamp evolves from Machoke + 0x0, // Bellsprout is a base evolution + 0x45, // Weepinbell evolves from Bellsprout + 0x46, // Victreebel evolves from Weepinbell + 0x0, // Tentacool is a base evolution + 0x48, // Tentacruel evolves from Tentacool + 0x0, // Geodude is a base evolution + 0x4a, // Graveler evolves from Geodude + 0x4b, // Golem evolves from Graveler + 0x0, // Ponyta is a base evolution + 0x4d, // Rapidash evolves from Ponyta + 0x0, // Slowpoke is a base evolution + 0x4f, // Slowbro evolves from Slowpoke + 0x0, // Magnemite is a base evolution + 0x51, // Magneton evolves from Magnemite + 0x0, // Farfetchd is a base evolution + 0x0, // Doduo is a base evolution + 0x54, // Dodrio evolves from Doduo + 0x0, // Seel is a base evolution + 0x56, // Dewgong evolves from Seel + 0x0, // Grimer is a base evolution + 0x58, // Muk evolves from Grimer + 0x0, // Shellder is a base evolution + 0x5a, // Cloyster evolves from Shellder + 0x0, // Gastly is a base evolution + 0x5c, // Haunter evolves from Gastly + 0x5d, // Gengar evolves from Haunter + 0x0, // Onix is a base evolution + 0x0, // Drowzee is a base evolution + 0x60, // Hypno evolves from Drowzee + 0x0, // Krabby is a base evolution + 0x62, // Kingler evolves from Krabby + 0x0, // Voltorb is a base evolution + 0x64, // Electrode evolves from Voltorb + 0x0, // Exeggcute is a base evolution + 0x66, // Exeggutor evolves from Exeggcute + 0x0, // Cubone is a base evolution + 0x68, // Marowak evolves from Cubone + 0xec, // Hitmonlee evolves from Tyrogue + 0xec, // Hitmonchan evolves from Tyrogue + 0x0, // Lickitung is a base evolution + 0x0, // Koffing is a base evolution + 0x6d, // Weezing evolves from Koffing + 0x0, // Rhyhorn is a base evolution + 0x6f, // Rhydon evolves from Rhyhorn + 0x0, // Chansey is a base evolution (in gen 2) + 0x0, // Tangela is a base evolution + 0x0, // Kangaskhan is a base evolution + 0x0, // Horsea is a base evolution + 0x74, // Seadra evolves from Horsea + 0x0, // Goldeen is a base evolution + 0x76, // Seaking evolves from Goldeen + 0x0, // Staryu is a base evolution + 0x78, // Starmie evolves from Staryu + 0x0, // Mr-mime is a base evolution (in gen 2) + 0x0, // Scyther is a base evolution + 0xee, // Jynx evolves from Smoochum + 0xef, // Electabuzz evolves from Elekid + 0xf0, // Magmar evolves from Magby + 0x0, // Pinsir is a base evolution + 0x0, // Tauros is a base evolution + 0x0, // Magikarp is a base evolution + 0x81, // Gyarados evolves from Magikarp + 0x0, // Lapras is a base evolution + 0x0, // Ditto is a base evolution + 0x0, // Eevee is a base evolution + 0x85, // Vaporeon evolves from Eevee + 0x85, // Jolteon evolves from Eevee + 0x85, // Flareon evolves from Eevee + 0x0, // Porygon is a base evolution + 0x0, // Omanyte is a base evolution + 0x8a, // Omastar evolves from Omanyte + 0x0, // Kabuto is a base evolution + 0x8c, // Kabutops evolves from Kabuto + 0x0, // Aerodactyl is a base evolution + 0x0, // Snorlax is a base evolution (in gen 2) + 0x0, // Articuno is a base evolution + 0x0, // Zapdos is a base evolution + 0x0, // Moltres is a base evolution + 0x0, // Dratini is a base evolution + 0x93, // Dragonair evolves from Dratini + 0x94, // Dragonite evolves from Dragonair + 0x0, // Mewtwo is a base evolution + 0x0, // Mew is a base evolution + 0x0, // Chikorita is a base evolution + 0x98, // Bayleef evolves from Chikorita + 0x99, // Meganium evolves from Bayleef + 0x0, // Cyndaquil is a base evolution + 0x9b, // Quilava evolves from Cyndaquil + 0x9c, // Typhlosion evolves from Quilava + 0x0, // Totodile is a base evolution + 0x9e, // Croconaw evolves from Totodile + 0x9f, // Feraligatr evolves from Croconaw + 0x0, // Sentret is a base evolution + 0xa1, // Furret evolves from Sentret + 0x0, // Hoothoot is a base evolution + 0xa3, // Noctowl evolves from Hoothoot + 0x0, // Ledyba is a base evolution + 0xa5, // Ledian evolves from Ledyba + 0x0, // Spinarak is a base evolution + 0xa7, // Ariados evolves from Spinarak + 0x2a, // Crobat evolves from Golbat + 0x0, // Chinchou is a base evolution + 0xaa, // Lanturn evolves from Chinchou + 0x0, // Pichu is a base evolution + 0x0, // Cleffa is a base evolution + 0x0, // Igglybuff is a base evolution + 0x0, // Togepi is a base evolution + 0xaf, // Togetic evolves from Togepi + 0x0, // Natu is a base evolution + 0xb1, // Xatu evolves from Natu + 0x0, // Mareep is a base evolution + 0xb3, // Flaaffy evolves from Mareep + 0xb4, // Ampharos evolves from Flaaffy + 0x2c, // Bellossom evolves from Gloom + 0x0, // Marill is a base evolution (in gen 2) + 0xb7, // Azumarill evolves from Marill + 0x0, // Sudowoodo is a base evolution (in gen 2) + 0x3d, // Politoed evolves from Poliwhirl + 0x0, // Hoppip is a base evolution + 0xbb, // Skiploom evolves from Hoppip + 0xbc, // Jumpluff evolves from Skiploom + 0x0, // Aipom is a base evolution + 0x0, // Sunkern is a base evolution + 0xbf, // Sunflora evolves from Sunkern + 0x0, // Yanma is a base evolution + 0x0, // Wooper is a base evolution + 0xc2, // Quagsire evolves from Wooper + 0x85, // Espeon evolves from Eevee + 0x85, // Umbreon evolves from Eevee + 0x0, // Murkrow is a base evolution + 0x4f, // Slowking evolves from Slowpoke + 0x0, // Misdreavus is a base evolution + 0x0, // Unown is a base evolution + 0x0, // Wobbuffet is a base evolution (in gen 2) + 0x0, // Girafarig is a base evolution + 0x0, // Pineco is a base evolution + 0xcc, // Forretress evolves from Pineco + 0x0, // Dunsparce is a base evolution + 0x0, // Gligar is a base evolution + 0x5f, // Steelix evolves from Onix + 0x0, // Snubbull is a base evolution + 0xd1, // Granbull evolves from Snubbull + 0x0, // Qwilfish is a base evolution + 0x7b, // Scizor evolves from Scyther + 0x0, // Shuckle is a base evolution + 0x0, // Heracross is a base evolution + 0x0, // Sneasel is a base evolution + 0x0, // Teddiursa is a base evolution + 0xd8, // Ursaring evolves from Teddiursa + 0x0, // Slugma is a base evolution + 0xda, // Magcargo evolves from Slugma + 0x0, // Swinub is a base evolution + 0xdc, // Piloswine evolves from Swinub + 0x0, // Corsola is a base evolution + 0x0, // Remoraid is a base evolution + 0xdf, // Octillery evolves from Remoraid + 0x0, // Delibird is a base evolution + 0x0, // Mantine is a base evolution (in gen 2) + 0x0, // Skarmory is a base evolution + 0x0, // Houndour is a base evolution + 0xe4, // Houndoom evolves from Houndour + 0x75, // Kingdra evolves from Seadra + 0x0, // Phanpy is a base evolution + 0xe7, // Donphan evolves from Phanpy + 0x89, // Porygon2 evolves from Porygon + 0x0, // Stantler is a base evolution + 0x0, // Smeargle is a base evolution + 0x0, // Tyrogue is a base evolution + 0xec, // Hitmontop evolves from Tyrogue + 0x0, // Smoochum is a base evolution + 0x0, // Elekid is a base evolution + 0x0, // Magby is a base evolution + 0x0, // Miltank is a base evolution + 0x71, // Blissey evolves from Chansey + 0x0, // Raikou is a base evolution + 0x0, // Entei is a base evolution + 0x0, // Suicune is a base evolution + 0x0, // Larvitar is a base evolution + 0xf6, // Pupitar evolves from Larvitar + 0xf7, // Tyranitar evolves from Pupitar + 0x0, // Lugia is a base evolution + 0x0, // Ho-oh is a base evolution + 0x0, // Celebi is a base evolution + 0x0, // Treecko is a base evolution +}; + +static void deduplicate_bits_across_evolutions(u8 *output_buffer, const u8 *input_buffer) +{ + const u8 *cur_input = input_buffer; + const u8 ROWSIZE = 32; + u8 base_moveset[ROWSIZE]; + u8 evolution; + u8 i, j; + for(i=0; i < POKEMON_ARRAY_SIZE; ++i) + { + evolution = EVOLUTIONS[i]; + if(evolution == 0) + { + memcpy(output_buffer, cur_input, ROWSIZE); + } + else + { + memcpy(base_moveset, input_buffer + (evolution * ROWSIZE), ROWSIZE); + // combine moveset of all base forms before comparing with our current moveset + // at most 2 base forms in gen I or gen II + evolution = EVOLUTIONS[evolution]; + + if(evolution) + { + for(j = 0; j < ROWSIZE; ++j) + { + base_moveset[j] |= input_buffer[evolution * ROWSIZE + j]; + } + } + // we want to set the resulting bit to 0 if it already was zero or if the basemoveset and current moveset both have 1 + // if only the current moveset is set to 1, it should be 1. + // This looks like a XOR operation is appropriate. + // technically a side-effect would be that if the base set has it set to 1 and the current moveset doesn't, that it will end up + // being a 1. But I don't think that's a valid scenario, especially because can_learn_move() in PTGB already OR'ed + // the results of evolution + bases together. + + for (j = 0; j < ROWSIZE; ++j) + { + output_buffer[j] = cur_input[j] ^ base_moveset[j]; + } + } + output_buffer += ROWSIZE; + cur_input += ROWSIZE; + } +} + +static u16 deduplicate_rows(u8 *output_buffer, const u8 *input_buffer) +{ + u16 num_unique_rows; + u8 *row_index = output_buffer; + u8 *row_space = output_buffer + POKEMON_ARRAY_SIZE; + const u8 ROWSIZE = 32; + u8 i; + int16_t j; + bool found; + + // first row is all zeros + memcpy(row_space, input_buffer, ROWSIZE); + row_index[0] = 0; + num_unique_rows = 1; + + for(i = 1; i < POKEMON_ARRAY_SIZE; ++i) + { + found = false; + for(j = num_unique_rows - 1; j >= 0; --j) + { + if(!memcmp(row_space + (j * ROWSIZE), input_buffer + (i * ROWSIZE), ROWSIZE)) + { + // matching entry found in previous rows. store a reference to it in the row index + row_index[i] = static_cast(j); + found = true; + break; + } + } + + if(!found) + { + // no matching entry found in previous rows (which means this is a unique entry up until now) + // insert the current entry into the resulting table and + memcpy(row_space + (num_unique_rows * ROWSIZE), input_buffer + (i * ROWSIZE), ROWSIZE); + row_index[i] = num_unique_rows; + ++num_unique_rows; + } + } + + return num_unique_rows; +} + +#include + +void check_unique_rows(const u8 *input_buffer) +{ + uint8_t unique_rows = 0; + const u8 *cur; + const u8 *row; + bool foundMatch; + + for(uint8_t i=0; i < POKEMON_ARRAY_SIZE; ++i) + { + cur = input_buffer + (i * 32); + row = input_buffer; + foundMatch = false; + + while(row < input_buffer + POKEMON_ARRAY_SIZE * 32) + { + if(row == cur) + { + row += 32; + continue; + } + if(!memcmp(cur, row, 32)) + { + foundMatch = true; + break; + } + row += 32; + } + + if(!foundMatch) + { + ++unique_rows; + } + } + + //printf("Unique rows: %hhu\n", unique_rows); +} + +/** + * This function is similar to the original implementation in PTGB's pokemon_data.cpp + * But it is used to test for differences between the optimized MOVESETS table and the unoptimized one. + */ +static bool can_learn_move(const u8* movesets_table, int pkmn_index, int move_index) +{ + u16 table_index = pkmn_index * 32 + (move_index / 8); + byte data_byte = movesets_table[table_index]; + table_index = EVOLUTIONS[pkmn_index] * 32 + (move_index / 8); + data_byte |= movesets_table[table_index]; // add in the previous evolution's moves (if they exist) + table_index = EVOLUTIONS[EVOLUTIONS[pkmn_index]] * 32 + (move_index / 8); + data_byte |= movesets_table[table_index]; // add in the first evolution's moves (if they exist) + return (data_byte >> (7 - (move_index % 8))) & 0x1; +} + +optimize_moveset_result optimize_movesets(u8 *output_buffer, const u8 *input_buffer, u16 input_buffer_size) +{ + optimize_moveset_result result = {0, 0}; + u8 work_buffer[MOVESETS_size]; + + if(input_buffer_size % 32) + { + // invalid input buffer. Must be divisible by 32 bytes! + return result; + } + + deduplicate_bits_across_evolutions(output_buffer, input_buffer); + check_unique_rows(output_buffer); + result.num_bytes = input_buffer_size; + + #if 0 + // test optimized table + bool problem_found = false; + for(u16 i=0; i < POKEMON_ARRAY_SIZE; ++i) + { + for(u16 move = 0; move < 256; ++move) + { + if(can_learn_move((const uint8_t*)MOVESETS, i, move) != can_learn_move(output_buffer, i, move)) + { + printf("ERROR: optimized MOVESETS differs at pkmn %hu, move %hhu!\n", i, move); + problem_found = true; + } + } + } + if(problem_found) + { + printf("ERROR: one or more issues detected in the optimized MOVESETS table\n"); + } + else + { + printf("INFO: no problems found in the optimized MOVESETS table!\n"); + } + #endif + + //deduplicate_bits_across_evolutions(work_buffer, input_buffer); + //check_unique_rows(work_buffer); + //result.num_unique_rows = deduplicate_rows(output_buffer, work_buffer); + //result.num_bytes = NUM_POKEMON + (result.num_unique_rows * 32); + + return result; +} \ No newline at end of file diff --git a/tools/data-generator/src/pokemon_data.cpp b/tools/data-generator/src/pokemon_data.cpp index f710c17..3e5c38b 100644 --- a/tools/data-generator/src/pokemon_data.cpp +++ b/tools/data-generator/src/pokemon_data.cpp @@ -1,12 +1,8 @@ #include "pokemon_data.h" #include "common.h" +#include "optimize_movesets.h" -typedef unsigned char u8; -typedef unsigned char byte; -typedef unsigned short u16; - -#define NUM_POKEMON 252 -#define POKEMON_ARRAY_SIZE NUM_POKEMON + 1 +#include const u16 gen_1_charsets[4][256] { // gen_1_Jpn_char_array @@ -3630,6 +3626,9 @@ const byte MOVESETS[POKEMON_ARRAY_SIZE][32] = { {0b00000000, 0b00000011, 0b00000000, 0b00000000, 0b00000010, 0b00000000, 0b00000000, 0b00000001, 0b00000000, 0b01001000, 0b00000000, 0b00001110, 0b00000010, 0b11000001, 0b01010010, 0b00000000, 0b01000000, 0b00100000, 0b00001000, 0b00001000, 0b00001000, 0b00000100, 0b00000010, 0b00000100, 0b00010000, 0b01110001, 0b00000011, 0b10110000, 0b00100000, 0b00000100, 0b11001011, 0b10000000}, // Celebi {0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000010, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000}, // Treecko }; + +extern const size_t MOVESETS_size = sizeof(MOVESETS); + const byte FIRST_MOVES[POKEMON_ARRAY_SIZE] = { // Data obtained through PokeAPI 0, // Offset the list to remove "off by one" errors @@ -4966,13 +4965,19 @@ const u8 TYPES[POKEMON_ARRAY_SIZE][2]{ void generate_pokemon_data() { + u8 optimized_movesets[MOVESETS_size]; +// printf("Optimizing movesets table..."); + optimize_moveset_result optimize_moveset_status = optimize_movesets(optimized_movesets, (const uint8_t*)(MOVESETS), MOVESETS_size); +// printf("done!\n\t-> unique rows: %hu, bytes: %hu\n", optimize_moveset_status.num_unique_rows, optimize_moveset_status.num_bytes); + writeTable("gen_1_charsets.bin", (const uint8_t*)gen_1_charsets, sizeof(gen_1_charsets)); writeTable("gen_2_charsets.bin", (const uint8_t*)gen_2_charsets, sizeof(gen_2_charsets)); writeTable("gen_3_charsets.bin", (const uint8_t*)gen_3_charsets, sizeof(gen_3_charsets)); writeTable("EXP_GROUPS.bin", EXP_GROUPS, sizeof(EXP_GROUPS)); writeTable("GENDER_RATIO.bin", GENDER_RATIO, sizeof(GENDER_RATIO)); writeTable("NUM_ABILITIES.bin", (const uint8_t*)(NUM_ABILITIES), sizeof(NUM_ABILITIES)); - writeTable("MOVESETS.bin", (const uint8_t*)(MOVESETS), sizeof(MOVESETS)); +// writeTable("MOVESETS.bin", (const uint8_t*)(MOVESETS), sizeof(MOVESETS)); + writeTable("MOVESETS.bin", optimized_movesets, optimize_moveset_status.num_bytes); writeTable("FIRST_MOVES.bin", FIRST_MOVES, sizeof(FIRST_MOVES)); writeTable("JPN_NAMES.bin", (const uint8_t*)JPN_NAMES, sizeof(JPN_NAMES)); writeTable("POWER_POINTS.bin", POWER_POINTS, sizeof(POWER_POINTS));