#include "global.h"
#include "gba/m4a_internal.h"
#include "pokedex.h"
#include "battle.h"
#include "data2.h"
#include "decompress.h"
#include "event_data.h"
#include "graphics.h"
#include "m4a.h"
#include "main.h"
#include "menu.h"
#include "menu_cursor.h"
#include "palette.h"
#include "pokedex_area_screen.h"
#include "pokedex_cry_screen.h"
#include "pokemon.h"
#include "random.h"
#include "overworld.h"
#include "constants/songs.h"
#include "sound.h"
#include "constants/species.h"
#include "string_util.h"
#include "strings.h"
#include "task.h"
#include "trig.h"
#include "scanline_effect.h"
#include "ewram.h"

struct PokedexListItem
{
    u16 dexNum;
    u16 seen:1;
    u16 owned:1;
};

struct PokedexView
{
    struct PokedexListItem pokedexList[NATIONAL_DEX_COUNT + 1];
    u16 pokemonListCount;
    u16 selectedPokemon;
    u16 selectedPokemonBackup;
    u16 dexMode;
    u16 dexModeBackup;
    u16 dexOrder;
    u16 dexOrderBackup;
    u16 seenCount;
    u16 ownCount;
    u16 monSpriteIds[4];
    u16 selectedMonSpriteId;
    s16 pokeBallRotationStep;
    s16 pokeBallRotationBackup;
    u8 pokeBallRotation;
    u8 initialVOffset;
    u8 scrollTimer;
    u8 scrollDirection;
    s16 listVOffset;
    s16 listMovingVOffset;
    s16 scrollMonIncrement;
    s16 maxScrollTimer;
    u16 scrollSpeed;
    u16 unk63A[4]; // Cleared, never read
    u8 unk642[8];
    u8 currentPage;
    u8 currentPageBackup;
    u8 isSearchResults:1;
    u8 selectedScreen;
    u8 descriptionPageNum;
    u8 screenSwitchState;
    u8 menuIsOpen;
    u16 menuCursorPos;
    s16 menuY;     //Menu Y position (inverted because we use REG_BG0VOFS for this)
    u8 unk656[8]; // Cleared, never read
    u8 unk65E[8]; // Cleared, never read
};

enum
{
    DEX_MODE_HOENN,
    DEX_MODE_NATIONAL
};

enum
{
    PAGE_SCREEN,
    AREA_SCREEN,
    CRY_SCREEN,
    SIZE_SCREEN
};

struct SearchOptionText
{
    const u8 *description;
    const u8 *title;
};

struct SearchOption
{
    const struct SearchOptionText *texts;
    u8 taskDataCursorPos;
    u8 taskDataScrollOffset;
    u16 numOptions;
};

struct SearchMenuTopBarItem
{
    const u8 *text;
    u8 highlightX;
    u8 highlightY;
    u8 highlightWidth;
};

struct SearchMenuItem
{
    const u8 *text;
    u8 titleBgX;
    u8 titleBgY;
    u8 titleBgWidth;
    u8 selectionBgX;
    u8 selectionBgY;
    u8 selectionBgWidth;
};

extern struct MusicPlayerInfo gMPlayInfo_BGM;
extern u8 gReservedSpritePaletteCount;
extern struct SpriteTemplate gCreatingSpriteTemplate;
extern u8 gDexCryScreenState;
extern const u8 gPokedexMenu_Gfx[];
extern const u8 gUnknown_08E96738[];
extern const u8 gPokedexStartMenuMain_Tilemap[];
extern const u8 gPokedexStartMenuSearchResults_Tilemap[];
extern const u8 gUnknown_08E9C6DC[];
extern const u8 gUnknown_08E96BD4[];
extern const u8 gPokedexScreenSelectBarMain_Tilemap[];
extern const u8 gPokedexScreenSelectBarSubmenu_Tilemap[];
extern const u16 gPokedexMenu_Pal[];
extern const u16 gPokedexMenu2_Pal[];
extern const struct CompressedSpriteSheet gTrainerFrontPicTable[];
extern const struct MonCoords gTrainerFrontPicCoords[];
extern const struct PokedexEntry gPokedexEntries[];
extern const u8 gPokedexMenuSearch_Gfx[];
extern const u8 gPokedexMenuSearch_Tilemap[];
extern const u16 gPokedexMenuSearch_Pal[];
extern const u8 gTypeNames[][7];
extern const u8 gPokedexMenu2_Gfx[];

static EWRAM_DATA struct PokedexView *gPokedexView = NULL;
static EWRAM_DATA u16 sLastSelectedPokemon = 0;
static EWRAM_DATA u8 sPokeBallRotation = 0;
static EWRAM_DATA struct PokedexListItem *sPokedexListItem = NULL;

u8 gUnusedPokedexU8;
IntrCallback gPokedexVBlankCB;

static u8 LoadSearchMenu(void);

static const u16 sPokedexSearchPalette[] = INCBIN_U16("graphics/pokedex/search.gbapal");
static const u16 sNationalPokedexPalette[] = INCBIN_U16("graphics/pokedex/national.gbapal");
const u8 gEmptySpacce_839F7FC[0xA4] = {0};
static const u8 gUnknown_0839F8A0[] = INCBIN_U8("graphics/pokedex/pokedex_cry_layout.bin.lz");
static const u8 gUnknown_0839F988[] = INCBIN_U8("graphics/pokedex/pokedex_size_layout.bin.lz");
#if ENGLISH
static const u8 gUnknown_0839FA7C[] = INCBIN_U8("graphics/pokedex/noball.4bpp.lz");
#elif GERMAN
extern const u8 gUnknown_0839FA7C[];
#endif

#include "data/pokedex_orders.h"
static const struct OamData gOamData_83A0404 =
{
    .y = 160,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 0,
    .x = 0,
    .matrixNum = 0,
    .size = 0,
    .tileNum = 0,
    .priority = 1,
    .paletteNum = 0,
    .affineParam = 0,
};
static const struct OamData gOamData_83A040C =
{
    .y = 160,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 1,
    .x = 0,
    .matrixNum = 0,
    .size = 0,
    .tileNum = 0,
    .priority = 0,
    .paletteNum = 0,
    .affineParam = 0,
};
static const struct OamData gOamData_83A0414 =
{
    .y = 160,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 1,
    .x = 0,
    .matrixNum = 0,
    .size = 3,
    .tileNum = 0,
    .priority = 0,
    .paletteNum = 0,
    .affineParam = 0,
};
static const struct OamData gOamData_83A041C =
{
    .y = 160,
    .affineMode = 0,
    .objMode = 2,
    .mosaic = 0,
    .bpp = 0,
    .shape = 0,
    .x = 0,
    .matrixNum = 0,
    .size = 2,
    .tileNum = 0,
    .priority = 1,
    .paletteNum = 0,
    .affineParam = 0,
};
static const struct OamData gOamData_83A0424 =
{
    .y = 160,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 1,
    .x = 0,
    .matrixNum = 0,
    .size = 3,
    .tileNum = 0,
    .priority = 0,
    .paletteNum = 0,
    .affineParam = 0,
};
static const struct OamData gOamData_83A042C =
{
    .y = 160,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 2,
    .x = 0,
    .matrixNum = 0,
    .size = 0,
    .tileNum = 0,
    .priority = 0,
    .paletteNum = 0,
    .affineParam = 0,
};
static const union AnimCmd gSpriteAnim_83A0434[] =
{
    ANIMCMD_FRAME(3, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A043C[] =
{
    ANIMCMD_FRAME(1, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A0444[] =
{
    ANIMCMD_FRAME(16, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A044C[] =
{
    ANIMCMD_FRAME(32, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A0454[] =
{
    ANIMCMD_FRAME(64, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A045C[] =
{
    ANIMCMD_FRAME(96, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A0464[] =
{
    ANIMCMD_FRAME(128, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A046C[] =
{
    ANIMCMD_FRAME(160, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A0474[] =
{
    ANIMCMD_FRAME(192, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A047C[] =
{
    ANIMCMD_FRAME(224, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A0484[] =
{
    ANIMCMD_FRAME(226, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A048C[] =
{
    ANIMCMD_FRAME(228, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A0494[] =
{
    ANIMCMD_FRAME(230, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A049C[] =
{
    ANIMCMD_FRAME(232, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A04A4[] =
{
    ANIMCMD_FRAME(234, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A04AC[] =
{
    ANIMCMD_FRAME(236, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A04B4[] =
{
    ANIMCMD_FRAME(238, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A04BC[] =
{
    ANIMCMD_FRAME(240, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A04C4[] =
{
    ANIMCMD_FRAME(242, 30),
    ANIMCMD_END,
};
static const union AnimCmd gSpriteAnim_83A04CC[] =
{
    ANIMCMD_FRAME(4, 30),
    ANIMCMD_END,
};
static const union AnimCmd *const gSpriteAnimTable_83A04D4[] =
{
    gSpriteAnim_83A0434,
};
static const union AnimCmd *const gSpriteAnimTable_83A04D8[] =
{
    gSpriteAnim_83A043C,
};
static const union AnimCmd *const gSpriteAnimTable_83A04DC[] =
{
    gSpriteAnim_83A0444,
};
static const union AnimCmd *const gSpriteAnimTable_83A04E0[] =
{
    gSpriteAnim_83A044C,
    gSpriteAnim_83A0454,
    gSpriteAnim_83A045C,
    gSpriteAnim_83A0464,
};
static const union AnimCmd *const gSpriteAnimTable_83A04F0[] =
{
    gSpriteAnim_83A046C,
    gSpriteAnim_83A0474,
};
static const union AnimCmd *const gSpriteAnimTable_83A04F8[] =
{
    gSpriteAnim_83A047C,
    gSpriteAnim_83A0484,
    gSpriteAnim_83A048C,
    gSpriteAnim_83A0494,
    gSpriteAnim_83A049C,
    gSpriteAnim_83A04A4,
    gSpriteAnim_83A04AC,
    gSpriteAnim_83A04B4,
    gSpriteAnim_83A04BC,
    gSpriteAnim_83A04C4,
};
static const union AnimCmd *const gSpriteAnimTable_83A0520[] =
{
    gSpriteAnim_83A04CC,
};
static void SpriteCB_Scrollbar(struct Sprite *);
static const struct SpriteTemplate gSpriteTemplate_83A0524 =
{
    .tileTag = 4096,
    .paletteTag = 4096,
    .oam = &gOamData_83A0404,
    .anims = gSpriteAnimTable_83A04D4,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_Scrollbar,
};
static void SpriteCB_ScrollArrow(struct Sprite *);
static const struct SpriteTemplate sScrollArrowSpriteTemplate =
{
    .tileTag = 4096,
    .paletteTag = 4096,
    .oam = &gOamData_83A040C,
    .anims = gSpriteAnimTable_83A04D8,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_ScrollArrow,
};
static void SpriteCB_DexListInterfaceText(struct Sprite *);
static const struct SpriteTemplate gSpriteTemplate_83A0554 =
{
    .tileTag = 4096,
    .paletteTag = 4096,
    .oam = &gOamData_83A0414,
    .anims = gSpriteAnimTable_83A04E0,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_DexListInterfaceText,
};
static void SpriteCB_RotatingPokeBall(struct Sprite *);
static const struct SpriteTemplate gSpriteTemplate_83A056C =
{
    .tileTag = 4096,
    .paletteTag = 4096,
    .oam = &gOamData_83A041C,
    .anims = gSpriteAnimTable_83A04DC,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_RotatingPokeBall,
};
static void SpriteCB_SeenOwnInfo(struct Sprite *);
static const struct SpriteTemplate gSpriteTemplate_83A0584 =
{
    .tileTag = 4096,
    .paletteTag = 4096,
    .oam = &gOamData_83A0424,
    .anims = gSpriteAnimTable_83A04F0,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_SeenOwnInfo,
};
static const struct SpriteTemplate gSpriteTemplate_83A059C =
{
    .tileTag = 4096,
    .paletteTag = 4096,
    .oam = &gOamData_83A042C,
    .anims = gSpriteAnimTable_83A04F8,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_SeenOwnInfo,
};
static void SpriteCB_DexListStartMenuCursor(struct Sprite *);
static const struct SpriteTemplate gSpriteTemplate_83A05B4 =
{
    .tileTag = 4096,
    .paletteTag = 4096,
    .oam = &gOamData_83A042C,
    .anims = gSpriteAnimTable_83A0520,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_DexListStartMenuCursor,
};
static const struct CompressedSpriteSheet sInterfaceSpriteSheet[] =
{
    {gPokedexMenu2_Gfx, 0x1F00, 0x1000},
    {NULL, 0, 0},
};
static const struct SpritePalette sInterfaceSpritePalette[] =
{
    {gPokedexMenu_Pal, 0x1000},
    {NULL, 0},
};
static const u8 gUnknown_083A05EC[] = {2, 4, 8, 16, 32};
static const u8 gUnknown_083A05F1[] = {16, 8, 4, 2, 1};
const u8 gEmptySpacce_83A05F6[] = {0, 0};  // Padding, maybe?
static const u8 gUnknown_083A05F8[] = _("");
#if ENGLISH
#include "data/pokedex_entries_en.h"
#elif GERMAN
#include "data/pokedex_entries_de.h"
#endif
static const u16 sSizeScreenSilhouette_Pal[16] = {0};
static const u8 *const sMonFootprintTable[] =
{
    gMonFootprint_Bulbasaur,
    gMonFootprint_Bulbasaur,
    gMonFootprint_Ivysaur,
    gMonFootprint_Venusaur,
    gMonFootprint_Charmander,
    gMonFootprint_Charmeleon,
    gMonFootprint_Charizard,
    gMonFootprint_Squirtle,
    gMonFootprint_Wartortle,
    gMonFootprint_Blastoise,
    gMonFootprint_Caterpie,
    gMonFootprint_Metapod,
    gMonFootprint_Butterfree,
    gMonFootprint_Weedle,
    gMonFootprint_Kakuna,
    gMonFootprint_Beedrill,
    gMonFootprint_Pidgey,
    gMonFootprint_Pidgeotto,
    gMonFootprint_Pidgeot,
    gMonFootprint_Rattata,
    gMonFootprint_Raticate,
    gMonFootprint_Spearow,
    gMonFootprint_Fearow,
    gMonFootprint_Ekans,
    gMonFootprint_Arbok,
    gMonFootprint_Pikachu,
    gMonFootprint_Raichu,
    gMonFootprint_Sandshrew,
    gMonFootprint_Sandslash,
    gMonFootprint_NidoranF,
    gMonFootprint_Nidorina,
    gMonFootprint_Nidoqueen,
    gMonFootprint_NidoranM,
    gMonFootprint_Nidorino,
    gMonFootprint_Nidoking,
    gMonFootprint_Clefairy,
    gMonFootprint_Clefable,
    gMonFootprint_Vulpix,
    gMonFootprint_Ninetales,
    gMonFootprint_Jigglypuff,
    gMonFootprint_Wigglytuff,
    gMonFootprint_Zubat,
    gMonFootprint_Golbat,
    gMonFootprint_Oddish,
    gMonFootprint_Gloom,
    gMonFootprint_Vileplume,
    gMonFootprint_Paras,
    gMonFootprint_Parasect,
    gMonFootprint_Venonat,
    gMonFootprint_Venomoth,
    gMonFootprint_Diglett,
    gMonFootprint_Dugtrio,
    gMonFootprint_Meowth,
    gMonFootprint_Persian,
    gMonFootprint_Psyduck,
    gMonFootprint_Golduck,
    gMonFootprint_Mankey,
    gMonFootprint_Primeape,
    gMonFootprint_Growlithe,
    gMonFootprint_Arcanine,
    gMonFootprint_Poliwag,
    gMonFootprint_Poliwhirl,
    gMonFootprint_Poliwrath,
    gMonFootprint_Abra,
    gMonFootprint_Kadabra,
    gMonFootprint_Alakazam,
    gMonFootprint_Machop,
    gMonFootprint_Machoke,
    gMonFootprint_Machamp,
    gMonFootprint_Bellsprout,
    gMonFootprint_Weepinbell,
    gMonFootprint_Victreebel,
    gMonFootprint_Tentacool,
    gMonFootprint_Tentacruel,
    gMonFootprint_Geodude,
    gMonFootprint_Graveler,
    gMonFootprint_Golem,
    gMonFootprint_Ponyta,
    gMonFootprint_Rapidash,
    gMonFootprint_Slowpoke,
    gMonFootprint_Slowbro,
    gMonFootprint_Magnemite,
    gMonFootprint_Magneton,
    gMonFootprint_Farfetchd,
    gMonFootprint_Doduo,
    gMonFootprint_Dodrio,
    gMonFootprint_Seel,
    gMonFootprint_Dewgong,
    gMonFootprint_Grimer,
    gMonFootprint_Muk,
    gMonFootprint_Shellder,
    gMonFootprint_Cloyster,
    gMonFootprint_Gastly,
    gMonFootprint_Haunter,
    gMonFootprint_Gengar,
    gMonFootprint_Onix,
    gMonFootprint_Drowzee,
    gMonFootprint_Hypno,
    gMonFootprint_Krabby,
    gMonFootprint_Kingler,
    gMonFootprint_Voltorb,
    gMonFootprint_Electrode,
    gMonFootprint_Exeggcute,
    gMonFootprint_Exeggutor,
    gMonFootprint_Cubone,
    gMonFootprint_Marowak,
    gMonFootprint_Hitmonlee,
    gMonFootprint_Hitmonchan,
    gMonFootprint_Lickitung,
    gMonFootprint_Koffing,
    gMonFootprint_Weezing,
    gMonFootprint_Rhyhorn,
    gMonFootprint_Rhydon,
    gMonFootprint_Chansey,
    gMonFootprint_Tangela,
    gMonFootprint_Kangaskhan,
    gMonFootprint_Horsea,
    gMonFootprint_Seadra,
    gMonFootprint_Goldeen,
    gMonFootprint_Seaking,
    gMonFootprint_Staryu,
    gMonFootprint_Starmie,
    gMonFootprint_Mrmime,
    gMonFootprint_Scyther,
    gMonFootprint_Jynx,
    gMonFootprint_Electabuzz,
    gMonFootprint_Magmar,
    gMonFootprint_Pinsir,
    gMonFootprint_Tauros,
    gMonFootprint_Magikarp,
    gMonFootprint_Gyarados,
    gMonFootprint_Lapras,
    gMonFootprint_Ditto,
    gMonFootprint_Eevee,
    gMonFootprint_Vaporeon,
    gMonFootprint_Jolteon,
    gMonFootprint_Flareon,
    gMonFootprint_Porygon,
    gMonFootprint_Omanyte,
    gMonFootprint_Omastar,
    gMonFootprint_Kabuto,
    gMonFootprint_Kabutops,
    gMonFootprint_Aerodactyl,
    gMonFootprint_Snorlax,
    gMonFootprint_Articuno,
    gMonFootprint_Zapdos,
    gMonFootprint_Moltres,
    gMonFootprint_Dratini,
    gMonFootprint_Dragonair,
    gMonFootprint_Dragonite,
    gMonFootprint_Mewtwo,
    gMonFootprint_Mew,
    gMonFootprint_Chikorita,
    gMonFootprint_Bayleef,
    gMonFootprint_Meganium,
    gMonFootprint_Cyndaquil,
    gMonFootprint_Quilava,
    gMonFootprint_Typhlosion,
    gMonFootprint_Totodile,
    gMonFootprint_Croconaw,
    gMonFootprint_Feraligatr,
    gMonFootprint_Sentret,
    gMonFootprint_Furret,
    gMonFootprint_Hoothoot,
    gMonFootprint_Noctowl,
    gMonFootprint_Ledyba,
    gMonFootprint_Ledian,
    gMonFootprint_Spinarak,
    gMonFootprint_Ariados,
    gMonFootprint_Crobat,
    gMonFootprint_Chinchou,
    gMonFootprint_Lanturn,
    gMonFootprint_Pichu,
    gMonFootprint_Cleffa,
    gMonFootprint_Igglybuff,
    gMonFootprint_Togepi,
    gMonFootprint_Togetic,
    gMonFootprint_Natu,
    gMonFootprint_Xatu,
    gMonFootprint_Mareep,
    gMonFootprint_Flaaffy,
    gMonFootprint_Ampharos,
    gMonFootprint_Bellossom,
    gMonFootprint_Marill,
    gMonFootprint_Azumarill,
    gMonFootprint_Sudowoodo,
    gMonFootprint_Politoed,
    gMonFootprint_Hoppip,
    gMonFootprint_Skiploom,
    gMonFootprint_Jumpluff,
    gMonFootprint_Aipom,
    gMonFootprint_Sunkern,
    gMonFootprint_Sunflora,
    gMonFootprint_Yanma,
    gMonFootprint_Wooper,
    gMonFootprint_Quagsire,
    gMonFootprint_Espeon,
    gMonFootprint_Umbreon,
    gMonFootprint_Murkrow,
    gMonFootprint_Slowking,
    gMonFootprint_Misdreavus,
    gMonFootprint_Unown,
    gMonFootprint_Wobbuffet,
    gMonFootprint_Girafarig,
    gMonFootprint_Pineco,
    gMonFootprint_Forretress,
    gMonFootprint_Dunsparce,
    gMonFootprint_Gligar,
    gMonFootprint_Steelix,
    gMonFootprint_Snubbull,
    gMonFootprint_Granbull,
    gMonFootprint_Qwilfish,
    gMonFootprint_Scizor,
    gMonFootprint_Shuckle,
    gMonFootprint_Heracross,
    gMonFootprint_Sneasel,
    gMonFootprint_Teddiursa,
    gMonFootprint_Ursaring,
    gMonFootprint_Slugma,
    gMonFootprint_Magcargo,
    gMonFootprint_Swinub,
    gMonFootprint_Piloswine,
    gMonFootprint_Corsola,
    gMonFootprint_Remoraid,
    gMonFootprint_Octillery,
    gMonFootprint_Delibird,
    gMonFootprint_Mantine,
    gMonFootprint_Skarmory,
    gMonFootprint_Houndour,
    gMonFootprint_Houndoom,
    gMonFootprint_Kingdra,
    gMonFootprint_Phanpy,
    gMonFootprint_Donphan,
    gMonFootprint_Porygon2,
    gMonFootprint_Stantler,
    gMonFootprint_Smeargle,
    gMonFootprint_Tyrogue,
    gMonFootprint_Hitmontop,
    gMonFootprint_Smoochum,
    gMonFootprint_Elekid,
    gMonFootprint_Magby,
    gMonFootprint_Miltank,
    gMonFootprint_Blissey,
    gMonFootprint_Raikou,
    gMonFootprint_Entei,
    gMonFootprint_Suicune,
    gMonFootprint_Larvitar,
    gMonFootprint_Pupitar,
    gMonFootprint_Tyranitar,
    gMonFootprint_Lugia,
    gMonFootprint_HoOh,
    gMonFootprint_Celebi,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_QuestionMark,
    gMonFootprint_Treecko,
    gMonFootprint_Grovyle,
    gMonFootprint_Sceptile,
    gMonFootprint_Torchic,
    gMonFootprint_Combusken,
    gMonFootprint_Blaziken,
    gMonFootprint_Mudkip,
    gMonFootprint_Marshtomp,
    gMonFootprint_Swampert,
    gMonFootprint_Poochyena,
    gMonFootprint_Mightyena,
    gMonFootprint_Zigzagoon,
    gMonFootprint_Linoone,
    gMonFootprint_Wurmple,
    gMonFootprint_Silcoon,
    gMonFootprint_Beautifly,
    gMonFootprint_Cascoon,
    gMonFootprint_Dustox,
    gMonFootprint_Lotad,
    gMonFootprint_Lombre,
    gMonFootprint_Ludicolo,
    gMonFootprint_Seedot,
    gMonFootprint_Nuzleaf,
    gMonFootprint_Shiftry,
    gMonFootprint_Nincada,
    gMonFootprint_Ninjask,
    gMonFootprint_Shedinja,
    gMonFootprint_Taillow,
    gMonFootprint_Swellow,
    gMonFootprint_Shroomish,
    gMonFootprint_Breloom,
    gMonFootprint_Spinda,
    gMonFootprint_Wingull,
    gMonFootprint_Pelipper,
    gMonFootprint_Surskit,
    gMonFootprint_Masquerain,
    gMonFootprint_Wailmer,
    gMonFootprint_Wailord,
    gMonFootprint_Skitty,
    gMonFootprint_Delcatty,
    gMonFootprint_Kecleon,
    gMonFootprint_Baltoy,
    gMonFootprint_Claydol,
    gMonFootprint_Nosepass,
    gMonFootprint_Torkoal,
    gMonFootprint_Sableye,
    gMonFootprint_Barboach,
    gMonFootprint_Whiscash,
    gMonFootprint_Luvdisc,
    gMonFootprint_Corphish,
    gMonFootprint_Crawdaunt,
    gMonFootprint_Feebas,
    gMonFootprint_Milotic,
    gMonFootprint_Carvanha,
    gMonFootprint_Sharpedo,
    gMonFootprint_Trapinch,
    gMonFootprint_Vibrava,
    gMonFootprint_Flygon,
    gMonFootprint_Makuhita,
    gMonFootprint_Hariyama,
    gMonFootprint_Electrike,
    gMonFootprint_Manectric,
    gMonFootprint_Numel,
    gMonFootprint_Camerupt,
    gMonFootprint_Spheal,
    gMonFootprint_Sealeo,
    gMonFootprint_Walrein,
    gMonFootprint_Cacnea,
    gMonFootprint_Cacturne,
    gMonFootprint_Snorunt,
    gMonFootprint_Glalie,
    gMonFootprint_Lunatone,
    gMonFootprint_Solrock,
    gMonFootprint_Azurill,
    gMonFootprint_Spoink,
    gMonFootprint_Grumpig,
    gMonFootprint_Plusle,
    gMonFootprint_Minun,
    gMonFootprint_Mawile,
    gMonFootprint_Meditite,
    gMonFootprint_Medicham,
    gMonFootprint_Swablu,
    gMonFootprint_Altaria,
    gMonFootprint_Wynaut,
    gMonFootprint_Duskull,
    gMonFootprint_Dusclops,
    gMonFootprint_Roselia,
    gMonFootprint_Slakoth,
    gMonFootprint_Vigoroth,
    gMonFootprint_Slaking,
    gMonFootprint_Gulpin,
    gMonFootprint_Swalot,
    gMonFootprint_Tropius,
    gMonFootprint_Whismur,
    gMonFootprint_Loudred,
    gMonFootprint_Exploud,
    gMonFootprint_Clamperl,
    gMonFootprint_Huntail,
    gMonFootprint_Gorebyss,
    gMonFootprint_Absol,
    gMonFootprint_Shuppet,
    gMonFootprint_Banette,
    gMonFootprint_Seviper,
    gMonFootprint_Zangoose,
    gMonFootprint_Relicanth,
    gMonFootprint_Aron,
    gMonFootprint_Lairon,
    gMonFootprint_Aggron,
    gMonFootprint_Castform,
    gMonFootprint_Volbeat,
    gMonFootprint_Illumise,
    gMonFootprint_Lileep,
    gMonFootprint_Cradily,
    gMonFootprint_Anorith,
    gMonFootprint_Armaldo,
    gMonFootprint_Ralts,
    gMonFootprint_Kirlia,
    gMonFootprint_Gardevoir,
    gMonFootprint_Bagon,
    gMonFootprint_Shelgon,
    gMonFootprint_Salamence,
    gMonFootprint_Beldum,
    gMonFootprint_Metang,
    gMonFootprint_Metagross,
    gMonFootprint_Regirock,
    gMonFootprint_Regice,
    gMonFootprint_Registeel,
    gMonFootprint_Kyogre,
    gMonFootprint_Groudon,
    gMonFootprint_Rayquaza,
    gMonFootprint_Latias,
    gMonFootprint_Latios,
    gMonFootprint_Jirachi,
    gMonFootprint_Deoxys,
    gMonFootprint_Chimecho,
    gMonFootprint_Bulbasaur,
};
static const u8 sText_ClearTo0[] = _("{CLEAR_TO 0}");
const u8 gUnknown_083B555C[] = INCBIN_U8("graphics/unknown/unknown_3B555C.bin");
static const struct OamData sOamData_MonOrTrainerPic =
{
    .y = 0,
    .affineMode = 0,
    .objMode = 0,
    .mosaic = 0,
    .bpp = 0,
    .shape = 0,
    .x = 0,
    .matrixNum = 0,
    .size = 3,
    .tileNum = 0,
    .priority = 1,
    .paletteNum = 0,
    .affineParam = 0,
};

static void *const sMonOrTrainerPicDecompressDestPtrs[] =
{
    ePokedexGraphicsBuffer + 0x0000,
    ePokedexGraphicsBuffer + 0x4000,
    ePokedexGraphicsBuffer + 0x8000,
    ePokedexGraphicsBuffer + 0xC000,
};

static const struct SpriteFrameImage sSpriteFrameImages_MonOrTrainerPic_0[] =
{
    {ePokedexGraphicsBuffer + 0x0000, 0x800},
    {ePokedexGraphicsBuffer + 0x0800, 0x800},
    {ePokedexGraphicsBuffer + 0x1000, 0x800},
    {ePokedexGraphicsBuffer + 0x1800, 0x800},
    {ePokedexGraphicsBuffer + 0x2000, 0x800},
    {ePokedexGraphicsBuffer + 0x2800, 0x800},
    {ePokedexGraphicsBuffer + 0x3000, 0x800},
    {ePokedexGraphicsBuffer + 0x3800, 0x800},
    {ePokedexGraphicsBuffer + 0x4000, 0x800},
    {ePokedexGraphicsBuffer + 0x4800, 0x800},
    {ePokedexGraphicsBuffer + 0x5000, 0x800},
    {ePokedexGraphicsBuffer + 0x5800, 0x800},
    {ePokedexGraphicsBuffer + 0x6000, 0x800},
    {ePokedexGraphicsBuffer + 0x6800, 0x800},
    {ePokedexGraphicsBuffer + 0x7000, 0x800},
    {ePokedexGraphicsBuffer + 0x7800, 0x800},
};

static const struct SpriteFrameImage sSpriteFrameImages_MonOrTrainerPic_1[] =
{
    {ePokedexGraphicsBuffer + 0x4000, 0x800},
    {ePokedexGraphicsBuffer + 0x4800, 0x800},
    {ePokedexGraphicsBuffer + 0x5000, 0x800},
    {ePokedexGraphicsBuffer + 0x5800, 0x800},
    {ePokedexGraphicsBuffer + 0x6000, 0x800},
    {ePokedexGraphicsBuffer + 0x6800, 0x800},
    {ePokedexGraphicsBuffer + 0x7000, 0x800},
    {ePokedexGraphicsBuffer + 0x7800, 0x800},
    {ePokedexGraphicsBuffer + 0x8000, 0x800},
    {ePokedexGraphicsBuffer + 0x8800, 0x800},
    {ePokedexGraphicsBuffer + 0x9000, 0x800},
    {ePokedexGraphicsBuffer + 0x9800, 0x800},
    {ePokedexGraphicsBuffer + 0xA000, 0x800},
    {ePokedexGraphicsBuffer + 0xA800, 0x800},
    {ePokedexGraphicsBuffer + 0xB000, 0x800},
    {ePokedexGraphicsBuffer + 0xB800, 0x800},
};

static const struct SpriteFrameImage sSpriteFrameImages_MonOrTrainerPic_2[] =
{
    {ePokedexGraphicsBuffer + 0x8000, 0x800},
    {ePokedexGraphicsBuffer + 0x8800, 0x800},
    {ePokedexGraphicsBuffer + 0x9000, 0x800},
    {ePokedexGraphicsBuffer + 0x9800, 0x800},
    {ePokedexGraphicsBuffer + 0xA000, 0x800},
    {ePokedexGraphicsBuffer + 0xA800, 0x800},
    {ePokedexGraphicsBuffer + 0xB000, 0x800},
    {ePokedexGraphicsBuffer + 0xB800, 0x800},
    {ePokedexGraphicsBuffer + 0xC000, 0x800},
    {ePokedexGraphicsBuffer + 0xC800, 0x800},
    {ePokedexGraphicsBuffer + 0xD000, 0x800},
    {ePokedexGraphicsBuffer + 0xD800, 0x800},
    {ePokedexGraphicsBuffer + 0xE000, 0x800},
    {ePokedexGraphicsBuffer + 0xE800, 0x800},
    {ePokedexGraphicsBuffer + 0xF000, 0x800},
    {ePokedexGraphicsBuffer + 0xF800, 0x800},
};

static const struct SpriteFrameImage sSpriteFrameImages_MonOrTrainerPic_3[] =
{
    {ePokedexGraphicsBuffer + 0xC000, 0x800},
    {ePokedexGraphicsBuffer + 0xC800, 0x800},
    {ePokedexGraphicsBuffer + 0xD000, 0x800},
    {ePokedexGraphicsBuffer + 0xD800, 0x800},
    {ePokedexGraphicsBuffer + 0xE000, 0x800},
    {ePokedexGraphicsBuffer + 0xE800, 0x800},
    {ePokedexGraphicsBuffer + 0xF000, 0x800},
    {ePokedexGraphicsBuffer + 0xF800, 0x800},
    // UB: may overlap ePokedexView1-4
    {ePokedexGraphicsBuffer + 0x10000, 0x800},
    {ePokedexGraphicsBuffer + 0x10800, 0x800},
    {ePokedexGraphicsBuffer + 0x11000, 0x800},
    {ePokedexGraphicsBuffer + 0x11800, 0x800},
    {ePokedexGraphicsBuffer + 0x12000, 0x800},
    {ePokedexGraphicsBuffer + 0x12800, 0x800},
    {ePokedexGraphicsBuffer + 0x13000, 0x800},
    {ePokedexGraphicsBuffer + 0x13800, 0x800},
};

static const struct SpriteFrameImage *const sSpriteFrameImages_MonOrTrainerPic[] =
{
    sSpriteFrameImages_MonOrTrainerPic_0,
    sSpriteFrameImages_MonOrTrainerPic_1,
    sSpriteFrameImages_MonOrTrainerPic_2,
    sSpriteFrameImages_MonOrTrainerPic_3,
};

static void _SpriteCB_Dummy(struct Sprite *);

static const struct SpriteTemplate sMonOrTrainerPicSpriteTemplate =
{
    .tileTag = 0xFFFF,
    .paletteTag = 0,
    .oam = &sOamData_MonOrTrainerPic,
    .anims = NULL,
    .images = sSpriteFrameImages_MonOrTrainerPic_0,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = _SpriteCB_Dummy,
};

// First character in range followed by number of characters in range for upper and lowercase
static const u8 sLetterSearchRanges[][4] =
{
    {0,      0, 0,      0},
    {CHAR_A, 3, CHAR_a, 3},
    {CHAR_D, 3, CHAR_d, 3},
    {CHAR_G, 3, CHAR_g, 3},
    {CHAR_J, 3, CHAR_j, 3},
    {CHAR_M, 3, CHAR_m, 3},
    {CHAR_P, 3, CHAR_p, 3},
    {CHAR_S, 3, CHAR_s, 3},
    {CHAR_V, 3, CHAR_v, 3},
    {CHAR_Y, 2, CHAR_y, 2},
};
static const struct SearchMenuTopBarItem sSearchMenuTopBarItems[] =
{
    {DexText_SearchForPoke, 0,  0, 5},
    {DexText_SwitchDex,     6,  0, 5},
    {DexText_ReturnToDex,  12,  0, 5},
};
static const struct SearchMenuItem sSearchMenuItems[] =
{
    {DexText_ListByABC,           0,  2,  5,  5,  2, 12},
    {DexText_ListByColor,         0,  4,  5,  5,  4, 12},
    {DexText_ListByType,          0,  6,  5,  5,  6,  6},
    {DexText_ListByType,          0,  6,  5, 11,  6,  6},
    {DexText_SelectDexList,       0,  8,  5,  5,  8, 12},
    {DexText_SelectDexMode,       0, 10,  5,  5, 10, 12},
    {DexText_ExecuteSearchSwitch, 0, 12,  5,  0,  0,  0},
};
static const u8 sSearchMovementMap_SearchNatDex[][4] =
{
    {0xFF, 0xFF, 0xFF,    1},
    {0xFF, 0xFF,    0,    2},
    {0xFF,    3,    1,    4},
    {   2, 0xFF,    1,    4},
    {0xFF, 0xFF,    2,    5},
    {0xFF, 0xFF,    4,    6},
    {0xFF, 0xFF,    5, 0xFF},
};
static const u8 sSearchMovementMap_ShiftNatDex[][4] =
{
    {0xFF, 0xFF, 0xFF, 0xFF},
    {0xFF, 0xFF, 0xFF, 0xFF},
    {0xFF, 0xFF, 0xFF, 0xFF},
    {0xFF, 0xFF, 0xFF, 0xFF},
    {0xFF, 0xFF, 0xFF,    5},
    {0xFF, 0xFF,    4,    6},
    {0xFF, 0xFF,    5, 0xFF},
};
static const u8 sSearchMovementMap_SearchHoennDex[][4] =
{
    {0xFF, 0xFF, 0xFF,    1},
    {0xFF, 0xFF,    0,    2},
    {0xFF,    3,    1,    4},
    {   2, 0xFF,    1,    4},
    {0xFF, 0xFF,    2,    6},
    {0xFF, 0xFF, 0xFF, 0xFF},
    {0xFF, 0xFF,    4, 0xFF},
};
static const u8 sSearchMovementMap_ShiftHoennDex[][4] =
{
    {0xFF, 0xFF, 0xFF, 0xFF},
    {0xFF, 0xFF, 0xFF, 0xFF},
    {0xFF, 0xFF, 0xFF, 0xFF},
    {0xFF, 0xFF, 0xFF, 0xFF},
    {0xFF, 0xFF, 0xFF,    6},
    {0xFF, 0xFF, 0xFF, 0xFF},
    {0xFF, 0xFF,    4, 0xFF},
};
static const struct SearchOptionText sDexModeOptions[] =
{
    {DexText_HoennDex2,    DexText_HoennDex},
    {DexText_NationalDex2, DexText_NationalDex},
    {NULL, NULL},
};
static const struct SearchOptionText sDexOrderOptions[] =
{
    {DexText_ListByNumber,          DexText_NumericalMode},
    {DexText_ListByABC2,            DexText_ABCMode},
    {DexText_ListByHeavyToLightest, DexText_HeaviestMode},
    {DexText_ListByLightToHeaviest, DexText_LightestMode},
    {DexText_ListByTallToSmallest,  DexText_TallestMode},
    {DexText_ListBySmallToTallest,  DexText_SmallestMode},
    {NULL, NULL},
};
static const struct SearchOptionText sDexSearchNameOptions[] =
{
    {DexText_Terminator5, DexText_DontSpecify},
    {DexText_Terminator5, DexText_ABC},
    {DexText_Terminator5, DexText_DEF},
    {DexText_Terminator5, DexText_GHI},
    {DexText_Terminator5, DexText_JKL},
    {DexText_Terminator5, DexText_MNO},
    {DexText_Terminator5, DexText_PQR},
    {DexText_Terminator5, DexText_STU},
    {DexText_Terminator5, DexText_VWX},
    {DexText_Terminator5, DexText_YZ},
    {NULL, NULL},
};
static const struct SearchOptionText sDexSearchColorOptions[] =
{
    {DexText_Terminator5, DexText_DontSpecify},
    {DexText_Terminator5, DexText_Red},
    {DexText_Terminator5, DexText_Blue},
    {DexText_Terminator5, DexText_Yellow},
    {DexText_Terminator5, DexText_Green},
    {DexText_Terminator5, DexText_Black},
    {DexText_Terminator5, DexText_Brown},
    {DexText_Terminator5, DexText_Purple},
    {DexText_Terminator5, DexText_Gray},
    {DexText_Terminator5, DexText_White},
    {DexText_Terminator5, DexText_Pink},
    {NULL, NULL},
};
static const struct SearchOptionText sDexSearchTypeOptions[] =
{
    {DexText_Terminator5, DexText_None},
    {DexText_Terminator5, gTypeNames[TYPE_NORMAL]},
    {DexText_Terminator5, gTypeNames[TYPE_FIGHTING]},
    {DexText_Terminator5, gTypeNames[TYPE_FLYING]},
    {DexText_Terminator5, gTypeNames[TYPE_POISON]},
    {DexText_Terminator5, gTypeNames[TYPE_GROUND]},
    {DexText_Terminator5, gTypeNames[TYPE_ROCK]},
    {DexText_Terminator5, gTypeNames[TYPE_BUG]},
    {DexText_Terminator5, gTypeNames[TYPE_GHOST]},
    {DexText_Terminator5, gTypeNames[TYPE_STEEL]},
    {DexText_Terminator5, gTypeNames[TYPE_FIRE]},
    {DexText_Terminator5, gTypeNames[TYPE_WATER]},
    {DexText_Terminator5, gTypeNames[TYPE_GRASS]},
    {DexText_Terminator5, gTypeNames[TYPE_ELECTRIC]},
    {DexText_Terminator5, gTypeNames[TYPE_PSYCHIC]},
    {DexText_Terminator5, gTypeNames[TYPE_ICE]},
    {DexText_Terminator5, gTypeNames[TYPE_DRAGON]},
    {DexText_Terminator5, gTypeNames[TYPE_DARK]},
    {NULL, NULL},
};
static const u8 sPokedexModes[] = {0, 1};
static const u8 sOrderOptions[] = {0, 1, 2, 3, 4, 5};
static const u8 sDexSearchTypeIds[] = {0xFF, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17};
static const struct SearchOption sSearchOptions[] =
{
    { sDexSearchNameOptions,  6,  7, 10},
    { sDexSearchColorOptions,  8,  9, 11},
    { sDexSearchTypeOptions, 10, 11, 18},
    { sDexSearchTypeOptions, 12, 13, 18},
    { sDexOrderOptions,  4,  5,  6},
    { sDexModeOptions,  2,  3,  2},
};
static const u8 gUnknown_083B5AAC[] = _("{STR_VAR_1}{CLEAR_TO 43}");
static const u8 gUnknown_083B5AB2[] = _("{STR_VAR_1}{CLEAR_TO 96}");

static void MainCB(void);
static void Task_PokedexShowMainScreen(u8 taskId);
static void Task_PokedexMainScreen(u8 taskId);
static void Task_WaitForScroll(u8 taskId);
static void Task_PokedexMainScreenMenu(u8 taskId);
static void Task_OpenInfoScreenAfterMonMovement(u8 taskId);
static void Task_WaitForExitInfoScreen(u8 taskId);
static void Task_WaitForExitSearch(u8 taskId);
static void Task_ClosePokedex(u8 taskId);
static void Task_OpenSearchResults(u8 taskId);
static void Task_PokedexResultsScreen(u8 taskId);
static void Task_WaitForSearchResultsScroll(u8 taskId);
static void Task_PokedexResultsScreenMenu(u8 taskId);
static void Task_OpenSearchResultsInfoScreenAfterMonMovement(u8 taskId);
static void Task_WaitForExitSearchResultsInfoScreen(u8 taskId);
static void Task_PokedexResultsScreenReturnToMainScreen(u8 taskId);
static void Task_PokedexResultsScreenExitPokedex(u8 taskId);
static bool8 LoadPokedexListPage(u8 a);
static void LoadPokedexBgPalette(void);
static void SortPokedex(u8 dexMode, u8 sortMode);
static void CreateMonDexNum(u16 entryNum, u8 x, u8 y, u16 screenBase);
static void CreateCaughtBall(u16 owned, u8 x, u8 y, u16 screenBase);
static u8 CreateMonName(u16 num, u8 b, u8 c);
static void ClearMonListEntry(u8 x, u8 y, u16 screenBase);
static void CreateMonSpritesAtPos(u16 selectedMon, u16 b);
static bool8 UpdateDexListScroll(u8 direction, u8 monMoveIncrement, u8 scrollTimerMax);
static u16 TryDoPokedexScroll(u16 a, u16 b);
static void UpdateSelectedMonSpriteId(void);
static u8 TryDoInfoScreenScroll(void);
static u8 ClearMonSprites(void);
static u16 GetPokemonSpriteToDisplay(u16 species);
static u32 CreatePokedexMonSprite(u16 num, s16 x, s16 y);
static void CreateInterfaceSprites(u8 a);
static void SpriteCB_MoveMonForInfoScreen(struct Sprite *sprite);
static void SpriteCB_PokedexListMonSprite(struct Sprite *sprite);
static u8 LoadInfoScreen(struct PokedexListItem *, u8);
static bool8 IsInfoScreenScrolling(u8 taskId);
static u8 sub_808F284(struct PokedexListItem *, u8);
static void Task_InitPageScreenMultistep(u8 taskId);
static void Task_PageScreenProcessInput(u8 taskId);
static void Task_LoadInfoScreenWaitForFade(u8 taskId);
static void Task_ClosePageScreen(u8 taskId);
static void Task_InitAreaScreenMultistep(u8 taskId);
static void Task_AreaScreenProcessInput(u8 taskId);
static void sub_808FA00(u8 taskId);
static void Task_InitCryScreenMultistep(u8 taskId);
static void Task_CryScreenProcessInput(u8 taskId);
static void Task_SwitchScreensFromCryScreen(u8 taskId);
static void LoadPlayArrowPalette(u8 cryPlaying);
static void Task_InitSizeScreenMultistep(u8 taskId);
static void Task_SizeScreenProcessInput(u8 taskId);
static void Task_SwitchScreensFromSizeScreen(u8 taskId);
static void LoadScreenSelectBarMain(u16 screenBase);
static void LoadScreenSelectBarSubmenu(u16 screenBase);
static void HighlightScreenSelectBarItem(u8 a, u16 b);
static void sub_8090644(u8 a, u16 b);
static void sub_8090750(u8);
static void sub_8090A3C(u8);
static void sub_8090B8C(u8);
static void sub_8090C28(struct Sprite *);
static void sub_8090C68(void);
static void ResetOtherVideoRegisters(u16);
static void PrintEntryScreenDexNum(u16 order, u8, u8);
static u8 PrintEntryScreenSpeciesName(u16 num, u8, u8);
static u8 PrintCryScreenSpeciesName(u16 num, u8, u8, u8);
static void UnusedPrintMonName(const u8 *name, u8, u8);
static void sub_8091458(u16 height, u8 i, u8 i1);
static void sub_8091564(u16 weight, u8 i, u8 i1);
void PrintFootprint(u16 num, u16 b, u16 c);
static void sub_80C0DC0(u16 a, u16 b);
static u16 GetNextPosition(u8 direction, u16 position, u16 min, u16 max);
u16 CreateMonSpriteFromNationalDexNumber(u16 num, s16 x, s16 y, u16 paletteNum);
static void Task_LoadSearchMenu(u8 taskId);
static void Task_SwitchToSearchMenuTopBar(u8 taskId);
static void Task_HandleSearchTopBarInput(u8 taskId);
static void Task_SwitchToSearchMenu(u8 taskId);
static void Task_HandleSearchMenuInput(u8 taskId);
static void Task_StartPokedexSearch(u8 taskId);
static void Task_WaitAndCompleteSearch(u8 taskId);
static void Task_SearchCompleteWaitForInput(u8 taskId);
static void Task_SelectSearchMenuItem(u8 taskId);
static void Task_HandleSearchParameterInput(u8 taskId);
static void Task_ExitSearch(u8 taskId);
static void Task_ExitSearchWaitForFade(u8 taskId);
static void HighlightSelectedSearchTopBarItem(u8 topBarItem);
static void HighlightSelectedSearchMenuItem(u8 topBarItem, u8 menuItem);
static void PrintSelectedSearchParameters(u8 taskId);
static void DrawOrEraseSearchParameterBox(bool8 erase);
static void PrintSearchParameterText(u8 taskId);
static u8 GetSearchModeSelection(u8 taskId, u8 b);
static void SetDefaultSearchModeAndOrder(u8 taskId);
static void CreateSearchParameterScrollArrows(u8 taskId);


void ResetPokedex(void)
{
    u16 i;

    sLastSelectedPokemon = 0;
    sPokeBallRotation = 0x40;
    gUnusedPokedexU8 = 0;
    gSaveBlock2.pokedex.mode = 0;
    gSaveBlock2.pokedex.order = 0;
    gSaveBlock2.pokedex.nationalMagic = 0;
    gSaveBlock2.pokedex.unknown2 = 0;
    gSaveBlock2.pokedex.unownPersonality = 0;
    gSaveBlock2.pokedex.spindaPersonality = 0;
    gSaveBlock2.pokedex.unknown3 = 0;
    DisableNationalPokedex();
    for (i = 0; i < DEX_FLAGS_NO; i++)
    {
        gSaveBlock2.pokedex.owned[i] = 0;
        gSaveBlock2.pokedex.seen[i] = 0;
        gSaveBlock1.dexSeen2[i] = 0;
        gSaveBlock1.dexSeen3[i] = 0;
    }
}

void ResetPokedexScrollPositions(void)
{
    sLastSelectedPokemon = 0;
    sPokeBallRotation = 0x40;
}

static void VBlankCB_Pokedex(void)
{
    LoadOam();
    ProcessSpriteCopyRequests();
    TransferPlttBuffer();
}

static void ClearPokedexView(struct PokedexView *pokedexView)
{
    u16 i;

    for (i = 0; i < NATIONAL_DEX_COUNT; i++)
    {
        pokedexView->pokedexList[i].dexNum |= 0xFFFF;
        pokedexView->pokedexList[i].seen = 0;
        pokedexView->pokedexList[i].owned = 0;
    }
    pokedexView->pokedexList[NATIONAL_DEX_COUNT].dexNum = 0;
    pokedexView->pokedexList[NATIONAL_DEX_COUNT].seen = 0;
    pokedexView->pokedexList[NATIONAL_DEX_COUNT].owned = 0;
    pokedexView->pokemonListCount = 0;
    pokedexView->selectedPokemon = 0;
    pokedexView->selectedPokemonBackup = 0;
    pokedexView->dexMode = 0;
    pokedexView->dexModeBackup = 0;
    pokedexView->dexOrder = 0;
    pokedexView->dexOrderBackup = 0;
    pokedexView->seenCount = 0;
    pokedexView->ownCount = 0;
    for (i = 0; i < 4; i++)
        pokedexView->monSpriteIds[i] |= 0xFFFF;
    pokedexView->pokeBallRotationStep = 0;
    pokedexView->pokeBallRotationBackup = 0;
    pokedexView->pokeBallRotation = 0;
    pokedexView->initialVOffset = 0;
    pokedexView->scrollTimer = 0;
    pokedexView->scrollDirection = 0;
    pokedexView->listVOffset = 0;
    pokedexView->listMovingVOffset = 0;
    pokedexView->scrollMonIncrement = 0;
    pokedexView->maxScrollTimer = 0;
    pokedexView->scrollSpeed = 0;
    for (i = 0; i < 4; i++)
        pokedexView->unk63A[i] = 0;
    pokedexView->currentPage = 0;
    pokedexView->currentPageBackup = 0;
    pokedexView->isSearchResults = FALSE;
    pokedexView->selectedScreen = 0;
    pokedexView->screenSwitchState = 0;
    pokedexView->menuIsOpen = 0;
    pokedexView->menuCursorPos = 0;
    pokedexView->menuY = 0;
    for (i = 0; i < 8; i++)
        pokedexView->unk656[i] = 0;
    for (i = 0; i < 8; i++)
        pokedexView->unk65E[i] = 0;
}

void CB2_InitPokedex(void)
{
    switch (gMain.state)
    {
    case 0:
    default:
        SetVBlankCallback(NULL);
        ResetOtherVideoRegisters(0);
        DmaFill16Large(3, 0, (void *)(VRAM + 0x0), VRAM_SIZE, 0x1000);
        DmaClear32(3, OAM, OAM_SIZE);
        DmaClear16(3, PLTT, PLTT_SIZE);
        gMain.state = 1;
        break;
    case 1:
        ScanlineEffect_Stop();
        ResetTasks();
        ResetSpriteData();
        ResetPaletteFade();
        FreeAllSpritePalettes();
        gReservedSpritePaletteCount = 8;
        Text_LoadWindowTemplate(&gWindowTemplate_81E7048);
        InitMenuWindow(&gWindowTemplate_81E7048);
        gMain.state++;
        break;
    case 2:
        switch (Random() & 3)
        {
        case 0:
        default:
            gPokedexView = ePokedexView1;
            break;
        case 1:
            gPokedexView = ePokedexView2;
            break;
        case 2:
            gPokedexView = ePokedexView3;
            break;
        case 3:
            gPokedexView = ePokedexView4;
            break;
        }
        ClearPokedexView(gPokedexView);
        CreateTask(Task_PokedexShowMainScreen, 0);
        gPokedexView->dexMode = gSaveBlock2.pokedex.mode;
        if (!IsNationalPokedexEnabled())
            gPokedexView->dexMode = DEX_MODE_HOENN;
        gPokedexView->dexOrder = gSaveBlock2.pokedex.order;
        gPokedexView->selectedPokemon = sLastSelectedPokemon;
        gPokedexView->pokeBallRotation = sPokeBallRotation;
        gPokedexView->selectedScreen = PAGE_SCREEN;
        gPokedexView->descriptionPageNum = 0;
        if (!IsNationalPokedexEnabled())
        {
            gPokedexView->seenCount = GetHoennPokedexCount(0);
            gPokedexView->ownCount = GetHoennPokedexCount(1);
        }
        else
        {
            gPokedexView->seenCount = GetNationalPokedexCount(0);
            gPokedexView->ownCount = GetNationalPokedexCount(1);
        }
        gPokedexView->initialVOffset = 8;
        gMain.state++;
        break;
    case 3:
        {
            u16 savedIme;

            savedIme = REG_IME;
            REG_IME = 0;
            REG_IE |= INTR_FLAG_VBLANK;
            REG_IME = savedIme;
            REG_DISPSTAT |= DISPSTAT_VBLANK_INTR;
            SetVBlankCallback(VBlankCB_Pokedex);
            SetMainCallback2(MainCB);
            SortPokedex(gPokedexView->dexMode, gPokedexView->dexOrder);
            m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 0x80);
        }
        break;
    }
}

UNUSED
u8 unref_sub_808C540(void (*func)(u8))
{
    u16 savedIme;
    u8 taskId;

    SetVBlankCallback(NULL);
    ResetOtherVideoRegisters(0x200);
    ScanlineEffect_Stop();
    ResetTasks();
    ResetPaletteFade();
    savedIme = REG_IME;
    REG_IME = 0;
    REG_IE |= INTR_FLAG_VBLANK;
    REG_IME = savedIme;
    REG_DISPSTAT |= DISPSTAT_VBLANK_INTR;
    SetVBlankCallback(VBlankCB_Pokedex);
    SetMainCallback2(MainCB);
    if (!gPokedexView->currentPageBackup)
        taskId = CreateTask(Task_WaitForExitInfoScreen, 0);
    else
        taskId = CreateTask(Task_WaitForExitSearchResultsInfoScreen, 0);
    gTasks[taskId].data[0] = CreateTask(func, 0);
    return gTasks[taskId].data[0];
}

static void MainCB(void)
{
    RunTasks();
    AnimateSprites();
    BuildOamBuffer();
    UpdatePaletteFade();
}

static void Task_PokedexShowMainScreen(u8 taskId)
{
    gPokedexView->isSearchResults = FALSE;
    if (LoadPokedexListPage(0))
        gTasks[taskId].func = Task_PokedexMainScreen;
}

//Hide menu and process input on main screen
void Task_PokedexMainScreen(u8 taskId)
{
    REG_BG0VOFS = gPokedexView->menuY;

    //If menu is open, slide it down, off screen
    if (gPokedexView->menuY)
        gPokedexView->menuY -= 8;
    else
    {
        if ((gMain.newKeys & A_BUTTON) && gPokedexView->pokedexList[gPokedexView->selectedPokemon].seen)
        {
            UpdateSelectedMonSpriteId();
            BeginNormalPaletteFade(
              ~(1 << (gSprites[gPokedexView->selectedMonSpriteId].oam.paletteNum + 16)),
              0, 0, 16, RGB(0, 0, 0));
            gSprites[gPokedexView->selectedMonSpriteId].callback = SpriteCB_MoveMonForInfoScreen;
            gTasks[taskId].func = Task_OpenInfoScreenAfterMonMovement;
            PlaySE(SE_PIN);
        }
        else if (gMain.newKeys & START_BUTTON)
        {
            //Open menu
            gPokedexView->menuY = 0;
            gPokedexView->menuIsOpen = 1;
            gPokedexView->menuCursorPos = 0;
            gTasks[taskId].func = Task_PokedexMainScreenMenu;
            PlaySE(SE_SELECT);
        }
        else if (gMain.newKeys & SELECT_BUTTON)
        {
            PlaySE(SE_SELECT);
            BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
            gTasks[taskId].data[0] = LoadSearchMenu();
            gPokedexView->screenSwitchState = 0;
            gPokedexView->pokeBallRotationBackup = gPokedexView->pokeBallRotation;
            gPokedexView->selectedPokemonBackup = gPokedexView->selectedPokemon;
            gPokedexView->dexModeBackup = gPokedexView->dexMode;
            gPokedexView->dexOrderBackup = gPokedexView->dexOrder;
            gTasks[taskId].func = Task_WaitForExitSearch;
            PlaySE(SE_PC_LOGIN);
        }
        else if (gMain.newKeys & B_BUTTON)
        {
            BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
            gTasks[taskId].func = Task_ClosePokedex;
            PlaySE(SE_PC_OFF);
        }
        else
        {
            //Handle D-pad
            gPokedexView->selectedPokemon = TryDoPokedexScroll(gPokedexView->selectedPokemon, 0xE);
            if (gPokedexView->scrollTimer)
                gTasks[taskId].func = Task_WaitForScroll;
        }
    }
}

static void Task_WaitForScroll(u8 taskId)
{
    if (UpdateDexListScroll(gPokedexView->scrollDirection,
            gPokedexView->scrollMonIncrement,
            gPokedexView->maxScrollTimer))
        gTasks[taskId].func = Task_PokedexMainScreen;
}

//Bring up menu and process menu input
static void Task_PokedexMainScreenMenu(u8 taskId)
{
    REG_BG0VOFS = gPokedexView->menuY;

    //If menu is not open, slide it up, on screen
    if (gPokedexView->menuY != 80)
        gPokedexView->menuY += 8;
    else
    {
        if (gMain.newKeys & A_BUTTON)
        {
            switch (gPokedexView->menuCursorPos)
            {
            case 0: //BACK TO LIST
            default:
                gMain.newKeys |= START_BUTTON;  //Exit menu
                break;
            case 1: //LIST TOP
                gPokedexView->selectedPokemon = 0;
                gPokedexView->pokeBallRotation = 0x40;
                ClearMonSprites();
                CreateMonSpritesAtPos(gPokedexView->selectedPokemon, 0xE);
                gMain.newKeys |= START_BUTTON;  //Exit menu
                break;
            case 2: //LIST BOTTOM
                gPokedexView->selectedPokemon = gPokedexView->pokemonListCount - 1;
                gPokedexView->pokeBallRotation = gPokedexView->pokemonListCount * 16 + 0x30;
                ClearMonSprites();
                CreateMonSpritesAtPos(gPokedexView->selectedPokemon, 0xE);
                gMain.newKeys |= START_BUTTON;  //Exit menu
                break;
            case 3: //CLOSE POKEDEX
                BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
                gTasks[taskId].func = Task_ClosePokedex;
                PlaySE(SE_PC_OFF);
                break;
            }
        }

        //Exit menu when Start or B is pressed
        if (gMain.newKeys & (START_BUTTON | B_BUTTON))
        {
            gPokedexView->menuIsOpen = 0;
            gTasks[taskId].func = Task_PokedexMainScreen;
            PlaySE(SE_SELECT);
        }
        else if ((gMain.newAndRepeatedKeys & DPAD_UP) && gPokedexView->menuCursorPos != 0)
        {
            gPokedexView->menuCursorPos--;
            PlaySE(SE_SELECT);
        }
        else if ((gMain.newAndRepeatedKeys & DPAD_DOWN) && gPokedexView->menuCursorPos <= 2)
        {
            gPokedexView->menuCursorPos++;
            PlaySE(SE_SELECT);
        }
    }
}

static void Task_OpenInfoScreenAfterMonMovement(u8 taskId)
{
    if (gSprites[gPokedexView->selectedMonSpriteId].x == 48
     && gSprites[gPokedexView->selectedMonSpriteId].y == 56)
    {
        gPokedexView->currentPageBackup = gPokedexView->currentPage;
        gTasks[taskId].data[0] = LoadInfoScreen(&gPokedexView->pokedexList[gPokedexView->selectedPokemon], gPokedexView->selectedMonSpriteId);
        gTasks[taskId].func = Task_WaitForExitInfoScreen;
    }
}

static void Task_WaitForExitInfoScreen(u8 taskId)
{
    if (gTasks[gTasks[taskId].data[0]].isActive)
    {
        if (gPokedexView->currentPage == 1 && !IsInfoScreenScrolling(gTasks[taskId].data[0]) && TryDoInfoScreenScroll())
            sub_808F284(&gPokedexView->pokedexList[gPokedexView->selectedPokemon], gTasks[taskId].data[0]);
    }
    else
    {
        sLastSelectedPokemon = gPokedexView->selectedPokemon;
        sPokeBallRotation = gPokedexView->pokeBallRotation;
        gTasks[taskId].func = Task_PokedexShowMainScreen;
    }
}

static void Task_WaitForExitSearch(u8 taskId)
{
    bool8 isActive = gTasks[gTasks[taskId].data[0]].isActive;

    if (!isActive)
    {
        if (gPokedexView->screenSwitchState != 0)
        {
            gPokedexView->selectedPokemon = isActive;
            gPokedexView->pokeBallRotation = 0x40;
            gTasks[taskId].func = Task_OpenSearchResults;
        }
        else
        {
            gPokedexView->pokeBallRotation = gPokedexView->pokeBallRotationBackup;
            gPokedexView->selectedPokemon = gPokedexView->selectedPokemonBackup;
            gPokedexView->dexMode = gPokedexView->dexModeBackup;
            if (!IsNationalPokedexEnabled())
                gPokedexView->dexMode = DEX_MODE_HOENN;
            gPokedexView->dexOrder = gPokedexView->dexOrderBackup;
            gTasks[taskId].func = Task_PokedexShowMainScreen;
        }
    }
}

static void Task_ClosePokedex(u8 taskId)
{
    if (!gPaletteFade.active)
    {
        gSaveBlock2.pokedex.mode = gPokedexView->dexMode;
        if (!IsNationalPokedexEnabled())
            gSaveBlock2.pokedex.mode = 0;
        gSaveBlock2.pokedex.order = gPokedexView->dexOrder;
        DestroyTask(taskId);
        SetMainCallback2(c2_exit_to_overworld_1_sub_8080DEC);
        m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 0x100);
    }
}

static void Task_OpenSearchResults(u8 taskId)
{
    gPokedexView->isSearchResults = TRUE;
    if (LoadPokedexListPage(3))
        gTasks[taskId].func = Task_PokedexResultsScreen;
}

static void Task_PokedexResultsScreen(u8 taskId)
{
    REG_BG0VOFS = gPokedexView->menuY;

    if (gPokedexView->menuY)
    {
        gPokedexView->menuY -= 8;
    }
    else
    {
        if ((gMain.newKeys & A_BUTTON) && gPokedexView->pokedexList[gPokedexView->selectedPokemon].seen)
        {
            u32 excludedPalettes;

            UpdateSelectedMonSpriteId();
            excludedPalettes = (1 << (gSprites[gPokedexView->selectedMonSpriteId].oam.paletteNum + 16));
            gSprites[gPokedexView->selectedMonSpriteId].callback = SpriteCB_MoveMonForInfoScreen;
            BeginNormalPaletteFade(~excludedPalettes, 0, 0, 16, RGB(0, 0, 0));
            gTasks[taskId].func = Task_OpenSearchResultsInfoScreenAfterMonMovement;
            PlaySE(SE_PIN);
        }
        else if (gMain.newKeys & START_BUTTON)
        {
            gPokedexView->menuY = 0;
            gPokedexView->menuIsOpen = 1;
            gPokedexView->menuCursorPos = 0;
            gTasks[taskId].func = Task_PokedexResultsScreenMenu;
            PlaySE(SE_SELECT);
        }
        else if (gMain.newKeys & SELECT_BUTTON)
        {
            BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
            gTasks[taskId].data[0] = LoadSearchMenu();
            gPokedexView->screenSwitchState = 0;
            gTasks[taskId].func = Task_WaitForExitSearch;
            PlaySE(SE_PC_LOGIN);
        }
        else if (gMain.newKeys & B_BUTTON)
        {
            BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
            gTasks[taskId].func = Task_PokedexResultsScreenReturnToMainScreen;
            PlaySE(SE_PC_OFF);
        }
        else
        {
            //Handle D-pad
            gPokedexView->selectedPokemon = TryDoPokedexScroll(gPokedexView->selectedPokemon, 0xE);
            if (gPokedexView->scrollTimer)
                gTasks[taskId].func = Task_WaitForSearchResultsScroll;
        }
    }
}

static void Task_WaitForSearchResultsScroll(u8 taskId)
{
    if (UpdateDexListScroll(gPokedexView->scrollDirection,
            gPokedexView->scrollMonIncrement,
            gPokedexView->maxScrollTimer))
        gTasks[taskId].func = Task_PokedexResultsScreen;
}

static void Task_PokedexResultsScreenMenu(u8 taskId)
{
    REG_BG0VOFS = gPokedexView->menuY;

    if (gPokedexView->menuY != 96)
    {
        gPokedexView->menuY += 8;
    }
    else
    {
        if (gMain.newKeys & A_BUTTON)
        {
            switch (gPokedexView->menuCursorPos)
            {
            case 0: //BACK TO LIST
            default:
                gMain.newKeys |= START_BUTTON;
                break;
            case 1: //LIST TOP
                gPokedexView->selectedPokemon = 0;
                gPokedexView->pokeBallRotation = 0x40;
                ClearMonSprites();
                CreateMonSpritesAtPos(gPokedexView->selectedPokemon, 0xE);
                gMain.newKeys |= START_BUTTON;
                break;
            case 2: //LIST BOTTOM
                gPokedexView->selectedPokemon = gPokedexView->pokemonListCount - 1;
                gPokedexView->pokeBallRotation = gPokedexView->pokemonListCount * 16 + 0x30;
                ClearMonSprites();
                CreateMonSpritesAtPos(gPokedexView->selectedPokemon, 0xE);
                gMain.newKeys |= START_BUTTON;
                break;
            case 3: //BACK TO POKEDEX
                BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
                gTasks[taskId].func = Task_PokedexResultsScreenReturnToMainScreen;
                PlaySE(SE_TRUCK_DOOR);
                break;
            case 4: //CLOSE POKEDEX
                BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
                gTasks[taskId].func = Task_PokedexResultsScreenExitPokedex;
                PlaySE(SE_PC_OFF);
                break;
            }
        }

        //Exit menu when Start or B is pressed
        if (gMain.newKeys & (START_BUTTON | B_BUTTON))
        {
            gPokedexView->menuIsOpen = 0;
            gTasks[taskId].func = Task_PokedexResultsScreen;
            PlaySE(SE_SELECT);
        }
        else if ((gMain.newAndRepeatedKeys & DPAD_UP) && gPokedexView->menuCursorPos)
        {
            gPokedexView->menuCursorPos--;
            PlaySE(SE_SELECT);
        }
        else if ((gMain.newAndRepeatedKeys & DPAD_DOWN) && gPokedexView->menuCursorPos <= 3)
        {
            gPokedexView->menuCursorPos++;
            PlaySE(SE_SELECT);
        }
    }
}

static void Task_OpenSearchResultsInfoScreenAfterMonMovement(u8 taskId)
{
    if (gSprites[gPokedexView->selectedMonSpriteId].x == 48
     && gSprites[gPokedexView->selectedMonSpriteId].y == 56)
    {
        gPokedexView->currentPageBackup = gPokedexView->currentPage;
        gTasks[taskId].data[0] = LoadInfoScreen(&gPokedexView->pokedexList[gPokedexView->selectedPokemon], gPokedexView->selectedMonSpriteId);
        gTasks[taskId].func = Task_WaitForExitSearchResultsInfoScreen;
    }
}

static void Task_WaitForExitSearchResultsInfoScreen(u8 taskId)
{
    if (gTasks[gTasks[taskId].data[0]].isActive)
    {
        if (gPokedexView->currentPage == 1 && !IsInfoScreenScrolling(gTasks[taskId].data[0]) && TryDoInfoScreenScroll())
            sub_808F284(&gPokedexView->pokedexList[gPokedexView->selectedPokemon], gTasks[taskId].data[0]);
    }
    else
    {
        gTasks[taskId].func = Task_OpenSearchResults;
    }
}

static void Task_PokedexResultsScreenReturnToMainScreen(u8 taskId)
{
    if (!gPaletteFade.active)
    {
        gPokedexView->pokeBallRotation = gPokedexView->pokeBallRotationBackup;
        gPokedexView->selectedPokemon = gPokedexView->selectedPokemonBackup;
        gPokedexView->dexMode = gPokedexView->dexModeBackup;
        if (!IsNationalPokedexEnabled())
            gPokedexView->dexMode = DEX_MODE_HOENN;
        gPokedexView->dexOrder = gPokedexView->dexOrderBackup;
        gTasks[taskId].func = Task_PokedexShowMainScreen;
    }
}

static void Task_PokedexResultsScreenExitPokedex(u8 taskId)
{
    if (!gPaletteFade.active)
    {
        gPokedexView->pokeBallRotation = gPokedexView->pokeBallRotationBackup;
        gPokedexView->selectedPokemon = gPokedexView->selectedPokemonBackup;
        gPokedexView->dexMode = gPokedexView->dexModeBackup;
        if (!IsNationalPokedexEnabled())
            gPokedexView->dexMode = DEX_MODE_HOENN;
        gPokedexView->dexOrder = gPokedexView->dexOrderBackup;
        gTasks[taskId].func = Task_ClosePokedex;
    }
}

static bool8 LoadPokedexListPage(u8 a)
{
    switch (gMain.state)
    {
    case 0:
    default:
        if (gPaletteFade.active)
            return 0;
        SetVBlankCallback(NULL);
        gPokedexView->currentPage = a;
        ResetOtherVideoRegisters(0);
        REG_BG2VOFS = gPokedexView->initialVOffset;
        LZ77UnCompVram(gPokedexMenu_Gfx, (void *)(VRAM));
        LZ77UnCompVram(gUnknown_08E96738, (void *)(VRAM + 0x6800));
        LZ77UnCompVram(gUnknown_08E9C6DC, (void *)(VRAM + 0x7800));
        DmaClear16(3, VRAM + 0x6000, 0x500);
        if (a == 0)
            LZ77UnCompVram(gPokedexStartMenuMain_Tilemap, (void *)(VRAM + 0x6500));
        else
            LZ77UnCompVram(gPokedexStartMenuSearchResults_Tilemap, (void *)(VRAM + 0x6500));
        ResetPaletteFade();
        if (a == 0)
            gPokedexView->isSearchResults = FALSE;
        else
            gPokedexView->isSearchResults = TRUE;
        LoadPokedexBgPalette();
        gMain.state = 1;
        break;
    case 1:
        ResetSpriteData();
        FreeAllSpritePalettes();
        gReservedSpritePaletteCount = 8;
        LoadCompressedObjectPic(&sInterfaceSpriteSheet[0]);
        LoadSpritePalettes(sInterfaceSpritePalette);
        CreateInterfaceSprites(a);
        gMain.state++;
        break;
    case 2:
        Text_LoadWindowTemplate(&gWindowTemplate_81E7048);
        InitMenuWindow(&gWindowTemplate_81E7048);
        LZ77UnCompVram(gUnknown_0839FA7C, (void *)(VRAM + 0xFF80));
        gMain.state++;
        break;
    case 3:
        if (a == 0)
            SortPokedex(gPokedexView->dexMode, gPokedexView->dexOrder);
        CreateMonSpritesAtPos(gPokedexView->selectedPokemon, 0xE);
        gPokedexView->menuIsOpen = 0;
        gPokedexView->menuY = 0;
        gMain.state++;
        break;
    case 4:
        BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB(0, 0, 0));
        SetVBlankCallback(VBlankCB_Pokedex);
        gMain.state++;
        break;
    case 5:
        REG_WININ = 0x3F3F;
        REG_WINOUT = 0x1D3F;
        REG_WIN0H = 0;
        REG_WIN0V = 0;
        REG_WIN1H = 0;
        REG_WIN1V = 0;
        REG_BLDCNT = 0;
        REG_BLDALPHA = 0;
        REG_BLDY = 0;
        REG_BG3CNT = BGCNT_PRIORITY(3) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(15) | BGCNT_16COLOR | BGCNT_TXT256x256;
        REG_BG1CNT = BGCNT_PRIORITY(1) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(13) | BGCNT_16COLOR | BGCNT_TXT256x256;
        REG_BG0CNT = BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(12) | BGCNT_16COLOR | BGCNT_TXT256x256;
        REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG_ALL_ON | DISPCNT_OBJ_ON | DISPCNT_OBJWIN_ON;
        gMain.state++;
        break;
    case 6:
        if (!gPaletteFade.active)
        {
            gMain.state = 0;
            return TRUE;
        }
        break;
    }
    return FALSE;
}

static void LoadPokedexBgPalette(void)
{
    if (gPokedexView->isSearchResults)
        LoadPalette(sPokedexSearchPalette + 1, 1, sizeof(sPokedexSearchPalette) - sizeof(u16));
    else if (!IsNationalPokedexEnabled())
        LoadPalette(gPokedexMenu_Pal + 1, 1, 0xBE);
    else
        LoadPalette(sNationalPokedexPalette + 1, 1, sizeof(sNationalPokedexPalette) - sizeof(u16));
}

static void SortPokedex(u8 dexMode, u8 sortMode)
{
    s16 i, r5, r10;
    u16 vars[3]; //I have no idea why three regular variables are stored in an array, but whatever.

    gPokedexView->pokemonListCount = 0;

    switch (dexMode)
    {
    default:
    case DEX_MODE_HOENN:
        vars[0] = HOENN_DEX_COUNT;
        vars[1] = 1;
        break;
    case DEX_MODE_NATIONAL:
        if (IsNationalPokedexEnabled())
        {
            vars[0] = NATIONAL_DEX_COUNT;
            vars[1] = 0;
        }
        else
        {
            vars[0] = HOENN_DEX_COUNT;
            vars[1] = 1;
        }
        break;
    }

    switch (sortMode)
    {
    case 0:
        if (vars[1])
        {
            for (i = 0; i < vars[0]; i++)
            {
                vars[2] = HoennToNationalOrder(i + 1);
                gPokedexView->pokedexList[i].dexNum = vars[2];
                gPokedexView->pokedexList[i].seen = GetSetPokedexFlag(vars[2], 0);
                gPokedexView->pokedexList[i].owned = GetSetPokedexFlag(vars[2], 1);
                if (gPokedexView->pokedexList[i].seen)
                    gPokedexView->pokemonListCount = i + 1;
            }
        }
        else
        {
            for (i = 0, r5= 0, r10 = 0; i < vars[0]; i++)
            {
                vars[2] = i + 1;
                if (GetSetPokedexFlag(vars[2], 0))
                    r10 = 1;
                if (r10)
                {
                    gPokedexView->pokedexList[r5].dexNum = vars[2];
                    gPokedexView->pokedexList[r5].seen = GetSetPokedexFlag(vars[2], 0);
                    gPokedexView->pokedexList[r5].owned = GetSetPokedexFlag(vars[2], 1);
                    if (gPokedexView->pokedexList[r5].seen)
                        gPokedexView->pokemonListCount = r5 + 1;
                    r5++;
                }
            }
        }
        break;
    case 1:
        for (i = 0; i < POKEMON_SLOTS_NUMBER - 1; i++)
        {
            vars[2] = gPokedexOrder_Alphabetical[i];

            if (NationalToHoennOrder(vars[2]) <= vars[0] && GetSetPokedexFlag(vars[2], 0))
            {
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].dexNum = vars[2];
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].seen = 1;
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].owned = GetSetPokedexFlag(vars[2], 1);
                gPokedexView->pokemonListCount++;
            }
        }
        break;
    case 2:
        for (i = NATIONAL_DEX_COUNT - 1; i >= 0; i--)
        {
            vars[2] = gPokedexOrder_Weight[i];

            if (NationalToHoennOrder(vars[2]) <= vars[0] && GetSetPokedexFlag(vars[2], 1))
            {
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].dexNum = vars[2];
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].seen = 1;
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].owned = 1;
                gPokedexView->pokemonListCount++;
            }
        }
        break;
    case 3:
        for (i = 0; i < NATIONAL_DEX_COUNT; i++)
        {
            vars[2] = gPokedexOrder_Weight[i];

            if (NationalToHoennOrder(vars[2]) <= vars[0] && GetSetPokedexFlag(vars[2], 1))
            {
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].dexNum = vars[2];
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].seen = 1;
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].owned = 1;
                gPokedexView->pokemonListCount++;
            }
        }
        break;
    case 4:
        for (i = NATIONAL_DEX_COUNT - 1; i >=0; i--)
        {
            vars[2] = gPokedexOrder_Height[i];

            if (NationalToHoennOrder(vars[2]) <= vars[0] && GetSetPokedexFlag(vars[2], 1))
            {
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].dexNum = vars[2];
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].seen = 1;
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].owned = 1;
                gPokedexView->pokemonListCount++;
            }
        }
        break;
    case 5:
        for (i = 0; i < NATIONAL_DEX_COUNT; i++)
        {
            vars[2] = gPokedexOrder_Height[i];

            if (NationalToHoennOrder(vars[2]) <= vars[0] && GetSetPokedexFlag(vars[2], 1))
            {
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].dexNum = vars[2];
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].seen = 1;
                gPokedexView->pokedexList[gPokedexView->pokemonListCount].owned = 1;
                gPokedexView->pokemonListCount++;
            }
        }
        break;
    }

    for (i = gPokedexView->pokemonListCount; i < NATIONAL_DEX_COUNT; i++)
    {
        gPokedexView->pokedexList[i].dexNum |= 0xFFFF;
        gPokedexView->pokedexList[i].seen = 0;
        gPokedexView->pokedexList[i].owned = 0;
    }
}

static void CreateMonListEntry(u8 position, u16 b, u16 screenBase)
{
    s16 entryNum;
    u16 i;
    u16 r2;

    switch (position)
    {
    case 0:
    default:
        entryNum = b - 5;
        for (i = 0; i <= 10; i++)
        {
            if (entryNum < 0 || entryNum >= NATIONAL_DEX_COUNT || gPokedexView->pokedexList[entryNum].dexNum == 0xFFFF)
            {
                ClearMonListEntry(0x11, i * 2, screenBase);
            }
            else
            {
                ClearMonListEntry(0x11, i * 2, screenBase);
                if (gPokedexView->pokedexList[entryNum].seen)
                {
                    CreateMonDexNum(entryNum, 0x12, i * 2, screenBase);
                    CreateCaughtBall(gPokedexView->pokedexList[entryNum].owned, 0x11, i * 2, screenBase);
                    CreateMonName(gPokedexView->pokedexList[entryNum].dexNum, 0x17, i * 2);
                }
                else
                {
                    CreateMonDexNum(entryNum, 0x12, i * 2, screenBase);
                    CreateCaughtBall(0, 0x11, i * 2, screenBase);
                    CreateMonName(0, 0x17, i * 2);
                }
            }
            entryNum++;
        }
        break;
    case 1:
        entryNum = b - 5;
        if (entryNum < 0 || entryNum >= NATIONAL_DEX_COUNT || gPokedexView->pokedexList[entryNum].dexNum == 0xFFFF)
        {
            ClearMonListEntry(0x11, gPokedexView->listVOffset * 2, screenBase);
        }
        else
        {
            ClearMonListEntry(0x11, gPokedexView->listVOffset * 2, screenBase);
            if (gPokedexView->pokedexList[entryNum].seen)
            {
                CreateMonDexNum(entryNum, 0x12, gPokedexView->listVOffset * 2, screenBase);
                CreateCaughtBall(gPokedexView->pokedexList[entryNum].owned, 0x11, gPokedexView->listVOffset * 2,
                    screenBase);
                CreateMonName(gPokedexView->pokedexList[entryNum].dexNum, 0x17, gPokedexView->listVOffset * 2);
            }
            else
            {
                CreateMonDexNum(entryNum, 0x12, gPokedexView->listVOffset * 2, screenBase);
                CreateCaughtBall(0, 0x11, gPokedexView->listVOffset * 2, screenBase);
                CreateMonName(0, 0x17, gPokedexView->listVOffset * 2);
            }
        }
        break;
    case 2:
        entryNum = b + 5;
        r2 = gPokedexView->listVOffset + 10;
        if (r2 > 15)
            r2 -= 16;
        if (entryNum < 0 || entryNum >= NATIONAL_DEX_COUNT || gPokedexView->pokedexList[entryNum].dexNum == 0xFFFF)
            ClearMonListEntry(0x11, r2 * 2, screenBase);
        else
        {
            ClearMonListEntry(0x11, r2 * 2, screenBase);
            if (gPokedexView->pokedexList[entryNum].seen)
            {
                CreateMonDexNum(entryNum, 0x12, r2 * 2, screenBase);
                CreateCaughtBall(gPokedexView->pokedexList[entryNum].owned, 0x11, r2 * 2, screenBase);
                CreateMonName(gPokedexView->pokedexList[entryNum].dexNum, 0x17, r2 * 2);
            }
            else
            {
                CreateMonDexNum(entryNum, 0x12, r2 * 2, screenBase);
                CreateCaughtBall(0, 0x11, r2 * 2, screenBase);
                CreateMonName(0, 0x17, r2 * 2);
            }
        }
        break;
    }
}

static void CreateMonDexNum(u16 entryNum, u8 x, u8 y, u16 screenBase)
{
    u8 text[4];
    u16 unk[2];
    u16 r7 = gPokedexView->pokedexList[entryNum].dexNum;

    if (gPokedexView->dexMode == DEX_MODE_HOENN)
        r7 = NationalToHoennOrder(r7);
    unk[0] = 0x3FC;
    unk[1] = 0x3FD;
    text[0] = CHAR_0 + r7 / 100;
    text[1] = CHAR_0 + (r7 % 100) / 10;
    text[2] = CHAR_0 + (r7 % 100) % 10;
    text[3] = EOS;
    *(u16 *)(BG_VRAM + screenBase * 0x800 + y * 0x40 + x * 2) = unk[0];
    *(u16 *)(BG_VRAM + screenBase * 0x800 + (y + 1) * 0x40 + x * 2) = unk[1];
    Menu_PrintText(text, x - 15, y);
}

static void CreateCaughtBall(u16 owned, u8 x, u8 y, u16 screenBase)
{
    u16 unk[2];

    if (owned)
    {
        unk[0] = 0x3FE;
        unk[1] = 0x3FF;
    }
    else
    {
        unk[0] = 0;
        unk[1] = 0;
    }
    *(u16 *)(BG_VRAM + screenBase * 0x800 + y * 0x40 + x * 2) = unk[0];
    *(u16 *)(BG_VRAM + screenBase * 0x800 + (y + 1) * 0x40 + x * 2) = unk[1];
}

static u8 CreateMonName(u16 num, u8 b, u8 c)
{
    u8 text[POKEMON_NAME_LENGTH + (MODERN ? 1 : 0)];
    u8 i;

    for (i = 0; i < 10; i++)
        text[i] = CHAR_SPACE;
    text[i] = EOS;

    num = NationalPokedexNumToSpecies(num);
    switch (num)
    {
    default:
        for (i = 0; gSpeciesNames[num][i] != EOS && i < POKEMON_NAME_LENGTH; i++)
            text[i] = gSpeciesNames[num][i];
        break;
    case 0:
        for (i = 0; i < POKEMON_NAME_LENGTH; i++)
            text[i] = CHAR_HYPHEN;
        break;
    }
    Menu_PrintTextPixelCoords(text, (b - 0x11) * 8 + 0xFC, c * 8, 0);
    return i;
}

static void ClearMonListEntry(u8 x, u8 y, u16 screenBase)
{
    u8 i;

    for (i = 0; i < 12; i++)
    {
        *(u16 *)(BG_VRAM + screenBase * 0x800 + y * 64 + (x + i) * 2) = 0;
        *(u16 *)(BG_VRAM + screenBase * 0x800 + (y + 1) * 64 + (x + i) * 2) = 0;
    }
}

static void CreateMonSpritesAtPos(u16 selectedMon, u16 b)
{
    u8 i;
    u16 unk;
    u8 spriteId;

    for (i = 0; i < 4; i++)
        gPokedexView->monSpriteIds[i] = 0xFFFF;
    gPokedexView->selectedMonSpriteId = 0xFFFF;
    CreateMonListEntry(0, selectedMon, b);
    REG_BG2VOFS = gPokedexView->initialVOffset;

    unk = GetPokemonSpriteToDisplay(selectedMon - 1);
    if (unk != 0xFFFF)
    {
        spriteId = CreatePokedexMonSprite(unk, 0x60, 0x50);
        gSprites[spriteId].callback = SpriteCB_PokedexListMonSprite;
        gSprites[spriteId].data[5] = -32;
    }

    unk = GetPokemonSpriteToDisplay(selectedMon);
    if (unk != 0xFFFF)
    {
        spriteId = CreatePokedexMonSprite(unk, 0x60, 0x50);
        gSprites[spriteId].callback = SpriteCB_PokedexListMonSprite;
        gSprites[spriteId].data[5] = 0;
    }

    unk = GetPokemonSpriteToDisplay(selectedMon + 1);
    if (unk != 0xFFFF)
    {
        spriteId = CreatePokedexMonSprite(unk, 0x60, 0x50);
        gSprites[spriteId].callback = SpriteCB_PokedexListMonSprite;
        gSprites[spriteId].data[5] = 32;
    }

    gPokedexView->listVOffset = 0;
    gPokedexView->listMovingVOffset = 0;
}

static bool8 UpdateDexListScroll(u8 direction, u8 monMoveIncrement, u8 scrollTimerMax)
{
    u16 i;
    u8 foo;

    if (gPokedexView->scrollTimer)
    {
        gPokedexView->scrollTimer--;
        switch (direction)
        {
        case 1:
            for (i = 0; i < 4; i++)
            {
                if (gPokedexView->monSpriteIds[i] != 0xFFFF)
                    gSprites[gPokedexView->monSpriteIds[i]].data[5] += monMoveIncrement;
            }
            foo = 16 * (scrollTimerMax - gPokedexView->scrollTimer) / scrollTimerMax;
            REG_BG2VOFS = gPokedexView->initialVOffset + gPokedexView->listMovingVOffset * 16 - foo;
            gPokedexView->pokeBallRotation -= gPokedexView->pokeBallRotationStep;
            break;
        case 2:
            for (i = 0; i < 4; i++)
            {
                if (gPokedexView->monSpriteIds[i] != 0xFFFF)
                    gSprites[gPokedexView->monSpriteIds[i]].data[5] -= monMoveIncrement;
            }
            foo = 16 * (scrollTimerMax - gPokedexView->scrollTimer) / scrollTimerMax;
            REG_BG2VOFS = gPokedexView->initialVOffset + gPokedexView->listMovingVOffset * 16 + foo;
            gPokedexView->pokeBallRotation += gPokedexView->pokeBallRotationStep;
            break;
        }
        return FALSE;
    }
    else
    {
        REG_BG2VOFS = gPokedexView->initialVOffset + gPokedexView->listVOffset * 16;
        return TRUE;
    }
}

static void CreateScrollingPokemonSprite(u8 direction, u16 selectedMon)
{
    u16 unk;
    u8 spriteId;

    gPokedexView->listMovingVOffset = gPokedexView->listVOffset;
    switch (direction)
    {
    case 1:
        unk = GetPokemonSpriteToDisplay(selectedMon - 1);
        if (unk != 0xFFFF)
        {
            spriteId = CreatePokedexMonSprite(unk, 0x60, 0x50);
            gSprites[spriteId].callback = SpriteCB_PokedexListMonSprite;
            gSprites[spriteId].data[5] = -64;
        }
        if (gPokedexView->listVOffset > 0)
            gPokedexView->listVOffset--;
        else
            gPokedexView->listVOffset = 15;
        break;
    case 2:
        unk = GetPokemonSpriteToDisplay(selectedMon + 1);
        if (unk != 0xFFFF)
        {
            spriteId = CreatePokedexMonSprite(unk, 0x60, 0x50);
            gSprites[spriteId].callback = SpriteCB_PokedexListMonSprite;
            gSprites[spriteId].data[5] = 0x40;
        }
        if (gPokedexView->listVOffset <= 0xE)
            gPokedexView->listVOffset++;
        else
            gPokedexView->listVOffset = 0;
        break;
    }
}

// Ugly, ugly, ugly. I couldn't get it to match otherwise.
static u16 TryDoPokedexScroll(u16 a, u16 b)
{
    u8 r3;
    u8 r5;
    u8 i;
    u16 r6;
    u8 r10 = 0;

    if (!((gMain.heldKeys & 0x40) && (a > 0)))
    {
        //_0808E4B6
        if (!((gMain.heldKeys & 0x80) && (a < gPokedexView->pokemonListCount - 1)))
        //_0808E4CE
        {
            if ((gMain.newKeys & 0x20) && (a > 0))
            {
                r6 = a;
                //_0808E4E0
                for (i = 0; i < 7; i++)
                    a = GetNextPosition(1, a, 0, gPokedexView->pokemonListCount - 1);
                gPokedexView->pokeBallRotation += 16 * (a - r6);
                ClearMonSprites();
                CreateMonSpritesAtPos(a, 0xE);
                PlaySE(0x6D);
            }
            //_0808E53C
            else if ((gMain.newKeys & 0x10) && (a < gPokedexView->pokemonListCount - 1))
            {
                r6 = a;
                for (i = 0; i < 7; i++)
                    a = GetNextPosition(0, a, 0, gPokedexView->pokemonListCount - 1);
                gPokedexView->pokeBallRotation += (a - r6) * 16;
                ClearMonSprites();
                CreateMonSpritesAtPos(a, 0xE);
                PlaySE(0x6D);
            }
          _0808E5A2:
            if (r10 == 0)
            {
                gPokedexView->scrollSpeed = 0;
                return a;
            }
        }
        else
        {
            // to _0808E5C4
            r10 = 2;
            a = GetNextPosition(0, a, 0, gPokedexView->pokemonListCount - 1);
            CreateScrollingPokemonSprite(2, a);
            //goto _0808E60E
            CreateMonListEntry(2, a, b);
            PlaySE(0x6C);
            goto _0808E5A2;
        }
    }
    else
    {
        //to _0808E5E4
        r10 = 1;
        a = GetNextPosition(1, a, 0, gPokedexView->pokemonListCount - 1);
        CreateScrollingPokemonSprite(1, a);
        //_0808E60E
        CreateMonListEntry(1, a, b);
        PlaySE(0x6C);
        goto _0808E5A2;
    }
    //_0808E628
    r5 = gUnknown_083A05EC[gPokedexView->scrollSpeed / 4];
    r3 = gUnknown_083A05F1[gPokedexView->scrollSpeed / 4];
    gPokedexView->scrollTimer = r3;
    gPokedexView->maxScrollTimer = r3;
    gPokedexView->scrollMonIncrement = r5;
    gPokedexView->scrollDirection = r10;
    gPokedexView->pokeBallRotationStep = r5 / 2;
    UpdateDexListScroll(gPokedexView->scrollDirection,
        gPokedexView->scrollMonIncrement,
        gPokedexView->maxScrollTimer);
    if (gPokedexView->scrollSpeed <= 0xB)
        gPokedexView->scrollSpeed++;
    return a;
}

static void UpdateSelectedMonSpriteId(void)
{
    u16 i;

    for (i = 0; i < 4; i++)
    {
        u16 spriteId = gPokedexView->monSpriteIds[i];

        if (gSprites[spriteId].x2 == 0 && gSprites[spriteId].y2 == 0 && spriteId != 0xFFFF)
            gPokedexView->selectedMonSpriteId = spriteId;
    }
}

static u8 TryDoInfoScreenScroll(void)
{
    u16 r2;
    u16 r4 = gPokedexView->selectedPokemon;

    if ((gMain.newKeys & DPAD_UP) && r4)
    {
        r2 = r4;
        while (r2 != 0)
        {
            r2 = GetNextPosition(1, r2, 0, gPokedexView->pokemonListCount - 1);

            if (gPokedexView->pokedexList[r2].seen)
            {
                r4 = r2;
                break;
            }
        }

        if (gPokedexView->selectedPokemon == r4)
            return 0;
        else
        {
            gPokedexView->selectedPokemon = r4;
            gPokedexView->pokeBallRotation -= 16;
            return 1;
        }
    }
    else if ((gMain.newKeys & DPAD_DOWN) && r4 < gPokedexView->pokemonListCount - 1)
    {
        r2 = r4;
        while (r2 < gPokedexView->pokemonListCount - 1)
        {
            r2 = GetNextPosition(0, r2, 0, gPokedexView->pokemonListCount - 1);

            if (gPokedexView->pokedexList[r2].seen)
            {
                r4 = r2;
                break;
            }
        }

        if (gPokedexView->selectedPokemon == r4)
            return 0;
        else
        {
            gPokedexView->selectedPokemon = r4;
            gPokedexView->pokeBallRotation += 16;
            return 1;
        }
    }
    return 0;
}

static u8 ClearMonSprites(void)
{
    u16 i;

    for (i = 0; i < 4; i++)
    {
        if (gPokedexView->monSpriteIds[i] != 0xFFFF)
        {
            DestroySprite(&gSprites[gPokedexView->monSpriteIds[i]]);
            gPokedexView->monSpriteIds[i] |= 0xFFFF;
        }
    }
    return 0;
}

static u16 GetPokemonSpriteToDisplay(u16 species)
{
    if (species >= NATIONAL_DEX_COUNT || gPokedexView->pokedexList[species].dexNum == 0xFFFF)
        return 0xFFFF;
    else if (gPokedexView->pokedexList[species].seen)
        return gPokedexView->pokedexList[species].dexNum;
    else
        return 0;
}

static u32 CreatePokedexMonSprite(u16 num, s16 x, s16 y)
{
    u8 i;

    for (i = 0; i < 4; i++)
    {
        if (gPokedexView->monSpriteIds[i] == 0xFFFF)
        {
            u8 spriteId = CreateMonSpriteFromNationalDexNumber(num, x, y, i);

            gSprites[spriteId].oam.affineMode = ST_OAM_AFFINE_NORMAL;
            gSprites[spriteId].oam.priority = 3;
            gSprites[spriteId].data[0] = 0;
            gSprites[spriteId].data[1] = i;
            gSprites[spriteId].data[2] = NationalPokedexNumToSpecies(num);
            gPokedexView->monSpriteIds[i] = spriteId;
            return spriteId;
        }
    }
    return 0xFFFF;
}

static void CreateInterfaceSprites(u8 a)
{
    u8 spriteId;
    u16 r5;

    spriteId = CreateSprite(&sScrollArrowSpriteTemplate, 184, 4, 0);
    gSprites[spriteId].data[1] = 0;

    spriteId = CreateSprite(&sScrollArrowSpriteTemplate, 184, 156, 0);
    gSprites[spriteId].data[1] = 1;
    gSprites[spriteId].vFlip = TRUE;

    CreateSprite(&gSpriteTemplate_83A0524, 234, 20, 0);
    CreateSprite(&gSpriteTemplate_83A0554, 16, 138, 0);

    spriteId = CreateSprite(&gSpriteTemplate_83A0554, 48, 138, 0);
    StartSpriteAnim(&gSprites[spriteId], 3);

    spriteId = CreateSprite(&gSpriteTemplate_83A0554, 16, 158, 0);
    StartSpriteAnim(&gSprites[spriteId], 2);
    gSprites[spriteId].data[2] = 0x80;

    spriteId = CreateSprite(&gSpriteTemplate_83A0554, 48, 158, 0);
    StartSpriteAnim(&gSprites[spriteId], 1);

    spriteId = CreateSprite(&gSpriteTemplate_83A056C, 0, 80, 2);
    gSprites[spriteId].oam.affineMode = 1;
    gSprites[spriteId].oam.matrixNum = 30;
    gSprites[spriteId].data[0] = 0x1E;
    gSprites[spriteId].data[1] = 0;

    spriteId = CreateSprite(&gSpriteTemplate_83A056C, 0, 80, 2);
    gSprites[spriteId].oam.affineMode = 1;
    gSprites[spriteId].oam.matrixNum = 31;
    gSprites[spriteId].data[0] = 0x1F;
    gSprites[spriteId].data[1] = 0x80;

    if (a == 0)
    {
        u32 _a;

        CreateSprite(&gSpriteTemplate_83A0584, 32, 40, 1);

        spriteId = CreateSprite(&gSpriteTemplate_83A0584, 32, 72, 1);
        StartSpriteAnim(&gSprites[spriteId], 1);
        _a = 0;

        spriteId = CreateSprite(&gSpriteTemplate_83A059C, 28, 48, 1);
        r5 = gPokedexView->seenCount / 100;
        StartSpriteAnim(&gSprites[spriteId], r5);
        if (r5 != 0)
            _a = 1;
        else
            gSprites[spriteId].invisible = TRUE;

        spriteId = CreateSprite(&gSpriteTemplate_83A059C, 34, 48, 1);
        r5 = (gPokedexView->seenCount % 100) / 10;
        if (r5 != 0 || _a != 0)
            StartSpriteAnim(&gSprites[spriteId], r5);
        else
            gSprites[spriteId].invisible = TRUE;

        spriteId = CreateSprite(&gSpriteTemplate_83A059C, 40, 48, 1);
        r5 = (gPokedexView->seenCount % 100) % 10;
        StartSpriteAnim(&gSprites[spriteId], r5);
        _a = 0;

        spriteId = CreateSprite(&gSpriteTemplate_83A059C, 28, 80, 1);
        r5 = gPokedexView->ownCount / 100;
        StartSpriteAnim(&gSprites[spriteId], r5);
        if (r5 != 0)
            _a = 1;
        else
            gSprites[spriteId].invisible = TRUE;

        spriteId = CreateSprite(&gSpriteTemplate_83A059C, 34, 80, 1);
        r5 = (gPokedexView->ownCount % 100) / 10;
        if (r5 != 0 || _a != 0)
            StartSpriteAnim(&gSprites[spriteId], r5);
        else
            gSprites[spriteId].invisible = TRUE;

        spriteId = CreateSprite(&gSpriteTemplate_83A059C, 40, 80, 1);
        r5 = (gPokedexView->ownCount % 100) % 10;
        StartSpriteAnim(&gSprites[spriteId], r5);

        spriteId = CreateSprite(&gSpriteTemplate_83A05B4, 140, 96, 1);
        gSprites[spriteId].invisible = TRUE;
    }
    else
    {
        spriteId = CreateSprite(&gSpriteTemplate_83A05B4, 140, 80, 1);
        gSprites[spriteId].invisible = TRUE;
    }
}

static void SpriteCB_EndMoveMonForInfoScreen(struct Sprite *sprite)
{
}

static void SpriteCB_SeenOwnInfo(struct Sprite *sprite)
{
    if (gPokedexView->currentPage != 0)
        DestroySprite(sprite);
}

//Move Pokemon into position for description page
static void SpriteCB_MoveMonForInfoScreen(struct Sprite *sprite)
{
    sprite->oam.priority = 0;
    sprite->oam.affineMode = 0;
    sprite->x2 = 0;
    sprite->y2 = 0;
    if (sprite->x != 48 || sprite->y != 56)
    {
        if (sprite->x > 48)
            sprite->x--;
        if (sprite->x < 48)
            sprite->x++;

        if (sprite->y > 56)
            sprite->y--;
        if (sprite->y < 56)
            sprite->y++;
    }
    else
    {
        sprite->callback = SpriteCB_EndMoveMonForInfoScreen;
    }
}

static void SpriteCB_PokedexListMonSprite(struct Sprite *sprite)
{
    u8 data1 = sprite->data[1];

    if (gPokedexView->currentPage != 0 && gPokedexView->currentPage != 3)
    {
        DestroySprite(sprite);
        gPokedexView->monSpriteIds[data1] = 0xFFFF;
    }
    else
    {
        u32 var;

        sprite->y2 = gSineTable[(u8)sprite->data[5]] * 76 / 256;
        var = 0x10000 / gSineTable[sprite->data[5] + 0x40];
        if (var > 0xFFFF)
            var = 0xFFFF;
        SetOamMatrix(sprite->data[1] + 1, 0x100, 0, 0, var);
        sprite->oam.matrixNum = data1 + 1;

        if (sprite->data[5] > -64 && sprite->data[5] < 64)
        {
            sprite->invisible = FALSE;
            sprite->data[0] = 1;
        }
        else
        {
            sprite->invisible = TRUE;
        }

        if ((sprite->data[5] <= -64 || sprite->data[5] >= 64) && sprite->data[0] != 0)
        {
            DestroySprite(sprite);
            gPokedexView->monSpriteIds[data1] = 0xFFFF;
        }
    }
}

static void SpriteCB_Scrollbar(struct Sprite *sprite)
{
    if (gPokedexView->currentPage != 0 && gPokedexView->currentPage != 3)
        DestroySprite(sprite);
    else
        sprite->y2 = gPokedexView->selectedPokemon * 120 / (gPokedexView->pokemonListCount - 1);
}

static void SpriteCB_ScrollArrow(struct Sprite *sprite)
{
    if (gPokedexView->currentPage != 0 && gPokedexView->currentPage != 3)
    {
        DestroySprite(sprite);
    }
    else
    {
        u8 r0;

        if (sprite->data[1] != 0)
        {
            if (gPokedexView->selectedPokemon == gPokedexView->pokemonListCount - 1)
                sprite->invisible = TRUE;
            else
                sprite->invisible = FALSE;
            r0 = sprite->data[2];
        }
        else
        {
            if (gPokedexView->selectedPokemon == 0)
                sprite->invisible = TRUE;
            else
                sprite->invisible = FALSE;
            r0 = sprite->data[2] - 128;
        }
        sprite->y2 = gSineTable[r0] / 64;
        sprite->data[2] = sprite->data[2] + 8;
        if (gPokedexView->menuIsOpen == 0 && gPokedexView->menuY == 0 && sprite->invisible == 0)
            sprite->invisible = FALSE;
        else
            sprite->invisible = TRUE;
    }
}

static void SpriteCB_DexListInterfaceText(struct Sprite *sprite)
{
    if (gPokedexView->currentPage != 0 && gPokedexView->currentPage != 3)
        DestroySprite(sprite);
}

static void SpriteCB_RotatingPokeBall(struct Sprite *sprite)
{
    if (gPokedexView->currentPage != 0 && gPokedexView->currentPage != 3)
    {
        DestroySprite(sprite);
    }
    else
    {
        u8 val;
        s16 r3;
        s16 r0;

        val = gPokedexView->pokeBallRotation + sprite->data[1];
        r3 = gSineTable[val];
        r0 = gSineTable[val + 0x40];
        SetOamMatrix(sprite->data[0], r0, r3, -r3, r0);

        val = gPokedexView->pokeBallRotation + (sprite->data[1] + 0x40);
        r3 = gSineTable[val];
        r0 = gSineTable[val + 0x40];
        sprite->x2 = r0 * 40 / 256;
        sprite->y2 = r3 * 40 / 256;
    }
}

static void SpriteCB_DexListStartMenuCursor(struct Sprite *sprite)
{
    if (gPokedexView->currentPage != 0 && gPokedexView->currentPage != 3)
    {
        DestroySprite(sprite);
    }
    else
    {
        u16 r1 = gPokedexView->currentPage == 0 ? 80 : 96;

        if (gPokedexView->menuIsOpen != 0 && gPokedexView->menuY == r1)
        {
            sprite->invisible = FALSE;
            sprite->y2 = gPokedexView->menuCursorPos * 16;
            sprite->x2 = gSineTable[(u8)sprite->data[2]] / 64;
            sprite->data[2] += 8;
        }
        else
        {
            sprite->invisible = TRUE;
        }
    }
}

static u8 LoadInfoScreen(struct PokedexListItem *item, u8 b)
{
    u8 taskId;

    sPokedexListItem = item;
    taskId = CreateTask(Task_InitPageScreenMultistep, 0);
    gTasks[taskId].data[0] = 0;
    gTasks[taskId].data[1] = 1;
    gTasks[taskId].data[2] = 0;
    gTasks[taskId].data[3] = 0;
    gTasks[taskId].data[4] = b;
    return taskId;
}

static bool8 IsInfoScreenScrolling(u8 taskId)
{
    if (gTasks[taskId].data[0] == 0 && gTasks[taskId].func == Task_PageScreenProcessInput)
        return FALSE;
    else
        return TRUE;
}

static u8 sub_808F284(struct PokedexListItem *item, u8 b)
{
    sPokedexListItem = item;
    gTasks[b].data[0] = 1;
    gTasks[b].data[1] = 0;
    gTasks[b].data[2] = 0;
    gTasks[b].data[3] = 0;
    return b;
}

#if ENGLISH
#define CATEGORY_LEFT (11)
#elif GERMAN
#define CATEGORY_LEFT (16)
#endif

static void Task_InitPageScreenMultistep(u8 taskId)
{
    switch (gMain.state)
    {
    case 0:
    default:
        if (!gPaletteFade.active)
        {
            u16 r2;

            gPokedexView->currentPage = 1;
            gPokedexView->descriptionPageNum = 0;
            gPokedexVBlankCB = gMain.vblankCallback;
            SetVBlankCallback(NULL);
            r2 = 0;
            if (gTasks[taskId].data[1] != 0)
                r2 += 0x1000;
            if (gTasks[taskId].data[2] != 0)
                r2 |= 0x200;
            ResetOtherVideoRegisters(r2);
            gMain.state = 1;
        }
        break;
    case 1:
        LZ77UnCompVram(gPokedexMenu_Gfx, (void *)VRAM);
        LZ77UnCompVram(gUnknown_08E96BD4, (void *)(VRAM + 0x7800));
        PrintFootprint(sPokedexListItem->dexNum, 2, 0x3FC);
        gMain.state++;
        break;
    case 2:
        LoadScreenSelectBarMain(0xD);
        HighlightScreenSelectBarItem(gPokedexView->selectedScreen, 0xD);
        LoadPokedexBgPalette();
        gMain.state++;
        break;
    case 3:
        Text_LoadWindowTemplate(&gWindowTemplate_81E7064);
        InitMenuWindow(&gWindowTemplate_81E7064);
        gMain.state++;
        break;
    case 4:
        if (gPokedexView->dexMode == DEX_MODE_HOENN)
            PrintEntryScreenDexNum(NationalToHoennOrder(sPokedexListItem->dexNum), 0xD, 3);
        else
            PrintEntryScreenDexNum(sPokedexListItem->dexNum, 0xD, 3);
        PrintEntryScreenSpeciesName(sPokedexListItem->dexNum, 0x10, 3);
        Menu_PrintText(gDexText_UnknownPoke, CATEGORY_LEFT, 5);
        Menu_PrintText(gDexText_UnknownHeight, 16, 7);
        Menu_PrintText(gDexText_UnknownWeight, 16, 9);
        if (sPokedexListItem->owned)
        {
            UnusedPrintMonName(gPokedexEntries[sPokedexListItem->dexNum].categoryName, CATEGORY_LEFT, 5);
            sub_8091458(gPokedexEntries[sPokedexListItem->dexNum].height, 16, 7);
            sub_8091564(gPokedexEntries[sPokedexListItem->dexNum].weight, 16, 9);
            Menu_PrintText(gPokedexEntries[sPokedexListItem->dexNum].descriptionPage1, 2, 13);
            sub_80C0DC0(14, 0x3FC);
        }
        else
        {
            Menu_PrintText(gUnknown_083A05F8, 2, 13);
            LoadPalette(gPlttBufferUnfaded + 1, 0x31, 0x1E);
        }
        gMain.state++;
        break;
    case 5:
        if (gTasks[taskId].data[1] == 0)
        {
            gTasks[taskId].data[4] = (u16)CreateMonSpriteFromNationalDexNumber(sPokedexListItem->dexNum, 0x30, 0x38, 0);
            gSprites[gTasks[taskId].data[4]].oam.priority = 0;
        }
        gMain.state++;
        break;
    case 6:
        {
            u32 excludedPalettes = 0;

            if (gTasks[taskId].data[2] != 0)
                excludedPalettes = 0x14;
            if (gTasks[taskId].data[1] != 0)
                excludedPalettes |= (1 << (gSprites[gTasks[taskId].data[4]].oam.paletteNum + 16));
            BeginNormalPaletteFade(~excludedPalettes, 0, 16, 0, RGB(0, 0, 0));
            SetVBlankCallback(gPokedexVBlankCB);
            gMain.state++;
        }
        break;
    case 7:
        REG_BLDCNT = 0;
        REG_BLDALPHA = 0;
        REG_BLDY = 0;
        REG_BG3CNT = BGCNT_PRIORITY(3) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(15) | BGCNT_16COLOR | BGCNT_TXT256x256;
        REG_BG1CNT = BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(13) | BGCNT_16COLOR | BGCNT_TXT256x256;
        REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG1_ON | DISPCNT_BG2_ON | DISPCNT_BG3_ON | DISPCNT_OBJ_ON;
        gMain.state++;
        break;
    case 8:
        if (!gPaletteFade.active)
        {
            gMain.state++;
            if (gTasks[taskId].data[3] == 0)
            {
                StopCryAndClearCrySongs();
                PlayCry2(NationalPokedexNumToSpecies(sPokedexListItem->dexNum), 0, 0x7D, 0xA);
            }
            else
            {
                gMain.state++;
            }
        }
        break;
    case 9:
        if (!IsCryPlayingOrClearCrySongs())
            gMain.state++;
        break;
    case 10:
        gTasks[taskId].data[0] = 0;
        gTasks[taskId].data[1] = 0;
        gTasks[taskId].data[2] = 1;
        gTasks[taskId].data[3] = 1;
        gTasks[taskId].func = Task_PageScreenProcessInput;
        gMain.state = 0;
        break;
    }
}

static void Task_PageScreenProcessInput(u8 taskId)
{
    if (gTasks[taskId].data[0] != 0)
    {
        BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
        gTasks[taskId].func = Task_LoadInfoScreenWaitForFade;
        PlaySE(SE_DEX_SCROLL);
        return;
    }
    if (gMain.newKeys & B_BUTTON)
    {
        BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
        gTasks[taskId].func = Task_ClosePageScreen;
        PlaySE(SE_PC_OFF);
        return;
    }
    if (gMain.newKeys & A_BUTTON)
    {
        switch (gPokedexView->selectedScreen)
        {
        case PAGE_SCREEN:
            sub_8090C68();
            break;
        case AREA_SCREEN:
            BeginNormalPaletteFade(0xFFFFFFEB, 0, 0, 16, RGB(0, 0, 0));
            gTasks[taskId].func = Task_InitAreaScreenMultistep;
            PlaySE(SE_PIN);
            break;
        case CRY_SCREEN:
            BeginNormalPaletteFade(0xFFFFFFEB, 0, 0, 16, RGB(0, 0, 0));
            gTasks[taskId].func = Task_InitCryScreenMultistep;
            PlaySE(SE_PIN);
            break;
        case SIZE_SCREEN:
            if (!sPokedexListItem->owned)
            {
                PlaySE(SE_FAILURE);
            }
            else
            {
                BeginNormalPaletteFade(0xFFFFFFEB, 0, 0, 16, RGB(0, 0, 0));
                gTasks[taskId].func = Task_InitSizeScreenMultistep;
                PlaySE(SE_PIN);
            }
            break;
        }
        return;
    }
    if (((gMain.newKeys & DPAD_LEFT)
     || ((gMain.newKeys & L_BUTTON) && gSaveBlock2.optionsButtonMode == OPTIONS_BUTTON_MODE_LR))
     && gPokedexView->selectedScreen > 0)
    {
        gPokedexView->selectedScreen--;
        HighlightScreenSelectBarItem(gPokedexView->selectedScreen, 0xD);
        PlaySE(SE_DEX_PAGE);
        return;
    }
    if (((gMain.newKeys & DPAD_RIGHT)
     || ((gMain.newKeys & R_BUTTON) && gSaveBlock2.optionsButtonMode == OPTIONS_BUTTON_MODE_LR))
     && gPokedexView->selectedScreen < 3)
    {
        gPokedexView->selectedScreen++;
        HighlightScreenSelectBarItem(gPokedexView->selectedScreen, 0xD);
        PlaySE(SE_DEX_PAGE);
        return;
    }
}

static void Task_LoadInfoScreenWaitForFade(u8 taskId)
{
    if (!gPaletteFade.active)
        gTasks[taskId].func = Task_InitPageScreenMultistep;
}

static void Task_ClosePageScreen(u8 taskId)
{
    if (!gPaletteFade.active)
        DestroyTask(taskId);
}

static void Task_InitAreaScreenMultistep(u8 taskId)
{
    switch (gMain.state)
    {
    case 0:
    default:
        if (!gPaletteFade.active)
        {
            gPokedexView->currentPage = 5;
            gPokedexVBlankCB = gMain.vblankCallback;
            SetVBlankCallback(NULL);
            ResetOtherVideoRegisters(0x200);
            gPokedexView->selectedScreen = AREA_SCREEN;
            gMain.state = 1;
        }
        break;
    case 1:
        LoadScreenSelectBarSubmenu(0xD);
        sub_8090644(1, 0xD);
        LoadPokedexBgPalette();
        REG_BG1CNT = BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(13) | BGCNT_16COLOR | BGCNT_TXT256x256;
        gMain.state++;
        break;
    case 2:
        ShowPokedexAreaScreen(NationalPokedexNumToSpecies(sPokedexListItem->dexNum), &gPokedexView->screenSwitchState);
        SetVBlankCallback(gPokedexVBlankCB);
        gPokedexView->screenSwitchState = 0;
        gMain.state = 0;
        gTasks[taskId].func = Task_AreaScreenProcessInput;
        break;
    }
}

static void Task_AreaScreenProcessInput(u8 taskId)
{
    if (gPokedexView->screenSwitchState != 0)
        gTasks[taskId].func = sub_808FA00;
}

static void sub_808FA00(u8 taskId)
{
    if (!gPaletteFade.active)
    {
        switch (gPokedexView->screenSwitchState)
        {
        case 1:
        default:
            gTasks[taskId].func = Task_InitPageScreenMultistep;
            break;
        case 2:
            gTasks[taskId].func = Task_InitCryScreenMultistep;
            break;
        }
    }
}

static void Task_InitCryScreenMultistep(u8 taskId)
{
    switch (gMain.state)
    {
    case 0:
    default:
        if (!gPaletteFade.active)
        {
            m4aMPlayStop(&gMPlayInfo_BGM);
            gPokedexView->currentPage = 6;
            gPokedexVBlankCB = gMain.vblankCallback;
            SetVBlankCallback(NULL);
            ResetOtherVideoRegisters(0x200);
            gPokedexView->selectedScreen = CRY_SCREEN;
            gMain.state = 1;
        }
        break;
    case 1:
        LZ77UnCompVram(gPokedexMenu_Gfx, (void *)VRAM);
        LZ77UnCompVram(gUnknown_0839F8A0, (void *)(VRAM + 0x7000));
        gMain.state++;
        break;
    case 2:
        LoadScreenSelectBarSubmenu(0xD);
        sub_8090644(2, 0xD);
        LoadPokedexBgPalette();
        DmaClear16(3, (void *)(VRAM + 0xF800), 0x500);
        gMain.state++;
        break;
    case 3:
        Text_LoadWindowTemplate(&gWindowTemplate_81E702C);
        InitMenuWindow(&gWindowTemplate_81E702C);
        ResetPaletteFade();
        gMain.state++;
        break;
    case 4:
        Menu_PrintText(gDexText_CryOf, 10, 4);
        PrintCryScreenSpeciesName(sPokedexListItem->dexNum, 10, 6, 2);
        gMain.state++;
        break;
    case 5:
        gTasks[taskId].data[4] =
            CreateMonSpriteFromNationalDexNumber(sPokedexListItem->dexNum, 0x30, 0x38, 0);
        gSprites[gTasks[taskId].data[4]].oam.priority = 0;
        gDexCryScreenState = 0;
        gMain.state++;
        break;
    case 6:
        {
            struct CryScreenWindow waveformWindow;

            waveformWindow.charBase = 0x4020;
            waveformWindow.screenBase = 0x1F;
            waveformWindow.paletteNo = 8;
            waveformWindow.yPos = 0x1E;
            waveformWindow.xPos = 0xC;
            if (LoadCryWaveformWindow(&waveformWindow, 0) != 0)
            {
                gMain.state++;
                gDexCryScreenState = 0;
            }
        }
        break;
    case 7:
        {
            struct CryScreenWindow sp10;

            sp10.charBase = 0x3000;
            sp10.screenBase = 0xE;
            sp10.paletteNo = 9;
            sp10.xPos = 0x12;
            sp10.yPos = 3;
            if (ShowPokedexCryScreen(&sp10, 1) != 0)
                gMain.state++;
        }
        break;
    case 8:
        BeginNormalPaletteFade(0xFFFFFFEB, 0, 16, 0, RGB(0, 0, 0));
        SetVBlankCallback(gPokedexVBlankCB);
        gMain.state++;
        break;
    case 9:
        REG_BLDCNT = 0;
        REG_BLDALPHA = 0;
        REG_BLDY = 0;
        REG_BG2CNT = BGCNT_PRIORITY(2) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(14) | BGCNT_16COLOR | BGCNT_TXT256x256;
        REG_BG0CNT = BGCNT_PRIORITY(3) | BGCNT_CHARBASE(1) | BGCNT_SCREENBASE(31) | BGCNT_16COLOR | BGCNT_TXT256x256;
        REG_BG1CNT = BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(13) | BGCNT_16COLOR | BGCNT_TXT256x256;
        REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG_ALL_ON | DISPCNT_OBJ_ON;
        gMain.state++;
        break;
    case 10:
        gPokedexView->screenSwitchState = 0;
        gMain.state = 0;
        gTasks[taskId].func = Task_CryScreenProcessInput;
        break;
    }
}

static void Task_CryScreenProcessInput(u8 taskId)
{
    UpdateCryWaveformWindow(0);

    if (IsCryPlaying())
        LoadPlayArrowPalette(1);
    else
        LoadPlayArrowPalette(0);

    if (gMain.newKeys & A_BUTTON)
    {
        LoadPlayArrowPalette(1);
        CryScreenPlayButton(NationalPokedexNumToSpecies(sPokedexListItem->dexNum));
        return;
    }
    else if (!gPaletteFade.active)
    {
        if (gMain.newKeys & B_BUTTON)
        {
            BeginNormalPaletteFade(0xFFFFFFEB, 0, 0, 16, RGB(0, 0, 0));
            m4aMPlayContinue(&gMPlayInfo_BGM);
            gPokedexView->screenSwitchState = 1;
            gTasks[taskId].func = Task_SwitchScreensFromCryScreen;
            PlaySE(SE_PC_OFF);
            return;
        }
        if ((gMain.newKeys & DPAD_LEFT)
         || ((gMain.newKeys & L_BUTTON) && gSaveBlock2.optionsButtonMode == OPTIONS_BUTTON_MODE_LR))
        {
            BeginNormalPaletteFade(0xFFFFFFEB, 0, 0, 16, RGB(0, 0, 0));
            m4aMPlayContinue(&gMPlayInfo_BGM);
            gPokedexView->screenSwitchState = 2;
            gTasks[taskId].func = Task_SwitchScreensFromCryScreen;
            PlaySE(SE_DEX_PAGE);
            return;
        }
        if ((gMain.newKeys & DPAD_RIGHT)
         || ((gMain.newKeys & R_BUTTON) && gSaveBlock2.optionsButtonMode == OPTIONS_BUTTON_MODE_LR))
        {
            if (!sPokedexListItem->owned)
            {
                PlaySE(SE_FAILURE);
            }
            else
            {
                BeginNormalPaletteFade(0xFFFFFFEB, 0, 0, 16, RGB(0, 0, 0));
                m4aMPlayContinue(&gMPlayInfo_BGM);
                gPokedexView->screenSwitchState = 3;
                gTasks[taskId].func = Task_SwitchScreensFromCryScreen;
                PlaySE(SE_DEX_PAGE);
            }
            return;
        }
    }
}

static void Task_SwitchScreensFromCryScreen(u8 taskId)
{
    if (!gPaletteFade.active)
    {
        DestroyCryMeterNeedleSprite();
        switch (gPokedexView->screenSwitchState)
        {
        default:
        case 1:
            gTasks[taskId].func = Task_InitPageScreenMultistep;
            break;
        case 2:
            gTasks[taskId].func = Task_InitAreaScreenMultistep;
            break;
        case 3:
            gTasks[taskId].func = Task_InitSizeScreenMultistep;
            break;
        }
    }
}

static void LoadPlayArrowPalette(u8 cryPlaying)
{
    u16 color;

    if (cryPlaying)
        color = RGB(18, 28, 0);
    else
        color = RGB(15, 21, 0);
    LoadPalette(&color, 0x5D, 2);
}

static void Task_InitSizeScreenMultistep(u8 taskId)
{
    u8 spriteId;

    switch (gMain.state)
    {
    default:
    case 0:
        if (!gPaletteFade.active)
        {
            gPokedexView->currentPage = 7;
            gPokedexVBlankCB = gMain.vblankCallback;
            SetVBlankCallback(NULL);
            ResetOtherVideoRegisters(0x200);
            gPokedexView->selectedScreen = SIZE_SCREEN;
            gMain.state = 1;
        }
        break;
    case 1:
        LZ77UnCompVram(gPokedexMenu_Gfx, (void *)VRAM);
        LZ77UnCompVram(gUnknown_0839F988, (void *)(VRAM + 0x7000));
        gMain.state++;
        break;
    case 2:
        LoadScreenSelectBarSubmenu(0xD);
        sub_8090644(3, 0xD);
        LoadPokedexBgPalette();
        gMain.state++;
        break;
    case 3:
        {
            // This only needs to be 25 chars long (31 in German).
            u8 string[40];  //I hope this is the correct size

            Text_LoadWindowTemplate(&gWindowTemplate_81E702C);
            InitMenuWindow(&gWindowTemplate_81E702C);
            string[0] = EOS;
            StringAppend(string, gDexText_SizeComparedTo);
            StringAppend(string, gSaveBlock2.playerName);
            MenuPrint_Centered(string, 3, 15, 0xC0);
            gMain.state++;
        }
        break;
    case 4:
        ResetPaletteFade();
        gMain.state++;
        break;
    case 5:
        spriteId = CreateSizeScreenTrainerPic(gSaveBlock2.playerGender, 152, 56, 0);
        gSprites[spriteId].oam.affineMode = 1;
        gSprites[spriteId].oam.matrixNum = 1;
        gSprites[spriteId].oam.priority = 0;
        gSprites[spriteId].y2 = gPokedexEntries[sPokedexListItem->dexNum].trainerOffset;
        SetOamMatrix(1, gPokedexEntries[sPokedexListItem->dexNum].trainerScale, 0, 0, gPokedexEntries[sPokedexListItem->dexNum].trainerScale);
        LoadPalette(sSizeScreenSilhouette_Pal, (gSprites[spriteId].oam.paletteNum + 16) * 16, sizeof(sSizeScreenSilhouette_Pal));
        gMain.state++;
        break;
    case 6:
        spriteId = CreateMonSpriteFromNationalDexNumber(sPokedexListItem->dexNum, 88, 56, 1);
        gSprites[spriteId].oam.affineMode = 1;
        gSprites[spriteId].oam.matrixNum = 2;
        gSprites[spriteId].oam.priority = 0;
        gSprites[spriteId].y2 = gPokedexEntries[sPokedexListItem->dexNum].pokemonOffset;
        SetOamMatrix(2, gPokedexEntries[sPokedexListItem->dexNum].pokemonScale, 0, 0, gPokedexEntries[sPokedexListItem->dexNum].pokemonScale);
        LoadPalette(sSizeScreenSilhouette_Pal, (gSprites[spriteId].oam.paletteNum + 16) * 16, sizeof(sSizeScreenSilhouette_Pal));
        gMain.state++;
        break;
    case 7:
        BeginNormalPaletteFade(0xFFFFFFEB, 0, 16, 0, RGB(0, 0, 0));
        SetVBlankCallback(gPokedexVBlankCB);
        gMain.state++;
        break;
    case 8:
        REG_BLDCNT = 0;
        REG_BLDALPHA = 0;
        REG_BLDY = 0;
        REG_BG2CNT = BGCNT_PRIORITY(3) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(14) | BGCNT_16COLOR | BGCNT_TXT256x256;
        REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG1_ON | DISPCNT_BG2_ON | DISPCNT_BG3_ON | DISPCNT_OBJ_ON;
        gMain.state++;
        break;
    case 9:
        if (!gPaletteFade.active)
        {
            gPokedexView->screenSwitchState = 0;
            gMain.state = 0;
            gTasks[taskId].func = Task_SizeScreenProcessInput;
        }
        break;
    }
}

static void Task_SizeScreenProcessInput(u8 taskId)
{
    if (gMain.newKeys & B_BUTTON)
    {
        BeginNormalPaletteFade(0xFFFFFFEB, 0, 0, 16, RGB(0, 0, 0));
        gPokedexView->screenSwitchState = 1;
        gTasks[taskId].func = Task_SwitchScreensFromSizeScreen;
        PlaySE(SE_PC_OFF);
    }
    else if ((gMain.newKeys & DPAD_LEFT)
     || ((gMain.newKeys & L_BUTTON) && gSaveBlock2.optionsButtonMode == OPTIONS_BUTTON_MODE_LR))
    {
        BeginNormalPaletteFade(0xFFFFFFEB, 0, 0, 16, RGB(0, 0, 0));
        gPokedexView->screenSwitchState = 2;
        gTasks[taskId].func = Task_SwitchScreensFromSizeScreen;
        PlaySE(SE_DEX_PAGE);
    }
}

static void Task_SwitchScreensFromSizeScreen(u8 taskId)
{
    if (!gPaletteFade.active)
    {
        switch (gPokedexView->screenSwitchState)
        {
        default:
        case 1:
            gTasks[taskId].func = Task_InitPageScreenMultistep;
            break;
        case 2:
            gTasks[taskId].func = Task_InitCryScreenMultistep;
            break;
        }
    }
}

static void LoadScreenSelectBarMain(u16 screenBase)
{
    LZ77UnCompVram(gPokedexScreenSelectBarMain_Tilemap, (void *)(VRAM + screenBase * 0x800));
    DmaClear16(3, (void *)(VRAM + screenBase * 0x800 + 0xC0), 0x440);
}

static void LoadScreenSelectBarSubmenu(u16 screenBase)
{
    LZ77UnCompVram(gPokedexScreenSelectBarSubmenu_Tilemap, (void *)(VRAM + screenBase * 0x800));
    DmaClear16(3, (void *)(VRAM + screenBase * 0x800 + 0xC0), 0x440);
}

#ifdef NONMATCHING
static void HighlightScreenSelectBarItem(u8 a, u16 b)
{
    u8 i;   //r1
    u8 j;   //r3
    u32 r6;
    register u8 r7;

    for (i = 0; i < 4; i++)
    {
        r7 = i * 5 + 1;
        r6 = 0x4000;

        if (i == a)
            r6 = 0x2000;

        for (j = 0; j < 5; j++)
        {
            u32 r0 = b * 0x800 + (r7 + j) * 2;
            u8 *ptr;

            ptr = (void *)VRAM;
            *(u16 *)(ptr + r0) = (*(u16 *)(ptr + r0) & 0xFFF) | r6;
            ptr = (void *)VRAM + 0x40;
            *(u16 *)(ptr + r0) = (*(u16 *)(ptr + r0) & 0xFFF) | r6;
        }
    }
    r6 = 0x4000;
    for (j = 0; j < 5; j++)
    {
        u32 r0 = b * 0x800 + j * 2;
        u8 *ptr;

        ptr = (void *)VRAM + 0x32;
        *(u16 *)(ptr + r0) = (*(u16 *)(ptr + r0) & 0xFFF) | r6;
        ptr = (void *)VRAM + 0x72;
        *(u16 *)(ptr + r0) = (*(u16 *)(ptr + r0) & 0xFFF) | r6;
    }
}
#else
NAKED
static void HighlightScreenSelectBarItem(u8 a, u16 b)
{
    asm(".syntax unified\n\
    push {r4-r7,lr}\n\
    mov r7, r10\n\
    mov r6, r9\n\
    mov r5, r8\n\
    push {r5-r7}\n\
    lsls r0, 24\n\
    lsrs r0, 24\n\
    mov r10, r0\n\
    lsls r1, 16\n\
    lsrs r1, 16\n\
    mov r9, r1\n\
    movs r1, 0\n\
_0809059C:\n\
    lsls r0, r1, 2\n\
    adds r0, r1\n\
    adds r0, 0x1\n\
    lsls r0, 24\n\
    lsrs r7, r0, 24\n\
    movs r6, 0x80\n\
    lsls r6, 7\n\
    cmp r1, r10\n\
    bne _080905B2\n\
    movs r6, 0x80\n\
    lsls r6, 6\n\
_080905B2:\n\
    movs r3, 0\n\
    mov r0, r9\n\
    lsls r0, 11\n\
    mov r12, r0\n\
    adds r1, 0x1\n\
    mov r8, r1\n\
    mov r5, r12\n\
    ldr r4, _08090634 @ =0x00000fff\n\
_080905C2:\n\
    adds r0, r7, r3\n\
    lsls r0, 1\n\
    adds r0, r5, r0\n\
    movs r2, 0xC0\n\
    lsls r2, 19\n\
    adds r1, r0, r2\n\
    ldrh r2, [r1]\n\
    ands r2, r4\n\
    orrs r2, r6\n\
    strh r2, [r1]\n\
    ldr r1, _08090638 @ =0x06000040\n\
    adds r0, r1\n\
    ldrh r2, [r0]\n\
    ands r2, r4\n\
    orrs r2, r6\n\
    strh r2, [r0]\n\
    adds r0, r3, 0x1\n\
    lsls r0, 24\n\
    lsrs r3, r0, 24\n\
    cmp r3, 0x4\n\
    bls _080905C2\n\
    mov r2, r8\n\
    lsls r0, r2, 24\n\
    lsrs r1, r0, 24\n\
    cmp r1, 0x3\n\
    bls _0809059C\n\
    movs r6, 0x80\n\
    lsls r6, 7\n\
    movs r3, 0\n\
    mov r5, r12\n\
    ldr r4, _08090634 @ =0x00000fff\n\
_08090600:\n\
    lsls r0, r3, 1\n\
    adds r0, r5, r0\n\
    ldr r2, _0809063C @ =0x06000032\n\
    adds r1, r0, r2\n\
    ldrh r2, [r1]\n\
    ands r2, r4\n\
    orrs r2, r6\n\
    strh r2, [r1]\n\
    ldr r1, _08090640 @ =0x06000072\n\
    adds r0, r1\n\
    ldrh r2, [r0]\n\
    ands r2, r4\n\
    orrs r2, r6\n\
    strh r2, [r0]\n\
    adds r0, r3, 0x1\n\
    lsls r0, 24\n\
    lsrs r3, r0, 24\n\
    cmp r3, 0x4\n\
    bls _08090600\n\
    pop {r3-r5}\n\
    mov r8, r3\n\
    mov r9, r4\n\
    mov r10, r5\n\
    pop {r4-r7}\n\
    pop {r0}\n\
    bx r0\n\
    .align 2, 0\n\
_08090634: .4byte 0x00000fff\n\
_08090638: .4byte 0x06000040\n\
_0809063C: .4byte 0x06000032\n\
_08090640: .4byte 0x06000072\n\
    .syntax divided\n");
}
#endif

//Nope, can't get this one to match, either.
#ifdef NONMATCHING
static void sub_8090644(u8 a, u16 b)
{
    u8 i;
    u8 j;

    for (i = 0; i < 4; i++)
    {
        u8 r8 = i * 5 + 1;
        u32 r5;

        if (i == a || i == 0)
            r5 = 0x2000;
        else if (a != 0)
            r5 = 0x4000;

        for (j = 0; j < 5; j++)
        {
            u16 (*vramData)[0x400];

            vramData = (u16 (*)[])VRAM;
            vramData[b][r8 + j] = (vramData[b][r8 + j] & 0xFFF) | r5;
            vramData = (u16 (*)[])(VRAM + 0x40);
            vramData[b][r8 + j] = (vramData[b][r8 + j] & 0xFFF) | r5;
        }
    }

    for (j = 0; j < 5; j++)
    {
        u16 (*vramData)[0x400];

        vramData = (u16 (*)[])(VRAM + 0x32);
        vramData[b][j] = (vramData[b][j] & 0xFFF) | 0x4000;
        vramData = (u16 (*)[])(VRAM + 0x72);
        vramData[b][j] = (vramData[b][j] & 0xFFF) | 0x4000;
    }
}
#else
NAKED
static void sub_8090644(u8 a, u16 b)
{
    asm(".syntax unified\n\
    push {r4-r7,lr}\n\
    mov r7, r10\n\
    mov r6, r9\n\
    mov r5, r8\n\
    push {r5-r7}\n\
    lsls r0, 24\n\
    lsrs r0, 24\n\
    mov r10, r0\n\
    lsls r1, 16\n\
    lsrs r1, 16\n\
    mov r9, r1\n\
    movs r1, 0\n\
_0809065C:\n\
    lsls r0, r1, 2\n\
    adds r0, r1\n\
    adds r0, 0x1\n\
    lsls r0, 24\n\
    lsrs r0, 24\n\
    mov r8, r0\n\
    cmp r1, r10\n\
    beq _08090670\n\
    cmp r1, 0\n\
    bne _08090676\n\
_08090670:\n\
    movs r5, 0x80\n\
    lsls r5, 6\n\
    b _0809067A\n\
_08090676:\n\
    movs r5, 0x80\n\
    lsls r5, 7\n\
_0809067A:\n\
    movs r3, 0\n\
    mov r0, r9\n\
    lsls r7, r0, 11\n\
    adds r1, 0x1\n\
    mov r12, r1\n\
    adds r6, r7, 0\n\
    ldr r4, _080906FC @ =0x00000fff\n\
_08090688:\n\
    mov r1, r8\n\
    adds r0, r1, r3\n\
    lsls r0, 1\n\
    adds r0, r6, r0\n\
    movs r2, 0xC0\n\
    lsls r2, 19\n\
    adds r1, r0, r2\n\
    ldrh r2, [r1]\n\
    ands r2, r4\n\
    orrs r2, r5\n\
    strh r2, [r1]\n\
    ldr r1, _08090700 @ =0x06000040\n\
    adds r0, r1\n\
    ldrh r2, [r0]\n\
    ands r2, r4\n\
    orrs r2, r5\n\
    strh r2, [r0]\n\
    adds r0, r3, 0x1\n\
    lsls r0, 24\n\
    lsrs r3, r0, 24\n\
    cmp r3, 0x4\n\
    bls _08090688\n\
    mov r2, r12\n\
    lsls r0, r2, 24\n\
    lsrs r1, r0, 24\n\
    cmp r1, 0x3\n\
    bls _0809065C\n\
    movs r5, 0x80\n\
    lsls r5, 7\n\
    movs r3, 0\n\
    adds r6, r7, 0\n\
    ldr r4, _080906FC @ =0x00000fff\n\
_080906C8:\n\
    lsls r0, r3, 1\n\
    adds r0, r6, r0\n\
    ldr r2, _08090704 @ =0x06000032\n\
    adds r1, r0, r2\n\
    ldrh r2, [r1]\n\
    ands r2, r4\n\
    orrs r2, r5\n\
    strh r2, [r1]\n\
    ldr r1, _08090708 @ =0x06000072\n\
    adds r0, r1\n\
    ldrh r2, [r0]\n\
    ands r2, r4\n\
    orrs r2, r5\n\
    strh r2, [r0]\n\
    adds r0, r3, 0x1\n\
    lsls r0, 24\n\
    lsrs r3, r0, 24\n\
    cmp r3, 0x4\n\
    bls _080906C8\n\
    pop {r3-r5}\n\
    mov r8, r3\n\
    mov r9, r4\n\
    mov r10, r5\n\
    pop {r4-r7}\n\
    pop {r0}\n\
    bx r0\n\
    .align 2, 0\n\
_080906FC: .4byte 0x00000fff\n\
_08090700: .4byte 0x06000040\n\
_08090704: .4byte 0x06000032\n\
_08090708: .4byte 0x06000072\n\
    .syntax divided\n");
}
#endif

u8 sub_809070C(u16 dexNum, u32 b, u32 c)
{
    u8 taskId = CreateTask(sub_8090750, 0);

    gTasks[taskId].data[0] = 0;
    gTasks[taskId].data[1] = dexNum;
    gTasks[taskId].data[12] = b;
    gTasks[taskId].data[13] = b >> 16;
    gTasks[taskId].data[14] = c;
    gTasks[taskId].data[15] = c >> 16;
    return taskId;
}

static void sub_8090750(u8 taskId)
{
    u8 spriteId;
    u16 dexNum = gTasks[taskId].data[1];
    u16 i;

    switch (gTasks[taskId].data[0])
    {
    default:
    case 0:
        if (!gPaletteFade.active)
        {
            gPokedexVBlankCB = gMain.vblankCallback;
            SetVBlankCallback(NULL);
            ResetOtherVideoRegisters(0x100);
            gTasks[taskId].data[0] = 1;
        }
        break;
    case 1:
        LZ77UnCompVram(gPokedexMenu_Gfx, (void *)(VRAM + 0x4000));
        LZ77UnCompVram(gUnknown_08E96BD4, (void *)(VRAM + 0x7800));
        for (i = 0; i < 0x280; i++)
        {
            u16 temp = *(u16 *)(BG_VRAM + 0x7800 + 2 * i);
            temp += 0x2000;
            *(u16 *)(BG_VRAM + 0x7800 + 2 * i) = temp;
        }
        PrintFootprint(gTasks[taskId].data[1], 2, 0x3FC);
        ResetPaletteFade();
        LoadPalette(gPokedexMenu_Pal + 1, 0x21, 0x9E);
        gTasks[taskId].data[0]++;
        break;
    case 2:
        Text_LoadWindowTemplate(&gWindowTemplate_81E7064);
        InitMenuWindow(&gWindowTemplate_81E7064);
        DmaClear16(3, (void *)(VRAM + 0xC000), 0x200);
        gTasks[taskId].data[0]++;
        break;
    case 3:
        MenuPrint_Centered(gDexText_RegisterComplete, 2, 0, 0xD0);
        if (!IsNationalPokedexEnabled())
            PrintEntryScreenDexNum(NationalToHoennOrder(dexNum), 13, 3);
        else
            PrintEntryScreenDexNum(dexNum, 13, 3);
        PrintEntryScreenSpeciesName(dexNum, 16, 3);
        Menu_PrintText(gDexText_UnknownPoke, CATEGORY_LEFT, 5);
        Menu_PrintText(gDexText_UnknownHeight, 16, 7);
        Menu_PrintText(gDexText_UnknownWeight, 16, 9);
        UnusedPrintMonName(gPokedexEntries[dexNum].categoryName, CATEGORY_LEFT, 5);
        sub_8091458(gPokedexEntries[dexNum].height, 16, 7);
        sub_8091564(gPokedexEntries[dexNum].weight, 16, 9);
        Menu_PrintText(gPokedexEntries[dexNum].descriptionPage1, 2, 13);
        sub_80C0DC0(14, 0x3FC);
        gTasks[taskId].data[0]++;
        break;
    case 4:
        spriteId = CreateMonSpriteFromNationalDexNumber(dexNum, 0x30, 0x38, 0);
        gSprites[spriteId].oam.priority = 0;
        BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB(0, 0, 0));
        SetVBlankCallback(gPokedexVBlankCB);
        gTasks[taskId].data[3] = spriteId;
        gTasks[taskId].data[0]++;
        break;
    case 5:
        REG_BLDCNT = 0;
        REG_BLDALPHA = 0;
        REG_BLDY = 0;
        REG_BG3CNT = BGCNT_PRIORITY(3) | BGCNT_CHARBASE(1) | BGCNT_SCREENBASE(15) | BGCNT_16COLOR | BGCNT_TXT256x256;
        REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG2_ON | DISPCNT_BG3_ON | DISPCNT_OBJ_ON;
        gTasks[taskId].data[0]++;
        break;
    case 6:
        if (!gPaletteFade.active)
        {
            PlayCry1(NationalPokedexNumToSpecies(dexNum), 0);
            gTasks[taskId].data[2] = 0;
            gTasks[taskId].data[4] = 0;
            gTasks[taskId].func = sub_8090A3C;
        }
        break;
    }
}

static void sub_8090A3C(u8 taskId)
{
    if (gMain.newKeys & B_BUTTON)
    {
        BeginNormalPaletteFade(0xFFFC, 0, 0, 16, RGB(0, 0, 0));
        gSprites[gTasks[taskId].data[3]].callback = sub_8090C28;
        gTasks[taskId].func = sub_8090B8C;
        return;
    }
    else if (gMain.newKeys & A_BUTTON)
    {
        if (gTasks[taskId].data[4] == 0)
        {
            u16 r4 = gTasks[taskId].data[1];

            Menu_EraseWindowRect(2, 13, 27, 19);
            Menu_PrintText(gPokedexEntries[r4].descriptionPage2, 2, 13);
            (*(u16 *)(BG_VRAM + 0x7ACA))++;
            (*(u16 *)(BG_VRAM + 0x7B0A))++;
            gTasks[taskId].data[4] = 1;
            PlaySE(SE_PIN);
        }
        else
        {
            BeginNormalPaletteFade(0xFFFC, 0, 0, 16, RGB(0, 0, 0));
            gSprites[gTasks[taskId].data[3]].callback = sub_8090C28;
            gTasks[taskId].func = sub_8090B8C;
            return;
        }
    }
    gTasks[taskId].data[2]++;
    if (gTasks[taskId].data[2] & 0x10)
        LoadPalette(gPokedexMenu_Pal + 1, 0x51, 14);
    else
        LoadPalette(gPokedexMenu2_Pal + 1, 0x51, 14);
}

static void sub_8090B8C(u8 taskId)
{
    if (!gPaletteFade.active)
    {
        u16 species;
        u32 otId;
        u32 personality;
        u8 paletteNum;
        const u8 *lzPaletteData;

        REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON | DISPCNT_BG3_ON | DISPCNT_OBJ_ON;
        CpuCopy16(gBattleTextboxTilemap, (void *)(VRAM + 0xC000), 0x1000);
        ApplyPlayerChosenFrameToBattleMenu();
        species = NationalPokedexNumToSpecies(gTasks[taskId].data[1]);
        otId = ((u16)gTasks[taskId].data[13] << 16) | (u16)gTasks[taskId].data[12];
        personality = ((u16)gTasks[taskId].data[15] << 16) | (u16)gTasks[taskId].data[14];
        paletteNum = gSprites[gTasks[taskId].data[3]].oam.paletteNum;
        lzPaletteData = GetMonSpritePalFromOtIdPersonality(species, otId, personality);
        LoadCompressedPalette(lzPaletteData, 0x100 | paletteNum * 16, 32);
        DestroyTask(taskId);
    }
}

static void sub_8090C28(struct Sprite *sprite)
{
    if (sprite->x < 0x78)
        sprite->x += 2;
    if (sprite->x > 0x78)
        sprite->x -= 2;

    if (sprite->y < 0x50)
        sprite->y += 1;
    if (sprite->y > 0x50)
        sprite->y -= 1;
}

static void sub_8090C68(void)
{
    if (sPokedexListItem->owned)
    {
        if (gPokedexView->descriptionPageNum == 0)
        {
            Menu_EraseWindowRect(2, 13, 27, 19);
            Menu_PrintText(gPokedexEntries[sPokedexListItem->dexNum].descriptionPage2, 2, 13);
            gPokedexView->descriptionPageNum = 1;
            (*(u16 *)(BG_VRAM + 0x7ACA))++;
            (*(u16 *)(BG_VRAM + 0x7B0A))++;
            PlaySE(SE_PIN);
        }
        else
        {
            Menu_EraseWindowRect(2, 13, 27, 19);
            Menu_PrintText(gPokedexEntries[sPokedexListItem->dexNum].descriptionPage1, 2, 13);
            gPokedexView->descriptionPageNum = 0;
            (*(u16 *)(BG_VRAM + 0x7ACA))--;
            (*(u16 *)(BG_VRAM + 0x7B0A))--;
            PlaySE(SE_PIN);
        }
    }
}

const u8 *GetPokemonCategory(u16 dexNum)
{
    return gPokedexEntries[dexNum].categoryName;
}

u16 GetPokedexHeightWeight(u16 dexNum, u8 data)
{
    switch (data)
    {
    case 0:  // height
        return gPokedexEntries[dexNum].height;
    case 1:  // weight
        return gPokedexEntries[dexNum].weight;
    default:
        return 1;
    }
}

s8 GetSetPokedexFlag(u16 nationalDexNo, u8 caseID)
{
    u8 index;
    u8 bit;
    u8 mask;
    s8 retVal;

    nationalDexNo--;
    index = nationalDexNo / 8;
    bit = nationalDexNo % 8;
    mask = 1 << bit;
    retVal = 0;
    switch (caseID)
    {
    case FLAG_GET_SEEN:
        if (gSaveBlock2.pokedex.seen[index] & mask)
        {
            if ((gSaveBlock2.pokedex.seen[index] & mask) == (gSaveBlock1.dexSeen2[index] & mask)
             && (gSaveBlock2.pokedex.seen[index] & mask) == (gSaveBlock1.dexSeen3[index] & mask))
                retVal = 1;
            else
            {
                gSaveBlock2.pokedex.seen[index] &= ~mask;
                gSaveBlock1.dexSeen2[index] &= ~mask;
                gSaveBlock1.dexSeen3[index] &= ~mask;
                retVal = 0;
            }
        }
        break;
    case FLAG_GET_CAUGHT:
        if (gSaveBlock2.pokedex.owned[index] & mask)
        {
            if ((gSaveBlock2.pokedex.owned[index] & mask) == (gSaveBlock2.pokedex.seen[index] & mask)
             && (gSaveBlock2.pokedex.owned[index] & mask) == (gSaveBlock1.dexSeen2[index] & mask)
             && (gSaveBlock2.pokedex.owned[index] & mask) == (gSaveBlock1.dexSeen3[index] & mask))
                retVal = 1;
            else
            {
                gSaveBlock2.pokedex.owned[index] &= ~mask;
                gSaveBlock2.pokedex.seen[index] &= ~mask;
                gSaveBlock1.dexSeen2[index] &= ~mask;
                gSaveBlock1.dexSeen3[index] &= ~mask;
                retVal = 0;
            }
        }
        break;
    case FLAG_SET_SEEN:
        gSaveBlock2.pokedex.seen[index] |= mask;
        gSaveBlock1.dexSeen2[index] |= mask;
        gSaveBlock1.dexSeen3[index] |= mask;
        break;
    case FLAG_SET_CAUGHT:
        gSaveBlock2.pokedex.owned[index] |= mask;
        break;
    }
    return retVal;
}

u16 GetNationalPokedexCount(u8 caseID)
{
    u16 count = 0;
    u16 i;

    for (i = 0; i < NATIONAL_DEX_COUNT; i++)
    {
        switch (caseID)
        {
        case FLAG_GET_SEEN:
            if (GetSetPokedexFlag(i + 1, FLAG_GET_SEEN))
                count++;
            break;
        case FLAG_GET_CAUGHT:
            if (GetSetPokedexFlag(i + 1, FLAG_GET_CAUGHT))
                count++;
            break;
        }
    }
    return count;
}

u16 GetHoennPokedexCount(u8 caseID)
{
    u16 count = 0;
    u16 i;

    for (i = 0; i < HOENN_DEX_COUNT; i++)
    {
        switch (caseID)
        {
        case FLAG_GET_SEEN:
            if (GetSetPokedexFlag(HoennToNationalOrder(i + 1), FLAG_GET_SEEN))
                count++;
            break;
        case FLAG_GET_CAUGHT:
            if (GetSetPokedexFlag(HoennToNationalOrder(i + 1), FLAG_GET_CAUGHT))
                count++;
            break;
        }
    }
    return count;
}

bool8 CompletedHoennPokedex(void)
{
    u16 i;

    for (i = 0; i < 200; i++)
    {
        if (!GetSetPokedexFlag(HoennToNationalOrder(i + 1), FLAG_GET_CAUGHT))
            return FALSE;
    }
    return TRUE;
}

bool16 CompletedNationalPokedex(void)
{
    u16 i;

    // BUG: This function indexes pokemon checks by 0, but adds
    // 1 before passing to GetSetPokedexFlag. Normally, this is
    // fine, because GetSetPokedexFlag subtracts by 1 to get the
    // array index value, but since the array is 0 indexed
    // starting with Bulbasaur, values passed actually means that
    // dex entries 152 (Chikorita) and 252 (Treecko) are skipped.
    // Because an earlier Hoenn Dex check prevented Treecko from
    // being skippable, it means that Chikorita is not required
    // to obtain the National Diploma. This was fixed in Emerald.
    for (i = 0; i < 150; i++)
    {
        if (GetSetPokedexFlag(i + 1, 1) == 0)
            return FALSE;
    }
    for (i = 152; i < 250; i++)
    {
        if (GetSetPokedexFlag(i + 1, 1) == 0)
            return FALSE;
    }
    for (i = 252; i < 384; i++)
    {
        if (GetSetPokedexFlag(i + 1, 1) == 0)
            return FALSE;
    }
    return TRUE;
}

static void ResetOtherVideoRegisters(u16 a)
{
    if (!(a & 0x100))
    {
        REG_DISPCNT &= 0xFEFF;
        REG_BG0CNT = 0;
        REG_BG0HOFS = 0;
        REG_BG0VOFS = 0;
    }
    if (!(a & 0x200))
    {
        REG_DISPCNT &= 0xFDFF;
        REG_BG1CNT = 0;
        REG_BG1HOFS = 0;
        REG_BG1VOFS = 0;
    }
    if (!(a & 0x400))
    {
        REG_DISPCNT &= 0xFBFF;
        REG_BG2CNT = 0;
        REG_BG2HOFS = 0;
        REG_BG2VOFS = 0;
    }
    if (!(a & 0x800))
    {
        REG_DISPCNT &= 0xF7FF;
        REG_BG3CNT = 0;
        REG_BG3HOFS = 0;
        REG_BG3VOFS = 0;
    }
    if (!(a & 0x1000))
    {
        REG_DISPCNT &= 0xEFFF;
        ResetSpriteData();
        FreeAllSpritePalettes();
        gReservedSpritePaletteCount = 8;
    }
}

static void PrintEntryScreenDexNum(u16 order, u8 b, u8 c)
{
    u8 str[4];

    str[0] = CHAR_0 + order / 100;
    str[1] = CHAR_0 + (order % 100) / 10;
    str[2] = CHAR_0 + (order % 100) % 10;
    str[3] = EOS;
    Menu_PrintText(str, b, c);
}

static u8 PrintEntryScreenSpeciesName(u16 num, u8 b, u8 c)
{
    u8 str[11];
    u8 i;

    for (i = 0; i < 11; i++)
        str[i] = EOS;
    num = NationalPokedexNumToSpecies(num);
    if (num)
    {
        for (i = 0; gSpeciesNames[num][i] != EOS && i < 10; i++)
            str[i] = gSpeciesNames[num][i];
    }
    else
    {
        for (i = 0; i < 10; i++)
            str[i] = 0xAE;
    }
    Menu_PrintText(str, b, c);
    return i;
}

static u8 PrintCryScreenSpeciesName(u16 num, u8 b, u8 c, u8 d)
{
    u8 str[40];
    u8 *end;
    u8 i;

    end = StringCopy(str, sText_ClearTo0);
    str[2] = d;
    num = NationalPokedexNumToSpecies(num);
    switch (num)
    {
    default:
        for (i = 0; gSpeciesNames[num][i] != EOS && i < 10; i++)
            end[i] = gSpeciesNames[num][i];
        break;
    case 0:
        for (i = 0; i < 10; i++)
            end[i] = 0xAE;
        break;
    }
    end[i] = EOS;
    Menu_PrintText(str, b, c);
    return i;
}

static void UnusedPrintMonName(const u8 *name, u8 left, u8 top)
{
    u8 str[32];
    u8 i;
#if ENGLISH
    u8 j;
#endif

    for (i = 0; name[i] != EOS && i < 11; i++)
        str[i] = name[i];
#if ENGLISH
    for (j = 0; gDexText_UnknownPoke[j] == CHAR_QUESTION_MARK || gDexText_UnknownPoke[j] == CHAR_SPACE; j++)
        ;
    j--;
    while (gDexText_UnknownPoke[j] != EOS)
        str[i++] = gDexText_UnknownPoke[j++];
#endif
    str[i] = EOS;
    MenuPrint_AlignedToRightOfReferenceString(str, left, top, gDexText_UnknownPoke);
}

#if ENGLISH
void UnusedPrintDecimalNum(u16 a, u8 left, u8 top)
{
    u8 str[6];
    bool8 outputted = FALSE;
    u8 result;

    result = a / 1000;
    if (result == 0)
    {
        str[0] = CHAR_SPACE;
        outputted = FALSE;
    }
    else
    {
        str[0] = CHAR_0 + result;
        outputted = TRUE;
    }

    result = (a % 1000) / 100;
    if (result == 0 && !outputted)
    {
        str[1] = CHAR_SPACE;
        outputted = FALSE;
    }
    else
    {
        str[1] = CHAR_0 + result;
        outputted = TRUE;
    }

    str[2] = CHAR_0 + ((a % 1000) % 100) / 10;
    str[3] = CHAR_PERIOD;
    str[4] = CHAR_0 + ((a % 1000) % 100) % 10;
    str[5] = EOS;
    Menu_PrintText(str, left, top);
}
#elif GERMAN
void UnusedPrintDecimalNum(u16 arg0, u8 left, u8 top) {
    u8 buffer[8];
    int offset;
    u8 result;

    u8 r6 = 0;
    offset = 0;


    buffer[r6++] = 0xFC;
    buffer[r6++] = 0x13;
    r6++;

    result = (arg0 / 1000);
    if (result == 0)
    {
        offset = 6;
    }
    else
    {
        buffer[r6++] = result + CHAR_0;
    }


    result = (arg0 % 1000) / 100;

    if (result == 0 && offset != 0)
    {
        offset += 6;
    }
    else
    {
        buffer[r6++] = result + CHAR_0;
    }

    buffer[r6++] = (((arg0 % 1000) % 100) / 10) + CHAR_0;
    buffer[r6++] = CHAR_COMMA;
    buffer[r6++] = (((arg0 % 1000) % 100) % 10) + CHAR_0;

    buffer[r6++] = EOS;
    buffer[2] = offset;
    Menu_PrintText(buffer, left, top);
}
#endif

#ifdef UNITS_IMPERIAL
#define CHAR_PRIME (0xB4)
#define CHAR_DOUBLE_PRIME (0xB2)
static void sub_8091458(u16 height, u8 left, u8 top)
{
    u8 buffer[16];
    u32 inches, feet;
    u8 i = 0;

    inches = (height * 10000) / 254;
    if (inches % 10 >= 5)
        inches += 10;
    feet = inches / 120;
    inches = (inches - (feet * 120)) / 10;

    buffer[i++] = EXT_CTRL_CODE_BEGIN;
    buffer[i++] = 0x13;
    if (feet / 10 == 0)
    {
        buffer[i++] = 18;
        buffer[i++] = feet + CHAR_0;
    }
    else
    {
        buffer[i++] = 12;
        buffer[i++] = feet / 10 + CHAR_0;
        buffer[i++] = (feet % 10) + CHAR_0;
    }
    buffer[i++] = CHAR_PRIME;
    buffer[i++] = (inches / 10) + CHAR_0;
    buffer[i++] = (inches % 10) + CHAR_0;
    buffer[i++] = CHAR_DOUBLE_PRIME;
    buffer[i++] = EOS;
    Menu_PrintText(buffer, left, top);
}
#else
static void sub_8091458(u16 height, u8 left, u8 top)
{
    UnusedPrintDecimalNum(height, left, top);
}
#endif

#ifdef UNITS_IMPERIAL
static void sub_8091564(u16 weight, u8 left, u8 top)
{
    u8 buffer[16];
    u32 lbs;
    u8 i = 0;
    bool8 output;

    lbs = (weight * 100000) / 4536;
    if (lbs % 10 >= 5)
        lbs += 10;
    output = FALSE;

    buffer[i] = (lbs / 100000) + CHAR_0;
    if (buffer[i] == CHAR_0 && output == FALSE)
    {
        buffer[i++] = CHAR_SPACE;
        buffer[i++] = CHAR_SPACE;
    }
    else
    {
        output = TRUE;
        i++;
    }

    lbs = (lbs % 100000);
    buffer[i] = (lbs / 10000) + CHAR_0;
    if (buffer[i] == CHAR_0 && output == FALSE)
    {
        buffer[i++] = CHAR_SPACE;
        buffer[i++] = CHAR_SPACE;
    }
    else
    {
        output = TRUE;
        i++;
    }

    lbs = (lbs % 10000);
    buffer[i] = (lbs / 1000) + CHAR_0;
    if (buffer[i] == CHAR_0 && output == FALSE)
    {
        buffer[i++] = CHAR_SPACE;
        buffer[i++] = CHAR_SPACE;
    }
    else
    {
        output = TRUE;
        i++;
    }
    lbs = (lbs % 1000);
    buffer[i++] = (lbs / 100) + CHAR_0;
    lbs = (lbs % 100);
    buffer[i++] = CHAR_PERIOD;
    buffer[i++] = (lbs / 10) + CHAR_0;
    buffer[i++] = CHAR_SPACE;
    buffer[i++] = CHAR_l;
    buffer[i++] = CHAR_b;
    buffer[i++] = CHAR_s;
    buffer[i++] = CHAR_PERIOD;
    buffer[i++] = EOS;
    Menu_PrintText(buffer, left, top);
}
#else
static void sub_8091564(u16 arg0, u8 left, u8 top)
{
    UnusedPrintDecimalNum(arg0, left, top);
}
#endif

void PrintFootprint(u16 num, u16 b, u16 c)
{
    u8 arr[0x80];
    u16 i, j, r7;
    const u8 *r12;
    u8 r3, r1;

    r12 = (const u8*)sMonFootprintTable[NationalPokedexNumToSpecies(num)];
    r7 = 0;
    for (i = 0; i < 32; i++)
    {
        r3 = r12[i];
        for (j = 0; j < 4; j++)
        {
            r1 = 0;
            if (r3 & (1 << (j * 2)))
                r1 |= 0x01;
            if (r3 & (2 << (j * 2)))
                r1 |= 0x10;

            arr[r7] = r1;
            r7++;
        }
    }
    CpuCopy16((void*)arr, (void *)(BG_VRAM + b * 0x4000 + c * 0x20), 0x80);
}

static void sub_80C0DC0(u16 a, u16 b)
{
    *(u16 *)(BG_VRAM + a * 0x800 + 0x232) = 0xF000 + b + 0;
    *(u16 *)(BG_VRAM + a * 0x800 + 0x234) = 0xF000 + b + 1;
    *(u16 *)(BG_VRAM + a * 0x800+ 0x272) = 0xF000 + b + 2;
    *(u16 *)(BG_VRAM + a * 0x800 + 0x274) = 0xF000 + b + 3;
}

static u16 GetNextPosition(u8 direction, u16 position, u16 min, u16 max)
{
    switch (direction)
    {
    case 1:
        if (position > min)
            position--;
        break;
    case 0:
        if (position < max)
            position++;
        break;
    case 3:
        if (position > min)
            position--;
        else
            position = max;
        break;
    case 2:
        if (position < max)
            position++;
        else
            position = min;
        break;
    }
    return position;
}

static void _SpriteCB_Dummy(struct Sprite *sprite)
{
}

static void SetSpriteTemplateForPokemonPic(u16 paletteTag, u8 imagesSet)
{
    gCreatingSpriteTemplate = sMonOrTrainerPicSpriteTemplate;
    gCreatingSpriteTemplate.paletteTag = paletteTag;
    gCreatingSpriteTemplate.images = sSpriteFrameImages_MonOrTrainerPic[imagesSet];
    gCreatingSpriteTemplate.anims = gSpriteAnimTable_81E7C64;
}

static void SetSpriteTemplateForSizeComparisonTrainerPic(u16 paletteTag, u8 imagesSet)
{
    gCreatingSpriteTemplate = sMonOrTrainerPicSpriteTemplate;
    gCreatingSpriteTemplate.paletteTag = paletteTag;
    gCreatingSpriteTemplate.images = sSpriteFrameImages_MonOrTrainerPic[imagesSet];
    gCreatingSpriteTemplate.anims = gUnknown_081EC2A4[0];
}

u16 CreateMonSpriteFromNationalDexNumber(u16 num, s16 x, s16 y, u16 paletteNum)
{
    u8 spriteId;

    num = NationalPokedexNumToSpecies(num);
    switch (num)
    {
    default:
        DecompressPicFromTable_2(
          &gMonFrontPicTable[num],
          gMonFrontPicCoords[num].coords,
          gMonFrontPicCoords[num].y_offset,
          (void *)EWRAM,
          sMonOrTrainerPicDecompressDestPtrs[paletteNum],
          num);
        break;
    case SPECIES_SPINDA:
        LoadSpecialPokePic(
          &gMonFrontPicTable[num],
          gMonFrontPicCoords[num].coords,
          gMonFrontPicCoords[num].y_offset,
          (void *)EWRAM,
          sMonOrTrainerPicDecompressDestPtrs[paletteNum],
          num,
          gSaveBlock2.pokedex.spindaPersonality,
          1);
        break;
    case SPECIES_UNOWN:
        LoadSpecialPokePic(
          &gMonFrontPicTable[num],
          gMonFrontPicCoords[num].coords,
          gMonFrontPicCoords[num].y_offset,
          (void *)EWRAM,
          sMonOrTrainerPicDecompressDestPtrs[paletteNum],
          num,
          gSaveBlock2.pokedex.unownPersonality,
          1);
        break;
    }
    LoadCompressedPalette(gMonPaletteTable[num].data, 0x100 + paletteNum * 16, 32);
    SetSpriteTemplateForPokemonPic(paletteNum, paletteNum);
    spriteId = CreateSprite(&gCreatingSpriteTemplate, x, y, 0);
    gSprites[spriteId].oam.paletteNum = paletteNum;
    return spriteId;
}

u16 CreateSizeScreenTrainerPic(u16 gender, s16 x, s16 y, u16 paletteNum)
{
    u8 spriteId;

    DecompressPicFromTable_2(
      &gTrainerFrontPicTable[gender],
      gTrainerFrontPicCoords[gender].coords,
      gTrainerFrontPicCoords[gender].y_offset,
      (void *)EWRAM,
      sMonOrTrainerPicDecompressDestPtrs[0],
      gender);
    SetSpriteTemplateForSizeComparisonTrainerPic(gender, 0);
    spriteId = CreateSprite(&gCreatingSpriteTemplate, x, y, 0);
    gSprites[spriteId].oam.paletteNum = paletteNum;
    return spriteId;
}

int DoPokedexSearch(u8 dexMode, u8 order, u8 abcGroup, u8 bodyColor, u8 type1, u8 type2)
{
    u16 species;
    u16 i;
    u16 resultsCount;
    u8 types[2];

    SortPokedex(dexMode, order);

    for (i = 0, resultsCount = 0; i < NATIONAL_DEX_COUNT; i++)
    {
        if (gPokedexView->pokedexList[i].seen)
        {
            gPokedexView->pokedexList[resultsCount] = gPokedexView->pokedexList[i];
            resultsCount++;
        }
    }
    gPokedexView->pokemonListCount = resultsCount;

    // Search by name
    if (abcGroup != 0xFF)
    {
        for (i = 0, resultsCount = 0; i < gPokedexView->pokemonListCount; i++)
        {
            u8 r3;

            species = NationalPokedexNumToSpecies(gPokedexView->pokedexList[i].dexNum);
            r3 = gSpeciesNames[species][0];
            if ((r3 >= sLetterSearchRanges[abcGroup][0] && r3 < sLetterSearchRanges[abcGroup][0] + sLetterSearchRanges[abcGroup][1])
             || (r3 >= sLetterSearchRanges[abcGroup][2] && r3 < sLetterSearchRanges[abcGroup][2] + sLetterSearchRanges[abcGroup][3]))
            {
                gPokedexView->pokedexList[resultsCount] = gPokedexView->pokedexList[i];
                resultsCount++;
            }
        }
        gPokedexView->pokemonListCount = resultsCount;
    }

    // Search by body color
    if (bodyColor != 0xFF)
    {
        for (i = 0, resultsCount = 0; i < gPokedexView->pokemonListCount; i++)
        {
            species = NationalPokedexNumToSpecies(gPokedexView->pokedexList[i].dexNum);

            if (bodyColor == gBaseStats[species].bodyColor)
            {
                gPokedexView->pokedexList[resultsCount] = gPokedexView->pokedexList[i];
                resultsCount++;
            }
        }
        gPokedexView->pokemonListCount = resultsCount;
    }

    // Search by type
    if (type1 != 0xFF || type2 != 0xFF)
    {
        if (type1 == 0xFF)
        {
            type1 = type2;
            type2 = 0xFF;
        }

        if (type2 == 0xFF)
        {
            for (i = 0, resultsCount = 0; i < gPokedexView->pokemonListCount; i++)
            {
                if (gPokedexView->pokedexList[i].owned)
                {
                    species = NationalPokedexNumToSpecies(gPokedexView->pokedexList[i].dexNum);

                    types[0] = gBaseStats[species].type1;
                    types[1] = gBaseStats[species].type2;
                    if (types[0] == type1 || types[1] == type1)
                    {
                        gPokedexView->pokedexList[resultsCount] = gPokedexView->pokedexList[i];
                        resultsCount++;
                    }
                }
            }
        }
        else
        {
            for (i = 0, resultsCount = 0; i < gPokedexView->pokemonListCount; i++)
            {
                if (gPokedexView->pokedexList[i].owned)
                {
                    species = NationalPokedexNumToSpecies(gPokedexView->pokedexList[i].dexNum);

                    types[0] = gBaseStats[species].type1;
                    types[1] = gBaseStats[species].type2;
                    if ((types[0] == type1 && types[1] == type2) || (types[0] == type2 && types[1] == type1))
                    {
                        gPokedexView->pokedexList[resultsCount] = gPokedexView->pokedexList[i];
                        resultsCount++;
                    }
                }
            }
        }
        gPokedexView->pokemonListCount = resultsCount;
    }

    if (gPokedexView->pokemonListCount != 0)
    {
        for (i = gPokedexView->pokemonListCount; i < NATIONAL_DEX_COUNT; i++)
        {
            gPokedexView->pokedexList[i].dexNum = 0xFFFF;
            gPokedexView->pokedexList[i].seen = FALSE;
            gPokedexView->pokedexList[i].owned = FALSE;

        }
    }

    return resultsCount;
}

#if ENGLISH
#define SUB_8091E20_WIDTH (208)
#elif GERMAN
#define SUB_8091E20_WIDTH (216)
#endif

void EraseAndPrintSearchTextBox(const u8 *str)
{
    sub_8072AB0(str, 9, 120, SUB_8091E20_WIDTH, 32, 1);
}

u8 LoadSearchMenu(void)
{
    return CreateTask(Task_LoadSearchMenu, 0);
}

static void Task_LoadSearchMenu(u8 taskId)
{
    u16 i;

    switch (gMain.state)
    {
    default:
    case 0:
        if (!gPaletteFade.active)
        {
            gPokedexView->currentPage = 2;
            ResetOtherVideoRegisters(0);
            LZ77UnCompVram(gPokedexMenuSearch_Gfx, (void *)VRAM);
            LZ77UnCompVram(gPokedexMenuSearch_Tilemap, (void *)(VRAM + 0x7800));
            LoadPalette(gPokedexMenuSearch_Pal + 1, 1, 0x7E);
            if (!IsNationalPokedexEnabled())
            {
                for (i = 0; i < 17; i++)
                {
                    ((u16 *)(VRAM + 0x7A80))[i] = ((u16 *)(VRAM + 0x7B00))[i];
                    ((u16 *)(VRAM + 0x7AC0))[i] = ((u16 *)(VRAM + 0x7B40))[i];
                    ((u16 *)(VRAM + 0x7B00))[i] = 1;
                    ((u16 *)(VRAM + 0x7B40))[i] = 1;
                }
            }
            gMain.state = 1;
        }
        break;
    case 1:
        Text_LoadWindowTemplate(&gWindowTemplate_81E7064);
        InitMenuWindow(&gWindowTemplate_81E7064);
        LoadCompressedObjectPic(&sInterfaceSpriteSheet[0]);
        LoadSpritePalettes(sInterfaceSpritePalette);
        CreateSearchParameterScrollArrows(taskId);
        for (i = 0; i < 16; i++)
            gTasks[taskId].data[i] = 0;
        SetDefaultSearchModeAndOrder(taskId);
        HighlightSelectedSearchTopBarItem(0);
        PrintSelectedSearchParameters(taskId);
        gMain.state++;
        break;
    case 2:
        BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB(0, 0, 0));
        gMain.state++;
        break;
    case 3:
        REG_BG3CNT = 0x0F03;
        REG_DISPCNT = 0x1C40;
        gMain.state++;
        break;
    case 4:
        if (!gPaletteFade.active)
        {
            gTasks[taskId].func = Task_SwitchToSearchMenuTopBar;
            gMain.state = 0;
        }
        break;
    }
}

static void Task_SwitchToSearchMenuTopBar(u8 taskId)
{
    HighlightSelectedSearchTopBarItem(gTasks[taskId].data[0]);
    PrintSelectedSearchParameters(taskId);
    gTasks[taskId].func = Task_HandleSearchTopBarInput;
}

static void Task_HandleSearchTopBarInput(u8 taskId)
{
    if (gMain.newKeys & B_BUTTON)
    {
        PlaySE(SE_PC_OFF);
        gTasks[taskId].func = Task_ExitSearch;
        return;
    }
    if (gMain.newKeys & A_BUTTON)
    {
        switch (gTasks[taskId].data[0])
        {
        case 0:
            PlaySE(SE_PIN);
            gTasks[taskId].data[1] = 0;
            gTasks[taskId].func = Task_SwitchToSearchMenu;
            break;
        case 1:
            PlaySE(SE_PIN);
            gTasks[taskId].data[1] = 4;
            gTasks[taskId].func = Task_SwitchToSearchMenu;
            break;
        case 2:
            PlaySE(SE_PC_OFF);
            gTasks[taskId].func = Task_ExitSearch;
            break;
        }
        return;
    }
    if ((gMain.newKeys & DPAD_LEFT) && gTasks[taskId].data[0] > 0)
    {
        PlaySE(SE_DEX_PAGE);
        gTasks[taskId].data[0]--;
        HighlightSelectedSearchTopBarItem(gTasks[taskId].data[0]);
    }
    if ((gMain.newKeys & DPAD_RIGHT) && gTasks[taskId].data[0] < 2)
    {
        PlaySE(SE_DEX_PAGE);
        gTasks[taskId].data[0]++;
        HighlightSelectedSearchTopBarItem(gTasks[taskId].data[0]);
    }
}

static void Task_SwitchToSearchMenu(u8 taskId)
{
    HighlightSelectedSearchMenuItem(gTasks[taskId].data[0], gTasks[taskId].data[1]);
    PrintSelectedSearchParameters(taskId);
    gTasks[taskId].func = Task_HandleSearchMenuInput;
}

static void Task_HandleSearchMenuInput(u8 taskId)
{
    const u8 (*r6)[4];

    if (gTasks[taskId].data[0] != 0)
    {
        if (!IsNationalPokedexEnabled())
            r6 = sSearchMovementMap_ShiftHoennDex;
        else
            r6 = sSearchMovementMap_ShiftNatDex;
    }
    else
    {
        if (!IsNationalPokedexEnabled())
            r6 = sSearchMovementMap_SearchHoennDex;
        else
            r6 = sSearchMovementMap_SearchNatDex;
    }

    if (gMain.newKeys & B_BUTTON)
    {
        PlaySE(SE_BALL);
        SetDefaultSearchModeAndOrder(taskId);
        gTasks[taskId].func = Task_SwitchToSearchMenuTopBar;
        return;
    }
    if (gMain.newKeys & A_BUTTON)
    {
        if (gTasks[taskId].data[1] == 6)
        {
            if (gTasks[taskId].data[0] != 0)
            {
                sPokeBallRotation = 0x40;
                gPokedexView->pokeBallRotationBackup = 0x40;
                sLastSelectedPokemon = 0;
                gPokedexView->selectedPokemonBackup = 0;
                gSaveBlock2.pokedex.mode = GetSearchModeSelection(taskId, 5);
                if (!IsNationalPokedexEnabled())
                    gSaveBlock2.pokedex.mode = 0;
                gPokedexView->dexModeBackup = gSaveBlock2.pokedex.mode;
                gSaveBlock2.pokedex.order = GetSearchModeSelection(taskId, 4);
                gPokedexView->dexOrderBackup = gSaveBlock2.pokedex.order;
                PlaySE(SE_PC_OFF);
                gTasks[taskId].func = Task_ExitSearch;
            }
            else
            {
                EraseAndPrintSearchTextBox(gDexText_Searching);
                gTasks[taskId].func = Task_StartPokedexSearch;
                PlaySE(SE_DEX_SEARCH);
            }
        }
        else
        {
            PlaySE(SE_PIN);
            gTasks[taskId].func = Task_SelectSearchMenuItem;
        }
        return;
    }

    if ((gMain.newKeys & DPAD_LEFT) && r6[gTasks[taskId].data[1]][0] != 0xFF)
    {
        PlaySE(SE_SELECT);
        gTasks[taskId].data[1] = r6[gTasks[taskId].data[1]][0];
        HighlightSelectedSearchMenuItem(gTasks[taskId].data[0], gTasks[taskId].data[1]);
    }
    if ((gMain.newKeys & DPAD_RIGHT) && r6[gTasks[taskId].data[1]][1] != 0xFF)
    {
        PlaySE(SE_SELECT);
        gTasks[taskId].data[1] = r6[gTasks[taskId].data[1]][1];
        HighlightSelectedSearchMenuItem(gTasks[taskId].data[0], gTasks[taskId].data[1]);
    }
    if ((gMain.newKeys & DPAD_UP) && r6[gTasks[taskId].data[1]][2] != 0xFF)
    {
        PlaySE(SE_SELECT);
        gTasks[taskId].data[1] = r6[gTasks[taskId].data[1]][2];
        HighlightSelectedSearchMenuItem(gTasks[taskId].data[0], gTasks[taskId].data[1]);
    }
    if ((gMain.newKeys & DPAD_DOWN) && r6[gTasks[taskId].data[1]][3] != 0xFF)
    {
        PlaySE(SE_SELECT);
        gTasks[taskId].data[1] = r6[gTasks[taskId].data[1]][3];
        HighlightSelectedSearchMenuItem(gTasks[taskId].data[0], gTasks[taskId].data[1]);
    }
}

static void Task_StartPokedexSearch(u8 taskId)
{
    u8 r10 = GetSearchModeSelection(taskId, 5);
    u8 r9 = GetSearchModeSelection(taskId, 4);
    u8 r8 = GetSearchModeSelection(taskId, 0);
    u8 r6 = GetSearchModeSelection(taskId, 1);
    u8 r4 = GetSearchModeSelection(taskId, 2);
    u8 r0 = GetSearchModeSelection(taskId, 3);

    DoPokedexSearch(r10, r9, r8, r6, r4, r0);
    gTasks[taskId].func = Task_WaitAndCompleteSearch;
}

static void Task_WaitAndCompleteSearch(u8 taskId)
{
    if (!IsSEPlaying())
    {
        if (gPokedexView->pokemonListCount != 0)
        {
            PlaySE(SE_SUCCESS);
            EraseAndPrintSearchTextBox(gDexText_SearchComplete);
        }
        else
        {
            PlaySE(SE_FAILURE);
            EraseAndPrintSearchTextBox(gDexText_NoMatching);
        }
        gTasks[taskId].func = Task_SearchCompleteWaitForInput;
    }
}

static void Task_SearchCompleteWaitForInput(u8 taskId)
{
    if (gMain.newKeys & A_BUTTON)
    {
        if (gPokedexView->pokemonListCount != 0)
        {
            gPokedexView->screenSwitchState = 1;
            gPokedexView->dexMode = GetSearchModeSelection(taskId, 5);
            gPokedexView->dexOrder = GetSearchModeSelection(taskId, 4);
            gTasks[taskId].func = Task_ExitSearch;
            PlaySE(SE_PC_OFF);
        }
        else
        {
            gTasks[taskId].func = Task_SwitchToSearchMenu;
            PlaySE(SE_BALL);
        }
    }
}

static void PrintSelectorArrow(u16 a, int unused)
{
    MenuCursor_SetPos814AD7C(0x90, (a * 2 + 1) * 8);
}

static void Task_SelectSearchMenuItem(u8 taskId)
{
    u8 r0;
    u16 *p1;
    u16 *p2;

    DrawOrEraseSearchParameterBox(0);
    r0 = gTasks[taskId].data[1];
    p1 = (u16 *)&gTasks[taskId].data[sSearchOptions[r0].taskDataCursorPos];
    p2 = (u16 *)&gTasks[taskId].data[sSearchOptions[r0].taskDataScrollOffset];
    gTasks[taskId].data[14] = *p1;
    gTasks[taskId].data[15] = *p2;
    PrintSearchParameterText(taskId);
    CreateBlendedOutlineCursor(16, 0xFFFF, 12, 0x2D9F, 11);
    PrintSelectorArrow(*p1, 1);
    gTasks[taskId].func = Task_HandleSearchParameterInput;
}

static void Task_HandleSearchParameterInput(u8 taskId)
{
    u8 r1;
    const struct SearchOptionText *r8;
    u16 *p1;
    u16 *p2;
    u16 r2;
    bool8 r3;

    r1 = gTasks[taskId].data[1];
    r8 = sSearchOptions[r1].texts;
    p1 = (u16 *)&gTasks[taskId].data[sSearchOptions[r1].taskDataCursorPos];
    p2 = (u16 *)&gTasks[taskId].data[sSearchOptions[r1].taskDataScrollOffset];
    r2 = sSearchOptions[r1].numOptions - 1;
    if (gMain.newKeys & A_BUTTON)
    {
        sub_814ADC8();
        PlaySE(SE_PIN);
        Menu_EraseWindowRect(18, 1, 28, 12);
        DrawOrEraseSearchParameterBox(1);
        gTasks[taskId].func = Task_SwitchToSearchMenu;
        return;
    }
    if (gMain.newKeys & B_BUTTON)
    {
        sub_814ADC8();
        PlaySE(SE_BALL);
        Menu_EraseWindowRect(18, 1, 28, 12);
        DrawOrEraseSearchParameterBox(1);
        *p1 = gTasks[taskId].data[14];
        *p2 = gTasks[taskId].data[15];
        gTasks[taskId].func = Task_SwitchToSearchMenu;
        return;
    }
    r3 = FALSE;
    if (gMain.newAndRepeatedKeys & DPAD_UP)
    {
        if (*p1 != 0)
        {
            PrintSelectorArrow(*p1, 0);
            (*p1)--;
            PrintSelectorArrow(*p1, 1);
            r3 = TRUE;
        }
        else if (*p2 != 0)
        {
            (*p2)--;
            PrintSearchParameterText(taskId);
            PrintSelectorArrow(*p1, 1);
            r3 = TRUE;
        }
        if (r3)
        {
            PlaySE(SE_SELECT);
            EraseAndPrintSearchTextBox(r8[*p1 + *p2].description);
        }
        return;
    }
    if (gMain.newAndRepeatedKeys & DPAD_DOWN)
    {
        if (*p1 < 5 && *p1 < r2)
        {
            PrintSelectorArrow(*p1, 0);
            (*p1)++;
            PrintSelectorArrow(*p1, 1);
            r3 = TRUE;
        }
        else if (r2 > 5 && *p2 < r2 - 5)
        {
            (*p2)++;
            PrintSearchParameterText(taskId);
            PrintSelectorArrow(5, 1);
            r3 = TRUE;
        }
        if (r3)
        {
            PlaySE(SE_SELECT);
            EraseAndPrintSearchTextBox(r8[*p1 + *p2].description);
        }
        return;
    }
}

static void Task_ExitSearch(u8 taskId)
{
    BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB(0, 0, 0));
    gTasks[taskId].func = Task_ExitSearchWaitForFade;
}

static void Task_ExitSearchWaitForFade(u8 taskId)
{
    if (!gPaletteFade.active)
        DestroyTask(taskId);
}

#define VRAM_ADDR(a, b, c) *(u16 *)(BG_VRAM + (15 * 0x800) + (c) * 64 + ((b) + (a))*2)
void SetSearchRectHighlight(u8 a, u8 b, u8 c, u8 d)
{
    u16 i, j;

    for (i = 0; i < d; i++)
    {
        j = VRAM_ADDR(i, b, c);
        j &= 0xFFF;
        j |= a << 12;

        VRAM_ADDR(i, b, c) = j;

        j = VRAM_ADDR(i, b, c + 1);
        j &= 0xFFF;
        j |= a << 12;
        VRAM_ADDR(i, b, c + 1) = j;
    }
}

static void DrawSearchMenuItemBgHighlight(u8 searchBg, u8 unselected, u8 disabled)
{
    u8 r5 = (unselected & 1) | ((disabled & 1) << 1);

    switch (searchBg)
    {
    case 0:
    case 1:
    case 2:
        SetSearchRectHighlight(r5,
            sSearchMenuTopBarItems[searchBg].highlightX,
            sSearchMenuTopBarItems[searchBg].highlightY,
            sSearchMenuTopBarItems[searchBg].highlightWidth);
        break;
    case 3:
    case 4:
    case 7:
    case 8:
        SetSearchRectHighlight(r5,
            sSearchMenuItems[searchBg - 3].titleBgX,
            sSearchMenuItems[searchBg - 3].titleBgY,
            sSearchMenuItems[searchBg - 3].titleBgWidth);
        // fall through
    case 5:
    case 6:
        SetSearchRectHighlight(r5,
            sSearchMenuItems[searchBg - 3].selectionBgX,
            sSearchMenuItems[searchBg - 3].selectionBgY,
            sSearchMenuItems[searchBg - 3].selectionBgWidth);
        break;
    case 10:
        SetSearchRectHighlight(
            r5, sSearchMenuItems[2].titleBgX, sSearchMenuItems[2].titleBgY, sSearchMenuItems[2].titleBgWidth);
        break;
    case 9:
        if (!IsNationalPokedexEnabled())
            SetSearchRectHighlight(r5,
                sSearchMenuItems[searchBg - 3].titleBgX,
                sSearchMenuItems[searchBg - 3].titleBgY - 2,
                sSearchMenuItems[searchBg - 3].titleBgWidth);
        else
            SetSearchRectHighlight(r5,
                sSearchMenuItems[searchBg - 3].titleBgX,
                sSearchMenuItems[searchBg - 3].titleBgY,
                sSearchMenuItems[searchBg - 3].titleBgWidth);
        break;
    }
}

static void SetInitialSearchMenuBgHighlights(u8 topBarItem)
{
    switch (topBarItem)
    {
    case 0:
        DrawSearchMenuItemBgHighlight(0, 0, 0);
        DrawSearchMenuItemBgHighlight(1, 1, 0);
        DrawSearchMenuItemBgHighlight(2, 1, 0);
        DrawSearchMenuItemBgHighlight(3, 1, 0);
        DrawSearchMenuItemBgHighlight(4, 1, 0);
        DrawSearchMenuItemBgHighlight(10, 1, 0);
        DrawSearchMenuItemBgHighlight(5, 1, 0);
        DrawSearchMenuItemBgHighlight(6, 1, 0);
        DrawSearchMenuItemBgHighlight(7, 1, 0);
        DrawSearchMenuItemBgHighlight(8, 1, 0);
        DrawSearchMenuItemBgHighlight(9, 1, 0);
        break;
    case 1:
        DrawSearchMenuItemBgHighlight(0, 1, 0);
        DrawSearchMenuItemBgHighlight(1, 0, 0);
        DrawSearchMenuItemBgHighlight(2, 1, 0);
        DrawSearchMenuItemBgHighlight(3, 1, 1);
        DrawSearchMenuItemBgHighlight(4, 1, 1);
        DrawSearchMenuItemBgHighlight(10, 1, 1);
        DrawSearchMenuItemBgHighlight(5, 1, 1);
        DrawSearchMenuItemBgHighlight(6, 1, 1);
        DrawSearchMenuItemBgHighlight(7, 1, 0);
        DrawSearchMenuItemBgHighlight(8, 1, 0);
        DrawSearchMenuItemBgHighlight(9, 1, 0);
        break;
    case 2:
        DrawSearchMenuItemBgHighlight(0, 1, 0);
        DrawSearchMenuItemBgHighlight(1, 1, 0);
        DrawSearchMenuItemBgHighlight(2, 0, 0);
        DrawSearchMenuItemBgHighlight(3, 1, 1);
        DrawSearchMenuItemBgHighlight(4, 1, 1);
        DrawSearchMenuItemBgHighlight(10, 1, 1);
        DrawSearchMenuItemBgHighlight(5, 1, 1);
        DrawSearchMenuItemBgHighlight(6, 1, 1);
        DrawSearchMenuItemBgHighlight(7, 1, 1);
        DrawSearchMenuItemBgHighlight(8, 1, 1);
        DrawSearchMenuItemBgHighlight(9, 1, 1);
        break;
    }
}

static void HighlightSelectedSearchTopBarItem(u8 topBarItem)
{
    SetInitialSearchMenuBgHighlights(topBarItem);
    EraseAndPrintSearchTextBox(sSearchMenuTopBarItems[topBarItem].text);
}

static void HighlightSelectedSearchMenuItem(u8 topBarItem, u8 menuItem)
{
    SetInitialSearchMenuBgHighlights(topBarItem);
    switch (menuItem)
    {
    case 0:
        DrawSearchMenuItemBgHighlight(3, 0, 0);
        break;
    case 1:
        DrawSearchMenuItemBgHighlight(4, 0, 0);
        break;
    case 2:
        DrawSearchMenuItemBgHighlight(10, 0, 0);
        DrawSearchMenuItemBgHighlight(5, 0, 0);
        break;
    case 3:
        DrawSearchMenuItemBgHighlight(10, 0, 0);
        DrawSearchMenuItemBgHighlight(6, 0, 0);
        break;
    case 4:
        DrawSearchMenuItemBgHighlight(7, 0, 0);
        break;
    case 5:
        DrawSearchMenuItemBgHighlight(8, 0, 0);
        break;
    case 6:
        DrawSearchMenuItemBgHighlight(9, 0, 0);
        break;
    }
    EraseAndPrintSearchTextBox(sSearchMenuItems[menuItem].text);
}

static void PrintSelectedSearchParameters(u8 taskId)
{
    u16 var;

    var = gTasks[taskId].data[6] + gTasks[taskId].data[7];
    StringCopy(gStringVar1, sDexSearchNameOptions[var].title);
    Menu_PrintTextPixelCoords(gUnknown_083B5AB2, 45, 16, 1);

    var = gTasks[taskId].data[8] + gTasks[taskId].data[9];
    StringCopy(gStringVar1, sDexSearchColorOptions[var].title);
    Menu_PrintTextPixelCoords(gUnknown_083B5AB2, 45, 32, 1);

    var = gTasks[taskId].data[10] + gTasks[taskId].data[11];
    StringCopy(gStringVar1, sDexSearchTypeOptions[var].title);
    Menu_PrintTextPixelCoords(gUnknown_083B5AAC, 45, 48, 1);

    var = gTasks[taskId].data[12] + gTasks[taskId].data[13];
    StringCopy(gStringVar1, sDexSearchTypeOptions[var].title);
    Menu_PrintTextPixelCoords(gUnknown_083B5AAC, 93, 48, 1);

    var = gTasks[taskId].data[4] + gTasks[taskId].data[5];
    StringCopy(gStringVar1, sDexOrderOptions[var].title);
    Menu_PrintTextPixelCoords(gUnknown_083B5AB2, 45, 64, 1);

    if (IsNationalPokedexEnabled())
    {
        var = gTasks[taskId].data[2] + gTasks[taskId].data[3];
        StringCopy(gStringVar1, sDexModeOptions[var].title);
        Menu_PrintTextPixelCoords(gUnknown_083B5AB2, 45, 80, 1);
    }
}

static void DrawOrEraseSearchParameterBox(u8 a)
{
    u16 i;
    u16 j;

    if (a == 0)
    {
        *((u16 *)(VRAM + 0x7800 + 0x22)) = 0xC0B;
        for (i = 0x12; i < 0x1D; i++)
            *((u16 *)(VRAM + 0x7800 + i * 2)) = 0x80D;
        *((u16 *)(VRAM + 0x7800 + 0x3A)) = 0x80B;
        for (j = 1; j < 13; j++)
        {
            *((u16 *)(VRAM + 0x7800 + 0x22 + j * 64)) = 0x40A;
            for (i = 0x12; i < 0x1D; i++)
                *((u16 *)(VRAM + 0x7800 + j * 64 + i * 2)) = 2;
            *((u16 *)(VRAM + 0x7800 + 0x3A + j * 64)) = 0xA;
        }
        *((u16 *)(VRAM + 0x7800 + 0x362)) = 0x40B;
        for (i = 0x12; i < 0x1D; i++)
            *((u16 *)(VRAM + 0x7800 + 0x340 + i * 2)) = 0xD;
        *((u16 *)(VRAM + 0x7800 + 0x37A)) = 0xB;
    }
    else
    {
        for (j = 0; j < 14; j++)
        {
            for (i = 0x11; i < 0x1E; i++)
            {
                *((u16 *)(VRAM + 0x7800 + j * 64 + i * 2)) = 0x4F;
            }
        }
    }
}

// Prints the currently viewable search parameter titles in the right-hand text box
// and the currently selected search parameter description in the bottom text box
static void PrintSearchParameterText(u8 taskId)
{
    const struct SearchOptionText *r6 = sSearchOptions[gTasks[taskId].data[1]].texts;
    const u16 *r8 = &gTasks[taskId].data[sSearchOptions[gTasks[taskId].data[1]].taskDataCursorPos];
    const u16 *r7 = &gTasks[taskId].data[sSearchOptions[gTasks[taskId].data[1]].taskDataScrollOffset];
    u16 i;
    u16 j;

    Menu_EraseWindowRect(18, 1, 28, 12);
    for (i = 0, j = *r7; i < 6; i++, j++)
    {
        if (r6[j].title == NULL)
            break;
        Menu_PrintText(r6[j].title, 18, i * 2 + 1);
    }
    EraseAndPrintSearchTextBox(r6[*r8 + *r7].description);
}

static u8 GetSearchModeSelection(u8 taskId, u8 b)
{
    const u16 *ptr1 = &gTasks[taskId].data[sSearchOptions[b].taskDataCursorPos];
    const u16 *ptr2 = &gTasks[taskId].data[sSearchOptions[b].taskDataScrollOffset];
    u16 r2 = *ptr1 + *ptr2;

    switch (b)
    {
    default:
        return 0;
    case 5:
        return sPokedexModes[r2];
    case 4:
        return sOrderOptions[r2];
    case 0:
        if (r2 == 0)
            return 0xFF;
        else
            return r2;
    case 1:
        if (r2 == 0)
            return 0xFF;
        else
            return r2 - 1;
    case 2:
    case 3:
        return sDexSearchTypeIds[r2];
    }
}

static void SetDefaultSearchModeAndOrder(u8 taskId)
{
    u16 r3;

    switch (gPokedexView->dexModeBackup)
    {
    default:
    case 0:
        r3 = 0;
        break;
    case 1:
        r3 = 1;
        break;
    }
    gTasks[taskId].data[2] = r3;

    switch (gPokedexView->dexOrderBackup)
    {
    default:
    case 0:
        r3 = 0;
        break;
    case 1:
        r3 = 1;
        break;
    case 2:
        r3 = 2;
        break;
    case 3:
        r3 = 3;
        break;
    case 4:
        r3 = 4;
        break;
    case 5:
        r3 = 5;
        break;
    }
    gTasks[taskId].data[4] = r3;
}

static bool8 SearchParamCantScrollUp(u8 taskId)
{
    u8 val1 = gTasks[taskId].data[1];
    const u16 *ptr = (const u16 *)&gTasks[taskId].data[sSearchOptions[val1].taskDataScrollOffset];
    u16 val2 = sSearchOptions[val1].numOptions - 1;

    if (val2 > 5 && *ptr != 0)
        return FALSE;
    else
        return TRUE;
}

static bool8 SearchParamCantScrollDown(u8 taskId)
{
    u8 val1 = gTasks[taskId].data[1];
    const u16 *ptr = (const u16 *)&gTasks[taskId].data[sSearchOptions[val1].taskDataScrollOffset];
    u16 val2 = sSearchOptions[val1].numOptions - 1;

    if (val2 > 5 && *ptr < val2 - 5)
        return FALSE;
    else
        return TRUE;
}

static void SpriteCB_SearchParameterScrollArrow(struct Sprite *sprite)
{
    if (gTasks[sprite->data[0]].func == Task_HandleSearchParameterInput)
    {
        u8 val;

        if (sprite->data[1] != 0)
        {
            if (SearchParamCantScrollDown(sprite->data[0]))
                sprite->invisible = TRUE;
            else
                sprite->invisible = FALSE;
        }
        else
        {
            if (SearchParamCantScrollUp(sprite->data[0]))
                sprite->invisible = TRUE;
            else
                sprite->invisible = FALSE;
        }
        val = sprite->data[2] + sprite->data[1] * 128;
        sprite->y2 = gSineTable[val] / 128;
        sprite->data[2] += 8;
    }
    else
    {
        sprite->invisible = TRUE;
    }
}

static void CreateSearchParameterScrollArrows(u8 taskId)
{
    u8 spriteId;

    spriteId = CreateSprite(&sScrollArrowSpriteTemplate, 184, 4, 0);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = 0;
    gSprites[spriteId].callback = SpriteCB_SearchParameterScrollArrow;

    spriteId = CreateSprite(&sScrollArrowSpriteTemplate, 184, 108, 0);
    gSprites[spriteId].data[0] = taskId;
    gSprites[spriteId].data[1] = 1;
    gSprites[spriteId].vFlip = TRUE;
    gSprites[spriteId].callback = SpriteCB_SearchParameterScrollArrow;
}
